diff --git a/Cargo.lock b/Cargo.lock index 6db89963c..0500f78b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,19 +23,13 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli 0.29.0", + "gimli 0.31.1", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -197,9 +191,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "approx" @@ -210,20 +204,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "aquamarine" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" -dependencies = [ - "include_dir", - "itertools 0.10.5", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "aquamarine" version = "0.5.0" @@ -235,7 +215,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -463,21 +443,6 @@ dependencies = [ "scale-info", ] -[[package]] -name = "ark-secret-scalar" -version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", - "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=0fef826)", - "digest 0.10.7", - "getrandom_or_panic", - "zeroize", -] - [[package]] name = "ark-secret-scalar" version = "0.0.2" @@ -530,7 +495,8 @@ dependencies = [ [[package]] name = "ark-transcript" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563084372d89271122bd743ef0a608179726f5fad0566008ba55bd0f756489b8" dependencies = [ "ark-ff", "ark-serialize", @@ -553,12 +519,6 @@ dependencies = [ "sha3", ] -[[package]] -name = "array-bytes" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" - [[package]] name = "array-bytes" version = "6.2.3" @@ -567,9 +527,9 @@ checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -656,13 +616,13 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.0", + "fastrand 2.1.1", "futures-lite 2.3.0", "slab", ] @@ -712,7 +672,7 @@ dependencies = [ "futures-lite 2.3.0", "parking", "polling 3.7.3", - "rustix 0.38.34", + "rustix 0.38.37", "slab", "tracing", "windows-sys 0.59.0", @@ -762,7 +722,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.34", + "rustix 0.38.37", "windows-sys 0.48.0", ] @@ -778,7 +738,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.34", + "rustix 0.38.37", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -792,13 +752,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -828,44 +788,23 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ - "addr2line 0.22.0", - "cc", + "addr2line 0.24.2", "cfg-if", "libc", - "miniz_oxide 0.7.4", - "object 0.36.3", + "miniz_oxide", + "object 0.36.5", "rustc-demangle", -] - -[[package]] -name = "bandersnatch_vrfs" -version = "0.0.4" -source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" -dependencies = [ - "ark-bls12-381", - "ark-ec", - "ark-ed-on-bls12-381-bandersnatch", - "ark-ff", - "ark-serialize", - "ark-std", - "dleq_vrf 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=0fef826)", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "ring 0.1.0 (git+https://github.com/w3f/ring-proof?rev=665f5f5)", - "sha2 0.10.8", - "sp-ark-bls12-381", - "sp-ark-ed-on-bls12-381-bandersnatch", - "zeroize", + "windows-targets 0.52.6", ] [[package]] @@ -879,12 +818,12 @@ dependencies = [ "ark-ff", "ark-serialize", "ark-std", - "dleq_vrf 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", + "dleq_vrf", "fflonk", "merlin", "rand_chacha 0.3.1", "rand_core 0.6.4", - "ring 0.1.0 (git+https://github.com/w3f/ring-proof)", + "ring 0.1.0", "sha2 0.10.8", "sp-ark-bls12-381", "sp-ark-ed-on-bls12-381-bandersnatch", @@ -965,7 +904,7 @@ dependencies = [ [[package]] name = "binary-merkle-tree" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hash-db", "log", @@ -992,13 +931,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.20", + "prettyplease 0.2.22", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1100,7 +1039,7 @@ checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.3.0", + "constant_time_eq 0.3.1", ] [[package]] @@ -1111,7 +1050,7 @@ checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.3.0", + "constant_time_eq 0.3.1", ] [[package]] @@ -1124,7 +1063,7 @@ dependencies = [ "arrayvec 0.7.6", "cc", "cfg-if", - "constant_time_eq 0.3.0", + "constant_time_eq 0.3.1", ] [[package]] @@ -1182,11 +1121,11 @@ dependencies = [ [[package]] name = "bp-xcm-bridge-hub-router" version = "0.6.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] @@ -1234,9 +1173,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -1246,9 +1185,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bzip2-sys" @@ -1305,9 +1244,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.13" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -1438,15 +1377,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ckb-merkle-mountain-range" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" -dependencies = [ - "cfg-if", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -1460,9 +1390,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -1470,9 +1400,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -1483,14 +1413,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1538,7 +1468,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -1571,23 +1501,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=665f5f5#665f5f51af5734c7b6d90b985dd6861d4c5b4752" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "fflonk", - "getrandom_or_panic", - "merlin", - "rand_chacha 0.3.1", -] - -[[package]] -name = "common" -version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" +source = "git+https://github.com/w3f/ring-proof#652286c32f96beb9ce7f5793f5e2c2c923f63b73" dependencies = [ "ark-ec", "ark-ff", @@ -1596,8 +1510,7 @@ dependencies = [ "ark-std", "fflonk", "getrandom_or_panic", - "merlin", - "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -1662,9 +1575,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "constcat" @@ -1724,9 +1637,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1958,7 +1871,7 @@ dependencies = [ [[package]] name = "cumulus-client-cli" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "clap", "parity-scale-codec", @@ -1967,7 +1880,7 @@ dependencies = [ "sc-client-api", "sc-service", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "url", ] @@ -1975,7 +1888,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1990,7 +1903,7 @@ dependencies = [ "sc-client-api", "sp-api 26.0.0", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "tracing", ] @@ -1998,7 +1911,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-aura" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-client-collator", @@ -2027,7 +1940,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-aura", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", @@ -2040,7 +1953,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -2058,10 +1971,10 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-timestamp 26.0.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-trie 29.0.0", "substrate-prometheus-endpoint", "tracing", ] @@ -2069,7 +1982,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-proposer" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "anyhow", "async-trait", @@ -2084,7 +1997,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -2098,7 +2011,7 @@ dependencies = [ "sc-client-api", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", "tracing", @@ -2107,7 +2020,7 @@ dependencies = [ [[package]] name = "cumulus-client-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2118,20 +2031,20 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api 26.0.0", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "tracing", ] [[package]] name = "cumulus-client-pov-recovery" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2155,7 +2068,7 @@ dependencies = [ [[package]] name = "cumulus-client-service" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", @@ -2183,7 +2096,7 @@ dependencies = [ "sp-api 26.0.0", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-transaction-pool", @@ -2192,7 +2105,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-aura-ext" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-pallet-parachain-system", "frame-support 28.0.0", @@ -2204,31 +2117,13 @@ dependencies = [ "sp-application-crypto 30.0.0", "sp-consensus-aura", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", -] - -[[package]] -name = "cumulus-pallet-dmp-queue" -version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" -dependencies = [ - "cumulus-primitives-core", - "frame-benchmarking 28.0.0", - "frame-support 28.0.0", - "frame-system 28.0.0", - "log", - "parity-scale-codec", - "scale-info", - "sp-io 30.0.0", - "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "staging-xcm 7.0.0", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "cumulus-pallet-parachain-system" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -2247,34 +2142,35 @@ dependencies = [ "polkadot-runtime-common 7.0.0", "polkadot-runtime-parachains 7.0.0", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "sp-version 29.0.0", "staging-xcm 7.0.0", + "staging-xcm-builder 7.0.0", "trie-db 0.29.1", ] [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "cumulus-pallet-session-benchmarking" version = "9.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -2282,13 +2178,13 @@ dependencies = [ "pallet-session 28.0.0", "parity-scale-codec", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "cumulus-pallet-xcm" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "frame-support 28.0.0", @@ -2297,14 +2193,14 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", ] [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bounded-collections", "bp-xcm-bridge-hub-router", @@ -2318,18 +2214,19 @@ dependencies = [ "polkadot-runtime-common 7.0.0", "polkadot-runtime-parachains 7.0.0", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", + "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", ] [[package]] name = "cumulus-primitives-aura" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "polkadot-core-primitives 7.0.0", @@ -2337,13 +2234,13 @@ dependencies = [ "sp-api 26.0.0", "sp-consensus-aura", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "cumulus-primitives-core" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "polkadot-core-primitives 7.0.0", @@ -2352,42 +2249,42 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "staging-xcm 7.0.0", ] [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] name = "cumulus-primitives-proof-size-hostfunction" version = "0.2.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] name = "cumulus-primitives-storage-weight-reclaim" version = "1.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "cumulus-primitives-proof-size-hostfunction", @@ -2398,13 +2295,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "cumulus-primitives-utility" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "frame-support 28.0.0", @@ -2415,7 +2312,7 @@ dependencies = [ "polkadot-runtime-parachains 7.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", @@ -2424,7 +2321,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2440,7 +2337,7 @@ dependencies = [ "sc-tracing", "sp-api 26.0.0", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", ] @@ -2448,7 +2345,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2466,9 +2363,9 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", @@ -2508,7 +2405,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2532,10 +2429,10 @@ dependencies = [ "sp-api 26.0.0", "sp-authority-discovery 26.0.0", "sp-consensus-babe 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-version 29.0.0", "thiserror", "tokio", @@ -2547,15 +2444,15 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives 7.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -2595,7 +2492,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2613,9 +2510,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.126" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c4eae4b7fc8dcb0032eb3b1beee46b38d371cdeaf2d0c64b9944f6f69ad7755" +checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" dependencies = [ "cc", "cxxbridge-flags", @@ -2625,9 +2522,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.126" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c822bf7fb755d97328d6c337120b6f843678178751cba33c9da25cf522272e0" +checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" dependencies = [ "cc", "codespan-reporting", @@ -2635,24 +2532,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "cxxbridge-flags" -version = "1.0.126" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719d6197dc016c88744aff3c0d0340a01ecce12e8939fc282e7c8f583ee64bc6" +checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" [[package]] name = "cxxbridge-macro" -version = "1.0.126" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35de3b547387863c8f82013c4f79f1c2162edee956383e4089e1d04c18c4f16c" +checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2676,7 +2573,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2687,7 +2584,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2792,7 +2689,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2805,7 +2702,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2851,7 +2748,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2860,7 +2757,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -2949,23 +2846,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", -] - -[[package]] -name = "dleq_vrf" -version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=0fef826#0fef8266d851932ad25d6b41bc4b34d834d1e11d" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-scale", - "ark-secret-scalar 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=0fef826)", - "ark-serialize", - "ark-std", - "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=0fef826)", - "arrayvec 0.7.6", - "zeroize", + "syn 2.0.79", ] [[package]] @@ -2976,7 +2857,7 @@ dependencies = [ "ark-ec", "ark-ff", "ark-scale", - "ark-secret-scalar 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", + "ark-secret-scalar", "ark-serialize", "ark-std", "ark-transcript 0.0.2 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", @@ -3005,7 +2886,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.75", + "syn 2.0.79", "termcolor", "toml 0.8.19", "walkdir", @@ -3034,7 +2915,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3215,7 +3096,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3235,7 +3116,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3246,7 +3127,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3331,18 +3212,6 @@ dependencies = [ "futures", ] -[[package]] -name = "expander" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" -dependencies = [ - "blake3", - "fs-err", - "proc-macro2", - "quote", -] - [[package]] name = "expander" version = "2.2.1" @@ -3352,10 +3221,10 @@ dependencies = [ "blake2 0.10.6", "file-guard", "fs-err", - "prettyplease 0.2.20", + "prettyplease 0.2.22", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3381,15 +3250,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fatality" -version = "0.0.6" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +checksum = "ec6f82451ff7f0568c6181287189126d492b5654e30a788add08027b6363d019" dependencies = [ "fatality-proc-macro", "thiserror", @@ -3397,17 +3266,16 @@ dependencies = [ [[package]] name = "fatality-proc-macro" -version = "0.0.6" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ - "expander 0.0.4", - "indexmap 1.9.3", - "proc-macro-crate 1.1.3", + "expander", + "indexmap 2.6.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", - "thiserror", + "syn 2.0.79", ] [[package]] @@ -3471,9 +3339,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", @@ -3517,13 +3385,13 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.32" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -3559,7 +3427,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fork-tree" version = "12.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", ] @@ -3573,6 +3441,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror", +] + [[package]] name = "fragile" version = "2.0.0" @@ -3582,7 +3460,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-support-procedural 23.0.0", @@ -3595,12 +3473,12 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "static_assertions", ] @@ -3633,10 +3511,10 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "32.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "Inflector", - "array-bytes 6.2.3", + "array-bytes", "chrono", "clap", "comfy-table", @@ -3645,7 +3523,7 @@ dependencies = [ "frame-system 28.0.0", "gethostname", "handlebars", - "itertools 0.10.5", + "itertools 0.11.0", "lazy_static", "linked-hash-map", "log", @@ -3664,18 +3542,18 @@ dependencies = [ "serde_json", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-database", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "thousands", ] @@ -3686,38 +3564,38 @@ version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5c3bff645e46577c69c272733c53fa3a77d1ee6e40dfb66157bc94b0740b8fc" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "frame-election-provider-solution-type" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "frame-election-provider-support" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "frame-election-provider-solution-type 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "frame-election-provider-solution-type 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-npos-elections 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -3741,20 +3619,20 @@ dependencies = [ [[package]] name = "frame-executive" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "aquamarine 0.3.3", + "aquamarine", "frame-support 28.0.0", "frame-system 28.0.0", "frame-try-runtime", "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -3772,9 +3650,9 @@ dependencies = [ [[package]] name = "frame-metadata-hash-extension" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "docify", "frame-support 28.0.0", "frame-system 28.0.0", @@ -3787,10 +3665,10 @@ dependencies = [ [[package]] name = "frame-support" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "aquamarine 0.5.0", - "array-bytes 6.2.3", + "aquamarine", + "array-bytes", "bitflags 1.3.2", "docify", "environmental", @@ -3808,18 +3686,18 @@ dependencies = [ "smallvec", "sp-api 26.0.0", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing-proc-macro 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing-proc-macro 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", - "sp-metadata-ir 0.6.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-metadata-ir 0.6.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", "sp-staking 26.0.0", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", "static_assertions", "tt-call", @@ -3831,8 +3709,8 @@ version = "29.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e52c84b611d2049d9253f83a62ab0f093e4be5c42a7ef42ea5bb16d6611e32" dependencies = [ - "aquamarine 0.5.0", - "array-bytes 6.2.3", + "aquamarine", + "array-bytes", "bitflags 1.3.2", "docify", "environmental", @@ -3870,20 +3748,20 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse 0.2.0", - "expander 2.2.1", - "frame-support-procedural-tools 10.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "itertools 0.10.5", + "expander", + "frame-support-procedural-tools 10.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "itertools 0.11.0", "macro_magic", "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "syn 2.0.75", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "syn 2.0.79", ] [[package]] @@ -3895,7 +3773,7 @@ dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse 0.1.5", - "expander 2.2.1", + "expander", "frame-support-procedural-tools 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.10.5", "macro_magic", @@ -3903,7 +3781,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3913,22 +3791,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3363df38464c47a73eb521a4f648bfcc7537a82d70347ef8af3f73b6d019e910" dependencies = [ "frame-support-procedural-tools-derive 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "frame-support-procedural-tools" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "frame-support-procedural-tools-derive 11.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "proc-macro-crate 3.1.0", + "frame-support-procedural-tools-derive 11.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -3939,23 +3817,23 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "frame-system" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cfg-if", "docify", @@ -3964,10 +3842,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-version 29.0.0", "sp-weights 27.0.0", ] @@ -3996,22 +3874,22 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "frame-system-rpc-runtime-api" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "sp-api 26.0.0", @@ -4020,13 +3898,13 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "parity-scale-codec", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -4054,7 +3932,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "rustix 0.38.34", + "rustix 0.38.37", "windows-sys 0.48.0", ] @@ -4134,7 +4012,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.1.0", + "fastrand 2.1.1", "futures-core", "futures-io", "parking", @@ -4149,7 +4027,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -4303,9 +4181,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -4356,7 +4234,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.4.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -4421,6 +4299,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.8.4" @@ -4571,9 +4455,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -4629,9 +4513,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4777,12 +4661,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -4855,9 +4739,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" @@ -4872,9 +4756,9 @@ dependencies = [ [[package]] name = "is_executable" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +checksum = "4ba3d8548b8b04dafdf2f4cc6f5e379db766d0a6d9aac233ad4c9a92ea892233" dependencies = [ "winapi", ] @@ -4961,7 +4845,7 @@ dependencies = [ "http", "jsonrpsee-core", "pin-project", - "rustls-native-certs 0.7.2", + "rustls-native-certs 0.7.3", "rustls-pki-types", "soketto", "thiserror", @@ -5004,10 +4888,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d0bb047e79a143b32ea03974a6bf59b62c2a4c5f5d42a381c907a8bbb3f75c0" dependencies = [ "heck 0.4.1", - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -5062,9 +4946,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -5158,9 +5042,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -5579,7 +5463,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.7", ] [[package]] @@ -5649,9 +5533,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.19" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "pkg-config", @@ -5735,7 +5619,7 @@ dependencies = [ "futures", "futures-timer", "hex-literal", - "indexmap 2.4.0", + "indexmap 2.6.0", "libc", "mockall 0.12.1", "multiaddr", @@ -5818,19 +5702,18 @@ dependencies = [ [[package]] name = "lz4" -version = "1.26.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958b4caa893816eea05507c20cfe47574a43d9a697138a7872990bba8a0ece68" +checksum = "4d1febb2b4a79ddd1980eede06a8f7902197960aa0383ffcfdd62fe723036725" dependencies = [ - "libc", "lz4-sys", ] [[package]] name = "lz4-sys" -version = "1.10.0" +version = "1.11.1+lz4-1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" dependencies = [ "cc", "libc", @@ -5854,7 +5737,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -5868,7 +5751,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -5879,7 +5762,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -5890,7 +5773,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -5955,7 +5838,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.34", + "rustix 0.38.37", ] [[package]] @@ -5969,9 +5852,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -6000,7 +5883,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f313fcff1d2a4bcaa2deeaa00bf7530d77d5f7bd0467a117dde2e29a75a7a17a" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "blake3", "frame-metadata", "parity-scale-codec", @@ -6037,15 +5920,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -6095,7 +5969,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "log", @@ -6106,7 +5980,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-beefy", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-mmr-primitives", "sp-runtime 31.0.1", ] @@ -6114,14 +5988,14 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "parity-scale-codec", "serde", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-mmr-primitives", "sp-runtime 31.0.1", ] @@ -6177,7 +6051,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -6309,7 +6183,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", "synstructure 0.13.1", ] @@ -6363,7 +6237,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -6472,12 +6346,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -6515,6 +6390,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + [[package]] name = "nonzero_ext" version = "0.3.0" @@ -6635,9 +6516,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.3" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] @@ -6653,9 +6534,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "opaque-debug" @@ -6692,7 +6576,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -6752,11 +6636,11 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1344346d5af32c95bbddea91b18a88cc83eac394192d20ef2fc4c40a74332355" dependencies = [ - "expander 2.2.1", - "indexmap 2.4.0", + "expander", + "indexmap 2.6.0", "itertools 0.11.0", "petgraph", - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -6780,7 +6664,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pallet-asset-conversion" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -6790,25 +6674,25 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-asset-rate" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -6830,7 +6714,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -6839,16 +6723,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-assets" version = "29.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -6856,15 +6740,15 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-aura" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -6875,13 +6759,13 @@ dependencies = [ "sp-application-crypto 30.0.0", "sp-consensus-aura", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-authority-discovery" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -6891,7 +6775,7 @@ dependencies = [ "sp-application-crypto 30.0.0", "sp-authority-discovery 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -6914,7 +6798,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -6922,7 +6806,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -6943,7 +6827,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -6956,12 +6840,12 @@ dependencies = [ "scale-info", "sp-application-crypto 30.0.0", "sp-consensus-babe 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -6992,9 +6876,9 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "aquamarine 0.5.0", + "aquamarine", "docify", "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", @@ -7004,17 +6888,17 @@ dependencies = [ "pallet-balances 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-balances" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -7024,7 +6908,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7047,7 +6931,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -7061,15 +6945,15 @@ dependencies = [ "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-beefy-mmr" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "binary-merkle-tree", "frame-support 28.0.0", "frame-system 28.0.0", @@ -7082,17 +6966,17 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-consensus-beefy", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-bounties" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7101,16 +6985,16 @@ dependencies = [ "pallet-treasury 27.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-broker" version = "0.6.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "frame-benchmarking 28.0.0", @@ -7121,9 +7005,9 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7164,18 +7048,18 @@ dependencies = [ "shp-constants", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] name = "pallet-child-bounties" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7185,16 +7069,16 @@ dependencies = [ "pallet-treasury 27.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-collator-selection" version = "9.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7208,13 +7092,13 @@ dependencies = [ "scale-info", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-collective" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7222,16 +7106,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-conviction-voting" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "assert_matches", "frame-benchmarking 28.0.0", @@ -7242,13 +7126,27 @@ dependencies = [ "serde", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", +] + +[[package]] +name = "pallet-delegated-staking" +version = "1.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" +dependencies = [ + "frame-support 28.0.0", + "frame-system 28.0.0", + "parity-scale-codec", + "scale-info", + "sp-runtime 31.0.1", + "sp-staking 26.0.0", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-democracy" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7257,16 +7155,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-election-provider-multi-phase" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", @@ -7278,11 +7176,11 @@ dependencies = [ "rand 0.8.5", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-npos-elections 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "strum 0.26.3", ] @@ -7313,7 +7211,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", @@ -7321,7 +7219,7 @@ dependencies = [ "parity-scale-codec", "sp-npos-elections 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7342,7 +7240,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7350,18 +7248,18 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-npos-elections 26.0.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-fast-unstake" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -7374,7 +7272,7 @@ dependencies = [ "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7419,12 +7317,12 @@ dependencies = [ "shp-constants", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -7440,7 +7338,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7452,18 +7350,18 @@ dependencies = [ "scale-info", "sp-application-crypto 30.0.0", "sp-consensus-grandpa", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-identity" -version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +version = "29.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "enumflags2", "frame-benchmarking 28.0.0", @@ -7474,7 +7372,7 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7498,7 +7396,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7508,34 +7406,34 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-indices" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-membership" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7543,16 +7441,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-message-queue" version = "31.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "environmental", "frame-benchmarking 28.0.0", @@ -7562,10 +7460,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", ] @@ -7593,7 +7491,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7601,17 +7499,17 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-mmr-primitives", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-multisig" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7621,13 +7519,13 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-nfts" version = "22.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "enumflags2", "frame-benchmarking 28.0.0", @@ -7636,16 +7534,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-nis" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7653,15 +7551,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-nomination-pools" version = "25.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -7669,49 +7567,50 @@ dependencies = [ "pallet-balances 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-nomination-pools-benchmarking" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "pallet-bags-list", + "pallet-delegated-staking", "pallet-nomination-pools", "pallet-staking 28.0.0", "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-nomination-pools-runtime-api" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", "sp-api 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-offences" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -7722,13 +7621,13 @@ dependencies = [ "serde", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-offences-benchmarking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", @@ -7746,13 +7645,13 @@ dependencies = [ "scale-info", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-parameters" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -7762,9 +7661,9 @@ dependencies = [ "paste", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7784,10 +7683,10 @@ dependencies = [ "shp-constants", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", ] [[package]] @@ -7798,13 +7697,13 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-preimage" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7812,10 +7711,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7835,11 +7734,11 @@ dependencies = [ "shp-constants", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -7850,13 +7749,13 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-proxy" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7865,7 +7764,7 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -7880,16 +7779,16 @@ dependencies = [ "scale-info", "serde", "shp-session-keys", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-ranked-collective" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7899,16 +7798,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-recovery" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -7917,13 +7816,13 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-referenda" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "assert_matches", "frame-benchmarking 28.0.0", @@ -7936,28 +7835,28 @@ dependencies = [ "sp-arithmetic 23.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-root-testing" version = "4.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-scheduler" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -7968,14 +7867,14 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", ] [[package]] name = "pallet-session" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -7984,14 +7883,14 @@ dependencies = [ "pallet-timestamp 27.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -8020,7 +7919,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8031,31 +7930,31 @@ dependencies = [ "rand 0.8.5", "sp-runtime 31.0.1", "sp-session 27.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-society" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "log", "parity-scale-codec", - "rand_chacha 0.2.2", + "rand_chacha 0.3.1", "scale-info", "sp-arithmetic 23.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-staking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-election-provider-support 28.0.0", @@ -8065,14 +7964,14 @@ dependencies = [ "pallet-authorship 28.0.0", "pallet-session 28.0.0", "parity-scale-codec", - "rand_chacha 0.2.2", + "rand_chacha 0.3.1", "scale-info", "serde", "sp-application-crypto 30.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -8101,18 +8000,18 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "pallet-staking-reward-fn" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "log", "sp-arithmetic 23.0.0", @@ -8131,7 +8030,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "sp-api 26.0.0", @@ -8141,7 +8040,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8149,10 +8048,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -8172,11 +8071,11 @@ dependencies = [ "shp-constants", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -8192,7 +8091,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -8202,13 +8101,13 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-timestamp" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -8220,8 +8119,8 @@ dependencies = [ "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-timestamp 26.0.0", ] @@ -8249,7 +8148,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8259,26 +8158,26 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-transaction-payment" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -8301,14 +8200,14 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "30.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-rpc", "sp-runtime 31.0.1", "sp-weights 27.0.0", @@ -8317,7 +8216,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "pallet-transaction-payment 28.0.0", "parity-scale-codec", @@ -8329,7 +8228,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "frame-benchmarking 28.0.0", @@ -8340,9 +8239,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -8368,7 +8267,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8377,29 +8276,29 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-utility" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", "frame-system 28.0.0", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-vesting" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8408,7 +8307,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -8430,7 +8329,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8439,13 +8338,13 @@ dependencies = [ "scale-info", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "pallet-xcm" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bounded-collections", "frame-benchmarking 28.0.0", @@ -8456,10 +8355,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", @@ -8469,7 +8368,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -8479,7 +8378,7 @@ dependencies = [ "scale-info", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", @@ -8488,7 +8387,7 @@ dependencies = [ [[package]] name = "parachains-common" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "cumulus-primitives-utility", @@ -8506,10 +8405,10 @@ dependencies = [ "polkadot-primitives 7.0.0", "scale-info", "sp-consensus-aura", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-parachain-info", "staging-xcm 7.0.0", "staging-xcm-executor 7.0.0", @@ -8571,7 +8470,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -8591,9 +8490,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -8638,7 +8537,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -8708,9 +8607,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -8719,9 +8618,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -8729,22 +8628,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -8758,7 +8657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.4.0", + "indexmap 2.6.0", ] [[package]] @@ -8796,7 +8695,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -8824,7 +8723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand 2.1.1", "futures-io", ] @@ -8840,19 +8739,19 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polkadot-approval-distribution" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "futures", "futures-timer", - "itertools 0.10.5", + "itertools 0.11.0", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", @@ -8867,7 +8766,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "always-assert", "futures", @@ -8883,7 +8782,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "derive_more", "fatality", @@ -8896,8 +8795,9 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives 7.0.0", "rand 0.8.5", + "sc-network", "schnellru", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "thiserror", "tracing-gum", @@ -8906,7 +8806,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "fatality", @@ -8926,10 +8826,20 @@ dependencies = [ "tracing-gum", ] +[[package]] +name = "polkadot-ckb-merkle-mountain-range" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b44320e5f7ce2c18227537a3032ae5b2c476a7e8eddba45333e1011fc31b92" +dependencies = [ + "cfg-if", + "itertools 0.10.5", +] + [[package]] name = "polkadot-cli" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cfg-if", "clap", @@ -8945,7 +8855,7 @@ dependencies = [ "sc-storage-monitor", "sc-sysinfo", "sc-tracing", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring", "sp-maybe-compressed-blob", @@ -8957,7 +8867,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "fatality", @@ -8968,7 +8878,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives 7.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "thiserror", @@ -8979,13 +8889,13 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -9004,13 +8914,13 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "derive_more", "fatality", "futures", "futures-timer", - "indexmap 2.4.0", + "indexmap 2.6.0", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -9029,21 +8939,21 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", "polkadot-primitives 7.0.0", "reed-solomon-novelpoly", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-trie 29.0.0", "thiserror", ] [[package]] name = "polkadot-gossip-support" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "futures-timer", @@ -9056,8 +8966,8 @@ dependencies = [ "sc-network", "sc-network-common", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", "tracing-gum", ] @@ -9065,7 +8975,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "always-assert", "async-trait", @@ -9088,7 +8998,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "parity-scale-codec", @@ -9097,7 +9007,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives 7.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-maybe-compressed-blob", "thiserror", "tracing-gum", @@ -9106,13 +9016,13 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "derive_more", "futures", "futures-timer", - "itertools 0.10.5", + "itertools 0.11.0", "kvdb", "merlin", "parity-scale-codec", @@ -9139,7 +9049,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "futures", @@ -9161,7 +9071,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "fatality", @@ -9181,7 +9091,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "polkadot-node-subsystem", @@ -9196,7 +9106,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -9217,7 +9127,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "polkadot-node-metrics", @@ -9231,7 +9141,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "futures-timer", @@ -9248,7 +9158,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "fatality", "futures", @@ -9267,7 +9177,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -9284,7 +9194,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-prospective-parachains" version = "6.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "fatality", @@ -9301,7 +9211,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "fatality", @@ -9319,10 +9229,10 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "always-assert", - "array-bytes 6.2.3", + "array-bytes", "blake3", "cfg-if", "futures", @@ -9338,7 +9248,7 @@ dependencies = [ "polkadot-primitives 7.0.0", "rand 0.8.5", "slotmap", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "tempfile", "thiserror", "tokio", @@ -9348,7 +9258,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "polkadot-node-primitives", @@ -9364,13 +9274,13 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-common" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cpu-time", "futures", "landlock", "libc", - "nix 0.27.1", + "nix 0.28.0", "parity-scale-codec", "polkadot-parachain-primitives 6.0.0", "polkadot-primitives 7.0.0", @@ -9378,11 +9288,11 @@ dependencies = [ "sc-executor-common", "sc-executor-wasmtime", "seccompiler", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-io 30.0.0", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "tracing-gum", ] @@ -9390,7 +9300,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "polkadot-node-metrics", @@ -9405,7 +9315,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "lazy_static", "log", @@ -9416,7 +9326,7 @@ dependencies = [ "polkadot-primitives 7.0.0", "sc-network", "sc-network-types", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "thiserror", "tokio", ] @@ -9424,7 +9334,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bs58 0.5.1", "futures", @@ -9443,7 +9353,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-channel 1.9.0", "async-trait", @@ -9469,7 +9379,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "bounded-vec", @@ -9481,7 +9391,7 @@ dependencies = [ "serde", "sp-application-crypto 30.0.0", "sp-consensus-babe 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-maybe-compressed-blob", "sp-runtime 31.0.1", @@ -9492,7 +9402,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -9502,11 +9412,12 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "bitvec", "derive_more", + "fatality", "futures", "orchestra", "polkadot-node-jaeger", @@ -9531,19 +9442,20 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "derive_more", "fatality", "futures", "futures-channel", - "itertools 0.10.5", + "itertools 0.11.0", "kvdb", "parity-db", "parity-scale-codec", "parking_lot 0.12.3", "pin-project", + "polkadot-erasure-coding", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", @@ -9557,7 +9469,7 @@ dependencies = [ "sc-client-api", "schnellru", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "thiserror", "tracing-gum", @@ -9566,7 +9478,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -9580,7 +9492,7 @@ dependencies = [ "polkadot-primitives 7.0.0", "sc-client-api", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "tikv-jemalloc-ctl", "tracing-gum", ] @@ -9588,7 +9500,7 @@ dependencies = [ [[package]] name = "polkadot-parachain-primitives" version = "6.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bounded-collections", "derive_more", @@ -9596,9 +9508,9 @@ dependencies = [ "polkadot-core-primitives 7.0.0", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", ] @@ -9623,7 +9535,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "hex-literal", @@ -9638,13 +9550,13 @@ dependencies = [ "sp-arithmetic 23.0.0", "sp-authority-discovery 26.0.0", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -9678,7 +9590,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -9698,10 +9610,12 @@ dependencies = [ "sc-sync-state-rpc", "sc-transaction-pool-api", "sp-api 26.0.0", + "sp-application-crypto 30.0.0", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe 0.32.0", + "sp-consensus-beefy", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "substrate-frame-rpc-system", @@ -9711,7 +9625,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitvec", "frame-benchmarking 28.0.0", @@ -9728,7 +9642,7 @@ dependencies = [ "pallet-broker 0.6.0", "pallet-election-provider-multi-phase 27.0.0", "pallet-fast-unstake 27.0.0", - "pallet-identity 28.0.0", + "pallet-identity 29.0.0", "pallet-session 28.0.0", "pallet-staking 28.0.0", "pallet-staking-reward-fn 19.0.0", @@ -9745,14 +9659,14 @@ dependencies = [ "serde_derive", "slot-range-helper 7.0.0", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-npos-elections 26.0.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", @@ -9828,14 +9742,14 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bs58 0.5.1", "frame-benchmarking 28.0.0", "parity-scale-codec", "polkadot-primitives 7.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -9855,7 +9769,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bitflags 1.3.2", "bitvec", @@ -9888,14 +9802,14 @@ dependencies = [ "sp-api 26.0.0", "sp-application-crypto 30.0.0", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-executor 7.0.0", "static_assertions", @@ -9953,7 +9867,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "bitvec", @@ -10048,7 +9962,7 @@ dependencies = [ "sp-consensus-babe 0.32.0", "sp-consensus-beefy", "sp-consensus-grandpa", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-keyring", @@ -10058,7 +9972,7 @@ dependencies = [ "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-state-machine 0.35.0", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-timestamp 26.0.0", "sp-transaction-pool", "sp-version 29.0.0", @@ -10074,14 +9988,14 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "arrayvec 0.7.6", "bitvec", "fatality", "futures", "futures-timer", - "indexmap 2.4.0", + "indexmap 2.6.0", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -10097,11 +10011,11 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "polkadot-primitives 7.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "tracing-gum", ] @@ -10154,7 +10068,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10164,7 +10078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10214,7 +10128,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite 0.2.14", - "rustix 0.38.34", + "rustix 0.38.37", "tracing", "windows-sys 0.59.0", ] @@ -10244,9 +10158,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "postgres-protocol" @@ -10353,12 +10267,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10411,11 +10325,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit 0.22.22", ] [[package]] @@ -10450,7 +10364,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10496,7 +10410,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10554,11 +10468,11 @@ dependencies = [ "multimap 0.10.0", "once_cell", "petgraph", - "prettyplease 0.2.20", + "prettyplease 0.2.22", "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.75", + "syn 2.0.79", "tempfile", ] @@ -10585,7 +10499,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10608,9 +10522,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] @@ -10720,9 +10634,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -10825,9 +10739,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.1.0" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ "bitflags 2.6.0", ] @@ -10881,9 +10795,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -10928,7 +10842,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -10973,14 +10887,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -10994,13 +10908,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -11011,9 +10925,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "resolv-conf" @@ -11038,35 +10952,18 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=665f5f5#665f5f51af5734c7b6d90b985dd6861d4c5b4752" +source = "git+https://github.com/w3f/ring-proof#652286c32f96beb9ce7f5793f5e2c2c923f63b73" dependencies = [ "ark-ec", "ark-ff", "ark-poly", "ark-serialize", "ark-std", + "ark-transcript 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.7.6", "blake2 0.10.6", - "common 0.1.0 (git+https://github.com/w3f/ring-proof?rev=665f5f5)", + "common", "fflonk", - "merlin", -] - -[[package]] -name = "ring" -version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "arrayvec 0.7.6", - "blake2 0.10.6", - "common 0.1.0 (git+https://github.com/w3f/ring-proof)", - "fflonk", - "merlin", ] [[package]] @@ -11121,7 +11018,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "binary-merkle-tree", "bitvec", @@ -11149,7 +11046,7 @@ dependencies = [ "pallet-democracy", "pallet-elections-phragmen", "pallet-grandpa", - "pallet-identity 28.0.0", + "pallet-identity 29.0.0", "pallet-indices", "pallet-membership", "pallet-message-queue 31.0.0", @@ -11198,8 +11095,8 @@ dependencies = [ "sp-consensus-babe 0.32.0", "sp-consensus-beefy", "sp-consensus-grandpa", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-mmr-primitives", @@ -11207,8 +11104,8 @@ dependencies = [ "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", "sp-version 29.0.0", "staging-xcm 7.0.0", @@ -11222,13 +11119,13 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "polkadot-primitives 7.0.0", "polkadot-runtime-common 7.0.0", "smallvec", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-weights 27.0.0", "staging-xcm 7.0.0", @@ -11297,9 +11194,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver 1.0.23", ] @@ -11343,9 +11240,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -11387,7 +11284,7 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.8", "subtle 2.6.1", "zeroize", ] @@ -11406,12 +11303,12 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04182dffc9091a404e0fc069ea5cd60e5b866c3adf881eff99a32d048242dffa" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -11428,19 +11325,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" @@ -11454,9 +11350,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -11518,18 +11414,18 @@ dependencies = [ [[package]] name = "sc-allocator" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "log", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", ] [[package]] name = "sc-authority-discovery" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -11542,7 +11438,7 @@ dependencies = [ "multihash-codetable", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.11.9", + "prost-build 0.12.6", "rand 0.8.5", "sc-client-api", "sc-network", @@ -11550,7 +11446,7 @@ dependencies = [ "sp-api 26.0.0", "sp-authority-discovery 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", @@ -11560,7 +11456,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "futures-timer", @@ -11573,7 +11469,7 @@ dependencies = [ "sp-api 26.0.0", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", @@ -11582,27 +11478,27 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "sp-api 26.0.0", "sp-block-builder", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-trie 29.0.0", ] [[package]] name = "sc-chain-spec" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "docify", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", @@ -11612,37 +11508,37 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-io 30.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "sc-chain-spec-derive" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sc-cli" version = "0.36.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "chrono", "clap", "fdlimit", "futures", - "itertools 0.10.5", + "itertools 0.11.0", "libp2p-identity", "log", "names", @@ -11663,10 +11559,10 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keyring", "sp-keystore 0.34.0", - "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", "sp-version 29.0.0", "thiserror", @@ -11676,7 +11572,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "fnv", "futures", @@ -11689,21 +11585,21 @@ dependencies = [ "sp-api 26.0.0", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-database", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", "sp-statement-store", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.35.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hash-db", "kvdb", @@ -11719,17 +11615,17 @@ dependencies = [ "schnellru", "sp-arithmetic 23.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-database", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-trie 29.0.0", ] [[package]] name = "sc-consensus" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -11744,7 +11640,7 @@ dependencies = [ "sp-api 26.0.0", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", "substrate-prometheus-endpoint", @@ -11754,7 +11650,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -11772,7 +11668,7 @@ dependencies = [ "sp-consensus", "sp-consensus-aura", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", @@ -11783,7 +11679,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "fork-tree", @@ -11807,8 +11703,8 @@ dependencies = [ "sp-consensus", "sp-consensus-babe 0.32.0", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", @@ -11819,7 +11715,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "jsonrpsee", @@ -11832,7 +11728,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "thiserror", @@ -11841,9 +11737,9 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "async-trait", "fnv", @@ -11864,10 +11760,9 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-beefy", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", - "sp-mmr-primitives", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", "thiserror", @@ -11878,7 +11773,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "jsonrpsee", @@ -11888,8 +11783,9 @@ dependencies = [ "sc-consensus-beefy", "sc-rpc", "serde", + "sp-application-crypto 30.0.0", "sp-consensus-beefy", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "thiserror", ] @@ -11897,7 +11793,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "fork-tree", "parity-scale-codec", @@ -11910,10 +11806,10 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.19.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "ahash 0.8.11", - "array-bytes 6.2.3", + "array-bytes", "async-trait", "dyn-clone", "finality-grandpa", @@ -11943,8 +11839,8 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", @@ -11954,7 +11850,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.19.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "finality-grandpa", "futures", @@ -11966,7 +11862,7 @@ dependencies = [ "sc-rpc", "serde", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "thiserror", ] @@ -11974,7 +11870,7 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" version = "0.35.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "assert_matches", "async-trait", @@ -11997,7 +11893,7 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-babe 0.32.0", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", @@ -12009,7 +11905,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -12023,7 +11919,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", @@ -12032,7 +11928,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", @@ -12041,26 +11937,26 @@ dependencies = [ "sc-executor-wasmtime", "schnellru", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-io 30.0.0", - "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "sp-version 29.0.0", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "tracing", ] [[package]] name = "sc-executor-common" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "polkavm", "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "wasm-instrument", ] @@ -12068,18 +11964,18 @@ dependencies = [ [[package]] name = "sc-executor-polkavm" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "log", "polkavm", "sc-executor-common", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "sc-executor-wasmtime" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "anyhow", "cfg-if", @@ -12089,15 +11985,15 @@ dependencies = [ "rustix 0.36.17", "sc-allocator", "sc-executor-common", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "wasmtime", ] [[package]] name = "sc-informant" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "ansi_term", "futures", @@ -12114,13 +12010,13 @@ dependencies = [ [[package]] name = "sc-keystore" version = "25.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "parking_lot 0.12.3", "serde_json", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "thiserror", ] @@ -12128,9 +12024,9 @@ dependencies = [ [[package]] name = "sc-mixnet" version = "0.4.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 4.2.0", + "array-bytes", "arrayvec 0.7.6", "blake2 0.10.6", "bytes", @@ -12147,7 +12043,7 @@ dependencies = [ "sc-transaction-pool-api", "sp-api 26.0.0", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-mixnet", "sp-runtime 31.0.1", @@ -12157,9 +12053,9 @@ dependencies = [ [[package]] name = "sc-network" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "async-trait", "asynchronous-codec", @@ -12180,8 +12076,8 @@ dependencies = [ "parking_lot 0.12.3", "partial_sort", "pin-project", - "prost 0.11.9", - "prost-build 0.11.9", + "prost 0.12.6", + "prost-build 0.12.6", "rand 0.8.5", "sc-client-api", "sc-network-common", @@ -12193,7 +12089,7 @@ dependencies = [ "smallvec", "sp-arithmetic 23.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", "thiserror", @@ -12208,14 +12104,14 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "bitflags 1.3.2", "futures", "libp2p-identity", "parity-scale-codec", - "prost-build 0.11.9", + "prost-build 0.12.6", "sc-consensus", "sc-network-types", "sp-consensus", @@ -12226,7 +12122,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "ahash 0.8.11", "futures", @@ -12246,20 +12142,20 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "futures", "log", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.11.9", + "prost-build 0.12.6", "sc-client-api", "sc-network", "sc-network-types", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "thiserror", ] @@ -12267,9 +12163,9 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "async-trait", "fork-tree", @@ -12280,7 +12176,7 @@ dependencies = [ "mockall 0.11.4", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.11.9", + "prost-build 0.12.6", "sc-client-api", "sc-consensus", "sc-network", @@ -12293,7 +12189,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "substrate-prometheus-endpoint", "thiserror", @@ -12304,9 +12200,9 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "futures", "libp2p", "log", @@ -12323,24 +12219,26 @@ dependencies = [ [[package]] name = "sc-network-types" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +version = "0.10.0" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "bs58 0.4.0", + "bs58 0.5.1", + "ed25519-dalek 2.1.1", "libp2p-identity", "litep2p", "multiaddr", "multihash 0.17.0", "rand 0.8.5", "thiserror", + "zeroize", ] [[package]] name = "sc-offchain" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "bytes", "fnv", "futures", @@ -12361,8 +12259,8 @@ dependencies = [ "sc-transaction-pool-api", "sc-utils", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", "sp-offchain", "sp-runtime 31.0.1", @@ -12373,7 +12271,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.17.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -12382,7 +12280,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "jsonrpsee", @@ -12400,7 +12298,7 @@ dependencies = [ "serde_json", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-offchain", "sp-rpc", @@ -12414,7 +12312,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -12424,7 +12322,7 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-rpc", "sp-runtime 31.0.1", "sp-version 29.0.0", @@ -12434,12 +12332,14 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ + "forwarded-header-value", "futures", "governor", "http", "hyper", + "ip_network", "jsonrpsee", "log", "serde_json", @@ -12452,9 +12352,9 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "futures", "futures-util", "hex", @@ -12472,7 +12372,7 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-rpc", "sp-runtime 31.0.1", "sp-version 29.0.0", @@ -12484,7 +12384,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.35.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "directories", @@ -12525,16 +12425,16 @@ dependencies = [ "sp-api 26.0.0", "sp-blockchain", "sp-consensus", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-state-machine 0.35.0", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", "sp-transaction-storage-proof", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-trie 29.0.0", "sp-version 29.0.0", "static_init", "substrate-prometheus-endpoint", @@ -12548,23 +12448,23 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.30.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.3", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", ] [[package]] name = "sc-storage-monitor" version = "0.16.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "clap", "fs4", "log", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "thiserror", "tokio", ] @@ -12572,7 +12472,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -12591,7 +12491,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "derive_more", "futures", @@ -12603,16 +12503,16 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-io 30.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "sc-telemetry" version = "15.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "chrono", "futures", @@ -12632,7 +12532,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "ansi_term", "chrono", @@ -12649,31 +12549,31 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-rpc", "sp-runtime 31.0.1", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "tracing", - "tracing-log 0.1.4", + "tracing-log 0.2.0", "tracing-subscriber 0.3.18", ] [[package]] name = "sc-tracing-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sc-transaction-pool" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -12688,10 +12588,10 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -12700,7 +12600,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", @@ -12708,7 +12608,7 @@ dependencies = [ "parity-scale-codec", "serde", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "thiserror", ] @@ -12716,7 +12616,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-channel 1.9.0", "futures", @@ -12771,7 +12671,7 @@ version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -12785,11 +12685,11 @@ checksum = "f0cded6518aa0bd6c1be2b88ac81bf7044992f0f154bfbabd5ad34f43512abcb" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -12951,9 +12851,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -13009,14 +12909,14 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -13026,9 +12926,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -13048,7 +12948,6 @@ name = "sh-xcm-simulator" version = "0.1.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", @@ -13108,23 +13007,24 @@ dependencies = [ "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-offchain", "sp-runtime 31.0.1", "sp-session 27.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "sp-version 29.0.0", "sp-weights 27.0.0", "staging-parachain-info", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", + "xcm-fee-payment-runtime-api", "xcm-simulator", ] @@ -13228,7 +13128,7 @@ dependencies = [ "sc-utils", "serde", "serde_json", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "tokio", ] @@ -13238,7 +13138,7 @@ name = "shc-blockchain-service" version = "0.1.0" dependencies = [ "anyhow", - "array-bytes 6.2.3", + "array-bytes", "async-trait", "cumulus-primitives-core", "cumulus-primitives-storage-weight-reclaim", @@ -13273,7 +13173,7 @@ dependencies = [ "shp-file-key-verifier", "shp-file-metadata", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "storage-hub-runtime", @@ -13308,11 +13208,11 @@ dependencies = [ "shp-forest-verifier", "shp-traits", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "storage-hub-runtime", "thiserror", "trie-db 0.29.1", @@ -13332,10 +13232,10 @@ dependencies = [ "serde_json", "shc-common", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "thiserror", "trie-db 0.29.1", ] @@ -13345,16 +13245,16 @@ name = "shc-file-transfer-service" version = "0.1.0" dependencies = [ "anyhow", - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "async-trait", "futures", - "libp2p-identity", "parity-scale-codec", "prost 0.12.6", "prost-build 0.12.6", "sc-client-api", "sc-network", + "sc-network-types", "sc-service", "sc-tracing", "sc-utils", @@ -13383,10 +13283,10 @@ dependencies = [ "shc-common", "shp-forest-verifier", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "thiserror", "tokio", "trie-db 0.29.1", @@ -13413,7 +13313,7 @@ name = "shc-indexer-service" version = "0.1.0" dependencies = [ "anyhow", - "array-bytes 6.2.3", + "array-bytes", "async-trait", "cumulus-primitives-core", "cumulus-primitives-storage-weight-reclaim", @@ -13450,7 +13350,7 @@ dependencies = [ "shc-indexer-db", "sp-api 26.0.0", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "storage-hub-runtime", "substrate-frame-rpc-system", @@ -13462,17 +13362,17 @@ dependencies = [ name = "shc-rpc" version = "0.1.0" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "jsonrpsee", "log", "serde", "shc-common", "shc-file-manager", "shc-forest-manager", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "tokio", ] @@ -13486,7 +13386,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" name = "shp-constants" version = "0.1.0" dependencies = [ - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] @@ -13503,11 +13403,11 @@ dependencies = [ "serde", "shp-file-metadata", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "trie-db 0.29.1", ] @@ -13521,8 +13421,8 @@ dependencies = [ "serde", "shp-traits", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -13535,11 +13435,11 @@ dependencies = [ "scale-info", "serde", "shp-traits", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "trie-db 0.29.1", ] @@ -13550,11 +13450,11 @@ dependencies = [ "async-trait", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -13565,11 +13465,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", ] [[package]] @@ -13649,13 +13549,13 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "slot-range-helper" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "enumn", "parity-scale-codec", "paste", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -13856,21 +13756,21 @@ dependencies = [ [[package]] name = "sp-api" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hash-db", "log", "parity-scale-codec", "scale-info", "sp-api-proc-macro 15.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-metadata-ir 0.6.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-metadata-ir 0.6.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "sp-version 29.0.0", "thiserror", ] @@ -13900,15 +13800,15 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "15.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "Inflector", "blake2 0.10.6", - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -13919,24 +13819,24 @@ checksum = "0301e2f77afb450fbf2b093f8b324c7ad88cc82e5e69bd5dc8658a1f068b2a96" dependencies = [ "Inflector", "blake2 0.10.6", - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-application-crypto" version = "30.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -13956,7 +13856,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "integer-sqrt", @@ -13964,7 +13864,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "static_assertions", ] @@ -14004,7 +13904,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", @@ -14030,7 +13930,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "sp-api 26.0.0", "sp-inherents 26.0.0", @@ -14040,7 +13940,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "futures", "log", @@ -14058,12 +13958,12 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "futures", "log", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", @@ -14073,7 +13973,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "parity-scale-codec", @@ -14089,7 +13989,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "parity-scale-codec", @@ -14098,7 +13998,7 @@ dependencies = [ "sp-api 26.0.0", "sp-application-crypto 30.0.0", "sp-consensus-slots 0.32.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", "sp-timestamp 26.0.0", @@ -14127,7 +14027,7 @@ dependencies = [ [[package]] name = "sp-consensus-beefy" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "lazy_static", "parity-scale-codec", @@ -14135,8 +14035,8 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-io 30.0.0", "sp-keystore 0.34.0", "sp-mmr-primitives", @@ -14147,7 +14047,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "finality-grandpa", "log", @@ -14156,7 +14056,7 @@ dependencies = [ "serde", "sp-api 26.0.0", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", ] @@ -14164,7 +14064,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", @@ -14188,57 +14088,10 @@ dependencies = [ [[package]] name = "sp-core" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", - "bandersnatch_vrfs 0.0.4 (git+https://github.com/w3f/ring-vrf?rev=e9782f9)", - "bitflags 1.3.2", - "blake2 0.10.6", - "bounded-collections", - "bs58 0.5.1", - "dyn-clonable", - "ed25519-zebra 3.1.0", - "futures", - "hash-db", - "hash256-std-hasher", - "impl-serde", - "itertools 0.10.5", - "k256", - "libsecp256k1", - "log", - "merlin", - "parity-bip39", - "parity-scale-codec", - "parking_lot 0.12.3", - "paste", - "primitive-types", - "rand 0.8.5", - "scale-info", - "schnorrkel 0.11.4", - "secp256k1", - "secrecy", - "serde", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "ss58-registry", - "substrate-bip39 0.4.7 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "thiserror", - "tracing", - "w3f-bls", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" -dependencies = [ - "array-bytes 6.2.3", - "bandersnatch_vrfs 0.0.4 (git+https://github.com/w3f/ring-vrf?rev=0fef826)", + "array-bytes", + "bandersnatch_vrfs", "bitflags 1.3.2", "blake2 0.10.6", "bounded-collections", @@ -14265,14 +14118,14 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "ss58-registry", - "substrate-bip39 0.4.7 (git+https://github.com/paritytech/polkadot-sdk.git)", + "substrate-bip39 0.4.7", "thiserror", "tracing", "w3f-bls", @@ -14285,7 +14138,7 @@ version = "29.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c33c7a1568175250628567d50c4e1c54a6ac5bc1190413b9be29a9e810cbe73" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "bip39", "bitflags 1.3.2", "blake2 0.10.6", @@ -14328,7 +14181,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -14342,7 +14195,7 @@ dependencies = [ "ark-ed-on-bls12-381-bandersnatch", "ark-ed-on-bls12-381-bandersnatch-ext", "ark-scale", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -14362,20 +14215,7 @@ dependencies = [ [[package]] name = "sp-crypto-hashing" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.8", - "sha3", - "twox-hash", -] - -[[package]] -name = "sp-crypto-hashing" -version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "blake2b_simd", "byteorder", @@ -14393,23 +14233,23 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "syn 2.0.75", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "syn 2.0.79", ] [[package]] name = "sp-database" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "kvdb", "parking_lot 0.12.3", @@ -14423,47 +14263,47 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -14493,7 +14333,7 @@ dependencies = [ [[package]] name = "sp-genesis-builder" version = "0.8.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", @@ -14505,7 +14345,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -14533,7 +14373,7 @@ dependencies = [ [[package]] name = "sp-io" version = "30.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bytes", "ed25519-dalek 2.1.1", @@ -14543,15 +14383,15 @@ dependencies = [ "polkavm-derive", "rustversion", "secp256k1", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-keystore 0.34.0", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-state-machine 0.35.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "tracing", "tracing-core", ] @@ -14585,9 +14425,9 @@ dependencies = [ [[package]] name = "sp-keyring" version = "31.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "strum 0.26.3", ] @@ -14595,12 +14435,12 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -14619,7 +14459,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "thiserror", "zstd 0.12.4", @@ -14640,7 +14480,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.6.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -14650,7 +14490,7 @@ dependencies = [ [[package]] name = "sp-mixnet" version = "0.4.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", @@ -14661,16 +14501,16 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "ckb-merkle-mountain-range", "log", "parity-scale-codec", + "polkadot-ckb-merkle-mountain-range", "scale-info", "serde", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", "thiserror", ] @@ -14678,13 +14518,13 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", "serde", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] @@ -14706,10 +14546,10 @@ dependencies = [ [[package]] name = "sp-offchain" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] @@ -14727,7 +14567,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "backtrace", "lazy_static", @@ -14737,23 +14577,24 @@ dependencies = [ [[package]] name = "sp-rpc" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "rustc-hash", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", ] [[package]] name = "sp-runtime" version = "31.0.1" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "docify", "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", + "num-traits", "parity-scale-codec", "paste", "rand 0.8.5", @@ -14762,9 +14603,9 @@ dependencies = [ "simple-mermaid", "sp-application-crypto 30.0.0", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", ] @@ -14796,38 +14637,38 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "polkavm-derive", "primitive-types", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-runtime-interface-proc-macro 17.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-runtime-interface-proc-macro 17.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "static_assertions", ] [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "polkavm-derive", "primitive-types", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-runtime-interface-proc-macro 17.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-runtime-interface-proc-macro 17.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk)", "static_assertions", ] @@ -14857,48 +14698,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfaf6e85b2ec12a4b99cd6d8d57d083e30c94b7f1b0d8f93547121495aae6f0c" dependencies = [ "Inflector", - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "Inflector", - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "Inflector", - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-session" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "scale-info", "sp-api 26.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-staking 26.0.0", @@ -14923,13 +14764,13 @@ dependencies = [ [[package]] name = "sp-staking" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] @@ -14951,7 +14792,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.35.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hash-db", "log", @@ -14959,10 +14800,10 @@ dependencies = [ "parking_lot 0.12.3", "rand 0.8.5", "smallvec", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-panic-handler 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-trie 29.0.0", "thiserror", "tracing", "trie-db 0.29.1", @@ -14993,7 +14834,7 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "aes-gcm", "curve25519-dalek 4.1.3", @@ -15005,11 +14846,11 @@ dependencies = [ "sha2 0.10.8", "sp-api 26.0.0", "sp-application-crypto 30.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", - "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "x25519-dalek 2.0.1", ] @@ -15023,35 +14864,35 @@ checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -15071,7 +14912,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "parity-scale-codec", @@ -15110,7 +14951,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "tracing", @@ -15121,7 +14962,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ "parity-scale-codec", "tracing", @@ -15132,7 +14973,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "sp-api 26.0.0", "sp-runtime 31.0.1", @@ -15141,44 +14982,21 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "async-trait", "parity-scale-codec", "scale-info", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-runtime 31.0.1", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", -] - -[[package]] -name = "sp-trie" -version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" -dependencies = [ - "ahash 0.8.11", - "hash-db", - "lazy_static", - "memory-db", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.3", - "rand 0.8.5", - "scale-info", - "schnellru", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "thiserror", - "tracing", - "trie-db 0.29.1", - "trie-root", + "sp-trie 29.0.0", ] [[package]] name = "sp-trie" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "ahash 0.8.11", "hash-db", @@ -15190,8 +15008,8 @@ dependencies = [ "rand 0.8.5", "scale-info", "schnellru", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", - "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-core 28.0.0", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", "tracing", "trie-db 0.29.1", @@ -15226,17 +15044,17 @@ dependencies = [ [[package]] name = "sp-version" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "impl-serde", "parity-scale-codec", "parity-wasm", "scale-info", "serde", - "sp-crypto-hashing-proc-macro 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-crypto-hashing-proc-macro 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-version-proc-macro 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-version-proc-macro 13.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "thiserror", ] @@ -15267,18 +15085,18 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "sp-version-proc-macro" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -15298,7 +15116,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -15310,8 +15128,9 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk#dada6cea6447ce2730a3f3b43a3b48b7a5c26cf6" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -15320,7 +15139,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "bounded-collections", "parity-scale-codec", @@ -15328,7 +15147,7 @@ dependencies = [ "serde", "smallvec", "sp-arithmetic 23.0.0", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] @@ -15380,9 +15199,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.47.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" +checksum = "43fce22ed1df64d04b262351c8f9d5c6da4f76f79f25ad15529792f893fad25d" dependencies = [ "Inflector", "num-format", @@ -15402,7 +15221,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "staging-parachain-info" version = "0.7.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "cumulus-primitives-core", "frame-support 28.0.0", @@ -15410,15 +15229,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", ] [[package]] name = "staging-xcm" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "bounded-collections", "derivative", "environmental", @@ -15437,7 +15256,7 @@ version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fa328b87de3466bc38cc9a07244c42c647b7755b81115e1dfeb47cc13fc6e6" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "bounded-collections", "derivative", "environmental", @@ -15453,7 +15272,7 @@ dependencies = [ [[package]] name = "staging-xcm-builder" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", @@ -15466,7 +15285,7 @@ dependencies = [ "sp-arithmetic 23.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", "staging-xcm 7.0.0", "staging-xcm-executor 7.0.0", @@ -15498,7 +15317,7 @@ dependencies = [ [[package]] name = "staging-xcm-executor" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "environmental", "frame-benchmarking 28.0.0", @@ -15508,10 +15327,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic 23.0.0", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", "staging-xcm 7.0.0", ] @@ -15577,7 +15396,7 @@ name = "storage-hub-node" version = "0.1.0" dependencies = [ "anyhow", - "array-bytes 6.2.3", + "array-bytes", "async-channel 1.9.0", "async-io 2.3.4", "async-trait", @@ -15658,14 +15477,14 @@ dependencies = [ "sp-block-builder", "sp-blockchain", "sp-consensus-aura", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-keyring", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-timestamp 26.0.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "staging-xcm 7.0.0", "storage-hub-runtime", "substrate-build-script-utils", @@ -15737,21 +15556,23 @@ dependencies = [ "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-offchain", "sp-runtime 31.0.1", "sp-session 27.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git)", + "sp-trie 29.0.0", "sp-version 29.0.0", + "sp-weights 27.0.0", "staging-parachain-info", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", "substrate-wasm-builder", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -15762,7 +15583,7 @@ checksum = "6706347e49b13373f7ddfafad47df7583ed52083d6fc8a594eb2c80497ef959d" dependencies = [ "combine", "crc", - "fastrand 2.1.0", + "fastrand 2.1.1", "hmac 0.12.1", "once_cell", "openssl", @@ -15845,7 +15666,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -15864,19 +15685,7 @@ dependencies = [ [[package]] name = "substrate-bip39" version = "0.4.7" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" -dependencies = [ - "hmac 0.12.1", - "pbkdf2 0.12.2", - "schnorrkel 0.11.4", - "sha2 0.10.8", - "zeroize", -] - -[[package]] -name = "substrate-bip39" -version = "0.4.7" -source = "git+https://github.com/paritytech/polkadot-sdk.git#b2ec017c0e5e49f3cbf782a5255bb0f9e88bd6c1" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hmac 0.12.1", "pbkdf2 0.12.2", @@ -15888,12 +15697,12 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" [[package]] name = "substrate-frame-rpc-system" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -15905,14 +15714,14 @@ dependencies = [ "sp-api 26.0.0", "sp-block-builder", "sp-blockchain", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", ] [[package]] name = "substrate-prometheus-endpoint" version = "0.17.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "hyper", "log", @@ -15924,26 +15733,26 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "jsonrpsee", "parity-scale-codec", "sc-client-api", "sc-rpc-api", "serde", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", - "sp-trie 29.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-trie 29.0.0", "trie-db 0.29.1", ] [[package]] name = "substrate-wasm-builder" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "array-bytes 6.2.3", + "array-bytes", "build-helper", "cargo_metadata", "console", @@ -15954,10 +15763,10 @@ dependencies = [ "parity-wasm", "polkavm-linker", "sc-executor", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-io 30.0.0", "sp-maybe-compressed-blob", - "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-version 29.0.0", "strum 0.26.3", "tempfile", @@ -15997,9 +15806,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -16026,7 +15835,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -16064,14 +15873,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand 2.1.1", "once_cell", - "rustix 0.38.34", + "rustix 0.38.37", "windows-sys 0.59.0", ] @@ -16086,12 +15895,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ - "rustix 0.38.34", - "windows-sys 0.48.0", + "rustix 0.38.37", + "windows-sys 0.59.0", ] [[package]] @@ -16102,9 +15911,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] @@ -16126,18 +15935,18 @@ checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -16256,9 +16065,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -16280,7 +16089,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -16332,9 +16141,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite 0.2.14", @@ -16359,9 +16168,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -16389,7 +16198,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.20", + "toml_edit 0.22.22", ] [[package]] @@ -16407,33 +16216,22 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.6.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.4.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" -dependencies = [ - "indexmap 2.4.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow 0.6.20", ] [[package]] @@ -16501,7 +16299,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -16527,7 +16325,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "coarsetime", "polkadot-primitives 7.0.0", @@ -16538,13 +16336,13 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ - "expander 2.2.1", - "proc-macro-crate 3.1.0", + "expander", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -16812,9 +16610,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -16830,15 +16628,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -16851,21 +16649,21 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -17047,7 +16845,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -17081,7 +16879,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -17433,7 +17231,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "binary-merkle-tree", "bitvec", @@ -17458,13 +17256,14 @@ dependencies = [ "pallet-beefy-mmr", "pallet-collective", "pallet-conviction-voting", + "pallet-delegated-staking", "pallet-democracy", "pallet-election-provider-multi-phase 27.0.0", "pallet-election-provider-support-benchmarking 27.0.0", "pallet-elections-phragmen", "pallet-fast-unstake 27.0.0", "pallet-grandpa", - "pallet-identity 28.0.0", + "pallet-identity 29.0.0", "pallet-indices", "pallet-membership", "pallet-message-queue 31.0.0", @@ -17515,8 +17314,8 @@ dependencies = [ "sp-block-builder", "sp-consensus-babe 0.32.0", "sp-consensus-beefy", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", + "sp-genesis-builder 0.8.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-inherents 26.0.0", "sp-io 30.0.0", "sp-mmr-primitives", @@ -17525,8 +17324,8 @@ dependencies = [ "sp-runtime 31.0.1", "sp-session 27.0.0", "sp-staking 26.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", - "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-transaction-pool", "sp-version 29.0.0", "staging-xcm 7.0.0", @@ -17540,13 +17339,13 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "polkadot-primitives 7.0.0", "polkadot-runtime-common 7.0.0", "smallvec", - "sp-core 28.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-core 28.0.0", "sp-runtime 31.0.1", "sp-weights 27.0.0", "staging-xcm 7.0.0", @@ -17562,7 +17361,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.34", + "rustix 0.38.37", ] [[package]] @@ -17571,7 +17370,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.3", + "redox_syscall 0.5.7", "wasite", "web-sys", ] @@ -17891,9 +17690,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -17978,14 +17777,14 @@ dependencies = [ [[package]] name = "xcm-fee-payment-runtime-api" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", "parity-scale-codec", "scale-info", "sp-api 26.0.0", "sp-runtime 31.0.1", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "sp-weights 27.0.0", "staging-xcm 7.0.0", ] @@ -17993,12 +17792,12 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -18010,22 +17809,26 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] name = "xcm-simulator" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0#8c8edacf8942298c3807a2e192860da9e7e4996a" +source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0#d5160c1d567cc73c7df6c816d41e21aa3adb188d" dependencies = [ "frame-support 28.0.0", + "frame-system 28.0.0", "parity-scale-codec", "paste", "polkadot-core-primitives 7.0.0", "polkadot-parachain-primitives 6.0.0", + "polkadot-primitives 7.0.0", "polkadot-runtime-parachains 7.0.0", + "scale-info", "sp-io 30.0.0", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?branch=release-polkadot-v1.11.0)", + "sp-runtime 31.0.1", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.13.0)", "staging-xcm 7.0.0", "staging-xcm-builder 7.0.0", "staging-xcm-executor 7.0.0", @@ -18072,7 +17875,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] @@ -18092,7 +17895,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d5db04562..815462fd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,241 +1,238 @@ -[workspace.package] -authors = ["Moonsong Labs"] -edition = "2021" -repository = "https://github.com/Moonsong-Labs/storage-hub-runtime.git" -license = "GPL-3.0-only" -homepage = "https://moonsonglabs.com/" - -[profile.release] -panic = "unwind" - -[workspace] -members = [ - "runtime", - "pallets/*", - "node", - "client/*", - "primitives/*", - "xcm-simulator", -] -resolver = "2" - -[workspace.dependencies] -anyhow = "1.0.81" -array-bytes = "6.1" -async-channel = "1.8.0" -async-io = "2.3.2" -async-trait = "0.1.42" -bincode = "1.3.3" -clap = { version = "4.5.3", features = ["derive"] } -chrono = "0.4" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ - "derive", -], default-features = false } -color-print = "0.3.4" -futures-timer = "3.0.1" -hash-db = "0.16.0" -hex = "0.4.3" -hex-literal = { version = "0.4.1" } -jsonrpsee = { version = "0.22", features = ["server"] } -kvdb = "0.13.0" -kvdb-memorydb = "0.13.0" -kvdb-rocksdb = "0.19.0" -rocksdb = "0.21.0" -futures = "0.3.30" -lazy-static = { package = "lazy_static", version = "1.4.0" } -libp2p-identity = "0.1.3" -log = { version = "0.4.21", default-features = false } -num-bigint = { version = "0.4.3", default-features = false } -parking_lot = "0.12.1" -prost = "0.12" -prost-build = "0.12.3" -rand = "0.8.5" -reference-trie = "0.29.1" -scale-info = { version = "2.11.0", default-features = false, features = [ - "derive", -] } -thiserror = "1.0.48" -tokio = "1.36.0" -trie-db = { version = "0.29.1", default-features = false } -serde = { version = "1.0.210", default-features = false } -serde_json = "1.0.108" -smallvec = "1.11.0" -diesel = { version = "2.2.4", features = ["postgres", "chrono", "numeric"] } -diesel-async = { version = "0.5.0", features = ["bb8", "postgres"] } -bigdecimal = { version = "0.4.5", features = ["serde"] } - -# Substrate -sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -# latest version containing trie-db upgrade (double ended iterator) -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk.git", version = "29.0.0", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-genesis-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-keyring = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-keystore = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sp-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-session = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-state-machine = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.11.0", default-features = false } -sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sp-version = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-consensus-manual-seal = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-executor = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-network = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-network-sync = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-service = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-sysinfo = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-tracing = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sc-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -sp-weights = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -substrate-prometheus-endpoint = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-executive = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-metadata-hash-extension = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -frame-try-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-message-queue = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-nfts = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-parameters = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-session = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-uniques = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } - -# Polkadot -polkadot-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", features = [ - "rococo-native", -] } -polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.11.0", default-features = false } -polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -polkadot-parachain-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.11.0", default-features = false } -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -xcm-simulator = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.11.0", default-features = false } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -runtime-constants = { package = "polkadot-runtime-constants", git = "https://github.com/polkadot-fellows/runtimes.git", tag = "v1.2.3", default-features = false } - -# Cumulus -cumulus-client-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-collator = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-consensus-common = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-consensus-proposer = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-parachain-inherent = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-client-service = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-relay-chain-interface = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0" } -cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false, features = [ - "parameterized-consensus-hook", -] } -cumulus-pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-primitives-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-primitives-core = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-primitives-utility = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -pallet-collator-selection = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -cumulus-primitives-storage-weight-reclaim = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -parachains-common = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } -parachain-info = { package = "staging-parachain-info", git = "https://github.com/paritytech/polkadot-sdk.git", branch = "release-polkadot-v1.11.0", default-features = false } - -# Local Pallets -pallet-bucket-nfts = { path = "pallets/bucket-nfts", default-features = false } -pallet-file-system = { path = "pallets/file-system", default-features = false } -pallet-file-system-runtime-api = { path = "pallets/file-system/runtime-api", default-features = false } -pallet-payment-streams = { path = "pallets/payment-streams", default-features = false } -pallet-payment-streams-runtime-api = { path = "pallets/payment-streams/runtime-api", default-features = false } -pallet-proofs-dealer = { path = "pallets/proofs-dealer", default-features = false } -pallet-proofs-dealer-runtime-api = { path = "pallets/proofs-dealer/runtime-api", default-features = false } -pallet-randomness = { path = "pallets/randomness", default-features = false } -pallet-storage-providers = { path = "pallets/providers", default-features = false } -pallet-storage-providers-runtime-api = { path = "pallets/providers/runtime-api", default-features = false } - -# Local - StorageHub Client (used by the node, can be std or no_std) -shc-actors-framework = { path = "client/actors-framework", default-features = false } -shc-blockchain-service = { path = "client/blockchain-service", default-features = false } -shc-file-transfer-service = { path = "client/file-transfer-service", default-features = false } -shc-indexer-service = { path = "client/indexer-service", default-features = false } -shc-indexer-db = { path = "client/indexer-db", default-features = false } -shc-common = { path = "client/common", default-features = false } -shc-file-manager = { path = "client/file-manager", default-features = false } -shc-forest-manager = { path = "client/forest-manager", default-features = false } -shc-rpc = { path = "client/rpc", default-features = false } - -# Local - StorageHub Primitives (used by the runtime and the node, must be no_std compatible) -shp-constants = { path = "primitives/constants", default-features = false } -shp-file-key-verifier = { path = "primitives/file-key-verifier", default-features = false } -shp-file-metadata = { path = "primitives/file-metadata", default-features = false } -shp-forest-verifier = { path = "primitives/forest-verifier", default-features = false } -shp-session-keys = { path = "primitives/session-keys", default-features = false } -shp-traits = { path = "primitives/traits", default-features = false } - -# Lcoal - StorageHub Runtime -storage-hub-runtime = { path = "runtime", default-features = false } - -[workspace.lints.rust] -suspicious_double_ref_op = { level = "allow", priority = 2 } - -[workspace.lints.clippy] -all = { level = "allow", priority = 0 } -correctness = { level = "warn", priority = 1 } -complexity = { level = "warn", priority = 1 } -if-same-then-else = { level = "allow", priority = 2 } -zero-prefixed-literal = { level = "allow", priority = 2 } # 00_1000_000 -type_complexity = { level = "allow", priority = 2 } # raison d'etre -nonminimal-bool = { level = "allow", priority = 2 } # maybe -borrowed-box = { level = "allow", priority = 2 } # Reasonable to fix this one -too-many-arguments = { level = "allow", priority = 2 } # (Turning this on would lead to) -needless-lifetimes = { level = "allow", priority = 2 } # generated code -unnecessary_cast = { level = "allow", priority = 2 } # Types may change -identity-op = { level = "allow", priority = 2 } # One case where we do 0 + -useless_conversion = { level = "allow", priority = 2 } # Types may change -unit_arg = { level = "allow", priority = 2 } # stylistic -option-map-unit-fn = { level = "allow", priority = 2 } # stylistic -bind_instead_of_map = { level = "allow", priority = 2 } # stylistic -erasing_op = { level = "allow", priority = 2 } # E.g. 0 * DOLLARS -eq_op = { level = "allow", priority = 2 } # In tests we test equality. -while_immutable_condition = { level = "allow", priority = 2 } # false positives -needless_option_as_deref = { level = "allow", priority = 2 } # false positives -derivable_impls = { level = "allow", priority = 2 } # false positives -stable_sort_primitive = { level = "allow", priority = 2 } # prefer stable sort -extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic -default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic +[workspace.package] +authors = ["Moonsong Labs"] +edition = "2021" +repository = "https://github.com/Moonsong-Labs/storage-hub-runtime.git" +license = "GPL-3.0-only" +homepage = "https://moonsonglabs.com/" + +[profile.release] +panic = "unwind" + +[workspace] +members = [ + "runtime", + "pallets/*", + "node", + "client/*", + "primitives/*", + "xcm-simulator", +] +resolver = "2" + +[workspace.dependencies] +anyhow = "1.0.81" +array-bytes = "6.1" +async-channel = "1.8.0" +async-io = "2.3.2" +async-trait = "0.1.42" +bincode = "1.3.3" +clap = { version = "4.5.3", features = ["derive"] } +chrono = "0.4" +codec = { package = "parity-scale-codec", version = "3.0.0", features = [ + "derive", +], default-features = false } +color-print = "0.3.4" +futures-timer = "3.0.1" +hash-db = "0.16.0" +hex = "0.4.3" +hex-literal = { version = "0.4.1" } +jsonrpsee = { version = "0.22", features = ["server"] } +kvdb = "0.13.0" +kvdb-memorydb = "0.13.0" +kvdb-rocksdb = "0.19.0" +rocksdb = "0.21.0" +futures = "0.3.30" +lazy-static = { package = "lazy_static", version = "1.4.0" } +log = { version = "0.4.21", default-features = false } +num-bigint = { version = "0.4.3", default-features = false } +parking_lot = "0.12.1" +prost = "0.12" +prost-build = "0.12.3" +rand = "0.8.5" +reference-trie = "0.29.1" +scale-info = { version = "2.11.0", default-features = false, features = [ + "derive", +] } +thiserror = "1.0.48" +tokio = "1.36.0" +trie-db = { version = "0.29.1", default-features = false } +serde = { version = "1.0.210", default-features = false } +serde_json = "1.0.108" +smallvec = "1.11.0" +diesel = { version = "2.2.4", features = ["postgres", "chrono", "numeric"] } +diesel-async = { version = "0.5.0", features = ["bb8", "postgres"] } +bigdecimal = { version = "0.4.5", features = ["serde"] } + +# Substrate +sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-trie = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-genesis-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-keyring = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-keystore = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-state-machine = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.13.0", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-version = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-consensus-manual-seal = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-executor = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-network = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-network-sync = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-network-types = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-service = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-sysinfo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-tracing = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sc-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +sp-weights = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-executive = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-metadata-hash-extension = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-system-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +frame-try-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-message-queue = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-nfts = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-parameters = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-session = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-uniques = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } + +# Polkadot +polkadot-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", features = [ + "rococo-native", +], default-features = false } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.13.0", default-features = false } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +polkadot-parachain-primitives = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.13.0", default-features = false } +xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +xcm-simulator = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.13.0", default-features = false } +xcm-fee-payment-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.13.0", default-features = false } +xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +runtime-constants = { package = "polkadot-runtime-constants", git = "https://github.com/polkadot-fellows/runtimes.git", tag = "v1.2.3", default-features = false } + +# Cumulus +cumulus-client-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-collator = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-consensus-common = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-consensus-proposer = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-parachain-inherent = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-client-service = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-pallet-session-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-primitives-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-primitives-utility = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +pallet-collator-selection = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +cumulus-primitives-storage-weight-reclaim = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +parachains-common = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } +parachain-info = { package = "staging-parachain-info", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.13.0", default-features = false } + +# Local Pallets +pallet-bucket-nfts = { path = "pallets/bucket-nfts", default-features = false } +pallet-file-system = { path = "pallets/file-system", default-features = false } +pallet-file-system-runtime-api = { path = "pallets/file-system/runtime-api", default-features = false } +pallet-payment-streams = { path = "pallets/payment-streams", default-features = false } +pallet-payment-streams-runtime-api = { path = "pallets/payment-streams/runtime-api", default-features = false } +pallet-proofs-dealer = { path = "pallets/proofs-dealer", default-features = false } +pallet-proofs-dealer-runtime-api = { path = "pallets/proofs-dealer/runtime-api", default-features = false } +pallet-randomness = { path = "pallets/randomness", default-features = false } +pallet-storage-providers = { path = "pallets/providers", default-features = false } +pallet-storage-providers-runtime-api = { path = "pallets/providers/runtime-api", default-features = false } + +# Local - StorageHub Client (used by the node, can be std or no_std) +shc-actors-framework = { path = "client/actors-framework", default-features = false } +shc-blockchain-service = { path = "client/blockchain-service", default-features = false } +shc-file-transfer-service = { path = "client/file-transfer-service", default-features = false } +shc-indexer-service = { path = "client/indexer-service", default-features = false } +shc-indexer-db = { path = "client/indexer-db", default-features = false } +shc-common = { path = "client/common", default-features = false } +shc-file-manager = { path = "client/file-manager", default-features = false } +shc-forest-manager = { path = "client/forest-manager", default-features = false } +shc-rpc = { path = "client/rpc", default-features = false } + +# Local - StorageHub Primitives (used by the runtime and the node, must be no_std compatible) +shp-constants = { path = "primitives/constants", default-features = false } +shp-file-key-verifier = { path = "primitives/file-key-verifier", default-features = false } +shp-file-metadata = { path = "primitives/file-metadata", default-features = false } +shp-forest-verifier = { path = "primitives/forest-verifier", default-features = false } +shp-session-keys = { path = "primitives/session-keys", default-features = false } +shp-traits = { path = "primitives/traits", default-features = false } + +# Lcoal - StorageHub Runtime +storage-hub-runtime = { path = "runtime", default-features = false } + +[workspace.lints.rust] +suspicious_double_ref_op = { level = "allow", priority = 2 } + +[workspace.lints.clippy] +all = { level = "allow", priority = 0 } +correctness = { level = "warn", priority = 1 } +complexity = { level = "warn", priority = 1 } +if-same-then-else = { level = "allow", priority = 2 } +zero-prefixed-literal = { level = "allow", priority = 2 } # 00_1000_000 +type_complexity = { level = "allow", priority = 2 } # raison d'etre +nonminimal-bool = { level = "allow", priority = 2 } # maybe +borrowed-box = { level = "allow", priority = 2 } # Reasonable to fix this one +too-many-arguments = { level = "allow", priority = 2 } # (Turning this on would lead to) +needless-lifetimes = { level = "allow", priority = 2 } # generated code +unnecessary_cast = { level = "allow", priority = 2 } # Types may change +identity-op = { level = "allow", priority = 2 } # One case where we do 0 + +useless_conversion = { level = "allow", priority = 2 } # Types may change +unit_arg = { level = "allow", priority = 2 } # stylistic +option-map-unit-fn = { level = "allow", priority = 2 } # stylistic +bind_instead_of_map = { level = "allow", priority = 2 } # stylistic +erasing_op = { level = "allow", priority = 2 } # E.g. 0 * DOLLARS +eq_op = { level = "allow", priority = 2 } # In tests we test equality. +while_immutable_condition = { level = "allow", priority = 2 } # false positives +needless_option_as_deref = { level = "allow", priority = 2 } # false positives +derivable_impls = { level = "allow", priority = 2 } # false positives +stable_sort_primitive = { level = "allow", priority = 2 } # prefer stable sort +extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic +default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic diff --git a/api-augment/dist/interfaces/lookup.js b/api-augment/dist/interfaces/lookup.js index f4a577d12..b85467c40 100644 --- a/api-augment/dist/interfaces/lookup.js +++ b/api-augment/dist/interfaces/lookup.js @@ -733,7 +733,7 @@ export default { xcm: "StagingXcmV4Xcm" }, Transact: { - originKind: "XcmV2OriginKind", + originKind: "XcmV3OriginKind", requireWeightAtMost: "SpWeightsWeightV2Weight", call: "XcmDoubleEncoded" }, @@ -923,9 +923,9 @@ export default { } }, /** - * Lookup86: xcm::v2::OriginKind + * Lookup86: xcm::v3::OriginKind **/ - XcmV2OriginKind: { + XcmV3OriginKind: { _enum: ["Native", "SovereignAccount", "Superuser", "Xcm"] }, /** @@ -1330,7 +1330,8 @@ export default { Corrupt: "Null", Unsupported: "Null", Overweight: "SpWeightsWeightV2Weight", - Yield: "Null" + Yield: "Null", + StackLimitReached: "Null" } }, /** @@ -2531,6 +2532,10 @@ export default { force_adjust_total_issuance: { direction: "PalletBalancesAdjustmentDirection", delta: "Compact" + }, + burn: { + value: "Compact", + keepAlive: "bool" } } }, @@ -2913,7 +2918,13 @@ export default { } }, /** - * Lookup294: xcm::v2::multiasset::MultiAssetFilter + * Lookup294: xcm::v2::OriginKind + **/ + XcmV2OriginKind: { + _enum: ["Native", "SovereignAccount", "Superuser", "Xcm"] + }, + /** + * Lookup295: xcm::v2::multiasset::MultiAssetFilter **/ XcmV2MultiassetMultiAssetFilter: { _enum: { @@ -2922,7 +2933,7 @@ export default { } }, /** - * Lookup295: xcm::v2::multiasset::WildMultiAsset + * Lookup296: xcm::v2::multiasset::WildMultiAsset **/ XcmV2MultiassetWildMultiAsset: { _enum: { @@ -2934,13 +2945,13 @@ export default { } }, /** - * Lookup296: xcm::v2::multiasset::WildFungibility + * Lookup297: xcm::v2::multiasset::WildFungibility **/ XcmV2MultiassetWildFungibility: { _enum: ["Fungible", "NonFungible"] }, /** - * Lookup297: xcm::v2::WeightLimit + * Lookup298: xcm::v2::WeightLimit **/ XcmV2WeightLimit: { _enum: { @@ -2949,11 +2960,11 @@ export default { } }, /** - * Lookup298: xcm::v3::Xcm + * Lookup299: xcm::v3::Xcm **/ XcmV3Xcm: "Vec", /** - * Lookup300: xcm::v3::Instruction + * Lookup301: xcm::v3::Instruction **/ XcmV3Instruction: { _enum: { @@ -2976,7 +2987,7 @@ export default { xcm: "XcmV3Xcm" }, Transact: { - originKind: "XcmV2OriginKind", + originKind: "XcmV3OriginKind", requireWeightAtMost: "SpWeightsWeightV2Weight", call: "XcmDoubleEncoded" }, @@ -3095,7 +3106,7 @@ export default { } }, /** - * Lookup301: xcm::v3::Response + * Lookup302: xcm::v3::Response **/ XcmV3Response: { _enum: { @@ -3108,7 +3119,7 @@ export default { } }, /** - * Lookup303: xcm::v3::PalletInfo + * Lookup304: xcm::v3::PalletInfo **/ XcmV3PalletInfo: { index: "Compact", @@ -3119,7 +3130,7 @@ export default { patch: "Compact" }, /** - * Lookup307: xcm::v3::QueryResponseInfo + * Lookup308: xcm::v3::QueryResponseInfo **/ XcmV3QueryResponseInfo: { destination: "StagingXcmV3MultiLocation", @@ -3127,7 +3138,7 @@ export default { maxWeight: "SpWeightsWeightV2Weight" }, /** - * Lookup308: xcm::v3::multiasset::MultiAssetFilter + * Lookup309: xcm::v3::multiasset::MultiAssetFilter **/ XcmV3MultiassetMultiAssetFilter: { _enum: { @@ -3136,7 +3147,7 @@ export default { } }, /** - * Lookup309: xcm::v3::multiasset::WildMultiAsset + * Lookup310: xcm::v3::multiasset::WildMultiAsset **/ XcmV3MultiassetWildMultiAsset: { _enum: { @@ -3154,13 +3165,13 @@ export default { } }, /** - * Lookup310: xcm::v3::multiasset::WildFungibility + * Lookup311: xcm::v3::multiasset::WildFungibility **/ XcmV3MultiassetWildFungibility: { _enum: ["Fungible", "NonFungible"] }, /** - * Lookup322: staging_xcm_executor::traits::asset_transfer::TransferType + * Lookup323: staging_xcm_executor::traits::asset_transfer::TransferType **/ StagingXcmExecutorAssetTransferTransferType: { _enum: { @@ -3171,7 +3182,7 @@ export default { } }, /** - * Lookup323: xcm::VersionedAssetId + * Lookup324: xcm::VersionedAssetId **/ XcmVersionedAssetId: { _enum: { @@ -3183,11 +3194,11 @@ export default { } }, /** - * Lookup324: cumulus_pallet_xcm::pallet::Call + * Lookup325: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: "Null", /** - * Lookup325: pallet_message_queue::pallet::Call + * Lookup326: pallet_message_queue::pallet::Call **/ PalletMessageQueueCall: { _enum: { @@ -3204,7 +3215,7 @@ export default { } }, /** - * Lookup326: pallet_storage_providers::pallet::Call + * Lookup327: pallet_storage_providers::pallet::Call **/ PalletStorageProvidersCall: { _enum: { @@ -3253,7 +3264,7 @@ export default { } }, /** - * Lookup327: pallet_file_system::pallet::Call + * Lookup328: pallet_file_system::pallet::Call **/ PalletFileSystemCall: { _enum: { @@ -3357,27 +3368,27 @@ export default { } }, /** - * Lookup328: pallet_file_system::types::BucketMoveRequestResponse + * Lookup329: pallet_file_system::types::BucketMoveRequestResponse **/ PalletFileSystemBucketMoveRequestResponse: { _enum: ["Accepted", "Rejected"] }, /** - * Lookup331: pallet_file_system::types::MspStorageRequestResponse + * Lookup332: pallet_file_system::types::MspStorageRequestResponse **/ PalletFileSystemMspStorageRequestResponse: { accept: "Option", reject: "Option>" }, /** - * Lookup333: pallet_file_system::types::AcceptedStorageRequestParameters + * Lookup334: pallet_file_system::types::AcceptedStorageRequestParameters **/ PalletFileSystemAcceptedStorageRequestParameters: { fileKeysAndProofs: "Vec<(H256,ShpFileKeyVerifierFileKeyProof)>", nonInclusionForestProof: "SpTrieStorageProofCompactProof" }, /** - * Lookup340: pallet_proofs_dealer::pallet::Call + * Lookup341: pallet_proofs_dealer::pallet::Call **/ PalletProofsDealerCall: { _enum: { @@ -3397,13 +3408,13 @@ export default { } }, /** - * Lookup341: pallet_randomness::pallet::Call + * Lookup342: pallet_randomness::pallet::Call **/ PalletRandomnessCall: { _enum: ["set_babe_randomness"] }, /** - * Lookup342: pallet_payment_streams::pallet::Call + * Lookup343: pallet_payment_streams::pallet::Call **/ PalletPaymentStreamsCall: { _enum: { @@ -3443,7 +3454,7 @@ export default { } }, /** - * Lookup343: pallet_bucket_nfts::pallet::Call + * Lookup344: pallet_bucket_nfts::pallet::Call **/ PalletBucketNftsCall: { _enum: { @@ -3461,7 +3472,7 @@ export default { } }, /** - * Lookup345: pallet_nfts::pallet::Call + * Lookup346: pallet_nfts::pallet::Call **/ PalletNftsCall: { _enum: { @@ -3658,7 +3669,7 @@ export default { } }, /** - * Lookup346: pallet_nfts::types::CollectionConfig + * Lookup347: pallet_nfts::types::CollectionConfig **/ PalletNftsCollectionConfig: { settings: "u64", @@ -3666,7 +3677,7 @@ export default { mintSettings: "PalletNftsMintSettings" }, /** - * Lookup348: pallet_nfts::types::CollectionSetting + * Lookup349: pallet_nfts::types::CollectionSetting **/ PalletNftsCollectionSetting: { _enum: [ @@ -3690,7 +3701,7 @@ export default { ] }, /** - * Lookup349: pallet_nfts::types::MintSettings + * Lookup350: pallet_nfts::types::MintSettings **/ PalletNftsMintSettings: { mintType: "PalletNftsMintType", @@ -3700,7 +3711,7 @@ export default { defaultItemSettings: "u64" }, /** - * Lookup350: pallet_nfts::types::MintType + * Lookup351: pallet_nfts::types::MintType **/ PalletNftsMintType: { _enum: { @@ -3710,13 +3721,13 @@ export default { } }, /** - * Lookup353: pallet_nfts::types::ItemSetting + * Lookup354: pallet_nfts::types::ItemSetting **/ PalletNftsItemSetting: { _enum: ["__Unused0", "Transferable", "UnlockedMetadata", "__Unused3", "UnlockedAttributes"] }, /** - * Lookup354: pallet_nfts::types::DestroyWitness + * Lookup355: pallet_nfts::types::DestroyWitness **/ PalletNftsDestroyWitness: { itemMetadatas: "Compact", @@ -3724,26 +3735,26 @@ export default { attributes: "Compact" }, /** - * Lookup356: pallet_nfts::types::MintWitness + * Lookup357: pallet_nfts::types::MintWitness **/ PalletNftsMintWitness: { ownedItem: "Option", mintPrice: "Option" }, /** - * Lookup357: pallet_nfts::types::ItemConfig + * Lookup358: pallet_nfts::types::ItemConfig **/ PalletNftsItemConfig: { settings: "u64" }, /** - * Lookup359: pallet_nfts::types::CancelAttributesApprovalWitness + * Lookup360: pallet_nfts::types::CancelAttributesApprovalWitness **/ PalletNftsCancelAttributesApprovalWitness: { accountAttributes: "u32" }, /** - * Lookup361: pallet_nfts::types::ItemTip + * Lookup362: pallet_nfts::types::ItemTip **/ PalletNftsItemTip: { collection: "u32", @@ -3752,7 +3763,7 @@ export default { amount: "u128" }, /** - * Lookup363: pallet_nfts::types::PreSignedMint + * Lookup364: pallet_nfts::types::PreSignedMint **/ PalletNftsPreSignedMint: { collection: "u32", @@ -3764,7 +3775,7 @@ export default { mintPrice: "Option" }, /** - * Lookup364: sp_runtime::MultiSignature + * Lookup365: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3774,7 +3785,7 @@ export default { } }, /** - * Lookup367: pallet_nfts::types::PreSignedAttributes + * Lookup368: pallet_nfts::types::PreSignedAttributes **/ PalletNftsPreSignedAttributes: { collection: "u32", @@ -3784,7 +3795,7 @@ export default { deadline: "u32" }, /** - * Lookup368: pallet_parameters::pallet::Call + * Lookup369: pallet_parameters::pallet::Call **/ PalletParametersCall: { _enum: { @@ -3794,7 +3805,7 @@ export default { } }, /** - * Lookup369: storage_hub_runtime::configs::runtime_params::RuntimeParameters + * Lookup370: storage_hub_runtime::configs::runtime_params::RuntimeParameters **/ StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters: { _enum: { @@ -3802,7 +3813,7 @@ export default { } }, /** - * Lookup370: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters + * Lookup371: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters **/ StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters: { _enum: { @@ -3817,20 +3828,20 @@ export default { } }, /** - * Lookup371: pallet_sudo::pallet::Error + * Lookup372: pallet_sudo::pallet::Error **/ PalletSudoError: { _enum: ["RequireSudo"] }, /** - * Lookup374: pallet_collator_selection::pallet::CandidateInfo + * Lookup375: pallet_collator_selection::pallet::CandidateInfo **/ PalletCollatorSelectionCandidateInfo: { who: "AccountId32", deposit: "u128" }, /** - * Lookup376: pallet_collator_selection::pallet::Error + * Lookup377: pallet_collator_selection::pallet::Error **/ PalletCollatorSelectionError: { _enum: [ @@ -3854,17 +3865,17 @@ export default { ] }, /** - * Lookup380: sp_core::crypto::KeyTypeId + * Lookup381: sp_core::crypto::KeyTypeId **/ SpCoreCryptoKeyTypeId: "[u8;4]", /** - * Lookup381: pallet_session::pallet::Error + * Lookup382: pallet_session::pallet::Error **/ PalletSessionError: { _enum: ["InvalidProof", "NoAssociatedValidatorId", "DuplicatedKey", "NoKeys", "NoAccount"] }, /** - * Lookup390: cumulus_pallet_xcmp_queue::OutboundChannelDetails + * Lookup391: cumulus_pallet_xcmp_queue::OutboundChannelDetails **/ CumulusPalletXcmpQueueOutboundChannelDetails: { recipient: "u32", @@ -3874,13 +3885,13 @@ export default { lastIndex: "u16" }, /** - * Lookup391: cumulus_pallet_xcmp_queue::OutboundState + * Lookup392: cumulus_pallet_xcmp_queue::OutboundState **/ CumulusPalletXcmpQueueOutboundState: { _enum: ["Ok", "Suspended"] }, /** - * Lookup393: cumulus_pallet_xcmp_queue::QueueConfigData + * Lookup396: cumulus_pallet_xcmp_queue::QueueConfigData **/ CumulusPalletXcmpQueueQueueConfigData: { suspendThreshold: "u32", @@ -3888,13 +3899,19 @@ export default { resumeThreshold: "u32" }, /** - * Lookup394: cumulus_pallet_xcmp_queue::pallet::Error + * Lookup397: cumulus_pallet_xcmp_queue::pallet::Error **/ CumulusPalletXcmpQueueError: { - _enum: ["BadQueueConfig", "AlreadySuspended", "AlreadyResumed"] + _enum: [ + "BadQueueConfig", + "AlreadySuspended", + "AlreadyResumed", + "TooManyActiveOutboundChannels", + "TooBig" + ] }, /** - * Lookup395: pallet_xcm::pallet::QueryStatus + * Lookup398: pallet_xcm::pallet::QueryStatus **/ PalletXcmQueryStatus: { _enum: { @@ -3915,7 +3932,7 @@ export default { } }, /** - * Lookup399: xcm::VersionedResponse + * Lookup402: xcm::VersionedResponse **/ XcmVersionedResponse: { _enum: { @@ -3927,7 +3944,7 @@ export default { } }, /** - * Lookup405: pallet_xcm::pallet::VersionMigrationStage + * Lookup408: pallet_xcm::pallet::VersionMigrationStage **/ PalletXcmVersionMigrationStage: { _enum: { @@ -3938,7 +3955,7 @@ export default { } }, /** - * Lookup408: pallet_xcm::pallet::RemoteLockedFungibleRecord + * Lookup411: pallet_xcm::pallet::RemoteLockedFungibleRecord **/ PalletXcmRemoteLockedFungibleRecord: { amount: "u128", @@ -3947,7 +3964,7 @@ export default { consumers: "Vec<(Null,u128)>" }, /** - * Lookup415: pallet_xcm::pallet::Error + * Lookup418: pallet_xcm::pallet::Error **/ PalletXcmError: { _enum: [ @@ -3979,7 +3996,7 @@ export default { ] }, /** - * Lookup416: pallet_message_queue::BookState + * Lookup419: pallet_message_queue::BookState **/ PalletMessageQueueBookState: { _alias: { @@ -3993,14 +4010,14 @@ export default { size_: "u64" }, /** - * Lookup418: pallet_message_queue::Neighbours + * Lookup421: pallet_message_queue::Neighbours **/ PalletMessageQueueNeighbours: { prev: "CumulusPrimitivesCoreAggregateMessageOrigin", next: "CumulusPrimitivesCoreAggregateMessageOrigin" }, /** - * Lookup420: pallet_message_queue::Page + * Lookup423: pallet_message_queue::Page **/ PalletMessageQueuePage: { remaining: "u32", @@ -4011,7 +4028,7 @@ export default { heap: "Bytes" }, /** - * Lookup422: pallet_message_queue::pallet::Error + * Lookup425: pallet_message_queue::pallet::Error **/ PalletMessageQueueError: { _enum: [ @@ -4027,7 +4044,7 @@ export default { ] }, /** - * Lookup424: pallet_storage_providers::types::StorageProvider + * Lookup427: pallet_storage_providers::types::StorageProvider **/ PalletStorageProvidersStorageProvider: { _enum: { @@ -4036,7 +4053,7 @@ export default { } }, /** - * Lookup425: pallet_storage_providers::types::BackupStorageProvider + * Lookup428: pallet_storage_providers::types::BackupStorageProvider **/ PalletStorageProvidersBackupStorageProvider: { capacity: "u64", @@ -4049,7 +4066,7 @@ export default { reputationWeight: "u32" }, /** - * Lookup426: pallet_storage_providers::types::MainStorageProvider + * Lookup429: pallet_storage_providers::types::MainStorageProvider **/ PalletStorageProvidersMainStorageProvider: { buckets: "Vec", @@ -4062,7 +4079,7 @@ export default { paymentAccount: "AccountId32" }, /** - * Lookup428: pallet_storage_providers::types::Bucket + * Lookup431: pallet_storage_providers::types::Bucket **/ PalletStorageProvidersBucket: { _alias: { @@ -4076,7 +4093,7 @@ export default { size_: "u64" }, /** - * Lookup431: pallet_storage_providers::pallet::Error + * Lookup434: pallet_storage_providers::pallet::Error **/ PalletStorageProvidersError: { _enum: [ @@ -4110,7 +4127,7 @@ export default { ] }, /** - * Lookup432: pallet_file_system::types::StorageRequestMetadata + * Lookup435: pallet_file_system::types::StorageRequestMetadata **/ PalletFileSystemStorageRequestMetadata: { _alias: { @@ -4130,19 +4147,19 @@ export default { bspsVolunteered: "u32" }, /** - * Lookup437: pallet_file_system::types::StorageRequestBspsMetadata + * Lookup440: pallet_file_system::types::StorageRequestBspsMetadata **/ PalletFileSystemStorageRequestBspsMetadata: { confirmed: "bool" }, /** - * Lookup446: pallet_file_system::types::MoveBucketRequestMetadata + * Lookup449: pallet_file_system::types::MoveBucketRequestMetadata **/ PalletFileSystemMoveBucketRequestMetadata: { requester: "AccountId32" }, /** - * Lookup447: pallet_file_system::pallet::Error + * Lookup450: pallet_file_system::pallet::Error **/ PalletFileSystemError: { _enum: [ @@ -4210,7 +4227,7 @@ export default { ] }, /** - * Lookup454: pallet_proofs_dealer::pallet::Error + * Lookup457: pallet_proofs_dealer::pallet::Error **/ PalletProofsDealerError: { _enum: [ @@ -4239,7 +4256,7 @@ export default { ] }, /** - * Lookup457: pallet_payment_streams::types::FixedRatePaymentStream + * Lookup460: pallet_payment_streams::types::FixedRatePaymentStream **/ PalletPaymentStreamsFixedRatePaymentStream: { rate: "u128", @@ -4248,7 +4265,7 @@ export default { outOfFundsTick: "Option" }, /** - * Lookup458: pallet_payment_streams::types::DynamicRatePaymentStream + * Lookup461: pallet_payment_streams::types::DynamicRatePaymentStream **/ PalletPaymentStreamsDynamicRatePaymentStream: { amountProvided: "u64", @@ -4257,14 +4274,14 @@ export default { outOfFundsTick: "Option" }, /** - * Lookup459: pallet_payment_streams::types::ProviderLastChargeableInfo + * Lookup462: pallet_payment_streams::types::ProviderLastChargeableInfo **/ PalletPaymentStreamsProviderLastChargeableInfo: { lastChargeableTick: "u32", priceIndex: "u128" }, /** - * Lookup460: pallet_payment_streams::pallet::Error + * Lookup463: pallet_payment_streams::pallet::Error **/ PalletPaymentStreamsError: { _enum: [ @@ -4287,7 +4304,7 @@ export default { ] }, /** - * Lookup461: pallet_bucket_nfts::pallet::Error + * Lookup464: pallet_bucket_nfts::pallet::Error **/ PalletBucketNftsError: { _enum: [ @@ -4298,7 +4315,7 @@ export default { ] }, /** - * Lookup462: pallet_nfts::types::CollectionDetails + * Lookup465: pallet_nfts::types::CollectionDetails **/ PalletNftsCollectionDetails: { owner: "AccountId32", @@ -4309,13 +4326,13 @@ export default { attributes: "u32" }, /** - * Lookup467: pallet_nfts::types::CollectionRole + * Lookup470: pallet_nfts::types::CollectionRole **/ PalletNftsCollectionRole: { _enum: ["__Unused0", "Issuer", "Freezer", "__Unused3", "Admin"] }, /** - * Lookup468: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> + * Lookup471: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> **/ PalletNftsItemDetails: { owner: "AccountId32", @@ -4323,42 +4340,42 @@ export default { deposit: "PalletNftsItemDeposit" }, /** - * Lookup469: pallet_nfts::types::ItemDeposit + * Lookup472: pallet_nfts::types::ItemDeposit **/ PalletNftsItemDeposit: { account: "AccountId32", amount: "u128" }, /** - * Lookup474: pallet_nfts::types::CollectionMetadata + * Lookup477: pallet_nfts::types::CollectionMetadata **/ PalletNftsCollectionMetadata: { deposit: "u128", data: "Bytes" }, /** - * Lookup475: pallet_nfts::types::ItemMetadata, StringLimit> + * Lookup478: pallet_nfts::types::ItemMetadata, StringLimit> **/ PalletNftsItemMetadata: { deposit: "PalletNftsItemMetadataDeposit", data: "Bytes" }, /** - * Lookup476: pallet_nfts::types::ItemMetadataDeposit + * Lookup479: pallet_nfts::types::ItemMetadataDeposit **/ PalletNftsItemMetadataDeposit: { account: "Option", amount: "u128" }, /** - * Lookup479: pallet_nfts::types::AttributeDeposit + * Lookup482: pallet_nfts::types::AttributeDeposit **/ PalletNftsAttributeDeposit: { account: "Option", amount: "u128" }, /** - * Lookup483: pallet_nfts::types::PendingSwap, Deadline> + * Lookup486: pallet_nfts::types::PendingSwap, Deadline> **/ PalletNftsPendingSwap: { desiredCollection: "u32", @@ -4367,7 +4384,7 @@ export default { deadline: "u32" }, /** - * Lookup485: pallet_nfts::types::PalletFeature + * Lookup488: pallet_nfts::types::PalletFeature **/ PalletNftsPalletFeature: { _enum: [ @@ -4383,7 +4400,7 @@ export default { ] }, /** - * Lookup486: pallet_nfts::pallet::Error + * Lookup489: pallet_nfts::pallet::Error **/ PalletNftsError: { _enum: [ @@ -4435,51 +4452,51 @@ export default { ] }, /** - * Lookup489: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender + * Lookup492: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender **/ FrameSystemExtensionsCheckNonZeroSender: "Null", /** - * Lookup490: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup493: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: "Null", /** - * Lookup491: frame_system::extensions::check_tx_version::CheckTxVersion + * Lookup494: frame_system::extensions::check_tx_version::CheckTxVersion **/ FrameSystemExtensionsCheckTxVersion: "Null", /** - * Lookup492: frame_system::extensions::check_genesis::CheckGenesis + * Lookup495: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: "Null", /** - * Lookup495: frame_system::extensions::check_nonce::CheckNonce + * Lookup498: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: "Compact", /** - * Lookup496: frame_system::extensions::check_weight::CheckWeight + * Lookup499: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: "Null", /** - * Lookup497: pallet_transaction_payment::ChargeTransactionPayment + * Lookup500: pallet_transaction_payment::ChargeTransactionPayment **/ PalletTransactionPaymentChargeTransactionPayment: "Compact", /** - * Lookup498: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim + * Lookup501: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim **/ CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim: "Null", /** - * Lookup499: frame_metadata_hash_extension::CheckMetadataHash + * Lookup502: frame_metadata_hash_extension::CheckMetadataHash **/ FrameMetadataHashExtensionCheckMetadataHash: { mode: "FrameMetadataHashExtensionMode" }, /** - * Lookup500: frame_metadata_hash_extension::Mode + * Lookup503: frame_metadata_hash_extension::Mode **/ FrameMetadataHashExtensionMode: { _enum: ["Disabled", "Enabled"] }, /** - * Lookup501: storage_hub_runtime::Runtime + * Lookup504: storage_hub_runtime::Runtime **/ StorageHubRuntimeRuntime: "Null" }; diff --git a/api-augment/dist/interfaces/lookup.js.map b/api-augment/dist/interfaces/lookup.js.map index 19fc4db7f..6ca51228e 100644 --- a/api-augment/dist/interfaces/lookup.js.map +++ b/api-augment/dist/interfaces/lookup.js.map @@ -1 +1 @@ -{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/interfaces/lookup.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oBAAoB;AAEpB,8BAA8B;AAE9B,eAAe;IACb;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,KAAK;QAClB,IAAI,EAAE,2BAA2B;KAClC;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,MAAM;KACd;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,MAAM,EAAE,yBAAyB;QACjC,WAAW,EAAE,yBAAyB;QACtC,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,IAAI,EAAE,gCAAgC;KACvC;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,gBAAgB;YAC3B,IAAI,EAAE,gBAAgB;YACtB,UAAU,EAAE,gBAAgB;YAC5B,SAAS,EAAE,MAAM;YACjB,yBAAyB,EAAE,MAAM;SAClC;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,WAAW;KACpB;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,YAAY,EAAE,kCAAkC;aACjD;YACD,eAAe,EAAE;gBACf,aAAa,EAAE,wBAAwB;gBACvC,YAAY,EAAE,kCAAkC;aACjD;YACD,WAAW,EAAE,MAAM;YACnB,UAAU,EAAE;gBACV,OAAO,EAAE,aAAa;aACvB;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,aAAa;aACvB;YACD,QAAQ,EAAE;gBACR,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,MAAM;aACd;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,MAAM;aACrB;SACF;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,MAAM,EAAE,yBAAyB;QACjC,KAAK,EAAE,mCAAmC;QAC1C,OAAO,EAAE,0BAA0B;KACpC;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,CAAC;KAC9C;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;KACrB;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,MAAM;YACpB,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,sBAAsB;YAC9B,iBAAiB,EAAE,MAAM;YACzB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;YACxB,KAAK,EAAE,qBAAqB;YAC5B,UAAU,EAAE,6BAA6B;YACzC,aAAa,EAAE,6BAA6B;YAC5C,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,MAAM;SACvB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,QAAQ;KAChB;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE,CAAC,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,SAAS,CAAC;KACrK;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,gBAAgB,CAAC;KACnD;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC;KACnC;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE;YACL,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE;gBACzB,kBAAkB,EAAE,KAAK;aAC1B;YACD,2BAA2B,EAAE,MAAM;YACnC,wBAAwB,EAAE;gBACxB,KAAK,EAAE,KAAK;aACb;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,yBAAyB;gBACrC,OAAO,EAAE,MAAM;aAChB;YACD,iBAAiB,EAAE;gBACjB,WAAW,EAAE,iBAAiB;aAC/B;SACF;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,aAAa;gBACtB,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE;gBACR,OAAO,EAAE,aAAa;gBACtB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;gBACjB,MAAM,EAAE,MAAM;aACf;YACD,UAAU,EAAE;gBACV,GAAG,EAAE,aAAa;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,UAAU,EAAE;gBACV,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,kBAAkB,EAAE;gBAClB,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;gBACjB,MAAM,EAAE,MAAM;gBACd,iBAAiB,EAAE,qCAAqC;aACzD;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;aACnB;YACD,MAAM,EAAE;gBACN,MAAM,EAAE,MAAM;aACf;YACD,SAAS,EAAE;gBACT,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,MAAM;gBACX,IAAI,EAAE,MAAM;aACb;SACF;KACF;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;KAC5B;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,kBAAkB,EAAE;gBAClB,GAAG,EAAE,aAAa;gBAClB,SAAS,EAAE,MAAM;gBACjB,GAAG,EAAE,MAAM;aACZ;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,UAAU,EAAE,sCAAsC;aACnD;YACD,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,qBAAqB;gBAC1B,IAAI,EAAE,aAAa;aACpB;YACD,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,UAAU,EAAE,sCAAsC;aACnD;SACF;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,aAAa,EAAE,kBAAkB;aAClC;YACD,iBAAiB,EAAE;gBACjB,SAAS,EAAE,aAAa;aACzB;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,aAAa;aACzB;YACD,oBAAoB,EAAE;gBACpB,iBAAiB,EAAE,KAAK;aACzB;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,MAAM;aACnB;YACD,cAAc,EAAE;gBACd,SAAS,EAAE,aAAa;gBACxB,OAAO,EAAE,MAAM;aAChB;YACD,oBAAoB,EAAE;gBACpB,SAAS,EAAE,aAAa;gBACxB,OAAO,EAAE,MAAM;aAChB;YACD,gBAAgB,EAAE;gBAChB,SAAS,EAAE,aAAa;aACzB;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,aAAa;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;aAChB;YACD,0BAA0B,EAAE;gBAC1B,SAAS,EAAE,aAAa;aACzB;SACF;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,UAAU,EAAE;gBACV,YAAY,EAAE,KAAK;aACpB;SACF;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,eAAe,EAAE;gBACf,WAAW,EAAE,SAAS;aACvB;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,OAAO,EAAE,2BAA2B;aACrC;YACD,IAAI,EAAE;gBACJ,MAAM,EAAE,sBAAsB;gBAC9B,WAAW,EAAE,sBAAsB;gBACnC,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,SAAS;aACrB;YACD,kBAAkB,EAAE;gBAClB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,sBAAsB;aACjC;YACD,QAAQ,EAAE;gBACR,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,gBAAgB,EAAE;gBAChB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,yBAAyB;gBACvC,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,mBAAmB,EAAE;gBACnB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,kBAAkB,EAAE;gBAClB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;gBACd,gBAAgB,EAAE,8BAA8B;aACjD;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,sBAAsB;gBAC9B,MAAM,EAAE,oBAAoB;aAC7B;YACD,qBAAqB,EAAE;gBACrB,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,uBAAuB,EAAE;gBACvB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,oBAAoB,EAAE;gBACpB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,kBAAkB;aAC1B;YACD,yBAAyB,EAAE;gBACzB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,cAAc,EAAE;gBACd,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;gBACd,eAAe,EAAE,sBAAsB;gBACvC,kBAAkB,EAAE,8BAA8B;aACnD;YACD,oBAAoB,EAAE;gBACpB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,sBAAsB,EAAE;gBACtB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,wBAAwB,EAAE;gBACxB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE;gBACR,MAAM,EAAE,sBAAsB;gBAC9B,IAAI,EAAE,yBAAyB;aAChC;YACD,aAAa,EAAE;gBACb,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,sBAAsB;gBAC9B,MAAM,EAAE,oBAAoB;aAC7B;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,KAAK;aACf;SACF;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,QAAQ,EAAE;gBACR,IAAI,EAAE,yBAAyB;aAChC;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EAAE,kBAAkB;aAC1B;YACD,KAAK,EAAE;gBACL,KAAK,EAAE,kBAAkB;aAC1B;SACF;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,aAAa,EAAE,MAAM;YACrB,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE,MAAM;YACjC,YAAY,EAAE,MAAM;YACpB,qBAAqB,EAAE,MAAM;YAC7B,SAAS,EAAE,MAAM;YACjB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,MAAM;YACrB,qBAAqB,EAAE,MAAM;YAC7B,eAAe,EAAE,MAAM;YACvB,kBAAkB,EAAE,MAAM;YAC1B,qBAAqB,EAAE,MAAM;YAC7B,sBAAsB,EAAE,MAAM;YAC9B,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;YACtB,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,IAAI,EAAE,KAAK;YACX,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,mBAAmB,EAAE,MAAM;YAC3B,oBAAoB,EAAE,MAAM;YAC5B,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,MAAM;YACtB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,MAAM;YACjB,YAAY,EAAE,MAAM;YACpB,UAAU,EAAE,MAAM;YAClB,cAAc,EAAE,MAAM;YACtB,mBAAmB,EAAE,MAAM;YAC3B,kBAAkB,EAAE,yBAAyB;YAC7C,OAAO,EAAE,MAAM;YACf,mBAAmB,EAAE,MAAM;YAC3B,iBAAiB,EAAE,MAAM;SAC1B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,uBAAuB;KAClC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,uCAAuC;gBAChD,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,uCAAuC;gBAChD,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,uCAAuC;gBAChD,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE;gBACV,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,SAAS;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,qBAAqB;gBACzB,IAAI,EAAE,uBAAuB;aAC9B;YACD,eAAe,EAAE,+BAA+B;SACjD;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE;gBACR,OAAO,EAAE,cAAc;aACxB;YACD,WAAW,EAAE,MAAM;YACnB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,QAAQ;YACjB,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,MAAM;SACjB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,OAAO,EAAE;gBACP,KAAK,EAAE,cAAc;aACtB;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,kBAAkB,EAAE;gBAClB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE,8BAA8B;IAC/C;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,aAAa,EAAE,yBAAyB;YACxC,qBAAqB,EAAE,yBAAyB;YAChD,sBAAsB,EAAE,yBAAyB;YACjD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,sBAAsB;gBAChC,SAAS,EAAE,yBAAyB;gBACpC,OAAO,EAAE,8BAA8B;aACxC;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,yBAAyB;gBACjC,WAAW,EAAE,sBAAsB;aACpC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,yBAAyB;gBACjC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,yBAAyB;gBAC9C,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,uBAAuB;YACtC,WAAW,EAAE,+BAA+B;YAC5C,YAAY,EAAE;gBACZ,MAAM,EAAE,8BAA8B;gBACtC,WAAW,EAAE,sBAAsB;aACpC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,8BAA8B;gBACtC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,8BAA8B;gBACpC,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,8BAA8B;gBACtC,OAAO,EAAE,sBAAsB;gBAC/B,GAAG,EAAE,iBAAiB;aACvB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,8BAA8B;gBACtC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,aAAa,EAAE;gBACb,YAAY,EAAE,+BAA+B;gBAC7C,MAAM,EAAE,8BAA8B;aACvC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,mBAAmB;gBACzB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,iBAAiB;YAClC,WAAW,EAAE,iBAAiB;YAC9B,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,sBAAsB;aAC/B;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,kBAAkB,EAAE,MAAM;YAC1B,SAAS,EAAE,yBAAyB;YACpC,WAAW,EAAE,yBAAyB;YACtC,YAAY,EAAE,8BAA8B;YAC5C,WAAW,EAAE,gCAAgC;YAC7C,oBAAoB,EAAE,qBAAqB;YAC3C,WAAW,EAAE;gBACX,UAAU,EAAE,OAAO;gBACnB,YAAY,EAAE,+BAA+B;aAC9C;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,OAAO;gBACnB,UAAU,EAAE,cAAc;gBAC1B,aAAa,EAAE,cAAc;aAC9B;YACD,oBAAoB,EAAE,+BAA+B;YACrD,mBAAmB,EAAE,MAAM;YAC3B,eAAe,EAAE,sBAAsB;YACvC,aAAa,EAAE;gBACb,OAAO,EAAE,+BAA+B;gBACxC,WAAW,EAAE,uBAAuB;gBACpC,GAAG,EAAE,iBAAiB;aACvB;YACD,SAAS,EAAE;gBACT,KAAK,EAAE,mBAAmB;gBAC1B,QAAQ,EAAE,sBAAsB;aACjC;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,sBAAsB;aAC/B;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,mBAAmB;gBAC1B,KAAK,EAAE,sBAAsB;aAC9B;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,sBAAsB;aAC/B;YACD,WAAW,EAAE;gBACX,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,sBAAsB;YACnC,eAAe,EAAE;gBACf,WAAW,EAAE,kBAAkB;gBAC/B,WAAW,EAAE,8BAA8B;aAC5C;SACF;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE,wBAAwB;IACjD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,EAAE,EAAE,0BAA0B;QAC9B,GAAG,EAAE,8BAA8B;KACpC;IACD;;QAEI;IACJ,wBAAwB,EAAE,sBAAsB;IAChD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,gCAAgC;SAC9C;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,yBAAyB;YACjC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,6BAA6B;YAC1C,cAAc,EAAE,qBAAqB;SACtC;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,OAAO;QACnB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;KACtB;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,KAAK,EAAE,OAAO;YACd,cAAc,EAAE,OAAO;SACxB;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,KAAK,CAAC;KAC1D;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,OAAO,EAAE,OAAO;KACjB;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,WAAW,EAAE,sBAAsB;QACnC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,QAAQ,EAAE,yBAAyB;YACnC,IAAI,EAAE,4BAA4B;SACnC;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,0BAA0B;gBAC9B,GAAG,EAAE,kCAAkC;aACxC;YACD,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE;gBACZ,EAAE,EAAE,0BAA0B;gBAC9B,GAAG,EAAE,kCAAkC;gBACvC,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,yBAAyB;SACnC;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,4BAA4B;YAChC,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,4BAA4B;YAChC,EAAE,EAAE,yBAAyB;SAC9B;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE,sBAAsB;IAClD;;QAEI;IACJ,eAAe,EAAE;QACf,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,4BAA4B;KAClC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,OAAO;SAClB;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,6BAA6B;KACxC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,+BAA+B;YACnC,EAAE,EAAE,6CAA6C;YACjD,EAAE,EAAE,2DAA2D;YAC/D,EAAE,EAAE,yEAAyE;YAC7E,EAAE,EAAE,uFAAuF;YAC3F,EAAE,EAAE,qGAAqG;YACzG,EAAE,EAAE,mHAAmH;SACxH;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,gBAAgB;gBACzB,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,gBAAgB;gBACzB,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,gBAAgB;gBACzB,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE,OAAO;YACnB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,aAAa;gBACjB,IAAI,EAAE,eAAe;aACtB;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;SACf;KACF;IACD;;QAEI;IACJ,WAAW,EAAE;QACX,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,MAAM;SACjB;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,OAAO,EAAE;gBACP,KAAK,EAAE,cAAc;aACtB;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,kBAAkB,EAAE;gBAClB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,8BAA8B;SAC5C;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,OAAO;SACd;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE,sBAAsB;IAClD;;QAEI;IACJ,eAAe,EAAE;QACf,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,4BAA4B;KAClC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,QAAQ,EAAE,2BAA2B;YACrC,QAAQ,EAAE,SAAS;SACpB;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,gBAAgB;KAC3B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,+BAA+B;YACnC,EAAE,EAAE,6CAA6C;YACjD,EAAE,EAAE,2DAA2D;YAC/D,EAAE,EAAE,yEAAyE;YAC7E,EAAE,EAAE,uFAAuF;YAC3F,EAAE,EAAE,qGAAqG;YACzG,EAAE,EAAE,mHAAmH;SACxH;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,gCAAgC;gBACzC,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,gCAAgC;gBACzC,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,gCAAgC;gBACzC,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE;gBACV,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,SAAS;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,qBAAqB;gBACzB,IAAI,EAAE,uBAAuB;aAC9B;YACD,eAAe,EAAE,wBAAwB;SAC1C;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE;gBACR,OAAO,EAAE,cAAc;aACxB;YACD,WAAW,EAAE,MAAM;YACnB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,8BAA8B;SAC5C;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,oBAAoB;YACxB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,2BAA2B;YAC/B,EAAE,EAAE,sBAAsB;SAC3B;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,aAAa,EAAE,SAAS;YACxB,kBAAkB,EAAE,SAAS;YAC7B,gBAAgB,EAAE,qCAAqC;SACxD;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,EAAE,EAAE,MAAM;gBACV,MAAM,EAAE,6CAA6C;gBACrD,KAAK,EAAE,yCAAyC;aACjD;YACD,SAAS,EAAE;gBACT,EAAE,EAAE,MAAM;gBACV,MAAM,EAAE,6CAA6C;gBACrD,UAAU,EAAE,yBAAyB;gBACrC,OAAO,EAAE,MAAM;aAChB;YACD,kBAAkB,EAAE;gBAClB,EAAE,EAAE,SAAS;gBACb,MAAM,EAAE,6CAA6C;gBACrD,SAAS,EAAE,KAAK;gBAChB,YAAY,EAAE,KAAK;aACpB;YACD,UAAU,EAAE;gBACV,MAAM,EAAE,6CAA6C;gBACrD,KAAK,EAAE,KAAK;aACb;SACF;KACF;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,KAAK;SACf;KACF;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,MAAM;YACnB,UAAU,EAAE,yBAAyB;YACrC,KAAK,EAAE,MAAM;SACd;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,uBAAuB,EAAE;gBACvB,GAAG,EAAE,aAAa;gBAClB,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,wCAAwC;aACpD;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,wCAAwC;aACpD;YACD,uBAAuB,EAAE;gBACvB,GAAG,EAAE,aAAa;gBAClB,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;aAChB;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;aAChB;YACD,qBAAqB,EAAE;gBACrB,GAAG,EAAE,aAAa;aACnB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,eAAe,EAAE;gBACf,GAAG,EAAE,aAAa;gBAClB,UAAU,EAAE,yCAAyC;gBACrD,WAAW,EAAE,KAAK;gBAClB,WAAW,EAAE,KAAK;gBAClB,0BAA0B,EAAE,KAAK;aAClC;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,MAAM;gBAClB,aAAa,EAAE,MAAM;aACtB;SACF;KACF;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,YAAY;KACxB;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,KAAK,EAAE;YACL,qBAAqB,EAAE,MAAM;YAC7B,mBAAmB,EAAE,MAAM;SAC5B;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,OAAO;gBACb,YAAY,EAAE,aAAa;gBAC3B,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,MAAM;aACjB;YACD,oBAAoB,EAAE;gBACpB,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,aAAa;gBAC3B,OAAO,EAAE,MAAM;aAChB;YACD,2BAA2B,EAAE;gBAC3B,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,KAAK;aACpB;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,GAAG,EAAE,aAAa;gBAClB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,YAAY;aACtB;YACD,6BAA6B,EAAE;gBAC7B,OAAO,EAAE,iDAAiD;aAC3D;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,cAAc,EAAE,YAAY;gBAC5B,KAAK,EAAE,aAAa;gBACpB,KAAK,EAAE,KAAK;aACb;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,WAAW;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,OAAO,EAAE,MAAM;aAChB;YACD,qBAAqB,EAAE;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,qBAAqB,EAAE;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,yBAAyB,EAAE;gBACzB,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,MAAM;gBACf,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,OAAO;aAClB;YACD,wBAAwB,EAAE;gBACxB,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,MAAM;aAChB;YACD,sCAAsC,EAAE;gBACtC,MAAM,EAAE,wCAAwC;gBAChD,OAAO,EAAE,MAAM;aAChB;YACD,0BAA0B,EAAE;gBAC1B,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,MAAM;gBACf,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,MAAM;aAChB;YACD,8BAA8B,EAAE;gBAC9B,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;gBACb,gBAAgB,EAAE,MAAM;aACzB;YACD,2CAA2C,EAAE;gBAC3C,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,gBAAgB,EAAE,MAAM;aACzB;YACD,4BAA4B,EAAE;gBAC5B,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,wBAAwB,EAAE;gBACxB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;aACjB;YACD,kBAAkB,EAAE;gBAClB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd;YACD,kBAAkB,EAAE;gBAClB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd;YACD,iCAAiC,EAAE;gBACjC,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;aACjB;SACF;KACF;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,KAAK,EAAE,MAAM;QACb,SAAS,EAAE,qCAAqC;KACjD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,QAAQ,EAAE,iDAAiD;YAC3D,QAAQ,EAAE,iDAAiD;YAC3D,MAAM,EAAE,+CAA+C;SACxD;KACF;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,QAAQ,EAAE,0DAA0D;QACpE,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,KAAK,EAAE,CAAC,wBAAwB,EAAE,sBAAsB,EAAE,eAAe,CAAC;KAC3E;IACD;;QAEI;IACJ,6CAA6C,EAAE;QAC7C,QAAQ,EAAE,oCAAoC;QAC9C,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,KAAK,EAAE;YACL,SAAS,EAAE,aAAa;YACxB,KAAK,EAAE,MAAM;SACd;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,GAAG,EAAE,aAAa;gBAClB,aAAa,EAAE,MAAM;aACtB;YACD,aAAa,EAAE;gBACb,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,yBAAyB;aACjC;YACD,gBAAgB,EAAE;gBAChB,gBAAgB,EAAE,KAAK;gBACvB,IAAI,EAAE,MAAM;aACb;YACD,sBAAsB,EAAE;gBACtB,gBAAgB,EAAE,KAAK;gBACvB,UAAU,EAAE,iDAAiD;aAC9D;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,qBAAqB,EAAE,KAAK;aAC7B;YACD,4BAA4B,EAAE;gBAC5B,QAAQ,EAAE,MAAM;aACjB;YACD,4BAA4B,EAAE;gBAC5B,WAAW,EAAE,KAAK;gBAClB,qBAAqB,EAAE,KAAK;gBAC5B,QAAQ,EAAE,MAAM;gBAChB,oBAAoB,EAAE,qBAAqB;aAC5C;YACD,gBAAgB,EAAE;gBAChB,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,yCAAyC;gBACpD,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,MAAM;aACf;SACF;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,WAAW,EAAE,gCAAgC;QAC7C,SAAS,EAAE,4CAA4C;KACxD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,YAAY,EAAE,YAAY;KAC3B;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE,gCAAgC;QACvC,cAAc,EAAE,KAAK;KACtB;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,YAAY,EAAE,6BAA6B;QAC3C,KAAK,EAAE,gCAAgC;KACxC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,cAAc;QACxB,WAAW,EAAE,4BAA4B;KAC1C;IACD;;QAEI;IACJ,0BAA0B,EAAE,SAAS;IACrC;;QAEI;IACJ,2BAA2B,EAAE,MAAM;IACnC;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,iCAAiC,EAAE;gBACjC,cAAc,EAAE,MAAM;gBACtB,SAAS,EAAE,KAAK;gBAChB,eAAe,EAAE,KAAK;aACvB;SACF;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,OAAO,EAAE,MAAM;aAChB;YACD,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;aACnB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,cAAc,EAAE,KAAK;aACtB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,iBAAiB,EAAE,KAAK;aACzB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;aACnB;YACD,oBAAoB,EAAE;gBACpB,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,MAAM;gBAClB,kBAAkB,EAAE,KAAK;gBACzB,wBAAwB,EAAE,MAAM;aACjC;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;aACnB;YACD,aAAa,EAAE;gBACb,GAAG,EAAE,aAAa;aACnB;YACD,WAAW,EAAE;gBACX,GAAG,EAAE,aAAa;aACnB;SACF;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,aAAa;aACzB;YACD,qBAAqB,EAAE;gBACrB,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;aACd;YACD,UAAU,EAAE;gBACV,OAAO,EAAE,aAAa;gBACtB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;aACd;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,aAAa;gBACtB,KAAK,EAAE,aAAa;aACrB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,aAAa;aACrB;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;aAClB;YACD,MAAM,EAAE;gBACN,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;aAClB;YACD,MAAM,EAAE;gBACN,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,MAAM;aACvB;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;aAClB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,aAAa;aACxB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,qBAAqB;gBAC7B,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,qBAAqB;aAC/B;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,aAAa;gBACvB,QAAQ,EAAE,aAAa;aACxB;YACD,iBAAiB,EAAE;gBACjB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,aAAa;aACxB;YACD,qBAAqB,EAAE;gBACrB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;aAClB;YACD,qBAAqB,EAAE;gBACrB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,OAAO;aACd;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;aAClB;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO;aACd;YACD,mBAAmB,EAAE;gBACnB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,eAAe,EAAE,UAAU;aAC5B;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;gBACd,SAAS,EAAE,8BAA8B;aAC1C;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,GAAG,EAAE,OAAO;gBACZ,SAAS,EAAE,8BAA8B;aAC1C;YACD,2BAA2B,EAAE;gBAC3B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,aAAa;aACxB;YACD,6BAA6B,EAAE;gBAC7B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,aAAa;aACxB;YACD,0BAA0B,EAAE;gBAC1B,GAAG,EAAE,aAAa;gBAClB,eAAe,EAAE,aAAa;aAC/B;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,KAAK;aACjB;YACD,6BAA6B,EAAE;gBAC7B,UAAU,EAAE,KAAK;aAClB;YACD,2BAA2B,EAAE;gBAC3B,MAAM,EAAE,aAAa;aACtB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,MAAM;gBACb,gBAAgB,EAAE,qBAAqB;aACxC;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,aAAa;aACrB;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,aAAa;gBACvB,MAAM,EAAE,MAAM;aACf;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,aAAa,EAAE;gBACb,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,WAAW,EAAE;gBACX,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,KAAK;gBACf,aAAa,EAAE,aAAa;gBAC5B,kBAAkB,EAAE,KAAK;gBACzB,YAAY,EAAE,KAAK;gBACnB,iBAAiB,EAAE,aAAa;gBAChC,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,SAAS,EAAE,8BAA8B;aAC1C;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,aAAa;gBACnB,SAAS,EAAE,4BAA4B;gBACvC,KAAK,EAAE,OAAO;aACf;SACF;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,MAAM;YACvB,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,aAAa;SACvB;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,0BAA0B;KACtC;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;KAC3B;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,WAAW,EAAE,KAAK;YAClB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,GAAG,EAAE,2DAA2D;gBAChE,QAAQ,EAAE,qEAAqE;gBAC/E,QAAQ,EAAE,qEAAqE;aAChF;SACF;KACF;IACD;;QAEI;IACJ,yDAAyD,EAAE;QACzD,KAAK,EAAE;YACL,aAAa,EAAE,8EAA8E;SAC9F;KACF;IACD;;QAEI;IACJ,4EAA4E,EAAE;QAC5E,KAAK,EAAE,CAAC,2BAA2B,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,oBAAoB,CAAC;KAClH;IACD;;QAEI;IACJ,wFAAwF,EAAE,MAAM;IAChG;;QAEI;IACJ,qFAAqF,EAAE,MAAM;IAC7F;;QAEI;IACJ,wFAAwF,EAAE,MAAM;IAChG;;QAEI;IACJ,iFAAiF,EAAE,MAAM;IACzF;;QAEI;IACJ,2DAA2D,EAAE;QAC3D,KAAK,EAAE;YACL,aAAa,EAAE,gFAAgF;SAChG;KACF;IACD;;QAEI;IACJ,8EAA8E,EAAE;QAC9E,KAAK,EAAE;YACL,yBAAyB,EAAE,MAAM;YACjC,sBAAsB,EAAE,MAAM;YAC9B,yBAAyB,EAAE,KAAK;YAChC,kBAAkB,EAAE,KAAK;SAC1B;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,cAAc,EAAE,KAAK;YACrB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;SACvB;KACF;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,WAAW,EAAE,cAAc;QAC3B,QAAQ,EAAE,MAAM;KACjB;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,MAAM;KACrB;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,MAAM,EAAE,OAAO;aAChB;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,KAAK;aACb;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,OAAO;aACd;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,OAAO;aACd;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,oBAAoB;aAC5B;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,YAAY;aACpB;YACD,WAAW,EAAE;gBACX,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK;aACf;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE,OAAO;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;aACjB;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,MAAM;aACjB;YACD,wBAAwB,EAAE;gBACxB,IAAI,EAAE,OAAO;aACd;SACF;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,SAAS,EAAE,yBAAyB;QACpC,QAAQ,EAAE,yBAAyB;QACnC,QAAQ,EAAE,qDAAqD;KAChE;IACD;;QAEI;IACJ,mDAAmD,EAAE;QACnD,MAAM,EAAE,kCAAkC;QAC1C,WAAW,EAAE,kCAAkC;QAC/C,SAAS,EAAE,kCAAkC;KAC9C;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,aAAa,EAAE,yBAAyB;QACxC,YAAY,EAAE,iCAAiC;QAC/C,QAAQ,EAAE,iCAAiC;QAC3C,QAAQ,EAAE,iCAAiC;KAC5C;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,GAAG,EAAE,yCAAyC;KAC/C;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,MAAM,EAAE,KAAK;QACb,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,KAAK;KACjB;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,MAAM;QAChB,gBAAgB,EAAE,KAAK;QACvB,WAAW,EAAE,KAAK;QAClB,WAAW,EAAE,KAAK;QAClB,IAAI,EAAE,mBAAmB;QACzB,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,IAAI;KACnB;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE,CAAC,iBAAiB,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,cAAc,EAAE,6BAA6B,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACxN;IACD;;QAEI;IACJ,qDAAqD,EAAE;QACrD,aAAa,EAAE,4DAA4D;QAC3E,YAAY,EAAE,cAAc;QAC5B,qBAAqB,EAAE,4CAA4C;KACpE;IACD;;QAEI;IACJ,0DAA0D,EAAE;QAC1D,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,+EAA+E;KAC9F;IACD;;QAEI;IACJ,8DAA8D,EAAE;QAC9D,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,kCAAkC,EAAE;QAClC,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;KAC5B;IACD;;QAEI;IACJ,2DAA2D,EAAE;QAC3D,aAAa,EAAE,4DAA4D;QAC3E,aAAa,EAAE,aAAa;QAC5B,qBAAqB,EAAE,4CAA4C;KACpE;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,UAAU,EAAE,OAAO;QACnB,iBAAiB,EAAE,KAAK;QACxB,sBAAsB,EAAE,MAAM;QAC9B,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,KAAK,EAAE,CAAC,SAAS,CAAC;KACnB;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,SAAS,EAAE,iBAAiB;KAC7B;IACD;;QAEI;IACJ,oEAAoE,EAAE;QACpE,UAAU,EAAE,MAAM;QAClB,mCAAmC,EAAE,mFAAmF;QACxH,eAAe,EAAE,oDAAoD;QACrE,cAAc,EAAE,oDAAoD;KACrE;IACD;;QAEI;IACJ,iFAAiF,EAAE;QACjF,cAAc,EAAE,KAAK;QACrB,aAAa,EAAE,KAAK;KACrB;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,cAAc;KACxB;IACD;;QAEI;IACJ,6CAA6C,EAAE;QAC7C,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,KAAK;QACtB,mBAAmB,EAAE,KAAK;QAC1B,kBAAkB,EAAE,KAAK;QACzB,oBAAoB,EAAE,KAAK;QAC3B,+BAA+B,EAAE,KAAK;QACtC,6BAA6B,EAAE,KAAK;QACpC,yBAAyB,EAAE,KAAK;QAChC,sBAAsB,EAAE,KAAK;QAC7B,kBAAkB,EAAE,oDAAoD;KACzE;IACD;;QAEI;IACJ,kDAAkD,EAAE;QAClD,iBAAiB,EAAE,KAAK;QACxB,kBAAkB,EAAE,KAAK;KAC1B;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE;YACL,mBAAmB,EAAE;gBACnB,IAAI,EAAE,yDAAyD;aAChE;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,OAAO;aACjB;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,MAAM;aACrB;YACD,wBAAwB,EAAE;gBACxB,IAAI,EAAE,OAAO;aACd;SACF;KACF;IACD;;QAEI;IACJ,uDAAuD,EAAE;QACvD,cAAc,EAAE,6CAA6C;QAC7D,eAAe,EAAE,oBAAoB;QACrC,gBAAgB,EAAE,mDAAmD;QACrE,kBAAkB,EAAE,8DAA8D;KACnF;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,OAAO;KACb;IACD;;QAEI;IACJ,wCAAwC,EAAE;QACxC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,QAAQ,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACrL;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,GAAG,EAAE;gBACH,GAAG,EAAE,cAAc;aACpB;SACF;KACF;IACD;;QAEI;IACJ,wBAAwB,EAAE,MAAM;IAChC;;QAEI;IACJ,yBAAyB,EAAE;QACzB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,uBAAuB;KACjC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC;KAC9B;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,kCAAkC,EAAE;QAClC,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,kCAAkC;YAC7C,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,cAAc,EAAE,gCAAgC;SACjD;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,wBAAwB,EAAE,eAAe,CAAC;KACnD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,sBAAsB,CAAC;KAChC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,EAAE,EAAE,MAAM;QACV,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,oBAAoB,EAAE;gBACpB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,SAAS,EAAE,MAAM;YACjB,cAAc,EAAE;gBACd,MAAM,EAAE,cAAc;gBACtB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,mBAAmB,EAAE;gBACnB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,cAAc;gBACpB,SAAS,EAAE,MAAM;aAClB;YACD,eAAe,EAAE;gBACf,GAAG,EAAE,cAAc;gBACnB,MAAM,EAAE,MAAM;aACf;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,kBAAkB;aACxB;YACD,SAAS,EAAE,MAAM;YACjB,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,OAAO,EAAE,eAAe;aACzB;YACD,2BAA2B,EAAE;gBAC3B,SAAS,EAAE,mCAAmC;gBAC9C,KAAK,EAAE,eAAe;aACvB;SACF;KACF;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;KAChC;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE,CAAC,gBAAgB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,eAAe,EAAE,yBAAyB,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,WAAW,CAAC;KACpP;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;KAC3B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,IAAI,EAAE;gBACJ,IAAI,EAAE,MAAM;aACb;YACD,qBAAqB,EAAE;gBACrB,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,yBAAyB;aAClC;YACD,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,cAAc;aACrB;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,cAAc;gBACnB,IAAI,EAAE,MAAM;aACb;YACD,UAAU,EAAE,MAAM;SACnB;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,kBAAkB;aACzB;YACD,sBAAsB,EAAE;gBACtB,GAAG,EAAE,KAAK;aACX;YACD,kBAAkB,EAAE;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,qBAAqB,EAAE,MAAM;YAC7B,YAAY,EAAE,MAAM;YACpB,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;aACnB;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;aACnB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,MAAM;aACnB;YACD,mBAAmB,EAAE;gBACnB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,aAAa;aACtB;SACF;KACF;IACD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,KAAK,EAAE;YACL,QAAQ,EAAE;gBACR,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,8BAA8B;gBACrC,KAAK,EAAE,OAAO;aACf;YACD,UAAU,EAAE,MAAM;SACnB;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,IAAI,EAAE,wCAAwC;KAC/C;IACD;;QAEI;IACJ,sCAAsC,EAAE,SAAS;IACjD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,qBAAqB,EAAE,MAAM;YAC7B,oBAAoB,EAAE,MAAM;YAC5B,wBAAwB,EAAE;gBACxB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;SACF;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE;gBACJ,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,iBAAiB;aAC3B;YACD,eAAe,EAAE;gBACf,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;aACpB;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;aACpB;YACD,OAAO,EAAE;gBACP,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,yBAAyB;aACrC;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,yBAAyB,EAAE;gBACzB,eAAe,EAAE,aAAa;aAC/B;YACD,8BAA8B,EAAE;gBAC9B,QAAQ,EAAE,sBAAsB;aACjC;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,sBAAsB;aACjC;YACD,+BAA+B,EAAE;gBAC/B,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,gBAAgB,EAAE;gBAChB,SAAS,EAAE,MAAM;aAClB;YACD,eAAe,EAAE;gBACf,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE,oBAAoB;gBAC5B,WAAW,EAAE,sBAAsB;aACpC;YACD,mCAAmC,EAAE;gBACnC,IAAI,EAAE,sBAAsB;gBAC5B,MAAM,EAAE,oBAAoB;gBAC5B,kBAAkB,EAAE,6CAA6C;gBACjE,YAAY,EAAE,qBAAqB;gBACnC,gBAAgB,EAAE,6CAA6C;gBAC/D,eAAe,EAAE,iBAAiB;gBAClC,WAAW,EAAE,kBAAkB;aAChC;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,iBAAiB;SACtB;KACF;IACD;;QAEI;IACJ,QAAQ,EAAE,uBAAuB;IACjC;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,aAAa,EAAE,4BAA4B;YAC3C,qBAAqB,EAAE,4BAA4B;YACnD,sBAAsB,EAAE,4BAA4B;YACpD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,eAAe;gBACzB,SAAS,EAAE,cAAc;aAC1B;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,4BAA4B;gBACpC,WAAW,EAAE,oBAAoB;aAClC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,4BAA4B;gBACpC,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,cAAc;gBACnC,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,6BAA6B;YAC5C,WAAW,EAAE;gBACX,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,oBAAoB;gBAC1B,iBAAiB,EAAE,cAAc;aAClC;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,cAAc;gBACzB,WAAW,EAAE,oBAAoB;aAClC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,cAAc;gBACzB,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,4BAA4B;aACtC;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,iCAAiC;gBACzC,OAAO,EAAE,oBAAoB;gBAC7B,GAAG,EAAE,UAAU;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,oBAAoB;gBAC1B,MAAM,EAAE,iCAAiC;gBACzC,iBAAiB,EAAE,cAAc;aAClC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,UAAU;YACvB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,4BAA4B;gBACpC,MAAM,EAAE,oBAAoB;aAC7B;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,cAAc;aAClC;YACD,kBAAkB,EAAE,MAAM;SAC3B;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,4BAA4B;YACpC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;SACf;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,aAAa,EAAE,MAAM;YACrB,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE,MAAM;YACjC,iBAAiB,EAAE,MAAM;YACzB,0BAA0B,EAAE,MAAM;YAClC,SAAS,EAAE,MAAM;YACjB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,MAAM;YACrB,qBAAqB,EAAE,MAAM;YAC7B,eAAe,EAAE,MAAM;YACvB,kBAAkB,EAAE,MAAM;YAC1B,qBAAqB,EAAE,MAAM;YAC7B,sBAAsB,EAAE,MAAM;YAC9B,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;YACtB,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,IAAI,EAAE,KAAK;YACX,mBAAmB,EAAE,MAAM;YAC3B,kBAAkB,EAAE,KAAK;YACzB,OAAO,EAAE,MAAM;YACf,mBAAmB,EAAE,MAAM;SAC5B;KACF;IACD;;QAEI;IACJ,+BAA+B,EAAE;QAC/B,KAAK,EAAE;YACL,QAAQ,EAAE,4BAA4B;YACtC,IAAI,EAAE,+BAA+B;SACtC;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;aACtC;SACF;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,cAAc;SACxB;KACF;IACD;;QAEI;IACJ,QAAQ,EAAE,uBAAuB;IACjC;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,aAAa,EAAE,4BAA4B;YAC3C,qBAAqB,EAAE,4BAA4B;YACnD,sBAAsB,EAAE,4BAA4B;YACpD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,eAAe;gBACzB,SAAS,EAAE,yBAAyB;gBACpC,OAAO,EAAE,mCAAmC;aAC7C;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,4BAA4B;gBACpC,WAAW,EAAE,2BAA2B;aACzC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,4BAA4B;gBACpC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,yBAAyB;gBAC9C,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,gBAAgB;YAC/B,WAAW,EAAE,wBAAwB;YACrC,YAAY,EAAE;gBACZ,MAAM,EAAE,iCAAiC;gBACzC,WAAW,EAAE,2BAA2B;aACzC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,iCAAiC;gBACvC,IAAI,EAAE,4BAA4B;gBAClC,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,iCAAiC;gBACzC,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,UAAU;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,YAAY,EAAE,wBAAwB;gBACtC,MAAM,EAAE,iCAAiC;aAC1C;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,UAAU;YACvB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,4BAA4B;gBACpC,MAAM,EAAE,2BAA2B;aACpC;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,kBAAkB,EAAE,MAAM;YAC1B,SAAS,EAAE,4BAA4B;YACvC,WAAW,EAAE,4BAA4B;YACzC,YAAY,EAAE,mCAAmC;YACjD,WAAW,EAAE,gCAAgC;YAC7C,oBAAoB,EAAE,qBAAqB;YAC3C,WAAW,EAAE;gBACX,UAAU,EAAE,OAAO;gBACnB,YAAY,EAAE,wBAAwB;aACvC;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,OAAO;gBACnB,UAAU,EAAE,cAAc;gBAC1B,aAAa,EAAE,cAAc;aAC9B;YACD,oBAAoB,EAAE,wBAAwB;YAC9C,mBAAmB,EAAE,MAAM;YAC3B,eAAe,EAAE,eAAe;YAChC,aAAa,EAAE;gBACb,OAAO,EAAE,wBAAwB;gBACjC,WAAW,EAAE,gBAAgB;gBAC7B,GAAG,EAAE,UAAU;aAChB;YACD,SAAS,EAAE;gBACT,KAAK,EAAE,iBAAiB;gBACxB,QAAQ,EAAE,2BAA2B;aACtC;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,2BAA2B;aACpC;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,iBAAiB;gBACxB,KAAK,EAAE,2BAA2B;aACnC;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,2BAA2B;aACpC;YACD,WAAW,EAAE;gBACX,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,2BAA2B;YACxC,eAAe,EAAE;gBACf,WAAW,EAAE,kBAAkB;gBAC/B,WAAW,EAAE,mCAAmC;aACjD;SACF;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,4BAA4B;YACpC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,sBAAsB;YACnC,cAAc,EAAE,qBAAqB;SACtC;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,OAAO;QACnB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;KACtB;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,+BAA+B,EAAE;QAC/B,KAAK,EAAE;YACL,QAAQ,EAAE,4BAA4B;YACtC,IAAI,EAAE,+BAA+B;SACtC;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;aACtC;YACD,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE;gBACZ,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;gBACrC,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,YAAY,EAAE,MAAM;YACpB,kBAAkB,EAAE,MAAM;YAC1B,aAAa,EAAE,sBAAsB;SACtC;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,wBAAwB;YAC5B,EAAE,EAAE,0BAA0B;SAC/B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE,MAAM;IAC5B;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,aAAa,EAAE,6CAA6C;gBAC5D,SAAS,EAAE,KAAK;aACjB;YACD,kBAAkB,EAAE;gBAClB,aAAa,EAAE,6CAA6C;gBAC5D,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,yBAAyB;aACvC;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,SAAS,EAAE,wCAAwC;gBACnD,cAAc,EAAE,aAAa;aAC9B;YACD,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,cAAc,EAAE,aAAa;aAC9B;YACD,eAAe,EAAE;gBACf,eAAe,EAAE,qBAAqB;aACvC;YACD,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,YAAY,EAAE,MAAM;YACpB,eAAe,EAAE;gBACf,WAAW,EAAE,KAAK;aACnB;YACD,cAAc,EAAE;gBACd,YAAY,EAAE,wCAAwC;aACvD;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,SAAS,EAAE,wCAAwC;gBACnD,cAAc,EAAE,aAAa;aAC9B;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,aAAa;aACtB;YACD,KAAK,EAAE;gBACL,UAAU,EAAE,MAAM;aACnB;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,aAAa,EAAE;gBACb,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,MAAM;aACjB;YACD,+BAA+B,EAAE;gBAC/B,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,2CAA2C;aACtD;YACD,qBAAqB,EAAE;gBACrB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,MAAM;aAChB;YACD,2CAA2C,EAAE;gBAC3C,QAAQ,EAAE,MAAM;aACjB;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,YAAY;aACtB;YACD,sBAAsB,EAAE;gBACtB,OAAO,EAAE,MAAM;aAChB;YACD,2CAA2C,EAAE;gBAC3C,QAAQ,EAAE,MAAM;aACjB;YACD,6CAA6C,EAAE;gBAC7C,qBAAqB,EAAE,uDAAuD;aAC/E;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,uBAAuB,EAAE,gCAAgC;gBACzD,iBAAiB,EAAE,4CAA4C;aAChE;YACD,wBAAwB,EAAE;gBACxB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,aAAa;gBACpB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,MAAM;gBAChB,oBAAoB,EAAE,gCAAgC;aACvD;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,MAAM;gBACf,oBAAoB,EAAE,gCAAgC;aACvD;YACD,+BAA+B,EAAE;gBAC/B,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,aAAa;gBACpB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,oBAAoB,EAAE,gCAAgC;aACvD;YACD,WAAW,EAAE;gBACX,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,MAAM;gBACnB,yBAAyB,EAAE,wCAAwC;aACpE;YACD,0CAA0C,EAAE;gBAC1C,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,WAAW,EAAE,gCAAgC;aAC9C;YACD,qBAAqB,EAAE;gBACrB,iBAAiB,EAAE,aAAa;gBAChC,2BAA2B,EAAE,aAAa;aAC3C;SACF;KACF;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;KAChC;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,MAAM,EAAE,0DAA0D;QAClE,MAAM,EAAE,kEAAkE;KAC3E;IACD;;QAEI;IACJ,gDAAgD,EAAE;QAChD,iBAAiB,EAAE,4CAA4C;QAC/D,uBAAuB,EAAE,gCAAgC;KAC1D;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,GAAG,EAAE,MAAM;aACZ;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,QAAQ,EAAE,cAAc;aACzB;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,MAAM;aACjB;YACD,UAAU,EAAE;gBACV,MAAM,EAAE,MAAM;aACf;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE,CAAC,qBAAqB,CAAC;KAC/B;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE;YACL,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,IAAI,EAAE,MAAM;aACb;YACD,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,OAAO,EAAE,MAAM;aAChB;YACD,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;aAC3B;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,cAAc,EAAE,KAAK;aACtB;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,iBAAiB,EAAE,KAAK;aACzB;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;aAC3B;YACD,sBAAsB,EAAE;gBACtB,WAAW,EAAE,aAAa;aAC3B;YACD,oBAAoB,EAAE,MAAM;YAC5B,oBAAoB,EAAE,MAAM;SAC7B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;gBACb,eAAe,EAAE,eAAe;aACjC;YACD,kBAAkB,EAAE;gBAClB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;gBACb,eAAe,EAAE,eAAe;aACjC;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,4BAA4B;aACrC;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,4BAA4B;aACrC;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,0BAA0B;aACpC;YACD,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,cAAc;gBACtB,WAAW,EAAE,+BAA+B;aAC7C;YACD,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,cAAc;gBACtB,UAAU,EAAE,sBAAsB;aACnC;YACD,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,cAAc;aACrB;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,UAAU;aAClB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,KAAK;aACpB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,cAAc;aACzB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,sBAAsB;gBAC9B,KAAK,EAAE,sBAAsB;gBAC7B,OAAO,EAAE,sBAAsB;aAChC;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,cAAc;aACtB;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,4BAA4B;aACrC;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;gBACxB,aAAa,EAAE,aAAa;aAC7B;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;aACzB;YACD,4BAA4B,EAAE;gBAC5B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,MAAM;aACvB;YACD,aAAa,EAAE;gBACb,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;aACf;YACD,mBAAmB,EAAE;gBACnB,KAAK,EAAE,qBAAqB;gBAC5B,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;aACf;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;aACb;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;aACzB;YACD,+BAA+B,EAAE;gBAC/B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;gBACxB,OAAO,EAAE,2CAA2C;aACrD;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO;aACd;YACD,cAAc,EAAE;gBACd,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,OAAO;aACd;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;aAClB;YACD,oBAAoB,EAAE;gBACpB,eAAe,EAAE,aAAa;aAC/B;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,KAAK;aACjB;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,wBAAwB;aACvC;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,cAAc;gBACrB,gBAAgB,EAAE,sBAAsB;aACzC;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,MAAM;aACjB;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,wBAAwB;aAC/B;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,gBAAgB,EAAE,aAAa;gBAC/B,UAAU,EAAE,sCAAsC;gBAClD,QAAQ,EAAE,KAAK;aAChB;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;aACnB;YACD,UAAU,EAAE;gBACV,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,KAAK;gBACf,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,sCAAsC;aACrD;YACD,eAAe,EAAE;gBACf,QAAQ,EAAE,yBAAyB;gBACnC,SAAS,EAAE,yBAAyB;gBACpC,MAAM,EAAE,aAAa;aACtB;YACD,yBAAyB,EAAE;gBACzB,IAAI,EAAE,+BAA+B;gBACrC,SAAS,EAAE,yBAAyB;gBACpC,MAAM,EAAE,aAAa;aACtB;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,aAAa;QACxB,YAAY,EAAE,wBAAwB;KACvC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,CAAC;KACjR;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,QAAQ,EAAE,oBAAoB;QAC9B,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,aAAa;QACzB,QAAQ,EAAE,aAAa;QACvB,mBAAmB,EAAE,KAAK;KAC3B;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,KAAK;SAChB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,CAAC;KAC5F;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,aAAa,EAAE,cAAc;QAC7B,WAAW,EAAE,cAAc;QAC3B,UAAU,EAAE,cAAc;KAC3B;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,SAAS,EAAE,aAAa;QACxB,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,iBAAiB,EAAE,KAAK;KACzB;IACD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE,aAAa;QACvB,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,qBAAqB;QAClC,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE,SAAS;SACjB;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,8BAA8B;QACzC,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,aAAa,EAAE;gBACb,QAAQ,EAAE,wDAAwD;aACnE;SACF;KACF;IACD;;QAEI;IACJ,sDAAsD,EAAE;QACtD,KAAK,EAAE;YACL,aAAa,EAAE,2EAA2E;SAC3F;KACF;IACD;;QAEI;IACJ,yEAAyE,EAAE;QACzE,KAAK,EAAE;YACL,yBAAyB,EAAE,yGAAyG;YACpI,sBAAsB,EAAE,sGAAsG;YAC9H,yBAAyB,EAAE,wGAAwG;YACnI,kBAAkB,EAAE,iGAAiG;SACtH;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,aAAa,CAAC;KACvB;IACD;;QAEI;IACJ,oCAAoC,EAAE;QACpC,GAAG,EAAE,aAAa;QAClB,OAAO,EAAE,MAAM;KAChB;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,cAAc,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,+BAA+B,EAAE,eAAe,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,kBAAkB,CAAC;KACrZ;IACD;;QAEI;IACJ,qBAAqB,EAAE,QAAQ;IAC/B;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE,CAAC,cAAc,EAAE,yBAAyB,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,CAAC;KAC3F;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,qCAAqC;QAC5C,YAAY,EAAE,MAAM;QACpB,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,KAAK;KACjB;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC;KAC3B;IACD;;QAEI;IACJ,qCAAqC,EAAE;QACrC,gBAAgB,EAAE,KAAK;QACvB,aAAa,EAAE,KAAK;QACpB,eAAe,EAAE,KAAK;KACvB;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC;KAChE;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,SAAS,EAAE,sBAAsB;gBACjC,iBAAiB,EAAE,8BAA8B;gBACjD,WAAW,EAAE,iBAAiB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,eAAe,EAAE;gBACf,MAAM,EAAE,sBAAsB;gBAC9B,QAAQ,EAAE,MAAM;aACjB;YACD,KAAK,EAAE;gBACL,QAAQ,EAAE,sBAAsB;gBAChC,EAAE,EAAE,KAAK;aACV;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,sBAAsB;SAC3B;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,uBAAuB,EAAE,MAAM;YAC/B,uBAAuB,EAAE,MAAM;YAC/B,oBAAoB,EAAE,eAAe;YACrC,0BAA0B,EAAE,MAAM;SACnC;KACF;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,sBAAsB;QAC7B,MAAM,EAAE,sBAAsB;QAC9B,SAAS,EAAE,kBAAkB;KAC9B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,iBAAiB,EAAE,0BAA0B,CAAC;KACxd;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,KAAK;QACV,KAAK,EAAE,KAAK;QACZ,eAAe,EAAE,sCAAsC;QACvD,YAAY,EAAE,KAAK;QACnB,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,IAAI,EAAE,6CAA6C;QACnD,IAAI,EAAE,6CAA6C;KACpD;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,SAAS,EAAE,KAAK;QAChB,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,aAAa,EAAE,qBAAqB,CAAC;KACpK;IACD;;QAEI;IACJ,qCAAqC,EAAE;QACrC,KAAK,EAAE;YACL,qBAAqB,EAAE,6CAA6C;YACpE,mBAAmB,EAAE,2CAA2C;SACjE;KACF;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,YAAY;QAC5B,IAAI,EAAE,MAAM;QACZ,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,aAAa;QAC3B,cAAc,EAAE,aAAa;QAC7B,gBAAgB,EAAE,KAAK;KACxB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,OAAO,EAAE,mCAAmC;QAC5C,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,wCAAwC;QACnD,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,aAAa;QAC3B,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,MAAM;QACf,iBAAiB,EAAE,aAAa;QAChC,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,kCAAkC,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,uCAAuC,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;KAC3oB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,WAAW,EAAE,KAAK;QAClB,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,MAAM;QACnB,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,qBAAqB;QAC1B,WAAW,EAAE,YAAY;QACzB,aAAa,EAAE,WAAW;QAC1B,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK;QACpB,eAAe,EAAE,KAAK;KACvB;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,SAAS,EAAE,MAAM;KAClB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,SAAS,EAAE,aAAa;KACzB;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,iCAAiC,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,2BAA2B,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,qCAAqC,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,0CAA0C,EAAE,sCAAsC,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,eAAe,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,cAAc,EAAE,+BAA+B,EAAE,uCAAuC,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,8BAA8B,EAAE,yCAAyC,EAAE,mCAAmC,EAAE,iCAAiC,EAAE,wCAAwC,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,iCAAiC,EAAE,uBAAuB,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,mCAAmC,CAAC;KACxnD;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,aAAa,EAAE,yBAAyB,EAAE,iCAAiC,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,WAAW,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,uCAAuC,EAAE,6BAA6B,CAAC;KAC9iB;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,IAAI,EAAE,MAAM;QACZ,eAAe,EAAE,KAAK;QACtB,WAAW,EAAE,MAAM;QACnB,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,cAAc,EAAE,KAAK;QACrB,yBAAyB,EAAE,MAAM;QACjC,WAAW,EAAE,MAAM;QACnB,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,8CAA8C,EAAE;QAC9C,kBAAkB,EAAE,KAAK;QACzB,UAAU,EAAE,MAAM;KACnB;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE,CAAC,4BAA4B,EAAE,uBAAuB,EAAE,cAAc,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sCAAsC,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,yBAAyB,CAAC;KACpb;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,oBAAoB,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,0BAA0B,CAAC;KACzG;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,aAAa;QACpB,YAAY,EAAE,MAAM;QACpB,KAAK,EAAE,KAAK;QACZ,aAAa,EAAE,KAAK;QACpB,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC;KAChE;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,oCAAoC;QAC/C,OAAO,EAAE,uBAAuB;KACjC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,OAAO,EAAE,+BAA+B;QACxC,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,iBAAiB,EAAE,KAAK;QACxB,WAAW,EAAE,aAAa;QAC1B,KAAK,EAAE,sCAAsC;QAC7C,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC;KACxH;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,wBAAwB,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,CAAC;KACj1B;IACD;;QAEI;IACJ,uCAAuC,EAAE,MAAM;IAC/C;;QAEI;IACJ,qCAAqC,EAAE,MAAM;IAC7C;;QAEI;IACJ,mCAAmC,EAAE,MAAM;IAC3C;;QAEI;IACJ,iCAAiC,EAAE,MAAM;IACzC;;QAEI;IACJ,+BAA+B,EAAE,cAAc;IAC/C;;QAEI;IACJ,gCAAgC,EAAE,MAAM;IACxC;;QAEI;IACJ,gDAAgD,EAAE,eAAe;IACjE;;QAEI;IACJ,yDAAyD,EAAE,MAAM;IACjE;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,IAAI,EAAE,gCAAgC;KACvC;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;KAC/B;IACD;;QAEI;IACJ,wBAAwB,EAAE,MAAM;CACjC,CAAC"} \ No newline at end of file +{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/interfaces/lookup.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oBAAoB;AAEpB,8BAA8B;AAE9B,eAAe;IACb;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,KAAK;QAClB,IAAI,EAAE,2BAA2B;KAClC;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,MAAM;KACd;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,MAAM,EAAE,yBAAyB;QACjC,WAAW,EAAE,yBAAyB;QACtC,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,IAAI,EAAE,gCAAgC;KACvC;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,gBAAgB;YAC3B,IAAI,EAAE,gBAAgB;YACtB,UAAU,EAAE,gBAAgB;YAC5B,SAAS,EAAE,MAAM;YACjB,yBAAyB,EAAE,MAAM;SAClC;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,WAAW;KACpB;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,YAAY,EAAE,kCAAkC;aACjD;YACD,eAAe,EAAE;gBACf,aAAa,EAAE,wBAAwB;gBACvC,YAAY,EAAE,kCAAkC;aACjD;YACD,WAAW,EAAE,MAAM;YACnB,UAAU,EAAE;gBACV,OAAO,EAAE,aAAa;aACvB;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,aAAa;aACvB;YACD,QAAQ,EAAE;gBACR,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,MAAM;aACd;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,MAAM;aACrB;SACF;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,MAAM,EAAE,yBAAyB;QACjC,KAAK,EAAE,mCAAmC;QAC1C,OAAO,EAAE,0BAA0B;KACpC;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,CAAC;KAC9C;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;KACrB;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,MAAM;YACpB,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,sBAAsB;YAC9B,iBAAiB,EAAE,MAAM;YACzB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;YACxB,KAAK,EAAE,qBAAqB;YAC5B,UAAU,EAAE,6BAA6B;YACzC,aAAa,EAAE,6BAA6B;YAC5C,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,MAAM;SACvB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,QAAQ;KAChB;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE,CAAC,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,SAAS,CAAC;KACrK;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,gBAAgB,CAAC;KACnD;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,cAAc,EAAE,SAAS,CAAC;KACnC;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE;YACL,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE;gBACzB,kBAAkB,EAAE,KAAK;aAC1B;YACD,2BAA2B,EAAE,MAAM;YACnC,wBAAwB,EAAE;gBACxB,KAAK,EAAE,KAAK;aACb;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,yBAAyB;gBACrC,OAAO,EAAE,MAAM;aAChB;YACD,iBAAiB,EAAE;gBACjB,WAAW,EAAE,iBAAiB;aAC/B;SACF;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,aAAa;gBACtB,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE;gBACR,OAAO,EAAE,aAAa;gBACtB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;gBACjB,MAAM,EAAE,MAAM;aACf;YACD,UAAU,EAAE;gBACV,GAAG,EAAE,aAAa;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,UAAU,EAAE;gBACV,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,kBAAkB,EAAE;gBAClB,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;gBACjB,MAAM,EAAE,MAAM;gBACd,iBAAiB,EAAE,qCAAqC;aACzD;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;aACnB;YACD,MAAM,EAAE;gBACN,MAAM,EAAE,MAAM;aACf;YACD,SAAS,EAAE;gBACT,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,aAAa;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,MAAM;gBACX,IAAI,EAAE,MAAM;aACb;SACF;KACF;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;KAC5B;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,kBAAkB,EAAE;gBAClB,GAAG,EAAE,aAAa;gBAClB,SAAS,EAAE,MAAM;gBACjB,GAAG,EAAE,MAAM;aACZ;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,UAAU,EAAE,sCAAsC;aACnD;YACD,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,qBAAqB;gBAC1B,IAAI,EAAE,aAAa;aACpB;YACD,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,UAAU,EAAE,sCAAsC;aACnD;SACF;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,aAAa,EAAE,kBAAkB;aAClC;YACD,iBAAiB,EAAE;gBACjB,SAAS,EAAE,aAAa;aACzB;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,aAAa;aACzB;YACD,oBAAoB,EAAE;gBACpB,iBAAiB,EAAE,KAAK;aACzB;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,MAAM;aACnB;YACD,cAAc,EAAE;gBACd,SAAS,EAAE,aAAa;gBACxB,OAAO,EAAE,MAAM;aAChB;YACD,oBAAoB,EAAE;gBACpB,SAAS,EAAE,aAAa;gBACxB,OAAO,EAAE,MAAM;aAChB;YACD,gBAAgB,EAAE;gBAChB,SAAS,EAAE,aAAa;aACzB;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,GAAG,EAAE,aAAa;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;aAChB;YACD,0BAA0B,EAAE;gBAC1B,SAAS,EAAE,aAAa;aACzB;SACF;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,UAAU,EAAE;gBACV,YAAY,EAAE,KAAK;aACpB;SACF;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,eAAe,EAAE;gBACf,WAAW,EAAE,SAAS;aACvB;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,OAAO,EAAE,2BAA2B;aACrC;YACD,IAAI,EAAE;gBACJ,MAAM,EAAE,sBAAsB;gBAC9B,WAAW,EAAE,sBAAsB;gBACnC,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,SAAS;aACrB;YACD,kBAAkB,EAAE;gBAClB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,sBAAsB;aACjC;YACD,QAAQ,EAAE;gBACR,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,gBAAgB,EAAE;gBAChB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,yBAAyB;gBACvC,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,mBAAmB,EAAE;gBACnB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,kBAAkB,EAAE;gBAClB,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;gBACd,gBAAgB,EAAE,8BAA8B;aACjD;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,sBAAsB;gBAC9B,MAAM,EAAE,oBAAoB;aAC7B;YACD,qBAAqB,EAAE;gBACrB,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,uBAAuB,EAAE;gBACvB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,oBAAoB,EAAE;gBACpB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,kBAAkB;aAC1B;YACD,yBAAyB,EAAE;gBACzB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,cAAc,EAAE;gBACd,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,KAAK;gBACd,eAAe,EAAE,sBAAsB;gBACvC,kBAAkB,EAAE,8BAA8B;aACnD;YACD,oBAAoB,EAAE;gBACpB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,sBAAsB,EAAE;gBACtB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,wBAAwB,EAAE;gBACxB,WAAW,EAAE,sBAAsB;gBACnC,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE;gBACR,MAAM,EAAE,sBAAsB;gBAC9B,IAAI,EAAE,yBAAyB;aAChC;YACD,aAAa,EAAE;gBACb,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,sBAAsB;gBAC9B,MAAM,EAAE,oBAAoB;aAC7B;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,KAAK;aACf;SACF;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,QAAQ,EAAE;gBACR,IAAI,EAAE,yBAAyB;aAChC;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,yBAAyB;gBAC/B,KAAK,EAAE,kBAAkB;aAC1B;YACD,KAAK,EAAE;gBACL,KAAK,EAAE,kBAAkB;aAC1B;SACF;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,aAAa,EAAE,MAAM;YACrB,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE,MAAM;YACjC,YAAY,EAAE,MAAM;YACpB,qBAAqB,EAAE,MAAM;YAC7B,SAAS,EAAE,MAAM;YACjB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,MAAM;YACrB,qBAAqB,EAAE,MAAM;YAC7B,eAAe,EAAE,MAAM;YACvB,kBAAkB,EAAE,MAAM;YAC1B,qBAAqB,EAAE,MAAM;YAC7B,sBAAsB,EAAE,MAAM;YAC9B,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;YACtB,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,IAAI,EAAE,KAAK;YACX,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,mBAAmB,EAAE,MAAM;YAC3B,oBAAoB,EAAE,MAAM;YAC5B,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,MAAM;YACtB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,MAAM;YACjB,YAAY,EAAE,MAAM;YACpB,UAAU,EAAE,MAAM;YAClB,cAAc,EAAE,MAAM;YACtB,mBAAmB,EAAE,MAAM;YAC3B,kBAAkB,EAAE,yBAAyB;YAC7C,OAAO,EAAE,MAAM;YACf,mBAAmB,EAAE,MAAM;YAC3B,iBAAiB,EAAE,MAAM;SAC1B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,uBAAuB;KAClC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;YAClB,EAAE,EAAE,cAAc;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,uCAAuC;gBAChD,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,uCAAuC;gBAChD,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,uCAAuC;gBAChD,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE;gBACV,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,SAAS;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,qBAAqB;gBACzB,IAAI,EAAE,uBAAuB;aAC9B;YACD,eAAe,EAAE,+BAA+B;SACjD;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE;gBACR,OAAO,EAAE,cAAc;aACxB;YACD,WAAW,EAAE,MAAM;YACnB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,QAAQ;YACjB,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,MAAM;SACjB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,OAAO,EAAE;gBACP,KAAK,EAAE,cAAc;aACtB;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,kBAAkB,EAAE;gBAClB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE,8BAA8B;IAC/C;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,aAAa,EAAE,yBAAyB;YACxC,qBAAqB,EAAE,yBAAyB;YAChD,sBAAsB,EAAE,yBAAyB;YACjD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,sBAAsB;gBAChC,SAAS,EAAE,yBAAyB;gBACpC,OAAO,EAAE,8BAA8B;aACxC;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,yBAAyB;gBACjC,WAAW,EAAE,sBAAsB;aACpC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,yBAAyB;gBACjC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,yBAAyB;gBAC9C,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,uBAAuB;YACtC,WAAW,EAAE,+BAA+B;YAC5C,YAAY,EAAE;gBACZ,MAAM,EAAE,8BAA8B;gBACtC,WAAW,EAAE,sBAAsB;aACpC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,8BAA8B;gBACtC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,8BAA8B;gBACpC,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,8BAA8B;gBACtC,OAAO,EAAE,sBAAsB;gBAC/B,GAAG,EAAE,iBAAiB;aACvB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,8BAA8B;gBACtC,IAAI,EAAE,sBAAsB;gBAC5B,GAAG,EAAE,iBAAiB;aACvB;YACD,aAAa,EAAE;gBACb,YAAY,EAAE,+BAA+B;gBAC7C,MAAM,EAAE,8BAA8B;aACvC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,mBAAmB;gBACzB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,iBAAiB;YAClC,WAAW,EAAE,iBAAiB;YAC9B,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,yBAAyB;gBACjC,MAAM,EAAE,sBAAsB;aAC/B;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,kBAAkB,EAAE,MAAM;YAC1B,SAAS,EAAE,yBAAyB;YACpC,WAAW,EAAE,yBAAyB;YACtC,YAAY,EAAE,8BAA8B;YAC5C,WAAW,EAAE,gCAAgC;YAC7C,oBAAoB,EAAE,qBAAqB;YAC3C,WAAW,EAAE;gBACX,UAAU,EAAE,OAAO;gBACnB,YAAY,EAAE,+BAA+B;aAC9C;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,OAAO;gBACnB,UAAU,EAAE,cAAc;gBAC1B,aAAa,EAAE,cAAc;aAC9B;YACD,oBAAoB,EAAE,+BAA+B;YACrD,mBAAmB,EAAE,MAAM;YAC3B,eAAe,EAAE,sBAAsB;YACvC,aAAa,EAAE;gBACb,OAAO,EAAE,+BAA+B;gBACxC,WAAW,EAAE,uBAAuB;gBACpC,GAAG,EAAE,iBAAiB;aACvB;YACD,SAAS,EAAE;gBACT,KAAK,EAAE,mBAAmB;gBAC1B,QAAQ,EAAE,sBAAsB;aACjC;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,sBAAsB;aAC/B;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,mBAAmB;gBAC1B,KAAK,EAAE,sBAAsB;aAC9B;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,sBAAsB;aAC/B;YACD,WAAW,EAAE;gBACX,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,sBAAsB;YACnC,eAAe,EAAE;gBACf,WAAW,EAAE,kBAAkB;gBAC/B,WAAW,EAAE,8BAA8B;aAC5C;SACF;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE,wBAAwB;IACjD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,EAAE,EAAE,0BAA0B;QAC9B,GAAG,EAAE,8BAA8B;KACpC;IACD;;QAEI;IACJ,wBAAwB,EAAE,sBAAsB;IAChD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,gCAAgC;SAC9C;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,yBAAyB;YACjC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,6BAA6B;YAC1C,cAAc,EAAE,qBAAqB;SACtC;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,OAAO;QACnB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;KACtB;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,KAAK,EAAE,OAAO;YACd,cAAc,EAAE,OAAO;SACxB;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,KAAK,CAAC;KAC1D;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,OAAO,EAAE,OAAO;KACjB;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,WAAW,EAAE,sBAAsB;QACnC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,QAAQ,EAAE,yBAAyB;YACnC,IAAI,EAAE,4BAA4B;SACnC;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,0BAA0B;gBAC9B,GAAG,EAAE,kCAAkC;aACxC;YACD,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE;gBACZ,EAAE,EAAE,0BAA0B;gBAC9B,GAAG,EAAE,kCAAkC;gBACvC,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,yBAAyB;SACnC;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,4BAA4B;YAChC,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,4BAA4B;YAChC,EAAE,EAAE,yBAAyB;SAC9B;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE,sBAAsB;IAClD;;QAEI;IACJ,eAAe,EAAE;QACf,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,4BAA4B;KAClC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,OAAO;SAClB;KACF;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,6BAA6B;KACxC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,+BAA+B;YACnC,EAAE,EAAE,6CAA6C;YACjD,EAAE,EAAE,2DAA2D;YAC/D,EAAE,EAAE,yEAAyE;YAC7E,EAAE,EAAE,uFAAuF;YAC3F,EAAE,EAAE,qGAAqG;YACzG,EAAE,EAAE,mHAAmH;SACxH;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,gBAAgB;gBACzB,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,gBAAgB;gBACzB,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,gBAAgB;gBACzB,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE,OAAO;YACnB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,aAAa;gBACjB,IAAI,EAAE,eAAe;aACtB;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;SACf;KACF;IACD;;QAEI;IACJ,WAAW,EAAE;QACX,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,MAAM;SACjB;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,OAAO,EAAE;gBACP,KAAK,EAAE,cAAc;aACtB;YACD,QAAQ,EAAE;gBACR,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;YACD,kBAAkB,EAAE;gBAClB,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,8BAA8B;SAC5C;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,OAAO;SACd;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE,sBAAsB;IAClD;;QAEI;IACJ,eAAe,EAAE;QACf,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,4BAA4B;KAClC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,QAAQ,EAAE,2BAA2B;YACrC,QAAQ,EAAE,SAAS;SACpB;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,gBAAgB;KAC3B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,+BAA+B;YACnC,EAAE,EAAE,6CAA6C;YACjD,EAAE,EAAE,2DAA2D;YAC/D,EAAE,EAAE,yEAAyE;YAC7E,EAAE,EAAE,uFAAuF;YAC3F,EAAE,EAAE,qGAAqG;YACzG,EAAE,EAAE,mHAAmH;SACxH;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,SAAS,EAAE,cAAc;YACzB,WAAW,EAAE;gBACX,OAAO,EAAE,gCAAgC;gBACzC,EAAE,EAAE,SAAS;aACd;YACD,cAAc,EAAE;gBACd,OAAO,EAAE,gCAAgC;gBACzC,KAAK,EAAE,cAAc;aACtB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,gCAAgC;gBACzC,GAAG,EAAE,SAAS;aACf;YACD,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE;gBACV,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,SAAS;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,EAAE,EAAE,qBAAqB;gBACzB,IAAI,EAAE,uBAAuB;aAC9B;YACD,eAAe,EAAE,wBAAwB;SAC1C;KACF;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,SAAS;aACrB;YACD,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE;gBACR,OAAO,EAAE,cAAc;aACxB;YACD,WAAW,EAAE,MAAM;YACnB,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,8BAA8B;SAC5C;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;SACnB;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,oBAAoB;YACxB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,2BAA2B;YAC/B,EAAE,EAAE,sBAAsB;SAC3B;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,aAAa,EAAE,SAAS;YACxB,kBAAkB,EAAE,SAAS;YAC7B,gBAAgB,EAAE,qCAAqC;SACxD;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,gBAAgB,EAAE;gBAChB,EAAE,EAAE,MAAM;gBACV,MAAM,EAAE,6CAA6C;gBACrD,KAAK,EAAE,yCAAyC;aACjD;YACD,SAAS,EAAE;gBACT,EAAE,EAAE,MAAM;gBACV,MAAM,EAAE,6CAA6C;gBACrD,UAAU,EAAE,yBAAyB;gBACrC,OAAO,EAAE,MAAM;aAChB;YACD,kBAAkB,EAAE;gBAClB,EAAE,EAAE,SAAS;gBACb,MAAM,EAAE,6CAA6C;gBACrD,SAAS,EAAE,KAAK;gBAChB,YAAY,EAAE,KAAK;aACpB;YACD,UAAU,EAAE;gBACV,MAAM,EAAE,6CAA6C;gBACrD,KAAK,EAAE,KAAK;aACb;SACF;KACF;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,KAAK;SACf;KACF;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,MAAM;YACnB,UAAU,EAAE,yBAAyB;YACrC,KAAK,EAAE,MAAM;YACb,iBAAiB,EAAE,MAAM;SAC1B;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,uBAAuB,EAAE;gBACvB,GAAG,EAAE,aAAa;gBAClB,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,wCAAwC;aACpD;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,wCAAwC;aACpD;YACD,uBAAuB,EAAE;gBACvB,GAAG,EAAE,aAAa;gBAClB,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;aAChB;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,cAAc,EAAE,YAAY;gBAC5B,QAAQ,EAAE,KAAK;aAChB;YACD,qBAAqB,EAAE;gBACrB,GAAG,EAAE,aAAa;aACnB;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,eAAe,EAAE;gBACf,GAAG,EAAE,aAAa;gBAClB,UAAU,EAAE,yCAAyC;gBACrD,WAAW,EAAE,KAAK;gBAClB,WAAW,EAAE,KAAK;gBAClB,0BAA0B,EAAE,KAAK;aAClC;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,MAAM;gBAClB,aAAa,EAAE,MAAM;aACtB;SACF;KACF;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,YAAY;KACxB;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,KAAK,EAAE;YACL,qBAAqB,EAAE,MAAM;YAC7B,mBAAmB,EAAE,MAAM;SAC5B;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,OAAO;gBACb,YAAY,EAAE,aAAa;gBAC3B,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,MAAM;aACjB;YACD,oBAAoB,EAAE;gBACpB,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,aAAa;gBAC3B,OAAO,EAAE,MAAM;aAChB;YACD,2BAA2B,EAAE;gBAC3B,GAAG,EAAE,aAAa;gBAClB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,KAAK;aACpB;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,GAAG,EAAE,aAAa;gBAClB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,YAAY;aACtB;YACD,6BAA6B,EAAE;gBAC7B,OAAO,EAAE,iDAAiD;aAC3D;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,cAAc,EAAE,YAAY;gBAC5B,KAAK,EAAE,aAAa;gBACpB,KAAK,EAAE,KAAK;aACb;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,WAAW;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,OAAO,EAAE,MAAM;aAChB;YACD,qBAAqB,EAAE;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,qBAAqB,EAAE;gBACrB,OAAO,EAAE,MAAM;aAChB;YACD,yBAAyB,EAAE;gBACzB,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,MAAM;gBACf,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,OAAO;aAClB;YACD,wBAAwB,EAAE;gBACxB,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,MAAM;aAChB;YACD,sCAAsC,EAAE;gBACtC,MAAM,EAAE,wCAAwC;gBAChD,OAAO,EAAE,MAAM;aAChB;YACD,0BAA0B,EAAE;gBAC1B,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,MAAM;gBACf,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,MAAM;aAChB;YACD,8BAA8B,EAAE;gBAC9B,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;gBACb,gBAAgB,EAAE,MAAM;aACzB;YACD,2CAA2C,EAAE;gBAC3C,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,gBAAgB,EAAE,MAAM;aACzB;YACD,4BAA4B,EAAE;gBAC5B,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;aACd;YACD,wBAAwB,EAAE;gBACxB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;aACjB;YACD,kBAAkB,EAAE;gBAClB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd;YACD,kBAAkB,EAAE;gBAClB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM;aACd;YACD,iCAAiC,EAAE;gBACjC,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,MAAM;aACjB;SACF;KACF;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,KAAK,EAAE,MAAM;QACb,SAAS,EAAE,qCAAqC;KACjD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,QAAQ,EAAE,iDAAiD;YAC3D,QAAQ,EAAE,iDAAiD;YAC3D,MAAM,EAAE,+CAA+C;SACxD;KACF;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,+CAA+C,EAAE;QAC/C,QAAQ,EAAE,0DAA0D;QACpE,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,KAAK,EAAE,CAAC,wBAAwB,EAAE,sBAAsB,EAAE,eAAe,CAAC;KAC3E;IACD;;QAEI;IACJ,6CAA6C,EAAE;QAC7C,QAAQ,EAAE,oCAAoC;QAC9C,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,aAAa;KACrB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,KAAK,EAAE;YACL,SAAS,EAAE,aAAa;YACxB,KAAK,EAAE,MAAM;SACd;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,GAAG,EAAE,aAAa;gBAClB,aAAa,EAAE,MAAM;aACtB;YACD,aAAa,EAAE;gBACb,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,yBAAyB;aACjC;YACD,gBAAgB,EAAE;gBAChB,gBAAgB,EAAE,KAAK;gBACvB,IAAI,EAAE,MAAM;aACb;YACD,sBAAsB,EAAE;gBACtB,gBAAgB,EAAE,KAAK;gBACvB,UAAU,EAAE,iDAAiD;aAC9D;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,qBAAqB,EAAE,KAAK;aAC7B;YACD,4BAA4B,EAAE;gBAC5B,QAAQ,EAAE,MAAM;aACjB;YACD,4BAA4B,EAAE;gBAC5B,WAAW,EAAE,KAAK;gBAClB,qBAAqB,EAAE,KAAK;gBAC5B,QAAQ,EAAE,MAAM;gBAChB,oBAAoB,EAAE,qBAAqB;aAC5C;YACD,gBAAgB,EAAE;gBAChB,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,yCAAyC;gBACpD,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,MAAM;aACf;SACF;KACF;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,WAAW,EAAE,gCAAgC;QAC7C,SAAS,EAAE,4CAA4C;KACxD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,YAAY,EAAE,YAAY;KAC3B;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE,gCAAgC;QACvC,cAAc,EAAE,KAAK;KACtB;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,YAAY,EAAE,6BAA6B;QAC3C,KAAK,EAAE,gCAAgC;KACxC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,cAAc;QACxB,WAAW,EAAE,4BAA4B;KAC1C;IACD;;QAEI;IACJ,0BAA0B,EAAE,SAAS;IACrC;;QAEI;IACJ,2BAA2B,EAAE,MAAM;IACnC;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,iCAAiC,EAAE;gBACjC,cAAc,EAAE,MAAM;gBACtB,SAAS,EAAE,KAAK;gBAChB,eAAe,EAAE,KAAK;aACvB;SACF;KACF;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE;YACL,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,OAAO,EAAE,MAAM;aAChB;YACD,6BAA6B,EAAE;gBAC7B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;aACnB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,cAAc,EAAE,KAAK;aACtB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,iBAAiB,EAAE,KAAK;aACzB;YACD,+BAA+B,EAAE;gBAC/B,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;aACnB;YACD,oBAAoB,EAAE;gBACpB,WAAW,EAAE,aAAa;gBAC1B,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,MAAM;aACf;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,MAAM;gBAClB,kBAAkB,EAAE,KAAK;gBACzB,wBAAwB,EAAE,MAAM;aACjC;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;aACnB;YACD,aAAa,EAAE;gBACb,GAAG,EAAE,aAAa;aACnB;YACD,WAAW,EAAE;gBACX,GAAG,EAAE,aAAa;aACnB;SACF;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,aAAa;aACzB;YACD,qBAAqB,EAAE;gBACrB,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;aACd;YACD,UAAU,EAAE;gBACV,OAAO,EAAE,aAAa;gBACtB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;aACd;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,aAAa;gBACtB,KAAK,EAAE,aAAa;aACrB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,aAAa;aACrB;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;aAClB;YACD,MAAM,EAAE;gBACN,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,aAAa;gBACnB,EAAE,EAAE,aAAa;aAClB;YACD,MAAM,EAAE;gBACN,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,MAAM;aACvB;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;aAClB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,aAAa;aACxB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,qBAAqB;gBAC7B,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,qBAAqB;aAC/B;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,aAAa;gBACvB,QAAQ,EAAE,aAAa;aACxB;YACD,iBAAiB,EAAE;gBACjB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;gBACpB,QAAQ,EAAE,aAAa;aACxB;YACD,qBAAqB,EAAE;gBACrB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,aAAa;aACrB;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;aAClB;YACD,qBAAqB,EAAE;gBACrB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,OAAO;aACd;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;aAClB;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO;aACd;YACD,mBAAmB,EAAE;gBACnB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK;gBACjB,eAAe,EAAE,UAAU;aAC5B;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;gBACd,SAAS,EAAE,8BAA8B;aAC1C;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,GAAG,EAAE,OAAO;gBACZ,SAAS,EAAE,8BAA8B;aAC1C;YACD,2BAA2B,EAAE;gBAC3B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,aAAa;aACxB;YACD,6BAA6B,EAAE;gBAC7B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,aAAa;aACxB;YACD,0BAA0B,EAAE;gBAC1B,GAAG,EAAE,aAAa;gBAClB,eAAe,EAAE,aAAa;aAC/B;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,KAAK;aACjB;YACD,6BAA6B,EAAE;gBAC7B,UAAU,EAAE,KAAK;aAClB;YACD,2BAA2B,EAAE;gBAC3B,MAAM,EAAE,aAAa;aACtB;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,MAAM;gBACb,gBAAgB,EAAE,qBAAqB;aACxC;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,aAAa;aACrB;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,aAAa;gBACvB,MAAM,EAAE,MAAM;aACf;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,aAAa,EAAE;gBACb,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,WAAW,EAAE;gBACX,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,KAAK;gBACf,aAAa,EAAE,aAAa;gBAC5B,kBAAkB,EAAE,KAAK;gBACzB,YAAY,EAAE,KAAK;gBACnB,iBAAiB,EAAE,aAAa;gBAChC,KAAK,EAAE,sCAAsC;gBAC7C,QAAQ,EAAE,KAAK;aAChB;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,SAAS,EAAE,8BAA8B;aAC1C;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,aAAa;gBACnB,SAAS,EAAE,4BAA4B;gBACvC,KAAK,EAAE,OAAO;aACf;SACF;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE;YACL,MAAM,EAAE,MAAM;YACd,eAAe,EAAE,MAAM;YACvB,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,aAAa;SACvB;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,0BAA0B;KACtC;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;KAC3B;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,WAAW,EAAE,KAAK;YAClB,gBAAgB,EAAE,MAAM;SACzB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,GAAG,EAAE,2DAA2D;gBAChE,QAAQ,EAAE,qEAAqE;gBAC/E,QAAQ,EAAE,qEAAqE;aAChF;SACF;KACF;IACD;;QAEI;IACJ,yDAAyD,EAAE;QACzD,KAAK,EAAE;YACL,aAAa,EAAE,8EAA8E;SAC9F;KACF;IACD;;QAEI;IACJ,4EAA4E,EAAE;QAC5E,KAAK,EAAE,CAAC,2BAA2B,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,oBAAoB,CAAC;KAClH;IACD;;QAEI;IACJ,wFAAwF,EAAE,MAAM;IAChG;;QAEI;IACJ,qFAAqF,EAAE,MAAM;IAC7F;;QAEI;IACJ,wFAAwF,EAAE,MAAM;IAChG;;QAEI;IACJ,iFAAiF,EAAE,MAAM;IACzF;;QAEI;IACJ,2DAA2D,EAAE;QAC3D,KAAK,EAAE;YACL,aAAa,EAAE,gFAAgF;SAChG;KACF;IACD;;QAEI;IACJ,8EAA8E,EAAE;QAC9E,KAAK,EAAE;YACL,yBAAyB,EAAE,MAAM;YACjC,sBAAsB,EAAE,MAAM;YAC9B,yBAAyB,EAAE,KAAK;YAChC,kBAAkB,EAAE,KAAK;SAC1B;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,cAAc,EAAE,KAAK;YACrB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;SACvB;KACF;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,WAAW,EAAE,cAAc;QAC3B,QAAQ,EAAE,MAAM;KACjB;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,MAAM;KACrB;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,MAAM,EAAE,OAAO;aAChB;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,KAAK;aACb;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,OAAO;aACd;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,OAAO;aACd;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,oBAAoB;aAC5B;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,YAAY;aACpB;YACD,WAAW,EAAE;gBACX,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK;aACf;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE,OAAO;aAChB;YACD,SAAS,EAAE,MAAM;YACjB,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;aACjB;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,MAAM;aACjB;YACD,wBAAwB,EAAE;gBACxB,IAAI,EAAE,OAAO;aACd;SACF;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,SAAS,EAAE,yBAAyB;QACpC,QAAQ,EAAE,yBAAyB;QACnC,QAAQ,EAAE,qDAAqD;KAChE;IACD;;QAEI;IACJ,mDAAmD,EAAE;QACnD,MAAM,EAAE,kCAAkC;QAC1C,WAAW,EAAE,kCAAkC;QAC/C,SAAS,EAAE,kCAAkC;KAC9C;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,aAAa,EAAE,yBAAyB;QACxC,YAAY,EAAE,iCAAiC;QAC/C,QAAQ,EAAE,iCAAiC;QAC3C,QAAQ,EAAE,iCAAiC;KAC5C;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,GAAG,EAAE,yCAAyC;KAC/C;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,MAAM,EAAE,KAAK;QACb,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,KAAK;KACjB;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,MAAM;QAChB,gBAAgB,EAAE,KAAK;QACvB,WAAW,EAAE,KAAK;QAClB,WAAW,EAAE,KAAK;QAClB,IAAI,EAAE,mBAAmB;QACzB,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,IAAI;KACnB;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE,CAAC,iBAAiB,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,cAAc,EAAE,6BAA6B,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACxN;IACD;;QAEI;IACJ,qDAAqD,EAAE;QACrD,aAAa,EAAE,4DAA4D;QAC3E,YAAY,EAAE,cAAc;QAC5B,qBAAqB,EAAE,4CAA4C;KACpE;IACD;;QAEI;IACJ,0DAA0D,EAAE;QAC1D,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,+EAA+E;KAC9F;IACD;;QAEI;IACJ,8DAA8D,EAAE;QAC9D,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,kCAAkC,EAAE;QAClC,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;KAC5B;IACD;;QAEI;IACJ,2DAA2D,EAAE;QAC3D,aAAa,EAAE,4DAA4D;QAC3E,aAAa,EAAE,aAAa;QAC5B,qBAAqB,EAAE,4CAA4C;KACpE;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,UAAU,EAAE,OAAO;QACnB,iBAAiB,EAAE,KAAK;QACxB,sBAAsB,EAAE,MAAM;QAC9B,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,KAAK,EAAE,CAAC,SAAS,CAAC;KACnB;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,SAAS,EAAE,iBAAiB;KAC7B;IACD;;QAEI;IACJ,oEAAoE,EAAE;QACpE,UAAU,EAAE,MAAM;QAClB,mCAAmC,EAAE,mFAAmF;QACxH,eAAe,EAAE,oDAAoD;QACrE,cAAc,EAAE,oDAAoD;KACrE;IACD;;QAEI;IACJ,iFAAiF,EAAE;QACjF,cAAc,EAAE,KAAK;QACrB,aAAa,EAAE,KAAK;KACrB;IACD;;QAEI;IACJ,uCAAuC,EAAE;QACvC,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,cAAc;KACxB;IACD;;QAEI;IACJ,6CAA6C,EAAE;QAC7C,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,KAAK;QACtB,mBAAmB,EAAE,KAAK;QAC1B,kBAAkB,EAAE,KAAK;QACzB,oBAAoB,EAAE,KAAK;QAC3B,+BAA+B,EAAE,KAAK;QACtC,6BAA6B,EAAE,KAAK;QACpC,yBAAyB,EAAE,KAAK;QAChC,sBAAsB,EAAE,KAAK;QAC7B,kBAAkB,EAAE,oDAAoD;KACzE;IACD;;QAEI;IACJ,kDAAkD,EAAE;QAClD,iBAAiB,EAAE,KAAK;QACxB,kBAAkB,EAAE,KAAK;KAC1B;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE;YACL,mBAAmB,EAAE;gBACnB,IAAI,EAAE,yDAAyD;aAChE;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,OAAO;aACjB;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,MAAM;gBAChB,YAAY,EAAE,MAAM;aACrB;YACD,wBAAwB,EAAE;gBACxB,IAAI,EAAE,OAAO;aACd;SACF;KACF;IACD;;QAEI;IACJ,uDAAuD,EAAE;QACvD,cAAc,EAAE,6CAA6C;QAC7D,eAAe,EAAE,oBAAoB;QACrC,gBAAgB,EAAE,mDAAmD;QACrE,kBAAkB,EAAE,8DAA8D;KACnF;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,OAAO;KACb;IACD;;QAEI;IACJ,wCAAwC,EAAE;QACxC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,QAAQ,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACrL;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,GAAG,EAAE;gBACH,GAAG,EAAE,cAAc;aACpB;SACF;KACF;IACD;;QAEI;IACJ,wBAAwB,EAAE,MAAM;IAChC;;QAEI;IACJ,yBAAyB,EAAE;QACzB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,uBAAuB;KACjC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC;KAC9B;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,EAAE,EAAE,QAAQ;QACZ,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,kCAAkC,EAAE;QAClC,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,kCAAkC;YAC7C,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,MAAM;YAClB,cAAc,EAAE,gCAAgC;SACjD;KACF;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,wBAAwB,EAAE,eAAe,CAAC;KACnD;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,sBAAsB,CAAC;KAChC;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,EAAE,EAAE,MAAM;QACV,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,oBAAoB,EAAE;gBACpB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,SAAS,EAAE,MAAM;YACjB,cAAc,EAAE;gBACd,MAAM,EAAE,cAAc;gBACtB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,mBAAmB,EAAE;gBACnB,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,eAAe;aACvB;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,cAAc;gBACpB,SAAS,EAAE,MAAM;aAClB;YACD,eAAe,EAAE;gBACf,GAAG,EAAE,cAAc;gBACnB,MAAM,EAAE,MAAM;aACf;YACD,gBAAgB,EAAE;gBAChB,GAAG,EAAE,kBAAkB;aACxB;YACD,SAAS,EAAE,MAAM;YACjB,iBAAiB,EAAE;gBACjB,GAAG,EAAE,cAAc;gBACnB,OAAO,EAAE,eAAe;aACzB;YACD,2BAA2B,EAAE;gBAC3B,SAAS,EAAE,mCAAmC;gBAC9C,KAAK,EAAE,eAAe;aACvB;YACD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,SAAS,EAAE,MAAM;aAClB;SACF;KACF;IACD;;QAEI;IACJ,iCAAiC,EAAE;QACjC,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;KAChC;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE,CAAC,gBAAgB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,eAAe,EAAE,yBAAyB,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,WAAW,CAAC;KACpP;IACD;;QAEI;IACJ,gCAAgC,EAAE;QAChC,KAAK,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;KAC3B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,IAAI,EAAE;gBACJ,IAAI,EAAE,MAAM;aACb;YACD,qBAAqB,EAAE;gBACrB,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,yBAAyB;aAClC;YACD,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,cAAc;aACrB;YACD,OAAO,EAAE;gBACP,GAAG,EAAE,cAAc;gBACnB,IAAI,EAAE,MAAM;aACb;YACD,UAAU,EAAE,MAAM;SACnB;KACF;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE;YACL,iBAAiB,EAAE;gBACjB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,kBAAkB;aACzB;YACD,sBAAsB,EAAE;gBACtB,GAAG,EAAE,KAAK;aACX;YACD,kBAAkB,EAAE;gBAClB,IAAI,EAAE,MAAM;aACb;YACD,qBAAqB,EAAE,MAAM;YAC7B,YAAY,EAAE,MAAM;YACpB,gBAAgB,EAAE;gBAChB,GAAG,EAAE,aAAa;aACnB;YACD,mBAAmB,EAAE;gBACnB,GAAG,EAAE,aAAa;aACnB;YACD,WAAW,EAAE;gBACX,UAAU,EAAE,MAAM;aACnB;YACD,mBAAmB,EAAE;gBACnB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,aAAa;aACtB;SACF;KACF;IACD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,KAAK,EAAE;YACL,QAAQ,EAAE;gBACR,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,KAAK,EAAE,8BAA8B;gBACrC,KAAK,EAAE,OAAO;aACf;YACD,UAAU,EAAE,MAAM;SACnB;KACF;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,IAAI,EAAE,wCAAwC;KAC/C;IACD;;QAEI;IACJ,sCAAsC,EAAE,SAAS;IACjD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,qBAAqB,EAAE,MAAM;YAC7B,oBAAoB,EAAE,MAAM;YAC5B,wBAAwB,EAAE;gBACxB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE;oBACN,IAAI,EAAE,KAAK;iBACZ;gBACD,IAAI,EAAE,KAAK;aACZ;SACF;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE;gBACJ,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,iBAAiB;aAC3B;YACD,eAAe,EAAE;gBACf,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;aACpB;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;aACpB;YACD,OAAO,EAAE;gBACP,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,yBAAyB;aACrC;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,KAAK;aACf;YACD,yBAAyB,EAAE;gBACzB,eAAe,EAAE,aAAa;aAC/B;YACD,8BAA8B,EAAE;gBAC9B,QAAQ,EAAE,sBAAsB;aACjC;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,sBAAsB;aACjC;YACD,+BAA+B,EAAE;gBAC/B,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,uBAAuB,EAAE;gBACvB,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,gBAAgB,EAAE;gBAChB,SAAS,EAAE,MAAM;aAClB;YACD,eAAe,EAAE;gBACf,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,kBAAkB;aAChC;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE,oBAAoB;gBAC5B,WAAW,EAAE,sBAAsB;aACpC;YACD,mCAAmC,EAAE;gBACnC,IAAI,EAAE,sBAAsB;gBAC5B,MAAM,EAAE,oBAAoB;gBAC5B,kBAAkB,EAAE,6CAA6C;gBACjE,YAAY,EAAE,qBAAqB;gBACnC,gBAAgB,EAAE,6CAA6C;gBAC/D,eAAe,EAAE,iBAAiB;gBAClC,WAAW,EAAE,kBAAkB;aAChC;SACF;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,iBAAiB;SACtB;KACF;IACD;;QAEI;IACJ,QAAQ,EAAE,uBAAuB;IACjC;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,aAAa,EAAE,4BAA4B;YAC3C,qBAAqB,EAAE,4BAA4B;YACnD,sBAAsB,EAAE,4BAA4B;YACpD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,eAAe;gBACzB,SAAS,EAAE,cAAc;aAC1B;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,4BAA4B;gBACpC,WAAW,EAAE,oBAAoB;aAClC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,4BAA4B;gBACpC,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,cAAc;gBACnC,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,6BAA6B;YAC5C,WAAW,EAAE;gBACX,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,oBAAoB;gBAC1B,iBAAiB,EAAE,cAAc;aAClC;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,cAAc;gBACzB,WAAW,EAAE,oBAAoB;aAClC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,iCAAiC;gBACzC,SAAS,EAAE,cAAc;gBACzB,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,4BAA4B;aACtC;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,iCAAiC;gBACzC,OAAO,EAAE,oBAAoB;gBAC7B,GAAG,EAAE,UAAU;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,oBAAoB;gBAC1B,GAAG,EAAE,UAAU;aAChB;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,oBAAoB;gBAC1B,MAAM,EAAE,iCAAiC;gBACzC,iBAAiB,EAAE,cAAc;aAClC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,UAAU;YACvB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,4BAA4B;gBACpC,MAAM,EAAE,oBAAoB;aAC7B;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,cAAc;aAClC;YACD,kBAAkB,EAAE,MAAM;SAC3B;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,4BAA4B;YACpC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;SACf;KACF;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,aAAa,EAAE,MAAM;YACrB,wBAAwB,EAAE,MAAM;YAChC,yBAAyB,EAAE,MAAM;YACjC,iBAAiB,EAAE,MAAM;YACzB,0BAA0B,EAAE,MAAM;YAClC,SAAS,EAAE,MAAM;YACjB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,MAAM;YACrB,qBAAqB,EAAE,MAAM;YAC7B,eAAe,EAAE,MAAM;YACvB,kBAAkB,EAAE,MAAM;YAC1B,qBAAqB,EAAE,MAAM;YAC7B,sBAAsB,EAAE,MAAM;YAC9B,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,MAAM;YAClB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,MAAM;YACtB,gBAAgB,EAAE,MAAM;YACxB,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,IAAI,EAAE,KAAK;YACX,mBAAmB,EAAE,MAAM;YAC3B,kBAAkB,EAAE,KAAK;YACzB,OAAO,EAAE,MAAM;YACf,mBAAmB,EAAE,MAAM;SAC5B;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,KAAK,CAAC;KAC1D;IACD;;QAEI;IACJ,+BAA+B,EAAE;QAC/B,KAAK,EAAE;YACL,QAAQ,EAAE,4BAA4B;YACtC,IAAI,EAAE,+BAA+B;SACtC;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;aACtC;SACF;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE,cAAc;SACxB;KACF;IACD;;QAEI;IACJ,QAAQ,EAAE,uBAAuB;IACjC;;QAEI;IACJ,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,aAAa,EAAE,4BAA4B;YAC3C,qBAAqB,EAAE,4BAA4B;YACnD,sBAAsB,EAAE,4BAA4B;YACpD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,eAAe;gBACzB,SAAS,EAAE,yBAAyB;gBACpC,OAAO,EAAE,mCAAmC;aAC7C;YACD,aAAa,EAAE;gBACb,MAAM,EAAE,4BAA4B;gBACpC,WAAW,EAAE,2BAA2B;aACzC;YACD,oBAAoB,EAAE;gBACpB,MAAM,EAAE,4BAA4B;gBACpC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,iBAAiB;gBAC7B,mBAAmB,EAAE,yBAAyB;gBAC9C,IAAI,EAAE,kBAAkB;aACzB;YACD,yBAAyB,EAAE;gBACzB,MAAM,EAAE,cAAc;gBACtB,cAAc,EAAE,cAAc;gBAC9B,WAAW,EAAE,cAAc;aAC5B;YACD,mBAAmB,EAAE;gBACnB,SAAS,EAAE,cAAc;aAC1B;YACD,kBAAkB,EAAE;gBAClB,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,cAAc;gBACtB,SAAS,EAAE,cAAc;aAC1B;YACD,WAAW,EAAE,MAAM;YACnB,aAAa,EAAE,gBAAgB;YAC/B,WAAW,EAAE,wBAAwB;YACrC,YAAY,EAAE;gBACZ,MAAM,EAAE,iCAAiC;gBACzC,WAAW,EAAE,2BAA2B;aACzC;YACD,mBAAmB,EAAE;gBACnB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,iCAAiC;gBACvC,IAAI,EAAE,4BAA4B;gBAClC,OAAO,EAAE,MAAM;aAChB;YACD,uBAAuB,EAAE;gBACvB,MAAM,EAAE,iCAAiC;gBACzC,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,UAAU;aAChB;YACD,gBAAgB,EAAE;gBAChB,MAAM,EAAE,iCAAiC;gBACzC,IAAI,EAAE,2BAA2B;gBACjC,GAAG,EAAE,UAAU;aAChB;YACD,aAAa,EAAE;gBACb,YAAY,EAAE,wBAAwB;gBACtC,MAAM,EAAE,iCAAiC;aAC1C;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,kBAAkB;aAChC;YACD,aAAa,EAAE,MAAM;YACrB,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,UAAU;YACvB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE;gBACV,MAAM,EAAE,4BAA4B;gBACpC,MAAM,EAAE,2BAA2B;aACpC;YACD,IAAI,EAAE,cAAc;YACpB,gBAAgB,EAAE;gBAChB,OAAO,EAAE,cAAc;gBACvB,iBAAiB,EAAE,yBAAyB;aAC7C;YACD,kBAAkB,EAAE,MAAM;YAC1B,SAAS,EAAE,4BAA4B;YACvC,WAAW,EAAE,4BAA4B;YACzC,YAAY,EAAE,mCAAmC;YACjD,WAAW,EAAE,gCAAgC;YAC7C,oBAAoB,EAAE,qBAAqB;YAC3C,WAAW,EAAE;gBACX,UAAU,EAAE,OAAO;gBACnB,YAAY,EAAE,wBAAwB;aACvC;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,OAAO;gBACnB,UAAU,EAAE,cAAc;gBAC1B,aAAa,EAAE,cAAc;aAC9B;YACD,oBAAoB,EAAE,wBAAwB;YAC9C,mBAAmB,EAAE,MAAM;YAC3B,eAAe,EAAE,eAAe;YAChC,aAAa,EAAE;gBACb,OAAO,EAAE,wBAAwB;gBACjC,WAAW,EAAE,gBAAgB;gBAC7B,GAAG,EAAE,UAAU;aAChB;YACD,SAAS,EAAE;gBACT,KAAK,EAAE,iBAAiB;gBACxB,QAAQ,EAAE,2BAA2B;aACtC;YACD,WAAW,EAAE;gBACX,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,2BAA2B;aACpC;YACD,cAAc,EAAE;gBACd,KAAK,EAAE,iBAAiB;gBACxB,KAAK,EAAE,2BAA2B;aACnC;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,2BAA2B;aACpC;YACD,WAAW,EAAE;gBACX,WAAW,EAAE,MAAM;aACpB;YACD,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,2BAA2B;YACxC,eAAe,EAAE;gBACf,WAAW,EAAE,kBAAkB;gBAC/B,WAAW,EAAE,mCAAmC;aACjD;SACF;KACF;IACD;;QAEI;IACJ,aAAa,EAAE;QACb,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,4BAA4B;YACpC,eAAe,EAAE,gCAAgC;YACjD,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,sBAAsB;YACnC,cAAc,EAAE,qBAAqB;SACtC;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,OAAO;QACnB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,cAAc;KACtB;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,yBAAyB;KACrC;IACD;;QAEI;IACJ,+BAA+B,EAAE;QAC/B,KAAK,EAAE;YACL,QAAQ,EAAE,4BAA4B;YACtC,IAAI,EAAE,+BAA+B;SACtC;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,KAAK,EAAE;YACL,GAAG,EAAE,MAAM;YACX,KAAK,EAAE;gBACL,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;aACtC;YACD,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE;gBACZ,EAAE,EAAE,wBAAwB;gBAC5B,GAAG,EAAE,gCAAgC;gBACrC,KAAK,EAAE,cAAc;aACtB;SACF;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC;KACnC;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,YAAY,EAAE,MAAM;YACpB,kBAAkB,EAAE,MAAM;YAC1B,aAAa,EAAE,sBAAsB;SACtC;KACF;IACD;;QAEI;IACJ,mBAAmB,EAAE;QACnB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,wBAAwB;YAC5B,EAAE,EAAE,0BAA0B;SAC/B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE,MAAM;IAC5B;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,aAAa,EAAE,6CAA6C;gBAC5D,SAAS,EAAE,KAAK;aACjB;YACD,kBAAkB,EAAE;gBAClB,aAAa,EAAE,6CAA6C;gBAC5D,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,yBAAyB;aACvC;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,KAAK,EAAE;YACL,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,SAAS,EAAE,wCAAwC;gBACnD,cAAc,EAAE,aAAa;aAC9B;YACD,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,cAAc,EAAE,aAAa;aAC9B;YACD,eAAe,EAAE;gBACf,eAAe,EAAE,qBAAqB;aACvC;YACD,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,MAAM;YACpB,YAAY,EAAE,MAAM;YACpB,eAAe,EAAE;gBACf,WAAW,EAAE,KAAK;aACnB;YACD,cAAc,EAAE;gBACd,YAAY,EAAE,wCAAwC;aACvD;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,SAAS,EAAE,wCAAwC;gBACnD,cAAc,EAAE,aAAa;aAC9B;YACD,iBAAiB,EAAE;gBACjB,GAAG,EAAE,aAAa;gBAClB,KAAK,EAAE,MAAM;gBACb,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE,YAAY;gBAC5B,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,aAAa;aACtB;YACD,KAAK,EAAE;gBACL,UAAU,EAAE,MAAM;aACnB;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,aAAa,EAAE;gBACb,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,MAAM;aACjB;YACD,+BAA+B,EAAE;gBAC/B,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,2CAA2C;aACtD;YACD,qBAAqB,EAAE;gBACrB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,MAAM;aAChB;YACD,2CAA2C,EAAE;gBAC3C,QAAQ,EAAE,MAAM;aACjB;YACD,qBAAqB,EAAE;gBACrB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,YAAY;aACtB;YACD,sBAAsB,EAAE;gBACtB,OAAO,EAAE,MAAM;aAChB;YACD,2CAA2C,EAAE;gBAC3C,QAAQ,EAAE,MAAM;aACjB;YACD,6CAA6C,EAAE;gBAC7C,qBAAqB,EAAE,uDAAuD;aAC/E;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,MAAM;aAChB;YACD,mBAAmB,EAAE;gBACnB,uBAAuB,EAAE,gCAAgC;gBACzD,iBAAiB,EAAE,4CAA4C;aAChE;YACD,wBAAwB,EAAE;gBACxB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,aAAa;gBACpB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,MAAM;gBAChB,oBAAoB,EAAE,gCAAgC;aACvD;YACD,wBAAwB,EAAE;gBACxB,OAAO,EAAE,MAAM;gBACf,oBAAoB,EAAE,gCAAgC;aACvD;YACD,+BAA+B,EAAE;gBAC/B,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,aAAa;gBACpB,WAAW,EAAE,MAAM;gBACnB,KAAK,EAAE,KAAK;gBACZ,oBAAoB,EAAE,gCAAgC;aACvD;YACD,WAAW,EAAE;gBACX,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;iBACd;gBACD,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,MAAM;gBACnB,yBAAyB,EAAE,wCAAwC;aACpE;YACD,0CAA0C,EAAE;gBAC1C,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,MAAM;gBAChB,WAAW,EAAE,gCAAgC;aAC9C;YACD,qBAAqB,EAAE;gBACrB,iBAAiB,EAAE,aAAa;gBAChC,2BAA2B,EAAE,aAAa;aAC3C;SACF;KACF;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;KAChC;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,MAAM,EAAE,0DAA0D;QAClE,MAAM,EAAE,kEAAkE;KAC3E;IACD;;QAEI;IACJ,gDAAgD,EAAE;QAChD,iBAAiB,EAAE,4CAA4C;QAC/D,uBAAuB,EAAE,gCAAgC;KAC1D;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,GAAG,EAAE,MAAM;aACZ;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,yBAAyB;gBAChC,QAAQ,EAAE,cAAc;aACzB;YACD,gCAAgC,EAAE;gBAChC,QAAQ,EAAE,MAAM;aACjB;YACD,UAAU,EAAE;gBACV,MAAM,EAAE,MAAM;aACf;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE,CAAC,qBAAqB,CAAC;KAC/B;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE;YACL,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,IAAI,EAAE,MAAM;aACb;YACD,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,OAAO,EAAE,MAAM;aAChB;YACD,gCAAgC,EAAE;gBAChC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;aAC3B;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,cAAc,EAAE,KAAK;aACtB;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;gBAC1B,iBAAiB,EAAE,KAAK;aACzB;YACD,kCAAkC,EAAE;gBAClC,UAAU,EAAE,MAAM;gBAClB,WAAW,EAAE,aAAa;aAC3B;YACD,sBAAsB,EAAE;gBACtB,WAAW,EAAE,aAAa;aAC3B;YACD,oBAAoB,EAAE,MAAM;YAC5B,oBAAoB,EAAE,MAAM;SAC7B;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ,SAAS,EAAE,cAAc;gBACzB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;gBACb,eAAe,EAAE,eAAe;aACjC;YACD,kBAAkB,EAAE;gBAClB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;gBACb,eAAe,EAAE,eAAe;aACjC;SACF;KACF;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,4BAA4B;aACrC;YACD,YAAY,EAAE;gBACZ,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,4BAA4B;aACrC;YACD,OAAO,EAAE;gBACP,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,0BAA0B;aACpC;YACD,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,cAAc;gBACtB,WAAW,EAAE,+BAA+B;aAC7C;YACD,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,cAAc;gBACtB,UAAU,EAAE,sBAAsB;aACnC;YACD,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,cAAc;aACrB;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,UAAU;aAClB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,KAAK;aACpB;YACD,kBAAkB,EAAE;gBAClB,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,cAAc;aACzB;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,sBAAsB;gBAC9B,KAAK,EAAE,sBAAsB;gBAC7B,OAAO,EAAE,sBAAsB;aAChC;YACD,sBAAsB,EAAE;gBACtB,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,cAAc;aACtB;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,4BAA4B;aACrC;YACD,gBAAgB,EAAE;gBAChB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;gBACxB,aAAa,EAAE,aAAa;aAC7B;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;aACzB;YACD,4BAA4B,EAAE;gBAC5B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,MAAM;aACvB;YACD,aAAa,EAAE;gBACb,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;aACf;YACD,mBAAmB,EAAE;gBACnB,KAAK,EAAE,qBAAqB;gBAC5B,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;gBACZ,KAAK,EAAE,OAAO;aACf;YACD,eAAe,EAAE;gBACf,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,aAAa;gBACxB,SAAS,EAAE,8BAA8B;gBACzC,GAAG,EAAE,OAAO;aACb;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;aACzB;YACD,+BAA+B,EAAE;gBAC/B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,cAAc;gBACxB,OAAO,EAAE,2CAA2C;aACrD;YACD,YAAY,EAAE;gBACZ,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO;aACd;YACD,cAAc,EAAE;gBACd,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;aACZ;YACD,uBAAuB,EAAE;gBACvB,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,OAAO;aACd;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;aAClB;YACD,oBAAoB,EAAE;gBACpB,eAAe,EAAE,aAAa;aAC/B;YACD,yBAAyB,EAAE;gBACzB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,KAAK;aACjB;YACD,oBAAoB,EAAE;gBACpB,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,wBAAwB;aACvC;YACD,SAAS,EAAE;gBACT,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,cAAc;gBACrB,gBAAgB,EAAE,sBAAsB;aACzC;YACD,QAAQ,EAAE;gBACR,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,MAAM;aACjB;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,wBAAwB;aAC/B;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,KAAK;gBACxB,gBAAgB,EAAE,aAAa;gBAC/B,UAAU,EAAE,sCAAsC;gBAClD,QAAQ,EAAE,KAAK;aAChB;YACD,WAAW,EAAE;gBACX,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;aACnB;YACD,UAAU,EAAE;gBACV,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,KAAK;gBACf,iBAAiB,EAAE,KAAK;gBACxB,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,sCAAsC;aACrD;YACD,eAAe,EAAE;gBACf,QAAQ,EAAE,yBAAyB;gBACnC,SAAS,EAAE,yBAAyB;gBACpC,MAAM,EAAE,aAAa;aACtB;YACD,yBAAyB,EAAE;gBACzB,IAAI,EAAE,+BAA+B;gBACrC,SAAS,EAAE,yBAAyB;gBACpC,MAAM,EAAE,aAAa;aACtB;SACF;KACF;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,aAAa;QACxB,YAAY,EAAE,wBAAwB;KACvC;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,CAAC;KACjR;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,QAAQ,EAAE,oBAAoB;QAC9B,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,aAAa;QACzB,QAAQ,EAAE,aAAa;QACvB,mBAAmB,EAAE,KAAK;KAC3B;IACD;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,KAAK;SAChB;KACF;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,CAAC;KAC5F;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,aAAa,EAAE,cAAc;QAC7B,WAAW,EAAE,cAAc;QAC3B,UAAU,EAAE,cAAc;KAC3B;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,SAAS,EAAE,aAAa;QACxB,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,iBAAiB,EAAE,KAAK;KACzB;IACD;;QAEI;IACJ,iBAAiB,EAAE;QACjB,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE,aAAa;QACvB,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,qBAAqB;QAClC,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,cAAc;KAC1B;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE;YACL,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE,SAAS;SACjB;KACF;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,UAAU,EAAE,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,8BAA8B;QACzC,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,aAAa,EAAE;gBACb,QAAQ,EAAE,wDAAwD;aACnE;SACF;KACF;IACD;;QAEI;IACJ,sDAAsD,EAAE;QACtD,KAAK,EAAE;YACL,aAAa,EAAE,2EAA2E;SAC3F;KACF;IACD;;QAEI;IACJ,yEAAyE,EAAE;QACzE,KAAK,EAAE;YACL,yBAAyB,EAAE,yGAAyG;YACpI,sBAAsB,EAAE,sGAAsG;YAC9H,yBAAyB,EAAE,wGAAwG;YACnI,kBAAkB,EAAE,iGAAiG;SACtH;KACF;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,aAAa,CAAC;KACvB;IACD;;QAEI;IACJ,oCAAoC,EAAE;QACpC,GAAG,EAAE,aAAa;QAClB,OAAO,EAAE,MAAM;KAChB;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,KAAK,EAAE,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,cAAc,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,+BAA+B,EAAE,eAAe,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,kBAAkB,CAAC;KACrZ;IACD;;QAEI;IACJ,qBAAqB,EAAE,QAAQ;IAC/B;;QAEI;IACJ,kBAAkB,EAAE;QAClB,KAAK,EAAE,CAAC,cAAc,EAAE,yBAAyB,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,CAAC;KAC3F;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,qCAAqC;QAC5C,YAAY,EAAE,MAAM;QACpB,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,KAAK;KACjB;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC;KAC3B;IACD;;QAEI;IACJ,qCAAqC,EAAE;QACrC,gBAAgB,EAAE,KAAK;QACvB,aAAa,EAAE,KAAK;QACpB,eAAe,EAAE,KAAK;KACvB;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,+BAA+B,EAAE,QAAQ,CAAC;KAC3G;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,SAAS,EAAE,sBAAsB;gBACjC,iBAAiB,EAAE,8BAA8B;gBACjD,WAAW,EAAE,iBAAiB;gBAC9B,OAAO,EAAE,KAAK;aACf;YACD,eAAe,EAAE;gBACf,MAAM,EAAE,sBAAsB;gBAC9B,QAAQ,EAAE,MAAM;aACjB;YACD,KAAK,EAAE;gBACL,QAAQ,EAAE,sBAAsB;gBAChC,EAAE,EAAE,KAAK;aACV;SACF;KACF;IACD;;QAEI;IACJ,oBAAoB,EAAE;QACpB,KAAK,EAAE;YACL,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE,MAAM;YACjB,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,eAAe;YACnB,EAAE,EAAE,sBAAsB;SAC3B;KACF;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE;YACL,uBAAuB,EAAE,MAAM;YAC/B,uBAAuB,EAAE,MAAM;YAC/B,oBAAoB,EAAE,eAAe;YACrC,0BAA0B,EAAE,MAAM;SACnC;KACF;IACD;;QAEI;IACJ,mCAAmC,EAAE;QACnC,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,sBAAsB;QAC7B,MAAM,EAAE,sBAAsB;QAC9B,SAAS,EAAE,kBAAkB;KAC9B;IACD;;QAEI;IACJ,cAAc,EAAE;QACd,KAAK,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,iBAAiB,EAAE,0BAA0B,CAAC;KACxd;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,KAAK;QACV,KAAK,EAAE,KAAK;QACZ,eAAe,EAAE,sCAAsC;QACvD,YAAY,EAAE,KAAK;QACnB,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,IAAI,EAAE,6CAA6C;QACnD,IAAI,EAAE,6CAA6C;KACpD;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,SAAS,EAAE,KAAK;QAChB,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,aAAa,EAAE,qBAAqB,CAAC;KACpK;IACD;;QAEI;IACJ,qCAAqC,EAAE;QACrC,KAAK,EAAE;YACL,qBAAqB,EAAE,6CAA6C;YACpE,mBAAmB,EAAE,2CAA2C;SACjE;KACF;IACD;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,YAAY;QAC5B,IAAI,EAAE,MAAM;QACZ,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,aAAa;QAC3B,cAAc,EAAE,aAAa;QAC7B,gBAAgB,EAAE,KAAK;KACxB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,OAAO,EAAE,mCAAmC;QAC5C,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,wCAAwC;QACnD,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,aAAa;QAC3B,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,MAAM;QACf,iBAAiB,EAAE,aAAa;QAChC,KAAK,EAAE,KAAK;KACb;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,CAAC,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,kCAAkC,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,uCAAuC,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;KAC3oB;IACD;;QAEI;IACJ,sCAAsC,EAAE;QACtC,MAAM,EAAE;YACN,KAAK,EAAE,MAAM;SACd;QACD,WAAW,EAAE,KAAK;QAClB,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,OAAO;QACjB,WAAW,EAAE,MAAM;QACnB,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,qBAAqB;QAC1B,WAAW,EAAE,YAAY;QACzB,aAAa,EAAE,WAAW;QAC1B,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK;QACpB,eAAe,EAAE,KAAK;KACvB;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,SAAS,EAAE,MAAM;KAClB;IACD;;QAEI;IACJ,yCAAyC,EAAE;QACzC,SAAS,EAAE,aAAa;KACzB;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,iCAAiC,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,2BAA2B,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,qCAAqC,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,0CAA0C,EAAE,sCAAsC,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,eAAe,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,cAAc,EAAE,+BAA+B,EAAE,uCAAuC,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,8BAA8B,EAAE,yCAAyC,EAAE,mCAAmC,EAAE,iCAAiC,EAAE,wCAAwC,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,iCAAiC,EAAE,uBAAuB,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,mCAAmC,CAAC;KACxnD;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,aAAa,EAAE,yBAAyB,EAAE,iCAAiC,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,WAAW,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,uCAAuC,EAAE,6BAA6B,CAAC;KAC9iB;IACD;;QAEI;IACJ,0CAA0C,EAAE;QAC1C,IAAI,EAAE,MAAM;QACZ,eAAe,EAAE,KAAK;QACtB,WAAW,EAAE,MAAM;QACnB,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,4CAA4C,EAAE;QAC5C,cAAc,EAAE,KAAK;QACrB,yBAAyB,EAAE,MAAM;QACjC,WAAW,EAAE,MAAM;QACnB,cAAc,EAAE,aAAa;KAC9B;IACD;;QAEI;IACJ,8CAA8C,EAAE;QAC9C,kBAAkB,EAAE,KAAK;QACzB,UAAU,EAAE,MAAM;KACnB;IACD;;QAEI;IACJ,yBAAyB,EAAE;QACzB,KAAK,EAAE,CAAC,4BAA4B,EAAE,uBAAuB,EAAE,cAAc,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sCAAsC,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,yBAAyB,CAAC;KACpb;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,CAAC,oBAAoB,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,0BAA0B,CAAC;KACzG;IACD;;QAEI;IACJ,2BAA2B,EAAE;QAC3B,KAAK,EAAE,aAAa;QACpB,YAAY,EAAE,MAAM;QACpB,KAAK,EAAE,KAAK;QACZ,aAAa,EAAE,KAAK;QACpB,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,KAAK;KAClB;IACD;;QAEI;IACJ,wBAAwB,EAAE;QACxB,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC;KAChE;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,oCAAoC;QAC/C,OAAO,EAAE,uBAAuB;KACjC;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,4BAA4B,EAAE;QAC5B,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,sBAAsB,EAAE;QACtB,OAAO,EAAE,+BAA+B;QACxC,IAAI,EAAE,OAAO;KACd;IACD;;QAEI;IACJ,6BAA6B,EAAE;QAC7B,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,0BAA0B,EAAE;QAC1B,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,MAAM;KACf;IACD;;QAEI;IACJ,qBAAqB,EAAE;QACrB,iBAAiB,EAAE,KAAK;QACxB,WAAW,EAAE,aAAa;QAC1B,KAAK,EAAE,sCAAsC;QAC7C,QAAQ,EAAE,KAAK;KAChB;IACD;;QAEI;IACJ,uBAAuB,EAAE;QACvB,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC;KACxH;IACD;;QAEI;IACJ,eAAe,EAAE;QACf,KAAK,EAAE,CAAC,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,wBAAwB,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,CAAC;KACj1B;IACD;;QAEI;IACJ,uCAAuC,EAAE,MAAM;IAC/C;;QAEI;IACJ,qCAAqC,EAAE,MAAM;IAC7C;;QAEI;IACJ,mCAAmC,EAAE,MAAM;IAC3C;;QAEI;IACJ,iCAAiC,EAAE,MAAM;IACzC;;QAEI;IACJ,+BAA+B,EAAE,cAAc;IAC/C;;QAEI;IACJ,gCAAgC,EAAE,MAAM;IACxC;;QAEI;IACJ,gDAAgD,EAAE,eAAe;IACjE;;QAEI;IACJ,yDAAyD,EAAE,MAAM;IACjE;;QAEI;IACJ,2CAA2C,EAAE;QAC3C,IAAI,EAAE,gCAAgC;KACvC;IACD;;QAEI;IACJ,8BAA8B,EAAE;QAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC;KAC/B;IACD;;QAEI;IACJ,wBAAwB,EAAE,MAAM;CACjC,CAAC"} \ No newline at end of file diff --git a/api-augment/dist/types/interfaces/augment-api-consts.d.ts b/api-augment/dist/types/interfaces/augment-api-consts.d.ts index a99f3dacc..41d982816 100644 --- a/api-augment/dist/types/interfaces/augment-api-consts.d.ts +++ b/api-augment/dist/types/interfaces/augment-api-consts.d.ts @@ -526,6 +526,17 @@ declare module "@polkadot/api-base/types/consts" { [key: string]: Codec; }; xcmpQueue: { + /** + * Maximal number of outbound XCMP channels that can have messages queued at the same time. + * + * If this is reached, then no further messages can be sent to channels that do not yet + * have a message queued. This should be set to the expected maximum of outbound channels + * which is determined by [`Self::ChannelInfo`]. It is important to set this large enough, + * since otherwise the congestion control protocol will not work as intended and messages + * may be dropped. This value increases the PoV and should therefore not be picked too + * high. Governance needs to pay attention to not open more channels than this value. + **/ + maxActiveOutboundChannels: u32 & AugmentedConst; /** * The maximum number of inbound XCMP channels that can be suspended simultaneously. * @@ -534,6 +545,14 @@ declare module "@polkadot/api-base/types/consts" { * [`InboundXcmpSuspended`] still applies at that scale. **/ maxInboundSuspended: u32 & AugmentedConst; + /** + * The maximal page size for HRMP message pages. + * + * A lower limit can be set dynamically, but this is the hard-limit for the PoV worst case + * benchmarking. The limit for the size of a message is slightly below this, since some + * overhead is incurred for encoding the format. + **/ + maxPageSize: u32 & AugmentedConst; /** * Generic const **/ diff --git a/api-augment/dist/types/interfaces/augment-api-errors.d.ts b/api-augment/dist/types/interfaces/augment-api-errors.d.ts index f96810668..152d60dc6 100644 --- a/api-augment/dist/types/interfaces/augment-api-errors.d.ts +++ b/api-augment/dist/types/interfaces/augment-api-errors.d.ts @@ -1178,6 +1178,14 @@ declare module "@polkadot/api-base/types/errors" { * Setting the queue config failed since one of its values was invalid. **/ BadQueueConfig: AugmentedError; + /** + * The message is too big. + **/ + TooBig: AugmentedError; + /** + * There are too many active outbound channels. + **/ + TooManyActiveOutboundChannels: AugmentedError; /** * Generic error **/ diff --git a/api-augment/dist/types/interfaces/augment-api-query.d.ts b/api-augment/dist/types/interfaces/augment-api-query.d.ts index 5bda72700..d2450f141 100644 --- a/api-augment/dist/types/interfaces/augment-api-query.d.ts +++ b/api-augment/dist/types/interfaces/augment-api-query.d.ts @@ -71,6 +71,7 @@ import type { SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, + StagingXcmV4Instruction, StorageHubRuntimeConfigsRuntimeParamsRuntimeParametersKey, StorageHubRuntimeConfigsRuntimeParamsRuntimeParametersValue, StorageHubRuntimeRuntimeHoldReason, @@ -1163,6 +1164,20 @@ declare module "@polkadot/api-base/types/storage" { **/ queryCounter: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally + * will be stored here. + * Runtime APIs can fetch the XCM that was executed by accessing this value. + * + * Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + * implementation in the XCM executor configuration. + **/ + recordedXcm: AugmentedQuery< + ApiType, + () => Observable>>, + [] + > & + QueryableStorageEntry; /** * Fungible assets which we know are locked on a remote chain. **/ @@ -1191,6 +1206,17 @@ declare module "@polkadot/api-base/types/storage" { **/ safeXcmVersion: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Whether or not incoming XCMs (both executed locally and received) should be recorded. + * Only one XCM program will be recorded at a time. + * This is meant to be used in runtime APIs, and it's advised it stays false + * for all other use cases, so as to not degrade regular performance. + * + * Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + * implementation in the XCM executor configuration. + **/ + shouldRecordXcm: AugmentedQuery Observable, []> & + QueryableStorageEntry; /** * The Latest versions that we know various locations support. **/ diff --git a/api-augment/dist/types/interfaces/augment-api-tx.d.ts b/api-augment/dist/types/interfaces/augment-api-tx.d.ts index 7daf6129d..5cc67b51f 100644 --- a/api-augment/dist/types/interfaces/augment-api-tx.d.ts +++ b/api-augment/dist/types/interfaces/augment-api-tx.d.ts @@ -48,6 +48,22 @@ export type __SubmittableExtrinsicFunction = declare module "@polkadot/api-base/types/submittable" { interface AugmentedSubmittables { balances: { + /** + * Burn the specified liquid free balance from the origin account. + * + * If the origin's account ends up below the existential deposit as a result + * of the burn and `keep_alive` is false, the account will be reaped. + * + * Unlike sending funds to a _burn_ address, which merely makes the funds inaccessible, + * this `burn` operation will reduce total issuance by the amount _burned_. + **/ + burn: AugmentedSubmittable< + ( + value: Compact | AnyNumber | Uint8Array, + keepAlive: bool | boolean | Uint8Array + ) => SubmittableExtrinsic, + [Compact, bool] + >; /** * Adjust the total issuance in a saturating way. * diff --git a/api-augment/dist/types/interfaces/augment-types.d.ts b/api-augment/dist/types/interfaces/augment-types.d.ts index c08ce1750..98ad6232c 100644 --- a/api-augment/dist/types/interfaces/augment-types.d.ts +++ b/api-augment/dist/types/interfaces/augment-types.d.ts @@ -318,6 +318,13 @@ import type { VotingDirectVote } from "@polkadot/types/interfaces/democracy"; import type { BlockStats } from "@polkadot/types/interfaces/dev"; +import type { + CallDryRunEffects, + DispatchResultWithPostInfo, + PostDispatchInfo, + XcmDryRunApiError, + XcmDryRunEffects +} from "@polkadot/types/interfaces/dryRunApi"; import type { ApprovalFlag, DefunctVoter, @@ -403,10 +410,13 @@ import type { ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, + ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, + ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, + ExtrinsicV5, ImmortalEra, MortalEra, MultiSignature, @@ -1170,48 +1180,88 @@ import type { import type { Multisig, Timepoint } from "@polkadot/types/interfaces/utility"; import type { VestingInfo } from "@polkadot/types/interfaces/vesting"; import type { + AssetIdV2, + AssetIdV3, + AssetIdV4, AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, + AssetInstanceV3, + AssetInstanceV4, BodyId, + BodyIdV2, + BodyIdV3, BodyPart, + BodyPartV2, + BodyPartV3, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, + FungibilityV3, + FungibilityV4, InboundStatus, InstructionV2, + InstructionV3, + InstructionV4, InteriorMultiLocation, + InteriorMultiLocationV2, + InteriorMultiLocationV3, Junction, JunctionV0, JunctionV1, JunctionV2, + JunctionV3, + JunctionV4, Junctions, JunctionsV1, JunctionsV2, + JunctionsV3, + JunctionsV4, + MaxPalletNameLen, + MaxPalletsInfo, + MaybeErrorCodeV3, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, + MultiAssetFilterV3, + MultiAssetFilterV4, MultiAssetV0, MultiAssetV1, MultiAssetV2, + MultiAssetV3, + MultiAssetV4, MultiAssets, MultiAssetsV1, MultiAssetsV2, + MultiAssetsV3, + MultiAssetsV4, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, + MultiLocationV3, + MultiLocationV4, NetworkId, + NetworkIdV2, + NetworkIdV3, + NetworkIdV4, OriginKindV0, OriginKindV1, OriginKindV2, + OriginKindV3, + OriginKindV4, OutboundStatus, Outcome, + OutcomeV4, + PalletInfoV3, + PalletInfoV4, QueryId, + QueryResponseInfoV3, + QueryResponseInfoV4, QueryStatus, QueueConfigData, Response, @@ -1219,36 +1269,49 @@ import type { ResponseV1, ResponseV2, ResponseV2Error, - ResponseV2Result, + ResponseV3, + ResponseV3Error, + ResponseV3Result, + ResponseV4, + UncheckedFungibilityV4, VersionMigrationStage, + VersionV3, + VersionV4, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, + WeightLimitV3, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, + WildFungibilityV3, + WildFungibilityV4, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, + WildMultiAssetV3, + WildMultiAssetV4, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, - XcmOrder, + XcmErrorV3, + XcmErrorV4, XcmOrderV0, XcmOrderV1, - XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, + XcmV3, + XcmV4, XcmVersion, XcmpMessageFormat } from "@polkadot/types/interfaces/xcm"; @@ -1330,10 +1393,15 @@ declare module "@polkadot/types/types/registry" { AssetDestroyWitness: AssetDestroyWitness; AssetDetails: AssetDetails; AssetId: AssetId; + AssetIdV2: AssetIdV2; + AssetIdV3: AssetIdV3; + AssetIdV4: AssetIdV4; AssetInstance: AssetInstance; AssetInstanceV0: AssetInstanceV0; AssetInstanceV1: AssetInstanceV1; AssetInstanceV2: AssetInstanceV2; + AssetInstanceV3: AssetInstanceV3; + AssetInstanceV4: AssetInstanceV4; AssetMetadata: AssetMetadata; AssetOptions: AssetOptions; AssignmentId: AssignmentId; @@ -1408,7 +1476,11 @@ declare module "@polkadot/types/types/registry" { BlockV2: BlockV2; BlockWeights: BlockWeights; BodyId: BodyId; + BodyIdV2: BodyIdV2; + BodyIdV3: BodyIdV3; BodyPart: BodyPart; + BodyPartV2: BodyPartV2; + BodyPartV3: BodyPartV3; bool: bool; Bool: Bool; Bounty: Bounty; @@ -1424,6 +1496,7 @@ declare module "@polkadot/types/types/registry" { BufferedSessionChange: BufferedSessionChange; Bytes: Bytes; Call: Call; + CallDryRunEffects: CallDryRunEffects; CallHash: CallHash; CallHashOf: CallHashOf; CallIndex: CallIndex; @@ -1584,6 +1657,7 @@ declare module "@polkadot/types/types/registry" { DispatchResult: DispatchResult; DispatchResultOf: DispatchResultOf; DispatchResultTo198: DispatchResultTo198; + DispatchResultWithPostInfo: DispatchResultWithPostInfo; DisputeLocation: DisputeLocation; DisputeProof: DisputeProof; DisputeResult: DisputeResult; @@ -1703,12 +1777,15 @@ declare module "@polkadot/types/types/registry" { ExtrinsicPayload: ExtrinsicPayload; ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown; ExtrinsicPayloadV4: ExtrinsicPayloadV4; + ExtrinsicPayloadV5: ExtrinsicPayloadV5; ExtrinsicSignature: ExtrinsicSignature; ExtrinsicSignatureV4: ExtrinsicSignatureV4; + ExtrinsicSignatureV5: ExtrinsicSignatureV5; ExtrinsicStatus: ExtrinsicStatus; ExtrinsicsWeight: ExtrinsicsWeight; ExtrinsicUnknown: ExtrinsicUnknown; ExtrinsicV4: ExtrinsicV4; + ExtrinsicV5: ExtrinsicV5; f32: f32; F32: F32; f64: f64; @@ -1745,6 +1822,8 @@ declare module "@polkadot/types/types/registry" { FungibilityV0: FungibilityV0; FungibilityV1: FungibilityV1; FungibilityV2: FungibilityV2; + FungibilityV3: FungibilityV3; + FungibilityV4: FungibilityV4; FungiblesAccessError: FungiblesAccessError; Gas: Gas; GenesisBuildErr: GenesisBuildErr; @@ -1840,8 +1919,12 @@ declare module "@polkadot/types/types/registry" { InstantiateReturnValueOk: InstantiateReturnValueOk; InstantiateReturnValueTo267: InstantiateReturnValueTo267; InstructionV2: InstructionV2; + InstructionV3: InstructionV3; + InstructionV4: InstructionV4; InstructionWeights: InstructionWeights; InteriorMultiLocation: InteriorMultiLocation; + InteriorMultiLocationV2: InteriorMultiLocationV2; + InteriorMultiLocationV3: InteriorMultiLocationV3; InvalidDisputeStatementKind: InvalidDisputeStatementKind; InvalidTransaction: InvalidTransaction; isize: isize; @@ -1851,9 +1934,13 @@ declare module "@polkadot/types/types/registry" { Junctions: Junctions; JunctionsV1: JunctionsV1; JunctionsV2: JunctionsV2; + JunctionsV3: JunctionsV3; + JunctionsV4: JunctionsV4; JunctionV0: JunctionV0; JunctionV1: JunctionV1; JunctionV2: JunctionV2; + JunctionV3: JunctionV3; + JunctionV4: JunctionV4; Justification: Justification; JustificationNotification: JustificationNotification; Justifications: Justifications; @@ -1879,6 +1966,9 @@ declare module "@polkadot/types/types/registry" { LookupTarget: LookupTarget; LotteryConfig: LotteryConfig; MainStorageProviderId: MainStorageProviderId; + MaxPalletNameLen: MaxPalletNameLen; + MaxPalletsInfo: MaxPalletsInfo; + MaybeErrorCodeV3: MaybeErrorCodeV3; MaybeRandomness: MaybeRandomness; MaybeVrf: MaybeVrf; MemberCount: MemberCount; @@ -1936,22 +2026,33 @@ declare module "@polkadot/types/types/registry" { MultiAssetFilter: MultiAssetFilter; MultiAssetFilterV1: MultiAssetFilterV1; MultiAssetFilterV2: MultiAssetFilterV2; + MultiAssetFilterV3: MultiAssetFilterV3; + MultiAssetFilterV4: MultiAssetFilterV4; MultiAssets: MultiAssets; MultiAssetsV1: MultiAssetsV1; MultiAssetsV2: MultiAssetsV2; + MultiAssetsV3: MultiAssetsV3; + MultiAssetsV4: MultiAssetsV4; MultiAssetV0: MultiAssetV0; MultiAssetV1: MultiAssetV1; MultiAssetV2: MultiAssetV2; + MultiAssetV3: MultiAssetV3; + MultiAssetV4: MultiAssetV4; MultiDisputeStatementSet: MultiDisputeStatementSet; MultiLocation: MultiLocation; MultiLocationV0: MultiLocationV0; MultiLocationV1: MultiLocationV1; MultiLocationV2: MultiLocationV2; + MultiLocationV3: MultiLocationV3; + MultiLocationV4: MultiLocationV4; Multiplier: Multiplier; Multisig: Multisig; MultiSignature: MultiSignature; MultiSigner: MultiSigner; NetworkId: NetworkId; + NetworkIdV2: NetworkIdV2; + NetworkIdV3: NetworkIdV3; + NetworkIdV4: NetworkIdV4; NetworkState: NetworkState; NetworkStatePeerset: NetworkStatePeerset; NetworkStatePeersetInfo: NetworkStatePeersetInfo; @@ -1995,6 +2096,8 @@ declare module "@polkadot/types/types/registry" { OriginKindV0: OriginKindV0; OriginKindV1: OriginKindV1; OriginKindV2: OriginKindV2; + OriginKindV3: OriginKindV3; + OriginKindV4: OriginKindV4; OutboundHrmpChannelLimitations: OutboundHrmpChannelLimitations; OutboundHrmpMessage: OutboundHrmpMessage; OutboundLaneData: OutboundLaneData; @@ -2002,6 +2105,7 @@ declare module "@polkadot/types/types/registry" { OutboundPayload: OutboundPayload; OutboundStatus: OutboundStatus; Outcome: Outcome; + OutcomeV4: OutcomeV4; OuterEnums15: OuterEnums15; OverweightIndex: OverweightIndex; Owner: Owner; @@ -2016,6 +2120,8 @@ declare module "@polkadot/types/types/registry" { PalletEventMetadataLatest: PalletEventMetadataLatest; PalletEventMetadataV14: PalletEventMetadataV14; PalletId: PalletId; + PalletInfoV3: PalletInfoV3; + PalletInfoV4: PalletInfoV4; PalletMetadataLatest: PalletMetadataLatest; PalletMetadataV14: PalletMetadataV14; PalletMetadataV15: PalletMetadataV15; @@ -2068,6 +2174,7 @@ declare module "@polkadot/types/types/registry" { Points: Points; PortableType: PortableType; PortableTypeV14: PortableTypeV14; + PostDispatchInfo: PostDispatchInfo; Precommits: Precommits; PrefabWasmModule: PrefabWasmModule; PrefixedStorageKey: PrefixedStorageKey; @@ -2096,6 +2203,8 @@ declare module "@polkadot/types/types/registry" { QueryId: QueryId; QueryMspConfirmChunksToProveForFileError: QueryMspConfirmChunksToProveForFileError; QueryMspIdOfBucketIdError: QueryMspIdOfBucketIdError; + QueryResponseInfoV3: QueryResponseInfoV3; + QueryResponseInfoV4: QueryResponseInfoV4; QueryStatus: QueryStatus; QueryStorageProviderCapacityError: QueryStorageProviderCapacityError; QueueConfigData: QueueConfigData; @@ -2155,7 +2264,10 @@ declare module "@polkadot/types/types/registry" { ResponseV1: ResponseV1; ResponseV2: ResponseV2; ResponseV2Error: ResponseV2Error; - ResponseV2Result: ResponseV2Result; + ResponseV3: ResponseV3; + ResponseV3Error: ResponseV3Error; + ResponseV3Result: ResponseV3Result; + ResponseV4: ResponseV4; Retriable: Retriable; RewardDestination: RewardDestination; RewardPoint: RewardPoint; @@ -2396,6 +2508,7 @@ declare module "@polkadot/types/types/registry" { U8: U8; UnappliedSlash: UnappliedSlash; UnappliedSlashOther: UnappliedSlashOther; + UncheckedFungibilityV4: UncheckedFungibilityV4; UncleEntryItem: UncleEntryItem; UnknownTransaction: UnknownTransaction; UnlockChunk: UnlockChunk; @@ -2434,6 +2547,8 @@ declare module "@polkadot/types/types/registry" { VersionedResponse: VersionedResponse; VersionedXcm: VersionedXcm; VersionMigrationStage: VersionMigrationStage; + VersionV3: VersionV3; + VersionV4: VersionV4; VestingInfo: VestingInfo; VestingSchedule: VestingSchedule; Vote: Vote; @@ -2454,6 +2569,7 @@ declare module "@polkadot/types/types/registry" { VrfProof: VrfProof; Weight: Weight; WeightLimitV2: WeightLimitV2; + WeightLimitV3: WeightLimitV3; WeightMultiplier: WeightMultiplier; WeightPerClass: WeightPerClass; WeightToFeeCoefficient: WeightToFeeCoefficient; @@ -2464,9 +2580,13 @@ declare module "@polkadot/types/types/registry" { WildFungibilityV0: WildFungibilityV0; WildFungibilityV1: WildFungibilityV1; WildFungibilityV2: WildFungibilityV2; + WildFungibilityV3: WildFungibilityV3; + WildFungibilityV4: WildFungibilityV4; WildMultiAsset: WildMultiAsset; WildMultiAssetV1: WildMultiAssetV1; WildMultiAssetV2: WildMultiAssetV2; + WildMultiAssetV3: WildMultiAssetV3; + WildMultiAssetV4: WildMultiAssetV4; WinnersData: WinnersData; WinnersData10: WinnersData10; WinnersDataTuple: WinnersDataTuple; @@ -2477,14 +2597,16 @@ declare module "@polkadot/types/types/registry" { WithdrawReasons: WithdrawReasons; Xcm: Xcm; XcmAssetId: XcmAssetId; + XcmDryRunApiError: XcmDryRunApiError; + XcmDryRunEffects: XcmDryRunEffects; XcmError: XcmError; XcmErrorV0: XcmErrorV0; XcmErrorV1: XcmErrorV1; XcmErrorV2: XcmErrorV2; - XcmOrder: XcmOrder; + XcmErrorV3: XcmErrorV3; + XcmErrorV4: XcmErrorV4; XcmOrderV0: XcmOrderV0; XcmOrderV1: XcmOrderV1; - XcmOrderV2: XcmOrderV2; XcmOrigin: XcmOrigin; XcmOriginKind: XcmOriginKind; XcmPaymentApiError: XcmPaymentApiError; @@ -2492,6 +2614,8 @@ declare module "@polkadot/types/types/registry" { XcmV0: XcmV0; XcmV1: XcmV1; XcmV2: XcmV2; + XcmV3: XcmV3; + XcmV4: XcmV4; XcmVersion: XcmVersion; } } diff --git a/api-augment/dist/types/interfaces/lookup.d.ts b/api-augment/dist/types/interfaces/lookup.d.ts index 8fc3e0c6b..83a48ece6 100644 --- a/api-augment/dist/types/interfaces/lookup.d.ts +++ b/api-augment/dist/types/interfaces/lookup.d.ts @@ -909,9 +909,9 @@ declare const _default: { }; }; /** - * Lookup86: xcm::v2::OriginKind + * Lookup86: xcm::v3::OriginKind **/ - XcmV2OriginKind: { + XcmV3OriginKind: { _enum: string[]; }; /** @@ -1317,6 +1317,7 @@ declare const _default: { Unsupported: string; Overweight: string; Yield: string; + StackLimitReached: string; }; }; /** @@ -2492,6 +2493,10 @@ declare const _default: { direction: string; delta: string; }; + burn: { + value: string; + keepAlive: string; + }; }; }; /** @@ -2860,7 +2865,13 @@ declare const _default: { }; }; /** - * Lookup294: xcm::v2::multiasset::MultiAssetFilter + * Lookup294: xcm::v2::OriginKind + **/ + XcmV2OriginKind: { + _enum: string[]; + }; + /** + * Lookup295: xcm::v2::multiasset::MultiAssetFilter **/ XcmV2MultiassetMultiAssetFilter: { _enum: { @@ -2869,7 +2880,7 @@ declare const _default: { }; }; /** - * Lookup295: xcm::v2::multiasset::WildMultiAsset + * Lookup296: xcm::v2::multiasset::WildMultiAsset **/ XcmV2MultiassetWildMultiAsset: { _enum: { @@ -2881,13 +2892,13 @@ declare const _default: { }; }; /** - * Lookup296: xcm::v2::multiasset::WildFungibility + * Lookup297: xcm::v2::multiasset::WildFungibility **/ XcmV2MultiassetWildFungibility: { _enum: string[]; }; /** - * Lookup297: xcm::v2::WeightLimit + * Lookup298: xcm::v2::WeightLimit **/ XcmV2WeightLimit: { _enum: { @@ -2896,11 +2907,11 @@ declare const _default: { }; }; /** - * Lookup298: xcm::v3::Xcm + * Lookup299: xcm::v3::Xcm **/ XcmV3Xcm: string; /** - * Lookup300: xcm::v3::Instruction + * Lookup301: xcm::v3::Instruction **/ XcmV3Instruction: { _enum: { @@ -3042,7 +3053,7 @@ declare const _default: { }; }; /** - * Lookup301: xcm::v3::Response + * Lookup302: xcm::v3::Response **/ XcmV3Response: { _enum: { @@ -3055,7 +3066,7 @@ declare const _default: { }; }; /** - * Lookup303: xcm::v3::PalletInfo + * Lookup304: xcm::v3::PalletInfo **/ XcmV3PalletInfo: { index: string; @@ -3066,7 +3077,7 @@ declare const _default: { patch: string; }; /** - * Lookup307: xcm::v3::QueryResponseInfo + * Lookup308: xcm::v3::QueryResponseInfo **/ XcmV3QueryResponseInfo: { destination: string; @@ -3074,7 +3085,7 @@ declare const _default: { maxWeight: string; }; /** - * Lookup308: xcm::v3::multiasset::MultiAssetFilter + * Lookup309: xcm::v3::multiasset::MultiAssetFilter **/ XcmV3MultiassetMultiAssetFilter: { _enum: { @@ -3083,7 +3094,7 @@ declare const _default: { }; }; /** - * Lookup309: xcm::v3::multiasset::WildMultiAsset + * Lookup310: xcm::v3::multiasset::WildMultiAsset **/ XcmV3MultiassetWildMultiAsset: { _enum: { @@ -3101,13 +3112,13 @@ declare const _default: { }; }; /** - * Lookup310: xcm::v3::multiasset::WildFungibility + * Lookup311: xcm::v3::multiasset::WildFungibility **/ XcmV3MultiassetWildFungibility: { _enum: string[]; }; /** - * Lookup322: staging_xcm_executor::traits::asset_transfer::TransferType + * Lookup323: staging_xcm_executor::traits::asset_transfer::TransferType **/ StagingXcmExecutorAssetTransferTransferType: { _enum: { @@ -3118,7 +3129,7 @@ declare const _default: { }; }; /** - * Lookup323: xcm::VersionedAssetId + * Lookup324: xcm::VersionedAssetId **/ XcmVersionedAssetId: { _enum: { @@ -3130,11 +3141,11 @@ declare const _default: { }; }; /** - * Lookup324: cumulus_pallet_xcm::pallet::Call + * Lookup325: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: string; /** - * Lookup325: pallet_message_queue::pallet::Call + * Lookup326: pallet_message_queue::pallet::Call **/ PalletMessageQueueCall: { _enum: { @@ -3151,7 +3162,7 @@ declare const _default: { }; }; /** - * Lookup326: pallet_storage_providers::pallet::Call + * Lookup327: pallet_storage_providers::pallet::Call **/ PalletStorageProvidersCall: { _enum: { @@ -3200,7 +3211,7 @@ declare const _default: { }; }; /** - * Lookup327: pallet_file_system::pallet::Call + * Lookup328: pallet_file_system::pallet::Call **/ PalletFileSystemCall: { _enum: { @@ -3304,27 +3315,27 @@ declare const _default: { }; }; /** - * Lookup328: pallet_file_system::types::BucketMoveRequestResponse + * Lookup329: pallet_file_system::types::BucketMoveRequestResponse **/ PalletFileSystemBucketMoveRequestResponse: { _enum: string[]; }; /** - * Lookup331: pallet_file_system::types::MspStorageRequestResponse + * Lookup332: pallet_file_system::types::MspStorageRequestResponse **/ PalletFileSystemMspStorageRequestResponse: { accept: string; reject: string; }; /** - * Lookup333: pallet_file_system::types::AcceptedStorageRequestParameters + * Lookup334: pallet_file_system::types::AcceptedStorageRequestParameters **/ PalletFileSystemAcceptedStorageRequestParameters: { fileKeysAndProofs: string; nonInclusionForestProof: string; }; /** - * Lookup340: pallet_proofs_dealer::pallet::Call + * Lookup341: pallet_proofs_dealer::pallet::Call **/ PalletProofsDealerCall: { _enum: { @@ -3344,13 +3355,13 @@ declare const _default: { }; }; /** - * Lookup341: pallet_randomness::pallet::Call + * Lookup342: pallet_randomness::pallet::Call **/ PalletRandomnessCall: { _enum: string[]; }; /** - * Lookup342: pallet_payment_streams::pallet::Call + * Lookup343: pallet_payment_streams::pallet::Call **/ PalletPaymentStreamsCall: { _enum: { @@ -3390,7 +3401,7 @@ declare const _default: { }; }; /** - * Lookup343: pallet_bucket_nfts::pallet::Call + * Lookup344: pallet_bucket_nfts::pallet::Call **/ PalletBucketNftsCall: { _enum: { @@ -3408,7 +3419,7 @@ declare const _default: { }; }; /** - * Lookup345: pallet_nfts::pallet::Call + * Lookup346: pallet_nfts::pallet::Call **/ PalletNftsCall: { _enum: { @@ -3605,7 +3616,7 @@ declare const _default: { }; }; /** - * Lookup346: pallet_nfts::types::CollectionConfig + * Lookup347: pallet_nfts::types::CollectionConfig **/ PalletNftsCollectionConfig: { settings: string; @@ -3613,13 +3624,13 @@ declare const _default: { mintSettings: string; }; /** - * Lookup348: pallet_nfts::types::CollectionSetting + * Lookup349: pallet_nfts::types::CollectionSetting **/ PalletNftsCollectionSetting: { _enum: string[]; }; /** - * Lookup349: pallet_nfts::types::MintSettings + * Lookup350: pallet_nfts::types::MintSettings **/ PalletNftsMintSettings: { mintType: string; @@ -3629,7 +3640,7 @@ declare const _default: { defaultItemSettings: string; }; /** - * Lookup350: pallet_nfts::types::MintType + * Lookup351: pallet_nfts::types::MintType **/ PalletNftsMintType: { _enum: { @@ -3639,13 +3650,13 @@ declare const _default: { }; }; /** - * Lookup353: pallet_nfts::types::ItemSetting + * Lookup354: pallet_nfts::types::ItemSetting **/ PalletNftsItemSetting: { _enum: string[]; }; /** - * Lookup354: pallet_nfts::types::DestroyWitness + * Lookup355: pallet_nfts::types::DestroyWitness **/ PalletNftsDestroyWitness: { itemMetadatas: string; @@ -3653,26 +3664,26 @@ declare const _default: { attributes: string; }; /** - * Lookup356: pallet_nfts::types::MintWitness + * Lookup357: pallet_nfts::types::MintWitness **/ PalletNftsMintWitness: { ownedItem: string; mintPrice: string; }; /** - * Lookup357: pallet_nfts::types::ItemConfig + * Lookup358: pallet_nfts::types::ItemConfig **/ PalletNftsItemConfig: { settings: string; }; /** - * Lookup359: pallet_nfts::types::CancelAttributesApprovalWitness + * Lookup360: pallet_nfts::types::CancelAttributesApprovalWitness **/ PalletNftsCancelAttributesApprovalWitness: { accountAttributes: string; }; /** - * Lookup361: pallet_nfts::types::ItemTip + * Lookup362: pallet_nfts::types::ItemTip **/ PalletNftsItemTip: { collection: string; @@ -3681,7 +3692,7 @@ declare const _default: { amount: string; }; /** - * Lookup363: pallet_nfts::types::PreSignedMint + * Lookup364: pallet_nfts::types::PreSignedMint **/ PalletNftsPreSignedMint: { collection: string; @@ -3693,7 +3704,7 @@ declare const _default: { mintPrice: string; }; /** - * Lookup364: sp_runtime::MultiSignature + * Lookup365: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3703,7 +3714,7 @@ declare const _default: { }; }; /** - * Lookup367: pallet_nfts::types::PreSignedAttributes + * Lookup368: pallet_nfts::types::PreSignedAttributes **/ PalletNftsPreSignedAttributes: { collection: string; @@ -3713,7 +3724,7 @@ declare const _default: { deadline: string; }; /** - * Lookup368: pallet_parameters::pallet::Call + * Lookup369: pallet_parameters::pallet::Call **/ PalletParametersCall: { _enum: { @@ -3723,7 +3734,7 @@ declare const _default: { }; }; /** - * Lookup369: storage_hub_runtime::configs::runtime_params::RuntimeParameters + * Lookup370: storage_hub_runtime::configs::runtime_params::RuntimeParameters **/ StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters: { _enum: { @@ -3731,7 +3742,7 @@ declare const _default: { }; }; /** - * Lookup370: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters + * Lookup371: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters **/ StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters: { _enum: { @@ -3742,36 +3753,36 @@ declare const _default: { }; }; /** - * Lookup371: pallet_sudo::pallet::Error + * Lookup372: pallet_sudo::pallet::Error **/ PalletSudoError: { _enum: string[]; }; /** - * Lookup374: pallet_collator_selection::pallet::CandidateInfo + * Lookup375: pallet_collator_selection::pallet::CandidateInfo **/ PalletCollatorSelectionCandidateInfo: { who: string; deposit: string; }; /** - * Lookup376: pallet_collator_selection::pallet::Error + * Lookup377: pallet_collator_selection::pallet::Error **/ PalletCollatorSelectionError: { _enum: string[]; }; /** - * Lookup380: sp_core::crypto::KeyTypeId + * Lookup381: sp_core::crypto::KeyTypeId **/ SpCoreCryptoKeyTypeId: string; /** - * Lookup381: pallet_session::pallet::Error + * Lookup382: pallet_session::pallet::Error **/ PalletSessionError: { _enum: string[]; }; /** - * Lookup390: cumulus_pallet_xcmp_queue::OutboundChannelDetails + * Lookup391: cumulus_pallet_xcmp_queue::OutboundChannelDetails **/ CumulusPalletXcmpQueueOutboundChannelDetails: { recipient: string; @@ -3781,13 +3792,13 @@ declare const _default: { lastIndex: string; }; /** - * Lookup391: cumulus_pallet_xcmp_queue::OutboundState + * Lookup392: cumulus_pallet_xcmp_queue::OutboundState **/ CumulusPalletXcmpQueueOutboundState: { _enum: string[]; }; /** - * Lookup393: cumulus_pallet_xcmp_queue::QueueConfigData + * Lookup396: cumulus_pallet_xcmp_queue::QueueConfigData **/ CumulusPalletXcmpQueueQueueConfigData: { suspendThreshold: string; @@ -3795,13 +3806,13 @@ declare const _default: { resumeThreshold: string; }; /** - * Lookup394: cumulus_pallet_xcmp_queue::pallet::Error + * Lookup397: cumulus_pallet_xcmp_queue::pallet::Error **/ CumulusPalletXcmpQueueError: { _enum: string[]; }; /** - * Lookup395: pallet_xcm::pallet::QueryStatus + * Lookup398: pallet_xcm::pallet::QueryStatus **/ PalletXcmQueryStatus: { _enum: { @@ -3822,7 +3833,7 @@ declare const _default: { }; }; /** - * Lookup399: xcm::VersionedResponse + * Lookup402: xcm::VersionedResponse **/ XcmVersionedResponse: { _enum: { @@ -3834,7 +3845,7 @@ declare const _default: { }; }; /** - * Lookup405: pallet_xcm::pallet::VersionMigrationStage + * Lookup408: pallet_xcm::pallet::VersionMigrationStage **/ PalletXcmVersionMigrationStage: { _enum: { @@ -3845,7 +3856,7 @@ declare const _default: { }; }; /** - * Lookup408: pallet_xcm::pallet::RemoteLockedFungibleRecord + * Lookup411: pallet_xcm::pallet::RemoteLockedFungibleRecord **/ PalletXcmRemoteLockedFungibleRecord: { amount: string; @@ -3854,13 +3865,13 @@ declare const _default: { consumers: string; }; /** - * Lookup415: pallet_xcm::pallet::Error + * Lookup418: pallet_xcm::pallet::Error **/ PalletXcmError: { _enum: string[]; }; /** - * Lookup416: pallet_message_queue::BookState + * Lookup419: pallet_message_queue::BookState **/ PalletMessageQueueBookState: { _alias: { @@ -3874,14 +3885,14 @@ declare const _default: { size_: string; }; /** - * Lookup418: pallet_message_queue::Neighbours + * Lookup421: pallet_message_queue::Neighbours **/ PalletMessageQueueNeighbours: { prev: string; next: string; }; /** - * Lookup420: pallet_message_queue::Page + * Lookup423: pallet_message_queue::Page **/ PalletMessageQueuePage: { remaining: string; @@ -3892,13 +3903,13 @@ declare const _default: { heap: string; }; /** - * Lookup422: pallet_message_queue::pallet::Error + * Lookup425: pallet_message_queue::pallet::Error **/ PalletMessageQueueError: { _enum: string[]; }; /** - * Lookup424: pallet_storage_providers::types::StorageProvider + * Lookup427: pallet_storage_providers::types::StorageProvider **/ PalletStorageProvidersStorageProvider: { _enum: { @@ -3907,7 +3918,7 @@ declare const _default: { }; }; /** - * Lookup425: pallet_storage_providers::types::BackupStorageProvider + * Lookup428: pallet_storage_providers::types::BackupStorageProvider **/ PalletStorageProvidersBackupStorageProvider: { capacity: string; @@ -3920,7 +3931,7 @@ declare const _default: { reputationWeight: string; }; /** - * Lookup426: pallet_storage_providers::types::MainStorageProvider + * Lookup429: pallet_storage_providers::types::MainStorageProvider **/ PalletStorageProvidersMainStorageProvider: { buckets: string; @@ -3933,7 +3944,7 @@ declare const _default: { paymentAccount: string; }; /** - * Lookup428: pallet_storage_providers::types::Bucket + * Lookup431: pallet_storage_providers::types::Bucket **/ PalletStorageProvidersBucket: { _alias: { @@ -3947,13 +3958,13 @@ declare const _default: { size_: string; }; /** - * Lookup431: pallet_storage_providers::pallet::Error + * Lookup434: pallet_storage_providers::pallet::Error **/ PalletStorageProvidersError: { _enum: string[]; }; /** - * Lookup432: pallet_file_system::types::StorageRequestMetadata + * Lookup435: pallet_file_system::types::StorageRequestMetadata **/ PalletFileSystemStorageRequestMetadata: { _alias: { @@ -3973,31 +3984,31 @@ declare const _default: { bspsVolunteered: string; }; /** - * Lookup437: pallet_file_system::types::StorageRequestBspsMetadata + * Lookup440: pallet_file_system::types::StorageRequestBspsMetadata **/ PalletFileSystemStorageRequestBspsMetadata: { confirmed: string; }; /** - * Lookup446: pallet_file_system::types::MoveBucketRequestMetadata + * Lookup449: pallet_file_system::types::MoveBucketRequestMetadata **/ PalletFileSystemMoveBucketRequestMetadata: { requester: string; }; /** - * Lookup447: pallet_file_system::pallet::Error + * Lookup450: pallet_file_system::pallet::Error **/ PalletFileSystemError: { _enum: string[]; }; /** - * Lookup454: pallet_proofs_dealer::pallet::Error + * Lookup457: pallet_proofs_dealer::pallet::Error **/ PalletProofsDealerError: { _enum: string[]; }; /** - * Lookup457: pallet_payment_streams::types::FixedRatePaymentStream + * Lookup460: pallet_payment_streams::types::FixedRatePaymentStream **/ PalletPaymentStreamsFixedRatePaymentStream: { rate: string; @@ -4006,7 +4017,7 @@ declare const _default: { outOfFundsTick: string; }; /** - * Lookup458: pallet_payment_streams::types::DynamicRatePaymentStream + * Lookup461: pallet_payment_streams::types::DynamicRatePaymentStream **/ PalletPaymentStreamsDynamicRatePaymentStream: { amountProvided: string; @@ -4015,26 +4026,26 @@ declare const _default: { outOfFundsTick: string; }; /** - * Lookup459: pallet_payment_streams::types::ProviderLastChargeableInfo + * Lookup462: pallet_payment_streams::types::ProviderLastChargeableInfo **/ PalletPaymentStreamsProviderLastChargeableInfo: { lastChargeableTick: string; priceIndex: string; }; /** - * Lookup460: pallet_payment_streams::pallet::Error + * Lookup463: pallet_payment_streams::pallet::Error **/ PalletPaymentStreamsError: { _enum: string[]; }; /** - * Lookup461: pallet_bucket_nfts::pallet::Error + * Lookup464: pallet_bucket_nfts::pallet::Error **/ PalletBucketNftsError: { _enum: string[]; }; /** - * Lookup462: pallet_nfts::types::CollectionDetails + * Lookup465: pallet_nfts::types::CollectionDetails **/ PalletNftsCollectionDetails: { owner: string; @@ -4045,13 +4056,13 @@ declare const _default: { attributes: string; }; /** - * Lookup467: pallet_nfts::types::CollectionRole + * Lookup470: pallet_nfts::types::CollectionRole **/ PalletNftsCollectionRole: { _enum: string[]; }; /** - * Lookup468: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> + * Lookup471: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> **/ PalletNftsItemDetails: { owner: string; @@ -4059,42 +4070,42 @@ declare const _default: { deposit: string; }; /** - * Lookup469: pallet_nfts::types::ItemDeposit + * Lookup472: pallet_nfts::types::ItemDeposit **/ PalletNftsItemDeposit: { account: string; amount: string; }; /** - * Lookup474: pallet_nfts::types::CollectionMetadata + * Lookup477: pallet_nfts::types::CollectionMetadata **/ PalletNftsCollectionMetadata: { deposit: string; data: string; }; /** - * Lookup475: pallet_nfts::types::ItemMetadata, StringLimit> + * Lookup478: pallet_nfts::types::ItemMetadata, StringLimit> **/ PalletNftsItemMetadata: { deposit: string; data: string; }; /** - * Lookup476: pallet_nfts::types::ItemMetadataDeposit + * Lookup479: pallet_nfts::types::ItemMetadataDeposit **/ PalletNftsItemMetadataDeposit: { account: string; amount: string; }; /** - * Lookup479: pallet_nfts::types::AttributeDeposit + * Lookup482: pallet_nfts::types::AttributeDeposit **/ PalletNftsAttributeDeposit: { account: string; amount: string; }; /** - * Lookup483: pallet_nfts::types::PendingSwap, Deadline> + * Lookup486: pallet_nfts::types::PendingSwap, Deadline> **/ PalletNftsPendingSwap: { desiredCollection: string; @@ -4103,63 +4114,63 @@ declare const _default: { deadline: string; }; /** - * Lookup485: pallet_nfts::types::PalletFeature + * Lookup488: pallet_nfts::types::PalletFeature **/ PalletNftsPalletFeature: { _enum: string[]; }; /** - * Lookup486: pallet_nfts::pallet::Error + * Lookup489: pallet_nfts::pallet::Error **/ PalletNftsError: { _enum: string[]; }; /** - * Lookup489: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender + * Lookup492: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender **/ FrameSystemExtensionsCheckNonZeroSender: string; /** - * Lookup490: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup493: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: string; /** - * Lookup491: frame_system::extensions::check_tx_version::CheckTxVersion + * Lookup494: frame_system::extensions::check_tx_version::CheckTxVersion **/ FrameSystemExtensionsCheckTxVersion: string; /** - * Lookup492: frame_system::extensions::check_genesis::CheckGenesis + * Lookup495: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: string; /** - * Lookup495: frame_system::extensions::check_nonce::CheckNonce + * Lookup498: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: string; /** - * Lookup496: frame_system::extensions::check_weight::CheckWeight + * Lookup499: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: string; /** - * Lookup497: pallet_transaction_payment::ChargeTransactionPayment + * Lookup500: pallet_transaction_payment::ChargeTransactionPayment **/ PalletTransactionPaymentChargeTransactionPayment: string; /** - * Lookup498: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim + * Lookup501: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim **/ CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim: string; /** - * Lookup499: frame_metadata_hash_extension::CheckMetadataHash + * Lookup502: frame_metadata_hash_extension::CheckMetadataHash **/ FrameMetadataHashExtensionCheckMetadataHash: { mode: string; }; /** - * Lookup500: frame_metadata_hash_extension::Mode + * Lookup503: frame_metadata_hash_extension::Mode **/ FrameMetadataHashExtensionMode: { _enum: string[]; }; /** - * Lookup501: storage_hub_runtime::Runtime + * Lookup504: storage_hub_runtime::Runtime **/ StorageHubRuntimeRuntime: string; }; diff --git a/api-augment/dist/types/interfaces/registry.d.ts b/api-augment/dist/types/interfaces/registry.d.ts index bc55b4ac5..16295be98 100644 --- a/api-augment/dist/types/interfaces/registry.d.ts +++ b/api-augment/dist/types/interfaces/registry.d.ts @@ -254,6 +254,7 @@ import type { XcmV3MultiassetMultiAssets, XcmV3MultiassetWildFungibility, XcmV3MultiassetWildMultiAsset, + XcmV3OriginKind, XcmV3PalletInfo, XcmV3QueryResponseInfo, XcmV3Response, @@ -522,6 +523,7 @@ declare module "@polkadot/types/types/registry" { XcmV3MultiassetMultiAssets: XcmV3MultiassetMultiAssets; XcmV3MultiassetWildFungibility: XcmV3MultiassetWildFungibility; XcmV3MultiassetWildMultiAsset: XcmV3MultiassetWildMultiAsset; + XcmV3OriginKind: XcmV3OriginKind; XcmV3PalletInfo: XcmV3PalletInfo; XcmV3QueryResponseInfo: XcmV3QueryResponseInfo; XcmV3Response: XcmV3Response; diff --git a/api-augment/dist/types/interfaces/storagehubclient/definitions.d.ts b/api-augment/dist/types/interfaces/storagehubclient/definitions.d.ts index e5c40a286..aa8afbe77 100644 --- a/api-augment/dist/types/interfaces/storagehubclient/definitions.d.ts +++ b/api-augment/dist/types/interfaces/storagehubclient/definitions.d.ts @@ -1,5 +1,5 @@ declare const _default: { - types: import("@polkadot/types-codec/types").RegistryTypes | undefined; + types: import("@polkadot/types/types").RegistryTypes | undefined; runtime: import("@polkadot/types/types").DefinitionsCall | undefined; rpc: | Record< diff --git a/api-augment/dist/types/interfaces/types-lookup.d.ts b/api-augment/dist/types/interfaces/types-lookup.d.ts index 4b4b6f8d7..ebb31302f 100644 --- a/api-augment/dist/types/interfaces/types-lookup.d.ts +++ b/api-augment/dist/types/interfaces/types-lookup.d.ts @@ -936,7 +936,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly isTransact: boolean; readonly asTransact: { - readonly originKind: XcmV2OriginKind; + readonly originKind: XcmV3OriginKind; readonly requireWeightAtMost: SpWeightsWeightV2Weight; readonly call: XcmDoubleEncoded; } & Struct; @@ -1208,8 +1208,8 @@ declare module "@polkadot/types/lookup" { readonly asTruncatedError: Bytes; readonly type: "Success" | "Error" | "TruncatedError"; } - /** @name XcmV2OriginKind (86) */ - interface XcmV2OriginKind extends Enum { + /** @name XcmV3OriginKind (86) */ + interface XcmV3OriginKind extends Enum { readonly isNative: boolean; readonly isSovereignAccount: boolean; readonly isSuperuser: boolean; @@ -1703,7 +1703,14 @@ declare module "@polkadot/types/lookup" { readonly isOverweight: boolean; readonly asOverweight: SpWeightsWeightV2Weight; readonly isYield: boolean; - readonly type: "BadFormat" | "Corrupt" | "Unsupported" | "Overweight" | "Yield"; + readonly isStackLimitReached: boolean; + readonly type: + | "BadFormat" + | "Corrupt" + | "Unsupported" + | "Overweight" + | "Yield" + | "StackLimitReached"; } /** @name PalletStorageProvidersEvent (124) */ interface PalletStorageProvidersEvent extends Enum { @@ -2957,6 +2964,11 @@ declare module "@polkadot/types/lookup" { readonly direction: PalletBalancesAdjustmentDirection; readonly delta: Compact; } & Struct; + readonly isBurn: boolean; + readonly asBurn: { + readonly value: Compact; + readonly keepAlive: bool; + } & Struct; readonly type: | "TransferAllowDeath" | "ForceTransfer" @@ -2965,7 +2977,8 @@ declare module "@polkadot/types/lookup" { | "ForceUnreserve" | "UpgradeAccounts" | "ForceSetBalance" - | "ForceAdjustTotalIssuance"; + | "ForceAdjustTotalIssuance" + | "Burn"; } /** @name PalletBalancesAdjustmentDirection (275) */ interface PalletBalancesAdjustmentDirection extends Enum { @@ -3443,7 +3456,15 @@ declare module "@polkadot/types/lookup" { | "Barrier" | "WeightNotComputable"; } - /** @name XcmV2MultiassetMultiAssetFilter (294) */ + /** @name XcmV2OriginKind (294) */ + interface XcmV2OriginKind extends Enum { + readonly isNative: boolean; + readonly isSovereignAccount: boolean; + readonly isSuperuser: boolean; + readonly isXcm: boolean; + readonly type: "Native" | "SovereignAccount" | "Superuser" | "Xcm"; + } + /** @name XcmV2MultiassetMultiAssetFilter (295) */ interface XcmV2MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV2MultiassetMultiAssets; @@ -3451,7 +3472,7 @@ declare module "@polkadot/types/lookup" { readonly asWild: XcmV2MultiassetWildMultiAsset; readonly type: "Definite" | "Wild"; } - /** @name XcmV2MultiassetWildMultiAsset (295) */ + /** @name XcmV2MultiassetWildMultiAsset (296) */ interface XcmV2MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -3461,22 +3482,22 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "All" | "AllOf"; } - /** @name XcmV2MultiassetWildFungibility (296) */ + /** @name XcmV2MultiassetWildFungibility (297) */ interface XcmV2MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: "Fungible" | "NonFungible"; } - /** @name XcmV2WeightLimit (297) */ + /** @name XcmV2WeightLimit (298) */ interface XcmV2WeightLimit extends Enum { readonly isUnlimited: boolean; readonly isLimited: boolean; readonly asLimited: Compact; readonly type: "Unlimited" | "Limited"; } - /** @name XcmV3Xcm (298) */ + /** @name XcmV3Xcm (299) */ interface XcmV3Xcm extends Vec {} - /** @name XcmV3Instruction (300) */ + /** @name XcmV3Instruction (301) */ interface XcmV3Instruction extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: XcmV3MultiassetMultiAssets; @@ -3504,7 +3525,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly isTransact: boolean; readonly asTransact: { - readonly originKind: XcmV2OriginKind; + readonly originKind: XcmV3OriginKind; readonly requireWeightAtMost: SpWeightsWeightV2Weight; readonly call: XcmDoubleEncoded; } & Struct; @@ -3705,7 +3726,7 @@ declare module "@polkadot/types/lookup" { | "AliasOrigin" | "UnpaidExecution"; } - /** @name XcmV3Response (301) */ + /** @name XcmV3Response (302) */ interface XcmV3Response extends Enum { readonly isNull: boolean; readonly isAssets: boolean; @@ -3726,7 +3747,7 @@ declare module "@polkadot/types/lookup" { | "PalletsInfo" | "DispatchResult"; } - /** @name XcmV3PalletInfo (303) */ + /** @name XcmV3PalletInfo (304) */ interface XcmV3PalletInfo extends Struct { readonly index: Compact; readonly name: Bytes; @@ -3735,13 +3756,13 @@ declare module "@polkadot/types/lookup" { readonly minor: Compact; readonly patch: Compact; } - /** @name XcmV3QueryResponseInfo (307) */ + /** @name XcmV3QueryResponseInfo (308) */ interface XcmV3QueryResponseInfo extends Struct { readonly destination: StagingXcmV3MultiLocation; readonly queryId: Compact; readonly maxWeight: SpWeightsWeightV2Weight; } - /** @name XcmV3MultiassetMultiAssetFilter (308) */ + /** @name XcmV3MultiassetMultiAssetFilter (309) */ interface XcmV3MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV3MultiassetMultiAssets; @@ -3749,7 +3770,7 @@ declare module "@polkadot/types/lookup" { readonly asWild: XcmV3MultiassetWildMultiAsset; readonly type: "Definite" | "Wild"; } - /** @name XcmV3MultiassetWildMultiAsset (309) */ + /** @name XcmV3MultiassetWildMultiAsset (310) */ interface XcmV3MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -3767,13 +3788,13 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "All" | "AllOf" | "AllCounted" | "AllOfCounted"; } - /** @name XcmV3MultiassetWildFungibility (310) */ + /** @name XcmV3MultiassetWildFungibility (311) */ interface XcmV3MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: "Fungible" | "NonFungible"; } - /** @name StagingXcmExecutorAssetTransferTransferType (322) */ + /** @name StagingXcmExecutorAssetTransferTransferType (323) */ interface StagingXcmExecutorAssetTransferTransferType extends Enum { readonly isTeleport: boolean; readonly isLocalReserve: boolean; @@ -3782,7 +3803,7 @@ declare module "@polkadot/types/lookup" { readonly asRemoteReserve: XcmVersionedLocation; readonly type: "Teleport" | "LocalReserve" | "DestinationReserve" | "RemoteReserve"; } - /** @name XcmVersionedAssetId (323) */ + /** @name XcmVersionedAssetId (324) */ interface XcmVersionedAssetId extends Enum { readonly isV3: boolean; readonly asV3: XcmV3MultiassetAssetId; @@ -3790,9 +3811,9 @@ declare module "@polkadot/types/lookup" { readonly asV4: StagingXcmV4AssetAssetId; readonly type: "V3" | "V4"; } - /** @name CumulusPalletXcmCall (324) */ + /** @name CumulusPalletXcmCall (325) */ type CumulusPalletXcmCall = Null; - /** @name PalletMessageQueueCall (325) */ + /** @name PalletMessageQueueCall (326) */ interface PalletMessageQueueCall extends Enum { readonly isReapPage: boolean; readonly asReapPage: { @@ -3808,7 +3829,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "ReapPage" | "ExecuteOverweight"; } - /** @name PalletStorageProvidersCall (326) */ + /** @name PalletStorageProvidersCall (327) */ interface PalletStorageProvidersCall extends Enum { readonly isRequestMspSignUp: boolean; readonly asRequestMspSignUp: { @@ -3873,7 +3894,7 @@ declare module "@polkadot/types/lookup" { | "ForceBspSignUp" | "Slash"; } - /** @name PalletFileSystemCall (327) */ + /** @name PalletFileSystemCall (328) */ interface PalletFileSystemCall extends Enum { readonly isCreateBucket: boolean; readonly asCreateBucket: { @@ -3998,23 +4019,23 @@ declare module "@polkadot/types/lookup" { | "PendingFileDeletionRequestSubmitProof" | "SetGlobalParameters"; } - /** @name PalletFileSystemBucketMoveRequestResponse (328) */ + /** @name PalletFileSystemBucketMoveRequestResponse (329) */ interface PalletFileSystemBucketMoveRequestResponse extends Enum { readonly isAccepted: boolean; readonly isRejected: boolean; readonly type: "Accepted" | "Rejected"; } - /** @name PalletFileSystemMspStorageRequestResponse (331) */ + /** @name PalletFileSystemMspStorageRequestResponse (332) */ interface PalletFileSystemMspStorageRequestResponse extends Struct { readonly accept: Option; readonly reject: Option>>; } - /** @name PalletFileSystemAcceptedStorageRequestParameters (333) */ + /** @name PalletFileSystemAcceptedStorageRequestParameters (334) */ interface PalletFileSystemAcceptedStorageRequestParameters extends Struct { readonly fileKeysAndProofs: Vec>; readonly nonInclusionForestProof: SpTrieStorageProofCompactProof; } - /** @name PalletProofsDealerCall (340) */ + /** @name PalletProofsDealerCall (341) */ interface PalletProofsDealerCall extends Enum { readonly isChallenge: boolean; readonly asChallenge: { @@ -4035,12 +4056,12 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "Challenge" | "SubmitProof" | "ForceInitialiseChallengeCycle" | "SetPaused"; } - /** @name PalletRandomnessCall (341) */ + /** @name PalletRandomnessCall (342) */ interface PalletRandomnessCall extends Enum { readonly isSetBabeRandomness: boolean; readonly type: "SetBabeRandomness"; } - /** @name PalletPaymentStreamsCall (342) */ + /** @name PalletPaymentStreamsCall (343) */ interface PalletPaymentStreamsCall extends Enum { readonly isCreateFixedRatePaymentStream: boolean; readonly asCreateFixedRatePaymentStream: { @@ -4093,7 +4114,7 @@ declare module "@polkadot/types/lookup" { | "PayOutstandingDebt" | "ClearInsolventFlag"; } - /** @name PalletBucketNftsCall (343) */ + /** @name PalletBucketNftsCall (344) */ interface PalletBucketNftsCall extends Enum { readonly isShareAccess: boolean; readonly asShareAccess: { @@ -4110,7 +4131,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "ShareAccess" | "UpdateReadAccess"; } - /** @name PalletNftsCall (345) */ + /** @name PalletNftsCall (346) */ interface PalletNftsCall extends Enum { readonly isCreate: boolean; readonly asCreate: { @@ -4382,13 +4403,13 @@ declare module "@polkadot/types/lookup" { | "MintPreSigned" | "SetAttributesPreSigned"; } - /** @name PalletNftsCollectionConfig (346) */ + /** @name PalletNftsCollectionConfig (347) */ interface PalletNftsCollectionConfig extends Struct { readonly settings: u64; readonly maxSupply: Option; readonly mintSettings: PalletNftsMintSettings; } - /** @name PalletNftsCollectionSetting (348) */ + /** @name PalletNftsCollectionSetting (349) */ interface PalletNftsCollectionSetting extends Enum { readonly isTransferableItems: boolean; readonly isUnlockedMetadata: boolean; @@ -4402,7 +4423,7 @@ declare module "@polkadot/types/lookup" { | "UnlockedMaxSupply" | "DepositRequired"; } - /** @name PalletNftsMintSettings (349) */ + /** @name PalletNftsMintSettings (350) */ interface PalletNftsMintSettings extends Struct { readonly mintType: PalletNftsMintType; readonly price: Option; @@ -4410,7 +4431,7 @@ declare module "@polkadot/types/lookup" { readonly endBlock: Option; readonly defaultItemSettings: u64; } - /** @name PalletNftsMintType (350) */ + /** @name PalletNftsMintType (351) */ interface PalletNftsMintType extends Enum { readonly isIssuer: boolean; readonly isPublic: boolean; @@ -4418,40 +4439,40 @@ declare module "@polkadot/types/lookup" { readonly asHolderOf: u32; readonly type: "Issuer" | "Public" | "HolderOf"; } - /** @name PalletNftsItemSetting (353) */ + /** @name PalletNftsItemSetting (354) */ interface PalletNftsItemSetting extends Enum { readonly isTransferable: boolean; readonly isUnlockedMetadata: boolean; readonly isUnlockedAttributes: boolean; readonly type: "Transferable" | "UnlockedMetadata" | "UnlockedAttributes"; } - /** @name PalletNftsDestroyWitness (354) */ + /** @name PalletNftsDestroyWitness (355) */ interface PalletNftsDestroyWitness extends Struct { readonly itemMetadatas: Compact; readonly itemConfigs: Compact; readonly attributes: Compact; } - /** @name PalletNftsMintWitness (356) */ + /** @name PalletNftsMintWitness (357) */ interface PalletNftsMintWitness extends Struct { readonly ownedItem: Option; readonly mintPrice: Option; } - /** @name PalletNftsItemConfig (357) */ + /** @name PalletNftsItemConfig (358) */ interface PalletNftsItemConfig extends Struct { readonly settings: u64; } - /** @name PalletNftsCancelAttributesApprovalWitness (359) */ + /** @name PalletNftsCancelAttributesApprovalWitness (360) */ interface PalletNftsCancelAttributesApprovalWitness extends Struct { readonly accountAttributes: u32; } - /** @name PalletNftsItemTip (361) */ + /** @name PalletNftsItemTip (362) */ interface PalletNftsItemTip extends Struct { readonly collection: u32; readonly item: u32; readonly receiver: AccountId32; readonly amount: u128; } - /** @name PalletNftsPreSignedMint (363) */ + /** @name PalletNftsPreSignedMint (364) */ interface PalletNftsPreSignedMint extends Struct { readonly collection: u32; readonly item: u32; @@ -4461,7 +4482,7 @@ declare module "@polkadot/types/lookup" { readonly deadline: u32; readonly mintPrice: Option; } - /** @name SpRuntimeMultiSignature (364) */ + /** @name SpRuntimeMultiSignature (365) */ interface SpRuntimeMultiSignature extends Enum { readonly isEd25519: boolean; readonly asEd25519: U8aFixed; @@ -4471,7 +4492,7 @@ declare module "@polkadot/types/lookup" { readonly asEcdsa: U8aFixed; readonly type: "Ed25519" | "Sr25519" | "Ecdsa"; } - /** @name PalletNftsPreSignedAttributes (367) */ + /** @name PalletNftsPreSignedAttributes (368) */ interface PalletNftsPreSignedAttributes extends Struct { readonly collection: u32; readonly item: u32; @@ -4479,7 +4500,7 @@ declare module "@polkadot/types/lookup" { readonly namespace: PalletNftsAttributeNamespace; readonly deadline: u32; } - /** @name PalletParametersCall (368) */ + /** @name PalletParametersCall (369) */ interface PalletParametersCall extends Enum { readonly isSetParameter: boolean; readonly asSetParameter: { @@ -4487,13 +4508,13 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "SetParameter"; } - /** @name StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters (369) */ + /** @name StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters (370) */ interface StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters extends Enum { readonly isRuntimeConfig: boolean; readonly asRuntimeConfig: StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters; readonly type: "RuntimeConfig"; } - /** @name StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters (370) */ + /** @name StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters (371) */ interface StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters extends Enum { readonly isSlashAmountPerMaxFileSize: boolean; readonly asSlashAmountPerMaxFileSize: ITuple< @@ -4529,17 +4550,17 @@ declare module "@polkadot/types/lookup" { | "CheckpointChallengePeriod" | "MinChallengePeriod"; } - /** @name PalletSudoError (371) */ + /** @name PalletSudoError (372) */ interface PalletSudoError extends Enum { readonly isRequireSudo: boolean; readonly type: "RequireSudo"; } - /** @name PalletCollatorSelectionCandidateInfo (374) */ + /** @name PalletCollatorSelectionCandidateInfo (375) */ interface PalletCollatorSelectionCandidateInfo extends Struct { readonly who: AccountId32; readonly deposit: u128; } - /** @name PalletCollatorSelectionError (376) */ + /** @name PalletCollatorSelectionError (377) */ interface PalletCollatorSelectionError extends Enum { readonly isTooManyCandidates: boolean; readonly isTooFewEligibleCollators: boolean; @@ -4577,9 +4598,9 @@ declare module "@polkadot/types/lookup" { | "IdenticalDeposit" | "InvalidUnreserve"; } - /** @name SpCoreCryptoKeyTypeId (380) */ + /** @name SpCoreCryptoKeyTypeId (381) */ interface SpCoreCryptoKeyTypeId extends U8aFixed {} - /** @name PalletSessionError (381) */ + /** @name PalletSessionError (382) */ interface PalletSessionError extends Enum { readonly isInvalidProof: boolean; readonly isNoAssociatedValidatorId: boolean; @@ -4593,7 +4614,7 @@ declare module "@polkadot/types/lookup" { | "NoKeys" | "NoAccount"; } - /** @name CumulusPalletXcmpQueueOutboundChannelDetails (390) */ + /** @name CumulusPalletXcmpQueueOutboundChannelDetails (391) */ interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { readonly recipient: u32; readonly state: CumulusPalletXcmpQueueOutboundState; @@ -4601,26 +4622,33 @@ declare module "@polkadot/types/lookup" { readonly firstIndex: u16; readonly lastIndex: u16; } - /** @name CumulusPalletXcmpQueueOutboundState (391) */ + /** @name CumulusPalletXcmpQueueOutboundState (392) */ interface CumulusPalletXcmpQueueOutboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: "Ok" | "Suspended"; } - /** @name CumulusPalletXcmpQueueQueueConfigData (393) */ + /** @name CumulusPalletXcmpQueueQueueConfigData (396) */ interface CumulusPalletXcmpQueueQueueConfigData extends Struct { readonly suspendThreshold: u32; readonly dropThreshold: u32; readonly resumeThreshold: u32; } - /** @name CumulusPalletXcmpQueueError (394) */ + /** @name CumulusPalletXcmpQueueError (397) */ interface CumulusPalletXcmpQueueError extends Enum { readonly isBadQueueConfig: boolean; readonly isAlreadySuspended: boolean; readonly isAlreadyResumed: boolean; - readonly type: "BadQueueConfig" | "AlreadySuspended" | "AlreadyResumed"; + readonly isTooManyActiveOutboundChannels: boolean; + readonly isTooBig: boolean; + readonly type: + | "BadQueueConfig" + | "AlreadySuspended" + | "AlreadyResumed" + | "TooManyActiveOutboundChannels" + | "TooBig"; } - /** @name PalletXcmQueryStatus (395) */ + /** @name PalletXcmQueryStatus (398) */ interface PalletXcmQueryStatus extends Enum { readonly isPending: boolean; readonly asPending: { @@ -4641,7 +4669,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly type: "Pending" | "VersionNotifier" | "Ready"; } - /** @name XcmVersionedResponse (399) */ + /** @name XcmVersionedResponse (402) */ interface XcmVersionedResponse extends Enum { readonly isV2: boolean; readonly asV2: XcmV2Response; @@ -4651,7 +4679,7 @@ declare module "@polkadot/types/lookup" { readonly asV4: StagingXcmV4Response; readonly type: "V2" | "V3" | "V4"; } - /** @name PalletXcmVersionMigrationStage (405) */ + /** @name PalletXcmVersionMigrationStage (408) */ interface PalletXcmVersionMigrationStage extends Enum { readonly isMigrateSupportedVersion: boolean; readonly isMigrateVersionNotifiers: boolean; @@ -4664,14 +4692,14 @@ declare module "@polkadot/types/lookup" { | "NotifyCurrentTargets" | "MigrateAndNotifyOldTargets"; } - /** @name PalletXcmRemoteLockedFungibleRecord (408) */ + /** @name PalletXcmRemoteLockedFungibleRecord (411) */ interface PalletXcmRemoteLockedFungibleRecord extends Struct { readonly amount: u128; readonly owner: XcmVersionedLocation; readonly locker: XcmVersionedLocation; readonly consumers: Vec>; } - /** @name PalletXcmError (415) */ + /** @name PalletXcmError (418) */ interface PalletXcmError extends Enum { readonly isUnreachable: boolean; readonly isSendFailure: boolean; @@ -4723,7 +4751,7 @@ declare module "@polkadot/types/lookup" { | "TooManyReserves" | "LocalExecutionIncomplete"; } - /** @name PalletMessageQueueBookState (416) */ + /** @name PalletMessageQueueBookState (419) */ interface PalletMessageQueueBookState extends Struct { readonly begin: u32; readonly end: u32; @@ -4732,12 +4760,12 @@ declare module "@polkadot/types/lookup" { readonly messageCount: u64; readonly size_: u64; } - /** @name PalletMessageQueueNeighbours (418) */ + /** @name PalletMessageQueueNeighbours (421) */ interface PalletMessageQueueNeighbours extends Struct { readonly prev: CumulusPrimitivesCoreAggregateMessageOrigin; readonly next: CumulusPrimitivesCoreAggregateMessageOrigin; } - /** @name PalletMessageQueuePage (420) */ + /** @name PalletMessageQueuePage (423) */ interface PalletMessageQueuePage extends Struct { readonly remaining: u32; readonly remainingSize: u32; @@ -4746,7 +4774,7 @@ declare module "@polkadot/types/lookup" { readonly last: u32; readonly heap: Bytes; } - /** @name PalletMessageQueueError (422) */ + /** @name PalletMessageQueueError (425) */ interface PalletMessageQueueError extends Enum { readonly isNotReapable: boolean; readonly isNoPage: boolean; @@ -4768,7 +4796,7 @@ declare module "@polkadot/types/lookup" { | "QueuePaused" | "RecursiveDisallowed"; } - /** @name PalletStorageProvidersStorageProvider (424) */ + /** @name PalletStorageProvidersStorageProvider (427) */ interface PalletStorageProvidersStorageProvider extends Enum { readonly isBackupStorageProvider: boolean; readonly asBackupStorageProvider: PalletStorageProvidersBackupStorageProvider; @@ -4776,7 +4804,7 @@ declare module "@polkadot/types/lookup" { readonly asMainStorageProvider: PalletStorageProvidersMainStorageProvider; readonly type: "BackupStorageProvider" | "MainStorageProvider"; } - /** @name PalletStorageProvidersBackupStorageProvider (425) */ + /** @name PalletStorageProvidersBackupStorageProvider (428) */ interface PalletStorageProvidersBackupStorageProvider extends Struct { readonly capacity: u64; readonly capacityUsed: u64; @@ -4787,7 +4815,7 @@ declare module "@polkadot/types/lookup" { readonly paymentAccount: AccountId32; readonly reputationWeight: u32; } - /** @name PalletStorageProvidersMainStorageProvider (426) */ + /** @name PalletStorageProvidersMainStorageProvider (429) */ interface PalletStorageProvidersMainStorageProvider extends Struct { readonly buckets: Vec; readonly capacity: u64; @@ -4798,7 +4826,7 @@ declare module "@polkadot/types/lookup" { readonly ownerAccount: AccountId32; readonly paymentAccount: AccountId32; } - /** @name PalletStorageProvidersBucket (428) */ + /** @name PalletStorageProvidersBucket (431) */ interface PalletStorageProvidersBucket extends Struct { readonly root: H256; readonly userId: AccountId32; @@ -4807,7 +4835,7 @@ declare module "@polkadot/types/lookup" { readonly readAccessGroupId: Option; readonly size_: u64; } - /** @name PalletStorageProvidersError (431) */ + /** @name PalletStorageProvidersError (434) */ interface PalletStorageProvidersError extends Enum { readonly isAlreadyRegistered: boolean; readonly isSignUpNotRequested: boolean; @@ -4865,7 +4893,7 @@ declare module "@polkadot/types/lookup" { | "InvalidEncodedAccountId" | "PaymentStreamNotFound"; } - /** @name PalletFileSystemStorageRequestMetadata (432) */ + /** @name PalletFileSystemStorageRequestMetadata (435) */ interface PalletFileSystemStorageRequestMetadata extends Struct { readonly requestedAt: u32; readonly owner: AccountId32; @@ -4880,15 +4908,15 @@ declare module "@polkadot/types/lookup" { readonly bspsConfirmed: u32; readonly bspsVolunteered: u32; } - /** @name PalletFileSystemStorageRequestBspsMetadata (437) */ + /** @name PalletFileSystemStorageRequestBspsMetadata (440) */ interface PalletFileSystemStorageRequestBspsMetadata extends Struct { readonly confirmed: bool; } - /** @name PalletFileSystemMoveBucketRequestMetadata (446) */ + /** @name PalletFileSystemMoveBucketRequestMetadata (449) */ interface PalletFileSystemMoveBucketRequestMetadata extends Struct { readonly requester: AccountId32; } - /** @name PalletFileSystemError (447) */ + /** @name PalletFileSystemError (450) */ interface PalletFileSystemError extends Enum { readonly isStorageRequestAlreadyRegistered: boolean; readonly isStorageRequestNotFound: boolean; @@ -5014,7 +5042,7 @@ declare module "@polkadot/types/lookup" { | "InvalidBucketIdFileKeyPair" | "InconsistentStateKeyAlreadyExists"; } - /** @name PalletProofsDealerError (454) */ + /** @name PalletProofsDealerError (457) */ interface PalletProofsDealerError extends Enum { readonly isNotProvider: boolean; readonly isChallengesQueueOverflow: boolean; @@ -5062,26 +5090,26 @@ declare module "@polkadot/types/lookup" { | "FailedToUpdateProviderAfterKeyRemoval" | "TooManyValidProofSubmitters"; } - /** @name PalletPaymentStreamsFixedRatePaymentStream (457) */ + /** @name PalletPaymentStreamsFixedRatePaymentStream (460) */ interface PalletPaymentStreamsFixedRatePaymentStream extends Struct { readonly rate: u128; readonly lastChargedTick: u32; readonly userDeposit: u128; readonly outOfFundsTick: Option; } - /** @name PalletPaymentStreamsDynamicRatePaymentStream (458) */ + /** @name PalletPaymentStreamsDynamicRatePaymentStream (461) */ interface PalletPaymentStreamsDynamicRatePaymentStream extends Struct { readonly amountProvided: u64; readonly priceIndexWhenLastCharged: u128; readonly userDeposit: u128; readonly outOfFundsTick: Option; } - /** @name PalletPaymentStreamsProviderLastChargeableInfo (459) */ + /** @name PalletPaymentStreamsProviderLastChargeableInfo (462) */ interface PalletPaymentStreamsProviderLastChargeableInfo extends Struct { readonly lastChargeableTick: u32; readonly priceIndex: u128; } - /** @name PalletPaymentStreamsError (460) */ + /** @name PalletPaymentStreamsError (463) */ interface PalletPaymentStreamsError extends Enum { readonly isPaymentStreamAlreadyExists: boolean; readonly isPaymentStreamNotFound: boolean; @@ -5117,7 +5145,7 @@ declare module "@polkadot/types/lookup" { | "UserNotFlaggedAsWithoutFunds" | "CooldownPeriodNotPassed"; } - /** @name PalletBucketNftsError (461) */ + /** @name PalletBucketNftsError (464) */ interface PalletBucketNftsError extends Enum { readonly isBucketIsNotPrivate: boolean; readonly isNotBucketOwner: boolean; @@ -5129,7 +5157,7 @@ declare module "@polkadot/types/lookup" { | "NoCorrespondingCollection" | "ConvertBytesToBoundedVec"; } - /** @name PalletNftsCollectionDetails (462) */ + /** @name PalletNftsCollectionDetails (465) */ interface PalletNftsCollectionDetails extends Struct { readonly owner: AccountId32; readonly ownerDeposit: u128; @@ -5138,52 +5166,52 @@ declare module "@polkadot/types/lookup" { readonly itemConfigs: u32; readonly attributes: u32; } - /** @name PalletNftsCollectionRole (467) */ + /** @name PalletNftsCollectionRole (470) */ interface PalletNftsCollectionRole extends Enum { readonly isIssuer: boolean; readonly isFreezer: boolean; readonly isAdmin: boolean; readonly type: "Issuer" | "Freezer" | "Admin"; } - /** @name PalletNftsItemDetails (468) */ + /** @name PalletNftsItemDetails (471) */ interface PalletNftsItemDetails extends Struct { readonly owner: AccountId32; readonly approvals: BTreeMap>; readonly deposit: PalletNftsItemDeposit; } - /** @name PalletNftsItemDeposit (469) */ + /** @name PalletNftsItemDeposit (472) */ interface PalletNftsItemDeposit extends Struct { readonly account: AccountId32; readonly amount: u128; } - /** @name PalletNftsCollectionMetadata (474) */ + /** @name PalletNftsCollectionMetadata (477) */ interface PalletNftsCollectionMetadata extends Struct { readonly deposit: u128; readonly data: Bytes; } - /** @name PalletNftsItemMetadata (475) */ + /** @name PalletNftsItemMetadata (478) */ interface PalletNftsItemMetadata extends Struct { readonly deposit: PalletNftsItemMetadataDeposit; readonly data: Bytes; } - /** @name PalletNftsItemMetadataDeposit (476) */ + /** @name PalletNftsItemMetadataDeposit (479) */ interface PalletNftsItemMetadataDeposit extends Struct { readonly account: Option; readonly amount: u128; } - /** @name PalletNftsAttributeDeposit (479) */ + /** @name PalletNftsAttributeDeposit (482) */ interface PalletNftsAttributeDeposit extends Struct { readonly account: Option; readonly amount: u128; } - /** @name PalletNftsPendingSwap (483) */ + /** @name PalletNftsPendingSwap (486) */ interface PalletNftsPendingSwap extends Struct { readonly desiredCollection: u32; readonly desiredItem: Option; readonly price: Option; readonly deadline: u32; } - /** @name PalletNftsPalletFeature (485) */ + /** @name PalletNftsPalletFeature (488) */ interface PalletNftsPalletFeature extends Enum { readonly isTrading: boolean; readonly isAttributes: boolean; @@ -5191,7 +5219,7 @@ declare module "@polkadot/types/lookup" { readonly isSwaps: boolean; readonly type: "Trading" | "Attributes" | "Approvals" | "Swaps"; } - /** @name PalletNftsError (486) */ + /** @name PalletNftsError (489) */ interface PalletNftsError extends Enum { readonly isNoPermission: boolean; readonly isUnknownCollection: boolean; @@ -5285,32 +5313,32 @@ declare module "@polkadot/types/lookup" { | "CollectionNotEmpty" | "WitnessRequired"; } - /** @name FrameSystemExtensionsCheckNonZeroSender (489) */ + /** @name FrameSystemExtensionsCheckNonZeroSender (492) */ type FrameSystemExtensionsCheckNonZeroSender = Null; - /** @name FrameSystemExtensionsCheckSpecVersion (490) */ + /** @name FrameSystemExtensionsCheckSpecVersion (493) */ type FrameSystemExtensionsCheckSpecVersion = Null; - /** @name FrameSystemExtensionsCheckTxVersion (491) */ + /** @name FrameSystemExtensionsCheckTxVersion (494) */ type FrameSystemExtensionsCheckTxVersion = Null; - /** @name FrameSystemExtensionsCheckGenesis (492) */ + /** @name FrameSystemExtensionsCheckGenesis (495) */ type FrameSystemExtensionsCheckGenesis = Null; - /** @name FrameSystemExtensionsCheckNonce (495) */ + /** @name FrameSystemExtensionsCheckNonce (498) */ interface FrameSystemExtensionsCheckNonce extends Compact {} - /** @name FrameSystemExtensionsCheckWeight (496) */ + /** @name FrameSystemExtensionsCheckWeight (499) */ type FrameSystemExtensionsCheckWeight = Null; - /** @name PalletTransactionPaymentChargeTransactionPayment (497) */ + /** @name PalletTransactionPaymentChargeTransactionPayment (500) */ interface PalletTransactionPaymentChargeTransactionPayment extends Compact {} - /** @name CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim (498) */ + /** @name CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim (501) */ type CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim = Null; - /** @name FrameMetadataHashExtensionCheckMetadataHash (499) */ + /** @name FrameMetadataHashExtensionCheckMetadataHash (502) */ interface FrameMetadataHashExtensionCheckMetadataHash extends Struct { readonly mode: FrameMetadataHashExtensionMode; } - /** @name FrameMetadataHashExtensionMode (500) */ + /** @name FrameMetadataHashExtensionMode (503) */ interface FrameMetadataHashExtensionMode extends Enum { readonly isDisabled: boolean; readonly isEnabled: boolean; readonly type: "Disabled" | "Enabled"; } - /** @name StorageHubRuntimeRuntime (501) */ + /** @name StorageHubRuntimeRuntime (504) */ type StorageHubRuntimeRuntime = Null; } diff --git a/api-augment/package.json b/api-augment/package.json index cd0c368c9..2e6b64eb0 100644 --- a/api-augment/package.json +++ b/api-augment/package.json @@ -25,12 +25,12 @@ }, "license": "ISC", "dependencies": { - "@polkadot/api": "12.4.2", - "@polkadot/api-base": "12.4.2", - "@polkadot/rpc-core": "12.4.2", - "@polkadot/typegen": "12.4.2", - "@polkadot/types": "12.4.2", - "@polkadot/types-codec": "12.4.2", + "@polkadot/api": "14.0.1", + "@polkadot/api-base": "14.0.1", + "@polkadot/rpc-core": "14.0.1", + "@polkadot/typegen": "14.0.1", + "@polkadot/types": "14.0.1", + "@polkadot/types-codec": "14.0.1", "@storagehub/types-bundle": "workspace:*", "tsx": "4.19.0", "typescript": "5.5.4" diff --git a/api-augment/src/interfaces/augment-api-consts.ts b/api-augment/src/interfaces/augment-api-consts.ts index 2f7ad2ead..af81d7dd7 100644 --- a/api-augment/src/interfaces/augment-api-consts.ts +++ b/api-augment/src/interfaces/augment-api-consts.ts @@ -534,6 +534,17 @@ declare module "@polkadot/api-base/types/consts" { [key: string]: Codec; }; xcmpQueue: { + /** + * Maximal number of outbound XCMP channels that can have messages queued at the same time. + * + * If this is reached, then no further messages can be sent to channels that do not yet + * have a message queued. This should be set to the expected maximum of outbound channels + * which is determined by [`Self::ChannelInfo`]. It is important to set this large enough, + * since otherwise the congestion control protocol will not work as intended and messages + * may be dropped. This value increases the PoV and should therefore not be picked too + * high. Governance needs to pay attention to not open more channels than this value. + **/ + maxActiveOutboundChannels: u32 & AugmentedConst; /** * The maximum number of inbound XCMP channels that can be suspended simultaneously. * @@ -542,6 +553,14 @@ declare module "@polkadot/api-base/types/consts" { * [`InboundXcmpSuspended`] still applies at that scale. **/ maxInboundSuspended: u32 & AugmentedConst; + /** + * The maximal page size for HRMP message pages. + * + * A lower limit can be set dynamically, but this is the hard-limit for the PoV worst case + * benchmarking. The limit for the size of a message is slightly below this, since some + * overhead is incurred for encoding the format. + **/ + maxPageSize: u32 & AugmentedConst; /** * Generic const **/ diff --git a/api-augment/src/interfaces/augment-api-errors.ts b/api-augment/src/interfaces/augment-api-errors.ts index 464256243..7b5af4ea5 100644 --- a/api-augment/src/interfaces/augment-api-errors.ts +++ b/api-augment/src/interfaces/augment-api-errors.ts @@ -1186,6 +1186,14 @@ declare module "@polkadot/api-base/types/errors" { * Setting the queue config failed since one of its values was invalid. **/ BadQueueConfig: AugmentedError; + /** + * The message is too big. + **/ + TooBig: AugmentedError; + /** + * There are too many active outbound channels. + **/ + TooManyActiveOutboundChannels: AugmentedError; /** * Generic error **/ diff --git a/api-augment/src/interfaces/augment-api-query.ts b/api-augment/src/interfaces/augment-api-query.ts index 15af75e48..7a774d051 100644 --- a/api-augment/src/interfaces/augment-api-query.ts +++ b/api-augment/src/interfaces/augment-api-query.ts @@ -77,6 +77,8 @@ import type { SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, + StagingXcmV4Instruction, + StagingXcmV4Xcm, StorageHubRuntimeConfigsRuntimeParamsRuntimeParametersKey, StorageHubRuntimeConfigsRuntimeParamsRuntimeParametersValue, StorageHubRuntimeRuntimeHoldReason, @@ -1149,6 +1151,20 @@ declare module "@polkadot/api-base/types/storage" { **/ queryCounter: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally + * will be stored here. + * Runtime APIs can fetch the XCM that was executed by accessing this value. + * + * Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + * implementation in the XCM executor configuration. + **/ + recordedXcm: AugmentedQuery< + ApiType, + () => Observable>>, + [] + > & + QueryableStorageEntry; /** * Fungible assets which we know are locked on a remote chain. **/ @@ -1168,6 +1184,17 @@ declare module "@polkadot/api-base/types/storage" { **/ safeXcmVersion: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Whether or not incoming XCMs (both executed locally and received) should be recorded. + * Only one XCM program will be recorded at a time. + * This is meant to be used in runtime APIs, and it's advised it stays false + * for all other use cases, so as to not degrade regular performance. + * + * Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + * implementation in the XCM executor configuration. + **/ + shouldRecordXcm: AugmentedQuery Observable, []> & + QueryableStorageEntry; /** * The Latest versions that we know various locations support. **/ diff --git a/api-augment/src/interfaces/augment-api-tx.ts b/api-augment/src/interfaces/augment-api-tx.ts index 856f2db78..31d5ad652 100644 --- a/api-augment/src/interfaces/augment-api-tx.ts +++ b/api-augment/src/interfaces/augment-api-tx.ts @@ -56,6 +56,22 @@ export type __SubmittableExtrinsicFunction = declare module "@polkadot/api-base/types/submittable" { interface AugmentedSubmittables { balances: { + /** + * Burn the specified liquid free balance from the origin account. + * + * If the origin's account ends up below the existential deposit as a result + * of the burn and `keep_alive` is false, the account will be reaped. + * + * Unlike sending funds to a _burn_ address, which merely makes the funds inaccessible, + * this `burn` operation will reduce total issuance by the amount _burned_. + **/ + burn: AugmentedSubmittable< + ( + value: Compact | AnyNumber | Uint8Array, + keepAlive: bool | boolean | Uint8Array + ) => SubmittableExtrinsic, + [Compact, bool] + >; /** * Adjust the total issuance in a saturating way. * diff --git a/api-augment/src/interfaces/augment-types.ts b/api-augment/src/interfaces/augment-types.ts index 0e7a0a143..5f11d65bb 100644 --- a/api-augment/src/interfaces/augment-types.ts +++ b/api-augment/src/interfaces/augment-types.ts @@ -324,6 +324,13 @@ import type { VotingDirectVote } from "@polkadot/types/interfaces/democracy"; import type { BlockStats } from "@polkadot/types/interfaces/dev"; +import type { + CallDryRunEffects, + DispatchResultWithPostInfo, + PostDispatchInfo, + XcmDryRunApiError, + XcmDryRunEffects +} from "@polkadot/types/interfaces/dryRunApi"; import type { ApprovalFlag, DefunctVoter, @@ -409,10 +416,13 @@ import type { ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, + ExtrinsicPayloadV5, ExtrinsicSignature, ExtrinsicSignatureV4, + ExtrinsicSignatureV5, ExtrinsicUnknown, ExtrinsicV4, + ExtrinsicV5, ImmortalEra, MortalEra, MultiSignature, @@ -1176,48 +1186,88 @@ import type { import type { Multisig, Timepoint } from "@polkadot/types/interfaces/utility"; import type { VestingInfo } from "@polkadot/types/interfaces/vesting"; import type { + AssetIdV2, + AssetIdV3, + AssetIdV4, AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, + AssetInstanceV3, + AssetInstanceV4, BodyId, + BodyIdV2, + BodyIdV3, BodyPart, + BodyPartV2, + BodyPartV3, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, + FungibilityV3, + FungibilityV4, InboundStatus, InstructionV2, + InstructionV3, + InstructionV4, InteriorMultiLocation, + InteriorMultiLocationV2, + InteriorMultiLocationV3, Junction, JunctionV0, JunctionV1, JunctionV2, + JunctionV3, + JunctionV4, Junctions, JunctionsV1, JunctionsV2, + JunctionsV3, + JunctionsV4, + MaxPalletNameLen, + MaxPalletsInfo, + MaybeErrorCodeV3, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, + MultiAssetFilterV3, + MultiAssetFilterV4, MultiAssetV0, MultiAssetV1, MultiAssetV2, + MultiAssetV3, + MultiAssetV4, MultiAssets, MultiAssetsV1, MultiAssetsV2, + MultiAssetsV3, + MultiAssetsV4, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, + MultiLocationV3, + MultiLocationV4, NetworkId, + NetworkIdV2, + NetworkIdV3, + NetworkIdV4, OriginKindV0, OriginKindV1, OriginKindV2, + OriginKindV3, + OriginKindV4, OutboundStatus, Outcome, + OutcomeV4, + PalletInfoV3, + PalletInfoV4, QueryId, + QueryResponseInfoV3, + QueryResponseInfoV4, QueryStatus, QueueConfigData, Response, @@ -1225,36 +1275,49 @@ import type { ResponseV1, ResponseV2, ResponseV2Error, - ResponseV2Result, + ResponseV3, + ResponseV3Error, + ResponseV3Result, + ResponseV4, + UncheckedFungibilityV4, VersionMigrationStage, + VersionV3, + VersionV4, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, + WeightLimitV3, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, + WildFungibilityV3, + WildFungibilityV4, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, + WildMultiAssetV3, + WildMultiAssetV4, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, - XcmOrder, + XcmErrorV3, + XcmErrorV4, XcmOrderV0, XcmOrderV1, - XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, + XcmV3, + XcmV4, XcmVersion, XcmpMessageFormat } from "@polkadot/types/interfaces/xcm"; @@ -1337,10 +1400,15 @@ declare module "@polkadot/types/types/registry" { AssetDestroyWitness: AssetDestroyWitness; AssetDetails: AssetDetails; AssetId: AssetId; + AssetIdV2: AssetIdV2; + AssetIdV3: AssetIdV3; + AssetIdV4: AssetIdV4; AssetInstance: AssetInstance; AssetInstanceV0: AssetInstanceV0; AssetInstanceV1: AssetInstanceV1; AssetInstanceV2: AssetInstanceV2; + AssetInstanceV3: AssetInstanceV3; + AssetInstanceV4: AssetInstanceV4; AssetMetadata: AssetMetadata; AssetOptions: AssetOptions; AssignmentId: AssignmentId; @@ -1415,7 +1483,11 @@ declare module "@polkadot/types/types/registry" { BlockV2: BlockV2; BlockWeights: BlockWeights; BodyId: BodyId; + BodyIdV2: BodyIdV2; + BodyIdV3: BodyIdV3; BodyPart: BodyPart; + BodyPartV2: BodyPartV2; + BodyPartV3: BodyPartV3; bool: bool; Bool: Bool; Bounty: Bounty; @@ -1431,6 +1503,7 @@ declare module "@polkadot/types/types/registry" { BufferedSessionChange: BufferedSessionChange; Bytes: Bytes; Call: Call; + CallDryRunEffects: CallDryRunEffects; CallHash: CallHash; CallHashOf: CallHashOf; CallIndex: CallIndex; @@ -1591,6 +1664,7 @@ declare module "@polkadot/types/types/registry" { DispatchResult: DispatchResult; DispatchResultOf: DispatchResultOf; DispatchResultTo198: DispatchResultTo198; + DispatchResultWithPostInfo: DispatchResultWithPostInfo; DisputeLocation: DisputeLocation; DisputeProof: DisputeProof; DisputeResult: DisputeResult; @@ -1710,12 +1784,15 @@ declare module "@polkadot/types/types/registry" { ExtrinsicPayload: ExtrinsicPayload; ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown; ExtrinsicPayloadV4: ExtrinsicPayloadV4; + ExtrinsicPayloadV5: ExtrinsicPayloadV5; ExtrinsicSignature: ExtrinsicSignature; ExtrinsicSignatureV4: ExtrinsicSignatureV4; + ExtrinsicSignatureV5: ExtrinsicSignatureV5; ExtrinsicStatus: ExtrinsicStatus; ExtrinsicsWeight: ExtrinsicsWeight; ExtrinsicUnknown: ExtrinsicUnknown; ExtrinsicV4: ExtrinsicV4; + ExtrinsicV5: ExtrinsicV5; f32: f32; F32: F32; f64: f64; @@ -1752,6 +1829,8 @@ declare module "@polkadot/types/types/registry" { FungibilityV0: FungibilityV0; FungibilityV1: FungibilityV1; FungibilityV2: FungibilityV2; + FungibilityV3: FungibilityV3; + FungibilityV4: FungibilityV4; FungiblesAccessError: FungiblesAccessError; Gas: Gas; GenesisBuildErr: GenesisBuildErr; @@ -1847,8 +1926,12 @@ declare module "@polkadot/types/types/registry" { InstantiateReturnValueOk: InstantiateReturnValueOk; InstantiateReturnValueTo267: InstantiateReturnValueTo267; InstructionV2: InstructionV2; + InstructionV3: InstructionV3; + InstructionV4: InstructionV4; InstructionWeights: InstructionWeights; InteriorMultiLocation: InteriorMultiLocation; + InteriorMultiLocationV2: InteriorMultiLocationV2; + InteriorMultiLocationV3: InteriorMultiLocationV3; InvalidDisputeStatementKind: InvalidDisputeStatementKind; InvalidTransaction: InvalidTransaction; isize: isize; @@ -1858,9 +1941,13 @@ declare module "@polkadot/types/types/registry" { Junctions: Junctions; JunctionsV1: JunctionsV1; JunctionsV2: JunctionsV2; + JunctionsV3: JunctionsV3; + JunctionsV4: JunctionsV4; JunctionV0: JunctionV0; JunctionV1: JunctionV1; JunctionV2: JunctionV2; + JunctionV3: JunctionV3; + JunctionV4: JunctionV4; Justification: Justification; JustificationNotification: JustificationNotification; Justifications: Justifications; @@ -1886,6 +1973,9 @@ declare module "@polkadot/types/types/registry" { LookupTarget: LookupTarget; LotteryConfig: LotteryConfig; MainStorageProviderId: MainStorageProviderId; + MaxPalletNameLen: MaxPalletNameLen; + MaxPalletsInfo: MaxPalletsInfo; + MaybeErrorCodeV3: MaybeErrorCodeV3; MaybeRandomness: MaybeRandomness; MaybeVrf: MaybeVrf; MemberCount: MemberCount; @@ -1943,22 +2033,33 @@ declare module "@polkadot/types/types/registry" { MultiAssetFilter: MultiAssetFilter; MultiAssetFilterV1: MultiAssetFilterV1; MultiAssetFilterV2: MultiAssetFilterV2; + MultiAssetFilterV3: MultiAssetFilterV3; + MultiAssetFilterV4: MultiAssetFilterV4; MultiAssets: MultiAssets; MultiAssetsV1: MultiAssetsV1; MultiAssetsV2: MultiAssetsV2; + MultiAssetsV3: MultiAssetsV3; + MultiAssetsV4: MultiAssetsV4; MultiAssetV0: MultiAssetV0; MultiAssetV1: MultiAssetV1; MultiAssetV2: MultiAssetV2; + MultiAssetV3: MultiAssetV3; + MultiAssetV4: MultiAssetV4; MultiDisputeStatementSet: MultiDisputeStatementSet; MultiLocation: MultiLocation; MultiLocationV0: MultiLocationV0; MultiLocationV1: MultiLocationV1; MultiLocationV2: MultiLocationV2; + MultiLocationV3: MultiLocationV3; + MultiLocationV4: MultiLocationV4; Multiplier: Multiplier; Multisig: Multisig; MultiSignature: MultiSignature; MultiSigner: MultiSigner; NetworkId: NetworkId; + NetworkIdV2: NetworkIdV2; + NetworkIdV3: NetworkIdV3; + NetworkIdV4: NetworkIdV4; NetworkState: NetworkState; NetworkStatePeerset: NetworkStatePeerset; NetworkStatePeersetInfo: NetworkStatePeersetInfo; @@ -2002,6 +2103,8 @@ declare module "@polkadot/types/types/registry" { OriginKindV0: OriginKindV0; OriginKindV1: OriginKindV1; OriginKindV2: OriginKindV2; + OriginKindV3: OriginKindV3; + OriginKindV4: OriginKindV4; OutboundHrmpChannelLimitations: OutboundHrmpChannelLimitations; OutboundHrmpMessage: OutboundHrmpMessage; OutboundLaneData: OutboundLaneData; @@ -2009,6 +2112,7 @@ declare module "@polkadot/types/types/registry" { OutboundPayload: OutboundPayload; OutboundStatus: OutboundStatus; Outcome: Outcome; + OutcomeV4: OutcomeV4; OuterEnums15: OuterEnums15; OverweightIndex: OverweightIndex; Owner: Owner; @@ -2023,6 +2127,8 @@ declare module "@polkadot/types/types/registry" { PalletEventMetadataLatest: PalletEventMetadataLatest; PalletEventMetadataV14: PalletEventMetadataV14; PalletId: PalletId; + PalletInfoV3: PalletInfoV3; + PalletInfoV4: PalletInfoV4; PalletMetadataLatest: PalletMetadataLatest; PalletMetadataV14: PalletMetadataV14; PalletMetadataV15: PalletMetadataV15; @@ -2075,6 +2181,7 @@ declare module "@polkadot/types/types/registry" { Points: Points; PortableType: PortableType; PortableTypeV14: PortableTypeV14; + PostDispatchInfo: PostDispatchInfo; Precommits: Precommits; PrefabWasmModule: PrefabWasmModule; PrefixedStorageKey: PrefixedStorageKey; @@ -2103,6 +2210,8 @@ declare module "@polkadot/types/types/registry" { QueryId: QueryId; QueryMspConfirmChunksToProveForFileError: QueryMspConfirmChunksToProveForFileError; QueryMspIdOfBucketIdError: QueryMspIdOfBucketIdError; + QueryResponseInfoV3: QueryResponseInfoV3; + QueryResponseInfoV4: QueryResponseInfoV4; QueryStatus: QueryStatus; QueryStorageProviderCapacityError: QueryStorageProviderCapacityError; QueueConfigData: QueueConfigData; @@ -2162,7 +2271,10 @@ declare module "@polkadot/types/types/registry" { ResponseV1: ResponseV1; ResponseV2: ResponseV2; ResponseV2Error: ResponseV2Error; - ResponseV2Result: ResponseV2Result; + ResponseV3: ResponseV3; + ResponseV3Error: ResponseV3Error; + ResponseV3Result: ResponseV3Result; + ResponseV4: ResponseV4; Retriable: Retriable; RewardDestination: RewardDestination; RewardPoint: RewardPoint; @@ -2403,6 +2515,7 @@ declare module "@polkadot/types/types/registry" { U8: U8; UnappliedSlash: UnappliedSlash; UnappliedSlashOther: UnappliedSlashOther; + UncheckedFungibilityV4: UncheckedFungibilityV4; UncleEntryItem: UncleEntryItem; UnknownTransaction: UnknownTransaction; UnlockChunk: UnlockChunk; @@ -2441,6 +2554,8 @@ declare module "@polkadot/types/types/registry" { VersionedResponse: VersionedResponse; VersionedXcm: VersionedXcm; VersionMigrationStage: VersionMigrationStage; + VersionV3: VersionV3; + VersionV4: VersionV4; VestingInfo: VestingInfo; VestingSchedule: VestingSchedule; Vote: Vote; @@ -2461,6 +2576,7 @@ declare module "@polkadot/types/types/registry" { VrfProof: VrfProof; Weight: Weight; WeightLimitV2: WeightLimitV2; + WeightLimitV3: WeightLimitV3; WeightMultiplier: WeightMultiplier; WeightPerClass: WeightPerClass; WeightToFeeCoefficient: WeightToFeeCoefficient; @@ -2471,9 +2587,13 @@ declare module "@polkadot/types/types/registry" { WildFungibilityV0: WildFungibilityV0; WildFungibilityV1: WildFungibilityV1; WildFungibilityV2: WildFungibilityV2; + WildFungibilityV3: WildFungibilityV3; + WildFungibilityV4: WildFungibilityV4; WildMultiAsset: WildMultiAsset; WildMultiAssetV1: WildMultiAssetV1; WildMultiAssetV2: WildMultiAssetV2; + WildMultiAssetV3: WildMultiAssetV3; + WildMultiAssetV4: WildMultiAssetV4; WinnersData: WinnersData; WinnersData10: WinnersData10; WinnersDataTuple: WinnersDataTuple; @@ -2484,14 +2604,16 @@ declare module "@polkadot/types/types/registry" { WithdrawReasons: WithdrawReasons; Xcm: Xcm; XcmAssetId: XcmAssetId; + XcmDryRunApiError: XcmDryRunApiError; + XcmDryRunEffects: XcmDryRunEffects; XcmError: XcmError; XcmErrorV0: XcmErrorV0; XcmErrorV1: XcmErrorV1; XcmErrorV2: XcmErrorV2; - XcmOrder: XcmOrder; + XcmErrorV3: XcmErrorV3; + XcmErrorV4: XcmErrorV4; XcmOrderV0: XcmOrderV0; XcmOrderV1: XcmOrderV1; - XcmOrderV2: XcmOrderV2; XcmOrigin: XcmOrigin; XcmOriginKind: XcmOriginKind; XcmPaymentApiError: XcmPaymentApiError; @@ -2499,6 +2621,8 @@ declare module "@polkadot/types/types/registry" { XcmV0: XcmV0; XcmV1: XcmV1; XcmV2: XcmV2; + XcmV3: XcmV3; + XcmV4: XcmV4; XcmVersion: XcmVersion; } // InterfaceTypes } // declare module diff --git a/api-augment/src/interfaces/lookup.ts b/api-augment/src/interfaces/lookup.ts index 8bd741fc0..9ff267b96 100644 --- a/api-augment/src/interfaces/lookup.ts +++ b/api-augment/src/interfaces/lookup.ts @@ -735,7 +735,7 @@ export default { xcm: "StagingXcmV4Xcm" }, Transact: { - originKind: "XcmV2OriginKind", + originKind: "XcmV3OriginKind", requireWeightAtMost: "SpWeightsWeightV2Weight", call: "XcmDoubleEncoded" }, @@ -925,9 +925,9 @@ export default { } }, /** - * Lookup86: xcm::v2::OriginKind + * Lookup86: xcm::v3::OriginKind **/ - XcmV2OriginKind: { + XcmV3OriginKind: { _enum: ["Native", "SovereignAccount", "Superuser", "Xcm"] }, /** @@ -1332,7 +1332,8 @@ export default { Corrupt: "Null", Unsupported: "Null", Overweight: "SpWeightsWeightV2Weight", - Yield: "Null" + Yield: "Null", + StackLimitReached: "Null" } }, /** @@ -2533,6 +2534,10 @@ export default { force_adjust_total_issuance: { direction: "PalletBalancesAdjustmentDirection", delta: "Compact" + }, + burn: { + value: "Compact", + keepAlive: "bool" } } }, @@ -2915,7 +2920,13 @@ export default { } }, /** - * Lookup294: xcm::v2::multiasset::MultiAssetFilter + * Lookup294: xcm::v2::OriginKind + **/ + XcmV2OriginKind: { + _enum: ["Native", "SovereignAccount", "Superuser", "Xcm"] + }, + /** + * Lookup295: xcm::v2::multiasset::MultiAssetFilter **/ XcmV2MultiassetMultiAssetFilter: { _enum: { @@ -2924,7 +2935,7 @@ export default { } }, /** - * Lookup295: xcm::v2::multiasset::WildMultiAsset + * Lookup296: xcm::v2::multiasset::WildMultiAsset **/ XcmV2MultiassetWildMultiAsset: { _enum: { @@ -2936,13 +2947,13 @@ export default { } }, /** - * Lookup296: xcm::v2::multiasset::WildFungibility + * Lookup297: xcm::v2::multiasset::WildFungibility **/ XcmV2MultiassetWildFungibility: { _enum: ["Fungible", "NonFungible"] }, /** - * Lookup297: xcm::v2::WeightLimit + * Lookup298: xcm::v2::WeightLimit **/ XcmV2WeightLimit: { _enum: { @@ -2951,11 +2962,11 @@ export default { } }, /** - * Lookup298: xcm::v3::Xcm + * Lookup299: xcm::v3::Xcm **/ XcmV3Xcm: "Vec", /** - * Lookup300: xcm::v3::Instruction + * Lookup301: xcm::v3::Instruction **/ XcmV3Instruction: { _enum: { @@ -2978,7 +2989,7 @@ export default { xcm: "XcmV3Xcm" }, Transact: { - originKind: "XcmV2OriginKind", + originKind: "XcmV3OriginKind", requireWeightAtMost: "SpWeightsWeightV2Weight", call: "XcmDoubleEncoded" }, @@ -3097,7 +3108,7 @@ export default { } }, /** - * Lookup301: xcm::v3::Response + * Lookup302: xcm::v3::Response **/ XcmV3Response: { _enum: { @@ -3110,7 +3121,7 @@ export default { } }, /** - * Lookup303: xcm::v3::PalletInfo + * Lookup304: xcm::v3::PalletInfo **/ XcmV3PalletInfo: { index: "Compact", @@ -3121,7 +3132,7 @@ export default { patch: "Compact" }, /** - * Lookup307: xcm::v3::QueryResponseInfo + * Lookup308: xcm::v3::QueryResponseInfo **/ XcmV3QueryResponseInfo: { destination: "StagingXcmV3MultiLocation", @@ -3129,7 +3140,7 @@ export default { maxWeight: "SpWeightsWeightV2Weight" }, /** - * Lookup308: xcm::v3::multiasset::MultiAssetFilter + * Lookup309: xcm::v3::multiasset::MultiAssetFilter **/ XcmV3MultiassetMultiAssetFilter: { _enum: { @@ -3138,7 +3149,7 @@ export default { } }, /** - * Lookup309: xcm::v3::multiasset::WildMultiAsset + * Lookup310: xcm::v3::multiasset::WildMultiAsset **/ XcmV3MultiassetWildMultiAsset: { _enum: { @@ -3156,13 +3167,13 @@ export default { } }, /** - * Lookup310: xcm::v3::multiasset::WildFungibility + * Lookup311: xcm::v3::multiasset::WildFungibility **/ XcmV3MultiassetWildFungibility: { _enum: ["Fungible", "NonFungible"] }, /** - * Lookup322: staging_xcm_executor::traits::asset_transfer::TransferType + * Lookup323: staging_xcm_executor::traits::asset_transfer::TransferType **/ StagingXcmExecutorAssetTransferTransferType: { _enum: { @@ -3173,7 +3184,7 @@ export default { } }, /** - * Lookup323: xcm::VersionedAssetId + * Lookup324: xcm::VersionedAssetId **/ XcmVersionedAssetId: { _enum: { @@ -3185,11 +3196,11 @@ export default { } }, /** - * Lookup324: cumulus_pallet_xcm::pallet::Call + * Lookup325: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: "Null", /** - * Lookup325: pallet_message_queue::pallet::Call + * Lookup326: pallet_message_queue::pallet::Call **/ PalletMessageQueueCall: { _enum: { @@ -3206,7 +3217,7 @@ export default { } }, /** - * Lookup326: pallet_storage_providers::pallet::Call + * Lookup327: pallet_storage_providers::pallet::Call **/ PalletStorageProvidersCall: { _enum: { @@ -3255,7 +3266,7 @@ export default { } }, /** - * Lookup327: pallet_file_system::pallet::Call + * Lookup328: pallet_file_system::pallet::Call **/ PalletFileSystemCall: { _enum: { @@ -3359,27 +3370,27 @@ export default { } }, /** - * Lookup328: pallet_file_system::types::BucketMoveRequestResponse + * Lookup329: pallet_file_system::types::BucketMoveRequestResponse **/ PalletFileSystemBucketMoveRequestResponse: { _enum: ["Accepted", "Rejected"] }, /** - * Lookup331: pallet_file_system::types::MspStorageRequestResponse + * Lookup332: pallet_file_system::types::MspStorageRequestResponse **/ PalletFileSystemMspStorageRequestResponse: { accept: "Option", reject: "Option>" }, /** - * Lookup333: pallet_file_system::types::AcceptedStorageRequestParameters + * Lookup334: pallet_file_system::types::AcceptedStorageRequestParameters **/ PalletFileSystemAcceptedStorageRequestParameters: { fileKeysAndProofs: "Vec<(H256,ShpFileKeyVerifierFileKeyProof)>", nonInclusionForestProof: "SpTrieStorageProofCompactProof" }, /** - * Lookup340: pallet_proofs_dealer::pallet::Call + * Lookup341: pallet_proofs_dealer::pallet::Call **/ PalletProofsDealerCall: { _enum: { @@ -3399,13 +3410,13 @@ export default { } }, /** - * Lookup341: pallet_randomness::pallet::Call + * Lookup342: pallet_randomness::pallet::Call **/ PalletRandomnessCall: { _enum: ["set_babe_randomness"] }, /** - * Lookup342: pallet_payment_streams::pallet::Call + * Lookup343: pallet_payment_streams::pallet::Call **/ PalletPaymentStreamsCall: { _enum: { @@ -3445,7 +3456,7 @@ export default { } }, /** - * Lookup343: pallet_bucket_nfts::pallet::Call + * Lookup344: pallet_bucket_nfts::pallet::Call **/ PalletBucketNftsCall: { _enum: { @@ -3463,7 +3474,7 @@ export default { } }, /** - * Lookup345: pallet_nfts::pallet::Call + * Lookup346: pallet_nfts::pallet::Call **/ PalletNftsCall: { _enum: { @@ -3660,7 +3671,7 @@ export default { } }, /** - * Lookup346: pallet_nfts::types::CollectionConfig + * Lookup347: pallet_nfts::types::CollectionConfig **/ PalletNftsCollectionConfig: { settings: "u64", @@ -3668,7 +3679,7 @@ export default { mintSettings: "PalletNftsMintSettings" }, /** - * Lookup348: pallet_nfts::types::CollectionSetting + * Lookup349: pallet_nfts::types::CollectionSetting **/ PalletNftsCollectionSetting: { _enum: [ @@ -3692,7 +3703,7 @@ export default { ] }, /** - * Lookup349: pallet_nfts::types::MintSettings + * Lookup350: pallet_nfts::types::MintSettings **/ PalletNftsMintSettings: { mintType: "PalletNftsMintType", @@ -3702,7 +3713,7 @@ export default { defaultItemSettings: "u64" }, /** - * Lookup350: pallet_nfts::types::MintType + * Lookup351: pallet_nfts::types::MintType **/ PalletNftsMintType: { _enum: { @@ -3712,13 +3723,13 @@ export default { } }, /** - * Lookup353: pallet_nfts::types::ItemSetting + * Lookup354: pallet_nfts::types::ItemSetting **/ PalletNftsItemSetting: { _enum: ["__Unused0", "Transferable", "UnlockedMetadata", "__Unused3", "UnlockedAttributes"] }, /** - * Lookup354: pallet_nfts::types::DestroyWitness + * Lookup355: pallet_nfts::types::DestroyWitness **/ PalletNftsDestroyWitness: { itemMetadatas: "Compact", @@ -3726,26 +3737,26 @@ export default { attributes: "Compact" }, /** - * Lookup356: pallet_nfts::types::MintWitness + * Lookup357: pallet_nfts::types::MintWitness **/ PalletNftsMintWitness: { ownedItem: "Option", mintPrice: "Option" }, /** - * Lookup357: pallet_nfts::types::ItemConfig + * Lookup358: pallet_nfts::types::ItemConfig **/ PalletNftsItemConfig: { settings: "u64" }, /** - * Lookup359: pallet_nfts::types::CancelAttributesApprovalWitness + * Lookup360: pallet_nfts::types::CancelAttributesApprovalWitness **/ PalletNftsCancelAttributesApprovalWitness: { accountAttributes: "u32" }, /** - * Lookup361: pallet_nfts::types::ItemTip + * Lookup362: pallet_nfts::types::ItemTip **/ PalletNftsItemTip: { collection: "u32", @@ -3754,7 +3765,7 @@ export default { amount: "u128" }, /** - * Lookup363: pallet_nfts::types::PreSignedMint + * Lookup364: pallet_nfts::types::PreSignedMint **/ PalletNftsPreSignedMint: { collection: "u32", @@ -3766,7 +3777,7 @@ export default { mintPrice: "Option" }, /** - * Lookup364: sp_runtime::MultiSignature + * Lookup365: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3776,7 +3787,7 @@ export default { } }, /** - * Lookup367: pallet_nfts::types::PreSignedAttributes + * Lookup368: pallet_nfts::types::PreSignedAttributes **/ PalletNftsPreSignedAttributes: { collection: "u32", @@ -3786,7 +3797,7 @@ export default { deadline: "u32" }, /** - * Lookup368: pallet_parameters::pallet::Call + * Lookup369: pallet_parameters::pallet::Call **/ PalletParametersCall: { _enum: { @@ -3796,7 +3807,7 @@ export default { } }, /** - * Lookup369: storage_hub_runtime::configs::runtime_params::RuntimeParameters + * Lookup370: storage_hub_runtime::configs::runtime_params::RuntimeParameters **/ StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters: { _enum: { @@ -3804,7 +3815,7 @@ export default { } }, /** - * Lookup370: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters + * Lookup371: storage_hub_runtime::configs::runtime_params::dynamic_params::runtime_config::Parameters **/ StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters: { _enum: { @@ -3819,20 +3830,20 @@ export default { } }, /** - * Lookup371: pallet_sudo::pallet::Error + * Lookup372: pallet_sudo::pallet::Error **/ PalletSudoError: { _enum: ["RequireSudo"] }, /** - * Lookup374: pallet_collator_selection::pallet::CandidateInfo + * Lookup375: pallet_collator_selection::pallet::CandidateInfo **/ PalletCollatorSelectionCandidateInfo: { who: "AccountId32", deposit: "u128" }, /** - * Lookup376: pallet_collator_selection::pallet::Error + * Lookup377: pallet_collator_selection::pallet::Error **/ PalletCollatorSelectionError: { _enum: [ @@ -3856,17 +3867,17 @@ export default { ] }, /** - * Lookup380: sp_core::crypto::KeyTypeId + * Lookup381: sp_core::crypto::KeyTypeId **/ SpCoreCryptoKeyTypeId: "[u8;4]", /** - * Lookup381: pallet_session::pallet::Error + * Lookup382: pallet_session::pallet::Error **/ PalletSessionError: { _enum: ["InvalidProof", "NoAssociatedValidatorId", "DuplicatedKey", "NoKeys", "NoAccount"] }, /** - * Lookup390: cumulus_pallet_xcmp_queue::OutboundChannelDetails + * Lookup391: cumulus_pallet_xcmp_queue::OutboundChannelDetails **/ CumulusPalletXcmpQueueOutboundChannelDetails: { recipient: "u32", @@ -3876,13 +3887,13 @@ export default { lastIndex: "u16" }, /** - * Lookup391: cumulus_pallet_xcmp_queue::OutboundState + * Lookup392: cumulus_pallet_xcmp_queue::OutboundState **/ CumulusPalletXcmpQueueOutboundState: { _enum: ["Ok", "Suspended"] }, /** - * Lookup393: cumulus_pallet_xcmp_queue::QueueConfigData + * Lookup396: cumulus_pallet_xcmp_queue::QueueConfigData **/ CumulusPalletXcmpQueueQueueConfigData: { suspendThreshold: "u32", @@ -3890,13 +3901,19 @@ export default { resumeThreshold: "u32" }, /** - * Lookup394: cumulus_pallet_xcmp_queue::pallet::Error + * Lookup397: cumulus_pallet_xcmp_queue::pallet::Error **/ CumulusPalletXcmpQueueError: { - _enum: ["BadQueueConfig", "AlreadySuspended", "AlreadyResumed"] + _enum: [ + "BadQueueConfig", + "AlreadySuspended", + "AlreadyResumed", + "TooManyActiveOutboundChannels", + "TooBig" + ] }, /** - * Lookup395: pallet_xcm::pallet::QueryStatus + * Lookup398: pallet_xcm::pallet::QueryStatus **/ PalletXcmQueryStatus: { _enum: { @@ -3917,7 +3934,7 @@ export default { } }, /** - * Lookup399: xcm::VersionedResponse + * Lookup402: xcm::VersionedResponse **/ XcmVersionedResponse: { _enum: { @@ -3929,7 +3946,7 @@ export default { } }, /** - * Lookup405: pallet_xcm::pallet::VersionMigrationStage + * Lookup408: pallet_xcm::pallet::VersionMigrationStage **/ PalletXcmVersionMigrationStage: { _enum: { @@ -3940,7 +3957,7 @@ export default { } }, /** - * Lookup408: pallet_xcm::pallet::RemoteLockedFungibleRecord + * Lookup411: pallet_xcm::pallet::RemoteLockedFungibleRecord **/ PalletXcmRemoteLockedFungibleRecord: { amount: "u128", @@ -3949,7 +3966,7 @@ export default { consumers: "Vec<(Null,u128)>" }, /** - * Lookup415: pallet_xcm::pallet::Error + * Lookup418: pallet_xcm::pallet::Error **/ PalletXcmError: { _enum: [ @@ -3981,7 +3998,7 @@ export default { ] }, /** - * Lookup416: pallet_message_queue::BookState + * Lookup419: pallet_message_queue::BookState **/ PalletMessageQueueBookState: { _alias: { @@ -3995,14 +4012,14 @@ export default { size_: "u64" }, /** - * Lookup418: pallet_message_queue::Neighbours + * Lookup421: pallet_message_queue::Neighbours **/ PalletMessageQueueNeighbours: { prev: "CumulusPrimitivesCoreAggregateMessageOrigin", next: "CumulusPrimitivesCoreAggregateMessageOrigin" }, /** - * Lookup420: pallet_message_queue::Page + * Lookup423: pallet_message_queue::Page **/ PalletMessageQueuePage: { remaining: "u32", @@ -4013,7 +4030,7 @@ export default { heap: "Bytes" }, /** - * Lookup422: pallet_message_queue::pallet::Error + * Lookup425: pallet_message_queue::pallet::Error **/ PalletMessageQueueError: { _enum: [ @@ -4029,7 +4046,7 @@ export default { ] }, /** - * Lookup424: pallet_storage_providers::types::StorageProvider + * Lookup427: pallet_storage_providers::types::StorageProvider **/ PalletStorageProvidersStorageProvider: { _enum: { @@ -4038,7 +4055,7 @@ export default { } }, /** - * Lookup425: pallet_storage_providers::types::BackupStorageProvider + * Lookup428: pallet_storage_providers::types::BackupStorageProvider **/ PalletStorageProvidersBackupStorageProvider: { capacity: "u64", @@ -4051,7 +4068,7 @@ export default { reputationWeight: "u32" }, /** - * Lookup426: pallet_storage_providers::types::MainStorageProvider + * Lookup429: pallet_storage_providers::types::MainStorageProvider **/ PalletStorageProvidersMainStorageProvider: { buckets: "Vec", @@ -4064,7 +4081,7 @@ export default { paymentAccount: "AccountId32" }, /** - * Lookup428: pallet_storage_providers::types::Bucket + * Lookup431: pallet_storage_providers::types::Bucket **/ PalletStorageProvidersBucket: { _alias: { @@ -4078,7 +4095,7 @@ export default { size_: "u64" }, /** - * Lookup431: pallet_storage_providers::pallet::Error + * Lookup434: pallet_storage_providers::pallet::Error **/ PalletStorageProvidersError: { _enum: [ @@ -4112,7 +4129,7 @@ export default { ] }, /** - * Lookup432: pallet_file_system::types::StorageRequestMetadata + * Lookup435: pallet_file_system::types::StorageRequestMetadata **/ PalletFileSystemStorageRequestMetadata: { _alias: { @@ -4132,19 +4149,19 @@ export default { bspsVolunteered: "u32" }, /** - * Lookup437: pallet_file_system::types::StorageRequestBspsMetadata + * Lookup440: pallet_file_system::types::StorageRequestBspsMetadata **/ PalletFileSystemStorageRequestBspsMetadata: { confirmed: "bool" }, /** - * Lookup446: pallet_file_system::types::MoveBucketRequestMetadata + * Lookup449: pallet_file_system::types::MoveBucketRequestMetadata **/ PalletFileSystemMoveBucketRequestMetadata: { requester: "AccountId32" }, /** - * Lookup447: pallet_file_system::pallet::Error + * Lookup450: pallet_file_system::pallet::Error **/ PalletFileSystemError: { _enum: [ @@ -4212,7 +4229,7 @@ export default { ] }, /** - * Lookup454: pallet_proofs_dealer::pallet::Error + * Lookup457: pallet_proofs_dealer::pallet::Error **/ PalletProofsDealerError: { _enum: [ @@ -4241,7 +4258,7 @@ export default { ] }, /** - * Lookup457: pallet_payment_streams::types::FixedRatePaymentStream + * Lookup460: pallet_payment_streams::types::FixedRatePaymentStream **/ PalletPaymentStreamsFixedRatePaymentStream: { rate: "u128", @@ -4250,7 +4267,7 @@ export default { outOfFundsTick: "Option" }, /** - * Lookup458: pallet_payment_streams::types::DynamicRatePaymentStream + * Lookup461: pallet_payment_streams::types::DynamicRatePaymentStream **/ PalletPaymentStreamsDynamicRatePaymentStream: { amountProvided: "u64", @@ -4259,14 +4276,14 @@ export default { outOfFundsTick: "Option" }, /** - * Lookup459: pallet_payment_streams::types::ProviderLastChargeableInfo + * Lookup462: pallet_payment_streams::types::ProviderLastChargeableInfo **/ PalletPaymentStreamsProviderLastChargeableInfo: { lastChargeableTick: "u32", priceIndex: "u128" }, /** - * Lookup460: pallet_payment_streams::pallet::Error + * Lookup463: pallet_payment_streams::pallet::Error **/ PalletPaymentStreamsError: { _enum: [ @@ -4289,7 +4306,7 @@ export default { ] }, /** - * Lookup461: pallet_bucket_nfts::pallet::Error + * Lookup464: pallet_bucket_nfts::pallet::Error **/ PalletBucketNftsError: { _enum: [ @@ -4300,7 +4317,7 @@ export default { ] }, /** - * Lookup462: pallet_nfts::types::CollectionDetails + * Lookup465: pallet_nfts::types::CollectionDetails **/ PalletNftsCollectionDetails: { owner: "AccountId32", @@ -4311,13 +4328,13 @@ export default { attributes: "u32" }, /** - * Lookup467: pallet_nfts::types::CollectionRole + * Lookup470: pallet_nfts::types::CollectionRole **/ PalletNftsCollectionRole: { _enum: ["__Unused0", "Issuer", "Freezer", "__Unused3", "Admin"] }, /** - * Lookup468: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> + * Lookup471: pallet_nfts::types::ItemDetails, bounded_collections::bounded_btree_map::BoundedBTreeMap, S>> **/ PalletNftsItemDetails: { owner: "AccountId32", @@ -4325,42 +4342,42 @@ export default { deposit: "PalletNftsItemDeposit" }, /** - * Lookup469: pallet_nfts::types::ItemDeposit + * Lookup472: pallet_nfts::types::ItemDeposit **/ PalletNftsItemDeposit: { account: "AccountId32", amount: "u128" }, /** - * Lookup474: pallet_nfts::types::CollectionMetadata + * Lookup477: pallet_nfts::types::CollectionMetadata **/ PalletNftsCollectionMetadata: { deposit: "u128", data: "Bytes" }, /** - * Lookup475: pallet_nfts::types::ItemMetadata, StringLimit> + * Lookup478: pallet_nfts::types::ItemMetadata, StringLimit> **/ PalletNftsItemMetadata: { deposit: "PalletNftsItemMetadataDeposit", data: "Bytes" }, /** - * Lookup476: pallet_nfts::types::ItemMetadataDeposit + * Lookup479: pallet_nfts::types::ItemMetadataDeposit **/ PalletNftsItemMetadataDeposit: { account: "Option", amount: "u128" }, /** - * Lookup479: pallet_nfts::types::AttributeDeposit + * Lookup482: pallet_nfts::types::AttributeDeposit **/ PalletNftsAttributeDeposit: { account: "Option", amount: "u128" }, /** - * Lookup483: pallet_nfts::types::PendingSwap, Deadline> + * Lookup486: pallet_nfts::types::PendingSwap, Deadline> **/ PalletNftsPendingSwap: { desiredCollection: "u32", @@ -4369,7 +4386,7 @@ export default { deadline: "u32" }, /** - * Lookup485: pallet_nfts::types::PalletFeature + * Lookup488: pallet_nfts::types::PalletFeature **/ PalletNftsPalletFeature: { _enum: [ @@ -4385,7 +4402,7 @@ export default { ] }, /** - * Lookup486: pallet_nfts::pallet::Error + * Lookup489: pallet_nfts::pallet::Error **/ PalletNftsError: { _enum: [ @@ -4437,51 +4454,51 @@ export default { ] }, /** - * Lookup489: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender + * Lookup492: frame_system::extensions::check_non_zero_sender::CheckNonZeroSender **/ FrameSystemExtensionsCheckNonZeroSender: "Null", /** - * Lookup490: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup493: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: "Null", /** - * Lookup491: frame_system::extensions::check_tx_version::CheckTxVersion + * Lookup494: frame_system::extensions::check_tx_version::CheckTxVersion **/ FrameSystemExtensionsCheckTxVersion: "Null", /** - * Lookup492: frame_system::extensions::check_genesis::CheckGenesis + * Lookup495: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: "Null", /** - * Lookup495: frame_system::extensions::check_nonce::CheckNonce + * Lookup498: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: "Compact", /** - * Lookup496: frame_system::extensions::check_weight::CheckWeight + * Lookup499: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: "Null", /** - * Lookup497: pallet_transaction_payment::ChargeTransactionPayment + * Lookup500: pallet_transaction_payment::ChargeTransactionPayment **/ PalletTransactionPaymentChargeTransactionPayment: "Compact", /** - * Lookup498: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim + * Lookup501: cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim **/ CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim: "Null", /** - * Lookup499: frame_metadata_hash_extension::CheckMetadataHash + * Lookup502: frame_metadata_hash_extension::CheckMetadataHash **/ FrameMetadataHashExtensionCheckMetadataHash: { mode: "FrameMetadataHashExtensionMode" }, /** - * Lookup500: frame_metadata_hash_extension::Mode + * Lookup503: frame_metadata_hash_extension::Mode **/ FrameMetadataHashExtensionMode: { _enum: ["Disabled", "Enabled"] }, /** - * Lookup501: storage_hub_runtime::Runtime + * Lookup504: storage_hub_runtime::Runtime **/ StorageHubRuntimeRuntime: "Null" }; diff --git a/api-augment/src/interfaces/registry.ts b/api-augment/src/interfaces/registry.ts index 178a865bf..ca45806ab 100644 --- a/api-augment/src/interfaces/registry.ts +++ b/api-augment/src/interfaces/registry.ts @@ -260,6 +260,7 @@ import type { XcmV3MultiassetMultiAssets, XcmV3MultiassetWildFungibility, XcmV3MultiassetWildMultiAsset, + XcmV3OriginKind, XcmV3PalletInfo, XcmV3QueryResponseInfo, XcmV3Response, @@ -529,6 +530,7 @@ declare module "@polkadot/types/types/registry" { XcmV3MultiassetMultiAssets: XcmV3MultiassetMultiAssets; XcmV3MultiassetWildFungibility: XcmV3MultiassetWildFungibility; XcmV3MultiassetWildMultiAsset: XcmV3MultiassetWildMultiAsset; + XcmV3OriginKind: XcmV3OriginKind; XcmV3PalletInfo: XcmV3PalletInfo; XcmV3QueryResponseInfo: XcmV3QueryResponseInfo; XcmV3Response: XcmV3Response; diff --git a/api-augment/src/interfaces/types-lookup.ts b/api-augment/src/interfaces/types-lookup.ts index 596fd233e..5db82791e 100644 --- a/api-augment/src/interfaces/types-lookup.ts +++ b/api-augment/src/interfaces/types-lookup.ts @@ -977,7 +977,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly isTransact: boolean; readonly asTransact: { - readonly originKind: XcmV2OriginKind; + readonly originKind: XcmV3OriginKind; readonly requireWeightAtMost: SpWeightsWeightV2Weight; readonly call: XcmDoubleEncoded; } & Struct; @@ -1258,8 +1258,8 @@ declare module "@polkadot/types/lookup" { readonly type: "Success" | "Error" | "TruncatedError"; } - /** @name XcmV2OriginKind (86) */ - interface XcmV2OriginKind extends Enum { + /** @name XcmV3OriginKind (86) */ + interface XcmV3OriginKind extends Enum { readonly isNative: boolean; readonly isSovereignAccount: boolean; readonly isSuperuser: boolean; @@ -1785,7 +1785,14 @@ declare module "@polkadot/types/lookup" { readonly isOverweight: boolean; readonly asOverweight: SpWeightsWeightV2Weight; readonly isYield: boolean; - readonly type: "BadFormat" | "Corrupt" | "Unsupported" | "Overweight" | "Yield"; + readonly isStackLimitReached: boolean; + readonly type: + | "BadFormat" + | "Corrupt" + | "Unsupported" + | "Overweight" + | "Yield" + | "StackLimitReached"; } /** @name PalletStorageProvidersEvent (124) */ @@ -3116,6 +3123,11 @@ declare module "@polkadot/types/lookup" { readonly direction: PalletBalancesAdjustmentDirection; readonly delta: Compact; } & Struct; + readonly isBurn: boolean; + readonly asBurn: { + readonly value: Compact; + readonly keepAlive: bool; + } & Struct; readonly type: | "TransferAllowDeath" | "ForceTransfer" @@ -3124,7 +3136,8 @@ declare module "@polkadot/types/lookup" { | "ForceUnreserve" | "UpgradeAccounts" | "ForceSetBalance" - | "ForceAdjustTotalIssuance"; + | "ForceAdjustTotalIssuance" + | "Burn"; } /** @name PalletBalancesAdjustmentDirection (275) */ @@ -3618,7 +3631,16 @@ declare module "@polkadot/types/lookup" { | "WeightNotComputable"; } - /** @name XcmV2MultiassetMultiAssetFilter (294) */ + /** @name XcmV2OriginKind (294) */ + interface XcmV2OriginKind extends Enum { + readonly isNative: boolean; + readonly isSovereignAccount: boolean; + readonly isSuperuser: boolean; + readonly isXcm: boolean; + readonly type: "Native" | "SovereignAccount" | "Superuser" | "Xcm"; + } + + /** @name XcmV2MultiassetMultiAssetFilter (295) */ interface XcmV2MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV2MultiassetMultiAssets; @@ -3627,7 +3649,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Definite" | "Wild"; } - /** @name XcmV2MultiassetWildMultiAsset (295) */ + /** @name XcmV2MultiassetWildMultiAsset (296) */ interface XcmV2MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -3638,14 +3660,14 @@ declare module "@polkadot/types/lookup" { readonly type: "All" | "AllOf"; } - /** @name XcmV2MultiassetWildFungibility (296) */ + /** @name XcmV2MultiassetWildFungibility (297) */ interface XcmV2MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: "Fungible" | "NonFungible"; } - /** @name XcmV2WeightLimit (297) */ + /** @name XcmV2WeightLimit (298) */ interface XcmV2WeightLimit extends Enum { readonly isUnlimited: boolean; readonly isLimited: boolean; @@ -3653,10 +3675,10 @@ declare module "@polkadot/types/lookup" { readonly type: "Unlimited" | "Limited"; } - /** @name XcmV3Xcm (298) */ + /** @name XcmV3Xcm (299) */ interface XcmV3Xcm extends Vec {} - /** @name XcmV3Instruction (300) */ + /** @name XcmV3Instruction (301) */ interface XcmV3Instruction extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: XcmV3MultiassetMultiAssets; @@ -3684,7 +3706,7 @@ declare module "@polkadot/types/lookup" { } & Struct; readonly isTransact: boolean; readonly asTransact: { - readonly originKind: XcmV2OriginKind; + readonly originKind: XcmV3OriginKind; readonly requireWeightAtMost: SpWeightsWeightV2Weight; readonly call: XcmDoubleEncoded; } & Struct; @@ -3886,7 +3908,7 @@ declare module "@polkadot/types/lookup" { | "UnpaidExecution"; } - /** @name XcmV3Response (301) */ + /** @name XcmV3Response (302) */ interface XcmV3Response extends Enum { readonly isNull: boolean; readonly isAssets: boolean; @@ -3908,7 +3930,7 @@ declare module "@polkadot/types/lookup" { | "DispatchResult"; } - /** @name XcmV3PalletInfo (303) */ + /** @name XcmV3PalletInfo (304) */ interface XcmV3PalletInfo extends Struct { readonly index: Compact; readonly name: Bytes; @@ -3918,14 +3940,14 @@ declare module "@polkadot/types/lookup" { readonly patch: Compact; } - /** @name XcmV3QueryResponseInfo (307) */ + /** @name XcmV3QueryResponseInfo (308) */ interface XcmV3QueryResponseInfo extends Struct { readonly destination: StagingXcmV3MultiLocation; readonly queryId: Compact; readonly maxWeight: SpWeightsWeightV2Weight; } - /** @name XcmV3MultiassetMultiAssetFilter (308) */ + /** @name XcmV3MultiassetMultiAssetFilter (309) */ interface XcmV3MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV3MultiassetMultiAssets; @@ -3934,7 +3956,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Definite" | "Wild"; } - /** @name XcmV3MultiassetWildMultiAsset (309) */ + /** @name XcmV3MultiassetWildMultiAsset (310) */ interface XcmV3MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -3953,14 +3975,14 @@ declare module "@polkadot/types/lookup" { readonly type: "All" | "AllOf" | "AllCounted" | "AllOfCounted"; } - /** @name XcmV3MultiassetWildFungibility (310) */ + /** @name XcmV3MultiassetWildFungibility (311) */ interface XcmV3MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: "Fungible" | "NonFungible"; } - /** @name StagingXcmExecutorAssetTransferTransferType (322) */ + /** @name StagingXcmExecutorAssetTransferTransferType (323) */ interface StagingXcmExecutorAssetTransferTransferType extends Enum { readonly isTeleport: boolean; readonly isLocalReserve: boolean; @@ -3970,7 +3992,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Teleport" | "LocalReserve" | "DestinationReserve" | "RemoteReserve"; } - /** @name XcmVersionedAssetId (323) */ + /** @name XcmVersionedAssetId (324) */ interface XcmVersionedAssetId extends Enum { readonly isV3: boolean; readonly asV3: XcmV3MultiassetAssetId; @@ -3979,10 +4001,10 @@ declare module "@polkadot/types/lookup" { readonly type: "V3" | "V4"; } - /** @name CumulusPalletXcmCall (324) */ + /** @name CumulusPalletXcmCall (325) */ type CumulusPalletXcmCall = Null; - /** @name PalletMessageQueueCall (325) */ + /** @name PalletMessageQueueCall (326) */ interface PalletMessageQueueCall extends Enum { readonly isReapPage: boolean; readonly asReapPage: { @@ -3999,7 +4021,7 @@ declare module "@polkadot/types/lookup" { readonly type: "ReapPage" | "ExecuteOverweight"; } - /** @name PalletStorageProvidersCall (326) */ + /** @name PalletStorageProvidersCall (327) */ interface PalletStorageProvidersCall extends Enum { readonly isRequestMspSignUp: boolean; readonly asRequestMspSignUp: { @@ -4065,7 +4087,7 @@ declare module "@polkadot/types/lookup" { | "Slash"; } - /** @name PalletFileSystemCall (327) */ + /** @name PalletFileSystemCall (328) */ interface PalletFileSystemCall extends Enum { readonly isCreateBucket: boolean; readonly asCreateBucket: { @@ -4191,26 +4213,26 @@ declare module "@polkadot/types/lookup" { | "SetGlobalParameters"; } - /** @name PalletFileSystemBucketMoveRequestResponse (328) */ + /** @name PalletFileSystemBucketMoveRequestResponse (329) */ interface PalletFileSystemBucketMoveRequestResponse extends Enum { readonly isAccepted: boolean; readonly isRejected: boolean; readonly type: "Accepted" | "Rejected"; } - /** @name PalletFileSystemMspStorageRequestResponse (331) */ + /** @name PalletFileSystemMspStorageRequestResponse (332) */ interface PalletFileSystemMspStorageRequestResponse extends Struct { readonly accept: Option; readonly reject: Option>>; } - /** @name PalletFileSystemAcceptedStorageRequestParameters (333) */ + /** @name PalletFileSystemAcceptedStorageRequestParameters (334) */ interface PalletFileSystemAcceptedStorageRequestParameters extends Struct { readonly fileKeysAndProofs: Vec>; readonly nonInclusionForestProof: SpTrieStorageProofCompactProof; } - /** @name PalletProofsDealerCall (340) */ + /** @name PalletProofsDealerCall (341) */ interface PalletProofsDealerCall extends Enum { readonly isChallenge: boolean; readonly asChallenge: { @@ -4232,13 +4254,13 @@ declare module "@polkadot/types/lookup" { readonly type: "Challenge" | "SubmitProof" | "ForceInitialiseChallengeCycle" | "SetPaused"; } - /** @name PalletRandomnessCall (341) */ + /** @name PalletRandomnessCall (342) */ interface PalletRandomnessCall extends Enum { readonly isSetBabeRandomness: boolean; readonly type: "SetBabeRandomness"; } - /** @name PalletPaymentStreamsCall (342) */ + /** @name PalletPaymentStreamsCall (343) */ interface PalletPaymentStreamsCall extends Enum { readonly isCreateFixedRatePaymentStream: boolean; readonly asCreateFixedRatePaymentStream: { @@ -4292,7 +4314,7 @@ declare module "@polkadot/types/lookup" { | "ClearInsolventFlag"; } - /** @name PalletBucketNftsCall (343) */ + /** @name PalletBucketNftsCall (344) */ interface PalletBucketNftsCall extends Enum { readonly isShareAccess: boolean; readonly asShareAccess: { @@ -4310,7 +4332,7 @@ declare module "@polkadot/types/lookup" { readonly type: "ShareAccess" | "UpdateReadAccess"; } - /** @name PalletNftsCall (345) */ + /** @name PalletNftsCall (346) */ interface PalletNftsCall extends Enum { readonly isCreate: boolean; readonly asCreate: { @@ -4583,14 +4605,14 @@ declare module "@polkadot/types/lookup" { | "SetAttributesPreSigned"; } - /** @name PalletNftsCollectionConfig (346) */ + /** @name PalletNftsCollectionConfig (347) */ interface PalletNftsCollectionConfig extends Struct { readonly settings: u64; readonly maxSupply: Option; readonly mintSettings: PalletNftsMintSettings; } - /** @name PalletNftsCollectionSetting (348) */ + /** @name PalletNftsCollectionSetting (349) */ interface PalletNftsCollectionSetting extends Enum { readonly isTransferableItems: boolean; readonly isUnlockedMetadata: boolean; @@ -4605,7 +4627,7 @@ declare module "@polkadot/types/lookup" { | "DepositRequired"; } - /** @name PalletNftsMintSettings (349) */ + /** @name PalletNftsMintSettings (350) */ interface PalletNftsMintSettings extends Struct { readonly mintType: PalletNftsMintType; readonly price: Option; @@ -4614,7 +4636,7 @@ declare module "@polkadot/types/lookup" { readonly defaultItemSettings: u64; } - /** @name PalletNftsMintType (350) */ + /** @name PalletNftsMintType (351) */ interface PalletNftsMintType extends Enum { readonly isIssuer: boolean; readonly isPublic: boolean; @@ -4623,7 +4645,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Issuer" | "Public" | "HolderOf"; } - /** @name PalletNftsItemSetting (353) */ + /** @name PalletNftsItemSetting (354) */ interface PalletNftsItemSetting extends Enum { readonly isTransferable: boolean; readonly isUnlockedMetadata: boolean; @@ -4631,30 +4653,30 @@ declare module "@polkadot/types/lookup" { readonly type: "Transferable" | "UnlockedMetadata" | "UnlockedAttributes"; } - /** @name PalletNftsDestroyWitness (354) */ + /** @name PalletNftsDestroyWitness (355) */ interface PalletNftsDestroyWitness extends Struct { readonly itemMetadatas: Compact; readonly itemConfigs: Compact; readonly attributes: Compact; } - /** @name PalletNftsMintWitness (356) */ + /** @name PalletNftsMintWitness (357) */ interface PalletNftsMintWitness extends Struct { readonly ownedItem: Option; readonly mintPrice: Option; } - /** @name PalletNftsItemConfig (357) */ + /** @name PalletNftsItemConfig (358) */ interface PalletNftsItemConfig extends Struct { readonly settings: u64; } - /** @name PalletNftsCancelAttributesApprovalWitness (359) */ + /** @name PalletNftsCancelAttributesApprovalWitness (360) */ interface PalletNftsCancelAttributesApprovalWitness extends Struct { readonly accountAttributes: u32; } - /** @name PalletNftsItemTip (361) */ + /** @name PalletNftsItemTip (362) */ interface PalletNftsItemTip extends Struct { readonly collection: u32; readonly item: u32; @@ -4662,7 +4684,7 @@ declare module "@polkadot/types/lookup" { readonly amount: u128; } - /** @name PalletNftsPreSignedMint (363) */ + /** @name PalletNftsPreSignedMint (364) */ interface PalletNftsPreSignedMint extends Struct { readonly collection: u32; readonly item: u32; @@ -4673,7 +4695,7 @@ declare module "@polkadot/types/lookup" { readonly mintPrice: Option; } - /** @name SpRuntimeMultiSignature (364) */ + /** @name SpRuntimeMultiSignature (365) */ interface SpRuntimeMultiSignature extends Enum { readonly isEd25519: boolean; readonly asEd25519: U8aFixed; @@ -4684,7 +4706,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Ed25519" | "Sr25519" | "Ecdsa"; } - /** @name PalletNftsPreSignedAttributes (367) */ + /** @name PalletNftsPreSignedAttributes (368) */ interface PalletNftsPreSignedAttributes extends Struct { readonly collection: u32; readonly item: u32; @@ -4693,7 +4715,7 @@ declare module "@polkadot/types/lookup" { readonly deadline: u32; } - /** @name PalletParametersCall (368) */ + /** @name PalletParametersCall (369) */ interface PalletParametersCall extends Enum { readonly isSetParameter: boolean; readonly asSetParameter: { @@ -4702,14 +4724,14 @@ declare module "@polkadot/types/lookup" { readonly type: "SetParameter"; } - /** @name StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters (369) */ + /** @name StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters (370) */ interface StorageHubRuntimeConfigsRuntimeParamsRuntimeParameters extends Enum { readonly isRuntimeConfig: boolean; readonly asRuntimeConfig: StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters; readonly type: "RuntimeConfig"; } - /** @name StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters (370) */ + /** @name StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters (371) */ interface StorageHubRuntimeConfigsRuntimeParamsDynamicParamsRuntimeConfigParameters extends Enum { readonly isSlashAmountPerMaxFileSize: boolean; readonly asSlashAmountPerMaxFileSize: ITuple< @@ -4746,19 +4768,19 @@ declare module "@polkadot/types/lookup" { | "MinChallengePeriod"; } - /** @name PalletSudoError (371) */ + /** @name PalletSudoError (372) */ interface PalletSudoError extends Enum { readonly isRequireSudo: boolean; readonly type: "RequireSudo"; } - /** @name PalletCollatorSelectionCandidateInfo (374) */ + /** @name PalletCollatorSelectionCandidateInfo (375) */ interface PalletCollatorSelectionCandidateInfo extends Struct { readonly who: AccountId32; readonly deposit: u128; } - /** @name PalletCollatorSelectionError (376) */ + /** @name PalletCollatorSelectionError (377) */ interface PalletCollatorSelectionError extends Enum { readonly isTooManyCandidates: boolean; readonly isTooFewEligibleCollators: boolean; @@ -4797,10 +4819,10 @@ declare module "@polkadot/types/lookup" { | "InvalidUnreserve"; } - /** @name SpCoreCryptoKeyTypeId (380) */ + /** @name SpCoreCryptoKeyTypeId (381) */ interface SpCoreCryptoKeyTypeId extends U8aFixed {} - /** @name PalletSessionError (381) */ + /** @name PalletSessionError (382) */ interface PalletSessionError extends Enum { readonly isInvalidProof: boolean; readonly isNoAssociatedValidatorId: boolean; @@ -4815,7 +4837,7 @@ declare module "@polkadot/types/lookup" { | "NoAccount"; } - /** @name CumulusPalletXcmpQueueOutboundChannelDetails (390) */ + /** @name CumulusPalletXcmpQueueOutboundChannelDetails (391) */ interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { readonly recipient: u32; readonly state: CumulusPalletXcmpQueueOutboundState; @@ -4824,29 +4846,36 @@ declare module "@polkadot/types/lookup" { readonly lastIndex: u16; } - /** @name CumulusPalletXcmpQueueOutboundState (391) */ + /** @name CumulusPalletXcmpQueueOutboundState (392) */ interface CumulusPalletXcmpQueueOutboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: "Ok" | "Suspended"; } - /** @name CumulusPalletXcmpQueueQueueConfigData (393) */ + /** @name CumulusPalletXcmpQueueQueueConfigData (396) */ interface CumulusPalletXcmpQueueQueueConfigData extends Struct { readonly suspendThreshold: u32; readonly dropThreshold: u32; readonly resumeThreshold: u32; } - /** @name CumulusPalletXcmpQueueError (394) */ + /** @name CumulusPalletXcmpQueueError (397) */ interface CumulusPalletXcmpQueueError extends Enum { readonly isBadQueueConfig: boolean; readonly isAlreadySuspended: boolean; readonly isAlreadyResumed: boolean; - readonly type: "BadQueueConfig" | "AlreadySuspended" | "AlreadyResumed"; + readonly isTooManyActiveOutboundChannels: boolean; + readonly isTooBig: boolean; + readonly type: + | "BadQueueConfig" + | "AlreadySuspended" + | "AlreadyResumed" + | "TooManyActiveOutboundChannels" + | "TooBig"; } - /** @name PalletXcmQueryStatus (395) */ + /** @name PalletXcmQueryStatus (398) */ interface PalletXcmQueryStatus extends Enum { readonly isPending: boolean; readonly asPending: { @@ -4868,7 +4897,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Pending" | "VersionNotifier" | "Ready"; } - /** @name XcmVersionedResponse (399) */ + /** @name XcmVersionedResponse (402) */ interface XcmVersionedResponse extends Enum { readonly isV2: boolean; readonly asV2: XcmV2Response; @@ -4879,7 +4908,7 @@ declare module "@polkadot/types/lookup" { readonly type: "V2" | "V3" | "V4"; } - /** @name PalletXcmVersionMigrationStage (405) */ + /** @name PalletXcmVersionMigrationStage (408) */ interface PalletXcmVersionMigrationStage extends Enum { readonly isMigrateSupportedVersion: boolean; readonly isMigrateVersionNotifiers: boolean; @@ -4893,7 +4922,7 @@ declare module "@polkadot/types/lookup" { | "MigrateAndNotifyOldTargets"; } - /** @name PalletXcmRemoteLockedFungibleRecord (408) */ + /** @name PalletXcmRemoteLockedFungibleRecord (411) */ interface PalletXcmRemoteLockedFungibleRecord extends Struct { readonly amount: u128; readonly owner: XcmVersionedLocation; @@ -4901,7 +4930,7 @@ declare module "@polkadot/types/lookup" { readonly consumers: Vec>; } - /** @name PalletXcmError (415) */ + /** @name PalletXcmError (418) */ interface PalletXcmError extends Enum { readonly isUnreachable: boolean; readonly isSendFailure: boolean; @@ -4954,7 +4983,7 @@ declare module "@polkadot/types/lookup" { | "LocalExecutionIncomplete"; } - /** @name PalletMessageQueueBookState (416) */ + /** @name PalletMessageQueueBookState (419) */ interface PalletMessageQueueBookState extends Struct { readonly begin: u32; readonly end: u32; @@ -4964,13 +4993,13 @@ declare module "@polkadot/types/lookup" { readonly size_: u64; } - /** @name PalletMessageQueueNeighbours (418) */ + /** @name PalletMessageQueueNeighbours (421) */ interface PalletMessageQueueNeighbours extends Struct { readonly prev: CumulusPrimitivesCoreAggregateMessageOrigin; readonly next: CumulusPrimitivesCoreAggregateMessageOrigin; } - /** @name PalletMessageQueuePage (420) */ + /** @name PalletMessageQueuePage (423) */ interface PalletMessageQueuePage extends Struct { readonly remaining: u32; readonly remainingSize: u32; @@ -4980,7 +5009,7 @@ declare module "@polkadot/types/lookup" { readonly heap: Bytes; } - /** @name PalletMessageQueueError (422) */ + /** @name PalletMessageQueueError (425) */ interface PalletMessageQueueError extends Enum { readonly isNotReapable: boolean; readonly isNoPage: boolean; @@ -5003,7 +5032,7 @@ declare module "@polkadot/types/lookup" { | "RecursiveDisallowed"; } - /** @name PalletStorageProvidersStorageProvider (424) */ + /** @name PalletStorageProvidersStorageProvider (427) */ interface PalletStorageProvidersStorageProvider extends Enum { readonly isBackupStorageProvider: boolean; readonly asBackupStorageProvider: PalletStorageProvidersBackupStorageProvider; @@ -5012,7 +5041,7 @@ declare module "@polkadot/types/lookup" { readonly type: "BackupStorageProvider" | "MainStorageProvider"; } - /** @name PalletStorageProvidersBackupStorageProvider (425) */ + /** @name PalletStorageProvidersBackupStorageProvider (428) */ interface PalletStorageProvidersBackupStorageProvider extends Struct { readonly capacity: u64; readonly capacityUsed: u64; @@ -5024,7 +5053,7 @@ declare module "@polkadot/types/lookup" { readonly reputationWeight: u32; } - /** @name PalletStorageProvidersMainStorageProvider (426) */ + /** @name PalletStorageProvidersMainStorageProvider (429) */ interface PalletStorageProvidersMainStorageProvider extends Struct { readonly buckets: Vec; readonly capacity: u64; @@ -5036,7 +5065,7 @@ declare module "@polkadot/types/lookup" { readonly paymentAccount: AccountId32; } - /** @name PalletStorageProvidersBucket (428) */ + /** @name PalletStorageProvidersBucket (431) */ interface PalletStorageProvidersBucket extends Struct { readonly root: H256; readonly userId: AccountId32; @@ -5046,7 +5075,7 @@ declare module "@polkadot/types/lookup" { readonly size_: u64; } - /** @name PalletStorageProvidersError (431) */ + /** @name PalletStorageProvidersError (434) */ interface PalletStorageProvidersError extends Enum { readonly isAlreadyRegistered: boolean; readonly isSignUpNotRequested: boolean; @@ -5105,7 +5134,7 @@ declare module "@polkadot/types/lookup" { | "PaymentStreamNotFound"; } - /** @name PalletFileSystemStorageRequestMetadata (432) */ + /** @name PalletFileSystemStorageRequestMetadata (435) */ interface PalletFileSystemStorageRequestMetadata extends Struct { readonly requestedAt: u32; readonly owner: AccountId32; @@ -5121,17 +5150,17 @@ declare module "@polkadot/types/lookup" { readonly bspsVolunteered: u32; } - /** @name PalletFileSystemStorageRequestBspsMetadata (437) */ + /** @name PalletFileSystemStorageRequestBspsMetadata (440) */ interface PalletFileSystemStorageRequestBspsMetadata extends Struct { readonly confirmed: bool; } - /** @name PalletFileSystemMoveBucketRequestMetadata (446) */ + /** @name PalletFileSystemMoveBucketRequestMetadata (449) */ interface PalletFileSystemMoveBucketRequestMetadata extends Struct { readonly requester: AccountId32; } - /** @name PalletFileSystemError (447) */ + /** @name PalletFileSystemError (450) */ interface PalletFileSystemError extends Enum { readonly isStorageRequestAlreadyRegistered: boolean; readonly isStorageRequestNotFound: boolean; @@ -5258,7 +5287,7 @@ declare module "@polkadot/types/lookup" { | "InconsistentStateKeyAlreadyExists"; } - /** @name PalletProofsDealerError (454) */ + /** @name PalletProofsDealerError (457) */ interface PalletProofsDealerError extends Enum { readonly isNotProvider: boolean; readonly isChallengesQueueOverflow: boolean; @@ -5307,7 +5336,7 @@ declare module "@polkadot/types/lookup" { | "TooManyValidProofSubmitters"; } - /** @name PalletPaymentStreamsFixedRatePaymentStream (457) */ + /** @name PalletPaymentStreamsFixedRatePaymentStream (460) */ interface PalletPaymentStreamsFixedRatePaymentStream extends Struct { readonly rate: u128; readonly lastChargedTick: u32; @@ -5315,7 +5344,7 @@ declare module "@polkadot/types/lookup" { readonly outOfFundsTick: Option; } - /** @name PalletPaymentStreamsDynamicRatePaymentStream (458) */ + /** @name PalletPaymentStreamsDynamicRatePaymentStream (461) */ interface PalletPaymentStreamsDynamicRatePaymentStream extends Struct { readonly amountProvided: u64; readonly priceIndexWhenLastCharged: u128; @@ -5323,13 +5352,13 @@ declare module "@polkadot/types/lookup" { readonly outOfFundsTick: Option; } - /** @name PalletPaymentStreamsProviderLastChargeableInfo (459) */ + /** @name PalletPaymentStreamsProviderLastChargeableInfo (462) */ interface PalletPaymentStreamsProviderLastChargeableInfo extends Struct { readonly lastChargeableTick: u32; readonly priceIndex: u128; } - /** @name PalletPaymentStreamsError (460) */ + /** @name PalletPaymentStreamsError (463) */ interface PalletPaymentStreamsError extends Enum { readonly isPaymentStreamAlreadyExists: boolean; readonly isPaymentStreamNotFound: boolean; @@ -5366,7 +5395,7 @@ declare module "@polkadot/types/lookup" { | "CooldownPeriodNotPassed"; } - /** @name PalletBucketNftsError (461) */ + /** @name PalletBucketNftsError (464) */ interface PalletBucketNftsError extends Enum { readonly isBucketIsNotPrivate: boolean; readonly isNotBucketOwner: boolean; @@ -5379,7 +5408,7 @@ declare module "@polkadot/types/lookup" { | "ConvertBytesToBoundedVec"; } - /** @name PalletNftsCollectionDetails (462) */ + /** @name PalletNftsCollectionDetails (465) */ interface PalletNftsCollectionDetails extends Struct { readonly owner: AccountId32; readonly ownerDeposit: u128; @@ -5389,7 +5418,7 @@ declare module "@polkadot/types/lookup" { readonly attributes: u32; } - /** @name PalletNftsCollectionRole (467) */ + /** @name PalletNftsCollectionRole (470) */ interface PalletNftsCollectionRole extends Enum { readonly isIssuer: boolean; readonly isFreezer: boolean; @@ -5397,44 +5426,44 @@ declare module "@polkadot/types/lookup" { readonly type: "Issuer" | "Freezer" | "Admin"; } - /** @name PalletNftsItemDetails (468) */ + /** @name PalletNftsItemDetails (471) */ interface PalletNftsItemDetails extends Struct { readonly owner: AccountId32; readonly approvals: BTreeMap>; readonly deposit: PalletNftsItemDeposit; } - /** @name PalletNftsItemDeposit (469) */ + /** @name PalletNftsItemDeposit (472) */ interface PalletNftsItemDeposit extends Struct { readonly account: AccountId32; readonly amount: u128; } - /** @name PalletNftsCollectionMetadata (474) */ + /** @name PalletNftsCollectionMetadata (477) */ interface PalletNftsCollectionMetadata extends Struct { readonly deposit: u128; readonly data: Bytes; } - /** @name PalletNftsItemMetadata (475) */ + /** @name PalletNftsItemMetadata (478) */ interface PalletNftsItemMetadata extends Struct { readonly deposit: PalletNftsItemMetadataDeposit; readonly data: Bytes; } - /** @name PalletNftsItemMetadataDeposit (476) */ + /** @name PalletNftsItemMetadataDeposit (479) */ interface PalletNftsItemMetadataDeposit extends Struct { readonly account: Option; readonly amount: u128; } - /** @name PalletNftsAttributeDeposit (479) */ + /** @name PalletNftsAttributeDeposit (482) */ interface PalletNftsAttributeDeposit extends Struct { readonly account: Option; readonly amount: u128; } - /** @name PalletNftsPendingSwap (483) */ + /** @name PalletNftsPendingSwap (486) */ interface PalletNftsPendingSwap extends Struct { readonly desiredCollection: u32; readonly desiredItem: Option; @@ -5442,7 +5471,7 @@ declare module "@polkadot/types/lookup" { readonly deadline: u32; } - /** @name PalletNftsPalletFeature (485) */ + /** @name PalletNftsPalletFeature (488) */ interface PalletNftsPalletFeature extends Enum { readonly isTrading: boolean; readonly isAttributes: boolean; @@ -5451,7 +5480,7 @@ declare module "@polkadot/types/lookup" { readonly type: "Trading" | "Attributes" | "Approvals" | "Swaps"; } - /** @name PalletNftsError (486) */ + /** @name PalletNftsError (489) */ interface PalletNftsError extends Enum { readonly isNoPermission: boolean; readonly isUnknownCollection: boolean; @@ -5546,42 +5575,42 @@ declare module "@polkadot/types/lookup" { | "WitnessRequired"; } - /** @name FrameSystemExtensionsCheckNonZeroSender (489) */ + /** @name FrameSystemExtensionsCheckNonZeroSender (492) */ type FrameSystemExtensionsCheckNonZeroSender = Null; - /** @name FrameSystemExtensionsCheckSpecVersion (490) */ + /** @name FrameSystemExtensionsCheckSpecVersion (493) */ type FrameSystemExtensionsCheckSpecVersion = Null; - /** @name FrameSystemExtensionsCheckTxVersion (491) */ + /** @name FrameSystemExtensionsCheckTxVersion (494) */ type FrameSystemExtensionsCheckTxVersion = Null; - /** @name FrameSystemExtensionsCheckGenesis (492) */ + /** @name FrameSystemExtensionsCheckGenesis (495) */ type FrameSystemExtensionsCheckGenesis = Null; - /** @name FrameSystemExtensionsCheckNonce (495) */ + /** @name FrameSystemExtensionsCheckNonce (498) */ interface FrameSystemExtensionsCheckNonce extends Compact {} - /** @name FrameSystemExtensionsCheckWeight (496) */ + /** @name FrameSystemExtensionsCheckWeight (499) */ type FrameSystemExtensionsCheckWeight = Null; - /** @name PalletTransactionPaymentChargeTransactionPayment (497) */ + /** @name PalletTransactionPaymentChargeTransactionPayment (500) */ interface PalletTransactionPaymentChargeTransactionPayment extends Compact {} - /** @name CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim (498) */ + /** @name CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim (501) */ type CumulusPrimitivesStorageWeightReclaimStorageWeightReclaim = Null; - /** @name FrameMetadataHashExtensionCheckMetadataHash (499) */ + /** @name FrameMetadataHashExtensionCheckMetadataHash (502) */ interface FrameMetadataHashExtensionCheckMetadataHash extends Struct { readonly mode: FrameMetadataHashExtensionMode; } - /** @name FrameMetadataHashExtensionMode (500) */ + /** @name FrameMetadataHashExtensionMode (503) */ interface FrameMetadataHashExtensionMode extends Enum { readonly isDisabled: boolean; readonly isEnabled: boolean; readonly type: "Disabled" | "Enabled"; } - /** @name StorageHubRuntimeRuntime (501) */ + /** @name StorageHubRuntimeRuntime (504) */ type StorageHubRuntimeRuntime = Null; } // declare module diff --git a/api-augment/storagehub.json b/api-augment/storagehub.json index cf8511ac5..ee6cebaa1 100644 --- a/api-augment/storagehub.json +++ b/api-augment/storagehub.json @@ -1 +1 @@ -{"jsonrpc":"2.0","result":"0x6d6574610ed907000c1c73705f636f72651863727970746f2c4163636f756e7449643332000004000401205b75383b2033325d0000040000032000000008000800000503000c08306672616d655f73797374656d2c4163636f756e74496e666f08144e6f6e636501102c4163636f756e74446174610114001401146e6f6e63651001144e6f6e6365000124636f6e73756d657273100120526566436f756e7400012470726f766964657273100120526566436f756e7400012c73756666696369656e7473100120526566436f756e740001106461746114012c4163636f756e74446174610000100000050500140c3c70616c6c65745f62616c616e6365731474797065732c4163636f756e7444617461041c42616c616e63650118001001106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000114666c6167731c01284578747261466c61677300001800000507001c0c3c70616c6c65745f62616c616e636573147479706573284578747261466c61677300000400180110753132380000200000050000240c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540128000c01186e6f726d616c2801045400012c6f7065726174696f6e616c280104540001246d616e6461746f7279280104540000280c2873705f77656967687473247765696768745f76321857656967687400000801207265665f74696d652c010c75363400012870726f6f665f73697a652c010c75363400002c000006300030000005060034083c7072696d69746976655f74797065731048323536000004000401205b75383b2033325d00003800000208003c102873705f72756e74696d651c67656e65726963186469676573741844696765737400000401106c6f677340013c5665633c4469676573744974656d3e000040000002440044102873705f72756e74696d651c67656e6572696318646967657374284469676573744974656d0001142850726552756e74696d650800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e00060024436f6e73656e7375730800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e000400105365616c0800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e000500144f74686572040038011c5665633c75383e0000006452756e74696d65456e7669726f6e6d656e745570646174656400080000480000030400000008004c00000250005008306672616d655f73797374656d2c4576656e745265636f7264080445015404540134000c01147068617365fd02011450686173650001146576656e7454010445000118746f706963732d0201185665633c543e000054084c73746f726167655f6875625f72756e74696d653052756e74696d654576656e7400014c1853797374656d04005801706672616d655f73797374656d3a3a4576656e743c52756e74696d653e0000003c50617261636861696e53797374656d04007c01bc63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d3a3a4576656e743c52756e74696d653e0001002042616c616e636573040084017c70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e000a00485472616e73616374696f6e5061796d656e7404008c01a870616c6c65745f7472616e73616374696f6e5f7061796d656e743a3a4576656e743c52756e74696d653e000b00105375646f040090016c70616c6c65745f7375646f3a3a4576656e743c52756e74696d653e000f0044436f6c6c61746f7253656c656374696f6e0400a001a470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a4576656e743c52756e74696d653e0015001c53657373696f6e0400a8015470616c6c65745f73657373696f6e3a3a4576656e740016002458636d7051756575650400ac01a463756d756c75735f70616c6c65745f78636d705f71756575653a3a4576656e743c52756e74696d653e001e002c506f6c6b61646f7458636d0400b0016870616c6c65745f78636d3a3a4576656e743c52756e74696d653e001f002843756d756c757358636d0400dd01018863756d756c75735f70616c6c65745f78636d3a3a4576656e743c52756e74696d653e002000304d65737361676551756575650400e101019070616c6c65745f6d6573736167655f71756575653a3a4576656e743c52756e74696d653e0021002450726f7669646572730400f10101a070616c6c65745f73746f726167655f70726f7669646572733a3a4576656e743c52756e74696d653e0028002846696c6553797374656d04000d02018870616c6c65745f66696c655f73797374656d3a3a4576656e743c52756e74696d653e0029003050726f6f66734465616c657204005d02019070616c6c65745f70726f6f66735f6465616c65723a3a4576656e743c52756e74696d653e002a002852616e646f6d6e6573730400a502018470616c6c65745f72616e646f6d6e6573733a3a4576656e743c52756e74696d653e002b00385061796d656e7453747265616d730400a902019870616c6c65745f7061796d656e745f73747265616d733a3a4576656e743c52756e74696d653e002c00284275636b65744e6674730400ad02018870616c6c65745f6275636b65745f6e6674733a3a4576656e743c52756e74696d653e002d00104e6674730400b102016c70616c6c65745f6e6674733a3a4576656e743c52756e74696d653e00320028506172616d65746572730400d502018470616c6c65745f706172616d65746572733a3a4576656e743c52756e74696d653e00330000580c306672616d655f73797374656d1870616c6c6574144576656e7404045400011c4045787472696e7369635375636365737304013464697370617463685f696e666f5c01304469737061746368496e666f00000490416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656408013864697370617463685f6572726f7268013444697370617463684572726f7200013464697370617463685f696e666f5c01304469737061746368496e666f00010450416e2065787472696e736963206661696c65642e2c436f64655570646174656400020450603a636f6465602077617320757064617465642e284e65774163636f756e7404011c6163636f756e74000130543a3a4163636f756e7449640003046841206e6577206163636f756e742077617320637265617465642e344b696c6c65644163636f756e7404011c6163636f756e74000130543a3a4163636f756e74496400040458416e206163636f756e7420776173207265617065642e2052656d61726b656408011873656e646572000130543a3a4163636f756e7449640001106861736834011c543a3a48617368000504704f6e206f6e2d636861696e2072656d61726b2068617070656e65642e4455706772616465417574686f72697a6564080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c00060468416e20757067726164652077617320617574686f72697a65642e04704576656e7420666f72207468652053797374656d2070616c6c65742e5c0c346672616d655f737570706f7274206469737061746368304469737061746368496e666f00000c0118776569676874280118576569676874000114636c6173736001344469737061746368436c617373000120706179735f666565640110506179730000600c346672616d655f737570706f7274206469737061746368344469737061746368436c61737300010c184e6f726d616c0000002c4f7065726174696f6e616c000100244d616e6461746f727900020000640c346672616d655f737570706f727420646973706174636810506179730001080c596573000000084e6f0001000068082873705f72756e74696d653444697370617463684572726f72000138144f746865720000003043616e6e6f744c6f6f6b7570000100244261644f726967696e000200184d6f64756c6504006c012c4d6f64756c654572726f7200030044436f6e73756d657252656d61696e696e670004002c4e6f50726f76696465727300050040546f6f4d616e79436f6e73756d65727300060014546f6b656e0400700128546f6b656e4572726f720007002841726974686d65746963040074013c41726974686d657469634572726f72000800345472616e73616374696f6e616c04007801485472616e73616374696f6e616c4572726f7200090024457868617573746564000a0028436f7272757074696f6e000b002c556e617661696c61626c65000c0038526f6f744e6f74416c6c6f776564000d00006c082873705f72756e74696d652c4d6f64756c654572726f720000080114696e64657808010875380001146572726f7248018c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d000070082873705f72756e74696d6528546f6b656e4572726f720001284046756e6473556e617661696c61626c65000000304f6e6c7950726f76696465720001003042656c6f774d696e696d756d0002003043616e6e6f7443726561746500030030556e6b6e6f776e41737365740004001846726f7a656e0005002c556e737570706f727465640006004043616e6e6f74437265617465486f6c64000700344e6f74457870656e6461626c650008001c426c6f636b65640009000074083473705f61726974686d657469633c41726974686d657469634572726f7200010c24556e646572666c6f77000000204f766572666c6f77000100384469766973696f6e42795a65726f0002000078082873705f72756e74696d65485472616e73616374696f6e616c4572726f72000108304c696d6974526561636865640000001c4e6f4c61796572000100007c0c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144576656e740404540001186056616c69646174696f6e46756e6374696f6e53746f726564000004d05468652076616c69646174696f6e2066756e6374696f6e20686173206265656e207363686564756c656420746f206170706c792e6456616c69646174696f6e46756e6374696f6e4170706c69656404015472656c61795f636861696e5f626c6f636b5f6e756d10015452656c6179436861696e426c6f636b4e756d62657200010445015468652076616c69646174696f6e2066756e6374696f6e20776173206170706c696564206173206f662074686520636f6e7461696e65642072656c617920636861696e20626c6f636b206e756d6265722e6c56616c69646174696f6e46756e6374696f6e446973636172646564000204b05468652072656c61792d636861696e2061626f727465642074686520757067726164652070726f636573732e60446f776e776172644d657373616765735265636569766564040114636f756e7410010c7533320003040101536f6d6520646f776e77617264206d657373616765732068617665206265656e20726563656976656420616e642077696c6c2062652070726f6365737365642e64446f776e776172644d6573736167657350726f63657373656408012c7765696768745f75736564280118576569676874000120646d715f6865616434014472656c61795f636861696e3a3a48617368000404e0446f776e77617264206d6573736167657320776572652070726f636573736564207573696e672074686520676976656e207765696768742e445570776172644d65737361676553656e740401306d6573736167655f6861736880013c4f7074696f6e3c58636d486173683e000504b8416e20757077617264206d657373616765207761732073656e7420746f207468652072656c617920636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748004184f7074696f6e04045401040108104e6f6e6500000010536f6d650400040000010000840c3c70616c6c65745f62616c616e6365731870616c6c6574144576656e740804540004490001581c456e646f77656408011c6163636f756e74000130543a3a4163636f756e744964000130667265655f62616c616e6365180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f737408011c6163636f756e74000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650001083d01416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c78726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e736665720c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2842616c616e636553657408010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e636500030468412062616c616e6365207761732073657420627920726f6f742e20526573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e726573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000504e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656410011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500014864657374696e6174696f6e5f7374617475738801185374617475730006084d01536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742ed846696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e1c4465706f73697408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000704d8536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e20576974686472617708010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650008041d01536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650009040101536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292e184d696e74656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a049c536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e184275726e656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b049c536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e2453757370656e64656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000c041501536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e20526573746f72656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d04a4536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e20557067726164656404010c77686f000130543a3a4163636f756e744964000e0460416e206163636f756e74207761732075706772616465642e18497373756564040118616d6f756e74180128543a3a42616c616e6365000f042d01546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e2452657363696e646564040118616d6f756e74180128543a3a42616c616e63650010042501546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e184c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500110460536f6d652062616c616e636520776173206c6f636b65642e20556e6c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500120468536f6d652062616c616e63652077617320756e6c6f636b65642e1846726f7a656e08010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500130460536f6d652062616c616e6365207761732066726f7a656e2e1854686177656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500140460536f6d652062616c616e636520776173207468617765642e4c546f74616c49737375616e6365466f7263656408010c6f6c64180128543a3a42616c616e636500010c6e6577180128543a3a42616c616e6365001504ac5468652060546f74616c49737375616e6365602077617320666f72636566756c6c79206368616e6765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748814346672616d655f737570706f72741874726169747318746f6b656e73106d6973633442616c616e63655374617475730001081046726565000000205265736572766564000100008c0c6870616c6c65745f7472616e73616374696f6e5f7061796d656e741870616c6c6574144576656e74040454000104485472616e73616374696f6e466565506169640c010c77686f000130543a3a4163636f756e74496400012861637475616c5f66656518013042616c616e63654f663c543e00010c74697018013042616c616e63654f663c543e000008590141207472616e73616374696f6e20666565206061637475616c5f666565602c206f662077686963682060746970602077617320616464656420746f20746865206d696e696d756d20696e636c7573696f6e206665652c5c686173206265656e2070616964206279206077686f602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574900c2c70616c6c65745f7375646f1870616c6c6574144576656e7404045400011014537564696404012c7375646f5f726573756c749401384469737061746368526573756c7404b454686520726573756c74206f66207468652063616c6c206d61646520627920746865207375646f20757365722e00047041207375646f2063616c6c206a75737420746f6f6b20706c6163652e284b65794368616e67656408010c6f6c649c01504f7074696f6e3c543a3a4163636f756e7449643e04b4546865206f6c64207375646f206b657920286966206f6e65207761732070726576696f75736c7920736574292e010c6e6577000130543a3a4163636f756e7449640488546865206e6577207375646f206b657920286966206f6e652077617320736574292e010478546865207375646f206b657920686173206265656e20757064617465642e284b657952656d6f76656400020480546865206b657920776173207065726d616e656e746c792072656d6f7665642e285375646f4173446f6e6504012c7375646f5f726573756c749401384469737061746368526573756c7404b454686520726573756c74206f66207468652063616c6c206d61646520627920746865207375646f20757365722e0304c841205b7375646f5f61735d2850616c6c65743a3a7375646f5f6173292063616c6c206a75737420746f6f6b20706c6163652e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574940418526573756c740804540198044501680108084f6b040098000000000c45727204006800000100009800000400009c04184f7074696f6e04045401000108104e6f6e6500000010536f6d650400000000010000a00c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144576656e74040454000128404e6577496e76756c6e657261626c6573040134696e76756c6e657261626c6573a401445665633c543a3a4163636f756e7449643e0000046c4e657720496e76756c6e657261626c65732077657265207365742e44496e76756c6e657261626c6541646465640401286163636f756e745f6964000130543a3a4163636f756e7449640001047441206e657720496e76756c6e657261626c65207761732061646465642e4c496e76756c6e657261626c6552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e74496400020470416e20496e76756c6e657261626c65207761732072656d6f7665642e504e65774465736972656443616e64696461746573040148646573697265645f63616e6469646174657310010c753332000304a4546865206e756d626572206f6620646573697265642063616e6469646174657320776173207365742e404e657743616e646964616379426f6e6404012c626f6e645f616d6f756e7418013042616c616e63654f663c543e0004046c5468652063616e64696461637920626f6e6420776173207365742e3843616e64696461746541646465640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e0005045c41206e65772063616e646964617465206a6f696e65642e5043616e646964617465426f6e64557064617465640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e00060470426f6e64206f6620612063616e64696461746520757064617465642e4043616e64696461746552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e74496400070460412063616e646964617465207761732072656d6f7665642e4443616e6469646174655265706c616365640c010c6f6c64000130543a3a4163636f756e74496400010c6e6577000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000804f4416e206163636f756e7420776173207265706c6163656420696e207468652063616e646964617465206c69737420627920616e6f74686572206f6e652e68496e76616c6964496e76756c6e657261626c65536b69707065640401286163636f756e745f6964000130543a3a4163636f756e7449640009085501416e206163636f756e742077617320756e61626c6520746f20626520616464656420746f2074686520496e76756c6e657261626c65732062656361757365207468657920646964206e6f742068617665206b657973c8726567697374657265642e204f7468657220496e76756c6e657261626c6573206d61792068617665206265656e207365742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a40000020000a80c3870616c6c65745f73657373696f6e1870616c6c6574144576656e74000104284e657753657373696f6e04013473657373696f6e5f696e64657810013053657373696f6e496e64657800000839014e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f74207468659c626c6f636b206e756d626572206173207468652074797065206d6967687420737567676573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574ac0c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144576656e740404540001043c58636d704d65737361676553656e740401306d6573736167655f6861736804011c58636d48617368000004c0416e2048524d50206d657373616765207761732073656e7420746f2061207369626c696e672070617261636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b00c2870616c6c65745f78636d1870616c6c6574144576656e7404045400016024417474656d7074656404011c6f7574636f6d65b4015078636d3a3a6c61746573743a3a4f7574636f6d65000004a8457865637574696f6e206f6620616e2058434d206d6573736167652077617320617474656d707465642e1053656e741001186f726967696ebc01204c6f636174696f6e00012c64657374696e6174696f6ebc01204c6f636174696f6e00011c6d6573736167650501011c58636d3c28293e0001286d6573736167655f696404011c58636d486173680001045c412058434d206d657373616765207761732073656e742e48556e6578706563746564526573706f6e73650801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400020c5901517565727920726573706f6e736520726563656976656420776869636820646f6573206e6f74206d61746368206120726567697374657265642071756572792e2054686973206d61792062652062656361757365206155016d61746368696e6720717565727920776173206e6576657220726567697374657265642c206974206d617920626520626563617573652069742069732061206475706c696361746520726573706f6e73652c206f727062656361757365207468652071756572792074696d6564206f75742e34526573706f6e7365526561647908012071756572795f696430011c51756572794964000120726573706f6e736531010120526573706f6e73650003085d01517565727920726573706f6e736520686173206265656e20726563656976656420616e6420697320726561647920666f722074616b696e672077697468206074616b655f726573706f6e7365602e205468657265206973806e6f2072656769737465726564206e6f74696669636174696f6e2063616c6c2e204e6f7469666965640c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380004085901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e20686173a86265656e206469737061746368656420616e64206578656375746564207375636365737366756c6c792e404e6f746966794f76657277656967687414012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800013461637475616c5f77656967687428011857656967687400014c6d61785f62756467657465645f77656967687428011857656967687400050c4901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e5901636f756c64206e6f742062652064697370617463686564206265636175736520746865206469737061746368207765696768742069732067726561746572207468616e20746865206d6178696d756d20776569676874e46f726967696e616c6c7920627564676574656420627920746869732072756e74696d6520666f722074686520717565727920726573756c742e4c4e6f7469667944697370617463684572726f720c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380006085501517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e2054686572652077617320612067656e6572616c206572726f722077697468886469737061746368696e6720746865206e6f74696669636174696f6e2063616c6c2e484e6f746966794465636f64654661696c65640c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800070c5101517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652064697370617463682077617320756e61626c6520746f20626559016465636f64656420696e746f2061206043616c6c603b2074686973206d696768742062652064756520746f2064697370617463682066756e6374696f6e20686176696e672061207369676e6174757265207768696368946973206e6f742060286f726967696e2c20517565727949642c20526573706f6e736529602e40496e76616c6964526573706f6e6465720c01186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400014465787065637465645f6c6f636174696f6e550101404f7074696f6e3c4c6f636174696f6e3e00080c5901457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206f726967696e206c6f636174696f6e206f662074686520726573706f6e736520646f657355016e6f74206d6174636820746861742065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5c496e76616c6964526573706f6e64657256657273696f6e0801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400091c5101457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206578706563746564206f726967696e206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e34526573706f6e736554616b656e04012071756572795f696430011c51756572794964000a04c8526563656976656420717565727920726573706f6e736520686173206265656e207265616420616e642072656d6f7665642e34417373657473547261707065640c011068617368340110483235360001186f726967696ebc01204c6f636174696f6e0001186173736574737501013c56657273696f6e6564417373657473000b04b8536f6d65206173736574732068617665206265656e20706c6163656420696e20616e20617373657420747261702e5456657273696f6e4368616e67654e6f74696669656410012c64657374696e6174696f6ebc01204c6f636174696f6e000118726573756c7410012858636d56657273696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d48617368000c0c2501416e2058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e206d65737361676520686173206265656e20617474656d7074656420746f2062652073656e742e00e054686520636f7374206f662073656e64696e672069742028626f726e652062792074686520636861696e2920697320696e636c756465642e5c537570706f7274656456657273696f6e4368616e6765640801206c6f636174696f6ebc01204c6f636174696f6e00011c76657273696f6e10012858636d56657273696f6e000d08390154686520737570706f727465642076657273696f6e206f662061206c6f636174696f6e20686173206265656e206368616e6765642e2054686973206d69676874206265207468726f75676820616ec06175746f6d61746963206e6f74696669636174696f6e206f722061206d616e75616c20696e74657276656e74696f6e2e504e6f7469667954617267657453656e644661696c0c01206c6f636174696f6ebc01204c6f636174696f6e00012071756572795f696430011c517565727949640001146572726f72b8012058636d4572726f72000e0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f727c73656e64696e6720746865206e6f74696669636174696f6e20746f2069742e644e6f746966795461726765744d6967726174696f6e4661696c0801206c6f636174696f6ed901014456657273696f6e65644c6f636174696f6e00012071756572795f696430011c51756572794964000f0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f72b46d6967726174696e6720746865206c6f636174696f6e20746f206f7572206e65772058434d20666f726d61742e54496e76616c69645175657269657256657273696f6e0801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400101c5501457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652065787065637465642071756572696572206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e38496e76616c6964517565726965721001186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400014065787065637465645f71756572696572bc01204c6f636174696f6e0001506d617962655f61637475616c5f71756572696572550101404f7074696f6e3c4c6f636174696f6e3e00110c5d01457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652071756572696572206c6f636174696f6e206f662074686520726573706f6e736520646f657351016e6f74206d61746368207468652065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5056657273696f6e4e6f74696679537461727465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d486173680012085901412072656d6f746520686173207265717565737465642058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e2066726f6d20757320616e64207765206861766520686f6e6f7265642069742e1d01412076657273696f6e20696e666f726d6174696f6e206d6573736167652069732073656e7420746f207468656d20616e642069747320636f737420697320696e636c756465642e5856657273696f6e4e6f746966795265717565737465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d486173680013043d015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073656e642075732058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e732e6056657273696f6e4e6f74696679556e7265717565737465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d4861736800140825015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073746f70732073656e64696e672075732058434d2076657273696f6e206368616e6765386e6f74696669636174696f6e732e204665657350616964080118706179696e67bc01204c6f636174696f6e0001106665657311010118417373657473001504310146656573207765726520706169642066726f6d2061206c6f636174696f6e20666f7220616e206f7065726174696f6e20286f6674656e20666f72207573696e67206053656e6458636d60292e34417373657473436c61696d65640c011068617368340110483235360001186f726967696ebc01204c6f636174696f6e0001186173736574737501013c56657273696f6e6564417373657473001604c0536f6d65206173736574732068617665206265656e20636c61696d65642066726f6d20616e20617373657420747261706056657273696f6e4d6967726174696f6e46696e697368656404011c76657273696f6e10012858636d56657273696f6e00170484412058434d2076657273696f6e206d6967726174696f6e2066696e69736865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b4102c73746167696e675f78636d087634187472616974731c4f7574636f6d6500010c20436f6d706c6574650401107573656428011857656967687400000028496e636f6d706c657465080110757365642801185765696768740001146572726f72b801144572726f72000100144572726f720401146572726f72b801144572726f7200020000b8100c78636d08763318747261697473144572726f720001a0204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300304c6f636174696f6e46756c6c000400544c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e736976650014001054726170040030010c753634001500404578706563746174696f6e46616c73650016003850616c6c65744e6f74466f756e64001700304e616d654d69736d617463680018004c56657273696f6e496e636f6d70617469626c6500190050486f6c64696e67576f756c644f766572666c6f77001a002c4578706f72744572726f72001b00385265616e63686f724661696c6564001c00184e6f4465616c001d0028466565734e6f744d6574001e00244c6f636b4572726f72001f00304e6f5065726d697373696f6e00200028556e616e63686f726564002100384e6f744465706f73697461626c650022004c556e68616e646c656458636d56657273696f6e002300485765696768744c696d69745265616368656404002801185765696768740024001c426172726965720025004c5765696768744e6f74436f6d70757461626c650026004445786365656473537461636b4c696d697400270000bc102c73746167696e675f78636d087634206c6f636174696f6e204c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72c001244a756e6374696f6e730000c0102c73746167696e675f78636d087634246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400c401484172633c5b4a756e6374696f6e3b20315d3e0001000858320400e801484172633c5b4a756e6374696f6e3b20325d3e0002000858330400ec01484172633c5b4a756e6374696f6e3b20335d3e0003000858340400f001484172633c5b4a756e6374696f6e3b20345d3e0004000858350400f401484172633c5b4a756e6374696f6e3b20355d3e0005000858360400f801484172633c5b4a756e6374696f6e3b20365d3e0006000858370400fc01484172633c5b4a756e6374696f6e3b20375d3e0007000858380400010101484172633c5b4a756e6374696f6e3b20385d3e00080000c400000301000000c800c8102c73746167696e675f78636d087634206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e00010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964e00118426f6479496400011070617274e40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400d401244e6574776f726b496400090000cc0000061000d004184f7074696f6e04045401d40108104e6f6e6500000010536f6d650400d40000010000d4102c73746167696e675f78636d087634206a756e6374696f6e244e6574776f726b496400012c24427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d62657230010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f69642c010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090040506f6c6b61646f7442756c6c6574696e000a0000d8000003140000000800dc0000061800e0100c78636d087633206a756e6374696f6e18426f6479496400012810556e69740000001c4d6f6e696b6572040048011c5b75383b20345d00010014496e6465780400cc010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000e4100c78636d087633206a756e6374696f6e20426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74cc010c753332000100204672616374696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c75333200040000e800000302000000c800ec00000303000000c800f000000304000000c800f400000305000000c800f800000306000000c800fc00000307000000c800010100000308000000c80005010c2c73746167696e675f78636d0876340c58636d041043616c6c00000400090101585665633c496e737472756374696f6e3c43616c6c3e3e000009010000020d01000d010c2c73746167696e675f78636d0876342c496e737472756374696f6e041043616c6c0001c03457697468647261774173736574040011010118417373657473000000545265736572766541737365744465706f7369746564040011010118417373657473000100585265636569766554656c65706f727465644173736574040011010118417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e736531010120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572550101404f7074696f6e3c4c6f636174696f6e3e000300345472616e7366657241737365740801186173736574731101011841737365747300012c62656e6566696369617279bc01204c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574731101011841737365747300011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6c5d01014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c00140496e746572696f724c6f636174696f6e000b002c5265706f72744572726f720400610101445175657279526573706f6e7365496e666f000c00304465706f73697441737365740801186173736574736501012c417373657446696c74657200012c62656e6566696369617279bc01204c6f636174696f6e000d004c4465706f7369745265736572766541737365740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000e003445786368616e676541737365740c0110676976656501012c417373657446696c74657200011077616e741101011841737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c01186173736574736501012c417373657446696c74657200011c72657365727665bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0001186173736574736501012c417373657446696c74657200120030427579457865637574696f6e080110666565731901011441737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204000501012458636d3c43616c6c3e0015002c536574417070656e64697804000501012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473110101184173736574730001187469636b6574bc01204c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e4173736574040011010118417373657473001c002c4578706563744173736574040011010118417373657473001d00304578706563744f726967696e0400550101404f7074696f6e3c4c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400610101445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c801204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bd401244e6574776f726b496400012c64657374696e6174696f6ec00140496e746572696f724c6f636174696f6e00010c78636d0501011c58636d3c28293e002600244c6f636b41737365740801146173736574190101144173736574000120756e6c6f636b6572bc01204c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574190101144173736574000118746172676574bc01204c6f636174696f6e002800384e6f7465556e6c6f636b61626c6508011461737365741901011441737365740001146f776e6572bc01204c6f636174696f6e0029003452657175657374556e6c6f636b08011461737365741901011441737365740001186c6f636b6572bc01204c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bc01204c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696e550101404f7074696f6e3c4c6f636174696f6e3e002f00001101102c73746167696e675f78636d0876341461737365741841737365747300000400150101285665633c41737365743e000015010000021901001901102c73746167696e675f78636d087634146173736574144173736574000008010869641d01011c4173736574496400010c66756e2101012c46756e676962696c69747900001d01102c73746167696e675f78636d0876341461737365741c4173736574496400000400bc01204c6f636174696f6e00002101102c73746167696e675f78636d0876341461737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400250101344173736574496e7374616e6365000100002501102c73746167696e675f78636d087634146173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d0005000029010000030800000008002d0100000310000000080031010c2c73746167696e675f78636d08763420526573706f6e7365000118104e756c6c000000184173736574730400110101184173736574730001003c457865637574696f6e526573756c740400350101504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f04003d010198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c7404004d0101384d617962654572726f72436f646500050000350104184f7074696f6e0404540139010108104e6f6e6500000010536f6d6504003901000001000039010000040810b8003d010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014101045300000400490101185665633c543e000041010c2c73746167696e675f78636d0876342850616c6c6574496e666f0000180114696e646578cc010c7533320001106e616d6545010180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d6545010180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72cc010c7533320001146d696e6f72cc010c7533320001147061746368cc010c753332000045010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e000049010000024101004d010c0c78636d087633384d617962654572726f72436f646500010c1c53756363657373000000144572726f7204005101018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e000100385472756e63617465644572726f7204005101018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e0002000051010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000550104184f7074696f6e04045401bc0108104e6f6e6500000010536f6d650400bc000001000059010c0c78636d087632284f726967696e4b696e64000110184e617469766500000040536f7665726569676e4163636f756e74000100245375706572757365720002000c58636d000300005d010c0c78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656438011c5665633c75383e000061010c2c73746167696e675f78636d087634445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ebc01204c6f636174696f6e00012071756572795f69642c011c517565727949640001286d61785f77656967687428011857656967687400006501102c73746167696e675f78636d0876341461737365742c417373657446696c74657200010820446566696e6974650400110101184173736574730000001057696c6404006901012457696c644173736574000100006901102c73746167696e675f78636d0876341461737365742457696c6441737365740001100c416c6c00000014416c6c4f6608010869641d01011c4173736574496400010c66756e6d01013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400cc010c75333200020030416c6c4f66436f756e7465640c010869641d01011c4173736574496400010c66756e6d01013c57696c6446756e676962696c697479000114636f756e74cc010c753332000300006d01102c73746167696e675f78636d0876341461737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c650001000071010c0c78636d0876332c5765696768744c696d697400010824556e6c696d697465640000001c4c696d697465640400280118576569676874000100007501080c78636d3c56657273696f6e656441737365747300010c08563204007901013c76323a3a4d756c74694173736574730001000856330400ad01013c76333a3a4d756c746941737365747300030008563404001101012876343a3a417373657473000400007901100c78636d087632286d756c746961737365742c4d756c7469417373657473000004007d01013c5665633c4d756c746941737365743e00007d010000028101008101100c78636d087632286d756c74696173736574284d756c74694173736574000008010869648501011c4173736574496400010c66756ea501012c46756e676962696c69747900008501100c78636d087632286d756c746961737365741c4173736574496400010820436f6e63726574650400890101344d756c74694c6f636174696f6e000000204162737472616374040038011c5665633c75383e000100008901100c78636d087632346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f728d0101244a756e6374696f6e7300008d01100c78636d087632346d756c74696c6f636174696f6e244a756e6374696f6e7300012410486572650000000858310400910101204a756e6374696f6e0001000858320800910101204a756e6374696f6e0000910101204a756e6374696f6e0002000858330c00910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0003000858341000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0004000858351400910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0005000858361800910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0006000858371c00910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0007000858382000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e000800009101100c78636d087632206a756e6374696f6e204a756e6374696f6e0001242450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726b950101244e6574776f726b496400010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726b950101244e6574776f726b4964000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726b950101244e6574776f726b496400010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e000600244f6e6c794368696c6400070024506c7572616c69747908010869649d010118426f6479496400011070617274a1010120426f6479506172740008000095010c0c78636d087632244e6574776f726b49640001100c416e79000000144e616d65640400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010020506f6c6b61646f74000200184b7573616d610003000099010c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401080453000004003801185665633c543e00009d010c0c78636d08763218426f6479496400012810556e6974000000144e616d65640400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010014496e6465780400cc010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000a1010c0c78636d08763220426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74cc010c753332000100204672616374696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c75333200040000a501100c78636d087632286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400a90101344173736574496e7374616e636500010000a901100c78636d087632286d756c74696173736574344173736574496e7374616e636500011c24556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050010426c6f62040038011c5665633c75383e00060000ad01100c78636d087633286d756c746961737365742c4d756c746941737365747300000400b101013c5665633c4d756c746941737365743e0000b101000002b50100b501100c78636d087633286d756c74696173736574284d756c7469417373657400000801086964b901011c4173736574496400010c66756ed101012c46756e676962696c6974790000b901100c78636d087633286d756c746961737365741c4173736574496400010820436f6e63726574650400bd0101344d756c74694c6f636174696f6e00000020416273747261637404000401205b75383b2033325d00010000bd01102c73746167696e675f78636d087633346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72c10101244a756e6374696f6e730000c101100c78636d087633246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400c50101204a756e6374696f6e0001000858320800c50101204a756e6374696f6e0000c50101204a756e6374696f6e0002000858330c00c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0003000858341000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0004000858351400c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0005000858361800c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0006000858371c00c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0007000858382000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e00080000c501100c78636d087633206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e00010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964e00118426f6479496400011070617274e40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400cd0101244e6574776f726b496400090000c90104184f7074696f6e04045401cd010108104e6f6e6500000010536f6d650400cd010000010000cd01100c78636d087633206a756e6374696f6e244e6574776f726b496400012c24427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d62657230010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f69642c010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090040506f6c6b61646f7442756c6c6574696e000a0000d101100c78636d087633286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400d50101344173736574496e7374616e636500010000d501100c78636d087633286d756c74696173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050000d901080c78636d4456657273696f6e65644c6f636174696f6e00010c08563204008901014476323a3a4d756c74694c6f636174696f6e0001000856330400bd01014476333a3a4d756c74694c6f636174696f6e0003000856340400bc013076343a3a4c6f636174696f6e00040000dd010c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144576656e7404045400010c34496e76616c6964466f726d617404000401205b75383b2033325d00000880446f776e77617264206d65737361676520697320696e76616c69642058434d2e205c5b206964205c5d48556e737570706f7274656456657273696f6e04000401205b75383b2033325d000108bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e205c5b206964205c5d404578656375746564446f776e7761726408000401205b75383b2033325d0000b4011c4f7574636f6d65000208c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e445c5b2069642c206f7574636f6d65205c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e1010c5070616c6c65745f6d6573736167655f71756575651870616c6c6574144576656e740404540001104050726f63657373696e674661696c65640c010869643401104832353604945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e01146572726f72ed01014c50726f636573734d6573736167654572726f721060546865206572726f722074686174206f636375727265642e00490154686973206572726f7220697320707265747479206f70617175652e204d6f72652066696e652d677261696e6564206572726f7273206e65656420746f20626520656d6974746564206173206576656e74736862792074686520604d65737361676550726f636573736f72602e000455014d657373616765206469736361726465642064756520746f20616e206572726f7220696e2074686520604d65737361676550726f636573736f72602028757375616c6c79206120666f726d6174206572726f72292e2450726f63657373656410010869643401104832353604945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e012c7765696768745f7573656428011857656967687404c0486f77206d7563682077656967687420776173207573656420746f2070726f6365737320746865206d6573736167652e011c73756363657373200110626f6f6c18885768657468657220746865206d657373616765207761732070726f6365737365642e0049014e6f74652074686174207468697320646f6573206e6f74206d65616e20746861742074686520756e6465726c79696e6720604d65737361676550726f636573736f72602077617320696e7465726e616c6c7935017375636365737366756c2e204974202a736f6c656c792a206d65616e73207468617420746865204d512070616c6c65742077696c6c2074726561742074686973206173206120737563636573734d01636f6e646974696f6e20616e64206469736361726420746865206d6573736167652e20416e7920696e7465726e616c206572726f72206e6565647320746f20626520656d6974746564206173206576656e74736862792074686520604d65737361676550726f636573736f72602e0104544d6573736167652069732070726f6365737365642e484f766572776569676874456e71756575656410010869640401205b75383b2033325d04945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e0128706167655f696e64657810012450616765496e64657804605468652070616765206f6620746865206d6573736167652e01346d6573736167655f696e64657810011c543a3a53697a6504a454686520696e646578206f6620746865206d6573736167652077697468696e2074686520706167652e02048c4d65737361676520706c6163656420696e206f7665727765696768742071756575652e28506167655265617065640801186f726967696ee50101484d6573736167654f726967696e4f663c543e0458546865207175657565206f662074686520706167652e0114696e64657810012450616765496e646578045854686520696e646578206f662074686520706167652e03045454686973207061676520776173207265617065642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e501085c63756d756c75735f7072696d6974697665735f636f7265584167677265676174654d6573736167654f726967696e00010c104865726500000018506172656e740001001c5369626c696e670400e901011850617261496400020000e9010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665730849640000040010010c7533320000ed0110346672616d655f737570706f727418747261697473206d657373616765734c50726f636573734d6573736167654572726f7200011424426164466f726d61740000001c436f72727570740001002c556e737570706f72746564000200284f7665727765696768740400280118576569676874000300145969656c6400040000f1010c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c6574144576656e740404540001245c4d7370526571756573745369676e55705375636365737310010c77686f000130543a3a4163636f756e7449640001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e000008b1014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f7669646572206861732072657175657374656420746f207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574e50174686174204d53502773206163636f756e742069642c20697473206d756c74696164647265737365732c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20616e64206974732076616c75652070726f706f736974696f6e2e404d73705369676e55705375636365737314010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e000108b5014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f76696465722068617320636f6e6669726d656420697473207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574dd0174686174204d53502773206163636f756e742069642c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20697473206d756c7469616464726573732c20616e64206974732076616c75652070726f706f736974696f6e2e5c427370526571756573745369676e5570537563636573730c010c77686f000130543a3a4163636f756e7449640001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e000208b9014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f7669646572206861732072657175657374656420746f207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574890174686174204253502773206163636f756e742069642c20697473206d756c74696164647265737365732c20616e642074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652e404273705369676e55705375636365737310010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e000308bd014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f76696465722068617320636f6e6669726d656420697473207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574810174686174204253502773206163636f756e742069642c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20616e6420697473206d756c7469616464726573732e545369676e55705265717565737443616e63656c656404010c77686f000130543a3a4163636f756e7449640004087d014576656e7420656d6974746564207768656e2061207369676e207570207265717565737420686173206265656e2063616e63656c6564207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574d4746865206163636f756e74206964206f6620746865207573657220746861742063616e63656c65642074686520726571756573742e444d73705369676e4f66665375636365737308010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e00050889014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f766964657220686173207369676e6564206f6666207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75745874686174204d53502773206163636f756e742069642e444273705369676e4f66665375636365737308010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e00060891014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f766964657220686173207369676e6564206f6666207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75745874686174204253502773206163636f756e742069642e3c43617061636974794368616e67656414010c77686f000130543a3a4163636f756e74496400012c70726f76696465725f69640902015053746f7261676550726f766964657249643c543e0001306f6c645f636170616369747930014853746f7261676544617461556e69743c543e0001306e65775f636170616369747930014853746f7261676544617461556e69743c543e0001786e6578745f626c6f636b5f7768656e5f6368616e67655f616c6c6f776564100144426c6f636b4e756d626572466f723c543e00070865014576656e7420656d6974746564207768656e206120535020686173206368616e67656420697473206361706163697479207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75744901746861742053502773206163636f756e742069642c20697473206f6c6420746f74616c2064617461207468617420636f756c642073746f72652c20616e6420746865206e657720746f74616c20646174612e1c536c617368656408012c70726f76696465725f69643401244861736849643c543e000138616d6f756e745f736c617368656418013042616c616e63654f663c543e000804a84576656e7420656d6974746564207768656e20616e20535020686173206265656e20736c61736865642e04b4546865206576656e747320746861742063616e20626520656d697474656420627920746869732070616c6c6574f5010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401f901045300000400fd0101185665633c543e0000f9010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000fd01000002f9010001020c6070616c6c65745f73746f726167655f70726f7669646572731474797065734056616c756550726f706f736974696f6e04045400000c01286964656e74696669657234013856616c756550726f7049643c543e000128646174615f6c696d697430014853746f7261676544617461556e69743c543e00012470726f746f636f6c73050201a4426f756e6465645665633c50726f746f636f6c733c543e2c204d617850726f746f636f6c733c543e3e000005020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401f901045300000400fd0101185665633c543e000009020c6070616c6c65745f73746f726167655f70726f7669646572731474797065734453746f7261676550726f76696465724964040454000108544261636b757053746f7261676550726f766964657204003401684261636b757053746f7261676550726f766964657249643c543e0000004c4d61696e53746f7261676550726f766964657204003401604d61696e53746f7261676550726f766964657249643c543e000100000d020c4870616c6c65745f66696c655f73797374656d1870616c6c6574144576656e7404045400015c244e65774275636b657418010c77686f000130543a3a4163636f756e7449640001186d73705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001106e616d65f90101404275636b65744e616d65466f723c543e000134636f6c6c656374696f6e5f6964110201684f7074696f6e3c436f6c6c656374696f6e4964466f723c543e3e00011c70726976617465200110626f6f6c000004b04e6f74696669657320746861742061206e6577206275636b657420686173206265656e20637265617465642e4c4d6f76654275636b65745265717565737465640c010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e0001286e65775f6d73705f696434014050726f76696465724964466f723c543e000104cc4e6f74696669657320746861742061206275636b6574206973206265696e67206d6f76656420746f2061206e6577204d53502e504275636b6574507269766163795570646174656410010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e000134636f6c6c656374696f6e5f6964110201684f7074696f6e3c436f6c6c656374696f6e4964466f723c543e3e00011c70726976617465200110626f6f6c000204c84e6f74696669657320746861742061206275636b65742773207072697661637920686173206265656e20757064617465642e6c4e6577436f6c6c656374696f6e416e644173736f63696174696f6e0c010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e000134636f6c6c656374696f6e5f6964100148436f6c6c656374696f6e4964466f723c543e00030435014e6f74696669657320746861742061206e657720636f6c6c656374696f6e20686173206265656e206372656174656420616e64206173736f63696174656420776974682061206275636b65742e444e657753746f72616765526571756573741c010c77686f000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e000120706565725f696473f5010128506565724964733c543e000404e44e6f74696669657320746861742061206e65772066696c6520686173206265656e2072657175657374656420746f2062652073746f7265642e744d7370526573706f6e646564546f53746f72616765526571756573747304011c726573756c7473190201884d7370526573706f6e6453746f726167655265717565737473526573756c743c543e000504e04e6f74696669657320746861742061204d53502068617320726573706f6e64656420746f2073746f7261676520726571756573742873292e504163636570746564427370566f6c756e746565721c01186273705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e0001386d756c7469616464726573736573f50101444d756c74694164647265737365733c543e0001146f776e6572000130543a3a4163636f756e74496400011073697a6530013853746f72616765446174613c543e000604f04e6f746966696573207468617420612042535020686173206265656e20616363657074656420746f2073746f7265206120676976656e2066696c652e4c427370436f6e6669726d656453746f72696e6710010c77686f000130543a3a4163636f756e7449640001186273705f696434014050726f76696465724964466f723c543e00012466696c655f6b657973290201f0426f756e6465645665633c4d65726b6c65486173683c543e2c20543a3a4d61784261746368436f6e6669726d53746f7261676552657175657374733e0001206e65775f726f6f743401344d65726b6c65486173683c543e000704c04e6f746966696573207468617420612042535020636f6e6669726d65642073746f72696e6720612066696c652873292e5c53746f726167655265717565737446756c66696c6c656404012066696c655f6b65793401344d65726b6c65486173683c543e00080409014e6f746966696573207468617420612073746f72616765207265717565737420666f7220612066696c65206b657920686173206265656e2066756c66696c6c65642e5453746f72616765526571756573744578706972656404012066696c655f6b65793401344d65726b6c65486173683c543e000904b44e6f746966696573207468652065787069726174696f6e206f6620612073746f7261676520726571756573742e5453746f72616765526571756573745265766f6b656404012066696c655f6b65793401344d65726b6c65486173683c543e000a0439014e6f746966696573207468617420612073746f72616765207265717565737420686173206265656e207265766f6b65642062792074686520757365722077686f20696e697469617465642069742e64427370526571756573746564546f53746f7053746f72696e671001186273705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e000b0401014e6f746966696573207468617420612042535020686173206f70656e65642061207265717565737420746f2073746f702073746f72696e6720612066696c652e60427370436f6e6669726d53746f7070656453746f72696e670c01186273705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001206e65775f726f6f743401344d65726b6c65486173683c543e000c04bc4e6f7469666965732074686174206120425350206861732073746f707065642073746f72696e6720612066696c652e985072696f726974794368616c6c656e6765466f7246696c6544656c6574696f6e517565756564080118697373756572590201644569746865724163636f756e7449644f724d737049643c543e00012066696c655f6b65793401344d65726b6c65486173683c543e000d0451014e6f746966696573207468617420612066696c65206b657920686173206265656e2071756575656420666f722061207072696f72697479206368616c6c656e676520666f722066696c652064656c6574696f6e2e68537053746f7053746f72696e67496e736f6c76656e745573657214011473705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001206e65775f726f6f743401344d65726b6c65486173683c543e000e0455014e6f74696669657320746861742061205350206861732073746f707065642073746f72696e6720612066696c65206265636175736520697473206f776e657220686173206265636f6d6520696e736f6c76656e742e784661696c6564546f51756575655072696f726974794368616c6c656e676508011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e000f0445014e6f74696669657320746861742061207072696f72697479206368616c6c656e6765206661696c656420746f2062652071756575656420666f722070656e64696e672066696c652064656c6574696f6e2e4c46696c6544656c6574696f6e5265717565737414011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e00014870726f6f665f6f665f696e636c7573696f6e200110626f6f6c001004944e6f746966696573207468617420612066696c652077696c6c2062652064656c657465642eac50726f6f665375626d6974746564466f7250656e64696e6746696c6544656c6574696f6e526571756573741401186d73705f696434014050726f76696465724964466f723c543e00011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e00014870726f6f665f6f665f696e636c7573696f6e200110626f6f6c00110435014e6f746966696573207468617420612070726f6f6620686173206265656e207375626d697474656420666f7220612070656e64696e672066696c652064656c6574696f6e20726571756573742e704273704368616c6c656e67654379636c65496e697469616c6973656408010c77686f000130543a3a4163636f756e7449640001186273705f696434014050726f76696465724964466f723c543e00120845014e6f74696669657320746861742061204253502773206368616c6c656e6765206379636c6520686173206265656e20696e697469616c697365642c20616464696e67207468652066697273742066696c65ac6b657928732920746f20746865204253502773204d65726b6c6520506174726963696120466f726573742e604d6f76654275636b657452657175657374457870697265640801186d73705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e001304c04e6f74696669657320746861742061206d6f7665206275636b657420726571756573742068617320657870697265642e484d6f76654275636b657441636365707465640801246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e001404cc4e6f74696669657320746861742061206275636b657420686173206265656e206d6f76656420746f2061206e6577204d53502e484d6f76654275636b657452656a65637465640801246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e00150405014e6f74696669657320746861742061206275636b6574206d6f7665207265717565737420686173206265656e2072656a656374656420627920746865204d53502e844461746153657276657252656769737465726564466f724d6f76654275636b65740801186273705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e00160429014e6f7469666965732074686174206120646174612073657276657220686173206265656e207265676973746572656420666f722061206d6f7665206275636b657420726571756573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574110204184f7074696f6e04045401100108104e6f6e6500000010536f6d65040010000001000015020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e000019020c4870616c6c65745f66696c655f73797374656d1474797065737c4d7370526573706f6e6453746f726167655265717565737473526573756c7404045400000801186d73705f696434014050726f76696465724964466f723c543e000124726573706f6e7365731d02010d01426f756e6465645665633c4261746368526573706f6e7365733c543e2c204d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e00001d020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012102045300000400550201185665633c543e000021020c4870616c6c65745f66696c655f73797374656d147479706573384261746368526573706f6e73657304045400010c2041636365707465640400250201884d73704163636570746564426174636853746f7261676552657175657374733c543e0000002052656a65637465640400310201884d737052656a6563746564426174636853746f7261676552657175657374733c543e000100184661696c65640400450201804d73704661696c6564426174636853746f7261676552657175657374733c543e0002000025020c4870616c6c65745f66696c655f73797374656d1474797065737c4d73704163636570746564426174636853746f726167655265717565737473040454000010012466696c655f6b657973290201fc426f756e6465645665633c4d65726b6c65486173683c543e2c204d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001246275636b65745f69643401384275636b65744964466f723c543e00013c6e65775f6275636b65745f726f6f743401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e744964000029020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e00002d02000002340031020c4870616c6c65745f66696c655f73797374656d1474797065737c4d737052656a6563746564426174636853746f72616765526571756573747304045400000c012466696c655f6b6579733502018101426f756e6465645665633c284d65726b6c65486173683c543e2c2052656a656374656453746f7261676552657175657374526561736f6e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e2c3e0001246275636b65745f69643401384275636b65744964466f723c543e0001146f776e6572000130543a3a4163636f756e744964000035020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454013902045300000400410201185665633c543e0000390200000408343d02003d020c4870616c6c65745f66696c655f73797374656d1474797065737052656a656374656453746f7261676552657175657374526561736f6e00010c58526561636865644d6178696d756d4361706163697479000000505265636569766564496e76616c696450726f6f6600010034496e7465726e616c4572726f7200020000410200000239020045020c4870616c6c65745f66696c655f73797374656d147479706573744d73704661696c6564426174636853746f72616765526571756573747304045400000c012466696c655f6b6579734902014101426f756e6465645665633c284d65726b6c65486173683c543e2c2044697370617463684572726f72292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001246275636b65745f69643401384275636b65744964466f723c543e0001146f776e6572000130543a3a4163636f756e744964000049020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014d02045300000400510201185665633c543e00004d020000040834680051020000024d0200550200000221020059020c4870616c6c65745f66696c655f73797374656d147479706573584569746865724163636f756e7449644f724d73704964040454000108244163636f756e7449640400000130543a3a4163636f756e744964000000144d73704964040034014050726f76696465724964466f723c543e000100005d020c5070616c6c65745f70726f6f66735f6465616c65721870616c6c6574144576656e74040454000124304e65774368616c6c656e676508010c77686f00013c4163636f756e744964466f723c543e0001386b65795f6368616c6c656e6765643401244b6579466f723c543e0000048441206d616e75616c206368616c6c656e676520776173207375626d69747465642e3450726f6f66416363657074656408012070726f766964657234014050726f76696465724964466f723c543e00011470726f6f666102012050726f6f663c543e00010454412070726f6f66207761732061636365707465642e404e65774368616c6c656e6765536565640801446368616c6c656e6765735f7469636b6572100144426c6f636b4e756d626572466f723c543e0001107365656434015852616e646f6d6e6573734f7574707574466f723c543e0002048c41206e6577206368616c6c656e67652073656564207761732067656e6572617465642e584e6577436865636b706f696e744368616c6c656e67650801446368616c6c656e6765735f7469636b6572100144426c6f636b4e756d626572466f723c543e0001286368616c6c656e6765738902015d01426f756e6465645665633c284b6579466f723c543e2c204f7074696f6e3c5472696552656d6f76654d75746174696f6e3e292c0a4d6178437573746f6d4368616c6c656e676573506572426c6f636b466f723c543e2c3e000304a441206e657720636865636b706f696e74206368616c6c656e6765207761732067656e6572617465642e44536c61736861626c6550726f766964657208012070726f766964657234014050726f76696465724964466f723c543e00015c6e6578745f6368616c6c656e67655f646561646c696e65100144426c6f636b4e756d626572466f723c543e0004045901412070726f766964657220776173206d61726b656420617320736c61736861626c6520616e64207468656972206368616c6c656e676520646561646c696e652077617320666f72636566756c6c79207075736865642e704e6f5265636f72644f664c6173745375626d697474656450726f6f6604012070726f766964657234014050726f76696465724964466f723c543e000504f84e6f207265636f7264206f6620746865206c617374207469636b207468652050726f7669646572207375626d697474656420612070726f6f6620666f722e704e65774368616c6c656e67654379636c65496e697469616c6973656410013063757272656e745f7469636b100144426c6f636b4e756d626572466f723c543e00015c6e6578745f6368616c6c656e67655f646561646c696e65100144426c6f636b4e756d626572466f723c543e00012070726f766964657234014050726f76696465724964466f723c543e0001586d617962655f70726f76696465725f6163636f756e749c01504f7074696f6e3c543a3a4163636f756e7449643e000604b4412070726f76696465722773206368616c6c656e6765206379636c652077617320696e697469616c697365642e404d75746174696f6e734170706c6965640c012070726f766964657234014050726f76696465724964466f723c543e0001246d75746174696f6e739d0201905665633c284b6579466f723c543e2c205472696552656d6f76654d75746174696f6e293e0001206e65775f726f6f743401244b6579466f723c543e000704c84120736574206f66206d75746174696f6e7320686173206265656e206170706c69656420746f2074686520466f726573742e4c4368616c6c656e6765735469636b6572536574040118706175736564200110626f6f6c000804d4546865205b604368616c6c656e6765735469636b6572605d20686173206265656e20706175736564206f7220756e7061757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657461020c5070616c6c65745f70726f6f66735f6465616c65721474797065731450726f6f660404540000080130666f726573745f70726f6f6665020164466f72657374566572696669657250726f6f66466f723c543e0001286b65795f70726f6f66736d02018042547265654d61703c4b6579466f723c543e2c204b657950726f6f663c543e3e000065020c1c73705f747269653473746f726167655f70726f6f6630436f6d7061637450726f6f660000040134656e636f6465645f6e6f646573690201305665633c5665633c75383e3e0000690200000238006d02042042547265654d617008044b01340456017102000400810200000071020c5070616c6c65745f70726f6f66735f6465616c6572147479706573204b657950726f6f66040454000008011470726f6f66750201584b6579566572696669657250726f6f66466f723c543e00013c6368616c6c656e67655f636f756e7410010c753332000075020c547368705f66696c655f6b65795f76657269666965721474797065733046696c654b657950726f6f66000008013466696c655f6d65746164617461790201d846696c654d657461646174613c485f4c454e4754482c204348554e4b5f53495a452c2053495a455f544f5f4348414c4c454e4745533e00011470726f6f6665020130436f6d7061637450726f6f660000790208447368705f66696c655f6d657461646174613046696c654d6574616461746100001401146f776e657238011c5665633c75383e0001246275636b65745f696438011c5665633c75383e0001206c6f636174696f6e38011c5665633c75383e00012466696c655f73697a652c010c75363400012c66696e6765727072696e747d02015446696e6765727072696e743c485f4c454e4754483e00007d0208447368705f66696c655f6d657461646174612c46696e6765727072696e7400000400040138486173683c485f4c454e4754483e000081020000028502008502000004083471020089020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454018d02045300000400990201185665633c543e00008d020000040834910200910204184f7074696f6e0404540195020108104e6f6e6500000010536f6d65040095020000010000950208287368705f747261697473485472696552656d6f76654d75746174696f6e0000000099020000028d02009d02000002a10200a1020000040834950200a5020c4470616c6c65745f72616e646f6d6e6573731870616c6c6574144576656e74040454000104844e65774f6e6545706f636841676f52616e646f6d6e657373417661696c61626c650c013c72616e646f6d6e6573735f7365656434011c543a3a4861736800012866726f6d5f65706f636830010c75363400014476616c69645f756e74696c5f626c6f636b100144426c6f636b4e756d626572466f723c543e00000419014576656e7420656d6974746564207768656e2061206e65772072616e646f6d207365656420697320617661696c61626c652066726f6d207468652072656c617920636861696e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a9020c5870616c6c65745f7061796d656e745f73747265616d731870616c6c6574144576656e7404045400012c744669786564526174655061796d656e7453747265616d437265617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e0001107261746518013042616c616e63654f663c543e000008e5014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d20697320637265617465642e2050726f766964657320696e666f726d6174696f6e2061626f7574207468652050726f766964657220616e642055736572206f66207468652073747265616d54616e642069747320696e697469616c20726174652e744669786564526174655061796d656e7453747265616d557064617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e0001206e65775f7261746518013042616c616e63654f663c543e000108e5014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d20697320757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d7c616e6420746865206e65772072617465206f66207468652073747265616d2e744669786564526174655061796d656e7453747265616d44656c65746564080130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000204e9014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d2069732072656d6f7665642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d2e7c44796e616d6963526174655061796d656e7453747265616d437265617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e00013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000308ed014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d20697320637265617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d80616e642074686520696e697469616c20616d6f756e742070726f76696465642e7c44796e616d6963526174655061796d656e7453747265616d557064617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e00014c6e65775f616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000408ed014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d20697320757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d70616e6420746865206e657720616d6f756e742070726f76696465642e7c44796e616d6963526174655061796d656e7453747265616d44656c65746564080130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000504f1014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d2069732072656d6f7665642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d2e505061796d656e7453747265616d436861726765640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000118616d6f756e7418013042616c616e63654f663c543e00060879014576656e7420656d6974746564207768656e2061207061796d656e7420697320636861726765642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865207573657220746861742077617320636861726765642c19017468652050726f76696465722074686174207265636569766564207468652066756e64732c20616e642074686520616d6f756e7420746861742077617320636861726765642e644c61737443686172676561626c65496e666f557064617465640c012c70726f76696465725f696434014050726f76696465724964466f723c543e0001506c6173745f63686172676561626c655f7469636b100144426c6f636b4e756d626572466f723c543e00016c6c6173745f63686172676561626c655f70726963655f696e64657818013042616c616e63654f663c543e00070821024576656e7420656d6974746564207768656e20612050726f76696465722773206c6173742063686172676561626c65207469636b20616e6420707269636520696e6465782061726520757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f7574207468652050726f7669646572206f66207468652073747265616d2c3501746865207469636b206e756d626572206f6620746865206c6173742063686172676561626c65207469636b20616e642074686520707269636520696e6465782061742074686174207469636b2e4055736572576974686f757446756e647304010c77686f000130543a3a4163636f756e74496400080c19024576656e7420656d6974746564207768656e20612050726f766964657220697320636f72726563746c7920747279696e6720746f206368617267652061205573657220616e642074686174205573657220646f6573206e6f74206861766520656e6f7567682066756e647320746f2070617920666f722074686569722073657276696365732e390254686973206576656e7420697320656d697474656420746f20666c616720746865207573657220616e64206c657420746865206e6574776f726b206b6e6f772074686174207468652075736572206973206e6f7420706179696e6720666f7220746865207265717565737465642073657276696365732c20736f206f746865722050726f7669646572732063616e9473746f702070726f766964696e6720736572766963657320746f207468617420757365722e345573657250616964446562747304010c77686f000130543a3a4163636f756e74496400090449024576656e7420656d6974746564207768656e20612055736572207468617420686173206265656e20666c6167676564206173206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220746865697220636f6e7472616374656420736572766963657320686173207061696420616c6c20697473206f75747374616e64696e6720646562742e2c55736572536f6c76656e7404010c77686f000130543a3a4163636f756e744964000a083d024576656e7420656d6974746564207768656e20612055736572207468617420686173206265656e20666c6167676564206173206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220746865697220636f6e7472616374656420736572766963657320686173207761697465642074686520636f6f6c646f776e20706572696f642c4901636f72726563746c79207061696420616c6c207468656972206f75747374616e64696e67206465627420616e642063616e206e6f7720636f6e7472616374206e657720736572766963657320616761696e2e04b4546865206576656e747320746861742063616e20626520656d697474656420627920746869732070616c6c6574ad020c4870616c6c65745f6275636b65745f6e6674731870616c6c6574144576656e7404045400010c30416363657373536861726564080118697373756572000130543a3a4163636f756e744964000124726563697069656e740001684163636f756e7449644c6f6f6b75705461726765744f663c543e00000419014e6f74696669657320746861742061636365737320746f2061206275636b657420686173206265656e20736861726564207769746820616e6f74686572206163636f756e742e544974656d52656164416363657373557064617465640c011461646d696e000130543a3a4163636f756e7449640001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000104ec4e6f74696669657320746861742074686520726561642061636365737320666f7220616e206974656d20686173206265656e20757064617465642e284974656d4275726e65640c011c6163636f756e74000130543a3a4163636f756e7449640001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000204984e6f746966696573207468617420616e206974656d20686173206265656e206275726e65642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b1020c2c70616c6c65745f6e6674731870616c6c6574144576656e740804540004490001981c437265617465640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011c63726561746f72000130543a3a4163636f756e7449640001146f776e6572000130543a3a4163636f756e7449640000046c412060636f6c6c656374696f6e602077617320637265617465642e30466f72636543726561746564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146f776e6572000130543a3a4163636f756e74496400010484412060636f6c6c656374696f6e602077617320666f7263652d637265617465642e2444657374726f796564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400020474412060636f6c6c656374696f6e60207761732064657374726f7965642e184973737565640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400030454416e20606974656d6020776173206973737565642e2c5472616e73666572726564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e74496400040468416e20606974656d6020776173207472616e736665727265642e184275726e65640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400050460416e20606974656d60207761732064657374726f7965642e484974656d5472616e736665724c6f636b6564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400060488416e20606974656d6020626563616d65206e6f6e2d7472616e7366657261626c652e504974656d5472616e73666572556e6c6f636b6564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400070478416e20606974656d6020626563616d65207472616e7366657261626c652e504974656d50726f706572746965734c6f636b6564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001346c6f636b5f6d65746164617461200110626f6f6c00013c6c6f636b5f61747472696275746573200110626f6f6c000804a8606974656d60206d65746164617461206f7220617474726962757465732077657265206c6f636b65642e40436f6c6c656374696f6e4c6f636b6564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400090474536f6d652060636f6c6c656374696f6e6020776173206c6f636b65642e304f776e65724368616e676564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001246e65775f6f776e6572000130543a3a4163636f756e744964000a0448546865206f776e6572206368616e6765642e2c5465616d4368616e676564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001186973737565729c01504f7074696f6e3c543a3a4163636f756e7449643e00011461646d696e9c01504f7074696f6e3c543a3a4163636f756e7449643e00011c667265657a65729c01504f7074696f6e3c543a3a4163636f756e7449643e000b0470546865206d616e6167656d656e74207465616d206368616e6765642e405472616e73666572417070726f766564140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400012064656c6567617465000130543a3a4163636f756e744964000120646561646c696e65110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000c083101416e20606974656d60206f6620612060636f6c6c656374696f6e6020686173206265656e20617070726f7665642062792074686520606f776e65726020666f72207472616e736665722062793461206064656c6567617465602e44417070726f76616c43616e63656c6c6564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400012064656c6567617465000130543a3a4163636f756e744964000d081901416e20617070726f76616c20666f722061206064656c656761746560206163636f756e7420746f207472616e736665722074686520606974656d60206f6620616e206974656da860636f6c6c656374696f6e60207761732063616e63656c6c65642062792069747320606f776e6572602e54416c6c417070726f76616c7343616e63656c6c65640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e744964000e049c416c6c20617070726f76616c73206f6620616e206974656d20676f742063616e63656c6c65642e5c436f6c6c656374696f6e436f6e6669674368616e676564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000f040101412060636f6c6c656374696f6e6020686173206861642069747320636f6e666967206368616e676564206279207468652060466f72636560206f726967696e2e54436f6c6c656374696f6e4d65746164617461536574080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001004b44e6577206d6574616461746120686173206265656e2073657420666f7220612060636f6c6c656374696f6e602e64436f6c6c656374696f6e4d65746164617461436c6561726564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001104b44d6574616461746120686173206265656e20636c656172656420666f7220612060636f6c6c656374696f6e602e3c4974656d4d657461646174615365740c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001204984e6577206d6574616461746120686173206265656e2073657420666f7220616e206974656d2e4c4974656d4d65746164617461436c6561726564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001304984d6574616461746120686173206265656e20636c656172656420666f7220616e206974656d2e2c52656465706f7369746564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001407375636365737366756c5f6974656d73b90201385665633c543a3a4974656d49643e0014042101546865206465706f73697420666f72206120736574206f6620606974656d60732077697468696e20612060636f6c6c656374696f6e6020686173206265656e20757064617465642e30417474726962757465536574140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00150405014e657720617474726962757465206d6574616461746120686173206265656e2073657420666f7220612060636f6c6c656374696f6e60206f7220606974656d602e40417474726962757465436c6561726564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e0016040501417474726962757465206d6574616461746120686173206265656e20636c656172656420666f7220612060636f6c6c656374696f6e60206f7220606974656d602e6c4974656d41747472696275746573417070726f76616c41646465640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465000130543a3a4163636f756e744964001704cc41206e657720617070726f76616c20746f206d6f64696679206974656d2061747472696275746573207761732061646465642e744974656d41747472696275746573417070726f76616c52656d6f7665640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465000130543a3a4163636f756e744964001804d441206e657720617070726f76616c20746f206d6f64696679206974656d2061747472696275746573207761732072656d6f7665642e684f776e657273686970416363657074616e63654368616e67656408010c77686f000130543a3a4163636f756e7449640001406d617962655f636f6c6c656374696f6e1102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001904c04f776e65727368697020616363657074616e636520686173206368616e67656420666f7220616e206163636f756e742e58436f6c6c656374696f6e4d6178537570706c79536574080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d61785f737570706c7910010c753332001a04a44d617820737570706c7920686173206265656e2073657420666f72206120636f6c6c656374696f6e2e74436f6c6c656374696f6e4d696e7453657474696e677355706461746564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001b04ac4d696e742073657474696e677320666f72206120636f6c6c656374696f6e20686164206368616e6765642e6c4e657874436f6c6c656374696f6e4964496e6372656d656e74656404011c6e6578745f69641102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001c0401014576656e74206765747320656d6974746564207768656e2074686520604e657874436f6c6c656374696f6e496460206765747320696e6372656d656e7465642e304974656d5072696365536574100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000114707269636518013c4974656d50726963653c542c20493e00014477686974656c69737465645f62757965729c01504f7074696f6e3c543a3a4163636f756e7449643e001d047c546865207072696365207761732073657420666f7220746865206974656d2e404974656d507269636552656d6f766564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001e048c54686520707269636520666f7220746865206974656d207761732072656d6f7665642e284974656d426f75676874140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000114707269636518013c4974656d50726963653c542c20493e00011873656c6c6572000130543a3a4163636f756e7449640001146275796572000130543a3a4163636f756e744964001f044c416e206974656d2077617320626f756768742e1c54697053656e74140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011873656e646572000130543a3a4163636f756e7449640001207265636569766572000130543a3a4163636f756e744964000118616d6f756e741801584465706f73697442616c616e63654f663c542c20493e0020043c4120746970207761732073656e742e2c53776170437265617465641801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e00210488416e20606974656d60207377617020696e74656e742077617320637265617465642e345377617043616e63656c6c65641801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e0022045c5468652073776170207761732063616e63656c6c65642e2c53776170436c61696d656420013c73656e745f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400012473656e745f6974656d100124543a3a4974656d496400013c73656e745f6974656d5f6f776e6572000130543a3a4163636f756e74496400014c72656365697665645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400013472656365697665645f6974656d100124543a3a4974656d496400014c72656365697665645f6974656d5f6f776e6572000130543a3a4163636f756e7449640001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e00230468546865207377617020686173206265656e20636c61696d65642e585072655369676e6564417474726962757465735365740c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e002404fc4e657720617474726962757465732068617665206265656e2073657420666f7220616e20606974656d60206f66207468652060636f6c6c656374696f6e602e4850616c6c6574417474726962757465536574100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d110201444f7074696f6e3c543a3a4974656d49643e000124617474726962757465d102018450616c6c6574417474726962757465733c543a3a436f6c6c656374696f6e49643e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e0025084d0141206e65772061747472696275746520696e20746865206050616c6c657460206e616d657370616365207761732073657420666f72207468652060636f6c6c656374696f6e60206f7220616e20606974656d606477697468696e20746861742060636f6c6c656374696f6e602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b5020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000b9020000021000bd020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000c1020c2c70616c6c65745f6e667473147479706573484174747269627574654e616d65737061636504244163636f756e744964010001101850616c6c65740000003c436f6c6c656374696f6e4f776e6572000100244974656d4f776e65720002001c4163636f756e7404000001244163636f756e74496400030000c50204184f7074696f6e04045401c9020108104e6f6e6500000010536f6d650400c9020000010000c9020c2c70616c6c65745f6e66747314747970657348507269636557697468446972656374696f6e0418416d6f756e74011800080118616d6f756e74180118416d6f756e74000124646972656374696f6ecd0201385072696365446972656374696f6e0000cd020c2c70616c6c65745f6e667473147479706573385072696365446972656374696f6e0001081053656e640000001c5265636569766500010000d1020c2c70616c6c65745f6e6674731474797065734050616c6c6574417474726962757465730430436f6c6c656374696f6e4964011001082c55736564546f436c61696d0400100130436f6c6c656374696f6e4964000000405472616e7366657244697361626c656400010000d5020c4470616c6c65745f706172616d65746572731870616c6c6574144576656e740404540001041c557064617465640c010c6b6579d90201c43c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a4b65790464546865206b657920746861742077617320757064617465642e01246f6c645f76616c7565f10201ec4f7074696f6e3c3c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a56616c75653e047c546865206f6c642076616c7565206265666f726520746869732063616c6c2e01246e65775f76616c7565f10201ec4f7074696f6e3c3c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a56616c75653e0478546865206e65772076616c756520616674657220746869732063616c6c2e000c504120506172616d6574657220776173207365742e00bc497320616c736f20656d6974746564207768656e207468652076616c756520776173206e6f74206368616e6765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d902104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d735052756e74696d65506172616d65746572734b65790001043452756e74696d65436f6e6669670400dd0201bd013c64796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d6574657273206173206672616d655f737570706f72743a3a7472616974733a3a0a64796e616d69635f706172616d733a3a416767726567617465644b657956616c75653e3a3a4b657900000000dd02184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696734506172616d65746572734b657900011064536c617368416d6f756e745065724d617846696c6553697a650400e1020164536c617368416d6f756e745065724d617846696c6553697a65000000585374616b65546f4368616c6c656e6765506572696f640400e50201585374616b65546f4368616c6c656e6765506572696f6400010064436865636b706f696e744368616c6c656e6765506572696f640400e9020164436865636b706f696e744368616c6c656e6765506572696f64000200484d696e4368616c6c656e6765506572696f640400ed0201484d696e4368616c6c656e6765506572696f6400030000e102184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696764536c617368416d6f756e745065724d617846696c6553697a6500000000e502184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e666967585374616b65546f4368616c6c656e6765506572696f6400000000e902184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696764436865636b706f696e744368616c6c656e6765506572696f6400000000ed02184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e666967484d696e4368616c6c656e6765506572696f6400000000f10204184f7074696f6e04045401f5020108104e6f6e6500000010536f6d650400f5020000010000f502104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d735852756e74696d65506172616d657465727356616c75650001043452756e74696d65436f6e6669670400f90201c5013c64796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d6574657273206173206672616d655f737570706f72743a3a7472616974733a3a0a64796e616d69635f706172616d733a3a416767726567617465644b657956616c75653e3a3a56616c756500000000f902184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e6669673c506172616d657465727356616c756500011064536c617368416d6f756e745065724d617846696c6553697a65040018011c42616c616e6365000000585374616b65546f4368616c6c656e6765506572696f64040018011c42616c616e636500010064436865636b706f696e744368616c6c656e6765506572696f64040010012c426c6f636b4e756d626572000200484d696e4368616c6c656e6765506572696f64040010012c426c6f636b4e756d62657200030000fd0208306672616d655f73797374656d14506861736500010c384170706c7945787472696e736963040010010c7533320000003046696e616c697a6174696f6e00010038496e697469616c697a6174696f6e000200000103000002050300050300000408101000090308306672616d655f73797374656d584c61737452756e74696d6555706772616465496e666f0000080130737065635f76657273696f6ecc014c636f6465633a3a436f6d706163743c7533323e000124737065635f6e616d650d03016473705f72756e74696d653a3a52756e74696d65537472696e6700000d030000050200110308306672616d655f73797374656d60436f646555706772616465417574686f72697a6174696f6e0404540000080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c000015030c306672616d655f73797374656d1870616c6c65741043616c6c04045400012c1872656d61726b04011872656d61726b38011c5665633c75383e00000c684d616b6520736f6d65206f6e2d636861696e2072656d61726b2e008843616e20626520657865637574656420627920657665727920606f726967696e602e387365745f686561705f7061676573040114706167657330010c753634000104f853657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040110636f646538011c5665633c75383e0002046453657420746865206e65772072756e74696d6520636f64652e5c7365745f636f64655f776974686f75745f636865636b73040110636f646538011c5665633c75383e000310190153657420746865206e65772072756e74696d6520636f646520776974686f757420646f696e6720616e7920636865636b73206f662074686520676976656e2060636f6465602e0051014e6f746520746861742072756e74696d652075706772616465732077696c6c206e6f742072756e20696620746869732069732063616c6c656420776974682061206e6f742d696e6372656173696e6720737065632076657273696f6e212c7365745f73746f726167650401146974656d73190301345665633c4b657956616c75653e0004046853657420736f6d65206974656d73206f662073746f726167652e306b696c6c5f73746f726167650401106b657973690201205665633c4b65793e000504744b696c6c20736f6d65206974656d732066726f6d2073746f726167652e2c6b696c6c5f70726566697808011870726566697838010c4b657900011c7375626b65797310010c75333200061011014b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e0039012a2a4e4f54453a2a2a2057652072656c79206f6e2074686520526f6f74206f726967696e20746f2070726f7669646520757320746865206e756d626572206f66207375626b65797320756e6465723d0174686520707265666978207765206172652072656d6f76696e6720746f2061636375726174656c792063616c63756c6174652074686520776569676874206f6620746869732066756e6374696f6e2e4472656d61726b5f776974685f6576656e7404011872656d61726b38011c5665633c75383e000704a44d616b6520736f6d65206f6e2d636861696e2072656d61726b20616e6420656d6974206576656e742e44617574686f72697a655f75706772616465040124636f64655f6861736834011c543a3a486173680009106101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e80617574686f72697a655f757067726164655f776974686f75745f636865636b73040124636f64655f6861736834011c543a3a48617368000a206101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e005d015741524e494e473a205468697320617574686f72697a657320616e207570677261646520746861742077696c6c2074616b6520706c61636520776974686f757420616e792073616665747920636865636b732c20666f7259016578616d706c652074686174207468652073706563206e616d652072656d61696e73207468652073616d6520616e642074686174207468652076657273696f6e206e756d62657220696e637265617365732e204e6f74f07265636f6d6d656e64656420666f72206e6f726d616c207573652e205573652060617574686f72697a655f757067726164656020696e73746561642e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e606170706c795f617574686f72697a65645f75706772616465040110636f646538011c5665633c75383e000b24550150726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e00490149662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d65e872656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e005901446570656e64696e67206f6e207468652072756e74696d65277320604f6e536574436f64656020636f6e66696775726174696f6e2c20746869732066756e6374696f6e206d6179206469726563746c79206170706c791101746865206e65772060636f64656020696e207468652073616d6520626c6f636b206f7220617474656d707420746f207363686564756c652074686520757067726164652e0060416c6c206f726967696e732061726520616c6c6f7765642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e19030000021d03001d030000040838380021030c306672616d655f73797374656d186c696d69747330426c6f636b5765696768747300000c0128626173655f626c6f636b2801185765696768740001246d61785f626c6f636b2801185765696768740001247065725f636c617373250301845065724469737061746368436c6173733c57656967687473506572436c6173733e000025030c346672616d655f737570706f7274206469737061746368405065724469737061746368436c617373040454012903000c01186e6f726d616c290301045400012c6f7065726174696f6e616c29030104540001246d616e6461746f72792903010454000029030c306672616d655f73797374656d186c696d6974733c57656967687473506572436c6173730000100138626173655f65787472696e7369632801185765696768740001346d61785f65787472696e7369632d0301384f7074696f6e3c5765696768743e0001246d61785f746f74616c2d0301384f7074696f6e3c5765696768743e00012072657365727665642d0301384f7074696f6e3c5765696768743e00002d0304184f7074696f6e04045401280108104e6f6e6500000010536f6d65040028000001000031030c306672616d655f73797374656d186c696d6974732c426c6f636b4c656e677468000004010c6d6178350301545065724469737061746368436c6173733c7533323e000035030c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540110000c01186e6f726d616c1001045400012c6f7065726174696f6e616c100104540001246d616e6461746f72791001045400003903082873705f776569676874733c52756e74696d65446257656967687400000801107265616430010c753634000114777269746530010c75363400003d03082873705f76657273696f6e3852756e74696d6556657273696f6e0000200124737065635f6e616d650d03013452756e74696d65537472696e67000124696d706c5f6e616d650d03013452756e74696d65537472696e67000144617574686f72696e675f76657273696f6e10010c753332000130737065635f76657273696f6e10010c753332000130696d706c5f76657273696f6e10010c753332000110617069734103011c4170697356656300014c7472616e73616374696f6e5f76657273696f6e10010c75333200013473746174655f76657273696f6e080108753800004103040c436f7704045401450300040045030000004503000002490300490300000408290110004d03000005040051030c306672616d655f73797374656d1870616c6c6574144572726f720404540001243c496e76616c6964537065634e616d650000081101546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e685370656356657273696f6e4e65656473546f496e63726561736500010841015468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e744661696c6564546f4578747261637452756e74696d6556657273696f6e00020cec4661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e0009014569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e4c4e6f6e44656661756c74436f6d706f73697465000304fc537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e3c4e6f6e5a65726f526566436f756e74000404350154686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e3043616c6c46696c7465726564000504d0546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e6c4d756c7469426c6f636b4d6967726174696f6e734f6e676f696e67000604550141206d756c74692d626c6f636b206d6967726174696f6e206973206f6e676f696e6720616e642070726576656e7473207468652063757272656e7420636f64652066726f6d206265696e67207265706c616365642e444e6f7468696e67417574686f72697a6564000704584e6f207570677261646520617574686f72697a65642e30556e617574686f72697a656400080494546865207375626d697474656420636f6465206973206e6f7420617574686f72697a65642e046c4572726f7220666f72207468652053797374656d2070616c6c6574550300000259030059030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e7420416e636573746f720404480134000c0138757365645f62616e6477696474685d0301345573656442616e647769647468000138706172615f686561645f68617368710301244f7074696f6e3c483e000160636f6e73756d65645f676f5f61686561645f7369676e616c7503018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e00005d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74345573656442616e64776964746800000c0134756d705f6d73675f636f756e7410010c75333200013c756d705f746f74616c5f627974657310010c75333200013468726d705f6f7574676f696e676103018c42547265654d61703c5061726149642c2048726d704368616e6e656c5570646174653e00006103042042547265654d617008044b01e9010456016503000400690300000065030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e744448726d704368616e6e656c55706461746500000801246d73675f636f756e7410010c75333200012c746f74616c5f627974657310010c753332000069030000026d03006d0300000408e901650300710304184f7074696f6e04045401340108104e6f6e6500000010536f6d650400340000010000750304184f7074696f6e0404540179030108104e6f6e6500000010536f6d6504007903000001000079030c4c706f6c6b61646f745f7072696d6974697665730876373855706772616465476f41686561640001081441626f72740000001c476f4168656164000100007d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74385365676d656e74547261636b65720404480134000c0138757365645f62616e6477696474685d0301345573656442616e64776964746800013868726d705f77617465726d61726b110201804f7074696f6e3c72656c61795f636861696e3a3a426c6f636b4e756d6265723e000160636f6e73756d65645f676f5f61686561645f7369676e616c7503018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e000081030c4c706f6c6b61646f745f7072696d6974697665730876375c50657273697374656456616c69646174696f6e446174610804480134044e01100010012c706172656e745f6865616485030120486561644461746100014c72656c61795f706172656e745f6e756d6265721001044e00016472656c61795f706172656e745f73746f726167655f726f6f74340104480001306d61785f706f765f73697a6510010c753332000085030c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665732048656164446174610000040038011c5665633c75383e0000890304184f7074696f6e040454018d030108104e6f6e6500000010536f6d6504008d0300000100008d030c4c706f6c6b61646f745f7072696d69746976657308763748557067726164655265737472696374696f6e0001041c50726573656e740000000091030c1c73705f747269653473746f726167655f70726f6f663053746f7261676550726f6f660000040128747269655f6e6f6465739503014442547265655365743c5665633c75383e3e00009503042042547265655365740404540138000400690200000099030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f74584d6573736167696e675374617465536e617073686f740000100130646d715f6d71635f6865616434014472656c61795f636861696e3a3a4861736800019c72656c61795f64697370617463685f71756575655f72656d61696e696e675f63617061636974799d03018c52656c61794469737061746368517565756552656d61696e696e674361706163697479000140696e67726573735f6368616e6e656c73a10301885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00013c6567726573735f6368616e6e656c73a10301885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00009d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f748c52656c61794469737061746368517565756552656d61696e696e674361706163697479000008013c72656d61696e696e675f636f756e7410010c75333200013872656d61696e696e675f73697a6510010c7533320000a103000002a50300a50300000408e901a90300a9030c4c706f6c6b61646f745f7072696d6974697665730876374c416272696467656448726d704368616e6e656c00001801306d61785f636170616369747910010c7533320001386d61785f746f74616c5f73697a6510010c7533320001406d61785f6d6573736167655f73697a6510010c7533320001246d73675f636f756e7410010c753332000128746f74616c5f73697a6510010c7533320001206d71635f68656164710301304f7074696f6e3c486173683e0000ad030c4c706f6c6b61646f745f7072696d697469766573087637644162726964676564486f7374436f6e66696775726174696f6e00002801346d61785f636f64655f73697a6510010c7533320001486d61785f686561645f646174615f73697a6510010c7533320001586d61785f7570776172645f71756575655f636f756e7410010c7533320001546d61785f7570776172645f71756575655f73697a6510010c75333200015c6d61785f7570776172645f6d6573736167655f73697a6510010c7533320001906d61785f7570776172645f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200018868726d705f6d61785f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200016c76616c69646174696f6e5f757067726164655f636f6f6c646f776e10012c426c6f636b4e756d62657200016076616c69646174696f6e5f757067726164655f64656c617910012c426c6f636b4e756d6265720001506173796e635f6261636b696e675f706172616d73b10301484173796e634261636b696e67506172616d730000b103104c706f6c6b61646f745f7072696d697469766573087637346173796e635f6261636b696e67484173796e634261636b696e67506172616d73000008014c6d61785f63616e6469646174655f646570746810010c753332000150616c6c6f7765645f616e6365737472795f6c656e10010c7533320000b503089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e74444d6573736167655175657565436861696e0000040034012452656c6179486173680000b903042042547265654d617008044b01e901045601b503000400bd03000000bd03000002c10300c10300000408e901b50300c503000002c90300c9030860706f6c6b61646f745f636f72655f7072696d6974697665734c4f7574626f756e6448726d704d6573736167650408496401e90100080124726563697069656e74e901010849640001106461746138015073705f7374643a3a7665633a3a5665633c75383e0000cd030c3473705f61726974686d657469632c66697865645f706f696e742446697865645531323800000400180110753132380000d1030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c65741043616c6c0404540001104c7365745f76616c69646174696f6e5f6461746104011064617461d503015450617261636861696e496e686572656e744461746100002480536574207468652063757272656e742076616c69646174696f6e20646174612e004101546869732073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e887068617365206966207468652063616c6c20776173206e6f7420696e766f6b65642e00d0546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e7460002101417320612073696465206566666563742c20746869732066756e6374696f6e207570677261646573207468652063757272656e742076616c69646174696f6e2066756e6374696f6e8469662074686520617070726f7072696174652074696d652068617320636f6d652e607375646f5f73656e645f7570776172645f6d65737361676504011c6d6573736167653801345570776172644d65737361676500010044617574686f72697a655f75706772616465080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c0002206101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e005d015468652060636865636b5f76657273696f6e6020706172616d657465722073657473206120626f6f6c65616e20666c616720666f722077686574686572206f72206e6f74207468652072756e74696d6527732073706563610176657273696f6e20616e64206e616d652073686f756c64206265207665726966696564206f6e20757067726164652e2053696e63652074686520617574686f72697a6174696f6e206f6e6c7920686173206120686173682cb069742063616e6e6f742061637475616c6c7920706572666f726d2074686520766572696669636174696f6e2e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e60656e6163745f617574686f72697a65645f75706772616465040110636f646538011c5665633c75383e000324550150726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e00490149662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d65e872656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e005d014e6f7465207468617420746869732066756e6374696f6e2077696c6c206e6f74206170706c7920746865206e65772060636f6465602c20627574206f6e6c7920617474656d707420746f207363686564756c652074686574757067726164652077697468207468652052656c617920436861696e2e0060416c6c206f726967696e732061726520616c6c6f7765642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ed503089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e745450617261636861696e496e686572656e7444617461000010013c76616c69646174696f6e5f646174618103015c50657273697374656456616c69646174696f6e4461746100014472656c61795f636861696e5f73746174659103015473705f747269653a3a53746f7261676550726f6f66000144646f776e776172645f6d65737361676573d903016c5665633c496e626f756e64446f776e776172644d6573736167653e00014c686f72697a6f6e74616c5f6d65737361676573e10301a442547265654d61703c5061726149642c205665633c496e626f756e6448726d704d6573736167653e3e0000d903000002dd0300dd030860706f6c6b61646f745f636f72655f7072696d69746976657358496e626f756e64446f776e776172644d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d62657200010c6d736738013c446f776e776172644d6573736167650000e103042042547265654d617008044b01e901045601e503000400ed03000000e503000002e90300e9030860706f6c6b61646f745f636f72655f7072696d69746976657348496e626f756e6448726d704d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d6265720001106461746138015073705f7374643a3a7665633a3a5665633c75383e0000ed03000002f10300f10300000408e901e50300f5030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144572726f720404540001204c4f7665726c617070696e6755706772616465730000041901417474656d707420746f20757067726164652076616c69646174696f6e2066756e6374696f6e207768696c65206578697374696e6720757067726164652070656e64696e672e5050726f686962697465644279506f6c6b61646f740001044d01506f6c6b61646f742063757272656e746c792070726f68696269747320746869732070617261636861696e2066726f6d20757067726164696e67206974732076616c69646174696f6e2066756e6374696f6e2e18546f6f426967000208450154686520737570706c6965642076616c69646174696f6e2066756e6374696f6e2068617320636f6d70696c656420696e746f206120626c6f62206c6172676572207468616e20506f6c6b61646f742069733c77696c6c696e6720746f2072756e2e6856616c69646174696f6e446174614e6f74417661696c61626c650003041d0154686520696e686572656e7420776869636820737570706c696573207468652076616c69646174696f6e206461746120646964206e6f742072756e207468697320626c6f636b2e74486f7374436f6e66696775726174696f6e4e6f74417661696c61626c65000404290154686520696e686572656e7420776869636820737570706c6965732074686520686f737420636f6e66696775726174696f6e20646964206e6f742072756e207468697320626c6f636b2e304e6f745363686564756c6564000504d84e6f2076616c69646174696f6e2066756e6374696f6e20757067726164652069732063757272656e746c79207363686564756c65642e444e6f7468696e67417574686f72697a6564000604904e6f20636f6465207570677261646520686173206265656e20617574686f72697a65642e30556e617574686f72697a6564000704bc54686520676976656e20636f6465207570677261646520686173206e6f74206265656e20617574686f72697a65642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ef9030c4070616c6c65745f74696d657374616d701870616c6c65741043616c6c0404540001040c73657404010c6e6f772c0124543a3a4d6f6d656e7400004c54536574207468652063757272656e742074696d652e005501546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6ed470686173652c20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e0041015468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e7420737065636966696564206279685b60436f6e6669673a3a4d696e696d756d506572696f64605d2e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f4e6f6e655f2e0051015468697320646973706174636820636c617373206973205f4d616e6461746f72795f20746f20656e73757265206974206765747320657865637574656420696e2074686520626c6f636b2e204265206177617265510174686174206368616e67696e672074686520636f6d706c6578697479206f6620746869732063616c6c20636f756c6420726573756c742065786861757374696e6720746865207265736f757263657320696e206184626c6f636b20746f206578656375746520616e79206f746865722063616c6c732e0034232320436f6d706c657869747931012d20604f2831296020284e6f7465207468617420696d706c656d656e746174696f6e73206f6620604f6e54696d657374616d7053657460206d75737420616c736f20626520604f283129602955012d20312073746f72616765207265616420616e6420312073746f72616765206d75746174696f6e2028636f64656320604f283129602062656361757365206f6620604469645570646174653a3a74616b656020696e402020606f6e5f66696e616c697a656029d42d2031206576656e742068616e646c657220606f6e5f74696d657374616d705f736574602e204d75737420626520604f283129602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732efd030c5873746167696e675f70617261636861696e5f696e666f1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e01040c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e6465645665630804540105040453000004000d0401185665633c543e000005040c3c70616c6c65745f62616c616e6365731474797065732c42616c616e63654c6f636b041c42616c616e63650118000c01086964290101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500011c726561736f6e730904011c526561736f6e73000009040c3c70616c6c65745f62616c616e6365731474797065731c526561736f6e7300010c0c466565000000104d6973630001000c416c6c000200000d0400000205040011040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011504045300000400190401185665633c543e000015040c3c70616c6c65745f62616c616e6365731474797065732c52657365727665446174610844526573657276654964656e7469666965720129011c42616c616e6365011800080108696429010144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e6365000019040000021504001d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012104045300000400310401185665633c543e000021040c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640125041c42616c616e63650118000801086964250401084964000118616d6f756e7418011c42616c616e636500002504084c73746f726167655f6875625f72756e74696d654452756e74696d65486f6c64526561736f6e0001082450726f76696465727304002904019070616c6c65745f73746f726167655f70726f7669646572733a3a486f6c64526561736f6e002800385061796d656e7453747265616d7304002d04018870616c6c65745f7061796d656e745f73747265616d733a3a486f6c64526561736f6e002c000029040c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c657428486f6c64526561736f6e0001085853746f7261676550726f76696465724465706f736974000000344275636b65744465706f736974000100002d040c5870616c6c65745f7061796d656e745f73747265616d731870616c6c657428486f6c64526561736f6e000104505061796d656e7453747265616d4465706f73697400000000310400000221040035040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540139040453000004003d0401185665633c543e000039040c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e740808496401981c42616c616e636501180008010869649801084964000118616d6f756e7418011c42616c616e636500003d0400000239040041040c3c70616c6c65745f62616c616e6365731870616c6c65741043616c6c080454000449000120507472616e736665725f616c6c6f775f646561746808011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e636500001cd45472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e003501607472616e736665725f616c6c6f775f6465617468602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e11014966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c74b06f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e001501546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e38666f7263655f7472616e736665720c0118736f75726365450401504163636f756e7449644c6f6f6b75704f663c543e00011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e6365000208610145786163746c7920617320607472616e736665725f616c6c6f775f6465617468602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e74446d6179206265207370656369666965642e4c7472616e736665725f6b6565705f616c69766508011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e6365000318590153616d6520617320746865205b607472616e736665725f616c6c6f775f6465617468605d2063616c6c2c206275742077697468206120636865636b207468617420746865207472616e736665722077696c6c206e6f74606b696c6c20746865206f726967696e206163636f756e742e00e8393925206f66207468652074696d6520796f752077616e74205b607472616e736665725f616c6c6f775f6465617468605d20696e73746561642e00f05b607472616e736665725f616c6c6f775f6465617468605d3a207374727563742e50616c6c65742e68746d6c236d6574686f642e7472616e73666572307472616e736665725f616c6c08011064657374450401504163636f756e7449644c6f6f6b75704f663c543e0001286b6565705f616c697665200110626f6f6c00043c05015472616e736665722074686520656e74697265207472616e7366657261626c652062616c616e63652066726f6d207468652063616c6c6572206163636f756e742e0059014e4f54453a20546869732066756e6374696f6e206f6e6c7920617474656d70747320746f207472616e73666572205f7472616e7366657261626c655f2062616c616e6365732e2054686973206d65616e7320746861746101616e79206c6f636b65642c2072657365727665642c206f72206578697374656e7469616c206465706f7369747320287768656e20606b6565705f616c6976656020697320607472756560292c2077696c6c206e6f742062655d017472616e7366657272656420627920746869732066756e6374696f6e2e20546f20656e73757265207468617420746869732066756e6374696f6e20726573756c747320696e2061206b696c6c6564206163636f756e742c4501796f75206d69676874206e65656420746f207072657061726520746865206163636f756e742062792072656d6f76696e6720616e79207265666572656e636520636f756e746572732c2073746f72616765406465706f736974732c206574632e2e2e00c0546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265205369676e65642e00a02d206064657374603a2054686520726563697069656e74206f6620746865207472616e736665722e59012d20606b6565705f616c697665603a204120626f6f6c65616e20746f2064657465726d696e652069662074686520607472616e736665725f616c6c60206f7065726174696f6e2073686f756c642073656e6420616c6c4d0120206f66207468652066756e647320746865206163636f756e74206861732c2063617573696e67207468652073656e646572206163636f756e7420746f206265206b696c6c6564202866616c7365292c206f72590120207472616e736665722065766572797468696e6720657863657074206174206c6561737420746865206578697374656e7469616c206465706f7369742c2077686963682077696c6c2067756172616e74656520746f9c20206b656570207468652073656e646572206163636f756e7420616c697665202874727565292e3c666f7263655f756e7265736572766508010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e000118616d6f756e74180128543a3a42616c616e636500050cb0556e7265736572766520736f6d652062616c616e63652066726f6d2061207573657220627920666f7263652e006c43616e206f6e6c792062652063616c6c656420627920524f4f542e40757067726164655f6163636f756e747304010c77686fa401445665633c543a3a4163636f756e7449643e0006207055706772616465206120737065636966696564206163636f756e742e00742d20606f726967696e603a204d75737420626520605369676e6564602e902d206077686f603a20546865206163636f756e7420746f2062652075706772616465642e005501546869732077696c6c20776169766520746865207472616e73616374696f6e20666565206966206174206c6561737420616c6c2062757420313025206f6620746865206163636f756e7473206e656564656420746f410162652075706772616465642e20285765206c657420736f6d65206e6f74206861766520746f206265207570677261646564206a75737420696e206f7264657220746f20616c6c6f7720666f722074686558706f73736962696c697479206f6620636875726e292e44666f7263655f7365745f62616c616e636508010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565dc0128543a3a42616c616e636500080cac5365742074686520726567756c61722062616c616e6365206f66206120676976656e206163636f756e742e00b0546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e6c666f7263655f61646a7573745f746f74616c5f69737375616e6365080124646972656374696f6e4d04014c41646a7573746d656e74446972656374696f6e00011464656c7461dc0128543a3a42616c616e6365000914b841646a7573742074686520746f74616c2069737375616e636520696e20612073617475726174696e67207761792e00fc43616e206f6e6c792062652063616c6c656420627920726f6f7420616e6420616c77617973206e65656473206120706f736974697665206064656c7461602e002423204578616d706c65040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e45040c2873705f72756e74696d65306d756c746961646472657373304d756c74694164647265737308244163636f756e7449640100304163636f756e74496e6465780198011408496404000001244163636f756e74496400000014496e6465780400490401304163636f756e74496e6465780001000c526177040038011c5665633c75383e0002002441646472657373333204000401205b75383b2033325d000300244164647265737332300400d801205b75383b2032305d00040000490400000698004d040c3c70616c6c65745f62616c616e6365731474797065734c41646a7573746d656e74446972656374696f6e00010820496e6372656173650000002044656372656173650001000051040c3c70616c6c65745f62616c616e6365731870616c6c6574144572726f720804540004490001303856657374696e6742616c616e63650000049c56657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e544c69717569646974795265737472696374696f6e73000104c84163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e4c496e73756666696369656e7442616c616e63650002047842616c616e636520746f6f206c6f7720746f2073656e642076616c75652e484578697374656e7469616c4465706f736974000304ec56616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e34457870656e646162696c697479000404905472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e5c4578697374696e6756657374696e675363686564756c65000504cc412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e2c446561644163636f756e740006048c42656e6566696369617279206163636f756e74206d757374207072652d65786973742e3c546f6f4d616e795265736572766573000704b84e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e30546f6f4d616e79486f6c6473000804f84e756d626572206f6620686f6c647320657863656564206056617269616e74436f756e744f663c543a3a52756e74696d65486f6c64526561736f6e3e602e38546f6f4d616e79467265657a6573000904984e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e4c49737375616e63654465616374697661746564000a0401015468652069737375616e63652063616e6e6f74206265206d6f6469666965642073696e636520697420697320616c72656164792064656163746976617465642e2444656c74615a65726f000b04645468652064656c74612063616e6e6f74206265207a65726f2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e5504086870616c6c65745f7472616e73616374696f6e5f7061796d656e742052656c6561736573000108245631416e6369656e740000000856320001000059040c2c70616c6c65745f7375646f1870616c6c65741043616c6c040454000114107375646f04011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e000004350141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e547375646f5f756e636865636b65645f77656967687408011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e000118776569676874280118576569676874000114350141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e2d01546869732066756e6374696f6e20646f6573206e6f7420636865636b2074686520776569676874206f66207468652063616c6c2c20616e6420696e737465616420616c6c6f777320746865b05375646f207573657220746f20737065636966792074686520776569676874206f66207468652063616c6c2e00cc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e1c7365745f6b657904010c6e6577450401504163636f756e7449644c6f6f6b75704f663c543e0002085d0141757468656e74696361746573207468652063757272656e74207375646f206b657920616e6420736574732074686520676976656e204163636f756e7449642028606e6577602920617320746865206e6577207375646f106b65792e1c7375646f5f617308010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e00011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0003104d0141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c207769746820605369676e656460206f726967696e2066726f6d406120676976656e206163636f756e742e00cc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e2872656d6f76655f6b657900040c845065726d616e656e746c792072656d6f76657320746865207375646f206b65792e006c2a2a546869732063616e6e6f7420626520756e2d646f6e652e2a2a040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e5d04084c73746f726167655f6875625f72756e74696d652c52756e74696d6543616c6c0001501853797374656d0400150301ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e0000003c50617261636861696e53797374656d0400d10301d10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e53797374656d2c2052756e74696d653e0001002454696d657374616d700400f90301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54696d657374616d702c2052756e74696d653e0002003450617261636861696e496e666f0400fd0301c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e496e666f2c2052756e74696d653e0003002042616c616e6365730400410401b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e000a00105375646f0400590401a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5375646f2c2052756e74696d653e000f0044436f6c6c61746f7253656c656374696f6e0400610401d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6c6c61746f7253656c656374696f6e2c2052756e74696d653e0015001c53657373696f6e0400650401b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53657373696f6e2c2052756e74696d653e0016002458636d7051756575650400710401b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58636d7051756575652c2052756e74696d653e001e002c506f6c6b61646f7458636d0400750401c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506f6c6b61646f7458636d2c2052756e74696d653e001f002843756d756c757358636d0400110501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c43756d756c757358636d2c2052756e74696d653e002000304d65737361676551756575650400150501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4d65737361676551756575652c2052756e74696d653e0021002450726f7669646572730400190501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50726f7669646572732c2052756e74696d653e0028002846696c6553797374656d04001d0501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46696c6553797374656d2c2052756e74696d653e0029003050726f6f66734465616c65720400510501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50726f6f66734465616c65722c2052756e74696d653e002a002852616e646f6d6e6573730400550501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c52616e646f6d6e6573732c2052756e74696d653e002b00385061796d656e7453747265616d730400590501cd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5061796d656e7453747265616d732c2052756e74696d653e002c00284275636b65744e66747304005d0501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4275636b65744e6674732c2052756e74696d653e002d00104e6674730400650501a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4e6674732c2052756e74696d653e00320028506172616d65746572730400c10501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506172616d65746572732c2052756e74696d653e0033000061040c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65741043616c6c040454000124447365745f696e76756c6e657261626c657304010c6e6577a401445665633c543a3a4163636f756e7449643e000034310153657420746865206c697374206f6620696e76756c6e657261626c65202866697865642920636f6c6c61746f72732e20546865736520636f6c6c61746f7273206d75737420646f20736f6d65d07072657061726174696f6e2c206e616d656c7920746f206861766520726567697374657265642073657373696f6e206b6579732e0059015468652063616c6c2077696c6c2072656d6f766520616e79206163636f756e747320746861742068617665206e6f742072656769737465726564206b6579732066726f6d20746865207365742e20546861742069732c5d016974206973206e6f6e2d61746f6d69633b207468652063616c6c6572206163636570747320616c6c20604163636f756e74496460732070617373656420696e20606e657760205f696e646976696475616c6c795f206173310161636365707461626c6520496e76756c6e657261626c65732c20616e64206973206e6f742070726f706f73696e672061205f7365745f206f66206e657720496e76756c6e657261626c65732e005901546869732063616c6c20646f6573206e6f74206d61696e7461696e206d757475616c206578636c75736976697479206f662060496e76756c6e657261626c65736020616e64206043616e64696461746573602e20497461016973207265636f6d6d656e64656420746f207573652061206261746368206f6620606164645f696e76756c6e657261626c656020616e64206072656d6f76655f696e76756c6e657261626c656020696e73746561642e204151016062617463685f616c6c602063616e20616c736f206265207573656420746f20656e666f7263652061746f6d69636974792e20496620616e792063616e646964617465732061726520696e636c7564656420696e4d01606e6577602c20746865792073686f756c642062652072656d6f7665642077697468206072656d6f76655f696e76756c6e657261626c655f63616e6469646174656020616674657220657865637574696f6e2e00944d7573742062652063616c6c65642062792074686520605570646174654f726967696e602e587365745f646573697265645f63616e6469646174657304010c6d617810010c75333200011455015365742074686520696465616c206e756d626572206f66206e6f6e2d696e76756c6e657261626c6520636f6c6c61746f72732e204966206c6f776572696e672074686973206e756d6265722c207468656e2074686561016e756d626572206f662072756e6e696e6720636f6c6c61746f727320636f756c6420626520686967686572207468616e2074686973206669677572652e2041736964652066726f6d2074686174206564676520636173652c350174686572652073686f756c64206265206e6f206f746865722077617920746f2068617665206d6f72652063616e64696461746573207468616e207468652064657369726564206e756d6265722e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e487365745f63616e6469646163795f626f6e64040110626f6e6418013042616c616e63654f663c543e00021c78536574207468652063616e64696461637920626f6e6420616d6f756e742e0051014966207468652063616e64696461637920626f6e6420697320696e6372656173656420627920746869732063616c6c2c20616c6c2063757272656e742063616e64696461746573207768696368206861766520614d016465706f736974206c6f776572207468616e20746865206e657720626f6e642077696c6c206265206b69636b65642066726f6d20746865206c69737420616e6420676574207468656972206465706f73697473146261636b2e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e5472656769737465725f61735f63616e646964617465000310410152656769737465722074686973206163636f756e74206173206120636f6c6c61746f722063616e6469646174652e20546865206163636f756e74206d7573742028612920616c726561647920686176651d01726567697374657265642073657373696f6e206b65797320616e64202862292062652061626c6520746f207265736572766520746865206043616e646964616379426f6e64602e00dc546869732063616c6c206973206e6f7420617661696c61626c6520746f2060496e76756c6e657261626c656020636f6c6c61746f72732e306c656176655f696e74656e7400041455014465726567697374657220606f726967696e60206173206120636f6c6c61746f722063616e6469646174652e204e6f746520746861742074686520636f6c6c61746f722063616e206f6e6c79206c65617665206f6e0d0173657373696f6e206368616e67652e20546865206043616e646964616379426f6e64602077696c6c20626520756e726573657276656420696d6d6564696174656c792e001901546869732063616c6c2077696c6c206661696c2069662074686520746f74616c206e756d626572206f662063616e6469646174657320776f756c642064726f702062656c6f775c604d696e456c696769626c65436f6c6c61746f7273602e406164645f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e74496400051045014164642061206e6577206163636f756e74206077686f6020746f20746865206c697374206f662060496e76756c6e657261626c65736020636f6c6c61746f72732e206077686f60206d75737420686176651d01726567697374657265642073657373696f6e206b6579732e204966206077686f6020697320612063616e6469646174652c20746865792077696c6c2062652072656d6f7665642e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e4c72656d6f76655f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e744964000610610152656d6f766520616e206163636f756e74206077686f602066726f6d20746865206c697374206f662060496e76756c6e657261626c65736020636f6c6c61746f72732e2060496e76756c6e657261626c657360206d75737428626520736f727465642e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e2c7570646174655f626f6e6404012c6e65775f6465706f73697418013042616c616e63654f663c543e00071c5d01557064617465207468652063616e64696461637920626f6e64206f6620636f6c6c61746f722063616e64696461746520606f726967696e6020746f2061206e657720616d6f756e7420606e65775f6465706f736974602e00410153657474696e67206120606e65775f6465706f736974602074686174206973206c6f776572207468616e207468652063757272656e74206465706f736974207768696c6520606f726967696e60206973e06f6363757079696e67206120746f702d604465736972656443616e646964617465736020736c6f74206973206e6f7420616c6c6f7765642e005901546869732063616c6c2077696c6c206661696c20696620606f726967696e60206973206e6f74206120636f6c6c61746f722063616e6469646174652c20746865207570646174656420626f6e64206973206c6f77657219017468616e20746865206d696e696d756d2063616e64696461637920626f6e642c20616e642f6f722074686520616d6f756e742063616e6e6f742062652072657365727665642e4c74616b655f63616e6469646174655f736c6f7408011c6465706f73697418013042616c616e63654f663c543e000118746172676574000130543a3a4163636f756e74496400081c4d015468652063616c6c657220606f726967696e60207265706c6163657320612063616e64696461746520607461726765746020696e2074686520636f6c6c61746f722063616e646964617465206c6973742062795501726573657276696e6720606465706f736974602e2054686520616d6f756e7420606465706f73697460207265736572766564206279207468652063616c6c6572206d7573742062652067726561746572207468616ee0746865206578697374696e6720626f6e64206f66207468652074617267657420697420697320747279696e6720746f207265706c6163652e005901546869732063616c6c2077696c6c206661696c206966207468652063616c6c657220697320616c7265616479206120636f6c6c61746f722063616e646964617465206f7220696e76756c6e657261626c652c20746865550163616c6c657220646f6573206e6f74206861766520726567697374657265642073657373696f6e206b6579732c2074686520746172676574206973206e6f74206120636f6c6c61746f722063616e6469646174652cbc616e642f6f722074686520606465706f7369746020616d6f756e742063616e6e6f742062652072657365727665642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65040c3870616c6c65745f73657373696f6e1870616c6c65741043616c6c040454000108207365745f6b6579730801106b6579736904011c543a3a4b65797300011470726f6f6638011c5665633c75383e000024e453657473207468652073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c657220746f20606b657973602e1d01416c6c6f777320616e206163636f756e7420746f20736574206974732073657373696f6e206b6579207072696f7220746f206265636f6d696e6720612076616c696461746f722ec05468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e00d0546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265207369676e65642e0034232320436f6d706c657869747959012d20604f283129602e2041637475616c20636f737420646570656e6473206f6e20746865206e756d626572206f66206c656e677468206f662060543a3a4b6579733a3a6b65795f69647328296020776869636820697320202066697865642e2870757267655f6b657973000130c852656d6f76657320616e792073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c65722e00c05468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e005501546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265205369676e656420616e6420746865206163636f756e74206d757374206265206569746865722062655d01636f6e7665727469626c6520746f20612076616c696461746f72204944207573696e672074686520636861696e2773207479706963616c2061646472657373696e672073797374656d20287468697320757375616c6c7951016d65616e73206265696e67206120636f6e74726f6c6c6572206163636f756e7429206f72206469726563746c7920636f6e7665727469626c6520696e746f20612076616c696461746f722049442028776869636894757375616c6c79206d65616e73206265696e672061207374617368206163636f756e74292e0034232320436f6d706c65786974793d012d20604f2831296020696e206e756d626572206f66206b65792074797065732e2041637475616c20636f737420646570656e6473206f6e20746865206e756d626572206f66206c656e677468206f6698202060543a3a4b6579733a3a6b65795f6964732829602077686963682069732066697865642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e6904084c73746f726167655f6875625f72756e74696d652c53657373696f6e4b6579730000040110617572616d0401c43c41757261206173202463726174653a3a426f756e64546f52756e74696d654170705075626c69633e3a3a5075626c696300006d04104473705f636f6e73656e7375735f617572611c737232353531392c6170705f73723235353139185075626c69630000040004013c737232353531393a3a5075626c6963000071040c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c65741043616c6c0404540001145473757370656e645f78636d5f657865637574696f6e00010c490153757370656e647320616c6c2058434d20657865637574696f6e7320666f72207468652058434d502071756575652c207265676172646c657373206f66207468652073656e6465722773206f726967696e2e00a42d20606f726967696e603a204d75737420706173732060436f6e74726f6c6c65724f726967696e602e50726573756d655f78636d5f657865637574696f6e000214b8526573756d657320616c6c2058434d20657865637574696f6e7320666f72207468652058434d502071756575652e003d014e6f7465207468617420746869732066756e6374696f6e20646f65736e2774206368616e67652074686520737461747573206f662074686520696e2f6f757420626f756e64206368616e6e656c732e00a42d20606f726967696e603a204d75737420706173732060436f6e74726f6c6c65724f726967696e602e607570646174655f73757370656e645f7468726573686f6c6404010c6e657710010c75333200031449014f76657277726974657320746865206e756d626572206f66207061676573207768696368206d75737420626520696e2074686520717565756520666f7220746865206f74686572207369646520746f20626578746f6c6420746f2073757370656e642074686569722073656e64696e672e00742d20606f726967696e603a204d75737420706173732060526f6f74602ee82d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e73757370656e645f76616c756560547570646174655f64726f705f7468726573686f6c6404010c6e657710010c75333200041445014f76657277726974657320746865206e756d626572206f66207061676573207768696368206d75737420626520696e207468652071756575652061667465722077686963682077652064726f7020616e798866757274686572206d657373616765732066726f6d20746865206368616e6e656c2e00742d20606f726967696e603a204d75737420706173732060526f6f74602eec2d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e64726f705f7468726573686f6c64605c7570646174655f726573756d655f7468726573686f6c6404010c6e657710010c7533320005144d014f76657277726974657320746865206e756d626572206f6620706167657320776869636820746865207175657565206d757374206265207265647563656420746f206265666f7265206974207369676e616c73010174686174206d6573736167652073656e64696e67206d6179207265636f6d6d656e636520616674657220697420686173206265656e2073757370656e6465642e00742d20606f726967696e603a204d75737420706173732060526f6f74602ef42d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e726573756d655f7468726573686f6c6460040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e75040c2870616c6c65745f78636d1870616c6c65741043616c6c0404540001381073656e6408011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011c6d65737361676579040154426f783c56657273696f6e656458636d3c28293e3e0000003c74656c65706f72745f61737365747310011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c753332000148110154656c65706f727420736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f20736f6d652064657374696e6174696f6e20636861696e2e001d012a2a546869732066756e6374696f6e20697320646570726563617465643a2055736520606c696d697465645f74656c65706f72745f6173736574736020696e73746561642e2a2a005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602e2054686520776569676874206c696d697420666f722066656573206973206e6f742070726f766964656420616e64207468757320697320756e6c696d697465642cb47769746820616c6c20666565732074616b656e206173206e65656465642066726f6d207468652061737365742e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865682020666565206f6e207468652060646573746020636861696e2e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e5c726573657276655f7472616e736665725f61737365747310011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320002785d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2c7864657374696e6174696f6e206f722072656d6f746520726573657276652e003d016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e20616e64206d6179206e6f742062652074656c65706f727461626c6520746f206064657374602e4d01202d2060617373657473602068617665206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5501202d20606173736574736020686176652064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e6101202d20606173736574736020686176652072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f7665610120202072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f20606465737460f4202020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e003d012a2a546869732066756e6374696f6e20697320646570726563617465643a2055736520606c696d697465645f726573657276655f7472616e736665725f6173736574736020696e73746561642e2a2a005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602e2054686520776569676874206c696d697420666f722066656573206973206e6f742070726f766964656420616e64207468757320697320756e6c696d697465642cb47769746820616c6c20666565732074616b656e206173206e65656465642066726f6d207468652061737365742e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e1c6578656375746508011c6d657373616765dd0401b4426f783c56657273696f6e656458636d3c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e3e0001286d61785f776569676874280118576569676874000320d04578656375746520616e2058434d206d6573736167652066726f6d2061206c6f63616c2c207369676e65642c206f726967696e2e004d01416e206576656e74206973206465706f736974656420696e6469636174696e67207768657468657220606d73676020636f756c6420626520657865637574656420636f6d706c6574656c79206f72206f6e6c79287061727469616c6c792e005d014e6f206d6f7265207468616e20606d61785f776569676874602077696c6c206265207573656420696e2069747320617474656d7074656420657865637574696f6e2e2049662074686973206973206c657373207468616e4101746865206d6178696d756d20616d6f756e74206f6620776569676874207468617420746865206d65737361676520636f756c642074616b6520746f2062652065786563757465642c207468656e206e6f7c657865637574696f6e20617474656d70742077696c6c206265206d6164652e44666f7263655f78636d5f76657273696f6e0801206c6f636174696f6ebc0134426f783c4c6f636174696f6e3e00011c76657273696f6e10012858636d56657273696f6e00041849014578746f6c6c2074686174206120706172746963756c61722064657374696e6174696f6e2063616e20626520636f6d6d756e6963617465642077697468207468726f756768206120706172746963756c61723c76657273696f6e206f662058434d2e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2ed82d20606c6f636174696f6e603a205468652064657374696e6174696f6e2074686174206973206265696e67206465736372696265642e11012d206078636d5f76657273696f6e603a20546865206c61746573742076657273696f6e206f662058434d207468617420606c6f636174696f6e6020737570706f7274732e64666f7263655f64656661756c745f78636d5f76657273696f6e0401446d617962655f78636d5f76657273696f6e110201484f7074696f6e3c58636d56657273696f6e3e0005145901536574206120736166652058434d2076657273696f6e20287468652076657273696f6e20746861742058434d2073686f756c6420626520656e636f646564207769746820696620746865206d6f737420726563656e74b476657273696f6e20612064657374696e6174696f6e2063616e2061636365707420697320756e6b6e6f776e292e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e39012d20606d617962655f78636d5f76657273696f6e603a205468652064656661756c742058434d20656e636f64696e672076657273696f6e2c206f7220604e6f6e656020746f2064697361626c652e78666f7263655f7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6ed9010158426f783c56657273696f6e65644c6f636174696f6e3e000610390141736b2061206c6f636174696f6e20746f206e6f7469667920757320726567617264696e672074686569722058434d2076657273696f6e20616e6420616e79206368616e67657320746f2069742e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e59012d20606c6f636174696f6e603a20546865206c6f636174696f6e20746f2077686963682077652073686f756c642073756273637269626520666f722058434d2076657273696f6e206e6f74696669636174696f6e732e80666f7263655f756e7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6ed9010158426f783c56657273696f6e65644c6f636174696f6e3e0007184901526571756972652074686174206120706172746963756c61722064657374696e6174696f6e2073686f756c64206e6f206c6f6e676572206e6f7469667920757320726567617264696e6720616e792058434d4076657273696f6e206368616e6765732e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e3d012d20606c6f636174696f6e603a20546865206c6f636174696f6e20746f207768696368207765206172652063757272656e746c79207375627363726962656420666f722058434d2076657273696f6ea820206e6f74696669636174696f6e73207768696368207765206e6f206c6f6e676572206465736972652e7c6c696d697465645f726573657276655f7472616e736665725f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d69740008785d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2c7864657374696e6174696f6e206f722072656d6f746520726573657276652e003d016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e20616e64206d6179206e6f742062652074656c65706f727461626c6520746f206064657374602e4d01202d2060617373657473602068617665206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5501202d20606173736574736020686176652064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e6101202d20606173736574736020686176652072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f7665610120202072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f20606465737460f4202020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602c20757020746f20656e6f75676820746f2070617920666f7220607765696768745f6c696d697460206f66207765696768742e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e5c6c696d697465645f74656c65706f72745f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d6974000948110154656c65706f727420736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f20736f6d652064657374696e6174696f6e20636861696e2e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602c20757020746f20656e6f75676820746f2070617920666f7220607765696768745f6c696d697460206f66207765696768742e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865682020666565206f6e207468652060646573746020636861696e2e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e40666f7263655f73757370656e73696f6e04012473757370656e646564200110626f6f6c000a10f4536574206f7220756e7365742074686520676c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2ed02d206073757370656e646564603a2060747275656020746f2073757370656e642c206066616c73656020746f20726573756d652e3c7472616e736665725f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d6974000b845d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2cd064657374696e6174696f6e206f722072656d6f746520726573657276652c206f72207468726f7567682074656c65706f7274732e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f663501696e64657820606665655f61737365745f6974656d60202868656e636520726566657272656420746f20617320606665657360292c20757020746f20656e6f75676820746f2070617920666f724101607765696768745f6c696d697460206f66207765696768742e204966206d6f726520776569676874206973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865dc6f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e006101606173736574736020286578636c7564696e672060666565736029206d75737420686176652073616d652072657365727665206c6f636174696f6e206f72206f74686572776973652062652074656c65706f727461626c65b0746f206064657374602c206e6f206c696d69746174696f6e7320696d706f736564206f6e206066656573602e4d01202d20666f72206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e20636861696e20616e644d01202020666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f442020206062656e6566696369617279602e6101202d20666f722064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f2060646573746020636861696e5901202020746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64206465706f736974207468656d50202020746f206062656e6566696369617279602e5d01202d20666f722072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f76652072657365727665735d0120202066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f2060646573746020746f206d696e74d4202020616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e5101202d20666f722074656c65706f7274733a206275726e206c6f63616c2061737365747320616e6420666f72776172642058434d20746f2060646573746020636861696e20746f206d696e742f74656c65706f7274b020202061737365747320616e64206465706f736974207468656d20746f206062656e6566696369617279602e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e2d012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c792062652060583228506172656e742c5501202050617261636861696e282e2e29296020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f72206058312850617261636861696e282e2e29296020746f2073656e6468202066726f6d2072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e30636c61696d5f61737365747308011861737365747375010150426f783c56657273696f6e65644173736574733e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e000c185501436c61696d73206173736574732074726170706564206f6e20746869732070616c6c65742062656361757365206f66206c6566746f7665722061737365747320647572696e672058434d20657865637574696f6e2e00ac2d20606f726967696e603a20416e796f6e652063616e2063616c6c20746869732065787472696e7369632e5d012d2060617373657473603a20546865206578616374206173736574732074686174207765726520747261707065642e20557365207468652076657273696f6e20746f207370656369667920776861742076657273696f6e9877617320746865206c6174657374207768656e2074686579207765726520747261707065642e45012d206062656e6566696369617279603a20546865206c6f636174696f6e2f6163636f756e742077686572652074686520636c61696d6564206173736574732077696c6c206265206465706f73697465642e8c7472616e736665725f6173736574735f7573696e675f747970655f616e645f7468656e1c011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001506173736574735f7472616e736665725f7479706509050144426f783c5472616e73666572547970653e00013872656d6f74655f666565735f69640d050154426f783c56657273696f6e6564417373657449643e000148666565735f7472616e736665725f7479706509050144426f783c5472616e73666572547970653e000148637573746f6d5f78636d5f6f6e5f6465737479040154426f783c56657273696f6e656458636d3c28293e3e0001307765696768745f6c696d69747101012c5765696768744c696d6974000dc055015472616e73666572206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207573696e67206578706c69636974207472616e7366657268747970657320666f722061737365747320616e6420666565732e0059016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e206f72206d61792062652074656c65706f727461626c6520746f206064657374602e2043616c6c6572206d757374ec70726f766964652074686520606173736574735f7472616e736665725f747970656020746f206265207573656420666f722060617373657473603a5101202d20605472616e73666572547970653a3a4c6f63616c52657365727665603a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5901202d20605472616e73666572547970653a3a44657374696e6174696f6e52657365727665603a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e5901202d20605472616e73666572547970653a3a52656d6f746552657365727665287265736572766529603a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f206072657365727665606101202020636861696e20746f206d6f76652072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f74686572550120202058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e205479706963616c6c79942020207468652072656d6f746520607265736572766560206973204173736574204875622e4501202d20605472616e73666572547970653a3a54656c65706f7274603a206275726e206c6f63616c2061737365747320616e6420666f72776172642058434d20746f2060646573746020636861696e20746fe82020206d696e742f74656c65706f72742061737365747320616e64206465706f736974207468656d20746f206062656e6566696369617279602e0055014f6e207468652064657374696e6174696f6e20636861696e2c2061732077656c6c20617320616e7920696e7465726d65646961727920686f70732c2060427579457865637574696f6e60206973207573656420746f210162757920657865637574696f6e207573696e67207472616e73666572726564206061737365747360206964656e746966696564206279206072656d6f74655f666565735f6964602e59014d616b65207375726520656e6f756768206f662074686520737065636966696564206072656d6f74655f666565735f69646020617373657420697320696e636c7564656420696e2074686520676976656e206c69737461016f662060617373657473602e206072656d6f74655f666565735f6964602073686f756c6420626520656e6f75676820746f2070617920666f7220607765696768745f6c696d6974602e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0045016072656d6f74655f666565735f696460206d61792075736520646966666572656e74207472616e736665722074797065207468616e2072657374206f6620606173736574736020616e642063616e2062659c737065636966696564207468726f7567682060666565735f7472616e736665725f74797065602e0061015468652063616c6c6572206e6565647320746f207370656369667920776861742073686f756c642068617070656e20746f20746865207472616e7366657272656420617373657473206f6e6365207468657920726561636841017468652060646573746020636861696e2e205468697320697320646f6e65207468726f756768207468652060637573746f6d5f78636d5f6f6e5f646573746020706172616d657465722c207768696368fc636f6e7461696e732074686520696e737472756374696f6e7320746f2065786563757465206f6e2060646573746020617320612066696e616c20737465702e7c20205468697320697320757375616c6c792061732073696d706c652061733a510120206058636d28766563215b4465706f7369744173736574207b206173736574733a2057696c6428416c6c436f756e746564286173736574732e6c656e282929292c2062656e6566696369617279207d5d29602c3101202062757420636f756c6420626520736f6d657468696e67206d6f72652065786f746963206c696b652073656e64696e6720746865206061737365747360206576656e20667572746865722e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d3d01202072656c617920746f2070617261636861696e2c206f72206028706172656e74733a20322c2028476c6f62616c436f6e73656e737573282e2e292c202e2e29296020746f2073656e642066726f6df4202070617261636861696e206163726f737320612062726964676520746f20616e6f746865722065636f73797374656d2064657374696e6174696f6e2e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e3d012d20606173736574735f7472616e736665725f74797065603a205468652058434d20605472616e736665725479706560207573656420746f207472616e73666572207468652060617373657473602e2d012d206072656d6f74655f666565735f6964603a204f6e65206f662074686520696e636c7564656420606173736574736020746f206265206265207573656420746f2070617920666565732e49012d2060666565735f7472616e736665725f74797065603a205468652058434d20605472616e736665725479706560207573656420746f207472616e736665722074686520606665657360206173736574732e59012d2060637573746f6d5f78636d5f6f6e5f64657374603a205468652058434d20746f206265206578656375746564206f6e2060646573746020636861696e20617320746865206c6173742073746570206f6620746865590120207472616e736665722c20776869636820616c736f2064657465726d696e657320776861742068617070656e7320746f2074686520617373657473206f6e207468652064657374696e6174696f6e20636861696e2e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e7904080c78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010c08563204007d04015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400a904015076333a3a58636d3c52756e74696d6543616c6c3e00030008563404000501015076343a3a58636d3c52756e74696d6543616c6c3e000400007d040c0c78636d0876320c58636d042c52756e74696d6543616c6c00000400810401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e0000810400000285040085040c0c78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404007901012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404007901012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404007901012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f69642c011c51756572794964000120726573706f6e736589040120526573706f6e73650001286d61785f7765696768742c010c753634000300345472616e7366657241737365740801186173736574737901012c4d756c746941737365747300012c62656e6566696369617279890101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574737901012c4d756c746941737365747300011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f73742c010c75363400011063616c6c5d010168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e04008d010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f7765696768742c010c753634000c00304465706f73697441737365740c0118617373657473990401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200012c62656e6566696369617279890101344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473990401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000e003445786368616e6765417373657408011067697665990401404d756c7469417373657446696c74657200011c726563656976657901012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473990401404d756c7469417373657446696c74657200011c72657365727665890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473990401404d756c7469417373657446696c74657200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e000118617373657473990401404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f7765696768742c010c75363400120030427579457865637574696f6e08011066656573810101284d756c746941737365740001307765696768745f6c696d6974a504012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204007d04014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804007d04014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574737901012c4d756c74694173736574730001187469636b6574890101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f7765696768742c010c753634001a0048556e73756273637269626556657273696f6e001b000089040c0c78636d08763220526573706f6e7365000110104e756c6c0000001841737365747304007901012c4d756c74694173736574730001003c457865637574696f6e526573756c7404008d0401504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e000300008d0404184f7074696f6e0404540191040108104e6f6e6500000010536f6d65040091040000010000910400000408109504009504100c78636d08763218747261697473144572726f72000168204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300444d756c74694c6f636174696f6e46756c6c000400684d756c74694c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e736976650014001054726170040030010c7536340015004c556e68616e646c656458636d56657273696f6e001600485765696768744c696d69745265616368656404003001185765696768740017001c426172726965720018004c5765696768744e6f74436f6d70757461626c65001900009904100c78636d087632286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e69746504007901012c4d756c74694173736574730000001057696c6404009d04013857696c644d756c74694173736574000100009d04100c78636d087632286d756c746961737365743857696c644d756c746941737365740001080c416c6c00000014416c6c4f6608010869648501011c4173736574496400010c66756ea104013c57696c6446756e676962696c69747900010000a104100c78636d087632286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000a5040c0c78636d0876322c5765696768744c696d697400010824556e6c696d697465640000001c4c696d6974656404002c010c75363400010000a9040c0c78636d0876330c58636d041043616c6c00000400ad0401585665633c496e737472756374696f6e3c43616c6c3e3e0000ad04000002b10400b1040c0c78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400ad01012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400ad01012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400ad01012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e7365b5040120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572c90401544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473ad01012c4d756c746941737365747300012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473ad01012c4d756c746941737365747300011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6c5d01014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c1010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400cd0401445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473d10401404d756c7469417373657446696c74657200012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473d10401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e000e003445786368616e676541737365740c011067697665d10401404d756c7469417373657446696c74657200011077616e74ad01012c4d756c746941737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473d10401404d756c7469417373657446696c74657200011c72657365727665bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473d10401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666fcd0401445175657279526573706f6e7365496e666f000118617373657473d10401404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573b50101284d756c746941737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400a904012458636d3c43616c6c3e0015002c536574417070656e6469780400a904012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473ad01012c4d756c74694173736574730001187469636b6574bd0101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400ad01012c4d756c7469417373657473001c002c45787065637441737365740400ad01012c4d756c7469417373657473001d00304578706563744f726967696e0400c90401544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666fcd0401445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400cd0401445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c50101204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bcd0101244e6574776f726b496400012c64657374696e6174696f6ec1010154496e746572696f724d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e002600244c6f636b41737365740801146173736574b50101284d756c74694173736574000120756e6c6f636b6572bd0101344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574b50101284d756c74694173736574000118746172676574bd0101344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574b50101284d756c746941737365740001146f776e6572bd0101344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574b50101284d756c746941737365740001186c6f636b6572bd0101344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bd0101344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696ec90401544f7074696f6e3c4d756c74694c6f636174696f6e3e002f0000b5040c0c78636d08763320526573706f6e7365000118104e756c6c000000184173736574730400ad01012c4d756c74694173736574730001003c457865637574696f6e526573756c740400350101504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f0400b9040198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c7404004d0101384d617962654572726f72436f646500050000b9040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401bd04045300000400c50401185665633c543e0000bd040c0c78636d0876332850616c6c6574496e666f0000180114696e646578cc010c7533320001106e616d65c1040180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d65c1040180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72cc010c7533320001146d696e6f72cc010c7533320001147061746368cc010c7533320000c1040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000c504000002bd0400c90404184f7074696f6e04045401bd010108104e6f6e6500000010536f6d650400bd010000010000cd040c0c78636d087633445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ebd0101344d756c74694c6f636174696f6e00012071756572795f69642c011c517565727949640001286d61785f7765696768742801185765696768740000d104100c78636d087633286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e6974650400ad01012c4d756c74694173736574730000001057696c640400d504013857696c644d756c7469417373657400010000d504100c78636d087633286d756c746961737365743857696c644d756c746941737365740001100c416c6c00000014416c6c4f660801086964b901011c4173736574496400010c66756ed904013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400cc010c75333200020030416c6c4f66436f756e7465640c01086964b901011c4173736574496400010c66756ed904013c57696c6446756e676962696c697479000114636f756e74cc010c75333200030000d904100c78636d087633286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000dd04080c78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010c0856320400e104015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400f104015076333a3a58636d3c52756e74696d6543616c6c3e0003000856340400fd04015076343a3a58636d3c52756e74696d6543616c6c3e00040000e1040c0c78636d0876320c58636d042c52756e74696d6543616c6c00000400e50401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e0000e504000002e90400e9040c0c78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404007901012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404007901012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404007901012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f69642c011c51756572794964000120726573706f6e736589040120526573706f6e73650001286d61785f7765696768742c010c753634000300345472616e7366657241737365740801186173736574737901012c4d756c746941737365747300012c62656e6566696369617279890101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574737901012c4d756c746941737365747300011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f73742c010c75363400011063616c6ced040168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e04008d010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f7765696768742c010c753634000c00304465706f73697441737365740c0118617373657473990401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200012c62656e6566696369617279890101344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473990401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000e003445786368616e6765417373657408011067697665990401404d756c7469417373657446696c74657200011c726563656976657901012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473990401404d756c7469417373657446696c74657200011c72657365727665890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473990401404d756c7469417373657446696c74657200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e000118617373657473990401404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f7765696768742c010c75363400120030427579457865637574696f6e08011066656573810101284d756c746941737365740001307765696768745f6c696d6974a504012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400e104014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e6469780400e104014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574737901012c4d756c74694173736574730001187469636b6574890101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f7765696768742c010c753634001a0048556e73756273637269626556657273696f6e001b0000ed040c0c78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656438011c5665633c75383e0000f1040c0c78636d0876330c58636d041043616c6c00000400f50401585665633c496e737472756374696f6e3c43616c6c3e3e0000f504000002f90400f9040c0c78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400ad01012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400ad01012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400ad01012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e7365b5040120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572c90401544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473ad01012c4d756c746941737365747300012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473ad01012c4d756c746941737365747300011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6ced04014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c1010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400cd0401445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473d10401404d756c7469417373657446696c74657200012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473d10401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e000e003445786368616e676541737365740c011067697665d10401404d756c7469417373657446696c74657200011077616e74ad01012c4d756c746941737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473d10401404d756c7469417373657446696c74657200011c72657365727665bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473d10401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666fcd0401445175657279526573706f6e7365496e666f000118617373657473d10401404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573b50101284d756c746941737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400f104012458636d3c43616c6c3e0015002c536574417070656e6469780400f104012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473ad01012c4d756c74694173736574730001187469636b6574bd0101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400ad01012c4d756c7469417373657473001c002c45787065637441737365740400ad01012c4d756c7469417373657473001d00304578706563744f726967696e0400c90401544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666fcd0401445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400cd0401445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c50101204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bcd0101244e6574776f726b496400012c64657374696e6174696f6ec1010154496e746572696f724d756c74694c6f636174696f6e00010c78636da904011c58636d3c28293e002600244c6f636b41737365740801146173736574b50101284d756c74694173736574000120756e6c6f636b6572bd0101344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574b50101284d756c74694173736574000118746172676574bd0101344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574b50101284d756c746941737365740001146f776e6572bd0101344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574b50101284d756c746941737365740001186c6f636b6572bd0101344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bd0101344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696ec90401544f7074696f6e3c4d756c74694c6f636174696f6e3e002f0000fd040c2c73746167696e675f78636d0876340c58636d041043616c6c00000400010501585665633c496e737472756374696f6e3c43616c6c3e3e0000010500000205050005050c2c73746167696e675f78636d0876342c496e737472756374696f6e041043616c6c0001c03457697468647261774173736574040011010118417373657473000000545265736572766541737365744465706f7369746564040011010118417373657473000100585265636569766554656c65706f727465644173736574040011010118417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e736531010120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572550101404f7074696f6e3c4c6f636174696f6e3e000300345472616e7366657241737365740801186173736574731101011841737365747300012c62656e6566696369617279bc01204c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574731101011841737365747300011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6ced04014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c00140496e746572696f724c6f636174696f6e000b002c5265706f72744572726f720400610101445175657279526573706f6e7365496e666f000c00304465706f73697441737365740801186173736574736501012c417373657446696c74657200012c62656e6566696369617279bc01204c6f636174696f6e000d004c4465706f7369745265736572766541737365740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000e003445786368616e676541737365740c0110676976656501012c417373657446696c74657200011077616e741101011841737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c01186173736574736501012c417373657446696c74657200011c72657365727665bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0001186173736574736501012c417373657446696c74657200120030427579457865637574696f6e080110666565731901011441737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400fd04012458636d3c43616c6c3e0015002c536574417070656e6469780400fd04012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473110101184173736574730001187469636b6574bc01204c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e4173736574040011010118417373657473001c002c4578706563744173736574040011010118417373657473001d00304578706563744f726967696e0400550101404f7074696f6e3c4c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400610101445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c801204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bd401244e6574776f726b496400012c64657374696e6174696f6ec00140496e746572696f724c6f636174696f6e00010c78636d0501011c58636d3c28293e002600244c6f636b41737365740801146173736574190101144173736574000120756e6c6f636b6572bc01204c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574190101144173736574000118746172676574bc01204c6f636174696f6e002800384e6f7465556e6c6f636b61626c6508011461737365741901011441737365740001146f776e6572bc01204c6f636174696f6e0029003452657175657374556e6c6f636b08011461737365741901011441737365740001186c6f636b6572bc01204c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bc01204c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696e550101404f7074696f6e3c4c6f636174696f6e3e002f00000905105073746167696e675f78636d5f6578656375746f72187472616974733861737365745f7472616e73666572305472616e73666572547970650001102054656c65706f7274000000304c6f63616c526573657276650001004844657374696e6174696f6e526573657276650002003452656d6f7465526573657276650400d901014456657273696f6e65644c6f636174696f6e000300000d05080c78636d4056657273696f6e6564417373657449640001080856330400b901012c76333a3a4173736574496400030008563404001d01012c76343a3a417373657449640004000011050c4863756d756c75735f70616c6c65745f78636d1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e15050c5070616c6c65745f6d6573736167655f71756575651870616c6c65741043616c6c04045400010824726561705f706167650801386d6573736167655f6f726967696ee50101484d6573736167654f726967696e4f663c543e000128706167655f696e64657810012450616765496e6465780000043d0152656d6f76652061207061676520776869636820686173206e6f206d6f7265206d657373616765732072656d61696e696e6720746f2062652070726f636573736564206f72206973207374616c652e48657865637574655f6f7665727765696768741001386d6573736167655f6f726967696ee50101484d6573736167654f726967696e4f663c543e0001107061676510012450616765496e646578000114696e64657810011c543a3a53697a650001307765696768745f6c696d6974280118576569676874000134784578656375746520616e206f766572776569676874206d6573736167652e004d0154656d706f726172792070726f63657373696e67206572726f72732077696c6c2062652070726f706167617465642077686572656173207065726d616e656e74206572726f7273206172652074726561746564546173207375636365737320636f6e646974696f6e2e00742d20606f726967696e603a204d75737420626520605369676e6564602e35012d20606d6573736167655f6f726967696e603a20546865206f726967696e2066726f6d20776869636820746865206d65737361676520746f20626520657865637574656420617272697665642e3d012d206070616765603a20546865207061676520696e2074686520717565756520696e20776869636820746865206d65737361676520746f2062652065786563757465642069732073697474696e672e09012d2060696e646578603a2054686520696e64657820696e746f20746865207175657565206f6620746865206d65737361676520746f2062652065786563757465642e59012d20607765696768745f6c696d6974603a20546865206d6178696d756d20616d6f756e74206f662077656967687420616c6c6f77656420746f20626520636f6e73756d656420696e2074686520657865637574696f6e4420206f6620746865206d6573736167652e00f442656e63686d61726b20636f6d706c657869747920636f6e73696465726174696f6e733a204f28696e646578202b207765696768745f6c696d6974292e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e19050c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c65741043616c6c04045400012c4c726571756573745f6d73705f7369676e5f7570100120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640000686901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207265717565737420746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4101546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00490146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742e002c506172616d65746572733a71012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204d61696e2053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292981012d206076616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865207369676e65722077696c6c2070726f766964652061732061204d61696e2053746f726167652050726f766964657220746fad01757365727320616e642077616e747320746f207265676973746572206f6e2d636861696e2e20497420636f756c642062652064617461206c696d6974732c20636f6d6d756e69636174696f6e2070726f746f636f6c7320746f2061636365737320746865207573657227733c646174612c20616e64206d6f72652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e2501322e20436865636b207468617420746865207369676e6572206973206e6f7420616c72656164792072656769737465726564206173206569746865722061204d5350206f72204253509c332e20436865636b207468617420746865206d756c7469616464726573732069732076616c69646101342e20436865636b207468617420746865206461746120746f2062652073746f7265642069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d652e9101352e2043616c63756c61746520686f77206d756368206465706f7369742077696c6c20746865207369676e6572206861766520746f20706179207573696e672074686520616d6f756e74206f6620646174612069742077616e747320746f2073746f7265f0362e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f2070617920746865206465706f7369748c372e20486f6c6420746865206465706f7369742066726f6d20746865207369676e65726901382e2055706461746520746865205369676e2055702052657175657374732073746f7261676520746f2061646420746865207369676e65722061732072657175657374696e6720746f207369676e2075702061732061204d535000d8456d69747320604d7370526571756573745369676e55705375636365737360206576656e74207768656e207375636365737366756c2e4c726571756573745f6273705f7369676e5f75700c0120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e74496400015c4501446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4901546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00490146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742e002c506172616d65746572733a79012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204261636b75702053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292900ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e2501322e20436865636b207468617420746865207369676e6572206973206e6f7420616c72656164792072656769737465726564206173206569746865722061204d5350206f72204253509c332e20436865636b207468617420746865206d756c7469616464726573732069732076616c69645d01342e20436865636b207468617420746865206461746120746f2062652073746f7265642069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d659101352e2043616c63756c61746520686f77206d756368206465706f7369742077696c6c20746865207369676e6572206861766520746f20706179207573696e672074686520616d6f756e74206f6620646174612069742077616e747320746f2073746f7265f0362e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f2070617920746865206465706f7369748c372e20486f6c6420746865206465706f7369742066726f6d20746865207369676e65726901382e2055706461746520746865205369676e2055702052657175657374732073746f7261676520746f2061646420746865207369676e65722061732072657175657374696e6720746f207369676e20757020617320612042535000d8456d6974732060427370526571756573745369676e55705375636365737360206576656e74207768656e207375636365737366756c2e3c636f6e6669726d5f7369676e5f757004014070726f76696465725f6163636f756e749c01504f7074696f6e3c543a3a4163636f756e7449643e00025cad01446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f20636f6e6669726d207468656972207369676e20757020617320612053746f726167652050726f76696465722c20656974686572204d5350206f72204253502e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e9d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722c20657863657074207768656e2070726f766964696e67206129016070726f76696465725f6163636f756e746020706172616d657465722c20696e207768696368206361736520746865206f726967696e2063616e20626520616e79206163636f756e742e002c506172616d65746572733ab9012d206070726f76696465725f6163636f756e74603a20546865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722e204966206e6f742070726f76696465642c20746865207369676e6572e477696c6c20626520636f6e7369646572656420746865206163636f756e7420746861742072657175657374656420746f207369676e2075702e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633a98312e20436865636b2074686174207468652065787472696e73696320776173207369676e65641101322e20436865636b207468617420746865206163636f756e74207265636569766564206861732072657175657374656420746f20726567697374657220617320612053508101332e20436865636b2074686174207468652063757272656e742072616e646f6d6e6573732069732073756666696369656e746c7920667265736820746f206265207573656420617320612073616c7420666f7220746861742072657175657374a4342e20436865636b207468617420746865207265717565737420686173206e6f7420657870697265643101352e20526567697374657220746865207369676e65722061732061204d5350206f722042535020776974682074686520646174612070726f766964656420696e207468652072657175657374009d01456d69747320604d73705369676e55705375636365737360206f7220604273705369676e55705375636365737360206576656e74207768656e207375636365737366756c2c20646570656e64696e67206f6e207468652074797065206f66207369676e2075702e00184e6f7465733a1d012d20546869732065787472696e73696320636f756c642062652063616c6c656420627920746865207573657220697473656c66206f722062792061207468697264207061727479b5012d20546865206465706f73697420746861742074686520757365722068617320746f2070617920746f20726567697374657220617320612053502069732068656c64207768656e20746865207573657220726571756573747320746f207265676973746572206173206120535081012d20496620746869732065787472696e736963206973207375636365737366756c2c2069742077696c6c206265206672656520666f72207468652063616c6c65722c20746f20696e63656e74697665207374617465206465626c6f6174696e673863616e63656c5f7369676e5f7570000330c101446973706174636861626c652065787472696e736963207468617420616c6c6f777320612075736572207769746820612070656e64696e67205369676e205570205265717565737420746f2063616e63656c2069742c2067657474696e6720746865206465706f736974206261636b2e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e3d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207369676e6572206861732072657175657374656420746f207369676e2075702061732061205350dc332e2044656c6574652074686520726571756573742066726f6d20746865205369676e2055702052657175657374732073746f726167658c342e2052657475726e20746865206465706f73697420746f20746865207369676e657200d0456d69747320605369676e55705265717565737443616e63656c656460206576656e74207768656e207375636365737366756c2e306d73705f7369676e5f6f66660004384101446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e206f66662061732061204d61696e2053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e206f66662061732061204d61696e2053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e657220697320726567697374657265642061732061204d53504d01332e20436865636b207468617420746865204d535020686173206e6f2073746f726167652061737369676e656420746f20697420286e6f206275636b657473206f722064617461207573656420627920697429e4342e2055706461746520746865204d5350732073746f726167652c2072656d6f76696e6720746865207369676e657220617320616e204d53508c352e2052657475726e20746865206465706f73697420746f20746865207369676e65724101362e2044656372656d656e74207468652073746f72616765207468617420686f6c647320746f74616c20616d6f756e74206f66204d5350732063757272656e746c7920696e207468652073797374656d00c0456d69747320604d73705369676e4f66665375636365737360206576656e74207768656e207375636365737366756c2e306273705f7369676e5f6f666600053c4901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e206f66662061732061204261636b75702053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e206f66662061732061204261636b75702053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e65722069732072656769737465726564206173206120425350cc332e20436865636b2074686174207468652042535020686173206e6f2073746f726167652061737369676e656420746f206974e4342e205570646174652074686520425350732073746f726167652c2072656d6f76696e6720746865207369676e657220617320616e204253503501352e205570646174652074686520746f74616c206361706163697479206f6620616c6c20425350732c2072656d6f76696e6720746865206361706163697479206f6620746865207369676e65728c362e2052657475726e20746865206465706f73697420746f20746865207369676e65724101372e2044656372656d656e74207468652073746f72616765207468617420686f6c647320746f74616c20616d6f756e74206f6620425350732063757272656e746c7920696e207468652073797374656d00c0456d69747320604273705369676e4f66665375636365737360206576656e74207768656e207375636365737366756c2e3c6368616e67655f63617061636974790401306e65775f636170616369747930014853746f7261676544617461556e69743c543e0006603901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f206368616e676520746865697220616d6f756e74206f662073746f726564206461746100c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f206368616e6765206974732063617061636974792e002c506172616d65746572733a8d012d20606e65775f6361706163697479603a20546865206e657720746f74616c20616d6f756e74206f6620646174612074686174207468652053746f726167652050726f76696465722077616e747320746f2062652061626c6520746f2073746f72652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722eb8322e20436865636b207468617420746865207369676e6572206973207265676973746572656420617320612053505101332e20436865636b207468617420656e6f7567682074696d6520686173207061737365642073696e636520746865206c6173742074696d6520746865205350206368616e676564206974732063617061636974794901342e20436865636b207468617420746865206e65772063617061636974792069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d651d01352e20436865636b207468617420746865206e65772063617061636974792069732067726561746572207468616e20746865206461746120757365642062792074686973205350e4362e2043616c63756c61746520746865206e6577206465706f736974206e656564656420666f722074686973206e65772063617061636974795501372e20436865636b20746f2073656520696620746865206e6577206465706f736974206e65656465642069732067726561746572206f72206c657373207468616e207468652063757272656e74206465706f736974ec09612e20496620746865206e6577206465706f7369742069732067726561746572207468616e207468652063757272656e74206465706f7369743a15010909692e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f207061792074686973206578747261206465706f736974b0090969692e20486f6c6420746865206578747261206465706f7369742066726f6d20746865207369676e6572850109622e20496620746865206e6577206465706f736974206973206c657373207468616e207468652063757272656e74206465706f7369742c2072657475726e207468652068656c6420646966666572656e636520746f20746865207369676e6572c8372e2055706461746520746865205350732073746f7261676520746f206368616e67652074686520746f74616c20646174617501382e20496620757365722069732061204253502c207570646174652074686520746f74616c206361706163697479206f6620746865206e6574776f726b202873756d206f6620616c6c2063617061636974696573206f6620425350732900b8456d697473206043617061636974794368616e67656460206576656e74207768656e207375636365737366756c2e386164645f76616c75655f70726f700401386e65775f76616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e0007409d01446973706174636861626c652065787472696e736963206f6e6c792063616c6c61626c6520627920616e204d5350207468617420616c6c6f777320697420746f2061646420612076616c75652070726f706f736974696f6e20746f20697473207365727669636500c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e1501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f2061646420612076616c75652070726f706f736974696f6e2e002c506172616d65746572733a4d012d20606e65775f76616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865204d53502077616e747320746f2061646420746f2069747320736572766963652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e657220697320726567697374657265642061732061204d53503901332e20436865636b207468617420746865204d535020686173206e6f74207265616368656420746865206d6178696d756d20616d6f756e74206f662076616c75652070726f706f736974696f6e734501342e20436865636b2074686174207468652076616c75652070726f706f736974696f6e2069732076616c6964202873697a6520616e6420616e79206f746865722072656c6576616e7420636865636b73293501352e2055706461746520746865204d5350732073746f7261676520746f20616464207468652076616c75652070726f706f736974696f6e20287769746820697473206964656e7469666965722900b4456d697473206056616c756550726f70416464656460206576656e74207768656e207375636365737366756c2e44666f7263655f6d73705f7369676e5f757018010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e000120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640008588d01446973706174636861626c652065787472696e736963207468617420616c6c6f777320746f20666f72636566756c6c7920616e64206175746f6d61746963616c6c792073696e672075702061204d61696e2053746f726167652050726f76696465722e00bc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f742e5101546865206077686f6020706172616d6574657220697320746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00c10146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722e002c506172616d65746572733a19012d206077686f603a20546865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722ed1012d20606d73705f6964603a20546865204d61696e2053746f726167652050726f7669646572204944207468617420746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722069732072657175657374696e6720746f207369676e2075702061732e71012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204d61696e2053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292981012d206076616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865207369676e65722077696c6c2070726f766964652061732061204d61696e2053746f726167652050726f766964657220746fad01757365727320616e642077616e747320746f207265676973746572206f6e2d636861696e2e20497420636f756c642062652064617461206c696d6974732c20636f6d6d756e69636174696f6e2070726f746f636f6c7320746f2061636365737320746865207573657227733c646174612c20616e64206d6f72652e00a4546869732065787472696e7369632077696c6c20706572666f726d20746865207374657073206f663a0d01312e205b726571756573745f6d73705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6d73705f7369676e5f757029ec322e205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f757029003901456d69747320604d7370526571756573745369676e5570537563636573736020616e6420604d73705369676e55705375636365737360206576656e7473207768656e207375636365737366756c2e44666f7263655f6273705f7369676e5f757018010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e000120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640001187765696768741102017c4f7074696f6e3c52657075746174696f6e576569676874547970653c543e3e00094c9501446973706174636861626c652065787472696e736963207468617420616c6c6f777320746f20666f72636566756c6c7920616e64206175746f6d61746963616c6c792073696e672075702061204261636b75702053746f726167652050726f76696465722e00bc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f742e5901546865206077686f6020706172616d6574657220697320746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00c10146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722e002c506172616d65746572733a21012d206077686f603a20546865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722ed9012d20606273705f6964603a20546865204261636b75702053746f726167652050726f7669646572204944207468617420746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722069732072657175657374696e6720746f207369676e2075702061732e79012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204261636b75702053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292900a4546869732065787472696e7369632077696c6c20706572666f726d20746865207374657073206f663a0d01312e205b726571756573745f6273705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6273705f7369676e5f757029ec322e205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f757029003901456d6974732060427370526571756573745369676e5570537563636573736020616e6420604273705369676e55705375636365737360206576656e7473207768656e207375636365737366756c2e14736c61736804012c70726f76696465725f69643401244861736849643c543e000a10fc446973706174636861626c652065787472696e73696320746f20736c6173682061205f736c61736861626c655f2053746f726167652050726f76696465722e00b501412053746f726167652050726f7669646572206973205f736c61736861626c655f2069666620697420686173206661696c656420746f20726573706f6e6420746f206368616c6c656e67657320666f722070726f766964696e672070726f6f6673206f662073746f726167652e4d02496e2074686520636f6e74657874206f66207468652053746f726167654875622070726f746f636f6c2c207468652070726f6f66732d6465616c65722070616c6c6574206d61726b7320612053746f726167652050726f7669646572206173205f736c61736861626c655f207768656e206974206661696c7320746f20726573706f6e6420746f206368616c6c656e6765732e04c4446973706174636861626c6573202865787472696e7369637329206578706f73656420627920746869732070616c6c65741d050c4870616c6c65745f66696c655f73797374656d1870616c6c65741043616c6c040454000144346372656174655f6275636b65740c01186d73705f696434014050726f76696465724964466f723c543e0001106e616d65f90101404275636b65744e616d65466f723c543e00011c70726976617465200110626f6f6c0000004c726571756573745f6d6f76655f6275636b65740801246275636b65745f69643401384275636b65744964466f723c543e0001286e65775f6d73705f696434014050726f76696465724964466f723c543e0001007c6d73705f726573706f6e645f6d6f76655f6275636b65745f726571756573740801246275636b65745f69643401384275636b65744964466f723c543e000120726573706f6e7365210501644275636b65744d6f766552657175657374526573706f6e7365000200547570646174655f6275636b65745f707269766163790801246275636b65745f69643401384275636b65744964466f723c543e00011c70726976617465200110626f6f6c000300ac6372656174655f616e645f6173736f63696174655f636f6c6c656374696f6e5f776974685f6275636b65740401246275636b65745f69643401384275636b65744964466f723c543e000404c043726561746520616e64206173736f6369617465206120636f6c6c656374696f6e20776974682061206275636b65742e5469737375655f73746f726167655f726571756573741801246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e0001186d73705f696434014050726f76696465724964466f723c543e000120706565725f696473f5010128506565724964733c543e0005049849737375652061206e65772073746f72616765207265717565737420666f7220612066696c65587265766f6b655f73746f726167655f7265717565737404012066696c655f6b65793401344d65726b6c65486173683c543e000604585265766f6b652073746f726167652072657175657374ac6273705f6164645f646174615f7365727665725f666f725f6d6f76655f6275636b65745f726571756573740401246275636b65745f69643401384275636b65744964466f723c543e000704690141646420796f757273656c66206173206120646174612073657276657220666f722070726f766964696e67207468652066696c6573206f6620746865206275636b65742072657175657374656420746f206265206d6f7665642eb46d73705f726573706f6e645f73746f726167655f72657175657374735f6d756c7469706c655f6275636b65747304016066696c655f6b65795f726573706f6e7365735f696e7075742505016046696c654b6579526573706f6e736573496e7075743c543e0008244901557365642062792061204d535020746f20616363657074206f72206465636c696e652073746f7261676520726571756573747320696e20626174636865732c2067726f75706564206279206275636b65742e00d9015468697320666f6c6c6f7773206120626573742d6566666f72742073747261746567792c206d65616e696e67207468617420616c6c2066696c65206b6579732077696c6c2062652070726f63657373656420616e64206465636c6172656420746f2068617665207375636365737366756c6c79206265590161636365707465642c2072656a6563746564206f722068617665206661696c656420746f2062652070726f63657373656420696e2074686520726573756c7473206f6620746865206576656e7420656d69747465642e00f501546865204d53502068617320746f2070726f7669646520612066696c652070726f6f6620666f7220616c6c207468652066696c65206b657973207468617420617265206265696e6720616363657074656420616e642061206e6f6e2d696e636c7573696f6e2070726f6f6620666f72207468652066696c65206b6579739501696e20746865206275636b65742773204d65726b6c6520506174726963696120466f726573742e205468652066696c652070726f6f667320666f72207468652066696c65206b657973206973206e656365737361727920746f2076657269667920746861748501746865204d53502061637475616c6c7920686173207468652066696c65732c207768696c6520746865206e6f6e2d696e636c7573696f6e2070726f6f66206973206e656365737361727920746f20766572696679207468617420746865204d5350647761736e27742073746f72696e67206974206265666f72652e346273705f766f6c756e7465657204012066696c655f6b65793401344d65726b6c65486173683c543e000918b85573656420627920612042535020746f20766f6c756e7465657220666f722073746f72696e6720612066696c652e008d01546865207472616e73616374696f6e2077696c6c206661696c2069662074686520584f52206265747765656e207468652066696c6520494420616e642074686520425350204944206973206e6f742062656c6f7720746865207468726573686f6c642c3d01736f206120425350206973207374726f6e676c79206164766973656420746f20636865636b206265666f726568616e642e20416e6f7468657220726561736f6e20666f72206661696c7572652069735101696620746865206d6178696d756d206e756d626572206f66204253507320686173206265656e20726561636865642e2041207375636365737366756c2061737369676e6d656e7420617320425350206d65616e73e87468617420736f6d65206f662074686520636f6c6c61746572616c20746f6b656e73206f662074686174204d5350206172652066726f7a656e2e4c6273705f636f6e6669726d5f73746f72696e670801686e6f6e5f696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e00015066696c655f6b6579735f616e645f70726f6f66733905013501426f756e6465645665633c284d65726b6c65486173683c543e2c204b657950726f6f663c543e292c20543a3a0a4d61784261746368436f6e6669726d53746f7261676552657175657374732c3e000a0411015573656420627920612042535020746f20636f6e6669726d2074686579206172652073746f72696e672064617461206f6620612073746f7261676520726571756573742e606273705f726571756573745f73746f705f73746f72696e6720012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001146f776e6572000130543a3a4163636f756e74496400012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e00012463616e5f7365727665200110626f6f6c000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000b20d0457865637574656420627920612042535020746f207265717565737420746f2073746f702073746f72696e6720612066696c652e006d01496e20746865206576656e74207768656e20612073746f726167652072657175657374206e6f206c6f6e6765722065786973747320666f722074686520646174612074686520425350206e6f206c6f6e6765722073746f7265732c7101697420697320726571756972656420746861742074686520425350207374696c6c206861732061636365737320746f20746865206d65746164617461206f662074686520696e697469616c2073746f7261676520726571756573742e89014966207468657920646f206e6f742c20746865792077696c6c206174206c65617374206e6565642074686174206d6574616461746120746f207265636f6e737472756374207468652046696c6520494420616e642066726f6d2077686572657665723d017468652042535020676574732074686174206461746120697320757020746f2069742e204f6e65206578616d706c6520636f756c642062652066726f6d207468652061737369676e6564204d53502e750154686973206d65746164617461206973206e65636573736172792073696e6365206974206973206e656564656420746f207265636f6e73747275637420746865206c656166206e6f6465206b657920696e207468652073746f726167656470726f76696465722773204d65726b6c6520466f726573742e606273705f636f6e6669726d5f73746f705f73746f72696e6708012066696c655f6b65793401344d65726b6c65486173683c543e000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000c14d0457865637574656420627920612042535020746f20636f6e6669726d20746f2073746f702073746f72696e6720612066696c652e00bd0149742068617320746f20686176652070726576696f75736c79206f70656e656420612070656e64696e672073746f702073746f72696e672072657175657374207573696e672074686520606273705f726571756573745f73746f705f73746f72696e67602065787472696e7369632eb901546865206d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e20746865207265717565737420616e642074686520636f6e6669726d6174696f6e20697320646566696e6564206279207468652072756e74696d652c20737563682074686174207468658d014253502063616e277420696d6d6564696174656c792073746f702073746f72696e6720612066696c65206974206861732070726576696f75736c79206c6f7374207768656e20726563656976696e672061206368616c6c656e676520666f722069742e7c73746f705f73746f72696e675f666f725f696e736f6c76656e745f757365721c012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001146f776e6572000130543a3a4163636f756e74496400012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000d18fc4578656375746564206279206120535020746f2073746f702073746f72696e6720612066696c652066726f6d20616e20696e736f6c76656e7420757365722e00b101546869732069732075736564207768656e2061207573657220686173206265636f6d6520696e736f6c76656e7420616e6420746865205350206e6565647320746f2073746f702073746f72696e67207468652066696c6573206f66207468617420757365722c2073696e6365a0697420776f6e27742062652067657474696e67207061696420666f7220697420616e796d6f72652e3d025468652076616c69646174696f6e73206172652073696d696c617220746f20746865206f6e657320696e2074686520606273705f726571756573745f73746f705f73746f72696e676020616e6420606273705f636f6e6669726d5f73746f705f73746f72696e67602065787472696e736963732c206275742074686520535020646f65736e2774206e65656420746f7d017761697420666f722061206d696e696d756d20616d6f756e74206f6620626c6f636b7320746f20636f6e6669726d20746f2073746f702073746f72696e67207468652066696c65206e6f722069742068617320746f2062652061204253502e2c64656c6574655f66696c651801246275636b65745f69643401384275636b65744964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00011073697a6530013853746f72616765446174613c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e0001706d617962655f696e636c7573696f6e5f666f726573745f70726f6f664d0501584f7074696f6e3c466f7265737450726f6f663c543e3e000e00a870656e64696e675f66696c655f64656c6574696f6e5f726571756573745f7375626d69745f70726f6f6610011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e000130666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000f00547365745f676c6f62616c5f706172616d65746572730801487265706c69636174696f6e5f746172676574110201804f7074696f6e3c543a3a5265706c69636174696f6e546172676574547970653e00017c7469636b5f72616e67655f746f5f6d6178696d756d5f7468726573686f6c64110201544f7074696f6e3c5469636b4e756d6265723c543e3e001000040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e21050c4870616c6c65745f66696c655f73797374656d147479706573644275636b65744d6f766552657175657374526573706f6e73650001082041636365707465640000002052656a65637465640001000025050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012905045300000400490501185665633c543e0000290500000408342d05002d050c4870616c6c65745f66696c655f73797374656d147479706573644d737053746f7261676552657175657374526573706f6e73650404540000080118616363657074310501ac4f7074696f6e3c416363657074656453746f7261676552657175657374506172616d65746572733c543e3e00011872656a656374450501a5014f7074696f6e3c426f756e6465645665633c284d65726b6c65486173683c543e2c2052656a656374656453746f7261676552657175657374526561736f6e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e2c3e2c3e0000310504184f7074696f6e0404540135050108104e6f6e6500000010536f6d6504003505000001000035050c4870616c6c65745f66696c655f73797374656d14747970657380416363657074656453746f7261676552657175657374506172616d6574657273040454000008015066696c655f6b6579735f616e645f70726f6f66733905013901426f756e6465645665633c284d65726b6c65486173683c543e2c204b657950726f6f663c543e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001686e6f6e5f696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000039050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454013d05045300000400410501185665633c543e00003d05000004083475020041050000023d0500450504184f7074696f6e0404540135020108104e6f6e6500000010536f6d6504003502000001000049050000022905004d0504184f7074696f6e0404540165020108104e6f6e6500000010536f6d6504006502000001000051050c5070616c6c65745f70726f6f66735f6465616c65721870616c6c65741043616c6c040454000110246368616c6c656e676504010c6b65793401244b6579466f723c543e00001868496e74726f647563652061206e6577206368616c6c656e67652e003101546869732066756e6374696f6e20616c6c6f777320616e796f6e6520746f206164642061206e6577206368616c6c656e676520746f2074686520604368616c6c656e6765735175657565602ed8546865206368616c6c656e67652077696c6c206265206469737061746368656420696e2074686520636f6d696e6720626c6f636b732efc5573657273206172652063686172676564206120736d616c6c2066656520666f72207375626d697474696e672061206368616c6c656e67652c20776869636854676f657320746f207468652054726561737572792e307375626d69745f70726f6f6608011470726f6f666102012050726f6f663c543e00012070726f7669646572710301604f7074696f6e3c50726f76696465724964466f723c543e3e00015484466f7220612050726f766964657220746f207375626d697420612070726f6f662e00e0436865636b732074686174206070726f766964657260206973206120726567697374657265642050726f76696465722e204966206e6f6e65090169732070726f76696465642c207468652070726f6f66207375626d697474657220697320636f6e7369646572656420746f206265207468652050726f76696465722ef852656c696573206f6e20612050726f7669646572732070616c6c657420746f206765742074686520726f6f7420666f72207468652050726f76696465722e390156616c6964617465732074686174207468652070726f6f6620636f72726573706f6e647320746f2061206368616c6c656e6765207468617420776173206d61646520696e2074686520706173742c3901627920636865636b696e672074686520605469636b546f4368616c6c656e67657353656564602053746f726167654d61702e20546865206368616c6c656e6765207469636b207468617420746865410150726f76696465722073686f756c642068617665207375626d697474656420612070726f6f662069732063616c63756c61746564206261736564206f6e20746865206c617374207469636b207468657969017375626d697474656420612070726f6f6620666f7220285b604c6173745469636b50726f76696465725375626d69747465644150726f6f66466f72605d292c20616e64207468652070726f76696e6720706572696f6420666f72c8746861742050726f76696465722c20776869636820697320612066756e6374696f6e206f66207468656972207374616b652e3901546869732065787472696e73696320616c736f20636865636b732074686174207468657265206861736e2774206265656e206120636865636b706f696e74206368616c6c656e676520726f756e642101696e206265747765656e20746865206c6173742074696d65207468652050726f7669646572207375626d697474656420612070726f6f6620666f7220616e6420746865207469636b2901666f72207768696368207468652070726f6f66206973206265696e67207375626d69747465642e20496620746865726520686173206265656e2c207468652050726f7669646572206973507375626a65637420746f20736c617368696e672e002449662076616c69643a55012d2050757368657320666f7277617264207468652050726f766964657220696e20746865205b605469636b546f50726f766964657273446561646c696e6573605d2053746f726167654d61702061206e756d626572d06f66207469636b7320636f72726573706f6e64696e6720746f20746865207374616b65206f66207468652050726f76696465722e3d012d205265676973746572732074686973207469636b20617320746865206c617374207469636b20696e207768696368207468652050726f7669646572207375626d697474656420612070726f6f662e001501457865637574696f6e206f6620746869732065787472696e7369632073686f756c6420626520726566756e646564206966207468652070726f6f662069732076616c69642e80666f7263655f696e697469616c6973655f6368616c6c656e67655f6379636c6504012070726f766964657234014050726f76696465724964466f723c543e000218a0496e697469616c69736520612050726f76696465722773206368616c6c656e6765206379636c652e00584f6e6c792063616c6c61626c65206279207375646f2e005d015365747320746865206c617374207469636b207468652050726f7669646572207375626d697474656420612070726f6f6620666f7220746f207468652063757272656e74207469636b2c20616e642073657473207468657101646561646c696e6520666f72207375626d697474696e6720612070726f6f6620746f207468652063757272656e74207469636b202b207468652050726f7669646572277320706572696f64202b2074686520746f6c6572616e63652e287365745f706175736564040118706175736564200110626f6f6c00030ce053657420746865205b604368616c6c656e6765735469636b6572506175736564605d20746f20607472756560206f72206066616c7365602e00584f6e6c792063616c6c61626c65206279207375646f2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e55050c4470616c6c65745f72616e646f6d6e6573731870616c6c65741043616c6c0404540001044c7365745f626162655f72616e646f6d6e65737300000ced015468697320696e686572656e742074686174206d75737420626520696e636c7564656420284469737061746368436c6173733a3a4d616e6461746f727929206174206561636820626c6f636b20736176657320746865206c61746573742072616e646f6d6e65737320617661696c61626c652066726f6d20746865810172656c617920636861696e20696e746f2061207661726961626c6520746861742063616e207468656e20626520757365642061732061207365656420666f7220636f6d6d69746d656e747320746861742068617070656e656420647572696e67787468652070726576696f75732072656c617920636861696e2065706f6368040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e59050c5870616c6c65745f7061796d656e745f73747265616d731870616c6c65741043616c6c040454000124806372656174655f66697865645f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e7449640001107261746518013042616c616e63654f663c543e0000489501446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2061646420612066697865642d72617465207061796d656e742073747265616d2066726f6d2061205573657220746f20612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722ec42d206072617465603a2054686520696e697469616c2072617465206f6620746865207061796d656e742073747265616d2e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696edc322e20436865636b207468617420746865207061796d656e742073747265616d20646f6573206e6f7420616c7265616479206578697374e8332e20436865636b20746861742074686520557365722068617320656e6f7567682066756e647320746f2070617920746865206465706f73697484342e20486f6c6420746865206465706f7369742066726f6d2074686520557365720d01352e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2061646420746865206e6577207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d4372656174656460206576656e74207768656e207375636365737366756c2e807570646174655f66697865645f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e7449640001206e65775f7261746518013042616c616e63654f663c543e000140d901446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2075706461746520616e206578697374696e672066697865642d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722ec42d20606e65775f72617465603a20546865206e65772072617465206f6620746865207061796d656e742073747265616d2e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2075706461746520746865207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d5570646174656460206576656e74207768656e207375636365737366756c2e8064656c6574655f66697865645f726174655f7061796d656e745f73747265616d08012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400023cd901446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2064656c65746520616e206578697374696e672066697865642d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2072656d6f766520746865207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d44656c6574656460206576656e74207768656e207375636365737366756c2e886372656174655f64796e616d69635f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e0003489d01446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2061646420612064796e616d69632d72617465207061796d656e742073747265616d2066726f6d2061205573657220746f20612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e05012d2060616d6f756e745f70726f7669646564603a2054686520696e697469616c20616d6f756e742070726f7669646564206279207468652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696edc322e20436865636b207468617420746865207061796d656e742073747265616d20646f6573206e6f7420616c7265616479206578697374e8332e20436865636b20746861742074686520557365722068617320656e6f7567682066756e647320746f2070617920746865206465706f73697484342e20486f6c6420746865206465706f7369742066726f6d2074686520557365720d01352e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2061646420746865206e6577207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d4372656174656460206576656e74207768656e207375636365737366756c2e887570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400014c6e65775f616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000440e101446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2075706461746520616e206578697374696e672064796e616d69632d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e05012d20606e65775f616d6f756e745f70726f7669646564603a20546865206e657720616d6f756e742070726f7669646564206279207468652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2075706461746520746865207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d5570646174656460206576656e74207768656e207375636365737366756c2e8864656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d08012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400053ce101446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2064656c65746520616e206578697374696e672064796e616d69632d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2072656d6f766520746865207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d44656c6574656460206576656e74207768656e207375636365737366756c2e586368617267655f7061796d656e745f73747265616d73040130757365725f6163636f756e74000130543a3a4163636f756e7449640006705101446973706174636861626c652065787472696e736963207468617420616c6c6f77732050726f76696465727320746f206368617267652061207061796d656e742073747265616d2066726f6d206120557365722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e6d01546865206f726967696e206d757374206265207468652050726f7669646572207468617420686173206174206c65617374206f6e652074797065206f66207061796d656e742073747265616d20776974682074686520557365722e002c506172616d65746572733a15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e4101322e20436865636b20746861742061207061796d656e742073747265616d206265747765656e20746865207369676e6572202850726f76696465722920616e6420746865205573657220657869737473ac332e20496620746865726520697320612066697865642d72617465207061796d656e742073747265616d3aa0202020312e20476574207468652072617465206f6620746865207061796d656e742073747265616dbd01202020322e204765742074686520646966666572656e6365206265747765656e20746865206c6173742063686172676564207469636b206e756d62657220616e6420746865206c6173742063686172676561626c65207469636b206e756d626572206f66207468652073747265616df8202020332e2043616c63756c6174652074686520616d6f756e7420746f2063686172676520646f696e67206072617465202a20646966666572656e636560e501202020342e204368617267652074686520757365722028696620746865207573657220646f6573206e6f74206861766520656e6f7567682066756e64732c206974206765747320666c616767656420616e642061206055736572576974686f757446756e647360206576656e7420697320656d697474656429fc202020352e2055706461746520746865206c6173742063686172676564207469636b206e756d626572206f6620746865207061796d656e742073747265616db4342e20496620746865726520697320612064796e616d69632d72617465207061796d656e742073747265616d3ab4202020312e204765742074686520616d6f756e742070726f7669646564206279207468652050726f7669646572f501202020322e204765742074686520646966666572656e6365206265747765656e20707269636520696e646578207768656e207468652073747265616d20776173206c617374206368617267656420616e642074686520707269636520696e64657820617420746865206c6173742063686172676561626c65207469636b2501202020332e2043616c63756c6174652074686520616d6f756e7420746f2063686172676520646f696e672060616d6f756e745f70726f7669646564202a20646966666572656e636560e501202020342e204368617267652074686520757365722028696620746865207573657220646f6573206e6f74206861766520656e6f7567682066756e64732c206974206765747320666c616767656420616e642061206055736572576974686f757446756e647360206576656e7420697320656d6974746564294d01202020352e205570646174652074686520707269636520696e646578207768656e207468652073747265616d20776173206c6173742063686172676564206f6620746865207061796d656e742073747265616d00d4456d697473206120605061796d656e7453747265616d4368617267656460206576656e74207768656e207375636365737366756c2e0001024e6f7465733a20612050726f766964657220636f756c64206861766520626f746820612066697865642d7261746520616e6420612064796e616d69632d72617465207061796d656e742073747265616d2077697468206120557365722e204966207468617427732074686520636173652c20746869732065787472696e736963b90177696c6c2074727920746f2063686172676520626f746820616e642074686520616d6f756e7420636861726765642077696c6c206265207468652073756d206f662074686520616d6f756e7473206368617267656420666f722065616368207061796d656e742073747265616d2e507061795f6f75747374616e64696e675f64656274000748f501446973706174636861626c652065787472696e736963207468617420616c6c6f77732061207573657220666c616767656420617320776974686f75742066756e647320746f2070617920616c6c2072656d61696e696e67207061796d656e742073747265616d7320746f2062652061626c6520746f207265636f76657234697473206465706f736974732e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0d01546865206f726967696e206d757374206265207468652055736572207468617420686173206265656e20666c616767656420617320776974686f75742066756e64732e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207573657220686173206265656e20666c616767656420617320776974686f75742066756e64732e4101332e2052656c6561736520746865207573657227732066756e6473207468617420776572652068656c642061732061206465706f73697420666f722065616368207061796d656e742073747265616d2e7901342e2047657420616c6c207061796d656e742073747265616d73206f6620746865207573657220616e6420636861726765207468656d2c20706179696e67207468652050726f76696465727320666f72207468652073657276696365732ea8352e2044656c65746520616c6c207061796d656e742073747265616d73206f662074686520757365722e00b8456d697473206120275573657250616964446562747327206576656e74207768656e207375636365737366756c2e0001024e6f7465733a20746869732065787472696e736963206974657261746573206f76657220616c6c207061796d656e742073747265616d73206f6620746865207573657220616e642063686172676573207468656d2c20736f2069742063616e20626520657870656e7369766520696e207465726d73206f66207765696768742efd015468652066656520746f20657865637574652069742073686f756c64206265206869676820656e6f75676820746f20636f6d70656e7361746520666f722074686520776569676874206f66207468652065787472696e7369632c20776974686f7574206265696e6720746f6f206869676820746861742074686520757365724d0166696e6473206d6f726520636f6e76656e69656e7420746f207761697420666f722050726f76696465727320746f2067657420697473206465706f73697473206f6e65206279206f6e6520696e73746561642e50636c6561725f696e736f6c76656e745f666c6167000858dd01446973706174636861626c652065787472696e736963207468617420616c6c6f77732061207573657220666c616767656420617320776974686f75742066756e6473206c6f6e672061676f20656e6f75676820746f20636c656172207468697320666c61672066726f6d20697473206163636f756e742c0102616c6c6f77696e6720697420746f20626567696e20636f6e7472616374696e6720616e6420706179696e6720666f7220736572766963657320616761696e2e204966207468657265277320616e79206f75747374616e64696e6720646562742c2069742077696c6c206265206368617267656420616e6420636c65617265642e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0d01546865206f726967696e206d757374206265207468652055736572207468617420686173206265656e20666c616767656420617320776974686f75742066756e64732e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207573657220686173206265656e20666c616767656420617320776974686f75742066756e64732e6501332e20436865636b20746861742074686520636f6f6c646f776e20706572696f6420686173207061737365642073696e63652074686520757365722077617320666c616767656420617320776974686f75742066756e64732e2101342e20436865636b206966207468657265277320616e79206f75747374616e64696e67206465627420616e64206368617267652069742e205468697320697320646f6e652062793a35012020612e2052656c656173696e6720616e792072656d61696e696e672066756e64732068656c642061732061206465706f73697420666f722065616368207061796d656e742073747265616d2e99012020622e2047657474696e6720616c6c207061796d656e742073747265616d73206f6620746865207573657220616e64206368617267696e67207468656d2c20706179696e67207468652050726f76696465727320666f72207468652073657276696365732eb02020632e2052657475726e696e6720746865205573657220616e792072656d61696e696e672066756e64732eb82020642e2044656c6574696e6720616c6c207061796d656e742073747265616d73206f662074686520757365722e90352e20556e666c616720746865207573657220617320776974686f75742066756e64732e00b0456d6974732061202755736572536f6c76656e7427206576656e74207768656e207375636365737366756c2e0029024e6f7465733a20746869732065787472696e736963206974657261746573206f76657220616c6c2072656d61696e696e67207061796d656e742073747265616d73206f6620746865207573657220616e642063686172676573207468656d2c20736f2069742063616e20626520657870656e7369766520696e207465726d73206f66207765696768742efd015468652066656520746f20657865637574652069742073686f756c64206265206869676820656e6f75676820746f20636f6d70656e7361746520666f722074686520776569676874206f66207468652065787472696e7369632c20776974686f7574206265696e6720746f6f206869676820746861742074686520757365724d0166696e6473206d6f726520636f6e76656e69656e7420746f207761697420666f722050726f76696465727320746f2067657420697473206465706f73697473206f6e65206279206f6e6520696e73746561642e04c4446973706174636861626c6573202865787472696e7369637329206578706f73656420627920746869732070616c6c65745d050c4870616c6c65745f6275636b65745f6e6674731870616c6c65741043616c6c0404540001083073686172655f616363657373100124726563697069656e74450401684163636f756e7449644c6f6f6b7570536f757263654f663c543e0001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000144726561645f6163636573735f7265676578610501684f7074696f6e3c5265616441636365737352656765783c543e3e00000cec53686172652061636365737320746f2066696c65732077697468696e2061206275636b6574207769746820616e6f74686572206163636f756e742e0079025468652060726561645f6163636573735f72656765786020706172616d65746572206973206f7074696f6e616c20616e64207768656e2073657420746f20604e6f6e6560206974206d65616e7320746861742074686520726563697069656e742077696c6c2062652064656e6965642061636365737320666f7220616e79207265616420726571756573742077697468696e20746865206275636b65742e487570646174655f726561645f6163636573730c01186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000144726561645f6163636573735f7265676578610501684f7074696f6e3c5265616441636365737352656765783c543e3e0001047c55706461746520726561642061636365737320666f7220616e206974656d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e610504184f7074696f6e04045401b5020108104e6f6e6500000010536f6d650400b502000001000065050c2c70616c6c65745f6e6674731870616c6c65741043616c6c08045400044900019c1863726561746508011461646d696e450401504163636f756e7449644c6f6f6b75704f663c543e000118636f6e66696769050164436f6c6c656374696f6e436f6e666967466f723c542c20493e00003c090149737375652061206e657720636f6c6c656374696f6e206f66206e6f6e2d66756e6769626c65206974656d732066726f6d2061207075626c6963206f726967696e2e001d0154686973206e657720636f6c6c656374696f6e20686173206e6f206974656d7320696e697469616c6c7920616e6420697473206f776e657220697320746865206f726967696e2e002501546865206f726967696e206d757374206265205369676e656420616e64207468652073656e646572206d75737420686176652073756666696369656e742066756e647320667265652e00c460436f6c6c656374696f6e4465706f736974602066756e6473206f662073656e646572206172652072657365727665642e002c506172616d65746572733a45012d206061646d696e603a205468652061646d696e206f66207468697320636f6c6c656374696f6e2e205468652061646d696e2069732074686520696e697469616c2061646472657373206f662065616368986d656d626572206f662074686520636f6c6c656374696f6e27732061646d696e207465616d2e0098456d69747320604372656174656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f2831296030666f7263655f6372656174650801146f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000118636f6e66696769050164436f6c6c656374696f6e436f6e666967466f723c542c20493e00013c190149737375652061206e657720636f6c6c656374696f6e206f66206e6f6e2d66756e6769626c65206974656d732066726f6d20612070726976696c65676564206f726967696e2e00ac54686973206e657720636f6c6c656374696f6e20686173206e6f206974656d7320696e697469616c6c792e00a4546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e602e009c556e6c696b652060637265617465602c206e6f2066756e6473206172652072657365727665642e0039012d20606f776e6572603a20546865206f776e6572206f66207468697320636f6c6c656374696f6e206f66206974656d732e20546865206f776e6572206861732066756c6c20737570657275736572590120207065726d697373696f6e73206f7665722074686973206974656d2c20627574206d6179206c61746572206368616e676520616e6420636f6e66696775726520746865207065726d697373696f6e73207573696e67982020607472616e736665725f6f776e6572736869706020616e6420607365745f7465616d602e00ac456d6974732060466f7263654372656174656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f283129601c64657374726f79080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011c7769746e6573738905013844657374726f795769746e6573730002449c44657374726f79206120636f6c6c656374696f6e206f662066756e6769626c65206974656d732e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d75737420626520605369676e65646020616e64207468652073656e646572206d75737420626520746865686f776e6572206f66207468652060636f6c6c656374696f6e602e00dc4e4f54453a2054686520636f6c6c656374696f6e206d75737420686176652030206974656d7320746f2062652064657374726f7965642e0005012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f2062652064657374726f7965642e31012d20607769746e657373603a20496e666f726d6174696f6e206f6e20746865206974656d73206d696e74656420696e2074686520636f6c6c656374696f6e2e2054686973206d75737420626520636f72726563742e00a0456d697473206044657374726f79656460206576656e74207768656e207375636365737366756c2e00745765696768743a20604f286d202b2063202b206129602077686572653a782d20606d203d207769746e6573732e6974656d5f6d657461646174617360702d206063203d207769746e6573732e6974656d5f636f6e6669677360682d206061203d207769746e6573732e6174747269627574657360106d696e74100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011c6d696e745f746f450401504163636f756e7449644c6f6f6b75704f663c543e0001307769746e6573735f646174618d0501d84f7074696f6e3c4d696e745769746e6573733c543a3a4974656d49642c204465706f73697442616c616e63654f663c542c20493e3e3e000340a04d696e7420616e206974656d206f66206120706172746963756c617220636f6c6c656374696f6e2e005101546865206f726967696e206d757374206265205369676e656420616e64207468652073656e646572206d75737420636f6d706c7920776974682074686520606d696e745f73657474696e6773602072756c65732e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206d696e7465642ea02d20606974656d603a20416e206964656e746966696572206f6620746865206e6577206974656d2ee02d20606d696e745f746f603a204163636f756e7420696e746f20776869636820746865206974656d2077696c6c206265206d696e7465642e45012d20607769746e6573735f64617461603a205768656e20746865206d696e7420747970652069732060486f6c6465724f6628636f6c6c656374696f6e5f696429602c207468656e20746865206f776e6564590120206974656d5f69642066726f6d207468617420636f6c6c656374696f6e206e6565647320746f2062652070726f76696465642077697468696e20746865207769746e6573732064617461206f626a6563742e20496661012020746865206d696e74207072696365206973207365742c207468656e2069742073686f756c64206265206164646974696f6e616c6c7920636f6e6669726d656420696e2074686520607769746e6573735f64617461602e0051014e6f74653a20746865206465706f7369742077696c6c2062652074616b656e2066726f6d2074686520606f726967696e6020616e64206e6f742074686520606f776e657260206f662074686520606974656d602e0094456d697473206049737375656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f2831296028666f7263655f6d696e74100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011c6d696e745f746f450401504163636f756e7449644c6f6f6b75704f663c543e00012c6974656d5f636f6e666967950501284974656d436f6e66696700043405014d696e7420616e206974656d206f66206120706172746963756c617220636f6c6c656374696f6e2066726f6d20612070726976696c65676564206f726967696e2e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d75737420626520605369676e65646020616e64207468652073656e646572206d757374206265207468656c497373756572206f66207468652060636f6c6c656374696f6e602e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206d696e7465642ea02d20606974656d603a20416e206964656e746966696572206f6620746865206e6577206974656d2ee02d20606d696e745f746f603a204163636f756e7420696e746f20776869636820746865206974656d2077696c6c206265206d696e7465642ea82d20606974656d5f636f6e666967603a204120636f6e666967206f6620746865206e6577206974656d2e0094456d697473206049737375656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f28312960106275726e080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400052c5844657374726f7920612073696e676c65206974656d2e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d757374206265205369676e656420616e6420746865207369676e696e67206163636f756e74206d7573746c626520746865206f776e6572206f662074686520606974656d602e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206275726e65642e802d20606974656d603a20546865206974656d20746f206265206275726e65642e003c456d69747320604275726e6564602e00385765696768743a20604f28312960207472616e736665720c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064657374450401504163636f756e7449644c6f6f6b75704f663c543e000638c04d6f766520616e206974656d2066726f6d207468652073656e646572206163636f756e7420746f20616e6f746865722e00f44f726967696e206d757374206265205369676e656420616e6420746865207369676e696e67206163636f756e74206d757374206265206569746865723a682d20746865204f776e6572206f662074686520606974656d603b35012d2074686520617070726f7665642064656c656761746520666f722074686520606974656d602028696e207468697320636173652c2074686520617070726f76616c206973207265736574292e0028417267756d656e74733af42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265207472616e736665727265642e942d20606974656d603a20546865206974656d20746f206265207472616e736665727265642edc2d206064657374603a20546865206163636f756e7420746f2072656365697665206f776e657273686970206f6620746865206974656d2e0050456d69747320605472616e73666572726564602e00385765696768743a20604f283129602472656465706f736974080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146974656d73b90201385665633c543a3a4974656d49643e0007449c52652d6576616c7561746520746865206465706f73697473206f6e20736f6d65206974656d732e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00f82d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d7320746f2062652072656576616c75617465642e29012d20606974656d73603a20546865206974656d73206f662074686520636f6c6c656374696f6e2077686f7365206465706f736974732077696c6c2062652072656576616c75617465642e002d014e4f54453a205468697320657869737473206173206120626573742d6566666f72742066756e6374696f6e2e20416e79206974656d732077686963682061726520756e6b6e6f776e206f723901696e207468652063617365207468617420746865206f776e6572206163636f756e7420646f6573206e6f7420686176652072657365727661626c652066756e647320746f2070617920666f7220614d016465706f73697420696e637265617365206172652069676e6f7265642e2047656e6572616c6c7920746865206f776e65722069736e277420676f696e6720746f2063616c6c2074686973206f6e206974656d73550177686f7365206578697374696e67206465706f736974206973206c657373207468616e2074686520726566726573686564206465706f73697420617320697420776f756c64206f6e6c7920636f7374207468656d2c78736f2069742773206f66206c6974746c6520636f6e73657175656e63652e00590149742077696c6c207374696c6c2072657475726e20616e206572726f7220696e20746865206361736520746861742074686520636f6c6c656374696f6e20697320756e6b6e6f776e206f7220746865207369676e6572706973206e6f74207065726d697474656420746f2063616c6c2069742e00605765696768743a20604f286974656d732e6c656e28292960486c6f636b5f6974656d5f7472616e73666572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000828c8446973616c6c6f77206675727468657220756e70726976696c65676564207472616e73666572206f6620616e206974656d2e003d014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c642062652074686520467265657a6572206f66207468652060636f6c6c656374696f6e602e00e42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206368616e6765642eb82d20606974656d603a20546865206974656d20746f206265636f6d65206e6f6e2d7472616e7366657261626c652e006c456d69747320604974656d5472616e736665724c6f636b6564602e00385765696768743a20604f2831296050756e6c6f636b5f6974656d5f7472616e73666572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000928a852652d616c6c6f7720756e70726976696c65676564207472616e73666572206f6620616e206974656d2e003d014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c642062652074686520467265657a6572206f66207468652060636f6c6c656374696f6e602e00e42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206368616e6765642ea82d20606974656d603a20546865206974656d20746f206265636f6d65207472616e7366657261626c652e0074456d69747320604974656d5472616e73666572556e6c6f636b6564602e00385765696768743a20604f283129603c6c6f636b5f636f6c6c656374696f6e080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001346c6f636b5f73657474696e67736d050148436f6c6c656374696f6e53657474696e6773000a30d8446973616c6c6f7773207370656369666965642073657474696e677320666f72207468652077686f6c6520636f6c6c656374696f6e2e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00b02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e20746f206265206c6f636b65642eb42d20606c6f636b5f73657474696e6773603a205468652073657474696e677320746f206265206c6f636b65642e001d014e6f74653a206974277320706f737369626c6520746f206f6e6c79206c6f636b2873657429207468652073657474696e672c20627574206e6f7420746f20756e7365742069742e0064456d6974732060436f6c6c656374696f6e4c6f636b6564602e00385765696768743a20604f28312960487472616e736665725f6f776e657273686970080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001246e65775f6f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000b2c844368616e676520746865204f776e6572206f66206120636f6c6c656374696f6e2e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00f42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2077686f7365206f776e65722073686f756c64206265206368616e6765642e09012d20606f776e6572603a20546865206e6577204f776e6572206f66207468697320636f6c6c656374696f6e2e2054686579206d75737420686176652063616c6c656449012020607365745f6163636570745f6f776e6572736869706020776974682060636f6c6c656374696f6e6020696e206f7264657220666f722074686973206f7065726174696f6e20746f20737563636565642e0054456d69747320604f776e65724368616e676564602e00385765696768743a20604f28312960207365745f7465616d100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000118697373756572990501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e00011461646d696e990501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e00011c667265657a6572990501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e000c40d44368616e676520746865204973737565722c2041646d696e20616e6420467265657a6572206f66206120636f6c6c656374696f6e2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468653460636f6c6c656374696f6e602e0051014e6f74653a2062792073657474696e672074686520726f6c6520746f20604e6f6e6560206f6e6c79207468652060466f7263654f726967696e602077696c6c2062652061626c6520746f206368616e676520697464616674657220746f2060536f6d65286163636f756e7429602e00f02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2077686f7365207465616d2073686f756c64206265206368616e6765642eb82d2060697373756572603a20546865206e657720497373756572206f66207468697320636f6c6c656374696f6e2eb02d206061646d696e603a20546865206e65772041646d696e206f66207468697320636f6c6c656374696f6e2ec02d2060667265657a6572603a20546865206e657720467265657a6572206f66207468697320636f6c6c656374696f6e2e0050456d69747320605465616d4368616e676564602e00385765696768743a20604f2831296058666f7263655f636f6c6c656374696f6e5f6f776e6572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000d28844368616e676520746865204f776e6572206f66206120636f6c6c656374696f6e2e00744f726967696e206d7573742062652060466f7263654f726967696e602e00c42d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2eb02d20606f776e6572603a20546865206e6577204f776e6572206f66207468697320636f6c6c656374696f6e2e0054456d69747320604f776e65724368616e676564602e00385765696768743a20604f283129605c666f7263655f636f6c6c656374696f6e5f636f6e666967080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000118636f6e66696769050164436f6c6c656374696f6e436f6e666967466f723c542c20493e000e28884368616e67652074686520636f6e666967206f66206120636f6c6c656374696f6e2e00744f726967696e206d7573742062652060466f7263654f726967696e602e00c42d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2eb82d2060636f6e666967603a20546865206e657720636f6e666967206f66207468697320636f6c6c656374696f6e2e0080456d6974732060436f6c6c656374696f6e436f6e6669674368616e676564602e00385765696768743a20604f2831296040617070726f76655f7472616e73666572100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e0001386d617962655f646561646c696e65110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000f381501417070726f766520616e206974656d20746f206265207472616e7366657272656420627920612064656c6567617465642074686972642d7061727479206163636f756e742e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468651c606974656d602e0045012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f20626520617070726f76656420666f722064656c656761746564207472616e736665722ee42d20606974656d603a20546865206974656d20746f20626520617070726f76656420666f722064656c656761746564207472616e736665722e19012d206064656c6567617465603a20546865206163636f756e7420746f2064656c6567617465207065726d697373696f6e20746f207472616e7366657220746865206974656d2e49012d20606d617962655f646561646c696e65603a204f7074696f6e616c20646561646c696e6520666f722074686520617070726f76616c2e205370656369666965642062792070726f766964696e6720746865d8096e756d626572206f6620626c6f636b732061667465722077686963682074686520617070726f76616c2077696c6c206578706972650090456d69747320605472616e73666572417070726f76656460206f6e20737563636573732e00385765696768743a20604f283129603c63616e63656c5f617070726f76616c0c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e001038e443616e63656c206f6e65206f6620746865207472616e7366657220617070726f76616c7320666f722061207370656369666963206974656d2e00584f726967696e206d757374206265206569746865723a542d207468652060466f72636560206f726967696e3be42d20605369676e656460207769746820746865207369676e6572206265696e6720746865204f776e6572206f662074686520606974656d603b0028417267756d656e74733a3d012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d206f662077686f736520617070726f76616c2077696c6c2062652063616e63656c6c65642e25012d20606974656d603a20546865206974656d206f662074686520636f6c6c656374696f6e206f662077686f736520617070726f76616c2077696c6c2062652063616e63656c6c65642e01012d206064656c6567617465603a20546865206163636f756e74207468617420697320676f696e6720746f206c6f6f736520746865697220617070726f76616c2e0094456d6974732060417070726f76616c43616e63656c6c656460206f6e20737563636573732e00385765696768743a20604f2831296070636c6561725f616c6c5f7472616e736665725f617070726f76616c73080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001134b043616e63656c20616c6c2074686520617070726f76616c73206f662061207370656369666963206974656d2e00584f726967696e206d757374206265206569746865723a542d207468652060466f72636560206f726967696e3be42d20605369676e656460207769746820746865207369676e6572206265696e6720746865204f776e6572206f662074686520606974656d603b0028417267756d656e74733a39012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d206f662077686f736520617070726f76616c732077696c6c20626520636c65617265642e21012d20606974656d603a20546865206974656d206f662074686520636f6c6c656374696f6e206f662077686f736520617070726f76616c732077696c6c20626520636c65617265642e00a4456d6974732060416c6c417070726f76616c7343616e63656c6c656460206f6e20737563636573732e00385765696768743a20604f28312960506c6f636b5f6974656d5f70726f70657274696573100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001346c6f636b5f6d65746164617461200110626f6f6c00013c6c6f636b5f61747472696275746573200110626f6f6c001244e8446973616c6c6f7773206368616e67696e6720746865206d65746164617461206f722061747472696275746573206f6620746865206974656d2e0041014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e506f66207468652060636f6c6c656374696f6e602e00b42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2069662074686520606974656d602e7c2d20606974656d603a20416e206974656d20746f206265206c6f636b65642e0d012d20606c6f636b5f6d65746164617461603a20537065636966696573207768657468657220746865206d657461646174612073686f756c64206265206c6f636b65642e61012d20606c6f636b5f61747472696275746573603a20537065636966696573207768657468657220746865206174747269627574657320696e207468652060436f6c6c656374696f6e4f776e657260206e616d6573706163654c202073686f756c64206265206c6f636b65642e005d014e6f74653a20606c6f636b5f6174747269627574657360206166666563747320746865206174747269627574657320696e207468652060436f6c6c656374696f6e4f776e657260206e616d657370616365206f6e6c792e45015768656e20746865206d65746164617461206f72206174747269627574657320617265206c6f636b65642c20697420776f6e277420626520706f737369626c652074686520756e6c6f636b207468656d2e0074456d69747320604974656d50726f706572746965734c6f636b6564602e00385765696768743a20604f28312960347365745f617474726962757465140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e001358a853657420616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e0001014f726967696e206d757374206265205369676e656420616e64206d75737420636f6e666f726d20746f20746865206e616d6573706163652072756c657365743a3d012d2060436f6c6c656374696f6e4f776e657260206e616d65737061636520636f756c64206265206d6f646966696564206279207468652060636f6c6c656374696f6e602041646d696e206f6e6c793b59012d20604974656d4f776e657260206e616d65737061636520636f756c64206265206d6f6469666965642062792074686520606d617962655f6974656d60206f776e6572206f6e6c792e20606d617962655f6974656d6074202073686f756c642062652073657420696e207468617420636173653b55012d20604163636f756e74284163636f756e7449642960206e616d65737061636520636f756c64206265206d6f646966696564206f6e6c79207768656e2074686520606f726967696e602077617320676976656e20615820207065726d697373696f6e20746f20646f20736f3b00f05468652066756e6473206f6620606f726967696e6020617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613a2d01604174747269627574654465706f73697442617365202b204465706f73697450657242797465202a20286b65792e6c656e202b2076616c75652e6c656e29602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0039012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742e05012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652ecc2d206076616c7565603a205468652076616c756520746f20776869636820746f2073657420746865206174747269627574652e0054456d6974732060417474726962757465536574602e00385765696768743a20604f283129604c666f7263655f7365745f6174747269627574651801187365745f61739c01504f7074696f6e3c543a3a4163636f756e7449643e000128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e001444c0466f7263652d73657420616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e00744f726967696e206d7573742062652060466f7263654f726967696e602e0039014966207468652061747472696275746520616c72656164792065786973747320616e64206974207761732073657420627920616e6f74686572206163636f756e742c20746865206465706f7369749c77696c6c2062652072657475726e656420746f207468652070726576696f7573206f776e65722e00bc2d20607365745f6173603a20416e206f7074696f6e616c206f776e6572206f6620746865206174747269627574652e39012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742e05012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652ecc2d206076616c7565603a205468652076616c756520746f20776869636820746f2073657420746865206174747269627574652e0054456d6974732060417474726962757465536574602e00385765696768743a20604f283129603c636c6561725f617474726962757465100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00153cb0436c65617220616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f6620746865286174747269627574652e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0041012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f20636c6561722e0d012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f20636c6561722e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652e0064456d6974732060417474726962757465436c6561726564602e00385765696768743a20604f283129605c617070726f76655f6974656d5f617474726962757465730c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e0016242d01417070726f7665206974656d2773206174747269627574657320746f206265206368616e67656420627920612064656c6567617465642074686972642d7061727479206163636f756e742e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00a42d2060636f6c6c656374696f6e603a204120636f6c6c656374696f6e206f6620746865206974656d2ea42d20606974656d603a20546865206974656d207468617420686f6c647320617474726962757465732e49012d206064656c6567617465603a20546865206163636f756e7420746f2064656c6567617465207065726d697373696f6e20746f206368616e67652061747472696275746573206f6620746865206974656d2e00bc456d69747320604974656d41747472696275746573417070726f76616c416464656460206f6e20737563636573732e7c63616e63656c5f6974656d5f617474726962757465735f617070726f76616c100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e00011c7769746e6573739d05017c43616e63656c41747472696275746573417070726f76616c5769746e657373001728110143616e63656c207468652070726576696f75736c792070726f766964656420617070726f76616c20746f206368616e6765206974656d277320617474726962757465732e1101416c6c207468652070726576696f75736c7920736574206174747269627574657320627920746865206064656c6567617465602077696c6c2062652072656d6f7665642e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00f42d2060636f6c6c656374696f6e603a20436f6c6c656374696f6e207468617420746865206974656d20697320636f6e7461696e65642077697468696e2ea42d20606974656d603a20546865206974656d207468617420686f6c647320617474726962757465732ee02d206064656c6567617465603a205468652070726576696f75736c7920617070726f766564206163636f756e7420746f2072656d6f76652e00c4456d69747320604974656d41747472696275746573417070726f76616c52656d6f76656460206f6e20737563636573732e307365745f6d657461646174610c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e0018407453657420746865206d6574616461746120666f7220616e206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66207468653460636f6c6c656374696f6e602e005101496620746865206f726967696e206973205369676e65642c207468656e2066756e6473206f66207369676e657220617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613af4604d657461646174614465706f73697442617365202b204465706f73697450657242797465202a20646174612e6c656e602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0039012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742eec2d20606974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e4d012d206064617461603a205468652067656e6572616c20696e666f726d6174696f6e206f662074686973206974656d2e204c696d6974656420696e206c656e6774682062792060537472696e674c696d6974602e0060456d69747320604974656d4d65746164617461536574602e00385765696768743a20604f2831296038636c6561725f6d65746164617461080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640019347c436c65617220746865206d6574616461746120666f7220616e206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66207468653460636f6c6c656374696f6e602e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0041012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f20636c6561722ef42d20606974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f20636c6561722e0070456d69747320604974656d4d65746164617461436c6561726564602e00385765696768743a20604f283129605c7365745f636f6c6c656374696f6e5f6d65746164617461080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001a3c8853657420746865206d6574616461746120666f72206120636f6c6c656374696f6e2e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66447468652060636f6c6c656374696f6e602e005901496620746865206f726967696e20697320605369676e6564602c207468656e2066756e6473206f66207369676e657220617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613af4604d657461646174614465706f73697442617365202b204465706f73697450657242797465202a20646174612e6c656e602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0011012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207570646174652e4d012d206064617461603a205468652067656e6572616c20696e666f726d6174696f6e206f662074686973206974656d2e204c696d6974656420696e206c656e6774682062792060537472696e674c696d6974602e0078456d6974732060436f6c6c656374696f6e4d65746164617461536574602e00385765696768743a20604f2831296064636c6561725f636f6c6c656374696f6e5f6d65746164617461040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001b3090436c65617220746865206d6574616461746120666f72206120636f6c6c656374696f6e2e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66447468652060636f6c6c656374696f6e602e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0025012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206d6574616461746120746f20636c6561722e0088456d6974732060436f6c6c656374696f6e4d65746164617461436c6561726564602e00385765696768743a20604f28312960507365745f6163636570745f6f776e6572736869700401406d617962655f636f6c6c656374696f6e1102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001c28110153657420286f72207265736574292074686520616363657074616e6365206f66206f776e65727368697020666f72206120706172746963756c6172206163636f756e742e0061014f726967696e206d75737420626520605369676e65646020616e6420696620606d617962655f636f6c6c656374696f6e602069732060536f6d65602c207468656e20746865207369676e6572206d757374206861766520614c70726f7669646572207265666572656e63652e0051012d20606d617962655f636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206f776e65727368697020746865207369676e65722069736101202077696c6c696e6720746f206163636570742c206f7220696620604e6f6e65602c20616e20696e6469636174696f6e207468617420746865207369676e65722069732077696c6c696e6720746f20616363657074206e6f5c20206f776e657273686970207472616e73666572616c2e008c456d69747320604f776e657273686970416363657074616e63654368616e676564602e647365745f636f6c6c656374696f6e5f6d61785f737570706c79080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d61785f737570706c7910010c753332001d24e053657420746865206d6178696d756d206e756d626572206f66206974656d73206120636f6c6c656374696f6e20636f756c6420686176652e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66447468652060636f6c6c656374696f6e602e00ec2d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f206368616e67652e11012d20606d61785f737570706c79603a20546865206d6178696d756d206e756d626572206f66206974656d73206120636f6c6c656374696f6e20636f756c6420686176652e00d4456d6974732060436f6c6c656374696f6e4d6178537570706c7953657460206576656e74207768656e207375636365737366756c2e507570646174655f6d696e745f73657474696e6773080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001346d696e745f73657474696e677375050105014d696e7453657474696e67733c42616c616e63654f663c542c20493e2c20426c6f636b4e756d626572466f723c543e2c20543a3a436f6c6c656374696f6e49643e001e2454557064617465206d696e742073657474696e67732e004d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c642062652074686520497373756572506f66207468652060636f6c6c656374696f6e602e00ec2d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f206368616e67652ea42d20606d696e745f73657474696e6773603a20546865206e6577206d696e742073657474696e67732e00f0456d6974732060436f6c6c656374696f6e4d696e7453657474696e67735570646174656460206576656e74207768656e207375636365737366756c2e247365745f7072696365100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011470726963657d05015c4f7074696f6e3c4974656d50726963653c542c20493e3e00014477686974656c69737465645f6275796572990501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e001f2c9453657420286f72207265736574292074686520707269636520666f7220616e206974656d2e00e84f726967696e206d757374206265205369676e656420616e64206d75737420626520746865206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea02d20606974656d603a20546865206974656d20746f207365742074686520707269636520666f722e0d012d20607072696365603a2054686520707269636520666f7220746865206974656d2e205061737320604e6f6e65602c20746f207265736574207468652070726963652ef42d20606275796572603a205265737472696374732074686520627579206f7065726174696f6e20746f2061207370656369666963206163636f756e742e00ec456d69747320604974656d507269636553657460206f6e207375636365737320696620746865207072696365206973206e6f7420604e6f6e65602eec456d69747320604974656d507269636552656d6f76656460206f6e20737563636573732069662074686520707269636520697320604e6f6e65602e206275795f6974656d0c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001246269645f707269636518013c4974656d50726963653c542c20493e002024a8416c6c6f777320746f2062757920616e206974656d206966206974277320757020666f722073616c652e00f84f726967696e206d757374206265205369676e656420616e64206d757374206e6f7420626520746865206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2eac2d20606974656d603a20546865206974656d207468652073656e6465722077616e747320746f206275792ed82d20606269645f7072696365603a20546865207072696365207468652073656e6465722069732077696c6c696e6720746f207061792e0078456d69747320604974656d426f7567687460206f6e20737563636573732e207061795f7469707304011074697073a105019c426f756e6465645665633c4974656d5469704f663c542c20493e2c20543a3a4d6178546970733e00211c5c416c6c6f777320746f207061792074686520746970732e00584f726967696e206d757374206265205369676e65642e00542d206074697073603a20546970732061727261792e0098456d697473206054697053656e7460206f6e20657665727920746970207472616e736665722e2c6372656174655f737761701801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001486d617962655f646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e00012c6d617962655f7072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e0001206475726174696f6e100144426c6f636b4e756d626572466f723c543e002240510152656769737465722061206e65772061746f6d696320737761702c206465636c6172696e6720616e20696e74656e74696f6e20746f2073656e6420616e20606974656d6020696e2065786368616e676520666f72fc60646573697265645f6974656d602066726f6d206f726967696e20746f20746172676574206f6e207468652063757272656e7420626c6f636b636861696e2e4d01546865207461726765742063616e206578656375746520746865207377617020647572696e67207468652073706563696669656420606475726174696f6e60206f6620626c6f636b732028696620736574292ef04164646974696f6e616c6c792c2074686520707269636520636f756c642062652073657420666f7220746865206465736972656420606974656d602e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea82d20606974656d603a20546865206974656d20616e206f776e65722077616e747320746f20676976652eec2d2060646573697265645f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f66207468652064657369726564206974656d2ef42d2060646573697265645f6974656d603a205468652064657369726564206974656d20616e206f776e65722077616e747320746f20726563656976652e61012d20606d617962655f7072696365603a2054686520707269636520616e206f776e65722069732077696c6c696e6720746f20706179206f72207265636569766520666f7220746865206465736972656420606974656d602e49012d20606475726174696f6e603a204120646561646c696e6520666f722074686520737761702e205370656369666965642062792070726f766964696e6720746865206e756d626572206f6620626c6f636b73880961667465722077686963682074686520737761702077696c6c206578706972652e007c456d6974732060537761704372656174656460206f6e20737563636573732e2c63616e63656c5f737761700801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d49640023245843616e63656c20616e2061746f6d696320737761702e00584f726967696e206d757374206265205369676e65642e15014f726967696e206d75737420626520616e206f776e6572206f662074686520606974656d602069662074686520646561646c696e65206861736e277420657870697265642e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea82d20606974656d603a20546865206974656d20616e206f776e65722077616e747320746f20676976652e0084456d69747320605377617043616e63656c6c656460206f6e20737563636573732e28636c61696d5f7377617014013c73656e645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400012473656e645f6974656d100124543a3a4974656d4964000148726563656976655f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130726563656976655f6974656d100124543a3a4974656d49640001347769746e6573735f7072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e00243054436c61696d20616e2061746f6d696320737761702e390154686973206d6574686f6420657865637574657320612070656e64696e6720737761702c2074686174207761732063726561746564206279206120636f756e74657270617274206265666f72652e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00ec2d206073656e645f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f2062652073656e742e8c2d206073656e645f6974656d603a20546865206974656d20746f2062652073656e742e09012d2060726563656976655f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f2062652072656365697665642ea82d2060726563656976655f6974656d603a20546865206974656d20746f2062652072656365697665642ee42d20607769746e6573735f7072696365603a20412070726963652074686174207761732070726576696f75736c7920616772656564206f6e2e007c456d697473206053776170436c61696d656460206f6e20737563636573732e3c6d696e745f7072655f7369676e65640c01246d696e745f64617461ad050168426f783c5072655369676e65644d696e744f663c542c20493e3e0001247369676e6174757265b1050150543a3a4f6666636861696e5369676e61747572650001187369676e6572000130543a3a4163636f756e744964002534c84d696e7420616e206974656d2062792070726f766964696e6720746865207072652d7369676e656420617070726f76616c2e00584f726967696e206d757374206265205369676e65642e005d012d20606d696e745f64617461603a20546865207072652d7369676e656420617070726f76616c207468617420636f6e7369737473206f662074686520696e666f726d6174696f6e2061626f757420746865206974656d2c51012020697473206d657461646174612c20617474726962757465732c2077686f2063616e206d696e742069742028604e6f6e656020666f7220616e796f6e652920616e6420756e74696c207768617420626c6f636b2420206e756d6265722ec82d20607369676e6174757265603a20546865207369676e6174757265206f662074686520606461746160206f626a6563742e39012d20607369676e6572603a2054686520606461746160206f626a6563742773207369676e65722e2053686f756c6420626520616e20497373756572206f662074686520636f6c6c656374696f6e2e0068456d697473206049737375656460206f6e20737563636573732ed4456d69747320604174747269627574655365746020696620746865206174747269627574657320776572652070726f76696465642ed8456d69747320604974656d4d657461646174615365746020696620746865206d6574616461746120776173206e6f7420656d7074792e647365745f617474726962757465735f7072655f7369676e65640c011064617461bd05016c5072655369676e6564417474726962757465734f663c542c20493e0001247369676e6174757265b1050150543a3a4f6666636861696e5369676e61747572650001187369676e6572000130543a3a4163636f756e7449640026340101536574206174747269627574657320666f7220616e206974656d2062792070726f766964696e6720746865207072652d7369676e656420617070726f76616c2e00f84f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f66207468652060646174612e6974656d602e0049012d206064617461603a20546865207072652d7369676e656420617070726f76616c207468617420636f6e7369737473206f662074686520696e666f726d6174696f6e2061626f757420746865206974656d2ccc20206174747269627574657320746f2075706461746520616e6420756e74696c207768617420626c6f636b206e756d6265722ec82d20607369676e6174757265603a20546865207369676e6174757265206f662074686520606461746160206f626a6563742e51012d20607369676e6572603a2054686520606461746160206f626a6563742773207369676e65722e2053686f756c6420626520616e2041646d696e206f662074686520636f6c6c656374696f6e20666f722074686578202060436f6c6c656374696f6e4f776e657260206e616d6573706163652e00c4456d69747320604174747269627574655365746020666f7220656163682070726f7669646564206174747269627574652e1901456d69747320604974656d41747472696275746573417070726f76616c4164646564602069662074686520617070726f76616c207761736e277420736574206265666f72652ea8456d69747320605072655369676e65644174747269627574657353657460206f6e20737563636573732e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e69050c2c70616c6c65745f6e66747314747970657340436f6c6c656374696f6e436f6e6669670c14507269636501182c426c6f636b4e756d626572011030436f6c6c656374696f6e49640110000c012073657474696e67736d050148436f6c6c656374696f6e53657474696e67730001286d61785f737570706c791102012c4f7074696f6e3c7533323e0001346d696e745f73657474696e6773750501b84d696e7453657474696e67733c50726963652c20426c6f636b4e756d6265722c20436f6c6c656374696f6e49643e00006d050c2c70616c6c65745f6e66747314747970657320426974466c616773040454017105000400300144436f6c6c656374696f6e53657474696e67000071050c2c70616c6c65745f6e66747314747970657344436f6c6c656374696f6e53657474696e67000114445472616e7366657261626c654974656d7300010040556e6c6f636b65644d6574616461746100020048556e6c6f636b65644174747269627574657300040044556e6c6f636b65644d6178537570706c790008003c4465706f73697452657175697265640010000075050c2c70616c6c65745f6e667473147479706573304d696e7453657474696e67730c14507269636501182c426c6f636b4e756d626572011030436f6c6c656374696f6e49640110001401246d696e745f74797065790501584d696e74547970653c436f6c6c656374696f6e49643e00011470726963657d0501344f7074696f6e3c50726963653e00012c73746172745f626c6f636b1102014c4f7074696f6e3c426c6f636b4e756d6265723e000124656e645f626c6f636b1102014c4f7074696f6e3c426c6f636b4e756d6265723e00015464656661756c745f6974656d5f73657474696e6773810501304974656d53657474696e6773000079050c2c70616c6c65745f6e667473147479706573204d696e74547970650430436f6c6c656374696f6e49640110010c18497373756572000000185075626c696300010020486f6c6465724f660400100130436f6c6c656374696f6e4964000200007d0504184f7074696f6e04045401180108104e6f6e6500000010536f6d65040018000001000081050c2c70616c6c65745f6e66747314747970657320426974466c61677304045401850500040030012c4974656d53657474696e67000085050c2c70616c6c65745f6e6674731474797065732c4974656d53657474696e6700010c305472616e7366657261626c6500010040556e6c6f636b65644d6574616461746100020048556e6c6f636b6564417474726962757465730004000089050c2c70616c6c65745f6e6674731474797065733844657374726f795769746e65737300000c01386974656d5f6d6574616461746173cc010c7533320001306974656d5f636f6e66696773cc010c75333200012861747472696275746573cc010c75333200008d0504184f7074696f6e0404540191050108104e6f6e6500000010536f6d6504009105000001000091050c2c70616c6c65745f6e6674731474797065732c4d696e745769746e65737308184974656d496401101c42616c616e63650118000801286f776e65645f6974656d110201384f7074696f6e3c4974656d49643e0001286d696e745f70726963657d05013c4f7074696f6e3c42616c616e63653e000095050c2c70616c6c65745f6e667473147479706573284974656d436f6e666967000004012073657474696e6773810501304974656d53657474696e67730000990504184f7074696f6e0404540145040108104e6f6e6500000010536f6d650400450400000100009d050c2c70616c6c65745f6e6674731474797065737c43616e63656c41747472696275746573417070726f76616c5769746e65737300000401486163636f756e745f6174747269627574657310010c7533320000a1050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401a505045300000400a90501185665633c543e0000a5050c2c70616c6c65745f6e6674731474797065731c4974656d5469701030436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010018416d6f756e74011800100128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d496400012072656365697665720001244163636f756e744964000118616d6f756e74180118416d6f756e740000a905000002a50500ad050c2c70616c6c65745f6e667473147479706573345072655369676e65644d696e741430436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010020446561646c696e6501101c42616c616e63650118001c0128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d4964000128617474726962757465731903015c5665633c285665633c75383e2c205665633c75383e293e0001206d6574616461746138011c5665633c75383e0001306f6e6c795f6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000120646561646c696e65100120446561646c696e650001286d696e745f70726963657d05013c4f7074696f6e3c42616c616e63653e0000b105082873705f72756e74696d65384d756c74695369676e617475726500010c1c456432353531390400b5050148656432353531393a3a5369676e61747572650000001c537232353531390400b5050148737232353531393a3a5369676e61747572650001001445636473610400b905014065636473613a3a5369676e617475726500020000b505000003400000000800b905000003410000000800bd050c2c70616c6c65745f6e6674731474797065734c5072655369676e6564417474726962757465731030436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010020446561646c696e65011000140128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d4964000128617474726962757465731903015c5665633c285665633c75383e2c205665633c75383e293e0001246e616d657370616365c10201744174747269627574654e616d6573706163653c4163636f756e7449643e000120646561646c696e65100120446561646c696e650000c1050c4470616c6c65745f706172616d65746572731870616c6c65741043616c6c040454000104347365745f706172616d657465720401246b65795f76616c7565c5050150543a3a52756e74696d65506172616d657465727300001074536574207468652076616c7565206f66206120706172616d657465722e005501546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265206041646d696e4f726967696e6020666f722074686520676976656e20606b6579602e2056616c7565732062658864656c657465642062792073657474696e67207468656d20746f20604e6f6e65602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec505104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d734452756e74696d65506172616d65746572730001043452756e74696d65436f6e6669670400c90501a864796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d657465727300000000c905184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696728506172616d657465727300011064536c617368416d6f756e745065724d617846696c6553697a650800e1020164536c617368416d6f756e745065724d617846696c6553697a6500007d05013c4f7074696f6e3c42616c616e63653e000000585374616b65546f4368616c6c656e6765506572696f640800e50201585374616b65546f4368616c6c656e6765506572696f6400007d05013c4f7074696f6e3c42616c616e63653e00010064436865636b706f696e744368616c6c656e6765506572696f640800e9020164436865636b706f696e744368616c6c656e6765506572696f6400001102014c4f7074696f6e3c426c6f636b4e756d6265723e000200484d696e4368616c6c656e6765506572696f640800ed0201484d696e4368616c6c656e6765506572696f6400001102014c4f7074696f6e3c426c6f636b4e756d6265723e00030000cd050c2c70616c6c65745f7375646f1870616c6c6574144572726f720404540001042c526571756972655375646f0000048053656e646572206d75737420626520746865205375646f206163636f756e742e04684572726f7220666f7220746865205375646f2070616c6c65742ed1050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400a401185665633c543e0000d5050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401d905045300000400dd0501185665633c543e0000d9050c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65743443616e646964617465496e666f08244163636f756e74496401001c42616c616e636501180008010c77686f0001244163636f756e74496400011c6465706f73697418011c42616c616e63650000dd05000002d90500e1050c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144572726f7204045400014444546f6f4d616e7943616e646964617465730000048c5468652070616c6c65742068617320746f6f206d616e792063616e646964617465732e5c546f6f466577456c696769626c65436f6c6c61746f7273000104ac4c656176696e6720776f756c6420726573756c7420696e20746f6f206665772063616e646964617465732e40416c726561647943616e6469646174650002047c4163636f756e7420697320616c726561647920612063616e6469646174652e304e6f7443616e6469646174650003046c4163636f756e74206973206e6f7420612063616e6469646174652e50546f6f4d616e79496e76756c6e657261626c65730004048454686572652061726520746f6f206d616e7920496e76756c6e657261626c65732e4c416c7265616479496e76756c6e657261626c650005048c4163636f756e7420697320616c726561647920616e20496e76756c6e657261626c652e3c4e6f74496e76756c6e657261626c650006047c4163636f756e74206973206e6f7420616e20496e76756c6e657261626c652e5c4e6f4173736f63696174656456616c696461746f7249640007049c4163636f756e7420686173206e6f206173736f6369617465642076616c696461746f722049442e5856616c696461746f724e6f74526567697374657265640008048c56616c696461746f72204944206973206e6f742079657420726567697374657265642e6c496e73657274546f43616e6469646174654c6973744661696c65640009049c436f756c64206e6f7420696e7365727420696e207468652063616e646964617465206c6973742e7452656d6f766546726f6d43616e6469646174654c6973744661696c6564000a04a4436f756c64206e6f742072656d6f76652066726f6d207468652063616e646964617465206c6973742e344465706f736974546f6f4c6f77000b04f44e6577206465706f73697420616d6f756e7420776f756c642062652062656c6f7720746865206d696e696d756d2063616e64696461637920626f6e642e6455706461746543616e6469646174654c6973744661696c6564000c0490436f756c64206e6f7420757064617465207468652063616e646964617465206c6973742e40496e73756666696369656e74426f6e64000d0429014465706f73697420616d6f756e7420697320746f6f206c6f7720746f2074616b652074686520746172676574277320736c6f7420696e207468652063616e646964617465206c6973742e5054617267657449734e6f7443616e646964617465000e042d0154686520746172676574206163636f756e7420746f206265207265706c6163656420696e207468652063616e646964617465206c697374206973206e6f7420612063616e6469646174652e404964656e746963616c4465706f736974000f040d015468652075706461746564206465706f73697420616d6f756e7420697320657175616c20746f2074686520616d6f756e7420616c72656164792072657365727665642e40496e76616c6964556e726573657276650010043d0143616e6e6f74206c6f7765722063616e64696461637920626f6e64207768696c65206f6363757079696e6720612066757475726520636f6c6c61746f7220736c6f7420696e20746865206c6973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ee505000002e90500e9050000040800690400ed0500000408f1053800f1050c1c73705f636f72651863727970746f244b65795479706549640000040048011c5b75383b20345d0000f5050c3870616c6c65745f73657373696f6e1870616c6c6574144572726f7204045400011430496e76616c696450726f6f6600000460496e76616c6964206f776e6572736869702070726f6f662e5c4e6f4173736f63696174656456616c696461746f7249640001049c4e6f206173736f6369617465642076616c696461746f7220494420666f72206163636f756e742e344475706c6963617465644b65790002046452656769737465726564206475706c6963617465206b65792e184e6f4b657973000304a44e6f206b65797320617265206173736f63696174656420776974682074686973206163636f756e742e244e6f4163636f756e7400040419014b65792073657474696e67206163636f756e74206973206e6f74206c6976652c20736f206974277320696d706f737369626c6520746f206173736f6369617465206b6579732e04744572726f7220666f72207468652073657373696f6e2070616c6c65742ef9050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454016d04045300000400fd0501185665633c543e0000fd050000026d04000106084873705f636f6e73656e7375735f736c6f747310536c6f740000040030010c75363400000506000004080106100009060c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401e9010453000004000d06012c42547265655365743c543e00000d060420425472656553657404045401e90100040011060000001106000002e9010015060000021906001906086463756d756c75735f70616c6c65745f78636d705f7175657565584f7574626f756e644368616e6e656c44657461696c730000140124726563697069656e74e901011850617261496400011473746174651d0601344f7574626f756e6453746174650001347369676e616c735f6578697374200110626f6f6c00012c66697273745f696e6465784d03010c7531360001286c6173745f696e6465784d03010c75313600001d06086463756d756c75735f70616c6c65745f78636d705f7175657565344f7574626f756e645374617465000108084f6b0000002453757370656e64656400010000210600000408e9014d03002506086463756d756c75735f70616c6c65745f78636d705f71756575653c5175657565436f6e6669674461746100000c014473757370656e645f7468726573686f6c6410010c75333200013864726f705f7468726573686f6c6410010c753332000140726573756d655f7468726573686f6c6410010c753332000029060c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144572726f7204045400010c384261645175657565436f6e666967000004110153657474696e672074686520717565756520636f6e666967206661696c65642073696e6365206f6e65206f66206974732076616c7565732077617320696e76616c69642e40416c726561647953757370656e6465640001048c54686520657865637574696f6e20697320616c72656164792073757370656e6465642e38416c7265616479526573756d65640002048454686520657865637574696f6e20697320616c726561647920726573756d65642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2d060c2870616c6c65745f78636d1870616c6c65742c5175657279537461747573042c426c6f636b4e756d6265720110010c1c50656e64696e67100124726573706f6e646572d901014456657273696f6e65644c6f636174696f6e00014c6d617962655f6d617463685f71756572696572310601644f7074696f6e3c56657273696f6e65644c6f636174696f6e3e0001306d617962655f6e6f74696679350601404f7074696f6e3c2875382c207538293e00011c74696d656f757410012c426c6f636b4e756d6265720000003c56657273696f6e4e6f7469666965720801186f726967696ed901014456657273696f6e65644c6f636174696f6e00012469735f616374697665200110626f6f6c000100145265616479080120726573706f6e73653d06014456657273696f6e6564526573706f6e7365000108617410012c426c6f636b4e756d62657200020000310604184f7074696f6e04045401d9010108104e6f6e6500000010536f6d650400d9010000010000350604184f7074696f6e0404540139060108104e6f6e6500000010536f6d650400390600000100003906000004080808003d06080c78636d4456657273696f6e6564526573706f6e736500010c08563204008904013076323a3a526573706f6e73650002000856330400b504013076333a3a526573706f6e736500030008563404003101013076343a3a526573706f6e73650004000041060000040810d9010045060000040c3028100049060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014d06045300000400510601185665633c543e00004d0600000408d901100051060000024d060055060c2870616c6c65745f78636d1870616c6c65745456657273696f6e4d6967726174696f6e53746167650001105c4d696772617465537570706f7274656456657273696f6e0000005c4d69677261746556657273696f6e4e6f74696669657273000100504e6f7469667943757272656e745461726765747304005906013c4f7074696f6e3c5665633c75383e3e000200684d696772617465416e644e6f746966794f6c645461726765747300030000590604184f7074696f6e04045401380108104e6f6e6500000010536f6d6504003800000100005d060000040c10000d050061060c2870616c6c65745f78636d1870616c6c65746852656d6f74654c6f636b656446756e6769626c655265636f72640848436f6e73756d65724964656e7469666965720198304d6178436f6e73756d6572730000100118616d6f756e74180110753132380001146f776e6572d901014456657273696f6e65644c6f636174696f6e0001186c6f636b6572d901014456657273696f6e65644c6f636174696f6e000124636f6e73756d657273650601d0426f756e6465645665633c28436f6e73756d65724964656e7469666965722c2075313238292c204d6178436f6e73756d6572733e000065060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540169060453000004006d0601185665633c543e00006906000004089818006d0600000269060071060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017506045300000400790601185665633c543e000075060000040818d9010079060000027506007d060c2870616c6c65745f78636d1870616c6c6574144572726f720404540001602c556e726561636861626c650000085d0154686520646573697265642064657374696e6174696f6e2077617320756e726561636861626c652c2067656e6572616c6c7920626563617573652074686572652069732061206e6f20776179206f6620726f7574696e6718746f2069742e2c53656e644661696c757265000108410154686572652077617320736f6d65206f746865722069737375652028692e652e206e6f7420746f20646f207769746820726f7574696e672920696e2073656e64696e6720746865206d6573736167652ec8506572686170732061206c61636b206f6620737061636520666f7220627566666572696e6720746865206d6573736167652e2046696c74657265640002049c546865206d65737361676520657865637574696f6e206661696c73207468652066696c7465722e48556e776569676861626c654d657373616765000304b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e6044657374696e6174696f6e4e6f74496e7665727469626c65000404dc5468652064657374696e6174696f6e20604c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e14456d707479000504805468652061737365747320746f2062652073656e742061726520656d7074792e3843616e6e6f745265616e63686f720006043501436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468652064657374696e6174696f6e20636861696e2e34546f6f4d616e79417373657473000704c4546f6f206d616e79206173736574732068617665206265656e20617474656d7074656420666f72207472616e736665722e34496e76616c69644f726967696e000804784f726967696e20697320696e76616c696420666f722073656e64696e672e2842616456657273696f6e00090421015468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626520696e7465727072657465642e2c4261644c6f636174696f6e000a08410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e384e6f537562736372697074696f6e000b04bc546865207265666572656e63656420737562736372697074696f6e20636f756c64206e6f7420626520666f756e642e44416c726561647953756273637269626564000c041101546865206c6f636174696f6e20697320696e76616c69642073696e636520697420616c726561647920686173206120737562736372697074696f6e2066726f6d2075732e5843616e6e6f74436865636b4f757454656c65706f7274000d042901436f756c64206e6f7420636865636b2d6f7574207468652061737365747320666f722074656c65706f72746174696f6e20746f207468652064657374696e6174696f6e20636861696e2e284c6f7742616c616e6365000e044101546865206f776e657220646f6573206e6f74206f776e2028616c6c29206f662074686520617373657420746861742074686579207769736820746f20646f20746865206f7065726174696f6e206f6e2e30546f6f4d616e794c6f636b73000f04c0546865206173736574206f776e65722068617320746f6f206d616e79206c6f636b73206f6e207468652061737365742e4c4163636f756e744e6f74536f7665726569676e001004310154686520676976656e206163636f756e74206973206e6f7420616e206964656e7469666961626c6520736f7665726569676e206163636f756e7420666f7220616e79206c6f636174696f6e2e28466565734e6f744d65740011042901546865206f7065726174696f6e207265717569726564206665657320746f20626520706169642077686963682074686520696e69746961746f7220636f756c64206e6f74206d6565742e304c6f636b4e6f74466f756e64001204f4412072656d6f7465206c6f636b20776974682074686520636f72726573706f6e64696e67206461746120636f756c64206e6f7420626520666f756e642e14496e557365001304490154686520756e6c6f636b206f7065726174696f6e2063616e6e6f742073756363656564206265636175736520746865726520617265207374696c6c20636f6e73756d657273206f6620746865206c6f636b2e68496e76616c69644173736574556e6b6e6f776e52657365727665001504f0496e76616c69642061737365742c207265736572766520636861696e20636f756c64206e6f742062652064657465726d696e656420666f722069742e78496e76616c69644173736574556e737570706f72746564526573657276650016044501496e76616c69642061737365742c20646f206e6f7420737570706f72742072656d6f7465206173736574207265736572766573207769746820646966666572656e7420666565732072657365727665732e3c546f6f4d616e7952657365727665730017044901546f6f206d616e7920617373657473207769746820646966666572656e742072657365727665206c6f636174696f6e732068617665206265656e20617474656d7074656420666f72207472616e736665722e604c6f63616c457865637574696f6e496e636f6d706c6574650018047c4c6f63616c2058434d20657865637574696f6e20696e636f6d706c6574652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8106085070616c6c65745f6d6573736167655f717565756524426f6f6b537461746504344d6573736167654f726967696e01e50100180114626567696e10012450616765496e64657800010c656e6410012450616765496e646578000114636f756e7410012450616765496e64657800014072656164795f6e65696768626f757273850601844f7074696f6e3c4e65696768626f7572733c4d6573736167654f726967696e3e3e0001346d6573736167655f636f756e7430010c75363400011073697a6530010c7536340000850604184f7074696f6e0404540189060108104e6f6e6500000010536f6d650400890600000100008906085070616c6c65745f6d6573736167655f7175657565284e65696768626f75727304344d6573736167654f726967696e01e5010008011070726576e50101344d6573736167654f726967696e0001106e657874e50101344d6573736167654f726967696e00008d0600000408e50110009106085070616c6c65745f6d6573736167655f71756575651050616765081053697a650110204865617053697a65000018012472656d61696e696e6710011053697a6500013872656d61696e696e675f73697a6510011053697a6500012c66697273745f696e64657810011053697a65000114666972737410011053697a650001106c61737410011053697a65000110686561709506019c426f756e6465645665633c75382c20496e746f5533323c4865617053697a652c2053697a653e3e000095060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e000099060c5070616c6c65745f6d6573736167655f71756575651870616c6c6574144572726f720404540001242c4e6f745265617061626c65000008490150616765206973206e6f74207265617061626c65206265636175736520697420686173206974656d732072656d61696e696e6720746f2062652070726f63657373656420616e64206973206e6f74206f6c641c656e6f7567682e184e6f50616765000104845061676520746f2062652072656170656420646f6573206e6f742065786973742e244e6f4d657373616765000204a8546865207265666572656e636564206d65737361676520636f756c64206e6f7420626520666f756e642e40416c726561647950726f6365737365640003040101546865206d6573736167652077617320616c72656164792070726f63657373656420616e642063616e6e6f742062652070726f63657373656420616761696e2e18517565756564000404ac546865206d6573736167652069732071756575656420666f722066757475726520657865637574696f6e2e48496e73756666696369656e74576569676874000504190154686572652069732074656d706f726172696c79206e6f7420656e6f7567682077656967687420746f20636f6e74696e756520736572766963696e67206d657373616765732e6054656d706f726172696c79556e70726f6365737361626c65000610a854686973206d6573736167652069732074656d706f726172696c7920756e70726f6365737361626c652e00590153756368206572726f7273206172652065787065637465642c20627574206e6f742067756172616e746565642c20746f207265736f6c7665207468656d73656c766573206576656e7475616c6c79207468726f756768247265747279696e672e2c517565756550617573656400070cec5468652071756575652069732070617573656420616e64206e6f206d6573736167652063616e2062652065786563757465642066726f6d2069742e001d01546869732063616e206368616e676520617420616e792074696d6520616e64206d6179207265736f6c766520696e20746865206675747572652062792072652d747279696e672e4c526563757273697665446973616c6c6f7765640008043101416e6f746865722063616c6c20697320696e2070726f677265737320616e64206e6565647320746f2066696e697368206265666f726520746869732063616c6c2063616e2068617070656e2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e9d0600000408a1061000a1060c6070616c6c65745f73746f726167655f70726f7669646572731474797065733c53746f7261676550726f7669646572040454000108544261636b757053746f7261676550726f76696465720400a50601604261636b757053746f7261676550726f76696465723c543e0000004c4d61696e53746f7261676550726f76696465720400a90601584d61696e53746f7261676550726f76696465723c543e00010000a5060c6070616c6c65745f73746f726167655f70726f766964657273147479706573544261636b757053746f7261676550726f76696465720404540000200120636170616369747930014853746f7261676544617461556e69743c543e00013463617061636974795f7573656430014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000110726f6f743401544d65726b6c655061747269636961526f6f743c543e0001506c6173745f63617061636974795f6368616e6765100144426c6f636b4e756d626572466f723c543e0001346f776e65725f6163636f756e74000130543a3a4163636f756e74496400013c7061796d656e745f6163636f756e74000130543a3a4163636f756e74496400014472657075746174696f6e5f77656967687410015c52657075746174696f6e576569676874547970653c543e0000a9060c6070616c6c65745f73746f726167655f70726f7669646572731474797065734c4d61696e53746f7261676550726f7669646572040454000020011c6275636b657473ad0601284275636b6574733c543e000120636170616369747930014853746f7261676544617461556e69743c543e00013463617061636974795f7573656430014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e0001506c6173745f63617061636974795f6368616e6765100144426c6f636b4e756d626572466f723c543e0001346f776e65725f6163636f756e74000130543a3a4163636f756e74496400013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640000ad060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401b106045300000400b50601185665633c543e0000b1060c6070616c6c65745f73746f726167655f70726f766964657273147479706573184275636b65740404540000180110726f6f743401544d65726b6c655061747269636961526f6f743c543e00011c757365725f6964000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e00011c70726976617465200110626f6f6c000150726561645f6163636573735f67726f75705f6964110201704f7074696f6e3c543a3a5265616441636365737347726f757049643e00011073697a6530014853746f7261676544617461556e69743c543e0000b506000002b10600b9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000bd060c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c6574144572726f7204045400016c44416c72656164795265676973746572656400000471014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020617320612053502062757420697320616c726561647920726567697374657265642061732061204d5350206f72204253502e485369676e55704e6f7452657175657374656400010459014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e207570207468617420776173206e6f74207265717565737465642070726576696f75736c792e505369676e55705265717565737450656e64696e670002048d014572726f72207468726f776e207768656e2061207573657220747269657320746f207265717565737420746f207369676e207570207768656e20697420616c7265616479206861732061207369676e20757020726571756573742070656e64696e672e384e6f4d756c7469416464726573730003040d014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020776974686f757420616e79206d756c7469616464726573732e4c496e76616c69644d756c74694164647265737300040491014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020617320612053502062757420616e79206f66207468652070726f7669646564206d756c746961646472657373657320697320696e76616c69642e3453746f72616765546f6f4c6f7700050401024572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e207570206f72206368616e67652069747320636170616369747920746f2073746f7265206c6573732073746f72616765207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d652e404e6f74456e6f75676842616c616e636500060431024572726f72207468726f776e207768656e2061207573657220646f6573206e6f74206861766520656e6f7567682062616c616e636520746f2070617920746865206465706f736974207468617420697420776f756c6420696e637572206279207369676e696e672075702061732061205350206f72206368616e67696e67206974732063617061636974792e4443616e6e6f74486f6c644465706f736974000704f9014572726f72207468726f776e207768656e207468652072756e74696d652063616e6e6f7420686f6c6420746865207265717569726564206465706f7369742066726f6d20746865206163636f756e7420746f2072656769737465722069742061732061205350206f72206368616e6765206974732063617061636974792e4453746f726167655374696c6c496e55736500080439014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e206f6666206173206120535020627574207374696c6c2068617320757365642073746f726167652e5452616e646f6d6e6573734e6f7456616c69645965740009048d014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e20757020627574207468652072616e646f6d6e65737320697320746f6f20667265736820746f2062652075736564207965742e505369676e55705265717565737445787069726564000a048d014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e2075702062757420746f6f206d7563682074696d6520686173207061737365642073696e63652074686520726571756573742e784e657743617061636974794c6573735468616e5573656453746f72616765000b0451014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f206c657373207468616e2069747320757365642073746f726167652e804e65774361706163697479457175616c7343757272656e744361706163697479000c045d014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f207468652073616d652076616c756520697420616c7265616479206861732e544e6577436170616369747943616e7442655a65726f000d04c9014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f207a65726f20287468657265206172652073706563696669632065787472696e7369637320746f207369676e206f66662061732061205350292e4c4e6f74456e6f75676854696d65506173736564000e04d9014572726f72207468726f776e207768656e206120535020747269657320746f206368616e6765206974732063617061636974792062757420697420686173206e6f74206265656e20656e6f7567682074696d652073696e636520746865206c6173742074696d65206974206368616e6765642069742e944e65775573656443617061636974794578636565647353746f726167654361706163697479000f04c5014572726f72207468726f776e207768656e206120535020747269657320746f206368616e6765206974732063617061636974792062757420746865206e6577206361706163697479206973206e6f7420656e6f75676820746f2073746f72652074686520757365642073746f726167652e344e6f745265676973746572656400100465014572726f72207468726f776e207768656e2061207573657220747269657320746f20696e746572616374206173206120535020627574206973206e6f7420726567697374657265642061732061204d5350206f72204253502e204e6f55736572496400110431014572726f72207468726f776e207768656e20747279696e6720746f20676574206120726f6f742066726f6d2061204d535020776974686f75742070617373696e67206120557365722049442e284e6f4275636b6574496400120439014572726f72207468726f776e207768656e20747279696e6720746f20676574206120726f6f742066726f6d2061204d535020776974686f75742070617373696e672061204275636b65742049442e6c537052656769737465726564427574446174614e6f74466f756e64001304d1014572726f72207468726f776e207768656e206120757365722068617320612053502049442061737369676e656420746f2069742062757420746865205350206461746120646f6573206e6f7420657869737420696e2073746f726167652028496e636f6e73697374656e6379206572726f72292e384275636b65744e6f74466f756e64001404d84572726f72207468726f776e207768656e2061206275636b6574204944206973206e6f7420666f756e6420696e2073746f726167652e4c4275636b6574416c7265616479457869737473001504e04572726f72207468726f776e207768656e2061206275636b657420494420616c72656164792065786973747320696e2073746f726167652e5c417070656e644275636b6574546f4d73704661696c656400160445014572726f72207468726f776e207768656e2061206275636b657420494420636f756c64206e6f7420626520616464656420746f20746865206c697374206f66206275636b657473206f662061204d53502e5050726f76696465724e6f74536c61736861626c650017043d014572726f72207468726f776e207768656e20616e20617474656d707420776173206d61646520746f20736c61736820616e20756e736c61736861626c652053746f726167652050726f76696465722e68496e76616c6964456e636f64656446696c654d6574616461746100180475014572726f72207468726f776e207768656e206661696c696e6720746f206465636f646520746865206d657461646174612066726f6d206120726563656976656420747269652076616c75652074686174207761732072656d6f7665642e5c496e76616c6964456e636f6465644163636f756e74496400190451014572726f72207468726f776e207768656e206661696c696e6720746f206465636f646520746865206f776e6572204163636f756e742049442066726f6d20746865207265636569766564206d657461646174612e545061796d656e7453747265616d4e6f74466f756e64001a0421014572726f72207468726f776e207768656e20747279696e6720746f207570646174652061207061796d656e742073747265616d207468617420646f6573206e6f742065786973742e044901546865206572726f727320746861742063616e206265207468726f776e20627920746869732070616c6c657420746f20696e666f726d2075736572732061626f757420776861742077656e742077726f6e67c1060c4870616c6c65745f66696c655f73797374656d1474797065735853746f72616765526571756573744d6574616461746104045400003001307265717565737465645f61741001345469636b4e756d6265723c543e0001146f776e6572000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e00010c6d7370c50601804f7074696f6e3c2850726f76696465724964466f723c543e2c20626f6f6c293e000134757365725f706565725f696473f5010128506565724964733c543e00013c646174615f7365727665725f737073cd0601e4426f756e6465645665633c50726f76696465724964466f723c543e2c204d61784273707350657253746f72616765526571756573743c543e3e000134627370735f72657175697265641001605265706c69636174696f6e546172676574547970653c543e000138627370735f636f6e6669726d65641001605265706c69636174696f6e546172676574547970653c543e000140627370735f766f6c756e7465657265641001605265706c69636174696f6e546172676574547970653c543e0000c50604184f7074696f6e04045401c9060108104e6f6e6500000010536f6d650400c9060000010000c90600000408342000cd060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000d10600000408343400d5060c4870616c6c65745f66696c655f73797374656d1474797065736853746f7261676552657175657374427370734d657461646174610404540000040124636f6e6669726d6564200110626f6f6c0000d9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000dd060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e106045300000400e50601185665633c543e0000e10600000408003400e506000002e10600e9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401d106045300000400ed0601185665633c543e0000ed06000002d10600f1060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401d106045300000400ed0601185665633c543e0000f50600000408103000f9060c4870616c6c65745f66696c655f73797374656d147479706573644d6f76654275636b6574526571756573744d657461646174610404540000040124726571756573746572000130543a3a4163636f756e7449640000fd060c4870616c6c65745f66696c655f73797374656d1870616c6c6574144572726f720404540001f47c53746f7261676552657175657374416c726561647952656769737465726564000004d853746f72616765207265717565737420616c7265616479207265676973746572656420666f722074686520676976656e2066696c652e5853746f72616765526571756573744e6f74466f756e64000104c853746f726167652072657175657374206e6f74207265676973746572656420666f722074686520676976656e2066696c652e6053746f72616765526571756573744e6f745265766f6b656400020415014f7065726174696f6e206e6f7420616c6c6f776564207768696c65207468652073746f726167652072657175657374206973206e6f74206265696e67207265766f6b65642e5053746f7261676552657175657374457869737473000304dc4f7065726174696f6e206e6f7420616c6c6f776564207768696c65207468652073746f726167652072657175657374206578697374732e745265706c69636174696f6e54617267657443616e6e6f7442655a65726f000404885265706c69636174696f6e207461726765742063616e6e6f74206265207a65726f2e644273707352657175697265644578636565647354617267657400050411014253507320726571756972656420666f722073746f7261676520726571756573742063616e6e6f742065786365656420746865206d6178696d756d20616c6c6f7765642e1c4e6f7441427370000604544163636f756e74206973206e6f742061204253502e1c4e6f74414d7370000704544163636f756e74206973206e6f742061204d53502e184e6f74415370000804504163636f756e74206973206e6f7420612053502e444273704e6f74566f6c756e746565726564000904c042535020686173206e6f7420766f6c756e74656572656420746f2073746f72652074686520676976656e2066696c652e3c4273704e6f74436f6e6669726d6564000a04b442535020686173206e6f7420636f6e6669726d65642073746f72696e672074686520676976656e2066696c652e4c427370416c7265616479436f6e6669726d6564000b04c44253502068617320616c726561647920636f6e6669726d65642073746f72696e672074686520676976656e2066696c652e8c53746f726167655265717565737442737073526571756972656446756c66696c6c6564000c04f44e756d626572206f66204253507320726571756972656420666f722073746f72616765207265717565737420686173206265656e20726561636865642e54427370416c7265616479566f6c756e746565726564000d04c042535020616c726561647920766f6c756e74656572656420746f2073746f72652074686520676976656e2066696c652e74496e73756666696369656e74417661696c61626c654361706163697479000e04ec535020646f6573206e6f74206861766520656e6f7567682073746f7261676520636170616369747920746f2073746f7265207468652066696c652ea0556e65787065637465644e756d6265724f6652656d6f766564566f6c756e74656572656442737073000f0485014e756d626572206f662072656d6f766564204253507320766f6c756e7465657265642066726f6d2073746f7261676520726571756573742070726566697820646964206e6f74206d6174636820746865206578706563746564206e756d6265722e9053746f7261676552657175657374457870697265644e6f536c6f74417661696c61626c6500100431014e6f20736c6f7420617661696c61626c6520666f756e6420696e20626c6f636b7320746f20696e736572742073746f7261676520726571756573742065787069726174696f6e2074696d652e6c53746f72616765526571756573744e6f74417574686f72697a6564001104b44e6f7420617574686f72697a656420746f2064656c657465207468652073746f7261676520726571756573742e544d6178426c6f636b4e756d6265725265616368656400120859014572726f72206372656174656420696e20323032342e20496620796f752073656520746869732c20796f75206172652077656c6c206265796f6e64207468652073696e67756c617269747920616e642073686f756c648070726f6261626c792073746f70207573696e6720746869732070616c6c65742e444661696c6564546f456e636f6465427370001304844661696c656420746f20656e636f64652042535020696420617320736c6963652e644661696c6564546f456e636f646546696e6765727072696e74001404984661696c656420746f20656e636f64652066696e6765727072696e7420617320736c6963652e5c4661696c6564546f4465636f64655468726573686f6c640015046c4661696c656420746f206465636f6465207468726573686f6c642e3841626f76655468726573686f6c640016049042535020646964206e6f742073756363656564207468726573686f6c6420636865636b2e605468726573686f6c6441726974686d657469634572726f72001704a841726974686d65746963206572726f7220696e207468726573686f6c642063616c63756c6174696f6e2e504661696c656454797065436f6e76657273696f6e001804904661696c656420746f20636f6e7665727420746f207072696d697469766520747970652e344469766964656442795a65726f0019043044697669646564206279203068496d706f737369626c654661696c6564546f47657456616c7565001a04c44661696c656420746f206765742076616c7565207768656e206a75737420636865636b656420697420657869737465642e484275636b657449734e6f7450726976617465001b041d014275636b6574206973206e6f7420707269766174652e2043616c6c20607570646174655f6275636b65745f707269766163796020746f206d616b6520697420707269766174652e384275636b65744e6f74466f756e64001c04544275636b657420646f6573206e6f74206578697374384e6f744275636b65744f776e6572001d0411014f7065726174696f6e206661696c6564206265636175736520746865206163636f756e74206973206e6f7420746865206f776e6572206f6620746865206275636b65742e5050726f7669646572526f6f744e6f74466f756e64001e047c526f6f74206f66207468652070726f7669646572206e6f7420666f756e642e6445787065637465644e6f6e496e636c7573696f6e50726f6f66001f0415014661696c656420746f207665726966792070726f6f663a20726571756972656420746f2070726f7669646520612070726f6f66206f66206e6f6e2d696e636c7573696f6e2e584578706563746564496e636c7573696f6e50726f6f6600200405014661696c656420746f207665726966792070726f6f663a20726571756972656420746f2070726f7669646520612070726f6f66206f6620696e636c7573696f6e2e58496e76616c696446696c654b65794d65746164617461002104c84d6574616461746120646f6573206e6f7420636f72726573706f6e6420746f2065787065637465642066696c65206b65792e5c5468726573686f6c6442656c6f774173796d70746f7465002204d0425350732061737369676e6d656e74207468726573686f6c642063616e6e6f742062652062656c6f77206173796d70746f74652e304e6f7446696c654f776e6572002304d4556e617574686f72697a6564206f7065726174696f6e2c207369676e657220646f6573206e6f74206f776e207468652066696c652e7446696c654b6579416c726561647950656e64696e6744656c6574696f6e0024048846696c65206b657920616c72656164792070656e64696e672064656c6574696f6e2e944d61785573657250656e64696e6744656c6574696f6e526571756573747352656163686564002504d44d6178206e756d626572206f6620757365722070656e64696e672064656c6574696f6e20726571756573747320726561636865642e4c4d73704e6f7453746f72696e674275636b6574002604f8556e617574686f72697a6564206f7065726174696f6e2c207369676e6572206973206e6f7420616e204d5350206f6620746865206275636b65742069642e6446696c654b65794e6f7450656e64696e6744656c6574696f6e002704c046696c65206b6579206e6f7420666f756e6420696e2070656e64696e672064656c6574696f6e2072657175657374732e5046696c6553697a6543616e6e6f7442655a65726f0028046446696c652073697a652063616e6e6f74206265207a65726f2e6c4e6f476c6f62616c52657075746174696f6e576569676874536574002904804e6f20676c6f62616c2072657075746174696f6e20776569676874207365742e704d6178696d756d5468726573686f6c6443616e6e6f7442655a65726f002a04844d6178696d756d207468726573686f6c642063616e6e6f74206265207a65726f2e9c5469636b52616e6765546f4d6178696d756d5468726573686f6c6443616e6e6f7442655a65726f002b04bc5469636b2072616e676520746f206d6178696d756d207468726573686f6c642063616e6e6f74206265207a65726f2e8450656e64696e6753746f7053746f72696e67526571756573744e6f74466f756e64002c049c50656e64696e672073746f702073746f72696e672072657175657374206e6f7420666f756e642e7c4d696e57616974466f7253746f7053746f72696e674e6f7452656163686564002d0479014d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e207468652072657175657374206f70656e696e6720616e64206265696e672061626c6520746f20636f6e6669726d206974206e6f7420726561636865642e9850656e64696e6753746f7053746f72696e6752657175657374416c7265616479457869737473002e04b050656e64696e672073746f702073746f72696e67207265717565737420616c7265616479206578697374732e40557365724e6f74496e736f6c76656e74002f04a9014120535020747269656420746f2073746f702073746f72696e672066696c65732066726f6d2061207573657220746861742077617320737570706f7365646c7920696e736f6c76656e742c20627574207468652075736572206973206e6f7420696e736f6c76656e742e384e6f7453656c65637465644d7370003004a101546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f726167652072657175657374206973206e6f7420746865206f6e652073656c656374656420746f2073746f72652069742e4c4d7370416c7265616479436f6e6669726d6564003104ad01546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f72616765207265717565737420746861742069742068617320616c726561647920636f6e6669726d656420746f2073746f72652e4452657175657374576974686f75744d73700032049901546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f726167652072657175657374207468617420646f6573206e6f7420686176652061204d53502061737369676e65642e5c4d7370416c726561647953746f72696e674275636b657400330498546865204d535020697320616c72656164792073746f72696e6720746865206275636b65742e644d6f76654275636b6574526571756573744e6f74466f756e64003404a44d6f7665206275636b65742072657175657374206e6f7420666f756e6420696e2073746f726167652e484275636b657449734265696e674d6f766564003504cc416374696f6e206e6f7420616c6c6f776564207768696c6520746865206275636b6574206973206265696e67206d6f7665642e50427370416c726561647944617461536572766572003604e442535020697320616c7265616479206120646174612073657276657220666f7220746865206d6f7665206275636b657420726571756573742e5842737044617461536572766572734578636565646564003704f4546f6f206d616e7920726567697374657265642064617461207365727665727320666f7220746865206d6f7665206275636b657420726571756573742e7c46696c654d6574616461746150726f63657373696e67517565756546756c6c003804910154686520626f756e64656420766563746f72207468617420686f6c64732066696c65206d6574616461746120746f2070726f636573732069742069732066756c6c206275742074686572652773207374696c6c206d6f726520746f2070726f636573732e54546f6f4d616e794261746368526573706f6e73657300390490546f6f206d616e7920626174636820726573706f6e73657320746f2070726f636573732e78546f6f4d616e7953746f7261676552657175657374526573706f6e736573003a048c546f6f206d616e792073746f72616765207265717565737420726573706f6e7365732e68496e76616c69644275636b6574496446696c654b657950616972003b049c4275636b657420696420616e642066696c65206b6579207061697220697320696e76616c69642e84496e636f6e73697374656e7453746174654b6579416c7265616479457869737473003c04c44b657920616c72656164792065786973747320696e206d617070696e67207768656e2069742073686f756c64206e6f742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e01070000040810340005070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e000009070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454018d02045300000400990201185665633c543e00000d070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401340453000004001107012c42547265655365743c543e000011070420425472656553657404045401340004002d0200000015070c3473705f61726974686d65746963287065725f7468696e67731c50657262696c6c0000040010010c753332000019070c5070616c6c65745f70726f6f66735f6465616c65721870616c6c6574144572726f720404540001582c4e6f7450726f76696465720000083847656e6572616c206572726f7273c45468652070726f6f66207375626d6974746572206973206e6f74206120726567697374657265642050726f76696465722e5c4368616c6c656e67657351756575654f766572666c6f7700010c70606368616c6c656e6765602065787472696e736963206572726f72730901546865204368616c6c656e67657351756575652069732066756c6c2e204e6f206d6f7265206d616e75616c206368616c6c656e6765732063616e206265206d616465e4756e74696c20736f6d65206f6620746865206368616c6c656e67657320696e207468652071756575652061726520646973706174636865642e7c5072696f726974794368616c6c656e67657351756575654f766572666c6f770002083101546865205072696f726974794368616c6c656e67657351756575652069732066756c6c2e204e6f206d6f7265207072696f72697479206368616c6c656e6765732063616e206265206d616465e4756e74696c20736f6d65206f6620746865206368616c6c656e67657320696e207468652071756575652061726520646973706174636865642e3c4665654368617267654661696c6564000304e05468652066656520666f72207375626d697474696e672061206368616c6c656e676520636f756c64206e6f7420626520636861726765642e38456d7074794b657950726f6f66730004087c607375626d69745f70726f6f66602065787472696e736963206572726f727388546865726520617265206e6f206b65792070726f6f6673207375626d69747465642e5050726f7669646572526f6f744e6f74466f756e64000504b454686520726f6f7420666f72207468652050726f766964657220636f756c64206e6f7420626520666f756e642e205a65726f526f6f7400060ce850726f7669646572206973207375626d697474696e6720612070726f6f66207768656e207468657920686176652061207a65726f20726f6f742e390150726f7669646572732077697468207a65726f20726f6f747320617265206e6f742070726f766964696e6720616e7920736572766963652c20736f20746865792073686f756c64206e6f74206265487375626d697474696e672070726f6f66732e704e6f5265636f72644f664c6173745375626d697474656450726f6f660007142d0150726f7669646572206973207375626d697474696e6720612070726f6f6620627574207468657265206973206e6f207265636f7264206f6620746865206c617374207469636b2074686579587375626d697474656420612070726f6f6620666f722e390150726f7669646572732077686f2061726520726571756972656420746f207375626d69742070726f6f66732073686f756c6420616c7761797320686176652061207265636f7264206f662074686535016c617374207469636b2074686579207375626d697474656420612070726f6f6620666f722c206f7468657277697365206974206d65616e73207468657920686176656e277420737461727465648c70726f766964696e67207365727669636520666f7220616e792075736572207965742e5450726f76696465725374616b654e6f74466f756e64000804985468652070726f7669646572207374616b6520636f756c64206e6f7420626520666f756e642e245a65726f5374616b65000904dc50726f7669646572206973207375626d697474696e6720612070726f6f6620627574207468656972207374616b65206973207a65726f2e605374616b65436f756c644e6f744265436f6e766572746564000a081101546865207374616b65642062616c616e6365206f66207468652050726f766964657220636f756c64206e6f7420626520636f6e76657274656420746f206075313238602e5901546869732073686f756c64206e6f7420626520706f737369626c652c20617320746865206042616c616e63656020747970652073686f756c6420626520616e20756e7369676e656420696e746567657220747970652e604368616c6c656e6765735469636b4e6f7452656163686564000b04e050726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b20696e20746865206675747572652e504368616c6c656e6765735469636b546f6f4f6c64000c08510150726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b206265666f726520746865206c617374207469636b20746869732070616c6c6574207265676973746572733c6368616c6c656e67657320666f722e544368616c6c656e6765735469636b546f6f4c617465000d08450150726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b20746f6f206c6174652c20692e652e207468617420746865206368616c6c656e676573207469636b450169732067726561746572206f7220657175616c207468616e20606368616c6c656e6765735f7469636b60202b2060543a3a4368616c6c656e67655469636b73546f6c6572616e63653a3a6765742829602e30536565644e6f74466f756e64000e0ca4546865207365656420666f7220746865207469636b20636f756c64206e6f7420626520666f756e642e5101546869732073686f756c64206e6f7420626520706f737369626c6520666f722061207469636b2077697468696e2074686520604368616c6c656e6765486973746f72794c656e677468602072616e67652c20617301017365656473206172652067656e65726174656420666f7220616c6c207469636b732c20616e642073746f7265642077697468696e20746869732072616e67652e70436865636b706f696e744368616c6c656e6765734e6f74466f756e64000f0ca4436865636b706f696e74206368616c6c656e676573206e6f7420666f756e6420696e20626c6f636b2e5d01546869732073686f756c64206f6e6c7920626520706f737369626c6520696620605469636b546f436865636b706f696e744368616c6c656e676573602069732064657265666572656e63656420666f722061207469636b7874686174206973206e6f74206120636865636b706f696e74207469636b2e74466f7265737450726f6f66566572696669636174696f6e4661696c656400100cd854686520666f726573742070726f6f66207375626d6974746564206279207468652050726f766964657220697320696e76616c69642e49015468697320636f756c642062652062656361757365207468652070726f6f66206973206e6f742076616c696420666f722074686520726f6f742c206f722062656361757365207468652070726f6f662069739c6e6f742073756666696369656e7420666f7220746865206368616c6c656e676573206d6164652e404b657950726f6f664e6f74466f756e6400110861015468657265206973206174206c65617374206f6e65206b65792070726f76656e20696e2074686520666f726573742070726f6f662c207468617420646f6573206e6f742068617665206120636f72726573706f6e64696e67286b65792070726f6f662e684b657950726f6f66566572696669636174696f6e4661696c656400120cc441206b65792070726f6f66207375626d6974746564206279207468652050726f766964657220697320696e76616c69642e6d015468697320636f756c642062652062656361757365207468652070726f6f66206973206e6f742076616c696420666f722074686520726f6f74206f662074686174206b65792c206f722062656361757365207468652070726f6f66a86973206e6f742073756666696369656e7420666f7220746865206368616c6c656e676573206d6164652e484661696c6564546f4170706c7944656c7461001304dc4661696c656420746f206170706c792064656c746120746f2074686520666f726573742070726f6f66207061727469616c20747269652e944661696c6564546f55706461746550726f766964657241667465724b657952656d6f76616c001404ec4661696c656420746f20757064617465207468652070726f76696465722061667465722061206b65792072656d6f76616c206d75746174696f6e2e6c546f6f4d616e7956616c696450726f6f665375626d6974746572730015044501546865206c696d6974206f662050726f76696465727320746861742063616e207375626d697420612070726f6f6620696e20612073696e676c65207469636b20686173206265656e20726561636865642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e1d070000040834100021070000040834000025070c5870616c6c65745f7061796d656e745f73747265616d73147479706573584669786564526174655061796d656e7453747265616d04045400001001107261746518013042616c616e63654f663c543e0001446c6173745f636861726765645f7469636b100144426c6f636b4e756d626572466f723c543e000130757365725f6465706f73697418013042616c616e63654f663c543e0001446f75745f6f665f66756e64735f7469636b110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000029070c5870616c6c65745f7061796d656e745f73747265616d731474797065736044796e616d6963526174655061796d656e7453747265616d040454000010013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e00017470726963655f696e6465785f7768656e5f6c6173745f6368617267656418013042616c616e63654f663c543e000130757365725f6465706f73697418013042616c616e63654f663c543e0001446f75745f6f665f66756e64735f7469636b110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e00002d070c5870616c6c65745f7061796d656e745f73747265616d731474797065736850726f76696465724c61737443686172676561626c65496e666f04045400000801506c6173745f63686172676561626c655f7469636b100144426c6f636b4e756d626572466f723c543e00012c70726963655f696e64657818013042616c616e63654f663c543e000031070c5870616c6c65745f7061796d656e745f73747265616d731870616c6c6574144572726f72040454000140685061796d656e7453747265616d416c726561647945786973747300000469014572726f72207468726f776e207768656e20612075736572206f6620746869732070616c6c657420747269657320746f206164642061207061796d656e742073747265616d207468617420616c7265616479206578697374732e545061796d656e7453747265616d4e6f74466f756e64000104bd014572726f72207468726f776e207768656e20612075736572206f6620746869732070616c6c657420747269657320746f207570646174652c2072656d6f7665206f72206368617267652061207061796d656e742073747265616d207468617420646f6573206e6f742065786973742e304e6f744150726f766964657200020471014572726f72207468726f776e207768656e2061207573657220747269657320746f206368617267652061207061796d656e742073747265616d20616e642069742773206e6f74206120726567697374657265642050726f76696465726850726f7669646572496e636f6e73697374656e63794572726f7200030435014572726f72207468726f776e207768656e206661696c696e6720746f2067657420746865207061796d656e74206163636f756e74206f66206120726567697374657265642050726f76696465724443616e6e6f74486f6c644465706f736974000404a9014572726f72207468726f776e207768656e207468652073797374656d2063616e277420686f6c642066756e64732066726f6d2074686520557365722061732061206465706f73697420666f72206372656174696e672061206e6577207061796d656e742073747265616d5055706461746552617465546f53616d655261746500050495014572726f72207468726f776e207768656e20747279696e6720746f20757064617465207468652072617465206f6620612066697865642d72617465207061796d656e742073747265616d20746f207468652073616d652072617465206173206265666f726560557064617465416d6f756e74546f53616d65416d6f756e74000604d1014572726f72207468726f776e207768656e20747279696e6720746f207570646174652074686520616d6f756e742070726f7669646564206f6620612064796e616d69632d72617465207061796d656e742073747265616d20746f207468652073616d6520616d6f756e74206173206265666f7265385261746543616e7442655a65726f000704bd024572726f72207468726f776e207768656e20747279696e6720746f206372656174652061206e65772066697865642d72617465207061796d656e742073747265616d207769746820726174652030206f7220757064617465207468652072617465206f6620616e206578697374696e67206f6e6520746f2030202873686f756c64207573652072656d6f76655f66697865645f726174655f7061796d656e745f73747265616d20696e73746561642960416d6f756e7450726f766964656443616e7442655a65726f00080425034572726f72207468726f776e207768656e20747279696e6720746f206372656174652061206e65772064796e616d69632d72617465207061796d656e742073747265616d207769746820616d6f756e742070726f76696465642030206f72207570646174652074686520616d6f756e742070726f7669646564206f6620616e206578697374696e67206f6e6520746f2030202873686f756c64207573652072656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d20696e737465616429904c61737443686172676564477265617465725468616e4c61737443686172676561626c6500090425024572726f72207468726f776e207768656e20746865207469636b206e756d626572206f66207768656e20746865207061796d656e742073747265616d20776173206c61737420636861726765642069732067726561746572207468616e20746865207469636b206e756d626572206f6620746865206c6173742063686172676561626c65207469636b80496e76616c69644c61737443686172676561626c65426c6f636b4e756d626572000a04bd024572726f72207468726f776e207768656e20746865206e6577206c6173742063686172676561626c65207469636b206e756d626572207468617420697320747279696e6720746f206265207365742069732067726561746572207468616e207468652063757272656e74207469636b206e756d626572206f7220736d616c6c6572207468616e207468652070726576696f7573206c6173742063686172676561626c65207469636b206e756d6265727c496e76616c69644c61737443686172676561626c655072696365496e646578000b04bd024572726f72207468726f776e207768656e20746865206e6577206c6173742063686172676561626c6520707269636520696e646578207468617420697320747279696e6720746f206265207365742069732067726561746572207468616e207468652063757272656e7420707269636520696e646578206f7220736d616c6c6572207468616e207468652070726576696f7573206c6173742063686172676561626c6520707269636520696e646578384368617267654f766572666c6f77000c046d014572726f72207468726f776e207768656e206368617267696e672061207061796d656e742073747265616d20776f756c6420726573756c7420696e20616e206f766572666c6f77206f66207468652062616c616e636520747970654055736572576974686f757446756e6473000d047d014572726f72207468726f776e207768656e20747279696e6720746f206f706572617465207768656e20746865205573657220686173206265656e20666c616767656420666f72206e6f7420686176696e6720656e6f7567682066756e64732e70557365724e6f74466c61676765644173576974686f757446756e6473000e04e9014572726f72207468726f776e207768656e20612075736572207468617420686173206e6f74206265656e20666c616767656420617320776974686f75742066756e647320747269657320746f20757365207468652065787472696e73696320746f2070617920697473206f75747374616e64696e6720646562745c436f6f6c646f776e506572696f644e6f74506173736564000f04b5014572726f72207468726f776e207768656e2061207573657220747269657320746f20636c6561722074686520666c6167206f66206265696e6720776974686f75742066756e6473206265666f72652074686520636f6f6c646f776e20706572696f642068617320706173736564044901546865206572726f727320746861742063616e206265207468726f776e20627920746869732070616c6c657420746f20696e666f726d2075736572732061626f757420776861742077656e742077726f6e6735070c4870616c6c65745f6275636b65745f6e6674731870616c6c6574144572726f72040454000110484275636b657449734e6f74507269766174650000048d014275636b6574206973206e6f7420707269766174652e2043616c6c20607570646174655f6275636b65745f70726976616379602066726f6d207468652066696c652073797374656d2070616c6c657420746f206d616b6520697420707269766174652e384e6f744275636b65744f776e65720001049c4163636f756e74206973206e6f7420746865206f776e6572206f6620746865206275636b65742e644e6f436f72726573706f6e64696e67436f6c6c656374696f6e000204dd014e6f20636f6c6c656374696f6e20636f72726573706f6e64696e6720746f20746865206275636b65742e2043616c6c20607570646174655f6275636b65745f70726976616379602066726f6d207468652066696c652073797374656d2070616c6c657420746f206d616b6520697420707269766174652e60436f6e766572744279746573546f426f756e6465645665630003049c4661696c656420746f20636f6e7665727420627974657320746f2060426f756e64656456656360048054686520604572726f726020656e756d206f6620746869732070616c6c65742e39070c2c70616c6c65745f6e66747314747970657344436f6c6c656374696f6e44657461696c7308244163636f756e7449640100384465706f73697442616c616e63650118001801146f776e65720001244163636f756e7449640001346f776e65725f6465706f7369741801384465706f73697442616c616e63650001146974656d7310010c7533320001386974656d5f6d657461646174617310010c7533320001306974656d5f636f6e6669677310010c7533320001286174747269627574657310010c75333200003d070000040c0010100041070000040800100045070000040810000049070c2c70616c6c65745f6e66747314747970657320426974466c616773040454014d07000400080138436f6c6c656374696f6e526f6c6500004d070c2c70616c6c65745f6e66747314747970657338436f6c6c656374696f6e526f6c6500010c184973737565720001001c467265657a65720002001441646d696e0004000051070c2c70616c6c65745f6e6674731474797065732c4974656d44657461696c730c244163636f756e74496401001c4465706f73697401550724417070726f76616c73015907000c01146f776e65720001244163636f756e744964000124617070726f76616c7359070124417070726f76616c7300011c6465706f7369745507011c4465706f736974000055070c2c70616c6c65745f6e6674731474797065732c4974656d4465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e740001244163636f756e744964000118616d6f756e741801384465706f73697442616c616e6365000059070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b010004560111020453000004005d07013842547265654d61703c4b2c20563e00005d07042042547265654d617008044b01000456011102000400610700000061070000026507006507000004080011020069070c2c70616c6c65745f6e66747314747970657348436f6c6c656374696f6e4d65746164617461081c4465706f73697401182c537472696e674c696d6974000008011c6465706f73697418011c4465706f73697400011064617461b502016c426f756e6465645665633c75382c20537472696e674c696d69743e00006d070c2c70616c6c65745f6e667473147479706573304974656d4d65746164617461081c4465706f7369740171072c537472696e674c696d6974000008011c6465706f7369747107011c4465706f73697400011064617461b502016c426f756e6465645665633c75382c20537472696e674c696d69743e000071070c2c70616c6c65745f6e6674731474797065734c4974656d4d657461646174614465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000118616d6f756e741801384465706f73697442616c616e63650000750700000410101102c102bd0200790700000408b5027d07007d070c2c70616c6c65745f6e667473147479706573404174747269627574654465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000118616d6f756e741801384465706f73697442616c616e63650000810700000408189c0085070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401000453000004008907012c42547265655365743c543e00008907042042547265655365740404540100000400a40000008d070c2c70616c6c65745f6e6674731474797065732c50656e64696e67537761701030436f6c6c656374696f6e49640110184974656d49640110584974656d507269636557697468446972656374696f6e01c90220446561646c696e65011000100148646573697265645f636f6c6c656374696f6e100130436f6c6c656374696f6e4964000130646573697265645f6974656d110201384f7074696f6e3c4974656d49643e0001147072696365c50201784f7074696f6e3c4974656d507269636557697468446972656374696f6e3e000120646561646c696e65100120446561646c696e65000091070c2c70616c6c65745f6e66747314747970657320426974466c61677304045401950700040030013450616c6c657446656174757265000095070c2c70616c6c65745f6e6674731474797065733450616c6c6574466561747572650001101c54726164696e67000100284174747269627574657300020024417070726f76616c730004001453776170730008000099070c2c70616c6c65745f6e6674731870616c6c6574144572726f720804540004490001b4304e6f5065726d697373696f6e000004e8546865207369676e696e67206163636f756e7420686173206e6f207065726d697373696f6e20746f20646f20746865206f7065726174696f6e2e44556e6b6e6f776e436f6c6c656374696f6e0001047454686520676976656e206974656d20494420697320756e6b6e6f776e2e34416c7265616479457869737473000204b8546865206974656d2049442068617320616c7265616479206265656e207573656420666f7220616e206974656d2e3c417070726f76616c45787069726564000304390154686520617070726f76616c20686164206120646561646c696e65207468617420657870697265642c20736f2074686520617070726f76616c2069736e27742076616c696420616e796d6f72652e2857726f6e674f776e6572000404e8546865206f776e6572207475726e6564206f757420746f20626520646966666572656e7420746f2077686174207761732065787065637465642e284261645769746e6573730005041501546865207769746e657373206461746120676976656e20646f6573206e6f74206d61746368207468652063757272656e74207374617465206f662074686520636861696e2e44436f6c6c656374696f6e4964496e5573650006047c436f6c6c656374696f6e20494420697320616c72656164792074616b656e2e504974656d734e6f6e5472616e7366657261626c65000704c84974656d732077697468696e207468617420636f6c6c656374696f6e20617265206e6f6e2d7472616e7366657261626c652e2c4e6f7444656c65676174650008049c5468652070726f7669646564206163636f756e74206973206e6f7420612064656c65676174652e3457726f6e6744656c6567617465000904f45468652064656c6567617465207475726e6564206f757420746f20626520646966666572656e7420746f2077686174207761732065787065637465642e28556e617070726f766564000a04c44e6f20617070726f76616c20657869737473207468617420776f756c6420616c6c6f7720746865207472616e736665722e28556e6163636570746564000b041901546865206e616d6564206f776e657220686173206e6f74207369676e6564206f776e65727368697020616363657074616e6365206f662074686520636f6c6c656374696f6e2e284974656d4c6f636b6564000c0498546865206974656d206973206c6f636b656420286e6f6e2d7472616e7366657261626c65292e504c6f636b65644974656d41747472696275746573000d04744974656d2773206174747269627574657320617265206c6f636b65642e684c6f636b6564436f6c6c656374696f6e41747472696275746573000e048c436f6c6c656374696f6e2773206174747269627574657320617265206c6f636b65642e484c6f636b65644974656d4d65746164617461000f04684974656d2773206d65746164617461206973206c6f636b65642e604c6f636b6564436f6c6c656374696f6e4d6574616461746100100480436f6c6c656374696f6e2773206d65746164617461206973206c6f636b65642e404d6178537570706c79526561636865640011046c416c6c206974656d732068617665206265656e206d696e7465642e3c4d6178537570706c794c6f636b6564001204b8546865206d617820737570706c79206973206c6f636b656420616e642063616e2774206265206368616e6765642e444d6178537570706c79546f6f536d616c6c00130449015468652070726f7669646564206d617820737570706c79206973206c657373207468616e20746865206e756d626572206f66206974656d73206120636f6c6c656374696f6e20616c7265616479206861732e2c556e6b6e6f776e4974656d0014047454686520676976656e206974656d20494420697320756e6b6e6f776e2e2c556e6b6e6f776e537761700015044c5377617020646f65736e27742065786973742e404d657461646174614e6f74466f756e640016048c54686520676976656e206974656d20686173206e6f206d65746164617461207365742e444174747269627574654e6f74466f756e64001704985468652070726f7669646564206174747269627574652063616e277420626520666f756e642e284e6f74466f7253616c65001804544974656d206973206e6f7420666f722073616c652e24426964546f6f4c6f77001904705468652070726f76696465642062696420697320746f6f206c6f772e5052656163686564417070726f76616c4c696d6974001a04a0546865206974656d2068617320726561636865642069747320617070726f76616c206c696d69742e3c446561646c696e6545787069726564001b048454686520646561646c696e652068617320616c726561647920657870697265642e3457726f6e674475726174696f6e001c043101546865206475726174696f6e2070726f76696465642073686f756c64206265206c657373207468616e206f7220657175616c20746f20604d6178446561646c696e654475726174696f6e602e384d6574686f6444697361626c6564001d04a8546865206d6574686f642069732064697361626c65642062792073797374656d2073657474696e67732e3057726f6e6753657474696e67001e04885468652070726f76696465642073657474696e672063616e2774206265207365742e58496e636f6e73697374656e744974656d436f6e666967001f0415014974656d277320636f6e66696720616c72656164792065786973747320616e642073686f756c6420626520657175616c20746f207468652070726f7669646564206f6e652e204e6f436f6e666967002004c8436f6e66696720666f72206120636f6c6c656374696f6e206f7220616e206974656d2063616e277420626520666f756e642e3c526f6c65734e6f74436c656172656400210470536f6d6520726f6c65732077657265206e6f7420636c65617265642e384d696e744e6f7453746172746564002204644d696e7420686173206e6f742073746172746564207965742e244d696e74456e6465640023045c4d696e742068617320616c726561647920656e6465642e38416c7265616479436c61696d6564002404c05468652070726f7669646564204974656d2077617320616c7265616479207573656420666f7220636c61696d696e672e34496e636f7272656374446174610025047c5468652070726f7669646564206461746120697320696e636f72726563742e2c57726f6e674f726967696e002604ac5468652065787472696e736963207761732073656e74206279207468652077726f6e67206f726967696e2e3857726f6e675369676e6174757265002704905468652070726f7669646564207369676e617475726520697320696e636f72726563742e44496e636f72726563744d65746164617461002804a05468652070726f7669646564206d65746164617461206d6967687420626520746f6f206c6f6e672e644d6178417474726962757465734c696d6974526561636865640029049c43616e277420736574206d6f7265206174747269627574657320706572206f6e652063616c6c2e3857726f6e674e616d657370616365002a04d05468652070726f7669646564206e616d6573706163652069736e277420737570706f7274656420696e20746869732063616c6c2e48436f6c6c656374696f6e4e6f74456d707479002b048c43616e27742064656c657465206e6f6e2d656d70747920636f6c6c656374696f6e732e3c5769746e6573735265717569726564002c0490546865207769746e65737320646174612073686f756c642062652070726f76696465642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e9d07102873705f72756e74696d651c67656e657269634c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c416464726573730145041043616c6c015d04245369676e617475726501b10514457874726101a10700040038000000a10700000428a507a907ad07b107b507bd07c107c507c907cd0700a50710306672616d655f73797374656d28657874656e73696f6e7354636865636b5f6e6f6e5f7a65726f5f73656e64657248436865636b4e6f6e5a65726f53656e64657204045400000000a90710306672616d655f73797374656d28657874656e73696f6e7348636865636b5f737065635f76657273696f6e40436865636b5370656356657273696f6e04045400000000ad0710306672616d655f73797374656d28657874656e73696f6e7340636865636b5f74785f76657273696f6e38436865636b547856657273696f6e04045400000000b10710306672616d655f73797374656d28657874656e73696f6e7334636865636b5f67656e6573697330436865636b47656e6573697304045400000000b50710306672616d655f73797374656d28657874656e73696f6e733c636865636b5f6d6f7274616c69747938436865636b4d6f7274616c69747904045400000400b907010c4572610000b907102873705f72756e74696d651c67656e657269630c6572610c4572610001010420496d6d6f7274616c0000001c4d6f7274616c31040008000001001c4d6f7274616c32040008000002001c4d6f7274616c33040008000003001c4d6f7274616c34040008000004001c4d6f7274616c35040008000005001c4d6f7274616c36040008000006001c4d6f7274616c37040008000007001c4d6f7274616c38040008000008001c4d6f7274616c3904000800000900204d6f7274616c313004000800000a00204d6f7274616c313104000800000b00204d6f7274616c313204000800000c00204d6f7274616c313304000800000d00204d6f7274616c313404000800000e00204d6f7274616c313504000800000f00204d6f7274616c313604000800001000204d6f7274616c313704000800001100204d6f7274616c313804000800001200204d6f7274616c313904000800001300204d6f7274616c323004000800001400204d6f7274616c323104000800001500204d6f7274616c323204000800001600204d6f7274616c323304000800001700204d6f7274616c323404000800001800204d6f7274616c323504000800001900204d6f7274616c323604000800001a00204d6f7274616c323704000800001b00204d6f7274616c323804000800001c00204d6f7274616c323904000800001d00204d6f7274616c333004000800001e00204d6f7274616c333104000800001f00204d6f7274616c333204000800002000204d6f7274616c333304000800002100204d6f7274616c333404000800002200204d6f7274616c333504000800002300204d6f7274616c333604000800002400204d6f7274616c333704000800002500204d6f7274616c333804000800002600204d6f7274616c333904000800002700204d6f7274616c343004000800002800204d6f7274616c343104000800002900204d6f7274616c343204000800002a00204d6f7274616c343304000800002b00204d6f7274616c343404000800002c00204d6f7274616c343504000800002d00204d6f7274616c343604000800002e00204d6f7274616c343704000800002f00204d6f7274616c343804000800003000204d6f7274616c343904000800003100204d6f7274616c353004000800003200204d6f7274616c353104000800003300204d6f7274616c353204000800003400204d6f7274616c353304000800003500204d6f7274616c353404000800003600204d6f7274616c353504000800003700204d6f7274616c353604000800003800204d6f7274616c353704000800003900204d6f7274616c353804000800003a00204d6f7274616c353904000800003b00204d6f7274616c363004000800003c00204d6f7274616c363104000800003d00204d6f7274616c363204000800003e00204d6f7274616c363304000800003f00204d6f7274616c363404000800004000204d6f7274616c363504000800004100204d6f7274616c363604000800004200204d6f7274616c363704000800004300204d6f7274616c363804000800004400204d6f7274616c363904000800004500204d6f7274616c373004000800004600204d6f7274616c373104000800004700204d6f7274616c373204000800004800204d6f7274616c373304000800004900204d6f7274616c373404000800004a00204d6f7274616c373504000800004b00204d6f7274616c373604000800004c00204d6f7274616c373704000800004d00204d6f7274616c373804000800004e00204d6f7274616c373904000800004f00204d6f7274616c383004000800005000204d6f7274616c383104000800005100204d6f7274616c383204000800005200204d6f7274616c383304000800005300204d6f7274616c383404000800005400204d6f7274616c383504000800005500204d6f7274616c383604000800005600204d6f7274616c383704000800005700204d6f7274616c383804000800005800204d6f7274616c383904000800005900204d6f7274616c393004000800005a00204d6f7274616c393104000800005b00204d6f7274616c393204000800005c00204d6f7274616c393304000800005d00204d6f7274616c393404000800005e00204d6f7274616c393504000800005f00204d6f7274616c393604000800006000204d6f7274616c393704000800006100204d6f7274616c393804000800006200204d6f7274616c393904000800006300244d6f7274616c31303004000800006400244d6f7274616c31303104000800006500244d6f7274616c31303204000800006600244d6f7274616c31303304000800006700244d6f7274616c31303404000800006800244d6f7274616c31303504000800006900244d6f7274616c31303604000800006a00244d6f7274616c31303704000800006b00244d6f7274616c31303804000800006c00244d6f7274616c31303904000800006d00244d6f7274616c31313004000800006e00244d6f7274616c31313104000800006f00244d6f7274616c31313204000800007000244d6f7274616c31313304000800007100244d6f7274616c31313404000800007200244d6f7274616c31313504000800007300244d6f7274616c31313604000800007400244d6f7274616c31313704000800007500244d6f7274616c31313804000800007600244d6f7274616c31313904000800007700244d6f7274616c31323004000800007800244d6f7274616c31323104000800007900244d6f7274616c31323204000800007a00244d6f7274616c31323304000800007b00244d6f7274616c31323404000800007c00244d6f7274616c31323504000800007d00244d6f7274616c31323604000800007e00244d6f7274616c31323704000800007f00244d6f7274616c31323804000800008000244d6f7274616c31323904000800008100244d6f7274616c31333004000800008200244d6f7274616c31333104000800008300244d6f7274616c31333204000800008400244d6f7274616c31333304000800008500244d6f7274616c31333404000800008600244d6f7274616c31333504000800008700244d6f7274616c31333604000800008800244d6f7274616c31333704000800008900244d6f7274616c31333804000800008a00244d6f7274616c31333904000800008b00244d6f7274616c31343004000800008c00244d6f7274616c31343104000800008d00244d6f7274616c31343204000800008e00244d6f7274616c31343304000800008f00244d6f7274616c31343404000800009000244d6f7274616c31343504000800009100244d6f7274616c31343604000800009200244d6f7274616c31343704000800009300244d6f7274616c31343804000800009400244d6f7274616c31343904000800009500244d6f7274616c31353004000800009600244d6f7274616c31353104000800009700244d6f7274616c31353204000800009800244d6f7274616c31353304000800009900244d6f7274616c31353404000800009a00244d6f7274616c31353504000800009b00244d6f7274616c31353604000800009c00244d6f7274616c31353704000800009d00244d6f7274616c31353804000800009e00244d6f7274616c31353904000800009f00244d6f7274616c3136300400080000a000244d6f7274616c3136310400080000a100244d6f7274616c3136320400080000a200244d6f7274616c3136330400080000a300244d6f7274616c3136340400080000a400244d6f7274616c3136350400080000a500244d6f7274616c3136360400080000a600244d6f7274616c3136370400080000a700244d6f7274616c3136380400080000a800244d6f7274616c3136390400080000a900244d6f7274616c3137300400080000aa00244d6f7274616c3137310400080000ab00244d6f7274616c3137320400080000ac00244d6f7274616c3137330400080000ad00244d6f7274616c3137340400080000ae00244d6f7274616c3137350400080000af00244d6f7274616c3137360400080000b000244d6f7274616c3137370400080000b100244d6f7274616c3137380400080000b200244d6f7274616c3137390400080000b300244d6f7274616c3138300400080000b400244d6f7274616c3138310400080000b500244d6f7274616c3138320400080000b600244d6f7274616c3138330400080000b700244d6f7274616c3138340400080000b800244d6f7274616c3138350400080000b900244d6f7274616c3138360400080000ba00244d6f7274616c3138370400080000bb00244d6f7274616c3138380400080000bc00244d6f7274616c3138390400080000bd00244d6f7274616c3139300400080000be00244d6f7274616c3139310400080000bf00244d6f7274616c3139320400080000c000244d6f7274616c3139330400080000c100244d6f7274616c3139340400080000c200244d6f7274616c3139350400080000c300244d6f7274616c3139360400080000c400244d6f7274616c3139370400080000c500244d6f7274616c3139380400080000c600244d6f7274616c3139390400080000c700244d6f7274616c3230300400080000c800244d6f7274616c3230310400080000c900244d6f7274616c3230320400080000ca00244d6f7274616c3230330400080000cb00244d6f7274616c3230340400080000cc00244d6f7274616c3230350400080000cd00244d6f7274616c3230360400080000ce00244d6f7274616c3230370400080000cf00244d6f7274616c3230380400080000d000244d6f7274616c3230390400080000d100244d6f7274616c3231300400080000d200244d6f7274616c3231310400080000d300244d6f7274616c3231320400080000d400244d6f7274616c3231330400080000d500244d6f7274616c3231340400080000d600244d6f7274616c3231350400080000d700244d6f7274616c3231360400080000d800244d6f7274616c3231370400080000d900244d6f7274616c3231380400080000da00244d6f7274616c3231390400080000db00244d6f7274616c3232300400080000dc00244d6f7274616c3232310400080000dd00244d6f7274616c3232320400080000de00244d6f7274616c3232330400080000df00244d6f7274616c3232340400080000e000244d6f7274616c3232350400080000e100244d6f7274616c3232360400080000e200244d6f7274616c3232370400080000e300244d6f7274616c3232380400080000e400244d6f7274616c3232390400080000e500244d6f7274616c3233300400080000e600244d6f7274616c3233310400080000e700244d6f7274616c3233320400080000e800244d6f7274616c3233330400080000e900244d6f7274616c3233340400080000ea00244d6f7274616c3233350400080000eb00244d6f7274616c3233360400080000ec00244d6f7274616c3233370400080000ed00244d6f7274616c3233380400080000ee00244d6f7274616c3233390400080000ef00244d6f7274616c3234300400080000f000244d6f7274616c3234310400080000f100244d6f7274616c3234320400080000f200244d6f7274616c3234330400080000f300244d6f7274616c3234340400080000f400244d6f7274616c3234350400080000f500244d6f7274616c3234360400080000f600244d6f7274616c3234370400080000f700244d6f7274616c3234380400080000f800244d6f7274616c3234390400080000f900244d6f7274616c3235300400080000fa00244d6f7274616c3235310400080000fb00244d6f7274616c3235320400080000fc00244d6f7274616c3235330400080000fd00244d6f7274616c3235340400080000fe00244d6f7274616c3235350400080000ff0000bd0710306672616d655f73797374656d28657874656e73696f6e732c636865636b5f6e6f6e636528436865636b4e6f6e636504045400000400cc0120543a3a4e6f6e63650000c10710306672616d655f73797374656d28657874656e73696f6e7330636865636b5f7765696768742c436865636b57656967687404045400000000c507086870616c6c65745f7472616e73616374696f6e5f7061796d656e74604368617267655472616e73616374696f6e5061796d656e7404045400000400dc013042616c616e63654f663c543e0000c90708a463756d756c75735f7072696d6974697665735f73746f726167655f7765696768745f7265636c61696d5053746f726167655765696768745265636c61696d04045400000000cd0708746672616d655f6d657461646174615f686173685f657874656e73696f6e44436865636b4d657461646174614861736804045400000401106d6f6465d10701104d6f64650000d10708746672616d655f6d657461646174615f686173685f657874656e73696f6e104d6f64650001082044697361626c65640000001c456e61626c656400010000d507084c73746f726167655f6875625f72756e74696d651c52756e74696d6500000000601853797374656d011853797374656d481c4163636f756e7401010402000c4101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004e8205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e3845787472696e736963436f756e74000010040004b820546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e40496e686572656e74734170706c696564010020040004a4205768657468657220616c6c20696e686572656e74732068617665206265656e206170706c6965642e2c426c6f636b576569676874010024180000000000000488205468652063757272656e742077656967687420666f722074686520626c6f636b2e40416c6c45787472696e736963734c656e000010040004410120546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e24426c6f636b486173680101040510348000000000000000000000000000000000000000000000000000000000000000000498204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e3445787472696e736963446174610101040510380400043d012045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e184e756d6265720100101000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801003480000000000000000000000000000000000000000000000000000000000000000004702048617368206f66207468652070726576696f757320626c6f636b2e1844696765737401003c040004f020446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e184576656e747301004c04001ca0204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e001d01204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2ed020497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e002d01204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279fc206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e284576656e74436f756e74010010100000000004b820546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e2c4576656e74546f70696373010104023401030400282501204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e646578657394206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e00510120416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e2054686973450120616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e64e420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e005901205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573744d012074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b0101206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e484c61737452756e74696d65557067726164650000090304000455012053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e545570677261646564546f553332526566436f756e740100200400044d012054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e605570677261646564546f547269706c65526566436f756e740100200400085d012054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c736548202864656661756c7429206966206e6f742e38457865637574696f6e50686173650000fd02040004882054686520657865637574696f6e207068617365206f662074686520626c6f636b2e44417574686f72697a65645570677261646500001103040004b82060536f6d6560206966206120636f6465207570677261646520686173206265656e20617574686f72697a65642e01150301581830426c6f636b57656967687473210389010700f2052a01000b00204aa9d101020040010265cd1d00010bc0560def45010200e000010b0098f73e5d010200f0000100000265cd1d00010bc0de5f59ba0102003001010b00204aa9d1010200400101070088526a74020050000265cd1d0000000004d020426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e2c426c6f636b4c656e67746831033000003c00000050000000500004a820546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e38426c6f636b48617368436f756e74101000100000045501204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e20446257656967687439034040787d010000000000e1f505000000000409012054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e1c56657273696f6e3d0319044c73746f726167652d6875622d72756e74696d654c73746f726167652d6875622d72756e74696d6501000000010000000000000044d7bdd8a272ca0d6501000000dd718d5cc53262d401000000df6acb689907609b0500000037e397fc7c91f5e40200000040fe3ad401f8959a06000000d2bc9897eed08f1503000000f78b278be53f454c02000000ab3c0572291feb8b01000000bc9d89904f5b923f0100000037c8bb1350a9a2a804000000f3ff14d5ab52705903000000ea93e3f16f3d696202000000fbc577b9d747efd601000000b9e7717ace5b45cd010000001078d7ac24a07b0e010000000be7208954c7c6c901000000966604ffe78eb0920100000001000000010484204765742074686520636861696e277320696e2d636f64652076657273696f6e2e28535335385072656669784d03082a0014a8205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e0039012054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e20697331012074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f662069742061737020616e206964656e746966696572206f662074686520636861696e2e015103003c50617261636861696e53797374656d013c50617261636861696e53797374656d6044556e696e636c756465645365676d656e74010055030400184901204c617465737420696e636c7564656420626c6f636b2064657363656e64616e7473207468652072756e74696d652061636365707465642e20496e206f7468657220776f7264732c20746865736520617265610120616e636573746f7273206f66207468652063757272656e746c7920657865637574696e6720626c6f636b2077686963682068617665206e6f74206265656e20696e636c7564656420696e20746865206f627365727665644c2072656c61792d636861696e2073746174652e00750120546865207365676d656e74206c656e677468206973206c696d69746564206279207468652063617061636974792072657475726e65642066726f6d20746865205b60436f6e73656e737573486f6f6b605d20636f6e666967757265643c20696e207468652070616c6c65742e6c41676772656761746564556e696e636c756465645365676d656e7400007d0304000c69012053746f72616765206669656c642074686174206b6565707320747261636b206f662062616e64776964746820757365642062792074686520756e696e636c75646564207365676d656e7420616c6f6e672077697468207468652d01206c61746573742048524d502077617465726d61726b2e205573656420666f72206c696d6974696e672074686520616363657074616e6365206f66206e657720626c6f636b73207769746890207265737065637420746f2072656c617920636861696e20636f6e73747261696e74732e5450656e64696e6756616c69646174696f6e436f6465010038040018590120496e2063617365206f662061207363686564756c656420757067726164652c20746869732073746f72616765206669656c6420636f6e7461696e73207468652076616c69646174696f6e20636f646520746f20626524206170706c6965642e003d0120417320736f6f6e206173207468652072656c617920636861696e2067697665732075732074686520676f2d6168656164207369676e616c2c2077652077696c6c206f7665727772697465207468657101205b603a636f6465605d5b73705f636f72653a3a73746f726167653a3a77656c6c5f6b6e6f776e5f6b6579733a3a434f44455d2077686963682077696c6c20726573756c7420746865206e65787420626c6f636b2070726f636573730901207769746820746865206e65772076616c69646174696f6e20636f64652e205468697320636f6e636c756465732074686520757067726164652070726f636573732e444e657756616c69646174696f6e436f64650000380400145d012056616c69646174696f6e20636f6465207468617420697320736574206279207468652070617261636861696e20616e6420697320746f20626520636f6d6d756e69636174656420746f20636f6c6c61746f7220616e647820636f6e73657175656e746c79207468652072656c61792d636861696e2e00650120546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b206966206e6f206f746865722070616c6c657420616c7265616479207365742c207468652076616c75652e3856616c69646174696f6e446174610000810304000cd020546865205b6050657273697374656456616c69646174696f6e44617461605d2073657420666f72207468697320626c6f636b2e2d0120546869732076616c756520697320657870656374656420746f20626520736574206f6e6c79206f6e63652070657220626c6f636b20616e642069742773206e657665722073746f7265643420696e2074686520747269652e5044696453657456616c69646174696f6e436f6465010020040004e02057657265207468652076616c69646174696f6e20646174612073657420746f206e6f74696679207468652072656c617920636861696e3f644c61737452656c6179436861696e426c6f636b4e756d62657201001010000000000c1d01205468652072656c617920636861696e20626c6f636b206e756d626572206173736f636961746564207769746820746865206c6173742070617261636861696e20626c6f636b2e00882054686973206973207570646174656420696e20606f6e5f66696e616c697a65602e60557067726164655265737472696374696f6e5369676e616c0100890304001c750120416e206f7074696f6e20776869636820696e64696361746573206966207468652072656c61792d636861696e20726573747269637473207369676e616c6c696e6720612076616c69646174696f6e20636f646520757067726164652e610120496e206f7468657220776f7264732c20696620746869732069732060536f6d656020616e64205b604e657756616c69646174696f6e436f6465605d2069732060536f6d6560207468656e207468652070726f64756365646c2063616e6469646174652077696c6c20626520696e76616c69642e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3855706772616465476f416865616401007503040014dc204f7074696f6e616c207570677261646520676f2d6168656164207369676e616c2066726f6d207468652072656c61792d636861696e2e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3c52656c6179537461746550726f6f6600009103040018c4205468652073746174652070726f6f6620666f7220746865206c6173742072656c617920706172656e7420626c6f636b2e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e5852656c6576616e744d6573736167696e6753746174650000990304001c65012054686520736e617073686f74206f6620736f6d652073746174652072656c6174656420746f206d6573736167696e672072656c6576616e7420746f207468652063757272656e742070617261636861696e2061732070657248207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e44486f7374436f6e66696775726174696f6e0000ad030400182901205468652070617261636861696e20686f737420636f6e66696775726174696f6e207468617420776173206f627461696e65642066726f6d207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e384c617374446d714d7163486561640100b50380000000000000000000000000000000000000000000000000000000000000000010f420546865206c61737420646f776e77617264206d65737361676520717565756520636861696e20686561642077652068617665206f627365727665642e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e404c61737448726d704d716348656164730100b903040010490120546865206d65737361676520717565756520636861696e2068656164732077652068617665206f62736572766564207065722065616368206368616e6e656c20696e636f6d696e67206368616e6e656c2e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e6450726f636573736564446f776e776172644d6573736167657301001010000000000cc8204e756d626572206f6620646f776e77617264206d657373616765732070726f63657373656420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e3448726d7057617465726d61726b01001010000000000ca02048524d502077617465726d61726b2074686174207761732073657420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5048726d704f7574626f756e644d657373616765730100c50304000ca42048524d50206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e385570776172644d657373616765730100690204000cac20557077617264206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5450656e64696e675570776172644d6573736167657301006902040004310120557077617264206d65737361676573207468617420617265207374696c6c2070656e64696e6720616e64206e6f74207965742073656e6420746f207468652072656c617920636861696e2e5c55707761726444656c6976657279466565466163746f720100cd0340000064a7b3b6e00d000000000000000004e42054686520666163746f7220746f206d756c7469706c792074686520626173652064656c69766572792066656520627920666f7220554d502e84416e6e6f756e63656448726d704d6573736167657350657243616e646964617465010010100000000008650120546865206e756d626572206f662048524d50206d65737361676573207765206f6273657276656420696e20606f6e5f696e697469616c697a656020616e64207468757320757365642074686174206e756d62657220666f72f020616e6e6f756e63696e672074686520776569676874206f6620606f6e5f696e697469616c697a656020616e6420606f6e5f66696e616c697a65602e68526573657276656458636d705765696768744f766572726964650000280400085d01205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e672058434d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e645265736572766564446d705765696768744f766572726964650000280400085901205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e6720444d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e60437573746f6d56616c69646174696f6e486561644461746100003804000c2901204120637573746f6d2068656164206461746120746861742073686f756c642062652072657475726e656420617320726573756c74206f66206076616c69646174655f626c6f636b602e00110120536565206050616c6c65743a3a7365745f637573746f6d5f76616c69646174696f6e5f686561645f646174616020666f72206d6f726520696e666f726d6174696f6e2e01d103017c042853656c66506172614964e90110e803000004b82052657475726e73207468652070617261636861696e204944207765206172652072756e6e696e6720776974682e01f503012454696d657374616d70012454696d657374616d70080c4e6f7701003020000000000000000004a0205468652063757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e24446964557064617465010020040010d82057686574686572207468652074696d657374616d7020686173206265656e207570646174656420696e207468697320626c6f636b2e00550120546869732076616c7565206973207570646174656420746f206074727565602075706f6e207375636365737366756c207375626d697373696f6e206f6620612074696d657374616d702062792061206e6f64652e4501204974206973207468656e20636865636b65642061742074686520656e64206f66206561636820626c6f636b20657865637574696f6e20696e2074686520606f6e5f66696e616c697a656020686f6f6b2e01f9030004344d696e696d756d506572696f643020b80b000000000000188c20546865206d696e696d756d20706572696f64206265747765656e20626c6f636b732e004d012042652061776172652074686174207468697320697320646966666572656e7420746f20746865202a65787065637465642a20706572696f6420746861742074686520626c6f636b2070726f64756374696f6e4901206170706172617475732070726f76696465732e20596f75722063686f73656e20636f6e73656e7375732073797374656d2077696c6c2067656e6572616c6c7920776f726b2077697468207468697320746f61012064657465726d696e6520612073656e7369626c6520626c6f636b2074696d652e20466f72206578616d706c652c20696e2074686520417572612070616c6c65742069742077696c6c20626520646f75626c6520746869737020706572696f64206f6e2064656661756c742073657474696e67732e00023450617261636861696e496e666f013450617261636861696e496e666f042c50617261636861696e49640100e90110640000000001fd03000000032042616c616e636573012042616c616e6365731c34546f74616c49737375616e6365010018400000000000000000000000000000000004982054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e40496e61637469766549737375616e636501001840000000000000000000000000000000000409012054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e1c4163636f756e74010104020014010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080600901205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b19022020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e0c20207d102060606000150120596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b7420202074797065204163636f756e7453746f7265203d2053797374656d0c20207d102060606000510120427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f7265736d0120606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e207468652901206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e4101204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e144c6f636b7301010402000104040010b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e00ad0120557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f602052657365727665730101040200110404000ca4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e00b10120557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f6014486f6c647301010402001d040400046c20486f6c6473206f6e206163636f756e742062616c616e6365732e1c467265657a6573010104020035040400048820467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e014104018410484578697374656e7469616c4465706f736974184000ca9a3b00000000000000000000000020410120546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2100590120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f72610120746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e590120496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e65787065637465648c206265686176696f757220696620796f7520736574207468697320746f207a65726f2e00f020426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e6521204d61784c6f636b7310103200000010f420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742edc204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e00ad0120557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f602c4d617852657365727665731010320000000c0d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e00b10120557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f60284d6178467265657a657310100000000004610120546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e0151040a485472616e73616374696f6e5061796d656e7401485472616e73616374696f6e5061796d656e7408444e6578744665654d756c7469706c6965720100cd0340000064a7b3b6e00d0000000000000000003853746f7261676556657273696f6e0100550404000000018c04604f7065726174696f6e616c4665654d756c7469706c696572080405545901204120666565206d756c7469706c69657220666f7220604f7065726174696f6e616c602065787472696e7369637320746f20636f6d7075746520227669727475616c207469702220746f20626f6f73742074686569722c20607072696f726974796000510120546869732076616c7565206973206d756c7469706c69656420627920746865206066696e616c5f6665656020746f206f627461696e206120227669727475616c20746970222074686174206973206c61746572f420616464656420746f20612074697020636f6d706f6e656e7420696e20726567756c617220607072696f72697479602063616c63756c6174696f6e732e4d01204974206d65616e732074686174206120604e6f726d616c60207472616e73616374696f6e2063616e2066726f6e742d72756e20612073696d696c61726c792d73697a656420604f7065726174696f6e616c6041012065787472696e736963202877697468206e6f20746970292c20627920696e636c7564696e672061207469702076616c75652067726561746572207468616e20746865207669727475616c207469702e003c20606060727573742c69676e6f726540202f2f20466f7220604e6f726d616c608c206c6574207072696f72697479203d207072696f726974795f63616c6328746970293b0054202f2f20466f7220604f7065726174696f6e616c601101206c6574207669727475616c5f746970203d2028696e636c7573696f6e5f666565202b2074697029202a204f7065726174696f6e616c4665654d756c7469706c6965723bc4206c6574207072696f72697479203d207072696f726974795f63616c6328746970202b207669727475616c5f746970293b1020606060005101204e6f746520746861742073696e636520776520757365206066696e616c5f6665656020746865206d756c7469706c696572206170706c69657320616c736f20746f2074686520726567756c61722060746970605d012073656e74207769746820746865207472616e73616374696f6e2e20536f2c206e6f74206f6e6c7920646f657320746865207472616e73616374696f6e206765742061207072696f726974792062756d702062617365646101206f6e207468652060696e636c7573696f6e5f666565602c2062757420776520616c736f20616d706c6966792074686520696d70616374206f662074697073206170706c69656420746f20604f7065726174696f6e616c6038207472616e73616374696f6e732e000b105375646f01105375646f040c4b6579000000040004842054686520604163636f756e74496460206f6620746865207375646f206b65792e01590401900001cd050f28417574686f72736869700128417574686f72736869700418417574686f720000000400046420417574686f72206f662063757272656e7420626c6f636b2e000000001444436f6c6c61746f7253656c656374696f6e0144436f6c6c61746f7253656c656374696f6e1434496e76756c6e657261626c65730100d10504000411012054686520696e76756c6e657261626c652c207065726d697373696f6e656420636f6c6c61746f72732e2054686973206c697374206d75737420626520736f727465642e3443616e6469646174654c6973740100d5050400146901205468652028636f6d6d756e6974792c206c696d697465642920636f6c6c6174696f6e2063616e646964617465732e206043616e646964617465736020616e642060496e76756c6e657261626c6573602073686f756c6420626550206d757475616c6c79206578636c75736976652e0075012054686973206c69737420697320736f7274656420696e20617363656e64696e67206f72646572206279206465706f73697420616e64207768656e20746865206465706f736974732061726520657175616c2c20746865206c65617374a020726563656e746c79207570646174656420697320636f6e7369646572656420677265617465722e444c617374417574686f726564426c6f636b01010405001010000000000484204c61737420626c6f636b20617574686f72656420627920636f6c6c61746f722e444465736972656443616e6469646174657301001010000000000c782044657369726564206e756d626572206f662063616e646964617465732e00750120546869732073686f756c6420696465616c6c7920616c77617973206265206c657373207468616e205b60436f6e6669673a3a4d617843616e64696461746573605d20666f72207765696768747320746f20626520636f72726563742e3443616e646964616379426f6e6401001840000000000000000000000000000000000cb820466978656420616d6f756e7420746f206465706f73697420746f206265636f6d65206120636f6c6c61746f722e004101205768656e206120636f6c6c61746f722063616c6c7320606c656176655f696e74656e7460207468657920696d6d6564696174656c79207265636569766520746865206465706f736974206261636b2e01610401a00001e105151c53657373696f6e011c53657373696f6e1c2856616c696461746f72730100a40400047c205468652063757272656e7420736574206f662076616c696461746f72732e3043757272656e74496e646578010010100000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e345175657565644368616e676564010020040008390120547275652069662074686520756e6465726c79696e672065636f6e6f6d6963206964656e746974696573206f7220776569676874696e6720626568696e64207468652076616c696461746f7273a420686173206368616e67656420696e20746865207175657565642076616c696461746f72207365742e285175657565644b6579730100e5050400083d012054686520717565756564206b65797320666f7220746865206e6578742073657373696f6e2e205768656e20746865206e6578742073657373696f6e20626567696e732c207468657365206b657973e02077696c6c206265207573656420746f2064657465726d696e65207468652076616c696461746f7227732073657373696f6e206b6579732e4844697361626c656456616c696461746f72730100b9020400148020496e6469636573206f662064697361626c65642076616c696461746f72732e003d01205468652076656320697320616c77617973206b65707420736f7274656420736f20746861742077652063616e2066696e642077686574686572206120676976656e2076616c696461746f722069733d012064697361626c6564207573696e672062696e617279207365617263682e204974206765747320636c6561726564207768656e20606f6e5f73657373696f6e5f656e64696e67602072657475726e73642061206e657720736574206f66206964656e7469746965732e204e6578744b657973000104050069040400049c20546865206e6578742073657373696f6e206b65797320666f7220612076616c696461746f722e204b65794f776e657200010405ed0500040004090120546865206f776e6572206f662061206b65792e20546865206b65792069732074686520604b657954797065496460202b2074686520656e636f646564206b65792e01650401a80001f505161041757261011041757261082c417574686f7269746965730100f9050400046c205468652063757272656e7420617574686f72697479207365742e2c43757272656e74536c6f74010001062000000000000000000c80205468652063757272656e7420736c6f74206f66207468697320626c6f636b2e009420546869732077696c6c2062652073657420696e20606f6e5f696e697469616c697a65602e00000430536c6f744475726174696f6e30207017000000000000100d012054686520736c6f74206475726174696f6e20417572612073686f756c642072756e20776974682c2065787072657373656420696e206d696c6c697365636f6e64732e3d0120546865206566666563746976652076616c7565206f66207468697320747970652073686f756c64206e6f74206368616e6765207768696c652074686520636861696e2069732072756e6e696e672e00350120466f72206261636b776172647320636f6d7061746962696c6974792065697468657220757365205b604d696e696d756d506572696f6454696d657354776f605d206f72206120636f6e73742e00171c41757261457874011c41757261457874082c417574686f7269746965730100f905040014942053657276657320617320636163686520666f722074686520617574686f7269746965732e0071012054686520617574686f72697469657320696e204175526120617265206f7665727772697474656e20696e20606f6e5f696e697469616c697a6560207768656e2077652073776974636820746f2061206e65772073657373696f6e2c5d0120627574207765207265717569726520746865206f6c6420617574686f72697469657320746f2076657269667920746865207365616c207768656e2076616c69646174696e67206120506f562e20546869732077696c6c0d0120616c77617973206265207570646174656420746f20746865206c6174657374204175526120617574686f72697469657320696e20606f6e5f66696e616c697a65602e20536c6f74496e666f0000050604000cd82043757272656e7420736c6f742070616972656420776974682061206e756d626572206f6620617574686f72656420626c6f636b732e00982055706461746564206f6e206561636820626c6f636b20696e697469616c697a6174696f6e2e00000000182458636d705175657565012458636d7051756575651c50496e626f756e6458636d7053757370656e646564010009060400200d01205468652073757370656e64656420696e626f756e642058434d50206368616e6e656c732e20416c6c206f746865727320617265206e6f742073757370656e6465642e00710120546869732069732061206053746f7261676556616c75656020696e7374656164206f662061206053746f726167654d6170602073696e636520776520657870656374206d756c7469706c652072656164732070657220626c6f636b690120746f20646966666572656e74206b65797320776974682061206f6e652062797465207061796c6f61642e205468652061636365737320746f2060426f756e6465644254726565536574602077696c6c2062652063616368656415012077697468696e2074686520626c6f636b20616e64207468657265666f7265206f6e6c7920696e636c75646564206f6e636520696e207468652070726f6f662073697a652e006501204e4f54453a2054686520506f562062656e63686d61726b696e672063616e6e6f74206b6e6f77207468697320616e642077696c6c206f7665722d657374696d6174652c20627574207468652061637475616c2070726f6f66442077696c6c20626520736d616c6c65722e484f7574626f756e6458636d70537461747573010015060400185d0120546865206e6f6e2d656d7074792058434d50206368616e6e656c7320696e206f72646572206f66206265636f6d696e67206e6f6e2d656d7074792c20616e642074686520696e646578206f6620746865206669727374510120616e64206c617374206f7574626f756e64206d6573736167652e204966207468652074776f20696e64696365732061726520657175616c2c207468656e20697420696e6469636174657320616e20656d707479590120717565756520616e64207468657265206d7573742062652061206e6f6e2d604f6b6020604f7574626f756e64537461747573602e20576520617373756d65207175657565732067726f77206e6f20677265617465725901207468616e203635353335206974656d732e20517565756520696e646963657320666f72206e6f726d616c206d6573736167657320626567696e206174206f6e653b207a65726f20697320726573657276656420696e11012063617365206f6620746865206e65656420746f2073656e64206120686967682d7072696f72697479207369676e616c206d657373616765207468697320626c6f636b2e09012054686520626f6f6c20697320747275652069662074686572652069732061207369676e616c206d6573736167652077616974696e6720746f2062652073656e742e504f7574626f756e6458636d704d657373616765730101080205210638040004bc20546865206d65737361676573206f7574626f756e6420696e206120676976656e2058434d50206368616e6e656c2e385369676e616c4d6573736167657301010402e90138040004a020416e79207369676e616c206d657373616765732077616974696e6720746f2062652073656e742e2c5175657565436f6e66696701002506302000000030000000080000000415012054686520636f6e66696775726174696f6e20776869636820636f6e74726f6c73207468652064796e616d696373206f6620746865206f7574626f756e642071756575652e38517565756553757370656e64656401002004000441012057686574686572206f72206e6f74207468652058434d502071756575652069732073757370656e6465642066726f6d20657865637574696e6720696e636f6d696e672058434d73206f72206e6f742e4444656c6976657279466565466163746f7201010405e901cd0340000064a7b3b6e00d000000000000000004c42054686520666163746f7220746f206d756c7469706c792074686520626173652064656c6976657279206665652062792e01710401ac044c4d6178496e626f756e6453757370656e6465641010e803000014490120546865206d6178696d756d206e756d626572206f6620696e626f756e642058434d50206368616e6e656c7320746861742063616e2062652073757370656e6465642073696d756c74616e656f75736c792e005d0120416e792066757274686572206368616e6e656c2073757370656e73696f6e732077696c6c206661696c20616e64206d65737361676573206d6179206765742064726f7070656420776974686f757420667572746865724501206e6f746963652e2043686f6f73696e67206120686967682076616c756520283130303029206973206f6b61793b207468652074726164652d6f666620746861742069732064657363726962656420696ed8205b60496e626f756e6458636d7053757370656e646564605d207374696c6c206170706c6965732061742074686174207363616c652e0129061e2c506f6c6b61646f7458636d012c506f6c6b61646f7458636d30305175657279436f756e746572010030200000000000000000048820546865206c617465737420617661696c61626c6520717565727920696e6465782e1c5175657269657300010402302d060400045420546865206f6e676f696e6720717565726965732e28417373657454726170730101040634101000000000106820546865206578697374696e672061737365742074726170732e006101204b65792069732074686520626c616b6532203235362068617368206f6620286f726967696e2c2076657273696f6e65642060417373657473602920706169722e2056616c756520697320746865206e756d626572206f661d012074696d65732074686973207061697220686173206265656e20747261707065642028757375616c6c79206a75737420312069662069742065786973747320617420616c6c292e385361666558636d56657273696f6e00001004000861012044656661756c742076657273696f6e20746f20656e636f64652058434d207768656e206c61746573742076657273696f6e206f662064657374696e6174696f6e20697320756e6b6e6f776e2e20496620604e6f6e65602c3d01207468656e207468652064657374696e6174696f6e732077686f73652058434d2076657273696f6e20697320756e6b6e6f776e2061726520636f6e7369646572656420756e726561636861626c652e40537570706f7274656456657273696f6e0001080502410610040004f020546865204c61746573742076657273696f6e732074686174207765206b6e6f7720766172696f7573206c6f636174696f6e7320737570706f72742e4056657273696f6e4e6f746966696572730001080502410630040004050120416c6c206c6f636174696f6e7320746861742077652068617665207265717565737465642076657273696f6e206e6f74696669636174696f6e732066726f6d2e5056657273696f6e4e6f746966795461726765747300010805024106450604000871012054686520746172676574206c6f636174696f6e73207468617420617265207375627363726962656420746f206f75722076657273696f6e206368616e6765732c2061732077656c6c20617320746865206d6f737420726563656e7494206f66206f75722076657273696f6e7320776520696e666f726d6564207468656d206f662e5456657273696f6e446973636f7665727951756575650100490604000c65012044657374696e6174696f6e732077686f7365206c61746573742058434d2076657273696f6e20776520776f756c64206c696b6520746f206b6e6f772e204475706c696361746573206e6f7420616c6c6f7765642c20616e6471012074686520607533326020636f756e74657220697320746865206e756d626572206f662074696d6573207468617420612073656e6420746f207468652064657374696e6174696f6e20686173206265656e20617474656d707465642c8c20776869636820697320757365642061732061207072696f726974697a6174696f6e2e4043757272656e744d6967726174696f6e000055060400049c205468652063757272656e74206d6967726174696f6e27732073746167652c20696620616e792e5452656d6f74654c6f636b656446756e6769626c657300010c0502025d066106040004f02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e20612072656d6f746520636861696e2e3c4c6f636b656446756e6769626c657300010402007106040004e02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e207468697320636861696e2e5458636d457865637574696f6e53757370656e646564010020040004b420476c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e01750401b000017d061f2843756d756c757358636d0001110501dd01000020304d657373616765517565756501304d65737361676551756575650c30426f6f6b5374617465466f7201010405e501810674000000000000000000000000000000000000000000000000000000000004cc2054686520696e646578206f662074686520666972737420616e64206c61737420286e6f6e2d656d707479292070616765732e2c53657276696365486561640000e501040004bc20546865206f726967696e2061742077686963682077652073686f756c6420626567696e20736572766963696e672e14506167657300010805058d0691060400048820546865206d6170206f66207061676520696e646963657320746f2070616765732e01150501e10110204865617053697a65101000000100143d01205468652073697a65206f662074686520706167653b207468697320696d706c69657320746865206d6178696d756d206d6573736167652073697a652077686963682063616e2062652073656e742e005901204120676f6f642076616c756520646570656e6473206f6e20746865206578706563746564206d6573736167652073697a65732c20746865697220776569676874732c207468652077656967687420746861742069735d0120617661696c61626c6520666f722070726f63657373696e67207468656d20616e6420746865206d6178696d616c206e6565646564206d6573736167652073697a652e20546865206d6178696d616c206d65737361676511012073697a6520697320736c696768746c79206c6f776572207468616e207468697320617320646566696e6564206279205b604d61784d6573736167654c656e4f66605d2e204d61785374616c651010080000000c5d0120546865206d6178696d756d206e756d626572206f66207374616c652070616765732028692e652e206f66206f766572776569676874206d657373616765732920616c6c6f776564206265666f72652063756c6c696e6751012063616e2068617070656e2e204f6e636520746865726520617265206d6f7265207374616c65207061676573207468616e20746869732c207468656e20686973746f726963616c207061676573206d6179206265fc2064726f707065642c206576656e206966207468657920636f6e7461696e20756e70726f636573736564206f766572776569676874206d657373616765732e34536572766963655765696768742d032c0107005840fba2020070001841012054686520616d6f756e74206f66207765696768742028696620616e79292077686963682073686f756c642062652070726f766964656420746f20746865206d65737361676520717565756520666f72a820736572766963696e6720656e717565756564206974656d7320606f6e5f696e697469616c697a65602e00fc2054686973206d6179206265206c65676974696d6174656c7920604e6f6e656020696e207468652063617365207468617420796f752077696c6c2063616c6c5d012060536572766963655175657565733a3a736572766963655f71756575657360206d616e75616c6c79206f7220736574205b6053656c663a3a49646c654d617853657276696365576569676874605d20746f2068617665542069742072756e20696e20606f6e5f69646c65602e5049646c654d6178536572766963655765696768742d030400145d0120546865206d6178696d756d20616d6f756e74206f66207765696768742028696620616e792920746f20626520757365642066726f6d2072656d61696e696e672077656967687420606f6e5f69646c656020776869636841012073686f756c642062652070726f766964656420746f20746865206d65737361676520717565756520666f7220736572766963696e6720656e717565756564206974656d7320606f6e5f69646c65602e3d012055736566756c20666f722070617261636861696e7320746f2070726f63657373206d65737361676573206174207468652073616d6520626c6f636b2074686579206172652072656365697665642e00290120496620604e6f6e65602c2069742077696c6c206e6f742063616c6c2060536572766963655175657565733a3a736572766963655f7175657565736020696e20606f6e5f69646c65602e019906212450726f766964657273012450726f76696465727330385369676e5570526571756573747300010402009d06040028ed0120546865206d617070696e672066726f6d20616e204163636f756e74496420746861742072657175657374656420746f207369676e20757020746f2061207475706c65206f6620746865206d6574616461746120776974682074797065206f662074686520726571756573742c20616e642074686520626c6f636b88206e756d626572207768656e20746865207265717565737420776173206d6164652e00d1012054686973206973207573656420666f72207468652074776f2d737465702070726f63657373206f66207265676973746572696e673a207768656e2061207573657220726571756573747320746f20726567697374657220617320612053502028656974686572204d5350206f7220425350292cb10120746861742072657175657374207769746820746865206d6574616461746120616e6420746865206465706f7369742068656c642069732073746f72656420686572652e205768656e20746865207573657220636f6e6669726d7320746865207369676e2075702c20746865250120726571756573742069732072656d6f7665642066726f6d20746869732073746f7261676520616e64207468652075736572206973207265676973746572656420617320612053502e007020546869732073746f72616765206973207570646174656420696e3aad02202d205b726571756573745f6d73705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6d73705f7369676e5f75702920616e64205b726571756573745f6273705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6273705f7369676e5f7570292c207768696368206164642061206e657720656e74727920746f20746865206d61702e9102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f75702920616e64205b63616e63656c5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a63616e63656c5f7369676e5f7570292c2077686963682072656d6f766520616e206578697374696e6720656e7472792066726f6d20746865206d61702e804163636f756e744964546f4d61696e53746f7261676550726f7669646572496400010402003404001ce820546865206d617070696e672066726f6d20616e204163636f756e74496420746f2061204d61696e53746f7261676550726f766964657249642e007d012054686973206973207573656420746f206765742061204d61696e2053746f726167652050726f7669646572277320756e69717565206964656e746966696572206e656564656420746f2061636365737320697473206d657461646174612e007020546869732073746f72616765206973207570646174656420696e3a5102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722ea901202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e504d61696e53746f7261676550726f7669646572730001040234a9060400280d0120546865206d617070696e672066726f6d2061204d61696e53746f7261676550726f7669646572496420746f2061204d61696e53746f7261676550726f76696465722e00e02054686973206973207573656420746f206765742061204d61696e2053746f726167652050726f76696465722773206d657461646174612ebd012049742072657475726e7320604e6f6e656020696620746865204d61696e2053746f726167652050726f766964657220494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564204d61696e2053746f726167652050726f76696465722e007020546869732073746f72616765206973207570646174656420696e3a5102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722ea901202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e8901202d205b6368616e67655f63617061636974795d2863726174653a3a646973706174636861626c65733a3a6368616e67655f6361706163697479292c207768696368206368616e6765732074686520656e747279277320606361706163697479602e5502202d205b6164645f76616c75655f70726f705d2863726174653a3a646973706174636861626c65733a3a6164645f76616c75655f70726f70292c20776869636820617070656e64732061206e65772076616c75652070726f706f736974696f6e20746f2074686520656e7472792773206578697374696e67206076616c75655f70726f706020626f756e64656420766563746f722e1c4275636b6574730001040234b106040024dc20546865206d617070696e672066726f6d2061204275636b6574496420746f2074686174206275636b65742773206d657461646174612e0031012054686973206973207573656420746f206765742061206275636b65742773206d657461646174612c207375636820617320726f6f742c20757365722049442c20616e64204d53502049442e45012049742072657475726e7320604e6f6e656020696620746865204275636b657420494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564206275636b65742e007020546869732073746f72616765206973207570646174656420696e3a9501202d205b6164645f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6164645f6275636b6574292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e0502202d205b6368616e67655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6368616e67655f726f6f745f6275636b6574292c207768696368206368616e6765732074686520636f72726573706f6e64696e67206275636b6574277320726f6f742e1d02202d205b72656d6f76655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a72656d6f76655f726f6f745f6275636b6574292c2077686963682072656d6f7665732074686520656e747279206f662074686520636f72726573706f6e64696e67206275636b65742e7c4d61696e53746f7261676550726f7669646572496473546f4275636b6574730001040234b90604001c0d0120546865206d617070696e672066726f6d2061204d61696e53746f7261676550726f7669646572496420746f206120766563746f72206f66204275636b65744964732e00b1012054686973206973207573656420746f20656666696369656e746c7920726574726965766520746865206c697374206f66206275636b65747320746861742061204d61696e2053746f726167652050726f76696465722069732063757272656e746c792073746f72696e672e007020546869732073746f72616765206973207570646174656420696e3a0501202d205b6164645f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6164645f6275636b6574294501202d205b72656d6f76655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a72656d6f76655f726f6f745f6275636b657429884163636f756e744964546f4261636b757053746f7261676550726f76696465724964000104020034040020f020546865206d617070696e672066726f6d20616e204163636f756e74496420746f2061204261636b757053746f7261676550726f766964657249642e0085012054686973206973207573656420746f206765742061204261636b75702053746f726167652050726f7669646572277320756e69717565206964656e746966696572206e656564656420746f2061636365737320697473206d657461646174612e007020546869732073746f72616765206973207570646174656420696e3a005902202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722ea901202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e584261636b757053746f7261676550726f7669646572730001040234a5060400241d0120546865206d617070696e672066726f6d2061204261636b757053746f7261676550726f7669646572496420746f2061204261636b757053746f7261676550726f76696465722e00e82054686973206973207573656420746f206765742061204261636b75702053746f726167652050726f76696465722773206d657461646174612ecd012049742072657475726e7320604e6f6e656020696620746865204261636b75702053746f726167652050726f766964657220494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564204261636b75702053746f726167652050726f76696465722e007020546869732073746f72616765206973207570646174656420696e3a5902202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722ea901202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e8901202d205b6368616e67655f63617061636974795d2863726174653a3a646973706174636861626c65733a3a6368616e67655f6361706163697479292c207768696368206368616e6765732074686520656e747279277320606361706163697479602e204d7370436f756e7401001010000000001c4d012054686520616d6f756e74206f66204d61696e2053746f726167652050726f7669646572732074686174206172652063757272656e746c79207265676973746572656420696e207468652072756e74696d652e0019012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f66204d53507320696e207468652073797374656d2e007020546869732073746f72616765206973207570646174656420696e3a4502202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c2077686963682061646473206f6e6520746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722e7501202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c20776869636820737562747261637473206f6e652066726f6d20746869732073746f726167652e20427370436f756e7401001010000000001c55012054686520616d6f756e74206f66204261636b75702053746f726167652050726f7669646572732074686174206172652063757272656e746c79207265676973746572656420696e207468652072756e74696d652e0019012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f66204253507320696e207468652073797374656d2e007020546869732073746f72616765206973207570646174656420696e3a4d02202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c2077686963682061646473206f6e6520746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722e7501202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c20776869636820737562747261637473206f6e652066726f6d20746869732073746f726167652e44546f74616c42737073436170616369747901003020000000000000000020d02054686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920616c6c204253507320686176652e00c9012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920616c6c2042535073206861766520696e207468652073797374656d2c20776869636820697320616c736f207468658d0120746f74616c20616d6f756e74206f662073746f7261676520636170616369747920746861742063616e206265207573656420627920757365727320696620776520666163746f7220696e20746865207265706c69636174696f6e20666163746f722e007020546869732073746f72616765206973207570646174656420696e3afd02202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c207768696368206164647320746865206361706163697479206f662074686520726567697374657265642053746f726167652050726f766964657220746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722e4502202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682073756274726163747320746865206361706163697479206f6620746865204261636b75702053746f726167652050726f766964657220746f207369676e206f66662066726f6d20746869732073746f726167652e4055736564427370734361706163697479010030200000000000000000101d012054686520746f74616c20616d6f756e74206f662073746f72616765206361706163697479206f66204253507320746861742069732063757272656e746c7920696e207573652e00e9012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920746861742069732063757272656e746c7920696e207573652062792075736572732c2077686963682069732075736566756c20666f720d012073797374656d206d65747269637320616e6420616c736f20746f2063616c63756c617465207468652063757272656e74207072696365206f662073746f726167652e68476c6f62616c4273707352657075746174696f6e576569676874010010100000000004c02054686520746f74616c20676c6f62616c2072657075746174696f6e20776569676874206f6620616c6c20425350732e01190501f1014020547265617375727900800000000000000000000000000000000000000000000000000000000000000000106020546865205472656173757279204163636f756e7449642e5820546865206163636f756e7420746f2077686963683adc202d20546865206665657320666f72207375626d697474696e672061206368616c6c656e676520617265207472616e736665727265642e94202d2054686520736c61736865642066756e647320617265207472616e736665727265642e3053704d696e4465706f736974184000407a10f35a0000000000000000000004410120546865206d696e696d756d20616d6f756e74207468617420616e206163636f756e742068617320746f206465706f73697420746f206265636f6d6520612073746f726167652070726f76696465722e3453704d696e4361706163697479302002000000000000000481012054686520616d6f756e74207468617420612042535020726563656976657320617320616c6c6f636174696f6e206f662073746f72616765206361706163697479207768656e206974206465706f736974732053704d696e4465706f7369742e384465706f736974506572446174611840020000000000000000000000000000000411032054686520736c6f7065206f662074686520636f6c6c61746572616c2076732073746f726167652063617061636974792063757276652e20496e206f74686572207465726d732c20686f77206d616e7920746f6b656e7320612053746f726167652050726f76696465722073686f756c642061646420617320636f6c6c61746572616c20746f20696e637265617365206974732073746f7261676520636170616369747920696e206f6e6520756e6974206f662053746f7261676544617461556e69742e2c4d617846696c6553697a653020ffffffffffffffff0cbc2054686520657374696d61746564206d6178696d756d2073697a65206f6620616e20756e6b6e6f776e2066696c652e00b9012055736564207072696d6172696c7920746f20736c61736820612053746f726167652050726f7669646572207768656e206974206661696c7320746f2070726f766964652061206368756e6b206f66206461746120666f7220616e20756e6b6e6f776e2066696c652073697a652e4c4d61784d756c74694164647265737353697a65101064000000049020546865206d6178696d756d2073697a65206f662061206d756c7469616464726573732e544d61784d756c746941646472657373416d6f756e74101005000000041d0120546865206d6178696d756d20616d6f756e74206f66206d756c7469616464726573736573207468617420612053746f726167652050726f76696465722063616e20686176652e304d617850726f746f636f6c73101064000000044d0120546865206d6178696d756d206e756d626572206f662070726f746f636f6c7320746865204d53502063616e20737570706f727420286174206c656173742077697468696e207468652072756e74696d65292e284d61784275636b65747310101027000004cc20546865206d6178696d756d20616d6f756e74206f66204275636b65747320746861742061204d53502063616e20686176652e344275636b65744465706f736974184000407a10f35a0000000000000000000004f82054686520616d6f756e74207468617420616e206163636f756e742068617320746f206465706f73697420746f206372656174652061206275636b65742e3c4275636b65744e616d654c696d697410106400000004d82054797065207468617420726570726573656e7473207468652062797465206c696d6974206f662061206275636b6574206e616d652e584d6178426c6f636b73466f7252616e646f6d6e6573731010b004000004990120546865206d6178696d756d20616d6f756e74206f6620626c6f636b732061667465722077686963682061207369676e2075702072657175657374206578706972657320736f207468652072616e646f6d6e6573732063616e6e6f742062652063686f73656e7c4d696e426c6f636b734265747765656e43617061636974794368616e67657310100a00000004fc20546865206d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e206361706163697479206368616e67657320666f7220612053504444656661756c744d65726b6c65526f6f74348003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314042501205468652064656661756c742076616c7565206f662074686520726f6f74206f6620746865204d65726b6c652050617472696369612054726965206f66207468652072756e74696d6564536c617368416d6f756e745065724d617846696c6553697a6518400040e59c30120000000000000000000004c5012054686520736c61736820666163746f722064656475637465642066726f6d20612053746f726167652050726f76696465722773206465706f73697420666f722065766572792073696e676c652073746f726167652070726f6f662074686579206661696c20746f2070726f766964652e605374617274696e6752657075746174696f6e57656967687410100100000004dc205374617274696e672072657075746174696f6e2077656967687420666f722061206e65776c792072656769737465726564204253502e01bd06282846696c6553797374656d012846696c6553797374656d443c53746f7261676552657175657374730001040234c1060400004853746f7261676552657175657374427370730001080202d106d5060400186901204120646f75626c65206d61702066726f6d2073746f72616765207265717565737420746f2042535020604163636f756e7449646073207468617420766f6c756e74656572656420746f2073746f7265207468652066696c652e009d0120416e792042535020756e64657220612073746f7261676520726571756573742070726566697820697320636f6e7369646572656420746f206265206120766f6c756e7465657220616e642063616e2062652072656d6f76656420617420616e792074696d652e8102204f6e6365206120425350207375626d69747320612076616c69642070726f6f6620746f20746865207669612074686520606273705f636f6e6669726d5f73746f72696e67602065787472696e7369632c207468652060636f6e6669726d656460206669656c6420696e205b6053746f7261676552657175657374427370734d65746164617461605d2077696c6c2062652073657420746f206074727565602e00bd01205768656e20612073746f7261676520726571756573742069732065787069726564206f722072656d6f7665642c2074686520636f72726573706f6e64696e672073746f7261676520726571756573742070726566697820696e2074686973206d61702069732072656d6f7665642e684275636b6574735769746853746f7261676552657175657374730001080202d10698040004f420426f6f6b6b656570696e67206f6620746865206275636b65747320636f6e7461696e696e67206f70656e2073746f726167652072657175657374732e6453746f726167655265717565737445787069726174696f6e730101040210d906040004b42041206d6170206f6620626c6f636b7320746f20657870697265642073746f726167652072657175657374732e7846696c6544656c6574696f6e5265717565737445787069726174696f6e730101040210dd06040004cc2041206d6170206f6620626c6f636b7320746f20657870697265642066696c652064656c6574696f6e2072657175657374732e704d6f76654275636b65745265717565737445787069726174696f6e730101040210e906040004c42041206d6170206f6620626c6f636b7320746f2065787069726564206d6f7665206275636b65742072657175657374732ea84e657874417661696c61626c6553746f726167655265717565737445787069726174696f6e426c6f636b01001010000000000c5901204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e65772073746f7261676520726571756573742065787069726174696f6e2e006d0120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a53746f726167655265717565737454746c605d2ebc4e657874417661696c61626c6546696c6544656c6574696f6e5265717565737445787069726174696f6e426c6f636b01001010000000000c7101204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e65772066696c652064656c6574696f6e20726571756573742065787069726174696f6e2e009d0120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a50656e64696e6746696c6544656c6574696f6e5265717565737454746c605d2eb44e657874417661696c61626c654d6f76654275636b65745265717565737445787069726174696f6e426c6f636b01001010000000000c6901204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e6577206d6f7665206275636b657420726571756573742065787069726174696f6e2e00790120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a4d6f76654275636b65745265717565737454746c605d2e684e6578745374617274696e67426c6f636b546f436c65616e55700100101000000000141901204120706f696e74657220746f20746865207374617274696e6720626c6f636b20746f20636c65616e20757020657870697265642073746f726167652072657175657374732e006901204966207468697320626c6f636b20697320626568696e64207468652063757272656e7420626c6f636b206e756d6265722c2074686520636c65616e757020616c676f726974686d20696e20606f6e5f69646c65602077696c6c750120617474656d707420746f20616363656c6572617465207468697320626c6f636b20706f696e74657220617320636c6f736520746f206f7220757020746f207468652063757272656e7420626c6f636b206e756d6265722e205468697319012077696c6c20657865637574652070726f7669646564207468617420746865726520697320656e6f7567682072656d61696e696e672077656967687420746f20646f20736f2e6c50656e64696e6746696c6544656c6574696f6e52657175657374730101040200f10604000c802050656e64696e672066696c652064656c6574696f6e2072657175657374732e00f5012041206d617070696e672066726f6d20612075736572206163636f756e7420696420746f2061206c697374206f662070656e64696e672066696c652064656c6574696f6e2072657175657374732c20686f6c64696e672061207475706c65206f66207468652066696c65206b657920616e64206275636b65742069642e6850656e64696e6753746f7053746f72696e6752657175657374730001080202d106f506040018902050656e64696e672066696c652073746f702073746f72696e672072657175657374732e001102204120646f75626c65206d617070696e672066726f6d204253502049447320746f2061206c697374206f662066696c65206b6579732070656e64696e672073746f702073746f72696e6720726571756573747320746f2074686520626c6f636b20696e2077686963682074686f73652072657175657374732077657265206f70656e65648420616e64207468652070726f76656e2073697a65206f66207468652066696c652efd012054686520626c6f636b206e756d626572206973207573656420746f2061766f69642042535073206265696e672061626c6520746f2073746f702073746f72696e672066696c657320696d6d6564696174656c7920776869636820776f756c6420616c6c6f77207468656d20746f2061766f6964206368616c6c656e676573d501206f66206d697373696e672066696c65732e205468652073697a6520697320746f2062652061626c6520746f2064656372656173652074686569722075736564206361706163697479207768656e207468657920636f6e6669726d20746f2073746f702073746f72696e67207468652066696c652e6450656e64696e674d6f76654275636b657452657175657374730001080202d106f906040010782050656e64696e67206d6f7665206275636b65742072657175657374732e009501204120646f75626c65206d617070696e672066726f6d204d53502049447320746f2061206c697374206f66206275636b65742049447320776869636820746865792063616e20616363657074206f72206465636c696e6520746f2074616b65206f7665722eb8205468652076616c75652069732074686520757365722077686f2072657175657374656420746865206d6f76652e604461746153657276657273466f724d6f76654275636b65740001080202d10698040004ac204253502064617461207365727665727320666f72206d6f7665206275636b65742072657175657374732e5050656e64696e674275636b657473546f4d6f76650101040234980004090120426f6f6b6b656570696e67206f66206275636b6574732074686174206172652070656e64696e6720746f206265206d6f76656420746f2061206e6577204d53502e445265706c69636174696f6e54617267657401001010000000000cd4204e756d626572206f66204253507320726571756972656420746f2066756c66696c6c20612073746f72616765207265717565737400b501205468697320697320616c736f207573656420617320612064656661756c742076616c756520696620746865204253507320726571756972656420617265206e6f7420737065636966696564207768656e206372656174696e6720612073746f7261676520726571756573742e6c5469636b52616e6765546f4d6178696d756d5468726573686f6c64010010100000000004ed01204e756d626572206f66207469636b7320756e74696c20616c6c204253507320776f756c6420726561636820746865205b60436f6e6669673a3a4d6178696d756d5468726573686f6c64605d20746f20656e73757265207468617420616c6c2042535073206172652061626c6520746f20766f6c756e746565722e011d05010d0238604d61784273707350657253746f726167655265717565737410100500000010e8204d6178696d756d206e756d626572206f662053507320284d5350202b20425350732920746861742063616e2073746f726520612066696c652e0055012054686973206973207573656420746f206c696d697420746865206e756d626572206f6620425350732073746f72696e6720612066696c6520616e6420636c61696d696e67207265776172647320666f722069742ead012049662074686973206e756d62657220697320746f6f20686967682c207468656e207468652072657761726420666f722073746f72696e6720612066696c65206d6967687420626520746f2064696c7574656420616e6420706f696e746c65737320746f2073746f72652e784d61784261746368436f6e6669726d53746f72616765526571756573747310100a000000049101204d6178696d756d206261746368206f662073746f7261676520726571756573747320746861742063616e20626520636f6e6669726d6564206174206f6e6365207768656e2063616c6c696e6720606273705f636f6e6669726d5f73746f72696e67602e844d617842617463684d7370526573706f6e6453746f72616765526571756573747310100a000000040502204d6178696d756d206261746368206f662073746f7261676520726571756573747320746861742063616e20626520726573706f6e64656420746f206174206f6e6365207768656e2063616c6c696e6720606d73705f726573706f6e645f73746f726167655f72657175657374735f6d756c7469706c655f6275636b657473602e3c4d617846696c655061746853697a651010000200000488204d6178696d756d20627974652073697a65206f6620612066696c6520706174682e344d617850656572496453697a651010640000000480204d6178696d756d20627974652073697a65206f66206120706565722069642e484d61784e756d6265724f665065657249647310100500000004c8204d6178696d756d206e756d626572206f6620706565722069647320666f7220612073746f7261676520726571756573742e6c4d6178446174615365727665724d756c746941646472657373657310100a00000004e0204d6178696d756d206e756d626572206f66206d756c746961646472657373657320666f7220612073746f7261676520726571756573742e584d6178457870697265644974656d73496e426c6f636b101064000000042901204d6178696d756d206e756d626572206f662065787069726564206974656d73202870657220747970652920746f20636c65616e20757020696e20612073696e676c6520626c6f636b2e4453746f726167655265717565737454746c10102800000004902054696d652d746f2d6c69766520666f7220612073746f7261676520726571756573742e7450656e64696e6746696c6544656c6574696f6e5265717565737454746c10102800000004e1012054696d652d746f2d6c69766520666f7220612070656e64696e672066696c652064656c6574696f6e20726571756573742c2061667465722077686963682061207072696f72697479206368616c6c656e67652069732073656e74206f757420746f20656e666f726365207468652064656c6574696f6e2e504d6f76654275636b65745265717565737454746c101028000000045d012054696d652d746f2d6c69766520666f722061206d6f7665206275636b657420726571756573742c20616674657220776869636820746865207265717565737420697320636f6e7369646572656420657870697265642e784d61785573657250656e64696e6744656c6574696f6e526571756573747310100a000000040901204d6178696d756d206e756d626572206f662066696c652064656c6574696f6e207265717565737473206120757365722063616e20686176652070656e64696e672e804d61785573657250656e64696e674d6f76654275636b6574526571756573747310100a000000040101204d6178696d756d206e756d626572206f66206d6f7665206275636b6574207265717565737473206120757365722063616e20686176652070656e64696e672e544d696e57616974466f7253746f7053746f72696e6710100a000000040d02204e756d626572206f6620626c6f636b7320726571756972656420746f2070617373206265747765656e2061204253502072657175657374696e6720746f2073746f702073746f72696e6720612066696c6520616e64206974206265696e672061626c6520746f20636f6e6669726d20746f2073746f702073746f72696e672069742e01fd06293050726f6f66734465616c6572013050726f6f66734465616c657238505469636b546f4368616c6c656e6765735365656400010402103404001085012041206d617070696e672066726f6d206368616c6c656e676573207469636b20746f20612072616e646f6d2073656564207573656420666f722067656e65726174696e6720746865206368616c6c656e67657320696e2074686174207469636b2e0001012054686973206973207573656420746f206b65657020747261636b206f6620746865206368616c6c656e67657327207365656420696e2074686520706173742e8d012054686973206d617070696e6720676f6573206261636b206f6e6c79205b604368616c6c656e6765486973746f72794c656e677468466f72605d20626c6f636b732e2050726576696f7573206368616c6c656e676573206172652072656d6f7665642e685469636b546f436865636b706f696e744368616c6c656e6765730001040210890204001851012041206d617070696e672066726f6d206368616c6c656e676573207469636b20746f206120766563746f72206f6620637573746f6d206368616c6c656e676564206b65797320666f722074686174207469636b2e006d012054686973206973207573656420746f206b65657020747261636b206f6620746865206368616c6c656e67657320746861742068617665206265656e206d61646520696e2074686520706173742c207370656369666963616c6c799020696e2074686520636865636b706f696e74206368616c6c656e676520726f756e64732ef42054686520766563746f7220697320626f756e646564206279205b604d6178437573746f6d4368616c6c656e676573506572426c6f636b466f72605d2e89012054686973206d617070696e6720676f6573206261636b206f6e6c79205b604368616c6c656e6765486973746f72794c656e677468466f72605d207469636b732e2050726576696f7573206368616c6c656e676573206172652072656d6f7665642e484c617374436865636b706f696e745469636b010010100000000018ec20546865206368616c6c656e6765207469636b206f6620746865206c61737420636865636b706f696e74206368616c6c656e676520726f756e642e0069012054686973206973207573656420746f2064657465726d696e65207768656e20746f20696e636c75646520746865206368616c6c656e6765732066726f6d20746865205b604368616c6c656e6765735175657565605d20616e647d01205b605072696f726974794368616c6c656e6765735175657565605d20696e20746865205b605469636b546f436865636b706f696e744368616c6c656e676573605d2053746f726167654d61702e20546865736520636865636b706f696e744d01206368616c6c656e676520726f756e6473206861766520746f20626520616e73776572656420627920414c4c2050726f7669646572732c20616e64207468697320697320656e666f72636564206279207468656820607375626d69745f70726f6f66602065787472696e7369632e605469636b546f50726f766964657273446561646c696e6573000108020201079804002045012041206d617070696e672066726f6d206368616c6c656e6765207469636b20746f206120766563746f72206f66206368616c6c656e6765642050726f76696465727320666f722074686174207469636b2e0049012054686973206973207573656420746f206b65657020747261636b206f66207468652050726f76696465727320746861742068617665206265656e206368616c6c656e6765642c20616e642073686f756c644901207375626d697420612070726f6f66206279207468652074696d65206f6620746865205b604368616c6c656e6765735469636b6572605d207265616368657320746865206e756d62657220757365642061735901206b657920696e20746865206d617070696e672e2050726f7669646572732077686f20646f207375626d697420612070726f6f66206172652072656d6f7665642066726f6d20746865697220726573706563746976653d0120656e74727920616e642070757368656420666f727761726420746f20746865206e657874207469636b20696e20776869636820746865792073686f756c64207375626d697420612070726f6f662e4d012054686f73652077686f20617265207374696c6c20696e2074686520656e747279206279207468652074696d6520746865207469636b20697320726561636865642061726520636f6e7369646572656420746fdc2068617665206661696c656420746f207375626d697420612070726f6f6620616e64207375626a65637420746f20736c617368696e672e884c6173745469636b50726f76696465725375626d69747465644150726f6f66466f7200010402341004002465012041206d617070696e672066726f6d20612050726f766964657220746f20746865206c617374207469636b20666f7220776869636820746865792053484f554c442068617665207375626d697474656420612070726f6f662e410120496620666f7220612050726f7669646572206070602c20604c6173745469636b50726f76696465725375626d69747465644150726f6f66466f725b705d6020697320606e602c207468656e2074686531012050726f76696465722073686f756c64207375626d697420612070726f6f6620666f72207469636b20606e202b207374616b655f746f5f6368616c6c656e67655f706572696f64287029602e007101205468697320676574732075706461746564207768656e20612050726f7669646572207375626d69747320612070726f6f66207375636365737366756c6c7920616e64206973207573656420746f2064657465726d696e65207468652d01206e657874207469636b20666f72207768696368207468652050726f76696465722073686f756c64207375626d697420612070726f6f662c20616e64206974277320646561646c696e652e007101204966207468652050726f7669646572206661696c7320746f207375626d697420612070726f6f6620696e2074696d6520616e6420697320736c61736865642c20746869732077696c6c207374696c6c206765742075706461746564c820746f20746865207469636b2069742073686f756c642068617665207375626d697474656420612070726f6f6620666f722e3c4368616c6c656e676573517565756501000507040018d02041207175657565206f66206b65797320746861742068617665206265656e206368616c6c656e676564206d616e75616c6c792e0011012054686520656c656d656e747320696e20746869732071756575652077696c6c206265206368616c6c656e67656420696e2074686520636f6d696e6720626c6f636b732c450120616c7761797320656e737572696e67207468617420746865206d6178696d756d206e756d626572206f66206368616c6c656e6765732070657220626c6f636b206973206e6f742065786365656465642e350120412060426f756e64656456656360206973207573656420626563617573652074686520607061726974795f7363616c655f636f6465633a3a4d6178456e636f6465644c656e6020747261697455012069732072657175697265642c20627574207573696e672061206056656344657175656020776f756c64206265206d6f726520656666696369656e7420617320746869732069732061204649464f2071756575652e5c5072696f726974794368616c6c656e676573517565756501000907040028f42041207072696f72697479207175657565206f66206b65797320746861742068617665206265656e206368616c6c656e676564206d616e75616c6c792e0025012054686520646966666572656e6365206265747765656e207468697320616e6420604368616c6c656e676573517565756560206973207468617420746865206368616c6c656e676573290120696e20746869732071756575652061726520676976656e207072696f72697479206f76657220746865206f74686572732e20536f20746869732071756575652073686f756c64206265390120656d7074696564206265666f726520616e79206f6620746865206368616c6c656e67657320696e2074686520604368616c6c656e6765735175657565602061726520646973706174636865642ecc20546869732071756575652073686f756c64206e6f742062652061636365737369626c6520746f20746865207075626c69632e11012054686520656c656d656e747320696e20746869732071756575652077696c6c206265206368616c6c656e67656420696e2074686520636f6d696e6720626c6f636b732c450120616c7761797320656e737572696e67207468617420746865206d6178696d756d206e756d626572206f66206368616c6c656e6765732070657220626c6f636b206973206e6f742065786365656465642e350120412060426f756e64656456656360206973207573656420626563617573652074686520607061726974795f7363616c655f636f6465633a3a4d6178456e636f6465644c656e6020747261697455012069732072657175697265642c20627574207573696e672061206056656344657175656020776f756c64206265206d6f726520656666696369656e7420617320746869732069732061204649464f2071756575652e404368616c6c656e6765735469636b6572010010100000000018e8204120636f756e746572206f6620626c6f636b7320696e207768696368206368616c6c656e67657320776572652064697374726962757465642e004101205468697320636f756e746572206973206e6f74206e65636573736172696c79207468652073616d652061732074686520626c6f636b206e756d6265722c206173206368616c6c656e67657320617265490120646973747269627574656420696e2074686520606f6e5f706f6c6c6020686f6f6b2c2077686963682068617070656e732061742074686520626567696e6e696e67206f6620657665727920626c6f636b2ced0120736f206c6f6e672061732074686520626c6f636b206973206e6f742070617274206f662061205b4d756c74692d426c6f636b2d4d6967726174696f6e5d2868747470733a2f2f6769746875622e636f6d2f706172697479746563682f706f6c6b61646f742d73646b2f70756c6c2f313738312920284d424d292e350120447572696e67204d424d736d2c2074686520626c6f636b206e756d62657220696e637265617365732c20627574205b604368616c6c656e6765735469636b6572605d20646f6573206e6f742e48536c61736861626c6550726f7669646572730001040234100400007456616c696450726f6f665375626d6974746572734c6173745469636b7300010402100d0704001495012041206d617070696e672066726f6d207469636b20746f2050726f7669646572732c20776869636820697320736574206966207468652050726f7669646572207375626d697474656420612076616c69642070726f6f6620696e2074686174207469636b2e005d012054686973206973207573656420746f206b65657020747261636b206f66207468652050726f76696465727320746861742068617665207375626d69747465642070726f6f667320696e20746865206c61737420666577b901207469636b732c20776865726520617661696c6162696c697479206f6e6c7920757020746f20746865206c617374205b60436f6e6669673a3a5461726765745469636b7353746f726167654f665375626d697474657273605d207469636b732069732067756172616e746565642e950120546869732073746f72616765206973207468656e206d61646520617661696c61626c6520666f72206f746865722070616c6c65747320746f20757365207468726f75676820746865206050726f6f665375626d697474657273496e74657266616365602e3c4c61737444656c657465645469636b010010100000000010b90120412076616c7565207468617420726570726573656e747320746865206c617374207469636b2074686174207761732064656c657465642066726f6d20746865205b6056616c696450726f6f665375626d6974746572734c6173745469636b73605d2053746f726167654d61702e00a5012054686973206973207573656420746f206b6e6f77207768696368207469636b20746f2064656c6574652066726f6d20746865205b6056616c696450726f6f665375626d6974746572734c6173745469636b73605d2053746f726167654d6170207768656e207468656820606f6e5f69646c656020686f6f6b2069732063616c6c65642e584368616c6c656e6765735469636b65725061757365640000980400201901204120626f6f6c65616e207468617420726570726573656e7473207768657468657220746865205b604368616c6c656e6765735469636b6572605d206973207061757365642e00c9012042792064656661756c742c2074686973206973206066616c7365602c206d65616e696e67207468617420746865205b604368616c6c656e6765735469636b6572605d20697320696e6372656d656e7465642065766572792074696d6520606f6e5f706f6c6c602069732063616c6c65642ec10120546869732063616e2062652073657420746f2060747275656020776869636820776f756c6420706175736520746865205b604368616c6c656e6765735469636b6572605d2c2070726576656e74696e672060646f5f6e65775f6368616c6c656e6765735f726f756e64602066726f6d6c206265696e672065786563757465642e205468657265666f72653a4d01202d204e6f206e65772072616e646f6d206368616c6c656e67657320776f756c6420626520656d697474656420616e6420616464656420746f205b605469636b546f4368616c6c656e67657353656564605d2e7501202d204e6f206e657720636865636b706f696e74206368616c6c656e67657320776f756c6420626520656d697474656420616e6420616464656420746f205b605469636b546f436865636b706f696e744368616c6c656e676573605d2ef8202d20446561646c696e657320666f722070726f6f66207375626d697373696f6e732061726520696e646566696e6974656c7920706f7374706f6e65642e4050617374426c6f636b73576569676874000104021028040010f82041206d617070696e672066726f6d20626c6f636b206e756d62657220746f2074686520776569676874207573656420696e207468617420626c6f636b2e0021012054686973206973207573656420746f20636865636b20696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e750120497420697320636c656172656420666f7220626c6f636b73206f6c646572207468616e206063757272656e745f626c6f636b60202d20285b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d202b2031292e484e6f7446756c6c426c6f636b73436f756e7401001010000000000c9d0120546865206e756d626572206f6620626c6f636b7320746861742068617665206265656e20636f6e73696465726564205f6e6f745f2066756c6c20696e20746865206c617374205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d2e0021012054686973206973207573656420746f20636865636b20696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e015105015d023c6052616e646f6d4368616c6c656e676573506572426c6f636b10100a00000008510120546865206e756d626572206f662072616e646f6d206368616c6c656e6765732074686174206172652067656e6572617465642070657220626c6f636b2c207573696e67207468652072616e646f6d2073656564682067656e65726174656420666f72207468617420626c6f636b2e6c4d6178437573746f6d4368616c6c656e676573506572426c6f636b10100a000000045d0120546865206d6178696d756d206e756d626572206f6620637573746f6d206368616c6c656e67657320746861742063616e206265206d61646520696e20612073696e676c6520636865636b706f696e7420626c6f636b2e584368616c6c656e6765486973746f72794c656e6774681010640000000ce420546865206e756d626572206f66207469636b732074686174206368616c6c656e67657320686973746f7279206973206b65707420666f722e61012041667465722074686973206d616e79207469636b732c206368616c6c656e676573206172652072656d6f7665642066726f6d205b605469636b546f4368616c6c656e67657353656564605d2053746f726167654d61702e4501204120227469636b2220697320757375616c6c79206f6e6520626c6f636b2c2062757420736f6d6520626c6f636b73206d617920626520736b69707065642064756520746f206d6967726174696f6e732e544368616c6c656e67657351756575654c656e6774681010640000000cc820546865206c656e677468206f662074686520604368616c6c656e6765735175657565602053746f7261676556616c75652e1101205468697320697320746f206c696d6974207468652073697a65206f66207468652071756575652c20616e64207468657265666f726520746865206e756d626572206f6690206d616e75616c206368616c6c656e67657320746861742063616e206265206d6164652e64436865636b706f696e744368616c6c656e6765506572696f641010b80b000024750120546865206e756d626572206f6620626c6f636b7320696e206265747765656e206120636865636b706f696e74206368616c6c656e67657320726f756e642028692e652e207769746820637573746f6d206368616c6c656e676573292e61012054686973206973207573656420746f2064657465726d696e65207768656e20746f20696e636c75646520746865206368616c6c656e6765732066726f6d2074686520604368616c6c656e67657351756575656020616e64710120605072696f726974794368616c6c656e67657351756575656020696e207468652060426c6f636b546f4368616c6c656e676573602053746f726167654d61702e20546865736520636865636b706f696e74206368616c6c656e6765610120726f756e6473206861766520746f20626520616e73776572656420627920414c4c2050726f7669646572732c20616e64207468697320697320656e666f726365642062792074686520607375626d69745f70726f6f66602c2065787472696e7369632e006d01205741524e494e473a205468697320706572696f64206e6565647320746f20626520657175616c206f72206c6172676572207468616e20746865206368616c6c656e676520706572696f64206f662074686520736d616c6c6573747d012050726f766964657220696e20746865206e6574776f726b2e2049662074686520736d616c6c6573742050726f7669646572206861732061206368616c6c656e676520706572696f64206f66203130207469636b732028626c6f636b73292c1101207468656e2074686520636865636b706f696e74206368616c6c656e676520706572696f64206e6565647320746f206265206174206c65617374203130207469636b732e585374616b65546f4368616c6c656e6765506572696f64184000009e1869d0290400000000000000000cd42054686520726174696f20746f20636f6e76657274207374616b65642062616c616e636520746f20626c6f636b20706572696f642e65012054686973206973207573656420746f2064657465726d696e652074686520706572696f6420696e20776869636820612050726f76696465722073686f756c64207375626d697420612070726f6f662c206261736564206f6ee501207468656972207374616b652e2054686520706572696f642069732063616c63756c6174656420617320605374616b65546f4368616c6c656e6765506572696f64202f207374616b65602c2073617475726174696e67206174205b60436f6e6669673a3a4d696e4368616c6c656e6765506572696f64605d2e484d696e4368616c6c656e6765506572696f6410101e00000004550120546865206d696e696d756d20706572696f6420696e20776869636820612050726f76696465722063616e206265206368616c6c656e6765642c207265676172646c657373206f66207468656972207374616b652e5c4368616c6c656e67655469636b73546f6c6572616e63651010320000001c59012054686520746f6c6572616e636520696e206e756d626572206f66207469636b732028616c6d6f7374206571756976616c656e7420746f20626c6f636b732c2062757420736b697070696e67204d424d292074686174590120612050726f76696465722068617320746f207375626d697420612070726f6f662c20636f756e74696e672066726f6d20746865207469636b20746865206368616c6c656e676520697320656d697474656420666f723c20746861742050726f76696465722e00650120466f72206578616d706c652c20696620612050726f766964657220697320737570706f73656420746f207375626d697420612070726f6f6620666f72207469636b20606e602c20616e642074686520746f6c6572616e636569012069732073657420746f206074602c207468656e207468652050726f76696465722068617320746f207375626d697420612070726f6f6620666f72206368616c6c656e67657320696e207469636b20606e602c206265666f72652420606e202b2074602e344368616c6c656e67657346656518400010a5d4e800000000000000000000000cb02054686520666565206368617267656420666f72207375626d697474696e672061206368616c6c656e67652e550120546869732066656520676f657320746f207468652054726561737572792c20616e64206973207573656420746f2070726576656e74207370616d2e20526567697374657265642050726f7669646572732061726558206578656d70742066726f6d2074686973206665652e785461726765745469636b7353746f726167654f665375626d6974746572731010030000000c89012054686520746172676574206e756d626572206f66207469636b7320666f7220776869636820746f2073746f726520746865207375626d6974746572732074686174207375626d69747465642076616c69642070726f6f667320696e207468656d2cad012073746f72656420696e20746865206056616c696450726f6f665375626d6974746572734c6173745469636b73602053746f726167654d61702e20546861742073746f726167652077696c6c206265207472696d6d656420646f776e20746f2074686973206e756d6265723101206f66207469636b7320696e2074686520606f6e5f69646c656020686f6f6b206f6620746869732070616c6c65742c20746f2061766f696420626c6f6174696e67207468652073746174652e504d61785375626d6974746572735065725469636b1010e8030000102d0120546865206d6178696d756d20616d6f756e74206f662050726f76696465727320746861742063616e207375626d697420612070726f6f6620696e20612073696e676c6520626c6f636b2e410120416c74686f75676820746869732063616e206265207365656e20617320616e20617262697472617279206c696d69742c2069662073657420746f2074686520616c7265616479206578697374696e67410120696d706c69636974206c696d697420746861742069732022686f77206d616e7920607375626d69745f70726f6f66602065787472696e736963732066697420696e2074686520776569676874206f66f0206120626c6f636b2c207468697320776f756c646e27742061646420616e79206164646974696f6e616c206172746966696369616c206c696d69742e20547265617375727900800000000000000000000000000000000000000000000000000000000000000000106020546865205472656173757279204163636f756e7449642e5820546865206163636f756e7420746f2077686963683adc202d20546865206665657320666f72207375626d697474696e672061206368616c6c656e676520617265207472616e736665727265642e94202d2054686520736c61736865642066756e647320617265207472616e736665727265642e4c426c6f636b46756c6c6e657373506572696f6410103200000024f82054686520706572696f64206f6620626c6f636b7320666f722077686963682074686520626c6f636b2066756c6c6e65737320697320636865636b65642e00690120546869732069732074686520616d6f756e74206f6620626c6f636b732066726f6d2074686520706173742c20666f722077686963682074686520626c6f636b2066756c6c6e65737320686173206265656e20636865636b65645d0120616e642069732073746f7265642e20426c6f636b73206f6c646572207468616e206063757272656e745f626c6f636b60202d205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d206172655820636c65617265642066726f6d2073746f726167652e008101205468697320636f6e7374616e742073686f756c6420626520657175616c206f7220736d616c6c6572207468616e20746865205b60436f6e6669673a3a4368616c6c656e67655469636b73546f6c6572616e6365605d20636f6e7374616e742c7d012069662074686520676f616c20697320746f2070726576656e74207370616d6d696e672061747461636b73207468617420776f756c642070726576656e7420686f6e6573742050726f7669646572732066726f6d207375626d697474696e67582074686569722070726f6f667320696e2074696d652e54426c6f636b46756c6c6e65737348656164726f6f6d2818a291e146d10414390120546865206d696e696d756d20756e75736564207765696768742074686174206120626c6f636b206d757374206861766520746f20626520636f6e73696465726564205f6e6f745f2066756c6c2e009101205468697320697320757365642061732070617274206f662074686520637269746572696120666f7220636865636b696e6720696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e890120466f72206578616d706c652c20746869732063616e2062652073657420746f207468652062656e63686d61726b656420776569676874206f66206120607375626d69745f70726f6f66602065787472696e7369632c20776869636820776f756c648d01206d65616e2074686174206120626c6f636b206973206e6f7420636f6e736964657265642066756c6c206966206120607375626d69745f70726f6f66602065787472696e73696320636f756c642068617665207374696c6c2066697420696e2069742e544d696e4e6f7446756c6c426c6f636b73526174696f1507100065cd1d24710120546865206d696e696d756d20726174696f20286f722070657263656e7461676520696620796f752077696c6c29206f6620626c6f636b732074686174206d75737420626520636f6e73696465726564205f6e6f745f2066756c6c2c51012066726f6d2074686520746f74616c206e756d626572206f66205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d20626c6f636b732074616b656e20696e746f206163636f756e742e008501204966206c657373207468616e20746869732070657263656e74616765206f6620626c6f636b7320617265206e6f742066756c6c2c20746865206e6574776f726b7320697320636f6e7369646572656420746f2062652070726573756d61626c795420756e6465722061207370616d2061747461636b2e890120546869732063616e20616c736f2062652074686f75676874206f6620617320746865206d6178696d756d20726174696f206f66206d69736265686176696e6720636f6c6c61746f727320746f6c6572617465642e20466f72206578616d706c652ca10120696620746869732069732073657420746f206050657262696c6c3a3a66726f6d5f70657263656e7428353029602c207468656e206966206d6f7265207468616e2068616c66206f6620746865206c6173742060426c6f636b46756c6c6e657373506572696f6460850120626c6f636b7320617265206e6f742066756c6c2c207468656e206f6e65206f662074686f736520626c6f636b7320737572656c79207761732070726f647563656420627920616e20686f6e65737420636f6c6c61746f722c206d65616e696e677501207468617420746865726520776173206174206c65617374206f6e65207472756c79205f6e6f745f2066756c6c20626c6f636b20696e20746865206c6173742060426c6f636b46756c6c6e657373506572696f646020626c6f636b732e0119072a2852616e646f6d6e657373012852616e646f6d6e657373106c4c61746573744f6e6545706f636841676f52616e646f6d6e65737300001d070400042d02204c61746573742072616e646f6d2073656564206f627461696e65642066726f6d20746865206f6e652065706f63682061676f2072616e646f6d6e6573732066726f6d20424142452c20616e6420746865206c617465737420626c6f636b20746861742069742063616e2070726f636573732072616e646f6d6e6573732072657175657374732066726f6d6c4c6174657374506172656e74426c6f636b52616e646f6d6e65737300001d070400042902204c61746573742072616e646f6d2073656564206f627461696e65642066726f6d2074686520706172656e7420626c6f636b2072616e646f6d6e6573732066726f6d20424142452c20616e6420746865206c617465737420626c6f636b20746861742069742063616e2070726f636573732072616e646f6d6e6573732072657175657374732066726f6d2852656c617945706f636801003020000000000000000004502043757272656e742072656c61792065706f636840496e686572656e74496e636c75646564000098040004e420456e737572657320746865206d616e6461746f727920696e686572656e742077617320696e636c7564656420696e2074686520626c6f636b01550501a50200002b385061796d656e7453747265616d7301385061796d656e7453747265616d7320304f6e506f6c6c5469636b65720100101000000000180901204120636f756e746572206f6620626c6f636b7320666f722077686963682050726f7669646572732063616e206368617267652074686569722073747265616d732e009d01205468697320636f756e746572206973206e6f74206e65636573736172696c79207468652073616d652061732074686520626c6f636b206e756d6265722c20617320746865206c6173742063686172676561626c6520696e666f206f662050726f766964657273bd012028616e642074686520676c6f62616c20707269636520696e6465782920617265207570646174656420696e2074686520606f6e5f706f6c6c6020686f6f6b2c2077686963682068617070656e732061742074686520626567696e6e696e67206f6620657665727920626c6f636b2ced0120736f206c6f6e672061732074686520626c6f636b206973206e6f742070617274206f662061205b4d756c74692d426c6f636b2d4d6967726174696f6e5d2868747470733a2f2f6769746875622e636f6d2f706172697479746563682f706f6c6b61646f742d73646b2f70756c6c2f313738312920284d424d292e190120447572696e67204d424d732c2074686520626c6f636b206e756d62657220696e637265617365732c2062757420604f6e506f6c6c5469636b65726020646f6573206e6f742e5c4669786564526174655061796d656e7453747265616d7300010802022107250704002481012054686520646f75626c65206d617070696e672066726f6d20612050726f76696465722c20746f206974732070726f76696465642055736572732c20746f2074686569722066697865642d72617465207061796d656e742073747265616d732e0065012054686973206973207573656420746f2073746f726520616e64206d616e6167652066697865642d72617465207061796d656e742073747265616d73206265747765656e20557365727320616e642050726f7669646572732e007020546869732073746f72616765206973207570646174656420696e3aed01202d205b6164645f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e4902202d205b64656c6574655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a64656c6574655f66697865645f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e0102202d205b7570646174655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a7570646174655f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820757064617465732074686520656e7472792773206072617465602ee501202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820757064617465732074686520656e747279277320606c6173745f636861726765645f7469636b602e6444796e616d6963526174655061796d656e7453747265616d7300010802022107290704002489012054686520646f75626c65206d617070696e672066726f6d20612050726f76696465722c20746f206974732070726f76696465642055736572732c20746f2074686569722064796e616d69632d72617465207061796d656e742073747265616d732e006d012054686973206973207573656420746f2073746f726520616e64206d616e6167652064796e616d69632d72617465207061796d656e742073747265616d73206265747765656e20557365727320616e642050726f7669646572732e007020546869732073746f72616765206973207570646174656420696e3afd01202d205b6164645f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e5902202d205b64656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a64656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e3d02202d205b7570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a7570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820757064617465732074686520656e74727927732060616d6f756e745f70726f7669646564602e1502202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820757064617465732074686520656e7472792773206070726963655f696e6465785f7768656e5f6c6173745f63686172676564602e484c61737443686172676561626c65496e666f01010402342d0750000000000000000000000000000000000000000018750220546865206d617070696e672066726f6d20612050726f766964657220746f20697473206c6173742063686172676561626c6520707269636520696e6465782028666f722064796e616d69632d72617465207061796d656e742073747265616d732920616e64206c6173742063686172676561626c65207469636b2028666f722066697865642d72617465207061796d656e742073747265616d73292e0069022054686973206973207573656420746f206b65657020747261636b206f6620746865206c6173742063686172676561626c6520707269636520696e64657820616e64207469636b206e756d62657220666f7220656163682050726f76696465722c20736f20746869732070616c6c65742063616e2063686172676520746865207061796d656e742073747265616d7320636f72726563746c792e007020546869732073746f72616765206973207570646174656420696e3a6502202d205b7570646174655f6c6173745f63686172676561626c655f696e666f5d2863726174653a3a5061796d656e744d616e616765723a3a7570646174655f6c6173745f63686172676561626c655f696e666f292c20776869636820757064617465732074686520656e747279277320606c6173745f63686172676561626c655f7469636b6020616e64206070726963655f696e646578602e445573657273576974686f757446756e6473000104020010040020c50120546865206d617070696e672066726f6d2061207573657220746f20696620697420686173206265656e20666c616767656420666f72206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220697473207265717565737465642073657276696365732e00c5012054686973206973207573656420746f20666c6167207573657273207468617420646f206e6f74206861766520656e6f7567682066756e647320746f2070617920666f72207468656972207265717565737465642073657276696365732c20736f206f746865722050726f766964657273942063616e2073746f702070726f766964696e6720736572766963657320746f207468656d2e007020546869732073746f72616765206973207570646174656420696e3a9902202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820656d6974732061206055736572576974686f757446756e647360206576656e7420616e642073657473207468652075736572277320656e74727920696e2074686973206d617020696620697420646f6573206e6f745501206861766520656e6f7567682066756e64732c20616e6420636c656172732074686520656e747279206966206974207761732073657420616e642074686520757365722068617320656e6f7567682066756e64732e3c526567697374657265645573657273010104020010100000000028b90120546865206d617070696e672066726f6d2061207573657220746f20696620697420686173206265656e207265676973746572656420746f20746865206e6574776f726b20616e642074686520616d6f756e74206f66207061796d656e742073747265616d73206974206861732e0029022053696e6365207573657273206861766520746f2070726f766964652061206465706f73697420746f2062652061626c6520746f206f70656e2065616368207061796d656e742073747265616d2c2074686973206973207573656420746f206b65657020747261636b206f662074686520616d6f756e74206f66207061796d656e742073747265616d7361012074686174206120757365722068617320616e6420697420697320616c736f2075736566756c20746f20636865636b2069662061207573657220686173207265676973746572656420746f20746865206e6574776f726b2e007020546869732073746f72616765206973207570646174656420696e3a6902202d205b6164645f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820686f6c647320746865206465706f736974206f6620746865207573657220616e642061646473206f6e6520746f20746869732073746f726167652e7902202d205b6164645f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820686f6c647320746865206465706f736974206f6620746865207573657220616e642061646473206f6e6520746f20746869732073746f726167652e7102202d205b72656d6f76655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a72656d6f76655f66697865645f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f766573206f6e652066726f6d20746869732073746f7261676520616e642072656c656173657320746865206465706f7369742e8102202d205b72656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a72656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f766573206f6e652066726f6d20746869732073746f7261676520616e642072656c656173657320746865206465706f7369742e6843757272656e745072696365506572556e69745065725469636b0100184000000000000000000000000000000000181902205468652063757272656e742070726963652070657220756e697420706572207469636b206f66207468652070726f766964656420736572766963652c207573656420746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732e009102205468697320697320757064617465642065616368207469636b207573696e672074686520666f726d756c61207468617420636f6e7369646572732063757272656e742073797374656d2063617061636974792028746f74616c2073746f72616765206f66207468652073797374656d2920616e642073797374656d20617661696c6162696c6974792028746f74616c2073746f7261676520617661696c61626c65292e007020546869732073746f72616765206973207570646174656420696e3a6d02202d205b646f5f7570646174655f63757272656e745f70726963655f7065725f756e69745f7065725f7469636b5d2863726174653a3a7574696c733a3a646f5f7570646174655f63757272656e745f70726963655f7065725f756e69745f7065725f7469636b292c2077686963682075706461746573207468652063757272656e742070726963652070657220756e697420706572207469636b2e54416363756d756c617465645072696365496e646578010018400000000000000000000000000000000020d1012054686520616363756d756c6174656420707269636520696e6465782073696e63652067656e657369732c207573656420746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732e00f1012054686973206973206571756976616c656e7420746f207768617420697420776f756c64206861766520636f737420746f2073746f7265206f6e6520756e6974206f66207468652070726f766964656420736572766963652073696e63652074686520626567696e6e696e67206f6620746865206e6574776f726b2e050220576520757365207468697320746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732c20627920636865636b696e67206f75742074686520646966666572656e6365206265747765656e2074686520696e6465785501207768656e20746865207061796d656e742073747265616d20776173206c61737420636861726765642c20616e642074686520696e64657820617420746865206c6173742063686172676561626c65207469636b2e007020546869732073746f72616765206973207570646174656420696e3a2d02202d205b646f5f7570646174655f70726963655f696e6465785d2863726174653a3a7574696c733a3a646f5f7570646174655f70726963655f696e646578292c20776869636820757064617465732074686520616363756d756c6174656420707269636520696e6465782c20616464696e6720746f206974207468652063757272656e742070726963652e01590501a90208404e657753747265616d4465706f73697410100a00000014910120546865206e756d626572206f66207469636b73207468617420636f72726573706f6e6420746f20746865206465706f7369742074686174206120557365722068617320746f2070617920746f206f70656e2061207061796d656e742073747265616d2ea1012054686973206d65616e7320746861742c2066726f6d207468652062616c616e6365206f6620746865205573657220666f7220776869636820746865207061796d656e742073747265616d206973206265696e6720637265617465642c2074686520616d6f756e74d420604e657753747265616d4465706f736974202a2072617465602077696c6c2062652068656c642061732061206465706f7369742e250220496e207468652063617365206f662064796e616d69632d72617465207061796d656e742073747265616d732c206072617465602077696c6c2062652060616d6f756e745f70726f7669646564202a2063757272656e745f736572766963655f7072696365602c207768657265206063757272656e745f736572766963655f70726963656020686173310120746f2062652070726f7669646564206279207468652070616c6c6574207573696e672074686520605061796d656e7453747265616d73496e746572666163656020696e746572666163652e6055736572576974686f757446756e6473436f6f6c646f776e10106400000008ed0120546865206e756d626572206f66207469636b732074686174206120757365722077696c6c206861766520746f207761697420616674657220697420686173206265656e20666c616767656420617320776974686f75742066756e647320746f2062652061626c6520746f20636c656172207468617420666c6167c90120616e642062652061626c6520746f2070617920666f7220736572766963657320616761696e2e204966207468657265277320616e79206f75747374616e64696e672064656274207768656e2074686520666c616720697320636c65617265642c2069742077696c6c20626520706169642e0131072c284275636b65744e66747300015d0501ad02000135072d104e66747301104e6674733c28436f6c6c656374696f6e00010402103907040004642044657461696c73206f66206120636f6c6c656374696f6e2e4c4f776e657273686970416363657074616e636500010402001004000429012054686520636f6c6c656374696f6e2c20696620616e792c206f6620776869636820616e206163636f756e742069732077696c6c696e6720746f2074616b65206f776e6572736869702e1c4163636f756e7400010c0202023d0798040008590120546865206974656d732068656c6420627920616e7920676976656e206163636f756e743b20736574206f757420746869732077617920736f2074686174206974656d73206f776e656420627920612073696e676c656c206163636f756e742063616e20626520656e756d6572617465642e44436f6c6c656374696f6e4163636f756e74000108020241079804000869012054686520636f6c6c656374696f6e73206f776e656420627920616e7920676976656e206163636f756e743b20736574206f757420746869732077617920736f207468617420636f6c6c656374696f6e73206f776e65642062799020612073696e676c65206163636f756e742063616e20626520656e756d6572617465642e40436f6c6c656374696f6e526f6c654f66000108020245074907040008d020546865206974656d7320696e206578697374656e636520616e64207468656972206f776e6572736869702064657461696c732ea02053746f72657320636f6c6c656374696f6e20726f6c657320617320706572206163636f756e742e104974656d000108020205035107040004d020546865206974656d7320696e206578697374656e636520616e64207468656972206f776e6572736869702064657461696c732e50436f6c6c656374696f6e4d657461646174614f660001040210690704000468204d65746164617461206f66206120636f6c6c656374696f6e2e384974656d4d657461646174614f66000108020205036d0704000454204d65746164617461206f6620616e206974656d2e244174747269627574650001100202020275077907040004702041747472696275746573206f66206120636f6c6c656374696f6e2e2c4974656d50726963654f66000108020205038107040004502041207072696365206f6620616e206974656d2e644974656d41747472696275746573417070726f76616c734f6601010802020503850704000468204974656d2061747472696275746520617070726f76616c732e404e657874436f6c6c656374696f6e496400001004000831012053746f726573207468652060436f6c6c656374696f6e496460207468617420697320676f696e6720746f206265207573656420666f7220746865206e65787420636f6c6c656374696f6e2ef02054686973206765747320696e6372656d656e746564207768656e657665722061206e657720636f6c6c656374696f6e20697320637265617465642e3450656e64696e67537761704f66000108020205038d070400047c2048616e646c657320616c6c207468652070656e64696e672073776170732e48436f6c6c656374696f6e436f6e6669674f66000104021069050400046020436f6e666967206f66206120636f6c6c656374696f6e2e304974656d436f6e6669674f660001080202050395050400044c20436f6e666967206f6620616e206974656d2e01650501b1023844436f6c6c656374696f6e4465706f736974184000407a10f35a000000000000000000000401012054686520626173696320616d6f756e74206f662066756e64732074686174206d75737420626520726573657276656420666f7220636f6c6c656374696f6e2e2c4974656d4465706f73697418400010a5d4e8000000000000000000000004f42054686520626173696320616d6f756e74206f662066756e64732074686174206d75737420626520726573657276656420666f7220616e206974656d2e4c4d657461646174614465706f73697442617365184000a0724e180900000000000000000000044d012054686520626173696320616d6f756e74206f662066756e64732074686174206d757374206265207265736572766564207768656e20616464696e67206d6574616461746120746f20796f7572206974656d2e504174747269627574654465706f73697442617365184000a0724e1809000000000000000000000455012054686520626173696320616d6f756e74206f662066756e64732074686174206d757374206265207265736572766564207768656e20616464696e6720616e2061747472696275746520746f20616e206974656d2e384465706f7369745065724279746518400010a5d4e8000000000000000000000008590120546865206164646974696f6e616c2066756e64732074686174206d75737420626520726573657276656420666f7220746865206e756d626572206f662062797465732073746f726520696e206d657461646174612cc02065697468657220226e6f726d616c22206d65746164617461206f7220617474726962757465206d657461646174612e2c537472696e674c696d697410100001000004b020546865206d6178696d756d206c656e677468206f6620646174612073746f726564206f6e2d636861696e2e204b65794c696d697410104000000004a020546865206d6178696d756d206c656e677468206f6620616e20617474726962757465206b65792e2856616c75654c696d697410100001000004a820546865206d6178696d756d206c656e677468206f6620616e206174747269627574652076616c75652e38417070726f76616c734c696d697410101400000004a820546865206d6178696d756d20617070726f76616c7320616e206974656d20636f756c6420686176652e704974656d41747472696275746573417070726f76616c734c696d697410101400000004d420546865206d6178696d756d206174747269627574657320617070726f76616c7320616e206974656d20636f756c6420686176652e1c4d61785469707310100a00000004a820546865206d6178206e756d626572206f6620746970732061207573657220636f756c642073656e642e4c4d6178446561646c696e654475726174696f6e1010001a4f0004a820546865206d6178206475726174696f6e20696e20626c6f636b7320666f7220646561646c696e65732e504d61784174747269627574657350657243616c6c10100a00000004e020546865206d6178206e756d626572206f6620617474726962757465732061207573657220636f756c6420736574207065722063616c6c2e204665617475726573910720000000000000000004902044697361626c657320736f6d65206f662070616c6c657427732066656174757265732e0199073228506172616d65746572730128506172616d65746572730428506172616d657465727300010402d902f5020400044c2053746f72656420706172616d65746572732e01c10501d5020000339d07042848436865636b4e6f6e5a65726f53656e646572a5079840436865636b5370656356657273696f6ea9071038436865636b547856657273696f6ead071030436865636b47656e65736973b1073438436865636b4d6f7274616c697479b5073428436865636b4e6f6e6365bd07982c436865636b576569676874c10798604368617267655472616e73616374696f6e5061796d656e74c507985053746f726167655765696768745265636c61696dc9079844436865636b4d6574616461746148617368cd0780d507","id":"1"} \ No newline at end of file +{"jsonrpc":"2.0","result":"0x6d6574610ee507000c1c73705f636f72651863727970746f2c4163636f756e7449643332000004000401205b75383b2033325d0000040000032000000008000800000503000c08306672616d655f73797374656d2c4163636f756e74496e666f08144e6f6e636501102c4163636f756e74446174610114001401146e6f6e63651001144e6f6e6365000124636f6e73756d657273100120526566436f756e7400012470726f766964657273100120526566436f756e7400012c73756666696369656e7473100120526566436f756e740001106461746114012c4163636f756e74446174610000100000050500140c3c70616c6c65745f62616c616e6365731474797065732c4163636f756e7444617461041c42616c616e63650118001001106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000114666c6167731c01284578747261466c61677300001800000507001c0c3c70616c6c65745f62616c616e636573147479706573284578747261466c61677300000400180110753132380000200000050000240c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540128000c01186e6f726d616c2801045400012c6f7065726174696f6e616c280104540001246d616e6461746f7279280104540000280c2873705f77656967687473247765696768745f76321857656967687400000801207265665f74696d652c010c75363400012870726f6f665f73697a652c010c75363400002c000006300030000005060034083c7072696d69746976655f74797065731048323536000004000401205b75383b2033325d00003800000208003c102873705f72756e74696d651c67656e65726963186469676573741844696765737400000401106c6f677340013c5665633c4469676573744974656d3e000040000002440044102873705f72756e74696d651c67656e6572696318646967657374284469676573744974656d0001142850726552756e74696d650800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e00060024436f6e73656e7375730800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e000400105365616c0800480144436f6e73656e737573456e67696e654964000038011c5665633c75383e000500144f74686572040038011c5665633c75383e0000006452756e74696d65456e7669726f6e6d656e745570646174656400080000480000030400000008004c00000250005008306672616d655f73797374656d2c4576656e745265636f7264080445015404540134000c01147068617365fd02011450686173650001146576656e7454010445000118746f706963732d0201185665633c543e000054084c73746f726167655f6875625f72756e74696d653052756e74696d654576656e7400014c1853797374656d04005801706672616d655f73797374656d3a3a4576656e743c52756e74696d653e0000003c50617261636861696e53797374656d04007c01bc63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d3a3a4576656e743c52756e74696d653e0001002042616c616e636573040084017c70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e000a00485472616e73616374696f6e5061796d656e7404008c01a870616c6c65745f7472616e73616374696f6e5f7061796d656e743a3a4576656e743c52756e74696d653e000b00105375646f040090016c70616c6c65745f7375646f3a3a4576656e743c52756e74696d653e000f0044436f6c6c61746f7253656c656374696f6e0400a001a470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a4576656e743c52756e74696d653e0015001c53657373696f6e0400a8015470616c6c65745f73657373696f6e3a3a4576656e740016002458636d7051756575650400ac01a463756d756c75735f70616c6c65745f78636d705f71756575653a3a4576656e743c52756e74696d653e001e002c506f6c6b61646f7458636d0400b0016870616c6c65745f78636d3a3a4576656e743c52756e74696d653e001f002843756d756c757358636d0400dd01018863756d756c75735f70616c6c65745f78636d3a3a4576656e743c52756e74696d653e002000304d65737361676551756575650400e101019070616c6c65745f6d6573736167655f71756575653a3a4576656e743c52756e74696d653e0021002450726f7669646572730400f10101a070616c6c65745f73746f726167655f70726f7669646572733a3a4576656e743c52756e74696d653e0028002846696c6553797374656d04000d02018870616c6c65745f66696c655f73797374656d3a3a4576656e743c52756e74696d653e0029003050726f6f66734465616c657204005d02019070616c6c65745f70726f6f66735f6465616c65723a3a4576656e743c52756e74696d653e002a002852616e646f6d6e6573730400a502018470616c6c65745f72616e646f6d6e6573733a3a4576656e743c52756e74696d653e002b00385061796d656e7453747265616d730400a902019870616c6c65745f7061796d656e745f73747265616d733a3a4576656e743c52756e74696d653e002c00284275636b65744e6674730400ad02018870616c6c65745f6275636b65745f6e6674733a3a4576656e743c52756e74696d653e002d00104e6674730400b102016c70616c6c65745f6e6674733a3a4576656e743c52756e74696d653e00320028506172616d65746572730400d502018470616c6c65745f706172616d65746572733a3a4576656e743c52756e74696d653e00330000580c306672616d655f73797374656d1870616c6c6574144576656e7404045400011c4045787472696e7369635375636365737304013464697370617463685f696e666f5c01304469737061746368496e666f00000490416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656408013864697370617463685f6572726f7268013444697370617463684572726f7200013464697370617463685f696e666f5c01304469737061746368496e666f00010450416e2065787472696e736963206661696c65642e2c436f64655570646174656400020450603a636f6465602077617320757064617465642e284e65774163636f756e7404011c6163636f756e74000130543a3a4163636f756e7449640003046841206e6577206163636f756e742077617320637265617465642e344b696c6c65644163636f756e7404011c6163636f756e74000130543a3a4163636f756e74496400040458416e206163636f756e7420776173207265617065642e2052656d61726b656408011873656e646572000130543a3a4163636f756e7449640001106861736834011c543a3a48617368000504704f6e206f6e2d636861696e2072656d61726b2068617070656e65642e4455706772616465417574686f72697a6564080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c00060468416e20757067726164652077617320617574686f72697a65642e04704576656e7420666f72207468652053797374656d2070616c6c65742e5c0c346672616d655f737570706f7274206469737061746368304469737061746368496e666f00000c0118776569676874280118576569676874000114636c6173736001344469737061746368436c617373000120706179735f666565640110506179730000600c346672616d655f737570706f7274206469737061746368344469737061746368436c61737300010c184e6f726d616c0000002c4f7065726174696f6e616c000100244d616e6461746f727900020000640c346672616d655f737570706f727420646973706174636810506179730001080c596573000000084e6f0001000068082873705f72756e74696d653444697370617463684572726f72000138144f746865720000003043616e6e6f744c6f6f6b7570000100244261644f726967696e000200184d6f64756c6504006c012c4d6f64756c654572726f7200030044436f6e73756d657252656d61696e696e670004002c4e6f50726f76696465727300050040546f6f4d616e79436f6e73756d65727300060014546f6b656e0400700128546f6b656e4572726f720007002841726974686d65746963040074013c41726974686d657469634572726f72000800345472616e73616374696f6e616c04007801485472616e73616374696f6e616c4572726f7200090024457868617573746564000a0028436f7272757074696f6e000b002c556e617661696c61626c65000c0038526f6f744e6f74416c6c6f776564000d00006c082873705f72756e74696d652c4d6f64756c654572726f720000080114696e64657808010875380001146572726f7248018c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d000070082873705f72756e74696d6528546f6b656e4572726f720001284046756e6473556e617661696c61626c65000000304f6e6c7950726f76696465720001003042656c6f774d696e696d756d0002003043616e6e6f7443726561746500030030556e6b6e6f776e41737365740004001846726f7a656e0005002c556e737570706f727465640006004043616e6e6f74437265617465486f6c64000700344e6f74457870656e6461626c650008001c426c6f636b65640009000074083473705f61726974686d657469633c41726974686d657469634572726f7200010c24556e646572666c6f77000000204f766572666c6f77000100384469766973696f6e42795a65726f0002000078082873705f72756e74696d65485472616e73616374696f6e616c4572726f72000108304c696d6974526561636865640000001c4e6f4c61796572000100007c0c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144576656e740404540001186056616c69646174696f6e46756e6374696f6e53746f726564000004d05468652076616c69646174696f6e2066756e6374696f6e20686173206265656e207363686564756c656420746f206170706c792e6456616c69646174696f6e46756e6374696f6e4170706c69656404015472656c61795f636861696e5f626c6f636b5f6e756d10015452656c6179436861696e426c6f636b4e756d62657200010445015468652076616c69646174696f6e2066756e6374696f6e20776173206170706c696564206173206f662074686520636f6e7461696e65642072656c617920636861696e20626c6f636b206e756d6265722e6c56616c69646174696f6e46756e6374696f6e446973636172646564000204b05468652072656c61792d636861696e2061626f727465642074686520757067726164652070726f636573732e60446f776e776172644d657373616765735265636569766564040114636f756e7410010c7533320003040101536f6d6520646f776e77617264206d657373616765732068617665206265656e20726563656976656420616e642077696c6c2062652070726f6365737365642e64446f776e776172644d6573736167657350726f63657373656408012c7765696768745f75736564280118576569676874000120646d715f6865616434014472656c61795f636861696e3a3a48617368000404e0446f776e77617264206d6573736167657320776572652070726f636573736564207573696e672074686520676976656e207765696768742e445570776172644d65737361676553656e740401306d6573736167655f6861736880013c4f7074696f6e3c58636d486173683e000504b8416e20757077617264206d657373616765207761732073656e7420746f207468652072656c617920636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748004184f7074696f6e04045401040108104e6f6e6500000010536f6d650400040000010000840c3c70616c6c65745f62616c616e6365731870616c6c6574144576656e740804540004490001581c456e646f77656408011c6163636f756e74000130543a3a4163636f756e744964000130667265655f62616c616e6365180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f737408011c6163636f756e74000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650001083d01416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c78726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e736665720c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2842616c616e636553657408010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e636500030468412062616c616e6365207761732073657420627920726f6f742e20526573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e726573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000504e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656410011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500014864657374696e6174696f6e5f7374617475738801185374617475730006084d01536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742ed846696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e1c4465706f73697408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000704d8536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e20576974686472617708010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650008041d01536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650009040101536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292e184d696e74656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a049c536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e184275726e656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b049c536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e2453757370656e64656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000c041501536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e20526573746f72656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d04a4536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e20557067726164656404010c77686f000130543a3a4163636f756e744964000e0460416e206163636f756e74207761732075706772616465642e18497373756564040118616d6f756e74180128543a3a42616c616e6365000f042d01546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e2452657363696e646564040118616d6f756e74180128543a3a42616c616e63650010042501546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e184c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500110460536f6d652062616c616e636520776173206c6f636b65642e20556e6c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500120468536f6d652062616c616e63652077617320756e6c6f636b65642e1846726f7a656e08010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500130460536f6d652062616c616e6365207761732066726f7a656e2e1854686177656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500140460536f6d652062616c616e636520776173207468617765642e4c546f74616c49737375616e6365466f7263656408010c6f6c64180128543a3a42616c616e636500010c6e6577180128543a3a42616c616e6365001504ac5468652060546f74616c49737375616e6365602077617320666f72636566756c6c79206368616e6765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748814346672616d655f737570706f72741874726169747318746f6b656e73106d6973633442616c616e63655374617475730001081046726565000000205265736572766564000100008c0c6870616c6c65745f7472616e73616374696f6e5f7061796d656e741870616c6c6574144576656e74040454000104485472616e73616374696f6e466565506169640c010c77686f000130543a3a4163636f756e74496400012861637475616c5f66656518013042616c616e63654f663c543e00010c74697018013042616c616e63654f663c543e000008590141207472616e73616374696f6e20666565206061637475616c5f666565602c206f662077686963682060746970602077617320616464656420746f20746865206d696e696d756d20696e636c7573696f6e206665652c5c686173206265656e2070616964206279206077686f602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574900c2c70616c6c65745f7375646f1870616c6c6574144576656e7404045400011014537564696404012c7375646f5f726573756c749401384469737061746368526573756c7404b454686520726573756c74206f66207468652063616c6c206d61646520627920746865207375646f20757365722e00047041207375646f2063616c6c206a75737420746f6f6b20706c6163652e284b65794368616e67656408010c6f6c649c01504f7074696f6e3c543a3a4163636f756e7449643e04b4546865206f6c64207375646f206b657920286966206f6e65207761732070726576696f75736c7920736574292e010c6e6577000130543a3a4163636f756e7449640488546865206e6577207375646f206b657920286966206f6e652077617320736574292e010478546865207375646f206b657920686173206265656e20757064617465642e284b657952656d6f76656400020480546865206b657920776173207065726d616e656e746c792072656d6f7665642e285375646f4173446f6e6504012c7375646f5f726573756c749401384469737061746368526573756c7404b454686520726573756c74206f66207468652063616c6c206d61646520627920746865207375646f20757365722e0304c841205b7375646f5f61735d2850616c6c65743a3a7375646f5f6173292063616c6c206a75737420746f6f6b20706c6163652e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574940418526573756c740804540198044501680108084f6b040098000000000c45727204006800000100009800000400009c04184f7074696f6e04045401000108104e6f6e6500000010536f6d650400000000010000a00c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144576656e74040454000128404e6577496e76756c6e657261626c6573040134696e76756c6e657261626c6573a401445665633c543a3a4163636f756e7449643e0000046c4e657720496e76756c6e657261626c65732077657265207365742e44496e76756c6e657261626c6541646465640401286163636f756e745f6964000130543a3a4163636f756e7449640001047441206e657720496e76756c6e657261626c65207761732061646465642e4c496e76756c6e657261626c6552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e74496400020470416e20496e76756c6e657261626c65207761732072656d6f7665642e504e65774465736972656443616e64696461746573040148646573697265645f63616e6469646174657310010c753332000304a4546865206e756d626572206f6620646573697265642063616e6469646174657320776173207365742e404e657743616e646964616379426f6e6404012c626f6e645f616d6f756e7418013042616c616e63654f663c543e0004046c5468652063616e64696461637920626f6e6420776173207365742e3843616e64696461746541646465640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e0005045c41206e65772063616e646964617465206a6f696e65642e5043616e646964617465426f6e64557064617465640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e00060470426f6e64206f6620612063616e64696461746520757064617465642e4043616e64696461746552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e74496400070460412063616e646964617465207761732072656d6f7665642e4443616e6469646174655265706c616365640c010c6f6c64000130543a3a4163636f756e74496400010c6e6577000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000804f4416e206163636f756e7420776173207265706c6163656420696e207468652063616e646964617465206c69737420627920616e6f74686572206f6e652e68496e76616c6964496e76756c6e657261626c65536b69707065640401286163636f756e745f6964000130543a3a4163636f756e7449640009085501416e206163636f756e742077617320756e61626c6520746f20626520616464656420746f2074686520496e76756c6e657261626c65732062656361757365207468657920646964206e6f742068617665206b657973c8726567697374657265642e204f7468657220496e76756c6e657261626c6573206d61792068617665206265656e207365742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a40000020000a80c3870616c6c65745f73657373696f6e1870616c6c6574144576656e74000104284e657753657373696f6e04013473657373696f6e5f696e64657810013053657373696f6e496e64657800000839014e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f74207468659c626c6f636b206e756d626572206173207468652074797065206d6967687420737567676573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574ac0c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144576656e740404540001043c58636d704d65737361676553656e740401306d6573736167655f6861736804011c58636d48617368000004c0416e2048524d50206d657373616765207761732073656e7420746f2061207369626c696e672070617261636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b00c2870616c6c65745f78636d1870616c6c6574144576656e7404045400016024417474656d7074656404011c6f7574636f6d65b4015078636d3a3a6c61746573743a3a4f7574636f6d65000004a8457865637574696f6e206f6620616e2058434d206d6573736167652077617320617474656d707465642e1053656e741001186f726967696ebc01204c6f636174696f6e00012c64657374696e6174696f6ebc01204c6f636174696f6e00011c6d6573736167650501011c58636d3c28293e0001286d6573736167655f696404011c58636d486173680001045c412058434d206d657373616765207761732073656e742e48556e6578706563746564526573706f6e73650801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400020c5901517565727920726573706f6e736520726563656976656420776869636820646f6573206e6f74206d61746368206120726567697374657265642071756572792e2054686973206d61792062652062656361757365206155016d61746368696e6720717565727920776173206e6576657220726567697374657265642c206974206d617920626520626563617573652069742069732061206475706c696361746520726573706f6e73652c206f727062656361757365207468652071756572792074696d6564206f75742e34526573706f6e7365526561647908012071756572795f696430011c51756572794964000120726573706f6e736531010120526573706f6e73650003085d01517565727920726573706f6e736520686173206265656e20726563656976656420616e6420697320726561647920666f722074616b696e672077697468206074616b655f726573706f6e7365602e205468657265206973806e6f2072656769737465726564206e6f74696669636174696f6e2063616c6c2e204e6f7469666965640c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380004085901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e20686173a86265656e206469737061746368656420616e64206578656375746564207375636365737366756c6c792e404e6f746966794f76657277656967687414012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800013461637475616c5f77656967687428011857656967687400014c6d61785f62756467657465645f77656967687428011857656967687400050c4901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e5901636f756c64206e6f742062652064697370617463686564206265636175736520746865206469737061746368207765696768742069732067726561746572207468616e20746865206d6178696d756d20776569676874e46f726967696e616c6c7920627564676574656420627920746869732072756e74696d6520666f722074686520717565727920726573756c742e4c4e6f7469667944697370617463684572726f720c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380006085501517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e2054686572652077617320612067656e6572616c206572726f722077697468886469737061746368696e6720746865206e6f74696669636174696f6e2063616c6c2e484e6f746966794465636f64654661696c65640c012071756572795f696430011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800070c5101517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652064697370617463682077617320756e61626c6520746f20626559016465636f64656420696e746f2061206043616c6c603b2074686973206d696768742062652064756520746f2064697370617463682066756e6374696f6e20686176696e672061207369676e6174757265207768696368946973206e6f742060286f726967696e2c20517565727949642c20526573706f6e736529602e40496e76616c6964526573706f6e6465720c01186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400014465787065637465645f6c6f636174696f6e550101404f7074696f6e3c4c6f636174696f6e3e00080c5901457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206f726967696e206c6f636174696f6e206f662074686520726573706f6e736520646f657355016e6f74206d6174636820746861742065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5c496e76616c6964526573706f6e64657256657273696f6e0801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400091c5101457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206578706563746564206f726967696e206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e34526573706f6e736554616b656e04012071756572795f696430011c51756572794964000a04c8526563656976656420717565727920726573706f6e736520686173206265656e207265616420616e642072656d6f7665642e34417373657473547261707065640c011068617368340110483235360001186f726967696ebc01204c6f636174696f6e0001186173736574737501013c56657273696f6e6564417373657473000b04b8536f6d65206173736574732068617665206265656e20706c6163656420696e20616e20617373657420747261702e5456657273696f6e4368616e67654e6f74696669656410012c64657374696e6174696f6ebc01204c6f636174696f6e000118726573756c7410012858636d56657273696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d48617368000c0c2501416e2058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e206d65737361676520686173206265656e20617474656d7074656420746f2062652073656e742e00e054686520636f7374206f662073656e64696e672069742028626f726e652062792074686520636861696e2920697320696e636c756465642e5c537570706f7274656456657273696f6e4368616e6765640801206c6f636174696f6ebc01204c6f636174696f6e00011c76657273696f6e10012858636d56657273696f6e000d08390154686520737570706f727465642076657273696f6e206f662061206c6f636174696f6e20686173206265656e206368616e6765642e2054686973206d69676874206265207468726f75676820616ec06175746f6d61746963206e6f74696669636174696f6e206f722061206d616e75616c20696e74657276656e74696f6e2e504e6f7469667954617267657453656e644661696c0c01206c6f636174696f6ebc01204c6f636174696f6e00012071756572795f696430011c517565727949640001146572726f72b8012058636d4572726f72000e0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f727c73656e64696e6720746865206e6f74696669636174696f6e20746f2069742e644e6f746966795461726765744d6967726174696f6e4661696c0801206c6f636174696f6ed901014456657273696f6e65644c6f636174696f6e00012071756572795f696430011c51756572794964000f0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f72b46d6967726174696e6720746865206c6f636174696f6e20746f206f7572206e65772058434d20666f726d61742e54496e76616c69645175657269657256657273696f6e0801186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400101c5501457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652065787065637465642071756572696572206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e38496e76616c6964517565726965721001186f726967696ebc01204c6f636174696f6e00012071756572795f696430011c5175657279496400014065787065637465645f71756572696572bc01204c6f636174696f6e0001506d617962655f61637475616c5f71756572696572550101404f7074696f6e3c4c6f636174696f6e3e00110c5d01457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652071756572696572206c6f636174696f6e206f662074686520726573706f6e736520646f657351016e6f74206d61746368207468652065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5056657273696f6e4e6f74696679537461727465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d486173680012085901412072656d6f746520686173207265717565737465642058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e2066726f6d20757320616e64207765206861766520686f6e6f7265642069742e1d01412076657273696f6e20696e666f726d6174696f6e206d6573736167652069732073656e7420746f207468656d20616e642069747320636f737420697320696e636c756465642e5856657273696f6e4e6f746966795265717565737465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d486173680013043d015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073656e642075732058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e732e6056657273696f6e4e6f74696679556e7265717565737465640c012c64657374696e6174696f6ebc01204c6f636174696f6e000110636f7374110101184173736574730001286d6573736167655f696404011c58636d4861736800140825015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073746f70732073656e64696e672075732058434d2076657273696f6e206368616e6765386e6f74696669636174696f6e732e204665657350616964080118706179696e67bc01204c6f636174696f6e0001106665657311010118417373657473001504310146656573207765726520706169642066726f6d2061206c6f636174696f6e20666f7220616e206f7065726174696f6e20286f6674656e20666f72207573696e67206053656e6458636d60292e34417373657473436c61696d65640c011068617368340110483235360001186f726967696ebc01204c6f636174696f6e0001186173736574737501013c56657273696f6e6564417373657473001604c0536f6d65206173736574732068617665206265656e20636c61696d65642066726f6d20616e20617373657420747261706056657273696f6e4d6967726174696f6e46696e697368656404011c76657273696f6e10012858636d56657273696f6e00170484412058434d2076657273696f6e206d6967726174696f6e2066696e69736865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b4102c73746167696e675f78636d087634187472616974731c4f7574636f6d6500010c20436f6d706c6574650401107573656428011857656967687400000028496e636f6d706c657465080110757365642801185765696768740001146572726f72b801144572726f72000100144572726f720401146572726f72b801144572726f7200020000b8100c78636d08763318747261697473144572726f720001a0204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300304c6f636174696f6e46756c6c000400544c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e736976650014001054726170040030010c753634001500404578706563746174696f6e46616c73650016003850616c6c65744e6f74466f756e64001700304e616d654d69736d617463680018004c56657273696f6e496e636f6d70617469626c6500190050486f6c64696e67576f756c644f766572666c6f77001a002c4578706f72744572726f72001b00385265616e63686f724661696c6564001c00184e6f4465616c001d0028466565734e6f744d6574001e00244c6f636b4572726f72001f00304e6f5065726d697373696f6e00200028556e616e63686f726564002100384e6f744465706f73697461626c650022004c556e68616e646c656458636d56657273696f6e002300485765696768744c696d69745265616368656404002801185765696768740024001c426172726965720025004c5765696768744e6f74436f6d70757461626c650026004445786365656473537461636b4c696d697400270000bc102c73746167696e675f78636d087634206c6f636174696f6e204c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72c001244a756e6374696f6e730000c0102c73746167696e675f78636d087634246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400c401484172633c5b4a756e6374696f6e3b20315d3e0001000858320400e801484172633c5b4a756e6374696f6e3b20325d3e0002000858330400ec01484172633c5b4a756e6374696f6e3b20335d3e0003000858340400f001484172633c5b4a756e6374696f6e3b20345d3e0004000858350400f401484172633c5b4a756e6374696f6e3b20355d3e0005000858360400f801484172633c5b4a756e6374696f6e3b20365d3e0006000858370400fc01484172633c5b4a756e6374696f6e3b20375d3e0007000858380400010101484172633c5b4a756e6374696f6e3b20385d3e00080000c400000301000000c800c8102c73746167696e675f78636d087634206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726bd001444f7074696f6e3c4e6574776f726b49643e00010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964e00118426f6479496400011070617274e40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400d401244e6574776f726b496400090000cc0000061000d004184f7074696f6e04045401d40108104e6f6e6500000010536f6d650400d40000010000d4102c73746167696e675f78636d087634206a756e6374696f6e244e6574776f726b496400012c24427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d62657230010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f69642c010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090040506f6c6b61646f7442756c6c6574696e000a0000d8000003140000000800dc0000061800e0100c78636d087633206a756e6374696f6e18426f6479496400012810556e69740000001c4d6f6e696b6572040048011c5b75383b20345d00010014496e6465780400cc010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000e4100c78636d087633206a756e6374696f6e20426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74cc010c753332000100204672616374696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c75333200040000e800000302000000c800ec00000303000000c800f000000304000000c800f400000305000000c800f800000306000000c800fc00000307000000c800010100000308000000c80005010c2c73746167696e675f78636d0876340c58636d041043616c6c00000400090101585665633c496e737472756374696f6e3c43616c6c3e3e000009010000020d01000d010c2c73746167696e675f78636d0876342c496e737472756374696f6e041043616c6c0001c03457697468647261774173736574040011010118417373657473000000545265736572766541737365744465706f7369746564040011010118417373657473000100585265636569766554656c65706f727465644173736574040011010118417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e736531010120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572550101404f7074696f6e3c4c6f636174696f6e3e000300345472616e7366657241737365740801186173736574731101011841737365747300012c62656e6566696369617279bc01204c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574731101011841737365747300011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6c5d01014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c00140496e746572696f724c6f636174696f6e000b002c5265706f72744572726f720400610101445175657279526573706f6e7365496e666f000c00304465706f73697441737365740801186173736574736501012c417373657446696c74657200012c62656e6566696369617279bc01204c6f636174696f6e000d004c4465706f7369745265736572766541737365740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000e003445786368616e676541737365740c0110676976656501012c417373657446696c74657200011077616e741101011841737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c01186173736574736501012c417373657446696c74657200011c72657365727665bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0001186173736574736501012c417373657446696c74657200120030427579457865637574696f6e080110666565731901011441737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204000501012458636d3c43616c6c3e0015002c536574417070656e64697804000501012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473110101184173736574730001187469636b6574bc01204c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e4173736574040011010118417373657473001c002c4578706563744173736574040011010118417373657473001d00304578706563744f726967696e0400550101404f7074696f6e3c4c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400610101445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c801204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bd401244e6574776f726b496400012c64657374696e6174696f6ec00140496e746572696f724c6f636174696f6e00010c78636d0501011c58636d3c28293e002600244c6f636b41737365740801146173736574190101144173736574000120756e6c6f636b6572bc01204c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574190101144173736574000118746172676574bc01204c6f636174696f6e002800384e6f7465556e6c6f636b61626c6508011461737365741901011441737365740001146f776e6572bc01204c6f636174696f6e0029003452657175657374556e6c6f636b08011461737365741901011441737365740001186c6f636b6572bc01204c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bc01204c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696e550101404f7074696f6e3c4c6f636174696f6e3e002f00001101102c73746167696e675f78636d0876341461737365741841737365747300000400150101285665633c41737365743e000015010000021901001901102c73746167696e675f78636d087634146173736574144173736574000008010869641d01011c4173736574496400010c66756e2101012c46756e676962696c69747900001d01102c73746167696e675f78636d0876341461737365741c4173736574496400000400bc01204c6f636174696f6e00002101102c73746167696e675f78636d0876341461737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400250101344173736574496e7374616e6365000100002501102c73746167696e675f78636d087634146173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d0005000029010000030800000008002d0100000310000000080031010c2c73746167696e675f78636d08763420526573706f6e7365000118104e756c6c000000184173736574730400110101184173736574730001003c457865637574696f6e526573756c740400350101504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f04003d010198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c7404004d0101384d617962654572726f72436f646500050000350104184f7074696f6e0404540139010108104e6f6e6500000010536f6d6504003901000001000039010000040810b8003d010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014101045300000400490101185665633c543e000041010c2c73746167696e675f78636d0876342850616c6c6574496e666f0000180114696e646578cc010c7533320001106e616d6545010180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d6545010180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72cc010c7533320001146d696e6f72cc010c7533320001147061746368cc010c753332000045010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e000049010000024101004d010c0c78636d087633384d617962654572726f72436f646500010c1c53756363657373000000144572726f7204005101018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e000100385472756e63617465644572726f7204005101018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e0002000051010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000550104184f7074696f6e04045401bc0108104e6f6e6500000010536f6d650400bc000001000059010c0c78636d087633284f726967696e4b696e64000110184e617469766500000040536f7665726569676e4163636f756e74000100245375706572757365720002000c58636d000300005d010c0c78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656438011c5665633c75383e000061010c2c73746167696e675f78636d087634445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ebc01204c6f636174696f6e00012071756572795f69642c011c517565727949640001286d61785f77656967687428011857656967687400006501102c73746167696e675f78636d0876341461737365742c417373657446696c74657200010820446566696e6974650400110101184173736574730000001057696c6404006901012457696c644173736574000100006901102c73746167696e675f78636d0876341461737365742457696c6441737365740001100c416c6c00000014416c6c4f6608010869641d01011c4173736574496400010c66756e6d01013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400cc010c75333200020030416c6c4f66436f756e7465640c010869641d01011c4173736574496400010c66756e6d01013c57696c6446756e676962696c697479000114636f756e74cc010c753332000300006d01102c73746167696e675f78636d0876341461737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c650001000071010c0c78636d0876332c5765696768744c696d697400010824556e6c696d697465640000001c4c696d697465640400280118576569676874000100007501080c78636d3c56657273696f6e656441737365747300010c08563204007901013c76323a3a4d756c74694173736574730001000856330400ad01013c76333a3a4d756c746941737365747300030008563404001101012876343a3a417373657473000400007901100c78636d087632286d756c746961737365742c4d756c7469417373657473000004007d01013c5665633c4d756c746941737365743e00007d010000028101008101100c78636d087632286d756c74696173736574284d756c74694173736574000008010869648501011c4173736574496400010c66756ea501012c46756e676962696c69747900008501100c78636d087632286d756c746961737365741c4173736574496400010820436f6e63726574650400890101344d756c74694c6f636174696f6e000000204162737472616374040038011c5665633c75383e000100008901100c78636d087632346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f728d0101244a756e6374696f6e7300008d01100c78636d087632346d756c74696c6f636174696f6e244a756e6374696f6e7300012410486572650000000858310400910101204a756e6374696f6e0001000858320800910101204a756e6374696f6e0000910101204a756e6374696f6e0002000858330c00910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0003000858341000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0004000858351400910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0005000858361800910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0006000858371c00910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0007000858382000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e0000910101204a756e6374696f6e000800009101100c78636d087632206a756e6374696f6e204a756e6374696f6e0001242450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726b950101244e6574776f726b496400010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726b950101244e6574776f726b4964000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726b950101244e6574776f726b496400010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e000600244f6e6c794368696c6400070024506c7572616c69747908010869649d010118426f6479496400011070617274a1010120426f6479506172740008000095010c0c78636d087632244e6574776f726b49640001100c416e79000000144e616d65640400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010020506f6c6b61646f74000200184b7573616d610003000099010c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401080453000004003801185665633c543e00009d010c0c78636d08763218426f6479496400012810556e6974000000144e616d65640400990101805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010014496e6465780400cc010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000a1010c0c78636d08763220426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74cc010c753332000100204672616374696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6dcc010c75333200011464656e6f6dcc010c75333200040000a501100c78636d087632286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400a90101344173736574496e7374616e636500010000a901100c78636d087632286d756c74696173736574344173736574496e7374616e636500011c24556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050010426c6f62040038011c5665633c75383e00060000ad01100c78636d087633286d756c746961737365742c4d756c746941737365747300000400b101013c5665633c4d756c746941737365743e0000b101000002b50100b501100c78636d087633286d756c74696173736574284d756c7469417373657400000801086964b901011c4173736574496400010c66756ed101012c46756e676962696c6974790000b901100c78636d087633286d756c746961737365741c4173736574496400010820436f6e63726574650400bd0101344d756c74694c6f636174696f6e00000020416273747261637404000401205b75383b2033325d00010000bd01102c73746167696e675f78636d087633346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72c10101244a756e6374696f6e730000c101100c78636d087633246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400c50101204a756e6374696f6e0001000858320800c50101204a756e6374696f6e0000c50101204a756e6374696f6e0002000858330c00c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0003000858341000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0004000858351400c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0005000858361800c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0006000858371c00c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0007000858382000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e0000c50101204a756e6374696f6e00080000c501100c78636d087633206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400cc010c7533320000002c4163636f756e744964333208011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e000114696e6465782c010c753634000200304163636f756e744b6579323008011c6e6574776f726bc90101444f7074696f6e3c4e6574776f726b49643e00010c6b6579d801205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400dc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964e00118426f6479496400011070617274e40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400cd0101244e6574776f726b496400090000c90104184f7074696f6e04045401cd010108104e6f6e6500000010536f6d650400cd010000010000cd01100c78636d087633206a756e6374696f6e244e6574776f726b496400012c24427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d62657230010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f69642c010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090040506f6c6b61646f7442756c6c6574696e000a0000d101100c78636d087633286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400dc0110753132380000002c4e6f6e46756e6769626c650400d50101344173736574496e7374616e636500010000d501100c78636d087633286d756c74696173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400dc01107531323800010018417272617934040048011c5b75383b20345d0002001841727261793804002901011c5b75383b20385d0003001c4172726179313604002d0101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050000d901080c78636d4456657273696f6e65644c6f636174696f6e00010c08563204008901014476323a3a4d756c74694c6f636174696f6e0001000856330400bd01014476333a3a4d756c74694c6f636174696f6e0003000856340400bc013076343a3a4c6f636174696f6e00040000dd010c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144576656e7404045400010c34496e76616c6964466f726d617404000401205b75383b2033325d00000880446f776e77617264206d65737361676520697320696e76616c69642058434d2e205c5b206964205c5d48556e737570706f7274656456657273696f6e04000401205b75383b2033325d000108bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e205c5b206964205c5d404578656375746564446f776e7761726408000401205b75383b2033325d0000b4011c4f7574636f6d65000208c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e445c5b2069642c206f7574636f6d65205c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e1010c5070616c6c65745f6d6573736167655f71756575651870616c6c6574144576656e740404540001104050726f63657373696e674661696c65640c010869643401104832353604945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e01146572726f72ed01014c50726f636573734d6573736167654572726f721060546865206572726f722074686174206f636375727265642e00490154686973206572726f7220697320707265747479206f70617175652e204d6f72652066696e652d677261696e6564206572726f7273206e65656420746f20626520656d6974746564206173206576656e74736862792074686520604d65737361676550726f636573736f72602e000455014d657373616765206469736361726465642064756520746f20616e206572726f7220696e2074686520604d65737361676550726f636573736f72602028757375616c6c79206120666f726d6174206572726f72292e2450726f63657373656410010869643401104832353604945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e012c7765696768745f7573656428011857656967687404c0486f77206d7563682077656967687420776173207573656420746f2070726f6365737320746865206d6573736167652e011c73756363657373200110626f6f6c18885768657468657220746865206d657373616765207761732070726f6365737365642e0049014e6f74652074686174207468697320646f6573206e6f74206d65616e20746861742074686520756e6465726c79696e6720604d65737361676550726f636573736f72602077617320696e7465726e616c6c7935017375636365737366756c2e204974202a736f6c656c792a206d65616e73207468617420746865204d512070616c6c65742077696c6c2074726561742074686973206173206120737563636573734d01636f6e646974696f6e20616e64206469736361726420746865206d6573736167652e20416e7920696e7465726e616c206572726f72206e6565647320746f20626520656d6974746564206173206576656e74736862792074686520604d65737361676550726f636573736f72602e0104544d6573736167652069732070726f6365737365642e484f766572776569676874456e71756575656410010869640401205b75383b2033325d04945468652060626c616b65325f323536602068617368206f6620746865206d6573736167652e01186f726967696ee50101484d6573736167654f726967696e4f663c543e0464546865207175657565206f6620746865206d6573736167652e0128706167655f696e64657810012450616765496e64657804605468652070616765206f6620746865206d6573736167652e01346d6573736167655f696e64657810011c543a3a53697a6504a454686520696e646578206f6620746865206d6573736167652077697468696e2074686520706167652e02048c4d65737361676520706c6163656420696e206f7665727765696768742071756575652e28506167655265617065640801186f726967696ee50101484d6573736167654f726967696e4f663c543e0458546865207175657565206f662074686520706167652e0114696e64657810012450616765496e646578045854686520696e646578206f662074686520706167652e03045454686973207061676520776173207265617065642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e501085c63756d756c75735f7072696d6974697665735f636f7265584167677265676174654d6573736167654f726967696e00010c104865726500000018506172656e740001001c5369626c696e670400e901011850617261496400020000e9010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665730849640000040010010c7533320000ed0110346672616d655f737570706f727418747261697473206d657373616765734c50726f636573734d6573736167654572726f7200011824426164466f726d61740000001c436f72727570740001002c556e737570706f72746564000200284f7665727765696768740400280118576569676874000300145969656c6400040044537461636b4c696d69745265616368656400050000f1010c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c6574144576656e740404540001245c4d7370526571756573745369676e55705375636365737310010c77686f000130543a3a4163636f756e7449640001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e000008b1014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f7669646572206861732072657175657374656420746f207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574e50174686174204d53502773206163636f756e742069642c20697473206d756c74696164647265737365732c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20616e64206974732076616c75652070726f706f736974696f6e2e404d73705369676e55705375636365737314010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e000108b5014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f76696465722068617320636f6e6669726d656420697473207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574dd0174686174204d53502773206163636f756e742069642c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20697473206d756c7469616464726573732c20616e64206974732076616c75652070726f706f736974696f6e2e5c427370526571756573745369676e5570537563636573730c010c77686f000130543a3a4163636f756e7449640001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e000208b9014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f7669646572206861732072657175657374656420746f207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574890174686174204253502773206163636f756e742069642c20697473206d756c74696164647265737365732c20616e642074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652e404273705369676e55705375636365737310010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000120636170616369747930014853746f7261676544617461556e69743c543e000308bd014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f76696465722068617320636f6e6669726d656420697473207369676e207570207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574810174686174204253502773206163636f756e742069642c2074686520746f74616c20646174612069742063616e2073746f7265206163636f7264696e6720746f20697473207374616b652c20616e6420697473206d756c7469616464726573732e545369676e55705265717565737443616e63656c656404010c77686f000130543a3a4163636f756e7449640004087d014576656e7420656d6974746564207768656e2061207369676e207570207265717565737420686173206265656e2063616e63656c6564207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f7574d4746865206163636f756e74206964206f6620746865207573657220746861742063616e63656c65642074686520726571756573742e444d73705369676e4f66665375636365737308010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e00050889014576656e7420656d6974746564207768656e2061204d61696e2053746f726167652050726f766964657220686173207369676e6564206f6666207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75745874686174204d53502773206163636f756e742069642e444273705369676e4f66665375636365737308010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e00060891014576656e7420656d6974746564207768656e2061204261636b75702053746f726167652050726f766964657220686173207369676e6564206f6666207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75745874686174204253502773206163636f756e742069642e3c43617061636974794368616e67656414010c77686f000130543a3a4163636f756e74496400012c70726f76696465725f69640902015053746f7261676550726f766964657249643c543e0001306f6c645f636170616369747930014853746f7261676544617461556e69743c543e0001306e65775f636170616369747930014853746f7261676544617461556e69743c543e0001786e6578745f626c6f636b5f7768656e5f6368616e67655f616c6c6f776564100144426c6f636b4e756d626572466f723c543e00070865014576656e7420656d6974746564207768656e206120535020686173206368616e67656420697473206361706163697479207375636365737366756c6c792e2050726f766964657320696e666f726d6174696f6e2061626f75744901746861742053502773206163636f756e742069642c20697473206f6c6420746f74616c2064617461207468617420636f756c642073746f72652c20616e6420746865206e657720746f74616c20646174612e1c536c617368656408012c70726f76696465725f69643401244861736849643c543e000138616d6f756e745f736c617368656418013042616c616e63654f663c543e000804a84576656e7420656d6974746564207768656e20616e20535020686173206265656e20736c61736865642e04b4546865206576656e747320746861742063616e20626520656d697474656420627920746869732070616c6c6574f5010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401f901045300000400fd0101185665633c543e0000f9010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000fd01000002f9010001020c6070616c6c65745f73746f726167655f70726f7669646572731474797065734056616c756550726f706f736974696f6e04045400000c01286964656e74696669657234013856616c756550726f7049643c543e000128646174615f6c696d697430014853746f7261676544617461556e69743c543e00012470726f746f636f6c73050201a4426f756e6465645665633c50726f746f636f6c733c543e2c204d617850726f746f636f6c733c543e3e000005020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401f901045300000400fd0101185665633c543e000009020c6070616c6c65745f73746f726167655f70726f7669646572731474797065734453746f7261676550726f76696465724964040454000108544261636b757053746f7261676550726f766964657204003401684261636b757053746f7261676550726f766964657249643c543e0000004c4d61696e53746f7261676550726f766964657204003401604d61696e53746f7261676550726f766964657249643c543e000100000d020c4870616c6c65745f66696c655f73797374656d1870616c6c6574144576656e7404045400015c244e65774275636b657418010c77686f000130543a3a4163636f756e7449640001186d73705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001106e616d65f90101404275636b65744e616d65466f723c543e000134636f6c6c656374696f6e5f6964110201684f7074696f6e3c436f6c6c656374696f6e4964466f723c543e3e00011c70726976617465200110626f6f6c000004b04e6f74696669657320746861742061206e6577206275636b657420686173206265656e20637265617465642e4c4d6f76654275636b65745265717565737465640c010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e0001286e65775f6d73705f696434014050726f76696465724964466f723c543e000104cc4e6f74696669657320746861742061206275636b6574206973206265696e67206d6f76656420746f2061206e6577204d53502e504275636b6574507269766163795570646174656410010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e000134636f6c6c656374696f6e5f6964110201684f7074696f6e3c436f6c6c656374696f6e4964466f723c543e3e00011c70726976617465200110626f6f6c000204c84e6f74696669657320746861742061206275636b65742773207072697661637920686173206265656e20757064617465642e6c4e6577436f6c6c656374696f6e416e644173736f63696174696f6e0c010c77686f000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e000134636f6c6c656374696f6e5f6964100148436f6c6c656374696f6e4964466f723c543e00030435014e6f74696669657320746861742061206e657720636f6c6c656374696f6e20686173206265656e206372656174656420616e64206173736f63696174656420776974682061206275636b65742e444e657753746f72616765526571756573741c010c77686f000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e000120706565725f696473f5010128506565724964733c543e000404e44e6f74696669657320746861742061206e65772066696c6520686173206265656e2072657175657374656420746f2062652073746f7265642e744d7370526573706f6e646564546f53746f72616765526571756573747304011c726573756c7473190201884d7370526573706f6e6453746f726167655265717565737473526573756c743c543e000504e04e6f74696669657320746861742061204d53502068617320726573706f6e64656420746f2073746f7261676520726571756573742873292e504163636570746564427370566f6c756e746565721c01186273705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e0001386d756c7469616464726573736573f50101444d756c74694164647265737365733c543e0001146f776e6572000130543a3a4163636f756e74496400011073697a6530013853746f72616765446174613c543e000604f04e6f746966696573207468617420612042535020686173206265656e20616363657074656420746f2073746f7265206120676976656e2066696c652e4c427370436f6e6669726d656453746f72696e6710010c77686f000130543a3a4163636f756e7449640001186273705f696434014050726f76696465724964466f723c543e00012466696c655f6b657973290201f0426f756e6465645665633c4d65726b6c65486173683c543e2c20543a3a4d61784261746368436f6e6669726d53746f7261676552657175657374733e0001206e65775f726f6f743401344d65726b6c65486173683c543e000704c04e6f746966696573207468617420612042535020636f6e6669726d65642073746f72696e6720612066696c652873292e5c53746f726167655265717565737446756c66696c6c656404012066696c655f6b65793401344d65726b6c65486173683c543e00080409014e6f746966696573207468617420612073746f72616765207265717565737420666f7220612066696c65206b657920686173206265656e2066756c66696c6c65642e5453746f72616765526571756573744578706972656404012066696c655f6b65793401344d65726b6c65486173683c543e000904b44e6f746966696573207468652065787069726174696f6e206f6620612073746f7261676520726571756573742e5453746f72616765526571756573745265766f6b656404012066696c655f6b65793401344d65726b6c65486173683c543e000a0439014e6f746966696573207468617420612073746f72616765207265717565737420686173206265656e207265766f6b65642062792074686520757365722077686f20696e697469617465642069742e64427370526571756573746564546f53746f7053746f72696e671001186273705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e000b0401014e6f746966696573207468617420612042535020686173206f70656e65642061207265717565737420746f2073746f702073746f72696e6720612066696c652e60427370436f6e6669726d53746f7070656453746f72696e670c01186273705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001206e65775f726f6f743401344d65726b6c65486173683c543e000c04bc4e6f7469666965732074686174206120425350206861732073746f707065642073746f72696e6720612066696c652e985072696f726974794368616c6c656e6765466f7246696c6544656c6574696f6e517565756564080118697373756572590201644569746865724163636f756e7449644f724d737049643c543e00012066696c655f6b65793401344d65726b6c65486173683c543e000d0451014e6f746966696573207468617420612066696c65206b657920686173206265656e2071756575656420666f722061207072696f72697479206368616c6c656e676520666f722066696c652064656c6574696f6e2e68537053746f7053746f72696e67496e736f6c76656e745573657214011473705f696434014050726f76696465724964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001206e65775f726f6f743401344d65726b6c65486173683c543e000e0455014e6f74696669657320746861742061205350206861732073746f707065642073746f72696e6720612066696c65206265636175736520697473206f776e657220686173206265636f6d6520696e736f6c76656e742e784661696c6564546f51756575655072696f726974794368616c6c656e676508011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e000f0445014e6f74696669657320746861742061207072696f72697479206368616c6c656e6765206661696c656420746f2062652071756575656420666f722070656e64696e672066696c652064656c6574696f6e2e4c46696c6544656c6574696f6e5265717565737414011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e00014870726f6f665f6f665f696e636c7573696f6e200110626f6f6c001004944e6f746966696573207468617420612066696c652077696c6c2062652064656c657465642eac50726f6f665375626d6974746564466f7250656e64696e6746696c6544656c6574696f6e526571756573741401186d73705f696434014050726f76696465724964466f723c543e00011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e00014870726f6f665f6f665f696e636c7573696f6e200110626f6f6c00110435014e6f746966696573207468617420612070726f6f6620686173206265656e207375626d697474656420666f7220612070656e64696e672066696c652064656c6574696f6e20726571756573742e704273704368616c6c656e67654379636c65496e697469616c6973656408010c77686f000130543a3a4163636f756e7449640001186273705f696434014050726f76696465724964466f723c543e00120845014e6f74696669657320746861742061204253502773206368616c6c656e6765206379636c6520686173206265656e20696e697469616c697365642c20616464696e67207468652066697273742066696c65ac6b657928732920746f20746865204253502773204d65726b6c6520506174726963696120466f726573742e604d6f76654275636b657452657175657374457870697265640801186d73705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e001304c04e6f74696669657320746861742061206d6f7665206275636b657420726571756573742068617320657870697265642e484d6f76654275636b657441636365707465640801246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e001404cc4e6f74696669657320746861742061206275636b657420686173206265656e206d6f76656420746f2061206e6577204d53502e484d6f76654275636b657452656a65637465640801246275636b65745f69643401384275636b65744964466f723c543e0001186d73705f696434014050726f76696465724964466f723c543e00150405014e6f74696669657320746861742061206275636b6574206d6f7665207265717565737420686173206265656e2072656a656374656420627920746865204d53502e844461746153657276657252656769737465726564466f724d6f76654275636b65740801186273705f696434014050726f76696465724964466f723c543e0001246275636b65745f69643401384275636b65744964466f723c543e00160429014e6f7469666965732074686174206120646174612073657276657220686173206265656e207265676973746572656420666f722061206d6f7665206275636b657420726571756573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574110204184f7074696f6e04045401100108104e6f6e6500000010536f6d65040010000001000015020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e000019020c4870616c6c65745f66696c655f73797374656d1474797065737c4d7370526573706f6e6453746f726167655265717565737473526573756c7404045400000801186d73705f696434014050726f76696465724964466f723c543e000124726573706f6e7365731d02010d01426f756e6465645665633c4261746368526573706f6e7365733c543e2c204d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e00001d020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012102045300000400550201185665633c543e000021020c4870616c6c65745f66696c655f73797374656d147479706573384261746368526573706f6e73657304045400010c2041636365707465640400250201884d73704163636570746564426174636853746f7261676552657175657374733c543e0000002052656a65637465640400310201884d737052656a6563746564426174636853746f7261676552657175657374733c543e000100184661696c65640400450201804d73704661696c6564426174636853746f7261676552657175657374733c543e0002000025020c4870616c6c65745f66696c655f73797374656d1474797065737c4d73704163636570746564426174636853746f726167655265717565737473040454000010012466696c655f6b657973290201fc426f756e6465645665633c4d65726b6c65486173683c543e2c204d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001246275636b65745f69643401384275636b65744964466f723c543e00013c6e65775f6275636b65745f726f6f743401344d65726b6c65486173683c543e0001146f776e6572000130543a3a4163636f756e744964000029020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e00002d02000002340031020c4870616c6c65745f66696c655f73797374656d1474797065737c4d737052656a6563746564426174636853746f72616765526571756573747304045400000c012466696c655f6b6579733502018101426f756e6465645665633c284d65726b6c65486173683c543e2c2052656a656374656453746f7261676552657175657374526561736f6e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e2c3e0001246275636b65745f69643401384275636b65744964466f723c543e0001146f776e6572000130543a3a4163636f756e744964000035020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454013902045300000400410201185665633c543e0000390200000408343d02003d020c4870616c6c65745f66696c655f73797374656d1474797065737052656a656374656453746f7261676552657175657374526561736f6e00010c58526561636865644d6178696d756d4361706163697479000000505265636569766564496e76616c696450726f6f6600010034496e7465726e616c4572726f7200020000410200000239020045020c4870616c6c65745f66696c655f73797374656d147479706573744d73704661696c6564426174636853746f72616765526571756573747304045400000c012466696c655f6b6579734902014101426f756e6465645665633c284d65726b6c65486173683c543e2c2044697370617463684572726f72292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001246275636b65745f69643401384275636b65744964466f723c543e0001146f776e6572000130543a3a4163636f756e744964000049020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014d02045300000400510201185665633c543e00004d020000040834680051020000024d0200550200000221020059020c4870616c6c65745f66696c655f73797374656d147479706573584569746865724163636f756e7449644f724d73704964040454000108244163636f756e7449640400000130543a3a4163636f756e744964000000144d73704964040034014050726f76696465724964466f723c543e000100005d020c5070616c6c65745f70726f6f66735f6465616c65721870616c6c6574144576656e74040454000124304e65774368616c6c656e676508010c77686f00013c4163636f756e744964466f723c543e0001386b65795f6368616c6c656e6765643401244b6579466f723c543e0000048441206d616e75616c206368616c6c656e676520776173207375626d69747465642e3450726f6f66416363657074656408012070726f766964657234014050726f76696465724964466f723c543e00011470726f6f666102012050726f6f663c543e00010454412070726f6f66207761732061636365707465642e404e65774368616c6c656e6765536565640801446368616c6c656e6765735f7469636b6572100144426c6f636b4e756d626572466f723c543e0001107365656434015852616e646f6d6e6573734f7574707574466f723c543e0002048c41206e6577206368616c6c656e67652073656564207761732067656e6572617465642e584e6577436865636b706f696e744368616c6c656e67650801446368616c6c656e6765735f7469636b6572100144426c6f636b4e756d626572466f723c543e0001286368616c6c656e6765738902015d01426f756e6465645665633c284b6579466f723c543e2c204f7074696f6e3c5472696552656d6f76654d75746174696f6e3e292c0a4d6178437573746f6d4368616c6c656e676573506572426c6f636b466f723c543e2c3e000304a441206e657720636865636b706f696e74206368616c6c656e6765207761732067656e6572617465642e44536c61736861626c6550726f766964657208012070726f766964657234014050726f76696465724964466f723c543e00015c6e6578745f6368616c6c656e67655f646561646c696e65100144426c6f636b4e756d626572466f723c543e0004045901412070726f766964657220776173206d61726b656420617320736c61736861626c6520616e64207468656972206368616c6c656e676520646561646c696e652077617320666f72636566756c6c79207075736865642e704e6f5265636f72644f664c6173745375626d697474656450726f6f6604012070726f766964657234014050726f76696465724964466f723c543e000504f84e6f207265636f7264206f6620746865206c617374207469636b207468652050726f7669646572207375626d697474656420612070726f6f6620666f722e704e65774368616c6c656e67654379636c65496e697469616c6973656410013063757272656e745f7469636b100144426c6f636b4e756d626572466f723c543e00015c6e6578745f6368616c6c656e67655f646561646c696e65100144426c6f636b4e756d626572466f723c543e00012070726f766964657234014050726f76696465724964466f723c543e0001586d617962655f70726f76696465725f6163636f756e749c01504f7074696f6e3c543a3a4163636f756e7449643e000604b4412070726f76696465722773206368616c6c656e6765206379636c652077617320696e697469616c697365642e404d75746174696f6e734170706c6965640c012070726f766964657234014050726f76696465724964466f723c543e0001246d75746174696f6e739d0201905665633c284b6579466f723c543e2c205472696552656d6f76654d75746174696f6e293e0001206e65775f726f6f743401244b6579466f723c543e000704c84120736574206f66206d75746174696f6e7320686173206265656e206170706c69656420746f2074686520466f726573742e4c4368616c6c656e6765735469636b6572536574040118706175736564200110626f6f6c000804d4546865205b604368616c6c656e6765735469636b6572605d20686173206265656e20706175736564206f7220756e7061757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657461020c5070616c6c65745f70726f6f66735f6465616c65721474797065731450726f6f660404540000080130666f726573745f70726f6f6665020164466f72657374566572696669657250726f6f66466f723c543e0001286b65795f70726f6f66736d02018042547265654d61703c4b6579466f723c543e2c204b657950726f6f663c543e3e000065020c1c73705f747269653473746f726167655f70726f6f6630436f6d7061637450726f6f660000040134656e636f6465645f6e6f646573690201305665633c5665633c75383e3e0000690200000238006d02042042547265654d617008044b01340456017102000400810200000071020c5070616c6c65745f70726f6f66735f6465616c6572147479706573204b657950726f6f66040454000008011470726f6f66750201584b6579566572696669657250726f6f66466f723c543e00013c6368616c6c656e67655f636f756e7410010c753332000075020c547368705f66696c655f6b65795f76657269666965721474797065733046696c654b657950726f6f66000008013466696c655f6d65746164617461790201d846696c654d657461646174613c485f4c454e4754482c204348554e4b5f53495a452c2053495a455f544f5f4348414c4c454e4745533e00011470726f6f6665020130436f6d7061637450726f6f660000790208447368705f66696c655f6d657461646174613046696c654d6574616461746100001401146f776e657238011c5665633c75383e0001246275636b65745f696438011c5665633c75383e0001206c6f636174696f6e38011c5665633c75383e00012466696c655f73697a652c010c75363400012c66696e6765727072696e747d02015446696e6765727072696e743c485f4c454e4754483e00007d0208447368705f66696c655f6d657461646174612c46696e6765727072696e7400000400040138486173683c485f4c454e4754483e000081020000028502008502000004083471020089020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454018d02045300000400990201185665633c543e00008d020000040834910200910204184f7074696f6e0404540195020108104e6f6e6500000010536f6d65040095020000010000950208287368705f747261697473485472696552656d6f76654d75746174696f6e0000000099020000028d02009d02000002a10200a1020000040834950200a5020c4470616c6c65745f72616e646f6d6e6573731870616c6c6574144576656e74040454000104844e65774f6e6545706f636841676f52616e646f6d6e657373417661696c61626c650c013c72616e646f6d6e6573735f7365656434011c543a3a4861736800012866726f6d5f65706f636830010c75363400014476616c69645f756e74696c5f626c6f636b100144426c6f636b4e756d626572466f723c543e00000419014576656e7420656d6974746564207768656e2061206e65772072616e646f6d207365656420697320617661696c61626c652066726f6d207468652072656c617920636861696e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a9020c5870616c6c65745f7061796d656e745f73747265616d731870616c6c6574144576656e7404045400012c744669786564526174655061796d656e7453747265616d437265617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e0001107261746518013042616c616e63654f663c543e000008e5014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d20697320637265617465642e2050726f766964657320696e666f726d6174696f6e2061626f7574207468652050726f766964657220616e642055736572206f66207468652073747265616d54616e642069747320696e697469616c20726174652e744669786564526174655061796d656e7453747265616d557064617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e0001206e65775f7261746518013042616c616e63654f663c543e000108e5014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d20697320757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d7c616e6420746865206e65772072617465206f66207468652073747265616d2e744669786564526174655061796d656e7453747265616d44656c65746564080130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000204e9014576656e7420656d6974746564207768656e20612066697865642d72617465207061796d656e742073747265616d2069732072656d6f7665642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d2e7c44796e616d6963526174655061796d656e7453747265616d437265617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e00013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000308ed014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d20697320637265617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d80616e642074686520696e697469616c20616d6f756e742070726f76696465642e7c44796e616d6963526174655061796d656e7453747265616d557064617465640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e00014c6e65775f616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000408ed014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d20697320757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d70616e6420746865206e657720616d6f756e742070726f76696465642e7c44796e616d6963526174655061796d656e7453747265616d44656c65746564080130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000504f1014576656e7420656d6974746564207768656e20612064796e616d69632d72617465207061796d656e742073747265616d2069732072656d6f7665642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865205573657220616e642050726f7669646572206f66207468652073747265616d2e505061796d656e7453747265616d436861726765640c0130757365725f6163636f756e74000130543a3a4163636f756e74496400012c70726f76696465725f696434014050726f76696465724964466f723c543e000118616d6f756e7418013042616c616e63654f663c543e00060879014576656e7420656d6974746564207768656e2061207061796d656e7420697320636861726765642e2050726f766964657320696e666f726d6174696f6e2061626f757420746865207573657220746861742077617320636861726765642c19017468652050726f76696465722074686174207265636569766564207468652066756e64732c20616e642074686520616d6f756e7420746861742077617320636861726765642e644c61737443686172676561626c65496e666f557064617465640c012c70726f76696465725f696434014050726f76696465724964466f723c543e0001506c6173745f63686172676561626c655f7469636b100144426c6f636b4e756d626572466f723c543e00016c6c6173745f63686172676561626c655f70726963655f696e64657818013042616c616e63654f663c543e00070821024576656e7420656d6974746564207768656e20612050726f76696465722773206c6173742063686172676561626c65207469636b20616e6420707269636520696e6465782061726520757064617465642e2050726f766964657320696e666f726d6174696f6e2061626f7574207468652050726f7669646572206f66207468652073747265616d2c3501746865207469636b206e756d626572206f6620746865206c6173742063686172676561626c65207469636b20616e642074686520707269636520696e6465782061742074686174207469636b2e4055736572576974686f757446756e647304010c77686f000130543a3a4163636f756e74496400080c19024576656e7420656d6974746564207768656e20612050726f766964657220697320636f72726563746c7920747279696e6720746f206368617267652061205573657220616e642074686174205573657220646f6573206e6f74206861766520656e6f7567682066756e647320746f2070617920666f722074686569722073657276696365732e390254686973206576656e7420697320656d697474656420746f20666c616720746865207573657220616e64206c657420746865206e6574776f726b206b6e6f772074686174207468652075736572206973206e6f7420706179696e6720666f7220746865207265717565737465642073657276696365732c20736f206f746865722050726f7669646572732063616e9473746f702070726f766964696e6720736572766963657320746f207468617420757365722e345573657250616964446562747304010c77686f000130543a3a4163636f756e74496400090449024576656e7420656d6974746564207768656e20612055736572207468617420686173206265656e20666c6167676564206173206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220746865697220636f6e7472616374656420736572766963657320686173207061696420616c6c20697473206f75747374616e64696e6720646562742e2c55736572536f6c76656e7404010c77686f000130543a3a4163636f756e744964000a083d024576656e7420656d6974746564207768656e20612055736572207468617420686173206265656e20666c6167676564206173206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220746865697220636f6e7472616374656420736572766963657320686173207761697465642074686520636f6f6c646f776e20706572696f642c4901636f72726563746c79207061696420616c6c207468656972206f75747374616e64696e67206465627420616e642063616e206e6f7720636f6e7472616374206e657720736572766963657320616761696e2e04b4546865206576656e747320746861742063616e20626520656d697474656420627920746869732070616c6c6574ad020c4870616c6c65745f6275636b65745f6e6674731870616c6c6574144576656e7404045400010c30416363657373536861726564080118697373756572000130543a3a4163636f756e744964000124726563697069656e740001684163636f756e7449644c6f6f6b75705461726765744f663c543e00000419014e6f74696669657320746861742061636365737320746f2061206275636b657420686173206265656e20736861726564207769746820616e6f74686572206163636f756e742e544974656d52656164416363657373557064617465640c011461646d696e000130543a3a4163636f756e7449640001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000104ec4e6f74696669657320746861742074686520726561642061636365737320666f7220616e206974656d20686173206265656e20757064617465642e284974656d4275726e65640c011c6163636f756e74000130543a3a4163636f756e7449640001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000204984e6f746966696573207468617420616e206974656d20686173206265656e206275726e65642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b1020c2c70616c6c65745f6e6674731870616c6c6574144576656e740804540004490001981c437265617465640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011c63726561746f72000130543a3a4163636f756e7449640001146f776e6572000130543a3a4163636f756e7449640000046c412060636f6c6c656374696f6e602077617320637265617465642e30466f72636543726561746564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146f776e6572000130543a3a4163636f756e74496400010484412060636f6c6c656374696f6e602077617320666f7263652d637265617465642e2444657374726f796564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400020474412060636f6c6c656374696f6e60207761732064657374726f7965642e184973737565640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400030454416e20606974656d6020776173206973737565642e2c5472616e73666572726564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e74496400040468416e20606974656d6020776173207472616e736665727265642e184275726e65640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400050460416e20606974656d60207761732064657374726f7965642e484974656d5472616e736665724c6f636b6564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400060488416e20606974656d6020626563616d65206e6f6e2d7472616e7366657261626c652e504974656d5472616e73666572556e6c6f636b6564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400070478416e20606974656d6020626563616d65207472616e7366657261626c652e504974656d50726f706572746965734c6f636b6564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001346c6f636b5f6d65746164617461200110626f6f6c00013c6c6f636b5f61747472696275746573200110626f6f6c000804a8606974656d60206d65746164617461206f7220617474726962757465732077657265206c6f636b65642e40436f6c6c656374696f6e4c6f636b6564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400090474536f6d652060636f6c6c656374696f6e6020776173206c6f636b65642e304f776e65724368616e676564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001246e65775f6f776e6572000130543a3a4163636f756e744964000a0448546865206f776e6572206368616e6765642e2c5465616d4368616e676564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001186973737565729c01504f7074696f6e3c543a3a4163636f756e7449643e00011461646d696e9c01504f7074696f6e3c543a3a4163636f756e7449643e00011c667265657a65729c01504f7074696f6e3c543a3a4163636f756e7449643e000b0470546865206d616e6167656d656e74207465616d206368616e6765642e405472616e73666572417070726f766564140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400012064656c6567617465000130543a3a4163636f756e744964000120646561646c696e65110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000c083101416e20606974656d60206f6620612060636f6c6c656374696f6e6020686173206265656e20617070726f7665642062792074686520606f776e65726020666f72207472616e736665722062793461206064656c6567617465602e44417070726f76616c43616e63656c6c6564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e74496400012064656c6567617465000130543a3a4163636f756e744964000d081901416e20617070726f76616c20666f722061206064656c656761746560206163636f756e7420746f207472616e736665722074686520606974656d60206f6620616e206974656da860636f6c6c656374696f6e60207761732063616e63656c6c65642062792069747320606f776e6572602e54416c6c417070726f76616c7343616e63656c6c65640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001146f776e6572000130543a3a4163636f756e744964000e049c416c6c20617070726f76616c73206f6620616e206974656d20676f742063616e63656c6c65642e5c436f6c6c656374696f6e436f6e6669674368616e676564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000f040101412060636f6c6c656374696f6e6020686173206861642069747320636f6e666967206368616e676564206279207468652060466f72636560206f726967696e2e54436f6c6c656374696f6e4d65746164617461536574080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001004b44e6577206d6574616461746120686173206265656e2073657420666f7220612060636f6c6c656374696f6e602e64436f6c6c656374696f6e4d65746164617461436c6561726564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001104b44d6574616461746120686173206265656e20636c656172656420666f7220612060636f6c6c656374696f6e602e3c4974656d4d657461646174615365740c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001204984e6577206d6574616461746120686173206265656e2073657420666f7220616e206974656d2e4c4974656d4d65746164617461436c6561726564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001304984d6574616461746120686173206265656e20636c656172656420666f7220616e206974656d2e2c52656465706f7369746564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001407375636365737366756c5f6974656d73b90201385665633c543a3a4974656d49643e0014042101546865206465706f73697420666f72206120736574206f6620606974656d60732077697468696e20612060636f6c6c656374696f6e6020686173206265656e20757064617465642e30417474726962757465536574140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00150405014e657720617474726962757465206d6574616461746120686173206265656e2073657420666f7220612060636f6c6c656374696f6e60206f7220606974656d602e40417474726962757465436c6561726564100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e0016040501417474726962757465206d6574616461746120686173206265656e20636c656172656420666f7220612060636f6c6c656374696f6e60206f7220606974656d602e6c4974656d41747472696275746573417070726f76616c41646465640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465000130543a3a4163636f756e744964001704cc41206e657720617070726f76616c20746f206d6f64696679206974656d2061747472696275746573207761732061646465642e744974656d41747472696275746573417070726f76616c52656d6f7665640c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465000130543a3a4163636f756e744964001804d441206e657720617070726f76616c20746f206d6f64696679206974656d2061747472696275746573207761732072656d6f7665642e684f776e657273686970416363657074616e63654368616e67656408010c77686f000130543a3a4163636f756e7449640001406d617962655f636f6c6c656374696f6e1102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001904c04f776e65727368697020616363657074616e636520686173206368616e67656420666f7220616e206163636f756e742e58436f6c6c656374696f6e4d6178537570706c79536574080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d61785f737570706c7910010c753332001a04a44d617820737570706c7920686173206265656e2073657420666f72206120636f6c6c656374696f6e2e74436f6c6c656374696f6e4d696e7453657474696e677355706461746564040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001b04ac4d696e742073657474696e677320666f72206120636f6c6c656374696f6e20686164206368616e6765642e6c4e657874436f6c6c656374696f6e4964496e6372656d656e74656404011c6e6578745f69641102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001c0401014576656e74206765747320656d6974746564207768656e2074686520604e657874436f6c6c656374696f6e496460206765747320696e6372656d656e7465642e304974656d5072696365536574100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000114707269636518013c4974656d50726963653c542c20493e00014477686974656c69737465645f62757965729c01504f7074696f6e3c543a3a4163636f756e7449643e001d047c546865207072696365207761732073657420666f7220746865206974656d2e404974656d507269636552656d6f766564080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001e048c54686520707269636520666f7220746865206974656d207761732072656d6f7665642e284974656d426f75676874140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000114707269636518013c4974656d50726963653c542c20493e00011873656c6c6572000130543a3a4163636f756e7449640001146275796572000130543a3a4163636f756e744964001f044c416e206974656d2077617320626f756768742e1c54697053656e74140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011873656e646572000130543a3a4163636f756e7449640001207265636569766572000130543a3a4163636f756e744964000118616d6f756e741801584465706f73697442616c616e63654f663c542c20493e0020043c4120746970207761732073656e742e2c53776170437265617465641801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e00210488416e20606974656d60207377617020696e74656e742077617320637265617465642e345377617043616e63656c6c65641801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e0022045c5468652073776170207761732063616e63656c6c65642e2c53776170436c61696d656420013c73656e745f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400012473656e745f6974656d100124543a3a4974656d496400013c73656e745f6974656d5f6f776e6572000130543a3a4163636f756e74496400014c72656365697665645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400013472656365697665645f6974656d100124543a3a4974656d496400014c72656365697665645f6974656d5f6f776e6572000130543a3a4163636f756e7449640001147072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e000120646561646c696e65100144426c6f636b4e756d626572466f723c543e00230468546865207377617020686173206265656e20636c61696d65642e585072655369676e6564417474726962757465735365740c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e002404fc4e657720617474726962757465732068617665206265656e2073657420666f7220616e20606974656d60206f66207468652060636f6c6c656374696f6e602e4850616c6c6574417474726962757465536574100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d110201444f7074696f6e3c543a3a4974656d49643e000124617474726962757465d102018450616c6c6574417474726962757465733c543a3a436f6c6c656374696f6e49643e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e0025084d0141206e65772061747472696275746520696e20746865206050616c6c657460206e616d657370616365207761732073657420666f72207468652060636f6c6c656374696f6e60206f7220616e20606974656d606477697468696e20746861742060636f6c6c656374696f6e602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b5020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000b9020000021000bd020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000c1020c2c70616c6c65745f6e667473147479706573484174747269627574654e616d65737061636504244163636f756e744964010001101850616c6c65740000003c436f6c6c656374696f6e4f776e6572000100244974656d4f776e65720002001c4163636f756e7404000001244163636f756e74496400030000c50204184f7074696f6e04045401c9020108104e6f6e6500000010536f6d650400c9020000010000c9020c2c70616c6c65745f6e66747314747970657348507269636557697468446972656374696f6e0418416d6f756e74011800080118616d6f756e74180118416d6f756e74000124646972656374696f6ecd0201385072696365446972656374696f6e0000cd020c2c70616c6c65745f6e667473147479706573385072696365446972656374696f6e0001081053656e640000001c5265636569766500010000d1020c2c70616c6c65745f6e6674731474797065734050616c6c6574417474726962757465730430436f6c6c656374696f6e4964011001082c55736564546f436c61696d0400100130436f6c6c656374696f6e4964000000405472616e7366657244697361626c656400010000d5020c4470616c6c65745f706172616d65746572731870616c6c6574144576656e740404540001041c557064617465640c010c6b6579d90201c43c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a4b65790464546865206b657920746861742077617320757064617465642e01246f6c645f76616c7565f10201ec4f7074696f6e3c3c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a56616c75653e047c546865206f6c642076616c7565206265666f726520746869732063616c6c2e01246e65775f76616c7565f10201ec4f7074696f6e3c3c543a3a52756e74696d65506172616d657465727320617320416767726567617465644b657956616c75653e3a3a56616c75653e0478546865206e65772076616c756520616674657220746869732063616c6c2e000c504120506172616d6574657220776173207365742e00bc497320616c736f20656d6974746564207768656e207468652076616c756520776173206e6f74206368616e6765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d902104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d735052756e74696d65506172616d65746572734b65790001043452756e74696d65436f6e6669670400dd0201bd013c64796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d6574657273206173206672616d655f737570706f72743a3a7472616974733a3a0a64796e616d69635f706172616d733a3a416767726567617465644b657956616c75653e3a3a4b657900000000dd02184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696734506172616d65746572734b657900011064536c617368416d6f756e745065724d617846696c6553697a650400e1020164536c617368416d6f756e745065724d617846696c6553697a65000000585374616b65546f4368616c6c656e6765506572696f640400e50201585374616b65546f4368616c6c656e6765506572696f6400010064436865636b706f696e744368616c6c656e6765506572696f640400e9020164436865636b706f696e744368616c6c656e6765506572696f64000200484d696e4368616c6c656e6765506572696f640400ed0201484d696e4368616c6c656e6765506572696f6400030000e102184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696764536c617368416d6f756e745065724d617846696c6553697a6500000000e502184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e666967585374616b65546f4368616c6c656e6765506572696f6400000000e902184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696764436865636b706f696e744368616c6c656e6765506572696f6400000000ed02184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e666967484d696e4368616c6c656e6765506572696f6400000000f10204184f7074696f6e04045401f5020108104e6f6e6500000010536f6d650400f5020000010000f502104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d735852756e74696d65506172616d657465727356616c75650001043452756e74696d65436f6e6669670400f90201c5013c64796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d6574657273206173206672616d655f737570706f72743a3a7472616974733a3a0a64796e616d69635f706172616d733a3a416767726567617465644b657956616c75653e3a3a56616c756500000000f902184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e6669673c506172616d657465727356616c756500011064536c617368416d6f756e745065724d617846696c6553697a65040018011c42616c616e6365000000585374616b65546f4368616c6c656e6765506572696f64040018011c42616c616e636500010064436865636b706f696e744368616c6c656e6765506572696f64040010012c426c6f636b4e756d626572000200484d696e4368616c6c656e6765506572696f64040010012c426c6f636b4e756d62657200030000fd0208306672616d655f73797374656d14506861736500010c384170706c7945787472696e736963040010010c7533320000003046696e616c697a6174696f6e00010038496e697469616c697a6174696f6e000200000103000002050300050300000408101000090308306672616d655f73797374656d584c61737452756e74696d6555706772616465496e666f0000080130737065635f76657273696f6ecc014c636f6465633a3a436f6d706163743c7533323e000124737065635f6e616d650d03016473705f72756e74696d653a3a52756e74696d65537472696e6700000d030000050200110308306672616d655f73797374656d60436f646555706772616465417574686f72697a6174696f6e0404540000080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c000015030c306672616d655f73797374656d1870616c6c65741043616c6c04045400012c1872656d61726b04011872656d61726b38011c5665633c75383e00000c684d616b6520736f6d65206f6e2d636861696e2072656d61726b2e008843616e20626520657865637574656420627920657665727920606f726967696e602e387365745f686561705f7061676573040114706167657330010c753634000104f853657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040110636f646538011c5665633c75383e0002046453657420746865206e65772072756e74696d6520636f64652e5c7365745f636f64655f776974686f75745f636865636b73040110636f646538011c5665633c75383e000310190153657420746865206e65772072756e74696d6520636f646520776974686f757420646f696e6720616e7920636865636b73206f662074686520676976656e2060636f6465602e0051014e6f746520746861742072756e74696d652075706772616465732077696c6c206e6f742072756e20696620746869732069732063616c6c656420776974682061206e6f742d696e6372656173696e6720737065632076657273696f6e212c7365745f73746f726167650401146974656d73190301345665633c4b657956616c75653e0004046853657420736f6d65206974656d73206f662073746f726167652e306b696c6c5f73746f726167650401106b657973690201205665633c4b65793e000504744b696c6c20736f6d65206974656d732066726f6d2073746f726167652e2c6b696c6c5f70726566697808011870726566697838010c4b657900011c7375626b65797310010c75333200061011014b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e0039012a2a4e4f54453a2a2a2057652072656c79206f6e2074686520526f6f74206f726967696e20746f2070726f7669646520757320746865206e756d626572206f66207375626b65797320756e6465723d0174686520707265666978207765206172652072656d6f76696e6720746f2061636375726174656c792063616c63756c6174652074686520776569676874206f6620746869732066756e6374696f6e2e4472656d61726b5f776974685f6576656e7404011872656d61726b38011c5665633c75383e000704a44d616b6520736f6d65206f6e2d636861696e2072656d61726b20616e6420656d6974206576656e742e44617574686f72697a655f75706772616465040124636f64655f6861736834011c543a3a486173680009106101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e80617574686f72697a655f757067726164655f776974686f75745f636865636b73040124636f64655f6861736834011c543a3a48617368000a206101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e005d015741524e494e473a205468697320617574686f72697a657320616e207570677261646520746861742077696c6c2074616b6520706c61636520776974686f757420616e792073616665747920636865636b732c20666f7259016578616d706c652074686174207468652073706563206e616d652072656d61696e73207468652073616d6520616e642074686174207468652076657273696f6e206e756d62657220696e637265617365732e204e6f74f07265636f6d6d656e64656420666f72206e6f726d616c207573652e205573652060617574686f72697a655f757067726164656020696e73746561642e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e606170706c795f617574686f72697a65645f75706772616465040110636f646538011c5665633c75383e000b24550150726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e00490149662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d65e872656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e005901446570656e64696e67206f6e207468652072756e74696d65277320604f6e536574436f64656020636f6e66696775726174696f6e2c20746869732066756e6374696f6e206d6179206469726563746c79206170706c791101746865206e65772060636f64656020696e207468652073616d6520626c6f636b206f7220617474656d707420746f207363686564756c652074686520757067726164652e0060416c6c206f726967696e732061726520616c6c6f7765642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e19030000021d03001d030000040838380021030c306672616d655f73797374656d186c696d69747330426c6f636b5765696768747300000c0128626173655f626c6f636b2801185765696768740001246d61785f626c6f636b2801185765696768740001247065725f636c617373250301845065724469737061746368436c6173733c57656967687473506572436c6173733e000025030c346672616d655f737570706f7274206469737061746368405065724469737061746368436c617373040454012903000c01186e6f726d616c290301045400012c6f7065726174696f6e616c29030104540001246d616e6461746f72792903010454000029030c306672616d655f73797374656d186c696d6974733c57656967687473506572436c6173730000100138626173655f65787472696e7369632801185765696768740001346d61785f65787472696e7369632d0301384f7074696f6e3c5765696768743e0001246d61785f746f74616c2d0301384f7074696f6e3c5765696768743e00012072657365727665642d0301384f7074696f6e3c5765696768743e00002d0304184f7074696f6e04045401280108104e6f6e6500000010536f6d65040028000001000031030c306672616d655f73797374656d186c696d6974732c426c6f636b4c656e677468000004010c6d6178350301545065724469737061746368436c6173733c7533323e000035030c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540110000c01186e6f726d616c1001045400012c6f7065726174696f6e616c100104540001246d616e6461746f72791001045400003903082873705f776569676874733c52756e74696d65446257656967687400000801107265616430010c753634000114777269746530010c75363400003d03082873705f76657273696f6e3852756e74696d6556657273696f6e0000200124737065635f6e616d650d03013452756e74696d65537472696e67000124696d706c5f6e616d650d03013452756e74696d65537472696e67000144617574686f72696e675f76657273696f6e10010c753332000130737065635f76657273696f6e10010c753332000130696d706c5f76657273696f6e10010c753332000110617069734103011c4170697356656300014c7472616e73616374696f6e5f76657273696f6e10010c75333200013473746174655f76657273696f6e080108753800004103040c436f7704045401450300040045030000004503000002490300490300000408290110004d03000005040051030c306672616d655f73797374656d1870616c6c6574144572726f720404540001243c496e76616c6964537065634e616d650000081101546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e685370656356657273696f6e4e65656473546f496e63726561736500010841015468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e744661696c6564546f4578747261637452756e74696d6556657273696f6e00020cec4661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e0009014569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e4c4e6f6e44656661756c74436f6d706f73697465000304fc537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e3c4e6f6e5a65726f526566436f756e74000404350154686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e3043616c6c46696c7465726564000504d0546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e6c4d756c7469426c6f636b4d6967726174696f6e734f6e676f696e67000604550141206d756c74692d626c6f636b206d6967726174696f6e206973206f6e676f696e6720616e642070726576656e7473207468652063757272656e7420636f64652066726f6d206265696e67207265706c616365642e444e6f7468696e67417574686f72697a6564000704584e6f207570677261646520617574686f72697a65642e30556e617574686f72697a656400080494546865207375626d697474656420636f6465206973206e6f7420617574686f72697a65642e046c4572726f7220666f72207468652053797374656d2070616c6c6574550300000259030059030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e7420416e636573746f720404480134000c0138757365645f62616e6477696474685d0301345573656442616e647769647468000138706172615f686561645f68617368710301244f7074696f6e3c483e000160636f6e73756d65645f676f5f61686561645f7369676e616c7503018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e00005d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74345573656442616e64776964746800000c0134756d705f6d73675f636f756e7410010c75333200013c756d705f746f74616c5f627974657310010c75333200013468726d705f6f7574676f696e676103018c42547265654d61703c5061726149642c2048726d704368616e6e656c5570646174653e00006103042042547265654d617008044b01e9010456016503000400690300000065030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e744448726d704368616e6e656c55706461746500000801246d73675f636f756e7410010c75333200012c746f74616c5f627974657310010c753332000069030000026d03006d0300000408e901650300710304184f7074696f6e04045401340108104e6f6e6500000010536f6d650400340000010000750304184f7074696f6e0404540179030108104e6f6e6500000010536f6d6504007903000001000079030c4c706f6c6b61646f745f7072696d6974697665730876373855706772616465476f41686561640001081441626f72740000001c476f4168656164000100007d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74385365676d656e74547261636b65720404480134000c0138757365645f62616e6477696474685d0301345573656442616e64776964746800013868726d705f77617465726d61726b110201804f7074696f6e3c72656c61795f636861696e3a3a426c6f636b4e756d6265723e000160636f6e73756d65645f676f5f61686561645f7369676e616c7503018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e000081030c4c706f6c6b61646f745f7072696d6974697665730876375c50657273697374656456616c69646174696f6e446174610804480134044e01100010012c706172656e745f6865616485030120486561644461746100014c72656c61795f706172656e745f6e756d6265721001044e00016472656c61795f706172656e745f73746f726167655f726f6f74340104480001306d61785f706f765f73697a6510010c753332000085030c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665732048656164446174610000040038011c5665633c75383e0000890304184f7074696f6e040454018d030108104e6f6e6500000010536f6d6504008d0300000100008d030c4c706f6c6b61646f745f7072696d69746976657308763748557067726164655265737472696374696f6e0001041c50726573656e740000000091030c1c73705f747269653473746f726167655f70726f6f663053746f7261676550726f6f660000040128747269655f6e6f6465739503014442547265655365743c5665633c75383e3e00009503042042547265655365740404540138000400690200000099030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f74584d6573736167696e675374617465536e617073686f740000100130646d715f6d71635f6865616434014472656c61795f636861696e3a3a4861736800019c72656c61795f64697370617463685f71756575655f72656d61696e696e675f63617061636974799d03018c52656c61794469737061746368517565756552656d61696e696e674361706163697479000140696e67726573735f6368616e6e656c73a10301885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00013c6567726573735f6368616e6e656c73a10301885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00009d030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f748c52656c61794469737061746368517565756552656d61696e696e674361706163697479000008013c72656d61696e696e675f636f756e7410010c75333200013872656d61696e696e675f73697a6510010c7533320000a103000002a50300a50300000408e901a90300a9030c4c706f6c6b61646f745f7072696d6974697665730876374c416272696467656448726d704368616e6e656c00001801306d61785f636170616369747910010c7533320001386d61785f746f74616c5f73697a6510010c7533320001406d61785f6d6573736167655f73697a6510010c7533320001246d73675f636f756e7410010c753332000128746f74616c5f73697a6510010c7533320001206d71635f68656164710301304f7074696f6e3c486173683e0000ad030c4c706f6c6b61646f745f7072696d697469766573087637644162726964676564486f7374436f6e66696775726174696f6e00002801346d61785f636f64655f73697a6510010c7533320001486d61785f686561645f646174615f73697a6510010c7533320001586d61785f7570776172645f71756575655f636f756e7410010c7533320001546d61785f7570776172645f71756575655f73697a6510010c75333200015c6d61785f7570776172645f6d6573736167655f73697a6510010c7533320001906d61785f7570776172645f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200018868726d705f6d61785f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200016c76616c69646174696f6e5f757067726164655f636f6f6c646f776e10012c426c6f636b4e756d62657200016076616c69646174696f6e5f757067726164655f64656c617910012c426c6f636b4e756d6265720001506173796e635f6261636b696e675f706172616d73b10301484173796e634261636b696e67506172616d730000b103104c706f6c6b61646f745f7072696d697469766573087637346173796e635f6261636b696e67484173796e634261636b696e67506172616d73000008014c6d61785f63616e6469646174655f646570746810010c753332000150616c6c6f7765645f616e6365737472795f6c656e10010c7533320000b503089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e74444d6573736167655175657565436861696e0000040034012452656c6179486173680000b903042042547265654d617008044b01e901045601b503000400bd03000000bd03000002c10300c10300000408e901b50300c503000002c90300c9030860706f6c6b61646f745f636f72655f7072696d6974697665734c4f7574626f756e6448726d704d6573736167650408496401e90100080124726563697069656e74e901010849640001106461746138015073705f7374643a3a7665633a3a5665633c75383e0000cd030c3473705f61726974686d657469632c66697865645f706f696e742446697865645531323800000400180110753132380000d1030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c65741043616c6c0404540001104c7365745f76616c69646174696f6e5f6461746104011064617461d503015450617261636861696e496e686572656e744461746100002480536574207468652063757272656e742076616c69646174696f6e20646174612e004101546869732073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e887068617365206966207468652063616c6c20776173206e6f7420696e766f6b65642e00d0546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e7460002101417320612073696465206566666563742c20746869732066756e6374696f6e207570677261646573207468652063757272656e742076616c69646174696f6e2066756e6374696f6e8469662074686520617070726f7072696174652074696d652068617320636f6d652e607375646f5f73656e645f7570776172645f6d65737361676504011c6d6573736167653801345570776172644d65737361676500010044617574686f72697a655f75706772616465080124636f64655f6861736834011c543a3a48617368000134636865636b5f76657273696f6e200110626f6f6c0002206101417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c696564186c617465722e005d015468652060636865636b5f76657273696f6e6020706172616d657465722073657473206120626f6f6c65616e20666c616720666f722077686574686572206f72206e6f74207468652072756e74696d6527732073706563610176657273696f6e20616e64206e616d652073686f756c64206265207665726966696564206f6e20757067726164652e2053696e63652074686520617574686f72697a6174696f6e206f6e6c7920686173206120686173682cb069742063616e6e6f742061637475616c6c7920706572666f726d2074686520766572696669636174696f6e2e007c546869732063616c6c20726571756972657320526f6f74206f726967696e2e60656e6163745f617574686f72697a65645f75706772616465040110636f646538011c5665633c75383e000324550150726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e00490149662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d65e872656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e005d014e6f7465207468617420746869732066756e6374696f6e2077696c6c206e6f74206170706c7920746865206e65772060636f6465602c20627574206f6e6c7920617474656d707420746f207363686564756c652074686574757067726164652077697468207468652052656c617920436861696e2e0060416c6c206f726967696e732061726520616c6c6f7765642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ed503089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e745450617261636861696e496e686572656e7444617461000010013c76616c69646174696f6e5f646174618103015c50657273697374656456616c69646174696f6e4461746100014472656c61795f636861696e5f73746174659103015473705f747269653a3a53746f7261676550726f6f66000144646f776e776172645f6d65737361676573d903016c5665633c496e626f756e64446f776e776172644d6573736167653e00014c686f72697a6f6e74616c5f6d65737361676573e10301a442547265654d61703c5061726149642c205665633c496e626f756e6448726d704d6573736167653e3e0000d903000002dd0300dd030860706f6c6b61646f745f636f72655f7072696d69746976657358496e626f756e64446f776e776172644d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d62657200010c6d736738013c446f776e776172644d6573736167650000e103042042547265654d617008044b01e901045601e503000400ed03000000e503000002e90300e9030860706f6c6b61646f745f636f72655f7072696d69746976657348496e626f756e6448726d704d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d6265720001106461746138015073705f7374643a3a7665633a3a5665633c75383e0000ed03000002f10300f10300000408e901e50300f5030c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144572726f720404540001204c4f7665726c617070696e6755706772616465730000041901417474656d707420746f20757067726164652076616c69646174696f6e2066756e6374696f6e207768696c65206578697374696e6720757067726164652070656e64696e672e5050726f686962697465644279506f6c6b61646f740001044d01506f6c6b61646f742063757272656e746c792070726f68696269747320746869732070617261636861696e2066726f6d20757067726164696e67206974732076616c69646174696f6e2066756e6374696f6e2e18546f6f426967000208450154686520737570706c6965642076616c69646174696f6e2066756e6374696f6e2068617320636f6d70696c656420696e746f206120626c6f62206c6172676572207468616e20506f6c6b61646f742069733c77696c6c696e6720746f2072756e2e6856616c69646174696f6e446174614e6f74417661696c61626c650003041d0154686520696e686572656e7420776869636820737570706c696573207468652076616c69646174696f6e206461746120646964206e6f742072756e207468697320626c6f636b2e74486f7374436f6e66696775726174696f6e4e6f74417661696c61626c65000404290154686520696e686572656e7420776869636820737570706c6965732074686520686f737420636f6e66696775726174696f6e20646964206e6f742072756e207468697320626c6f636b2e304e6f745363686564756c6564000504d84e6f2076616c69646174696f6e2066756e6374696f6e20757067726164652069732063757272656e746c79207363686564756c65642e444e6f7468696e67417574686f72697a6564000604904e6f20636f6465207570677261646520686173206265656e20617574686f72697a65642e30556e617574686f72697a6564000704bc54686520676976656e20636f6465207570677261646520686173206e6f74206265656e20617574686f72697a65642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ef9030c4070616c6c65745f74696d657374616d701870616c6c65741043616c6c0404540001040c73657404010c6e6f772c0124543a3a4d6f6d656e7400004c54536574207468652063757272656e742074696d652e005501546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6ed470686173652c20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e0041015468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e7420737065636966696564206279685b60436f6e6669673a3a4d696e696d756d506572696f64605d2e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f4e6f6e655f2e0051015468697320646973706174636820636c617373206973205f4d616e6461746f72795f20746f20656e73757265206974206765747320657865637574656420696e2074686520626c6f636b2e204265206177617265510174686174206368616e67696e672074686520636f6d706c6578697479206f6620746869732063616c6c20636f756c6420726573756c742065786861757374696e6720746865207265736f757263657320696e206184626c6f636b20746f206578656375746520616e79206f746865722063616c6c732e0034232320436f6d706c657869747931012d20604f2831296020284e6f7465207468617420696d706c656d656e746174696f6e73206f6620604f6e54696d657374616d7053657460206d75737420616c736f20626520604f283129602955012d20312073746f72616765207265616420616e6420312073746f72616765206d75746174696f6e2028636f64656320604f283129602062656361757365206f6620604469645570646174653a3a74616b656020696e402020606f6e5f66696e616c697a656029d42d2031206576656e742068616e646c657220606f6e5f74696d657374616d705f736574602e204d75737420626520604f283129602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732efd030c5873746167696e675f70617261636861696e5f696e666f1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e01040c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e6465645665630804540105040453000004000d0401185665633c543e000005040c3c70616c6c65745f62616c616e6365731474797065732c42616c616e63654c6f636b041c42616c616e63650118000c01086964290101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500011c726561736f6e730904011c526561736f6e73000009040c3c70616c6c65745f62616c616e6365731474797065731c526561736f6e7300010c0c466565000000104d6973630001000c416c6c000200000d0400000205040011040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011504045300000400190401185665633c543e000015040c3c70616c6c65745f62616c616e6365731474797065732c52657365727665446174610844526573657276654964656e7469666965720129011c42616c616e6365011800080108696429010144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e6365000019040000021504001d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012104045300000400310401185665633c543e000021040c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640125041c42616c616e63650118000801086964250401084964000118616d6f756e7418011c42616c616e636500002504084c73746f726167655f6875625f72756e74696d654452756e74696d65486f6c64526561736f6e0001082450726f76696465727304002904019070616c6c65745f73746f726167655f70726f7669646572733a3a486f6c64526561736f6e002800385061796d656e7453747265616d7304002d04018870616c6c65745f7061796d656e745f73747265616d733a3a486f6c64526561736f6e002c000029040c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c657428486f6c64526561736f6e0001085853746f7261676550726f76696465724465706f736974000000344275636b65744465706f736974000100002d040c5870616c6c65745f7061796d656e745f73747265616d731870616c6c657428486f6c64526561736f6e000104505061796d656e7453747265616d4465706f73697400000000310400000221040035040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540139040453000004003d0401185665633c543e000039040c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e740808496401981c42616c616e636501180008010869649801084964000118616d6f756e7418011c42616c616e636500003d0400000239040041040c3c70616c6c65745f62616c616e6365731870616c6c65741043616c6c080454000449000124507472616e736665725f616c6c6f775f646561746808011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e636500001cd45472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e003501607472616e736665725f616c6c6f775f6465617468602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e11014966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c74b06f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e001501546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e38666f7263655f7472616e736665720c0118736f75726365450401504163636f756e7449644c6f6f6b75704f663c543e00011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e6365000208610145786163746c7920617320607472616e736665725f616c6c6f775f6465617468602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e74446d6179206265207370656369666965642e4c7472616e736665725f6b6565705f616c69766508011064657374450401504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565dc0128543a3a42616c616e6365000318590153616d6520617320746865205b607472616e736665725f616c6c6f775f6465617468605d2063616c6c2c206275742077697468206120636865636b207468617420746865207472616e736665722077696c6c206e6f74606b696c6c20746865206f726967696e206163636f756e742e00e8393925206f66207468652074696d6520796f752077616e74205b607472616e736665725f616c6c6f775f6465617468605d20696e73746561642e00f05b607472616e736665725f616c6c6f775f6465617468605d3a207374727563742e50616c6c65742e68746d6c236d6574686f642e7472616e73666572307472616e736665725f616c6c08011064657374450401504163636f756e7449644c6f6f6b75704f663c543e0001286b6565705f616c697665200110626f6f6c00043c05015472616e736665722074686520656e74697265207472616e7366657261626c652062616c616e63652066726f6d207468652063616c6c6572206163636f756e742e0059014e4f54453a20546869732066756e6374696f6e206f6e6c7920617474656d70747320746f207472616e73666572205f7472616e7366657261626c655f2062616c616e6365732e2054686973206d65616e7320746861746101616e79206c6f636b65642c2072657365727665642c206f72206578697374656e7469616c206465706f7369747320287768656e20606b6565705f616c6976656020697320607472756560292c2077696c6c206e6f742062655d017472616e7366657272656420627920746869732066756e6374696f6e2e20546f20656e73757265207468617420746869732066756e6374696f6e20726573756c747320696e2061206b696c6c6564206163636f756e742c4501796f75206d69676874206e65656420746f207072657061726520746865206163636f756e742062792072656d6f76696e6720616e79207265666572656e636520636f756e746572732c2073746f72616765406465706f736974732c206574632e2e2e00c0546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265205369676e65642e00a02d206064657374603a2054686520726563697069656e74206f6620746865207472616e736665722e59012d20606b6565705f616c697665603a204120626f6f6c65616e20746f2064657465726d696e652069662074686520607472616e736665725f616c6c60206f7065726174696f6e2073686f756c642073656e6420616c6c4d0120206f66207468652066756e647320746865206163636f756e74206861732c2063617573696e67207468652073656e646572206163636f756e7420746f206265206b696c6c6564202866616c7365292c206f72590120207472616e736665722065766572797468696e6720657863657074206174206c6561737420746865206578697374656e7469616c206465706f7369742c2077686963682077696c6c2067756172616e74656520746f9c20206b656570207468652073656e646572206163636f756e7420616c697665202874727565292e3c666f7263655f756e7265736572766508010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e000118616d6f756e74180128543a3a42616c616e636500050cb0556e7265736572766520736f6d652062616c616e63652066726f6d2061207573657220627920666f7263652e006c43616e206f6e6c792062652063616c6c656420627920524f4f542e40757067726164655f6163636f756e747304010c77686fa401445665633c543a3a4163636f756e7449643e0006207055706772616465206120737065636966696564206163636f756e742e00742d20606f726967696e603a204d75737420626520605369676e6564602e902d206077686f603a20546865206163636f756e7420746f2062652075706772616465642e005501546869732077696c6c20776169766520746865207472616e73616374696f6e20666565206966206174206c6561737420616c6c2062757420313025206f6620746865206163636f756e7473206e656564656420746f410162652075706772616465642e20285765206c657420736f6d65206e6f74206861766520746f206265207570677261646564206a75737420696e206f7264657220746f20616c6c6f7720666f722074686558706f73736962696c697479206f6620636875726e292e44666f7263655f7365745f62616c616e636508010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565dc0128543a3a42616c616e636500080cac5365742074686520726567756c61722062616c616e6365206f66206120676976656e206163636f756e742e00b0546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e6c666f7263655f61646a7573745f746f74616c5f69737375616e6365080124646972656374696f6e4d04014c41646a7573746d656e74446972656374696f6e00011464656c7461dc0128543a3a42616c616e6365000914b841646a7573742074686520746f74616c2069737375616e636520696e20612073617475726174696e67207761792e00fc43616e206f6e6c792062652063616c6c656420627920726f6f7420616e6420616c77617973206e65656473206120706f736974697665206064656c7461602e002423204578616d706c65106275726e08011476616c7565dc0128543a3a42616c616e63650001286b6565705f616c697665200110626f6f6c000a1cfc4275726e2074686520737065636966696564206c697175696420667265652062616c616e63652066726f6d20746865206f726967696e206163636f756e742e002501496620746865206f726967696e2773206163636f756e7420656e64732075702062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c7409016f6620746865206275726e20616e6420606b6565705f616c697665602069732066616c73652c20746865206163636f756e742077696c6c206265207265617065642e005101556e6c696b652073656e64696e672066756e647320746f2061205f6275726e5f20616464726573732c207768696368206d6572656c79206d616b6573207468652066756e647320696e61636365737369626c652c21017468697320606275726e60206f7065726174696f6e2077696c6c2072656475636520746f74616c2069737375616e63652062792074686520616d6f756e74205f6275726e65645f2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e45040c2873705f72756e74696d65306d756c746961646472657373304d756c74694164647265737308244163636f756e7449640100304163636f756e74496e6465780198011408496404000001244163636f756e74496400000014496e6465780400490401304163636f756e74496e6465780001000c526177040038011c5665633c75383e0002002441646472657373333204000401205b75383b2033325d000300244164647265737332300400d801205b75383b2032305d00040000490400000698004d040c3c70616c6c65745f62616c616e6365731474797065734c41646a7573746d656e74446972656374696f6e00010820496e6372656173650000002044656372656173650001000051040c3c70616c6c65745f62616c616e6365731870616c6c6574144572726f720804540004490001303856657374696e6742616c616e63650000049c56657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e544c69717569646974795265737472696374696f6e73000104c84163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e4c496e73756666696369656e7442616c616e63650002047842616c616e636520746f6f206c6f7720746f2073656e642076616c75652e484578697374656e7469616c4465706f736974000304ec56616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e34457870656e646162696c697479000404905472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e5c4578697374696e6756657374696e675363686564756c65000504cc412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e2c446561644163636f756e740006048c42656e6566696369617279206163636f756e74206d757374207072652d65786973742e3c546f6f4d616e795265736572766573000704b84e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e30546f6f4d616e79486f6c6473000804f84e756d626572206f6620686f6c647320657863656564206056617269616e74436f756e744f663c543a3a52756e74696d65486f6c64526561736f6e3e602e38546f6f4d616e79467265657a6573000904984e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e4c49737375616e63654465616374697661746564000a0401015468652069737375616e63652063616e6e6f74206265206d6f6469666965642073696e636520697420697320616c72656164792064656163746976617465642e2444656c74615a65726f000b04645468652064656c74612063616e6e6f74206265207a65726f2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e5504086870616c6c65745f7472616e73616374696f6e5f7061796d656e742052656c6561736573000108245631416e6369656e740000000856320001000059040c2c70616c6c65745f7375646f1870616c6c65741043616c6c040454000114107375646f04011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e000004350141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e547375646f5f756e636865636b65645f77656967687408011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e000118776569676874280118576569676874000114350141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e2d01546869732066756e6374696f6e20646f6573206e6f7420636865636b2074686520776569676874206f66207468652063616c6c2c20616e6420696e737465616420616c6c6f777320746865b05375646f207573657220746f20737065636966792074686520776569676874206f66207468652063616c6c2e00cc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e1c7365745f6b657904010c6e6577450401504163636f756e7449644c6f6f6b75704f663c543e0002085d0141757468656e74696361746573207468652063757272656e74207375646f206b657920616e6420736574732074686520676976656e204163636f756e7449642028606e6577602920617320746865206e6577207375646f106b65792e1c7375646f5f617308010c77686f450401504163636f756e7449644c6f6f6b75704f663c543e00011063616c6c5d04017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0003104d0141757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c207769746820605369676e656460206f726967696e2066726f6d406120676976656e206163636f756e742e00cc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e2872656d6f76655f6b657900040c845065726d616e656e746c792072656d6f76657320746865207375646f206b65792e006c2a2a546869732063616e6e6f7420626520756e2d646f6e652e2a2a040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e5d04084c73746f726167655f6875625f72756e74696d652c52756e74696d6543616c6c0001501853797374656d0400150301ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e0000003c50617261636861696e53797374656d0400d10301d10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e53797374656d2c2052756e74696d653e0001002454696d657374616d700400f90301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54696d657374616d702c2052756e74696d653e0002003450617261636861696e496e666f0400fd0301c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e496e666f2c2052756e74696d653e0003002042616c616e6365730400410401b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e000a00105375646f0400590401a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5375646f2c2052756e74696d653e000f0044436f6c6c61746f7253656c656374696f6e0400610401d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6c6c61746f7253656c656374696f6e2c2052756e74696d653e0015001c53657373696f6e0400650401b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53657373696f6e2c2052756e74696d653e0016002458636d7051756575650400710401b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58636d7051756575652c2052756e74696d653e001e002c506f6c6b61646f7458636d0400750401c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506f6c6b61646f7458636d2c2052756e74696d653e001f002843756d756c757358636d0400150501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c43756d756c757358636d2c2052756e74696d653e002000304d65737361676551756575650400190501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4d65737361676551756575652c2052756e74696d653e0021002450726f76696465727304001d0501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50726f7669646572732c2052756e74696d653e0028002846696c6553797374656d0400210501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46696c6553797374656d2c2052756e74696d653e0029003050726f6f66734465616c65720400550501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50726f6f66734465616c65722c2052756e74696d653e002a002852616e646f6d6e6573730400590501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c52616e646f6d6e6573732c2052756e74696d653e002b00385061796d656e7453747265616d7304005d0501cd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5061796d656e7453747265616d732c2052756e74696d653e002c00284275636b65744e6674730400610501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4275636b65744e6674732c2052756e74696d653e002d00104e6674730400690501a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4e6674732c2052756e74696d653e00320028506172616d65746572730400c50501bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506172616d65746572732c2052756e74696d653e0033000061040c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65741043616c6c040454000124447365745f696e76756c6e657261626c657304010c6e6577a401445665633c543a3a4163636f756e7449643e000034310153657420746865206c697374206f6620696e76756c6e657261626c65202866697865642920636f6c6c61746f72732e20546865736520636f6c6c61746f7273206d75737420646f20736f6d65d07072657061726174696f6e2c206e616d656c7920746f206861766520726567697374657265642073657373696f6e206b6579732e0059015468652063616c6c2077696c6c2072656d6f766520616e79206163636f756e747320746861742068617665206e6f742072656769737465726564206b6579732066726f6d20746865207365742e20546861742069732c5d016974206973206e6f6e2d61746f6d69633b207468652063616c6c6572206163636570747320616c6c20604163636f756e74496460732070617373656420696e20606e657760205f696e646976696475616c6c795f206173310161636365707461626c6520496e76756c6e657261626c65732c20616e64206973206e6f742070726f706f73696e672061205f7365745f206f66206e657720496e76756c6e657261626c65732e005901546869732063616c6c20646f6573206e6f74206d61696e7461696e206d757475616c206578636c75736976697479206f662060496e76756c6e657261626c65736020616e64206043616e64696461746573602e20497461016973207265636f6d6d656e64656420746f207573652061206261746368206f6620606164645f696e76756c6e657261626c656020616e64206072656d6f76655f696e76756c6e657261626c656020696e73746561642e204151016062617463685f616c6c602063616e20616c736f206265207573656420746f20656e666f7263652061746f6d69636974792e20496620616e792063616e646964617465732061726520696e636c7564656420696e4d01606e6577602c20746865792073686f756c642062652072656d6f7665642077697468206072656d6f76655f696e76756c6e657261626c655f63616e6469646174656020616674657220657865637574696f6e2e00944d7573742062652063616c6c65642062792074686520605570646174654f726967696e602e587365745f646573697265645f63616e6469646174657304010c6d617810010c75333200011455015365742074686520696465616c206e756d626572206f66206e6f6e2d696e76756c6e657261626c6520636f6c6c61746f72732e204966206c6f776572696e672074686973206e756d6265722c207468656e2074686561016e756d626572206f662072756e6e696e6720636f6c6c61746f727320636f756c6420626520686967686572207468616e2074686973206669677572652e2041736964652066726f6d2074686174206564676520636173652c350174686572652073686f756c64206265206e6f206f746865722077617920746f2068617665206d6f72652063616e64696461746573207468616e207468652064657369726564206e756d6265722e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e487365745f63616e6469646163795f626f6e64040110626f6e6418013042616c616e63654f663c543e00021c78536574207468652063616e64696461637920626f6e6420616d6f756e742e0051014966207468652063616e64696461637920626f6e6420697320696e6372656173656420627920746869732063616c6c2c20616c6c2063757272656e742063616e64696461746573207768696368206861766520614d016465706f736974206c6f776572207468616e20746865206e657720626f6e642077696c6c206265206b69636b65642066726f6d20746865206c69737420616e6420676574207468656972206465706f73697473146261636b2e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e5472656769737465725f61735f63616e646964617465000310410152656769737465722074686973206163636f756e74206173206120636f6c6c61746f722063616e6469646174652e20546865206163636f756e74206d7573742028612920616c726561647920686176651d01726567697374657265642073657373696f6e206b65797320616e64202862292062652061626c6520746f207265736572766520746865206043616e646964616379426f6e64602e00dc546869732063616c6c206973206e6f7420617661696c61626c6520746f2060496e76756c6e657261626c656020636f6c6c61746f72732e306c656176655f696e74656e7400041455014465726567697374657220606f726967696e60206173206120636f6c6c61746f722063616e6469646174652e204e6f746520746861742074686520636f6c6c61746f722063616e206f6e6c79206c65617665206f6e0d0173657373696f6e206368616e67652e20546865206043616e646964616379426f6e64602077696c6c20626520756e726573657276656420696d6d6564696174656c792e001901546869732063616c6c2077696c6c206661696c2069662074686520746f74616c206e756d626572206f662063616e6469646174657320776f756c642064726f702062656c6f775c604d696e456c696769626c65436f6c6c61746f7273602e406164645f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e74496400051045014164642061206e6577206163636f756e74206077686f6020746f20746865206c697374206f662060496e76756c6e657261626c65736020636f6c6c61746f72732e206077686f60206d75737420686176651d01726567697374657265642073657373696f6e206b6579732e204966206077686f6020697320612063616e6469646174652c20746865792077696c6c2062652072656d6f7665642e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e4c72656d6f76655f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e744964000610610152656d6f766520616e206163636f756e74206077686f602066726f6d20746865206c697374206f662060496e76756c6e657261626c65736020636f6c6c61746f72732e2060496e76756c6e657261626c657360206d75737428626520736f727465642e00d0546865206f726967696e20666f7220746869732063616c6c206d7573742062652074686520605570646174654f726967696e602e2c7570646174655f626f6e6404012c6e65775f6465706f73697418013042616c616e63654f663c543e00071c5d01557064617465207468652063616e64696461637920626f6e64206f6620636f6c6c61746f722063616e64696461746520606f726967696e6020746f2061206e657720616d6f756e7420606e65775f6465706f736974602e00410153657474696e67206120606e65775f6465706f736974602074686174206973206c6f776572207468616e207468652063757272656e74206465706f736974207768696c6520606f726967696e60206973e06f6363757079696e67206120746f702d604465736972656443616e646964617465736020736c6f74206973206e6f7420616c6c6f7765642e005901546869732063616c6c2077696c6c206661696c20696620606f726967696e60206973206e6f74206120636f6c6c61746f722063616e6469646174652c20746865207570646174656420626f6e64206973206c6f77657219017468616e20746865206d696e696d756d2063616e64696461637920626f6e642c20616e642f6f722074686520616d6f756e742063616e6e6f742062652072657365727665642e4c74616b655f63616e6469646174655f736c6f7408011c6465706f73697418013042616c616e63654f663c543e000118746172676574000130543a3a4163636f756e74496400081c4d015468652063616c6c657220606f726967696e60207265706c6163657320612063616e64696461746520607461726765746020696e2074686520636f6c6c61746f722063616e646964617465206c6973742062795501726573657276696e6720606465706f736974602e2054686520616d6f756e7420606465706f73697460207265736572766564206279207468652063616c6c6572206d7573742062652067726561746572207468616ee0746865206578697374696e6720626f6e64206f66207468652074617267657420697420697320747279696e6720746f207265706c6163652e005901546869732063616c6c2077696c6c206661696c206966207468652063616c6c657220697320616c7265616479206120636f6c6c61746f722063616e646964617465206f7220696e76756c6e657261626c652c20746865550163616c6c657220646f6573206e6f74206861766520726567697374657265642073657373696f6e206b6579732c2074686520746172676574206973206e6f74206120636f6c6c61746f722063616e6469646174652cbc616e642f6f722074686520606465706f7369746020616d6f756e742063616e6e6f742062652072657365727665642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65040c3870616c6c65745f73657373696f6e1870616c6c65741043616c6c040454000108207365745f6b6579730801106b6579736904011c543a3a4b65797300011470726f6f6638011c5665633c75383e000024e453657473207468652073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c657220746f20606b657973602e1d01416c6c6f777320616e206163636f756e7420746f20736574206974732073657373696f6e206b6579207072696f7220746f206265636f6d696e6720612076616c696461746f722ec05468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e00d0546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265207369676e65642e0034232320436f6d706c657869747959012d20604f283129602e2041637475616c20636f737420646570656e6473206f6e20746865206e756d626572206f66206c656e677468206f662060543a3a4b6579733a3a6b65795f69647328296020776869636820697320202066697865642e2870757267655f6b657973000130c852656d6f76657320616e792073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c65722e00c05468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e005501546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265205369676e656420616e6420746865206163636f756e74206d757374206265206569746865722062655d01636f6e7665727469626c6520746f20612076616c696461746f72204944207573696e672074686520636861696e2773207479706963616c2061646472657373696e672073797374656d20287468697320757375616c6c7951016d65616e73206265696e67206120636f6e74726f6c6c6572206163636f756e7429206f72206469726563746c7920636f6e7665727469626c6520696e746f20612076616c696461746f722049442028776869636894757375616c6c79206d65616e73206265696e672061207374617368206163636f756e74292e0034232320436f6d706c65786974793d012d20604f2831296020696e206e756d626572206f66206b65792074797065732e2041637475616c20636f737420646570656e6473206f6e20746865206e756d626572206f66206c656e677468206f6698202060543a3a4b6579733a3a6b65795f6964732829602077686963682069732066697865642e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e6904084c73746f726167655f6875625f72756e74696d652c53657373696f6e4b6579730000040110617572616d0401c43c41757261206173202463726174653a3a426f756e64546f52756e74696d654170705075626c69633e3a3a5075626c696300006d04104473705f636f6e73656e7375735f617572611c737232353531392c6170705f73723235353139185075626c69630000040004013c737232353531393a3a5075626c6963000071040c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c65741043616c6c0404540001145473757370656e645f78636d5f657865637574696f6e00010c490153757370656e647320616c6c2058434d20657865637574696f6e7320666f72207468652058434d502071756575652c207265676172646c657373206f66207468652073656e6465722773206f726967696e2e00a42d20606f726967696e603a204d75737420706173732060436f6e74726f6c6c65724f726967696e602e50726573756d655f78636d5f657865637574696f6e000214b8526573756d657320616c6c2058434d20657865637574696f6e7320666f72207468652058434d502071756575652e003d014e6f7465207468617420746869732066756e6374696f6e20646f65736e2774206368616e67652074686520737461747573206f662074686520696e2f6f757420626f756e64206368616e6e656c732e00a42d20606f726967696e603a204d75737420706173732060436f6e74726f6c6c65724f726967696e602e607570646174655f73757370656e645f7468726573686f6c6404010c6e657710010c75333200031449014f76657277726974657320746865206e756d626572206f66207061676573207768696368206d75737420626520696e2074686520717565756520666f7220746865206f74686572207369646520746f20626578746f6c6420746f2073757370656e642074686569722073656e64696e672e00742d20606f726967696e603a204d75737420706173732060526f6f74602ee82d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e73757370656e645f76616c756560547570646174655f64726f705f7468726573686f6c6404010c6e657710010c75333200041445014f76657277726974657320746865206e756d626572206f66207061676573207768696368206d75737420626520696e207468652071756575652061667465722077686963682077652064726f7020616e798866757274686572206d657373616765732066726f6d20746865206368616e6e656c2e00742d20606f726967696e603a204d75737420706173732060526f6f74602eec2d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e64726f705f7468726573686f6c64605c7570646174655f726573756d655f7468726573686f6c6404010c6e657710010c7533320005144d014f76657277726974657320746865206e756d626572206f6620706167657320776869636820746865207175657565206d757374206265207265647563656420746f206265666f7265206974207369676e616c73010174686174206d6573736167652073656e64696e67206d6179207265636f6d6d656e636520616674657220697420686173206265656e2073757370656e6465642e00742d20606f726967696e603a204d75737420706173732060526f6f74602ef42d20606e6577603a20446573697265642076616c756520666f7220605175657565436f6e666967446174612e726573756d655f7468726573686f6c6460040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e75040c2870616c6c65745f78636d1870616c6c65741043616c6c0404540001381073656e6408011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011c6d65737361676579040154426f783c56657273696f6e656458636d3c28293e3e0000003c74656c65706f72745f61737365747310011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c753332000148110154656c65706f727420736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f20736f6d652064657374696e6174696f6e20636861696e2e001d012a2a546869732066756e6374696f6e20697320646570726563617465643a2055736520606c696d697465645f74656c65706f72745f6173736574736020696e73746561642e2a2a005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602e2054686520776569676874206c696d697420666f722066656573206973206e6f742070726f766964656420616e64207468757320697320756e6c696d697465642cb47769746820616c6c20666565732074616b656e206173206e65656465642066726f6d207468652061737365742e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865682020666565206f6e207468652060646573746020636861696e2e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e5c726573657276655f7472616e736665725f61737365747310011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320002785d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2c7864657374696e6174696f6e206f722072656d6f746520726573657276652e003d016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e20616e64206d6179206e6f742062652074656c65706f727461626c6520746f206064657374602e4d01202d2060617373657473602068617665206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5501202d20606173736574736020686176652064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e6101202d20606173736574736020686176652072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f7665610120202072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f20606465737460f4202020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e003d012a2a546869732066756e6374696f6e20697320646570726563617465643a2055736520606c696d697465645f726573657276655f7472616e736665725f6173736574736020696e73746561642e2a2a005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602e2054686520776569676874206c696d697420666f722066656573206973206e6f742070726f766964656420616e64207468757320697320756e6c696d697465642cb47769746820616c6c20666565732074616b656e206173206e65656465642066726f6d207468652061737365742e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e1c6578656375746508011c6d657373616765e10401b4426f783c56657273696f6e656458636d3c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e3e0001286d61785f776569676874280118576569676874000320d04578656375746520616e2058434d206d6573736167652066726f6d2061206c6f63616c2c207369676e65642c206f726967696e2e004d01416e206576656e74206973206465706f736974656420696e6469636174696e67207768657468657220606d73676020636f756c6420626520657865637574656420636f6d706c6574656c79206f72206f6e6c79287061727469616c6c792e005d014e6f206d6f7265207468616e20606d61785f776569676874602077696c6c206265207573656420696e2069747320617474656d7074656420657865637574696f6e2e2049662074686973206973206c657373207468616e4101746865206d6178696d756d20616d6f756e74206f6620776569676874207468617420746865206d65737361676520636f756c642074616b6520746f2062652065786563757465642c207468656e206e6f7c657865637574696f6e20617474656d70742077696c6c206265206d6164652e44666f7263655f78636d5f76657273696f6e0801206c6f636174696f6ebc0134426f783c4c6f636174696f6e3e00011c76657273696f6e10012858636d56657273696f6e00041849014578746f6c6c2074686174206120706172746963756c61722064657374696e6174696f6e2063616e20626520636f6d6d756e6963617465642077697468207468726f756768206120706172746963756c61723c76657273696f6e206f662058434d2e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2ed82d20606c6f636174696f6e603a205468652064657374696e6174696f6e2074686174206973206265696e67206465736372696265642e11012d206078636d5f76657273696f6e603a20546865206c61746573742076657273696f6e206f662058434d207468617420606c6f636174696f6e6020737570706f7274732e64666f7263655f64656661756c745f78636d5f76657273696f6e0401446d617962655f78636d5f76657273696f6e110201484f7074696f6e3c58636d56657273696f6e3e0005145901536574206120736166652058434d2076657273696f6e20287468652076657273696f6e20746861742058434d2073686f756c6420626520656e636f646564207769746820696620746865206d6f737420726563656e74b476657273696f6e20612064657374696e6174696f6e2063616e2061636365707420697320756e6b6e6f776e292e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e39012d20606d617962655f78636d5f76657273696f6e603a205468652064656661756c742058434d20656e636f64696e672076657273696f6e2c206f7220604e6f6e656020746f2064697361626c652e78666f7263655f7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6ed9010158426f783c56657273696f6e65644c6f636174696f6e3e000610390141736b2061206c6f636174696f6e20746f206e6f7469667920757320726567617264696e672074686569722058434d2076657273696f6e20616e6420616e79206368616e67657320746f2069742e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e59012d20606c6f636174696f6e603a20546865206c6f636174696f6e20746f2077686963682077652073686f756c642073756273637269626520666f722058434d2076657273696f6e206e6f74696669636174696f6e732e80666f7263655f756e7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6ed9010158426f783c56657273696f6e65644c6f636174696f6e3e0007184901526571756972652074686174206120706172746963756c61722064657374696e6174696f6e2073686f756c64206e6f206c6f6e676572206e6f7469667920757320726567617264696e6720616e792058434d4076657273696f6e206368616e6765732e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2e3d012d20606c6f636174696f6e603a20546865206c6f636174696f6e20746f207768696368207765206172652063757272656e746c79207375627363726962656420666f722058434d2076657273696f6ea820206e6f74696669636174696f6e73207768696368207765206e6f206c6f6e676572206465736972652e7c6c696d697465645f726573657276655f7472616e736665725f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d69740008785d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2c7864657374696e6174696f6e206f722072656d6f746520726573657276652e003d016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e20616e64206d6179206e6f742062652074656c65706f727461626c6520746f206064657374602e4d01202d2060617373657473602068617665206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5501202d20606173736574736020686176652064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e6101202d20606173736574736020686176652072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f7665610120202072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f20606465737460f4202020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602c20757020746f20656e6f75676820746f2070617920666f7220607765696768745f6c696d697460206f66207765696768742e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e5c6c696d697465645f74656c65706f72745f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d6974000948110154656c65706f727420736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f20736f6d652064657374696e6174696f6e20636861696e2e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f666101696e64657820606665655f61737365745f6974656d602c20757020746f20656e6f75676820746f2070617920666f7220607765696768745f6c696d697460206f66207765696768742e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d54202072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865682020666565206f6e207468652060646573746020636861696e2e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e40666f7263655f73757370656e73696f6e04012473757370656e646564200110626f6f6c000a10f4536574206f7220756e7365742074686520676c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e00dc2d20606f726967696e603a204d75737420626520616e206f726967696e207370656369666965642062792041646d696e4f726967696e2ed02d206073757370656e646564603a2060747275656020746f2073757370656e642c206066616c73656020746f20726573756d652e3c7472616e736665725f61737365747314011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69747101012c5765696768744c696d6974000b845d015472616e7366657220736f6d65206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207468726f756768207468656972206c6f63616c2cd064657374696e6174696f6e206f722072656d6f746520726573657276652c206f72207468726f7567682074656c65706f7274732e005101466565207061796d656e74206f6e207468652064657374696e6174696f6e2073696465206973206d6164652066726f6d2074686520617373657420696e2074686520606173736574736020766563746f72206f663501696e64657820606665655f61737365745f6974656d60202868656e636520726566657272656420746f20617320606665657360292c20757020746f20656e6f75676820746f2070617920666f724101607765696768745f6c696d697460206f66207765696768742e204966206d6f726520776569676874206973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865dc6f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e006101606173736574736020286578636c7564696e672060666565736029206d75737420686176652073616d652072657365727665206c6f636174696f6e206f72206f74686572776973652062652074656c65706f727461626c65b0746f206064657374602c206e6f206c696d69746174696f6e7320696d706f736564206f6e206066656573602e4d01202d20666f72206c6f63616c20726573657276653a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e20636861696e20616e644d01202020666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f442020206062656e6566696369617279602e6101202d20666f722064657374696e6174696f6e20726573657276653a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f2060646573746020636861696e5901202020746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64206465706f736974207468656d50202020746f206062656e6566696369617279602e5d01202d20666f722072656d6f746520726573657276653a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f207265736572766520636861696e20746f206d6f76652072657365727665735d0120202066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f746865722058434d20746f2060646573746020746f206d696e74d4202020616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e5101202d20666f722074656c65706f7274733a206275726e206c6f63616c2061737365747320616e6420666f72776172642058434d20746f2060646573746020636861696e20746f206d696e742f74656c65706f7274b020202061737365747320616e64206465706f736974207468656d20746f206062656e6566696369617279602e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e2d012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c792062652060583228506172656e742c5501202050617261636861696e282e2e29296020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f72206058312850617261636861696e282e2e29296020746f2073656e6468202066726f6d2072656c617920746f2070617261636861696e2e55012d206062656e6566696369617279603a20412062656e6566696369617279206c6f636174696f6e20666f72207468652061737365747320696e2074686520636f6e74657874206f66206064657374602e2057696c6c98202067656e6572616c6c7920626520616e20604163636f756e7449643332602076616c75652e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e4d012d20606665655f61737365745f6974656d603a2054686520696e64657820696e746f206061737365747360206f6620746865206974656d2077686963682073686f756c64206265207573656420746f207061791c2020666565732e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e30636c61696d5f61737365747308011861737365747375010150426f783c56657273696f6e65644173736574733e00012c62656e6566696369617279d9010158426f783c56657273696f6e65644c6f636174696f6e3e000c185501436c61696d73206173736574732074726170706564206f6e20746869732070616c6c65742062656361757365206f66206c6566746f7665722061737365747320647572696e672058434d20657865637574696f6e2e00ac2d20606f726967696e603a20416e796f6e652063616e2063616c6c20746869732065787472696e7369632e5d012d2060617373657473603a20546865206578616374206173736574732074686174207765726520747261707065642e20557365207468652076657273696f6e20746f207370656369667920776861742076657273696f6e9877617320746865206c6174657374207768656e2074686579207765726520747261707065642e45012d206062656e6566696369617279603a20546865206c6f636174696f6e2f6163636f756e742077686572652074686520636c61696d6564206173736574732077696c6c206265206465706f73697465642e8c7472616e736665725f6173736574735f7573696e675f747970655f616e645f7468656e1c011064657374d9010158426f783c56657273696f6e65644c6f636174696f6e3e00011861737365747375010150426f783c56657273696f6e65644173736574733e0001506173736574735f7472616e736665725f747970650d050144426f783c5472616e73666572547970653e00013872656d6f74655f666565735f696411050154426f783c56657273696f6e6564417373657449643e000148666565735f7472616e736665725f747970650d050144426f783c5472616e73666572547970653e000148637573746f6d5f78636d5f6f6e5f6465737479040154426f783c56657273696f6e656458636d3c28293e3e0001307765696768745f6c696d69747101012c5765696768744c696d6974000dc055015472616e73666572206173736574732066726f6d20746865206c6f63616c20636861696e20746f207468652064657374696e6174696f6e20636861696e207573696e67206578706c69636974207472616e7366657268747970657320666f722061737365747320616e6420666565732e0059016061737365747360206d75737420686176652073616d652072657365727665206c6f636174696f6e206f72206d61792062652074656c65706f727461626c6520746f206064657374602e2043616c6c6572206d757374ec70726f766964652074686520606173736574735f7472616e736665725f747970656020746f206265207573656420666f722060617373657473603a5101202d20605472616e73666572547970653a3a4c6f63616c52657365727665603a207472616e736665722061737365747320746f20736f7665726569676e206163636f756e74206f662064657374696e6174696f6e4d01202020636861696e20616e6420666f72776172642061206e6f74696669636174696f6e2058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365646c20202061737365747320746f206062656e6566696369617279602e5901202d20605472616e73666572547970653a3a44657374696e6174696f6e52657365727665603a206275726e206c6f63616c2061737365747320616e6420666f72776172642061206e6f74696669636174696f6e20746f590120202060646573746020636861696e20746f207769746864726177207468652072657365727665206173736574732066726f6d207468697320636861696e277320736f7665726569676e206163636f756e7420616e64842020206465706f736974207468656d20746f206062656e6566696369617279602e5901202d20605472616e73666572547970653a3a52656d6f746552657365727665287265736572766529603a206275726e206c6f63616c206173736574732c20666f72776172642058434d20746f206072657365727665606101202020636861696e20746f206d6f76652072657365727665732066726f6d207468697320636861696e277320534120746f2060646573746020636861696e27732053412c20616e6420666f727761726420616e6f74686572550120202058434d20746f2060646573746020746f206d696e7420616e64206465706f73697420726573657276652d62617365642061737365747320746f206062656e6566696369617279602e205479706963616c6c79942020207468652072656d6f746520607265736572766560206973204173736574204875622e4501202d20605472616e73666572547970653a3a54656c65706f7274603a206275726e206c6f63616c2061737365747320616e6420666f72776172642058434d20746f2060646573746020636861696e20746fe82020206d696e742f74656c65706f72742061737365747320616e64206465706f736974207468656d20746f206062656e6566696369617279602e0055014f6e207468652064657374696e6174696f6e20636861696e2c2061732077656c6c20617320616e7920696e7465726d65646961727920686f70732c2060427579457865637574696f6e60206973207573656420746f210162757920657865637574696f6e207573696e67207472616e73666572726564206061737365747360206964656e746966696564206279206072656d6f74655f666565735f6964602e59014d616b65207375726520656e6f756768206f662074686520737065636966696564206072656d6f74655f666565735f69646020617373657420697320696e636c7564656420696e2074686520676976656e206c69737461016f662060617373657473602e206072656d6f74655f666565735f6964602073686f756c6420626520656e6f75676820746f2070617920666f7220607765696768745f6c696d6974602e204966206d6f72652077656967687459016973206e6565646564207468616e20607765696768745f6c696d6974602c207468656e20746865206f7065726174696f6e2077696c6c206661696c20616e64207468652073656e7420617373657473206d6179206265206174207269736b2e0045016072656d6f74655f666565735f696460206d61792075736520646966666572656e74207472616e736665722074797065207468616e2072657374206f6620606173736574736020616e642063616e2062659c737065636966696564207468726f7567682060666565735f7472616e736665725f74797065602e0061015468652063616c6c6572206e6565647320746f207370656369667920776861742073686f756c642068617070656e20746f20746865207472616e7366657272656420617373657473206f6e6365207468657920726561636841017468652060646573746020636861696e2e205468697320697320646f6e65207468726f756768207468652060637573746f6d5f78636d5f6f6e5f646573746020706172616d657465722c207768696368fc636f6e7461696e732074686520696e737472756374696f6e7320746f2065786563757465206f6e2060646573746020617320612066696e616c20737465702e7c20205468697320697320757375616c6c792061732073696d706c652061733a510120206058636d28766563215b4465706f7369744173736574207b206173736574733a2057696c6428416c6c436f756e746564286173736574732e6c656e282929292c2062656e6566696369617279207d5d29602c3101202062757420636f756c6420626520736f6d657468696e67206d6f72652065786f746963206c696b652073656e64696e6720746865206061737365747360206576656e20667572746865722e0029012d20606f726967696e603a204d7573742062652063617061626c65206f66207769746864726177696e672074686520606173736574736020616e6420657865637574696e672058434d2e25012d206064657374603a2044657374696e6174696f6e20636f6e7465787420666f7220746865206173736574732e2057696c6c207479706963616c6c7920626520605b506172656e742c6101202050617261636861696e282e2e295d6020746f2073656e642066726f6d2070617261636861696e20746f2070617261636861696e2c206f7220605b50617261636861696e282e2e295d6020746f2073656e642066726f6d3d01202072656c617920746f2070617261636861696e2c206f72206028706172656e74733a20322c2028476c6f62616c436f6e73656e737573282e2e292c202e2e29296020746f2073656e642066726f6df4202070617261636861696e206163726f737320612062726964676520746f20616e6f746865722065636f73797374656d2064657374696e6174696f6e2e59012d2060617373657473603a205468652061737365747320746f2062652077697468647261776e2e20546869732073686f756c6420696e636c7564652074686520617373657473207573656420746f2070617920746865c82020666565206f6e20746865206064657374602028616e6420706f737369626c7920726573657276652920636861696e732e3d012d20606173736574735f7472616e736665725f74797065603a205468652058434d20605472616e736665725479706560207573656420746f207472616e73666572207468652060617373657473602e2d012d206072656d6f74655f666565735f6964603a204f6e65206f662074686520696e636c7564656420606173736574736020746f206265206265207573656420746f2070617920666565732e49012d2060666565735f7472616e736665725f74797065603a205468652058434d20605472616e736665725479706560207573656420746f207472616e736665722074686520606665657360206173736574732e59012d2060637573746f6d5f78636d5f6f6e5f64657374603a205468652058434d20746f206265206578656375746564206f6e2060646573746020636861696e20617320746865206c6173742073746570206f6620746865590120207472616e736665722c20776869636820616c736f2064657465726d696e657320776861742068617070656e7320746f2074686520617373657473206f6e207468652064657374696e6174696f6e20636861696e2e45012d20607765696768745f6c696d6974603a205468652072656d6f74652d7369646520776569676874206c696d69742c20696620616e792c20666f72207468652058434d206665652070757263686173652e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e7904080c78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010c08563204007d04015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400ad04015076333a3a58636d3c52756e74696d6543616c6c3e00030008563404000501015076343a3a58636d3c52756e74696d6543616c6c3e000400007d040c0c78636d0876320c58636d042c52756e74696d6543616c6c00000400810401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e0000810400000285040085040c0c78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404007901012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404007901012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404007901012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f69642c011c51756572794964000120726573706f6e736589040120526573706f6e73650001286d61785f7765696768742c010c753634000300345472616e7366657241737365740801186173736574737901012c4d756c746941737365747300012c62656e6566696369617279890101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574737901012c4d756c746941737365747300011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065990401284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f73742c010c75363400011063616c6c5d010168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e04008d010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f7765696768742c010c753634000c00304465706f73697441737365740c01186173736574739d0401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200012c62656e6566696369617279890101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365741001186173736574739d0401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000e003445786368616e67654173736574080110676976659d0401404d756c7469417373657446696c74657200011c726563656976657901012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c01186173736574739d0401404d756c7469417373657446696c74657200011c72657365727665890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574739d0401404d756c7469417373657446696c74657200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e0001186173736574739d0401404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f7765696768742c010c75363400120030427579457865637574696f6e08011066656573810101284d756c746941737365740001307765696768745f6c696d6974a904012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204007d04014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804007d04014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574737901012c4d756c74694173736574730001187469636b6574890101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f7765696768742c010c753634001a0048556e73756273637269626556657273696f6e001b000089040c0c78636d08763220526573706f6e7365000110104e756c6c0000001841737365747304007901012c4d756c74694173736574730001003c457865637574696f6e526573756c7404008d0401504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e000300008d0404184f7074696f6e0404540191040108104e6f6e6500000010536f6d65040091040000010000910400000408109504009504100c78636d08763218747261697473144572726f72000168204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300444d756c74694c6f636174696f6e46756c6c000400684d756c74694c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e736976650014001054726170040030010c7536340015004c556e68616e646c656458636d56657273696f6e001600485765696768744c696d69745265616368656404003001185765696768740017001c426172726965720018004c5765696768744e6f74436f6d70757461626c650019000099040c0c78636d087632284f726967696e4b696e64000110184e617469766500000040536f7665726569676e4163636f756e74000100245375706572757365720002000c58636d000300009d04100c78636d087632286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e69746504007901012c4d756c74694173736574730000001057696c640400a104013857696c644d756c7469417373657400010000a104100c78636d087632286d756c746961737365743857696c644d756c746941737365740001080c416c6c00000014416c6c4f6608010869648501011c4173736574496400010c66756ea504013c57696c6446756e676962696c69747900010000a504100c78636d087632286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000a9040c0c78636d0876322c5765696768744c696d697400010824556e6c696d697465640000001c4c696d6974656404002c010c75363400010000ad040c0c78636d0876330c58636d041043616c6c00000400b10401585665633c496e737472756374696f6e3c43616c6c3e3e0000b104000002b50400b5040c0c78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400ad01012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400ad01012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400ad01012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e7365b9040120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572cd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473ad01012c4d756c746941737365747300012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473ad01012c4d756c746941737365747300011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6c5d01014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c1010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400d10401445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473d50401404d756c7469417373657446696c74657200012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473d50401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e000e003445786368616e676541737365740c011067697665d50401404d756c7469417373657446696c74657200011077616e74ad01012c4d756c746941737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473d50401404d756c7469417373657446696c74657200011c72657365727665bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473d50401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666fd10401445175657279526573706f6e7365496e666f000118617373657473d50401404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573b50101284d756c746941737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400ad04012458636d3c43616c6c3e0015002c536574417070656e6469780400ad04012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473ad01012c4d756c74694173736574730001187469636b6574bd0101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400ad01012c4d756c7469417373657473001c002c45787065637441737365740400ad01012c4d756c7469417373657473001d00304578706563744f726967696e0400cd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666fd10401445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400d10401445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c50101204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bcd0101244e6574776f726b496400012c64657374696e6174696f6ec1010154496e746572696f724d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e002600244c6f636b41737365740801146173736574b50101284d756c74694173736574000120756e6c6f636b6572bd0101344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574b50101284d756c74694173736574000118746172676574bd0101344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574b50101284d756c746941737365740001146f776e6572bd0101344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574b50101284d756c746941737365740001186c6f636b6572bd0101344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bd0101344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696ecd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e002f0000b9040c0c78636d08763320526573706f6e7365000118104e756c6c000000184173736574730400ad01012c4d756c74694173736574730001003c457865637574696f6e526573756c740400350101504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f0400bd040198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c7404004d0101384d617962654572726f72436f646500050000bd040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401c104045300000400c90401185665633c543e0000c1040c0c78636d0876332850616c6c6574496e666f0000180114696e646578cc010c7533320001106e616d65c5040180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d65c5040180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72cc010c7533320001146d696e6f72cc010c7533320001147061746368cc010c7533320000c5040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000c904000002c10400cd0404184f7074696f6e04045401bd010108104e6f6e6500000010536f6d650400bd010000010000d1040c0c78636d087633445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ebd0101344d756c74694c6f636174696f6e00012071756572795f69642c011c517565727949640001286d61785f7765696768742801185765696768740000d504100c78636d087633286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e6974650400ad01012c4d756c74694173736574730000001057696c640400d904013857696c644d756c7469417373657400010000d904100c78636d087633286d756c746961737365743857696c644d756c746941737365740001100c416c6c00000014416c6c4f660801086964b901011c4173736574496400010c66756edd04013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400cc010c75333200020030416c6c4f66436f756e7465640c01086964b901011c4173736574496400010c66756edd04013c57696c6446756e676962696c697479000114636f756e74cc010c75333200030000dd04100c78636d087633286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000e104080c78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010c0856320400e504015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400f504015076333a3a58636d3c52756e74696d6543616c6c3e00030008563404000105015076343a3a58636d3c52756e74696d6543616c6c3e00040000e5040c0c78636d0876320c58636d042c52756e74696d6543616c6c00000400e90401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e0000e904000002ed0400ed040c0c78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404007901012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404007901012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404007901012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f69642c011c51756572794964000120726573706f6e736589040120526573706f6e73650001286d61785f7765696768742c010c753634000300345472616e7366657241737365740801186173736574737901012c4d756c746941737365747300012c62656e6566696369617279890101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574737901012c4d756c746941737365747300011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065990401284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f73742c010c75363400011063616c6cf1040168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e04008d010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f7765696768742c010c753634000c00304465706f73697441737365740c01186173736574739d0401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200012c62656e6566696369617279890101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365741001186173736574739d0401404d756c7469417373657446696c7465720001286d61785f617373657473cc010c75333200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e000e003445786368616e67654173736574080110676976659d0401404d756c7469417373657446696c74657200011c726563656976657901012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c01186173736574739d0401404d756c7469417373657446696c74657200011c72657365727665890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574739d0401404d756c7469417373657446696c74657200011064657374890101344d756c74694c6f636174696f6e00010c78636d7d04011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f69642c011c5175657279496400011064657374890101344d756c74694c6f636174696f6e0001186173736574739d0401404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f7765696768742c010c75363400120030427579457865637574696f6e08011066656573810101284d756c746941737365740001307765696768745f6c696d6974a904012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400e504014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e6469780400e504014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574737901012c4d756c74694173736574730001187469636b6574890101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f7765696768742c010c753634001a0048556e73756273637269626556657273696f6e001b0000f1040c0c78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656438011c5665633c75383e0000f5040c0c78636d0876330c58636d041043616c6c00000400f90401585665633c496e737472756374696f6e3c43616c6c3e3e0000f904000002fd0400fd040c0c78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400ad01012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400ad01012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400ad01012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e7365b9040120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572cd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473ad01012c4d756c746941737365747300012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473ad01012c4d756c746941737365747300011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6cf104014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c1010154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400d10401445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473d50401404d756c7469417373657446696c74657200012c62656e6566696369617279bd0101344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473d50401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e000e003445786368616e676541737365740c011067697665d50401404d756c7469417373657446696c74657200011077616e74ad01012c4d756c746941737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473d50401404d756c7469417373657446696c74657200011c72657365727665bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473d50401404d756c7469417373657446696c74657200011064657374bd0101344d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666fd10401445175657279526573706f6e7365496e666f000118617373657473d50401404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573b50101284d756c746941737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400f504012458636d3c43616c6c3e0015002c536574417070656e6469780400f504012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473ad01012c4d756c74694173736574730001187469636b6574bd0101344d756c74694c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400ad01012c4d756c7469417373657473001c002c45787065637441737365740400ad01012c4d756c7469417373657473001d00304578706563744f726967696e0400cd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666fd10401445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400d10401445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c50101204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bcd0101244e6574776f726b496400012c64657374696e6174696f6ec1010154496e746572696f724d756c74694c6f636174696f6e00010c78636dad04011c58636d3c28293e002600244c6f636b41737365740801146173736574b50101284d756c74694173736574000120756e6c6f636b6572bd0101344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574b50101284d756c74694173736574000118746172676574bd0101344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574b50101284d756c746941737365740001146f776e6572bd0101344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574b50101284d756c746941737365740001186c6f636b6572bd0101344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bd0101344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696ecd0401544f7074696f6e3c4d756c74694c6f636174696f6e3e002f000001050c2c73746167696e675f78636d0876340c58636d041043616c6c00000400050501585665633c496e737472756374696f6e3c43616c6c3e3e0000050500000209050009050c2c73746167696e675f78636d0876342c496e737472756374696f6e041043616c6c0001c03457697468647261774173736574040011010118417373657473000000545265736572766541737365744465706f7369746564040011010118417373657473000100585265636569766554656c65706f727465644173736574040011010118417373657473000200345175657279526573706f6e736510012071756572795f69642c011c51756572794964000120726573706f6e736531010120526573706f6e73650001286d61785f77656967687428011857656967687400011c71756572696572550101404f7074696f6e3c4c6f636174696f6e3e000300345472616e7366657241737365740801186173736574731101011841737365747300012c62656e6566696369617279bc01204c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574731101011841737365747300011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64590101284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428011857656967687400011063616c6cf104014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572cc010c7533320001406d61785f6d6573736167655f73697a65cc010c7533320001306d61785f6361706163697479cc010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74cc010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72cc010c75333200011873656e646572cc010c753332000124726563697069656e74cc010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400c00140496e746572696f724c6f636174696f6e000b002c5265706f72744572726f720400610101445175657279526573706f6e7365496e666f000c00304465706f73697441737365740801186173736574736501012c417373657446696c74657200012c62656e6566696369617279bc01204c6f636174696f6e000d004c4465706f7369745265736572766541737365740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e000e003445786368616e676541737365740c0110676976656501012c417373657446696c74657200011077616e741101011841737365747300011c6d6178696d616c200110626f6f6c000f005c496e6974696174655265736572766557697468647261770c01186173736574736501012c417373657446696c74657200011c72657365727665bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e00100040496e69746961746554656c65706f72740c01186173736574736501012c417373657446696c74657200011064657374bc01204c6f636174696f6e00010c78636d0501011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0001186173736574736501012c417373657446696c74657200120030427579457865637574696f6e080110666565731901011441737365740001307765696768745f6c696d69747101012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204000105012458636d3c43616c6c3e0015002c536574417070656e64697804000105012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473110101184173736574730001187469636b6574bc01204c6f636174696f6e001800105472617004002c010c7536340019004053756273637269626556657273696f6e08012071756572795f69642c011c5175657279496400014c6d61785f726573706f6e73655f776569676874280118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e4173736574040011010118417373657473001c002c4578706563744173736574040011010118417373657473001d00304578706563744f726967696e0400550101404f7074696f6e3c4c6f636174696f6e3e001e002c4578706563744572726f720400350101504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e7361637453746174757304004d0101384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6538011c5665633c75383e000134726573706f6e73655f696e666f610101445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578cc010c7533320001106e616d6538011c5665633c75383e00012c6d6f64756c655f6e616d6538011c5665633c75383e00012c63726174655f6d616a6f72cc010c75333200013c6d696e5f63726174655f6d696e6f72cc010c753332002200505265706f72745472616e736163745374617475730400610101445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400c801204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726bd401244e6574776f726b496400012c64657374696e6174696f6ec00140496e746572696f724c6f636174696f6e00010c78636d0501011c58636d3c28293e002600244c6f636b41737365740801146173736574190101144173736574000120756e6c6f636b6572bc01204c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574190101144173736574000118746172676574bc01204c6f636174696f6e002800384e6f7465556e6c6f636b61626c6508011461737365741901011441737365740001146f776e6572bc01204c6f636174696f6e0029003452657175657374556e6c6f636b08011461737365741901011441737365740001186c6f636b6572bc01204c6f636174696f6e002a002c536574466565734d6f64650401306a69745f7769746864726177200110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400bc01204c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69747101012c5765696768744c696d6974000130636865636b5f6f726967696e550101404f7074696f6e3c4c6f636174696f6e3e002f00000d05105073746167696e675f78636d5f6578656375746f72187472616974733861737365745f7472616e73666572305472616e73666572547970650001102054656c65706f7274000000304c6f63616c526573657276650001004844657374696e6174696f6e526573657276650002003452656d6f7465526573657276650400d901014456657273696f6e65644c6f636174696f6e000300001105080c78636d4056657273696f6e6564417373657449640001080856330400b901012c76333a3a4173736574496400030008563404001d01012c76343a3a417373657449640004000015050c4863756d756c75735f70616c6c65745f78636d1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e19050c5070616c6c65745f6d6573736167655f71756575651870616c6c65741043616c6c04045400010824726561705f706167650801386d6573736167655f6f726967696ee50101484d6573736167654f726967696e4f663c543e000128706167655f696e64657810012450616765496e6465780000043d0152656d6f76652061207061676520776869636820686173206e6f206d6f7265206d657373616765732072656d61696e696e6720746f2062652070726f636573736564206f72206973207374616c652e48657865637574655f6f7665727765696768741001386d6573736167655f6f726967696ee50101484d6573736167654f726967696e4f663c543e0001107061676510012450616765496e646578000114696e64657810011c543a3a53697a650001307765696768745f6c696d6974280118576569676874000134784578656375746520616e206f766572776569676874206d6573736167652e004d0154656d706f726172792070726f63657373696e67206572726f72732077696c6c2062652070726f706167617465642077686572656173207065726d616e656e74206572726f7273206172652074726561746564546173207375636365737320636f6e646974696f6e2e00742d20606f726967696e603a204d75737420626520605369676e6564602e35012d20606d6573736167655f6f726967696e603a20546865206f726967696e2066726f6d20776869636820746865206d65737361676520746f20626520657865637574656420617272697665642e3d012d206070616765603a20546865207061676520696e2074686520717565756520696e20776869636820746865206d65737361676520746f2062652065786563757465642069732073697474696e672e09012d2060696e646578603a2054686520696e64657820696e746f20746865207175657565206f6620746865206d65737361676520746f2062652065786563757465642e59012d20607765696768745f6c696d6974603a20546865206d6178696d756d20616d6f756e74206f662077656967687420616c6c6f77656420746f20626520636f6e73756d656420696e2074686520657865637574696f6e4420206f6620746865206d6573736167652e00f442656e63686d61726b20636f6d706c657869747920636f6e73696465726174696f6e733a204f28696e646578202b207765696768745f6c696d6974292e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e1d050c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c65741043616c6c04045400012c4c726571756573745f6d73705f7369676e5f7570100120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640000686901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207265717565737420746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4101546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00490146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742e002c506172616d65746572733a71012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204d61696e2053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292981012d206076616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865207369676e65722077696c6c2070726f766964652061732061204d61696e2053746f726167652050726f766964657220746fad01757365727320616e642077616e747320746f207265676973746572206f6e2d636861696e2e20497420636f756c642062652064617461206c696d6974732c20636f6d6d756e69636174696f6e2070726f746f636f6c7320746f2061636365737320746865207573657227733c646174612c20616e64206d6f72652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e2501322e20436865636b207468617420746865207369676e6572206973206e6f7420616c72656164792072656769737465726564206173206569746865722061204d5350206f72204253509c332e20436865636b207468617420746865206d756c7469616464726573732069732076616c69646101342e20436865636b207468617420746865206461746120746f2062652073746f7265642069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d652e9101352e2043616c63756c61746520686f77206d756368206465706f7369742077696c6c20746865207369676e6572206861766520746f20706179207573696e672074686520616d6f756e74206f6620646174612069742077616e747320746f2073746f7265f0362e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f2070617920746865206465706f7369748c372e20486f6c6420746865206465706f7369742066726f6d20746865207369676e65726901382e2055706461746520746865205369676e2055702052657175657374732073746f7261676520746f2061646420746865207369676e65722061732072657175657374696e6720746f207369676e2075702061732061204d535000d8456d69747320604d7370526571756573745369676e55705375636365737360206576656e74207768656e207375636365737366756c2e4c726571756573745f6273705f7369676e5f75700c0120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e74496400015c4501446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4901546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00490146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742e002c506172616d65746572733a79012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204261636b75702053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292900ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e2501322e20436865636b207468617420746865207369676e6572206973206e6f7420616c72656164792072656769737465726564206173206569746865722061204d5350206f72204253509c332e20436865636b207468617420746865206d756c7469616464726573732069732076616c69645d01342e20436865636b207468617420746865206461746120746f2062652073746f7265642069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d659101352e2043616c63756c61746520686f77206d756368206465706f7369742077696c6c20746865207369676e6572206861766520746f20706179207573696e672074686520616d6f756e74206f6620646174612069742077616e747320746f2073746f7265f0362e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f2070617920746865206465706f7369748c372e20486f6c6420746865206465706f7369742066726f6d20746865207369676e65726901382e2055706461746520746865205369676e2055702052657175657374732073746f7261676520746f2061646420746865207369676e65722061732072657175657374696e6720746f207369676e20757020617320612042535000d8456d6974732060427370526571756573745369676e55705375636365737360206576656e74207768656e207375636365737366756c2e3c636f6e6669726d5f7369676e5f757004014070726f76696465725f6163636f756e749c01504f7074696f6e3c543a3a4163636f756e7449643e00025cad01446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f20636f6e6669726d207468656972207369676e20757020617320612053746f726167652050726f76696465722c20656974686572204d5350206f72204253502e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e9d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722c20657863657074207768656e2070726f766964696e67206129016070726f76696465725f6163636f756e746020706172616d657465722c20696e207768696368206361736520746865206f726967696e2063616e20626520616e79206163636f756e742e002c506172616d65746572733ab9012d206070726f76696465725f6163636f756e74603a20546865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722e204966206e6f742070726f76696465642c20746865207369676e6572e477696c6c20626520636f6e7369646572656420746865206163636f756e7420746861742072657175657374656420746f207369676e2075702e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633a98312e20436865636b2074686174207468652065787472696e73696320776173207369676e65641101322e20436865636b207468617420746865206163636f756e74207265636569766564206861732072657175657374656420746f20726567697374657220617320612053508101332e20436865636b2074686174207468652063757272656e742072616e646f6d6e6573732069732073756666696369656e746c7920667265736820746f206265207573656420617320612073616c7420666f7220746861742072657175657374a4342e20436865636b207468617420746865207265717565737420686173206e6f7420657870697265643101352e20526567697374657220746865207369676e65722061732061204d5350206f722042535020776974682074686520646174612070726f766964656420696e207468652072657175657374009d01456d69747320604d73705369676e55705375636365737360206f7220604273705369676e55705375636365737360206576656e74207768656e207375636365737366756c2c20646570656e64696e67206f6e207468652074797065206f66207369676e2075702e00184e6f7465733a1d012d20546869732065787472696e73696320636f756c642062652063616c6c656420627920746865207573657220697473656c66206f722062792061207468697264207061727479b5012d20546865206465706f73697420746861742074686520757365722068617320746f2070617920746f20726567697374657220617320612053502069732068656c64207768656e20746865207573657220726571756573747320746f207265676973746572206173206120535081012d20496620746869732065787472696e736963206973207375636365737366756c2c2069742077696c6c206265206672656520666f72207468652063616c6c65722c20746f20696e63656e74697665207374617465206465626c6f6174696e673863616e63656c5f7369676e5f7570000330c101446973706174636861626c652065787472696e736963207468617420616c6c6f777320612075736572207769746820612070656e64696e67205369676e205570205265717565737420746f2063616e63656c2069742c2067657474696e6720746865206465706f736974206261636b2e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e3d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742072657175657374656420746f207369676e20757020617320612053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207369676e6572206861732072657175657374656420746f207369676e2075702061732061205350dc332e2044656c6574652074686520726571756573742066726f6d20746865205369676e2055702052657175657374732073746f726167658c342e2052657475726e20746865206465706f73697420746f20746865207369676e657200d0456d69747320605369676e55705265717565737443616e63656c656460206576656e74207768656e207375636365737366756c2e306d73705f7369676e5f6f66660004384101446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e206f66662061732061204d61696e2053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e206f66662061732061204d61696e2053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e657220697320726567697374657265642061732061204d53504d01332e20436865636b207468617420746865204d535020686173206e6f2073746f726167652061737369676e656420746f20697420286e6f206275636b657473206f722064617461207573656420627920697429e4342e2055706461746520746865204d5350732073746f726167652c2072656d6f76696e6720746865207369676e657220617320616e204d53508c352e2052657475726e20746865206465706f73697420746f20746865207369676e65724101362e2044656372656d656e74207468652073746f72616765207468617420686f6c647320746f74616c20616d6f756e74206f66204d5350732063757272656e746c7920696e207468652073797374656d00c0456d69747320604d73705369676e4f66665375636365737360206576656e74207768656e207375636365737366756c2e306273705f7369676e5f6f666600053c4901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f207369676e206f66662061732061204261636b75702053746f726167652050726f76696465722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e4d01546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f207369676e206f66662061732061204261636b75702053746f726167652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e65722069732072656769737465726564206173206120425350cc332e20436865636b2074686174207468652042535020686173206e6f2073746f726167652061737369676e656420746f206974e4342e205570646174652074686520425350732073746f726167652c2072656d6f76696e6720746865207369676e657220617320616e204253503501352e205570646174652074686520746f74616c206361706163697479206f6620616c6c20425350732c2072656d6f76696e6720746865206361706163697479206f6620746865207369676e65728c362e2052657475726e20746865206465706f73697420746f20746865207369676e65724101372e2044656372656d656e74207468652073746f72616765207468617420686f6c647320746f74616c20616d6f756e74206f6620425350732063757272656e746c7920696e207468652073797374656d00c0456d69747320604273705369676e4f66665375636365737360206576656e74207768656e207375636365737366756c2e3c6368616e67655f63617061636974790401306e65775f636170616369747930014853746f7261676544617461556e69743c543e0006603901446973706174636861626c652065787472696e736963207468617420616c6c6f777320757365727320746f206368616e676520746865697220616d6f756e74206f662073746f726564206461746100c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f206368616e6765206974732063617061636974792e002c506172616d65746572733a8d012d20606e65775f6361706163697479603a20546865206e657720746f74616c20616d6f756e74206f6620646174612074686174207468652053746f726167652050726f76696465722077616e747320746f2062652061626c6520746f2073746f72652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722eb8322e20436865636b207468617420746865207369676e6572206973207265676973746572656420617320612053505101332e20436865636b207468617420656e6f7567682074696d6520686173207061737365642073696e636520746865206c6173742074696d6520746865205350206368616e676564206974732063617061636974794901342e20436865636b207468617420746865206e65772063617061636974792069732067726561746572207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d651d01352e20436865636b207468617420746865206e65772063617061636974792069732067726561746572207468616e20746865206461746120757365642062792074686973205350e4362e2043616c63756c61746520746865206e6577206465706f736974206e656564656420666f722074686973206e65772063617061636974795501372e20436865636b20746f2073656520696620746865206e6577206465706f736974206e65656465642069732067726561746572206f72206c657373207468616e207468652063757272656e74206465706f736974ec09612e20496620746865206e6577206465706f7369742069732067726561746572207468616e207468652063757272656e74206465706f7369743a15010909692e20436865636b207468617420746865207369676e65722068617320656e6f7567682066756e647320746f207061792074686973206578747261206465706f736974b0090969692e20486f6c6420746865206578747261206465706f7369742066726f6d20746865207369676e6572850109622e20496620746865206e6577206465706f736974206973206c657373207468616e207468652063757272656e74206465706f7369742c2072657475726e207468652068656c6420646966666572656e636520746f20746865207369676e6572c8372e2055706461746520746865205350732073746f7261676520746f206368616e67652074686520746f74616c20646174617501382e20496620757365722069732061204253502c207570646174652074686520746f74616c206361706163697479206f6620746865206e6574776f726b202873756d206f6620616c6c2063617061636974696573206f6620425350732900b8456d697473206043617061636974794368616e67656460206576656e74207768656e207375636365737366756c2e386164645f76616c75655f70726f700401386e65775f76616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e0007409d01446973706174636861626c652065787472696e736963206f6e6c792063616c6c61626c6520627920616e204d5350207468617420616c6c6f777320697420746f2061646420612076616c75652070726f706f736974696f6e20746f20697473207365727669636500c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e1501546865206f726967696e206d75737420626520746865206163636f756e7420746861742077616e747320746f2061646420612076616c75652070726f706f736974696f6e2e002c506172616d65746572733a4d012d20606e65775f76616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865204d53502077616e747320746f2061646420746f2069747320736572766963652e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ebc322e20436865636b207468617420746865207369676e657220697320726567697374657265642061732061204d53503901332e20436865636b207468617420746865204d535020686173206e6f74207265616368656420746865206d6178696d756d20616d6f756e74206f662076616c75652070726f706f736974696f6e734501342e20436865636b2074686174207468652076616c75652070726f706f736974696f6e2069732076616c6964202873697a6520616e6420616e79206f746865722072656c6576616e7420636865636b73293501352e2055706461746520746865204d5350732073746f7261676520746f20616464207468652076616c75652070726f706f736974696f6e20287769746820697473206964656e7469666965722900b4456d697473206056616c756550726f70416464656460206576656e74207768656e207375636365737366756c2e44666f7263655f6d73705f7369676e5f757018010c77686f000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e000120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640008588d01446973706174636861626c652065787472696e736963207468617420616c6c6f777320746f20666f72636566756c6c7920616e64206175746f6d61746963616c6c792073696e672075702061204d61696e2053746f726167652050726f76696465722e00bc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f742e5101546865206077686f6020706172616d6574657220697320746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722e00c10146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722e002c506172616d65746572733a19012d206077686f603a20546865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204d61696e2053746f726167652050726f76696465722ed1012d20606d73705f6964603a20546865204d61696e2053746f726167652050726f7669646572204944207468617420746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722069732072657175657374696e6720746f207369676e2075702061732e71012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204d61696e2053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292981012d206076616c75655f70726f70603a205468652076616c75652070726f706f736974696f6e207468617420746865207369676e65722077696c6c2070726f766964652061732061204d61696e2053746f726167652050726f766964657220746fad01757365727320616e642077616e747320746f207265676973746572206f6e2d636861696e2e20497420636f756c642062652064617461206c696d6974732c20636f6d6d756e69636174696f6e2070726f746f636f6c7320746f2061636365737320746865207573657227733c646174612c20616e64206d6f72652e00a4546869732065787472696e7369632077696c6c20706572666f726d20746865207374657073206f663a0d01312e205b726571756573745f6d73705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6d73705f7369676e5f757029ec322e205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f757029003901456d69747320604d7370526571756573745369676e5570537563636573736020616e6420604d73705369676e55705375636365737360206576656e7473207768656e207375636365737366756c2e44666f7263655f6273705f7369676e5f757018010c77686f000130543a3a4163636f756e7449640001186273705f69643401684261636b757053746f7261676550726f766964657249643c543e000120636170616369747930014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640001187765696768741102017c4f7074696f6e3c52657075746174696f6e576569676874547970653c543e3e00094c9501446973706174636861626c652065787472696e736963207468617420616c6c6f777320746f20666f72636566756c6c7920616e64206175746f6d61746963616c6c792073696e672075702061204261636b75702053746f726167652050726f76696465722e00bc546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f742e5901546865206077686f6020706172616d6574657220697320746865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722e00c10146756e64732070726f706f7274696f6e616c20746f207468652063617061636974792072657175657374656420617265207265736572766564202868656c64292066726f6d20746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722e002c506172616d65746572733a21012d206077686f603a20546865206163636f756e7420746861742077616e747320746f207369676e2075702061732061204261636b75702053746f726167652050726f76696465722ed9012d20606273705f6964603a20546865204261636b75702053746f726167652050726f7669646572204944207468617420746865206163636f756e742070617373656420617320746865206077686f6020706172616d657465722069732072657175657374696e6720746f207369676e2075702061732e79012d20606361706163697479603a2054686520746f74616c20616d6f756e74206f662064617461207468617420746865204261636b75702053746f726167652050726f76696465722077696c6c2062652061626c6520746f2073746f72652e91012d20606d756c7469616464726573736573603a2054686520766563746f72206f66206d756c7469616464726573736573207468617420746865207369676e65722077616e747320746f20726567697374657220286163636f7264696e6720746f20746865f05b4d756c74696164647220737065635d2868747470733a2f2f6769746875622e636f6d2f6d756c7469666f726d6174732f6d756c746961646472292900a4546869732065787472696e7369632077696c6c20706572666f726d20746865207374657073206f663a0d01312e205b726571756573745f6273705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6273705f7369676e5f757029ec322e205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f757029003901456d6974732060427370526571756573745369676e5570537563636573736020616e6420604273705369676e55705375636365737360206576656e7473207768656e207375636365737366756c2e14736c61736804012c70726f76696465725f69643401244861736849643c543e000a10fc446973706174636861626c652065787472696e73696320746f20736c6173682061205f736c61736861626c655f2053746f726167652050726f76696465722e00b501412053746f726167652050726f7669646572206973205f736c61736861626c655f2069666620697420686173206661696c656420746f20726573706f6e6420746f206368616c6c656e67657320666f722070726f766964696e672070726f6f6673206f662073746f726167652e4d02496e2074686520636f6e74657874206f66207468652053746f726167654875622070726f746f636f6c2c207468652070726f6f66732d6465616c65722070616c6c6574206d61726b7320612053746f726167652050726f7669646572206173205f736c61736861626c655f207768656e206974206661696c7320746f20726573706f6e6420746f206368616c6c656e6765732e04c4446973706174636861626c6573202865787472696e7369637329206578706f73656420627920746869732070616c6c657421050c4870616c6c65745f66696c655f73797374656d1870616c6c65741043616c6c040454000144346372656174655f6275636b65740c01186d73705f696434014050726f76696465724964466f723c543e0001106e616d65f90101404275636b65744e616d65466f723c543e00011c70726976617465200110626f6f6c0000004c726571756573745f6d6f76655f6275636b65740801246275636b65745f69643401384275636b65744964466f723c543e0001286e65775f6d73705f696434014050726f76696465724964466f723c543e0001007c6d73705f726573706f6e645f6d6f76655f6275636b65745f726571756573740801246275636b65745f69643401384275636b65744964466f723c543e000120726573706f6e7365250501644275636b65744d6f766552657175657374526573706f6e7365000200547570646174655f6275636b65745f707269766163790801246275636b65745f69643401384275636b65744964466f723c543e00011c70726976617465200110626f6f6c000300ac6372656174655f616e645f6173736f63696174655f636f6c6c656374696f6e5f776974685f6275636b65740401246275636b65745f69643401384275636b65744964466f723c543e000404c043726561746520616e64206173736f6369617465206120636f6c6c656374696f6e20776974682061206275636b65742e5469737375655f73746f726167655f726571756573741801246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e0001186d73705f696434014050726f76696465724964466f723c543e000120706565725f696473f5010128506565724964733c543e0005049849737375652061206e65772073746f72616765207265717565737420666f7220612066696c65587265766f6b655f73746f726167655f7265717565737404012066696c655f6b65793401344d65726b6c65486173683c543e000604585265766f6b652073746f726167652072657175657374ac6273705f6164645f646174615f7365727665725f666f725f6d6f76655f6275636b65745f726571756573740401246275636b65745f69643401384275636b65744964466f723c543e000704690141646420796f757273656c66206173206120646174612073657276657220666f722070726f766964696e67207468652066696c6573206f6620746865206275636b65742072657175657374656420746f206265206d6f7665642eb46d73705f726573706f6e645f73746f726167655f72657175657374735f6d756c7469706c655f6275636b65747304016066696c655f6b65795f726573706f6e7365735f696e7075742905016046696c654b6579526573706f6e736573496e7075743c543e0008244901557365642062792061204d535020746f20616363657074206f72206465636c696e652073746f7261676520726571756573747320696e20626174636865732c2067726f75706564206279206275636b65742e00d9015468697320666f6c6c6f7773206120626573742d6566666f72742073747261746567792c206d65616e696e67207468617420616c6c2066696c65206b6579732077696c6c2062652070726f63657373656420616e64206465636c6172656420746f2068617665207375636365737366756c6c79206265590161636365707465642c2072656a6563746564206f722068617665206661696c656420746f2062652070726f63657373656420696e2074686520726573756c7473206f6620746865206576656e7420656d69747465642e00f501546865204d53502068617320746f2070726f7669646520612066696c652070726f6f6620666f7220616c6c207468652066696c65206b657973207468617420617265206265696e6720616363657074656420616e642061206e6f6e2d696e636c7573696f6e2070726f6f6620666f72207468652066696c65206b6579739501696e20746865206275636b65742773204d65726b6c6520506174726963696120466f726573742e205468652066696c652070726f6f667320666f72207468652066696c65206b657973206973206e656365737361727920746f2076657269667920746861748501746865204d53502061637475616c6c7920686173207468652066696c65732c207768696c6520746865206e6f6e2d696e636c7573696f6e2070726f6f66206973206e656365737361727920746f20766572696679207468617420746865204d5350647761736e27742073746f72696e67206974206265666f72652e346273705f766f6c756e7465657204012066696c655f6b65793401344d65726b6c65486173683c543e000918b85573656420627920612042535020746f20766f6c756e7465657220666f722073746f72696e6720612066696c652e008d01546865207472616e73616374696f6e2077696c6c206661696c2069662074686520584f52206265747765656e207468652066696c6520494420616e642074686520425350204944206973206e6f742062656c6f7720746865207468726573686f6c642c3d01736f206120425350206973207374726f6e676c79206164766973656420746f20636865636b206265666f726568616e642e20416e6f7468657220726561736f6e20666f72206661696c7572652069735101696620746865206d6178696d756d206e756d626572206f66204253507320686173206265656e20726561636865642e2041207375636365737366756c2061737369676e6d656e7420617320425350206d65616e73e87468617420736f6d65206f662074686520636f6c6c61746572616c20746f6b656e73206f662074686174204d5350206172652066726f7a656e2e4c6273705f636f6e6669726d5f73746f72696e670801686e6f6e5f696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e00015066696c655f6b6579735f616e645f70726f6f66733d05013501426f756e6465645665633c284d65726b6c65486173683c543e2c204b657950726f6f663c543e292c20543a3a0a4d61784261746368436f6e6669726d53746f7261676552657175657374732c3e000a0411015573656420627920612042535020746f20636f6e6669726d2074686579206172652073746f72696e672064617461206f6620612073746f7261676520726571756573742e606273705f726571756573745f73746f705f73746f72696e6720012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001146f776e6572000130543a3a4163636f756e74496400012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e00012463616e5f7365727665200110626f6f6c000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000b20d0457865637574656420627920612042535020746f207265717565737420746f2073746f702073746f72696e6720612066696c652e006d01496e20746865206576656e74207768656e20612073746f726167652072657175657374206e6f206c6f6e6765722065786973747320666f722074686520646174612074686520425350206e6f206c6f6e6765722073746f7265732c7101697420697320726571756972656420746861742074686520425350207374696c6c206861732061636365737320746f20746865206d65746164617461206f662074686520696e697469616c2073746f7261676520726571756573742e89014966207468657920646f206e6f742c20746865792077696c6c206174206c65617374206e6565642074686174206d6574616461746120746f207265636f6e737472756374207468652046696c6520494420616e642066726f6d2077686572657665723d017468652042535020676574732074686174206461746120697320757020746f2069742e204f6e65206578616d706c6520636f756c642062652066726f6d207468652061737369676e6564204d53502e750154686973206d65746164617461206973206e65636573736172792073696e6365206974206973206e656564656420746f207265636f6e73747275637420746865206c656166206e6f6465206b657920696e207468652073746f726167656470726f76696465722773204d65726b6c6520466f726573742e606273705f636f6e6669726d5f73746f705f73746f72696e6708012066696c655f6b65793401344d65726b6c65486173683c543e000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000c14d0457865637574656420627920612042535020746f20636f6e6669726d20746f2073746f702073746f72696e6720612066696c652e00bd0149742068617320746f20686176652070726576696f75736c79206f70656e656420612070656e64696e672073746f702073746f72696e672072657175657374207573696e672074686520606273705f726571756573745f73746f705f73746f72696e67602065787472696e7369632eb901546865206d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e20746865207265717565737420616e642074686520636f6e6669726d6174696f6e20697320646566696e6564206279207468652072756e74696d652c20737563682074686174207468658d014253502063616e277420696d6d6564696174656c792073746f702073746f72696e6720612066696c65206974206861732070726576696f75736c79206c6f7374207768656e20726563656976696e672061206368616c6c656e676520666f722069742e7c73746f705f73746f72696e675f666f725f696e736f6c76656e745f757365721c012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e0001146f776e6572000130543a3a4163636f756e74496400012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e000158696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000d18fc4578656375746564206279206120535020746f2073746f702073746f72696e6720612066696c652066726f6d20616e20696e736f6c76656e7420757365722e00b101546869732069732075736564207768656e2061207573657220686173206265636f6d6520696e736f6c76656e7420616e6420746865205350206e6565647320746f2073746f702073746f72696e67207468652066696c6573206f66207468617420757365722c2073696e6365a0697420776f6e27742062652067657474696e67207061696420666f7220697420616e796d6f72652e3d025468652076616c69646174696f6e73206172652073696d696c617220746f20746865206f6e657320696e2074686520606273705f726571756573745f73746f705f73746f72696e676020616e6420606273705f636f6e6669726d5f73746f705f73746f72696e67602065787472696e736963732c206275742074686520535020646f65736e2774206e65656420746f7d017761697420666f722061206d696e696d756d20616d6f756e74206f6620626c6f636b7320746f20636f6e6669726d20746f2073746f702073746f72696e67207468652066696c65206e6f722069742068617320746f2062652061204253502e2c64656c6574655f66696c651801246275636b65745f69643401384275636b65744964466f723c543e00012066696c655f6b65793401344d65726b6c65486173683c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00011073697a6530013853746f72616765446174613c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e0001706d617962655f696e636c7573696f6e5f666f726573745f70726f6f66510501584f7074696f6e3c466f7265737450726f6f663c543e3e000e00a870656e64696e675f66696c655f64656c6574696f6e5f726571756573745f7375626d69745f70726f6f6610011075736572000130543a3a4163636f756e74496400012066696c655f6b65793401344d65726b6c65486173683c543e0001246275636b65745f69643401384275636b65744964466f723c543e000130666f726573745f70726f6f6665020138466f7265737450726f6f663c543e000f00547365745f676c6f62616c5f706172616d65746572730801487265706c69636174696f6e5f746172676574110201804f7074696f6e3c543a3a5265706c69636174696f6e546172676574547970653e00017c7469636b5f72616e67655f746f5f6d6178696d756d5f7468726573686f6c64110201544f7074696f6e3c5469636b4e756d6265723c543e3e001000040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e25050c4870616c6c65745f66696c655f73797374656d147479706573644275636b65744d6f766552657175657374526573706f6e73650001082041636365707465640000002052656a65637465640001000029050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454012d050453000004004d0501185665633c543e00002d05000004083431050031050c4870616c6c65745f66696c655f73797374656d147479706573644d737053746f7261676552657175657374526573706f6e73650404540000080118616363657074350501ac4f7074696f6e3c416363657074656453746f7261676552657175657374506172616d65746572733c543e3e00011872656a656374490501a5014f7074696f6e3c426f756e6465645665633c284d65726b6c65486173683c543e2c2052656a656374656453746f7261676552657175657374526561736f6e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e2c3e2c3e0000350504184f7074696f6e0404540139050108104e6f6e6500000010536f6d6504003905000001000039050c4870616c6c65745f66696c655f73797374656d14747970657380416363657074656453746f7261676552657175657374506172616d6574657273040454000008015066696c655f6b6579735f616e645f70726f6f66733d05013901426f756e6465645665633c284d65726b6c65486173683c543e2c204b657950726f6f663c543e292c0a4d617842617463684d7370526573706f6e6453746f7261676552657175657374733c543e3e0001686e6f6e5f696e636c7573696f6e5f666f726573745f70726f6f6665020138466f7265737450726f6f663c543e00003d050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014105045300000400450501185665633c543e0000410500000408347502004505000002410500490504184f7074696f6e0404540135020108104e6f6e6500000010536f6d650400350200000100004d050000022d0500510504184f7074696f6e0404540165020108104e6f6e6500000010536f6d6504006502000001000055050c5070616c6c65745f70726f6f66735f6465616c65721870616c6c65741043616c6c040454000110246368616c6c656e676504010c6b65793401244b6579466f723c543e00001868496e74726f647563652061206e6577206368616c6c656e67652e003101546869732066756e6374696f6e20616c6c6f777320616e796f6e6520746f206164642061206e6577206368616c6c656e676520746f2074686520604368616c6c656e6765735175657565602ed8546865206368616c6c656e67652077696c6c206265206469737061746368656420696e2074686520636f6d696e6720626c6f636b732efc5573657273206172652063686172676564206120736d616c6c2066656520666f72207375626d697474696e672061206368616c6c656e67652c20776869636854676f657320746f207468652054726561737572792e307375626d69745f70726f6f6608011470726f6f666102012050726f6f663c543e00012070726f7669646572710301604f7074696f6e3c50726f76696465724964466f723c543e3e00015484466f7220612050726f766964657220746f207375626d697420612070726f6f662e00e0436865636b732074686174206070726f766964657260206973206120726567697374657265642050726f76696465722e204966206e6f6e65090169732070726f76696465642c207468652070726f6f66207375626d697474657220697320636f6e7369646572656420746f206265207468652050726f76696465722ef852656c696573206f6e20612050726f7669646572732070616c6c657420746f206765742074686520726f6f7420666f72207468652050726f76696465722e390156616c6964617465732074686174207468652070726f6f6620636f72726573706f6e647320746f2061206368616c6c656e6765207468617420776173206d61646520696e2074686520706173742c3901627920636865636b696e672074686520605469636b546f4368616c6c656e67657353656564602053746f726167654d61702e20546865206368616c6c656e6765207469636b207468617420746865410150726f76696465722073686f756c642068617665207375626d697474656420612070726f6f662069732063616c63756c61746564206261736564206f6e20746865206c617374207469636b207468657969017375626d697474656420612070726f6f6620666f7220285b604c6173745469636b50726f76696465725375626d69747465644150726f6f66466f72605d292c20616e64207468652070726f76696e6720706572696f6420666f72c8746861742050726f76696465722c20776869636820697320612066756e6374696f6e206f66207468656972207374616b652e3901546869732065787472696e73696320616c736f20636865636b732074686174207468657265206861736e2774206265656e206120636865636b706f696e74206368616c6c656e676520726f756e642101696e206265747765656e20746865206c6173742074696d65207468652050726f7669646572207375626d697474656420612070726f6f6620666f7220616e6420746865207469636b2901666f72207768696368207468652070726f6f66206973206265696e67207375626d69747465642e20496620746865726520686173206265656e2c207468652050726f7669646572206973507375626a65637420746f20736c617368696e672e002449662076616c69643a55012d2050757368657320666f7277617264207468652050726f766964657220696e20746865205b605469636b546f50726f766964657273446561646c696e6573605d2053746f726167654d61702061206e756d626572d06f66207469636b7320636f72726573706f6e64696e6720746f20746865207374616b65206f66207468652050726f76696465722e3d012d205265676973746572732074686973207469636b20617320746865206c617374207469636b20696e207768696368207468652050726f7669646572207375626d697474656420612070726f6f662e001501457865637574696f6e206f6620746869732065787472696e7369632073686f756c6420626520726566756e646564206966207468652070726f6f662069732076616c69642e80666f7263655f696e697469616c6973655f6368616c6c656e67655f6379636c6504012070726f766964657234014050726f76696465724964466f723c543e000218a0496e697469616c69736520612050726f76696465722773206368616c6c656e6765206379636c652e00584f6e6c792063616c6c61626c65206279207375646f2e005d015365747320746865206c617374207469636b207468652050726f7669646572207375626d697474656420612070726f6f6620666f7220746f207468652063757272656e74207469636b2c20616e642073657473207468657101646561646c696e6520666f72207375626d697474696e6720612070726f6f6620746f207468652063757272656e74207469636b202b207468652050726f7669646572277320706572696f64202b2074686520746f6c6572616e63652e287365745f706175736564040118706175736564200110626f6f6c00030ce053657420746865205b604368616c6c656e6765735469636b6572506175736564605d20746f20607472756560206f72206066616c7365602e00584f6e6c792063616c6c61626c65206279207375646f2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e59050c4470616c6c65745f72616e646f6d6e6573731870616c6c65741043616c6c0404540001044c7365745f626162655f72616e646f6d6e65737300000ced015468697320696e686572656e742074686174206d75737420626520696e636c7564656420284469737061746368436c6173733a3a4d616e6461746f727929206174206561636820626c6f636b20736176657320746865206c61746573742072616e646f6d6e65737320617661696c61626c652066726f6d20746865810172656c617920636861696e20696e746f2061207661726961626c6520746861742063616e207468656e20626520757365642061732061207365656420666f7220636f6d6d69746d656e747320746861742068617070656e656420647572696e67787468652070726576696f75732072656c617920636861696e2065706f6368040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e5d050c5870616c6c65745f7061796d656e745f73747265616d731870616c6c65741043616c6c040454000124806372656174655f66697865645f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e7449640001107261746518013042616c616e63654f663c543e0000489501446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2061646420612066697865642d72617465207061796d656e742073747265616d2066726f6d2061205573657220746f20612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722ec42d206072617465603a2054686520696e697469616c2072617465206f6620746865207061796d656e742073747265616d2e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696edc322e20436865636b207468617420746865207061796d656e742073747265616d20646f6573206e6f7420616c7265616479206578697374e8332e20436865636b20746861742074686520557365722068617320656e6f7567682066756e647320746f2070617920746865206465706f73697484342e20486f6c6420746865206465706f7369742066726f6d2074686520557365720d01352e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2061646420746865206e6577207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d4372656174656460206576656e74207768656e207375636365737366756c2e807570646174655f66697865645f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e7449640001206e65775f7261746518013042616c616e63654f663c543e000140d901446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2075706461746520616e206578697374696e672066697865642d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722ec42d20606e65775f72617465603a20546865206e65772072617465206f6620746865207061796d656e742073747265616d2e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2075706461746520746865207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d5570646174656460206576656e74207768656e207375636365737366756c2e8064656c6574655f66697865645f726174655f7061796d656e745f73747265616d08012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400023cd901446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2064656c65746520616e206578697374696e672066697865642d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2072656d6f766520746865207061796d656e742073747265616d00f0456d69747320604669786564526174655061796d656e7453747265616d44656c6574656460206576656e74207768656e207375636365737366756c2e886372656174655f64796e616d69635f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e0003489d01446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2061646420612064796e616d69632d72617465207061796d656e742073747265616d2066726f6d2061205573657220746f20612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e05012d2060616d6f756e745f70726f7669646564603a2054686520696e697469616c20616d6f756e742070726f7669646564206279207468652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696edc322e20436865636b207468617420746865207061796d656e742073747265616d20646f6573206e6f7420616c7265616479206578697374e8332e20436865636b20746861742074686520557365722068617320656e6f7567682066756e647320746f2070617920746865206465706f73697484342e20486f6c6420746865206465706f7369742066726f6d2074686520557365720d01352e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2061646420746865206e6577207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d4372656174656460206576656e74207768656e207375636365737366756c2e887570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d0c012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400014c6e65775f616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e000440e101446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2075706461746520616e206578697374696e672064796e616d69632d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e05012d20606e65775f616d6f756e745f70726f7669646564603a20546865206e657720616d6f756e742070726f7669646564206279207468652050726f76696465722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2075706461746520746865207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d5570646174656460206576656e74207768656e207375636365737366756c2e8864656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d08012c70726f76696465725f696434014050726f76696465724964466f723c543e000130757365725f6163636f756e74000130543a3a4163636f756e74496400053ce101446973706174636861626c652065787472696e736963207468617420616c6c6f777320726f6f7420746f2064656c65746520616e206578697374696e672064796e616d69632d72617465207061796d656e742073747265616d206265747765656e2061205573657220616e6420612050726f76696465722e00c101546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520526f6f7420285061796d656e742073747265616d732073686f756c64206f6e6c792062652061646465642062792074726169747320696e206f746865722070616c6c6574732c98746869732065787472696e73696320697320666f72206d616e75616c2074657374696e67292e002c506172616d65746572733a01012d206070726f76696465725f6964603a205468652050726f7669646572204944207468617420746865207061796d656e742073747265616d20697320666f722e15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633aec312e20436865636b2074686174207468652065787472696e736963207761732065786563757465642062792074686520726f6f74206f726967696e9c322e20436865636b207468617420746865207061796d656e742073747265616d206578697374730901332e2055706461746520746865205061796d656e742053747265616d732073746f7261676520746f2072656d6f766520746865207061796d656e742073747265616d00f8456d697473206044796e616d6963526174655061796d656e7453747265616d44656c6574656460206576656e74207768656e207375636365737366756c2e586368617267655f7061796d656e745f73747265616d73040130757365725f6163636f756e74000130543a3a4163636f756e7449640006705101446973706174636861626c652065787472696e736963207468617420616c6c6f77732050726f76696465727320746f206368617267652061207061796d656e742073747265616d2066726f6d206120557365722e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e6d01546865206f726967696e206d757374206265207468652050726f7669646572207468617420686173206174206c65617374206f6e652074797065206f66207061796d656e742073747265616d20776974682074686520557365722e002c506172616d65746572733a15012d2060757365725f6163636f756e74603a205468652055736572204163636f756e74204944207468617420746865207061796d656e742073747265616d20697320666f722e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722e4101322e20436865636b20746861742061207061796d656e742073747265616d206265747765656e20746865207369676e6572202850726f76696465722920616e6420746865205573657220657869737473ac332e20496620746865726520697320612066697865642d72617465207061796d656e742073747265616d3aa0202020312e20476574207468652072617465206f6620746865207061796d656e742073747265616dbd01202020322e204765742074686520646966666572656e6365206265747765656e20746865206c6173742063686172676564207469636b206e756d62657220616e6420746865206c6173742063686172676561626c65207469636b206e756d626572206f66207468652073747265616df8202020332e2043616c63756c6174652074686520616d6f756e7420746f2063686172676520646f696e67206072617465202a20646966666572656e636560e501202020342e204368617267652074686520757365722028696620746865207573657220646f6573206e6f74206861766520656e6f7567682066756e64732c206974206765747320666c616767656420616e642061206055736572576974686f757446756e647360206576656e7420697320656d697474656429fc202020352e2055706461746520746865206c6173742063686172676564207469636b206e756d626572206f6620746865207061796d656e742073747265616db4342e20496620746865726520697320612064796e616d69632d72617465207061796d656e742073747265616d3ab4202020312e204765742074686520616d6f756e742070726f7669646564206279207468652050726f7669646572f501202020322e204765742074686520646966666572656e6365206265747765656e20707269636520696e646578207768656e207468652073747265616d20776173206c617374206368617267656420616e642074686520707269636520696e64657820617420746865206c6173742063686172676561626c65207469636b2501202020332e2043616c63756c6174652074686520616d6f756e7420746f2063686172676520646f696e672060616d6f756e745f70726f7669646564202a20646966666572656e636560e501202020342e204368617267652074686520757365722028696620746865207573657220646f6573206e6f74206861766520656e6f7567682066756e64732c206974206765747320666c616767656420616e642061206055736572576974686f757446756e647360206576656e7420697320656d6974746564294d01202020352e205570646174652074686520707269636520696e646578207768656e207468652073747265616d20776173206c6173742063686172676564206f6620746865207061796d656e742073747265616d00d4456d697473206120605061796d656e7453747265616d4368617267656460206576656e74207768656e207375636365737366756c2e0001024e6f7465733a20612050726f766964657220636f756c64206861766520626f746820612066697865642d7261746520616e6420612064796e616d69632d72617465207061796d656e742073747265616d2077697468206120557365722e204966207468617427732074686520636173652c20746869732065787472696e736963b90177696c6c2074727920746f2063686172676520626f746820616e642074686520616d6f756e7420636861726765642077696c6c206265207468652073756d206f662074686520616d6f756e7473206368617267656420666f722065616368207061796d656e742073747265616d2e507061795f6f75747374616e64696e675f64656274000748f501446973706174636861626c652065787472696e736963207468617420616c6c6f77732061207573657220666c616767656420617320776974686f75742066756e647320746f2070617920616c6c2072656d61696e696e67207061796d656e742073747265616d7320746f2062652061626c6520746f207265636f76657234697473206465706f736974732e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0d01546865206f726967696e206d757374206265207468652055736572207468617420686173206265656e20666c616767656420617320776974686f75742066756e64732e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207573657220686173206265656e20666c616767656420617320776974686f75742066756e64732e4101332e2052656c6561736520746865207573657227732066756e6473207468617420776572652068656c642061732061206465706f73697420666f722065616368207061796d656e742073747265616d2e7901342e2047657420616c6c207061796d656e742073747265616d73206f6620746865207573657220616e6420636861726765207468656d2c20706179696e67207468652050726f76696465727320666f72207468652073657276696365732ea8352e2044656c65746520616c6c207061796d656e742073747265616d73206f662074686520757365722e00b8456d697473206120275573657250616964446562747327206576656e74207768656e207375636365737366756c2e0001024e6f7465733a20746869732065787472696e736963206974657261746573206f76657220616c6c207061796d656e742073747265616d73206f6620746865207573657220616e642063686172676573207468656d2c20736f2069742063616e20626520657870656e7369766520696e207465726d73206f66207765696768742efd015468652066656520746f20657865637574652069742073686f756c64206265206869676820656e6f75676820746f20636f6d70656e7361746520666f722074686520776569676874206f66207468652065787472696e7369632c20776974686f7574206265696e6720746f6f206869676820746861742074686520757365724d0166696e6473206d6f726520636f6e76656e69656e7420746f207761697420666f722050726f76696465727320746f2067657420697473206465706f73697473206f6e65206279206f6e6520696e73746561642e50636c6561725f696e736f6c76656e745f666c6167000858dd01446973706174636861626c652065787472696e736963207468617420616c6c6f77732061207573657220666c616767656420617320776974686f75742066756e6473206c6f6e672061676f20656e6f75676820746f20636c656172207468697320666c61672066726f6d20697473206163636f756e742c0102616c6c6f77696e6720697420746f20626567696e20636f6e7472616374696e6720616e6420706179696e6720666f7220736572766963657320616761696e2e204966207468657265277320616e79206f75747374616e64696e6720646562742c2069742077696c6c206265206368617267656420616e6420636c65617265642e00c4546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205369676e65642e0d01546865206f726967696e206d757374206265207468652055736572207468617420686173206265656e20666c616767656420617320776974686f75742066756e64732e00ec546869732065787472696e7369632077696c6c20706572666f726d2074686520666f6c6c6f77696e6720636865636b7320616e64206c6f6769633ae8312e20436865636b2074686174207468652065787472696e73696320776173207369676e656420616e642067657420746865207369676e65722ee4322e20436865636b207468617420746865207573657220686173206265656e20666c616767656420617320776974686f75742066756e64732e6501332e20436865636b20746861742074686520636f6f6c646f776e20706572696f6420686173207061737365642073696e63652074686520757365722077617320666c616767656420617320776974686f75742066756e64732e2101342e20436865636b206966207468657265277320616e79206f75747374616e64696e67206465627420616e64206368617267652069742e205468697320697320646f6e652062793a35012020612e2052656c656173696e6720616e792072656d61696e696e672066756e64732068656c642061732061206465706f73697420666f722065616368207061796d656e742073747265616d2e99012020622e2047657474696e6720616c6c207061796d656e742073747265616d73206f6620746865207573657220616e64206368617267696e67207468656d2c20706179696e67207468652050726f76696465727320666f72207468652073657276696365732eb02020632e2052657475726e696e6720746865205573657220616e792072656d61696e696e672066756e64732eb82020642e2044656c6574696e6720616c6c207061796d656e742073747265616d73206f662074686520757365722e90352e20556e666c616720746865207573657220617320776974686f75742066756e64732e00b0456d6974732061202755736572536f6c76656e7427206576656e74207768656e207375636365737366756c2e0029024e6f7465733a20746869732065787472696e736963206974657261746573206f76657220616c6c2072656d61696e696e67207061796d656e742073747265616d73206f6620746865207573657220616e642063686172676573207468656d2c20736f2069742063616e20626520657870656e7369766520696e207465726d73206f66207765696768742efd015468652066656520746f20657865637574652069742073686f756c64206265206869676820656e6f75676820746f20636f6d70656e7361746520666f722074686520776569676874206f66207468652065787472696e7369632c20776974686f7574206265696e6720746f6f206869676820746861742074686520757365724d0166696e6473206d6f726520636f6e76656e69656e7420746f207761697420666f722050726f76696465727320746f2067657420697473206465706f73697473206f6e65206279206f6e6520696e73746561642e04c4446973706174636861626c6573202865787472696e7369637329206578706f73656420627920746869732070616c6c657461050c4870616c6c65745f6275636b65745f6e6674731870616c6c65741043616c6c0404540001083073686172655f616363657373100124726563697069656e74450401684163636f756e7449644c6f6f6b7570536f757263654f663c543e0001186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000144726561645f6163636573735f7265676578650501684f7074696f6e3c5265616441636365737352656765783c543e3e00000cec53686172652061636365737320746f2066696c65732077697468696e2061206275636b6574207769746820616e6f74686572206163636f756e742e0079025468652060726561645f6163636573735f72656765786020706172616d65746572206973206f7074696f6e616c20616e64207768656e2073657420746f20604e6f6e6560206974206d65616e7320746861742074686520726563697069656e742077696c6c2062652064656e6965642061636365737320666f7220616e79207265616420726571756573742077697468696e20746865206275636b65742e487570646174655f726561645f6163636573730c01186275636b65743401384275636b65744964466f723c543e00011c6974656d5f6964100124543a3a4974656d4964000144726561645f6163636573735f7265676578650501684f7074696f6e3c5265616441636365737352656765783c543e3e0001047c55706461746520726561642061636365737320666f7220616e206974656d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e650504184f7074696f6e04045401b5020108104e6f6e6500000010536f6d650400b502000001000069050c2c70616c6c65745f6e6674731870616c6c65741043616c6c08045400044900019c1863726561746508011461646d696e450401504163636f756e7449644c6f6f6b75704f663c543e000118636f6e6669676d050164436f6c6c656374696f6e436f6e666967466f723c542c20493e00003c090149737375652061206e657720636f6c6c656374696f6e206f66206e6f6e2d66756e6769626c65206974656d732066726f6d2061207075626c6963206f726967696e2e001d0154686973206e657720636f6c6c656374696f6e20686173206e6f206974656d7320696e697469616c6c7920616e6420697473206f776e657220697320746865206f726967696e2e002501546865206f726967696e206d757374206265205369676e656420616e64207468652073656e646572206d75737420686176652073756666696369656e742066756e647320667265652e00c460436f6c6c656374696f6e4465706f736974602066756e6473206f662073656e646572206172652072657365727665642e002c506172616d65746572733a45012d206061646d696e603a205468652061646d696e206f66207468697320636f6c6c656374696f6e2e205468652061646d696e2069732074686520696e697469616c2061646472657373206f662065616368986d656d626572206f662074686520636f6c6c656374696f6e27732061646d696e207465616d2e0098456d69747320604372656174656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f2831296030666f7263655f6372656174650801146f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000118636f6e6669676d050164436f6c6c656374696f6e436f6e666967466f723c542c20493e00013c190149737375652061206e657720636f6c6c656374696f6e206f66206e6f6e2d66756e6769626c65206974656d732066726f6d20612070726976696c65676564206f726967696e2e00ac54686973206e657720636f6c6c656374696f6e20686173206e6f206974656d7320696e697469616c6c792e00a4546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e602e009c556e6c696b652060637265617465602c206e6f2066756e6473206172652072657365727665642e0039012d20606f776e6572603a20546865206f776e6572206f66207468697320636f6c6c656374696f6e206f66206974656d732e20546865206f776e6572206861732066756c6c20737570657275736572590120207065726d697373696f6e73206f7665722074686973206974656d2c20627574206d6179206c61746572206368616e676520616e6420636f6e66696775726520746865207065726d697373696f6e73207573696e67982020607472616e736665725f6f776e6572736869706020616e6420607365745f7465616d602e00ac456d6974732060466f7263654372656174656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f283129601c64657374726f79080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011c7769746e6573738d05013844657374726f795769746e6573730002449c44657374726f79206120636f6c6c656374696f6e206f662066756e6769626c65206974656d732e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d75737420626520605369676e65646020616e64207468652073656e646572206d75737420626520746865686f776e6572206f66207468652060636f6c6c656374696f6e602e00dc4e4f54453a2054686520636f6c6c656374696f6e206d75737420686176652030206974656d7320746f2062652064657374726f7965642e0005012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f2062652064657374726f7965642e31012d20607769746e657373603a20496e666f726d6174696f6e206f6e20746865206974656d73206d696e74656420696e2074686520636f6c6c656374696f6e2e2054686973206d75737420626520636f72726563742e00a0456d697473206044657374726f79656460206576656e74207768656e207375636365737366756c2e00745765696768743a20604f286d202b2063202b206129602077686572653a782d20606d203d207769746e6573732e6974656d5f6d657461646174617360702d206063203d207769746e6573732e6974656d5f636f6e6669677360682d206061203d207769746e6573732e6174747269627574657360106d696e74100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011c6d696e745f746f450401504163636f756e7449644c6f6f6b75704f663c543e0001307769746e6573735f64617461910501d84f7074696f6e3c4d696e745769746e6573733c543a3a4974656d49642c204465706f73697442616c616e63654f663c542c20493e3e3e000340a04d696e7420616e206974656d206f66206120706172746963756c617220636f6c6c656374696f6e2e005101546865206f726967696e206d757374206265205369676e656420616e64207468652073656e646572206d75737420636f6d706c7920776974682074686520606d696e745f73657474696e6773602072756c65732e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206d696e7465642ea02d20606974656d603a20416e206964656e746966696572206f6620746865206e6577206974656d2ee02d20606d696e745f746f603a204163636f756e7420696e746f20776869636820746865206974656d2077696c6c206265206d696e7465642e45012d20607769746e6573735f64617461603a205768656e20746865206d696e7420747970652069732060486f6c6465724f6628636f6c6c656374696f6e5f696429602c207468656e20746865206f776e6564590120206974656d5f69642066726f6d207468617420636f6c6c656374696f6e206e6565647320746f2062652070726f76696465642077697468696e20746865207769746e6573732064617461206f626a6563742e20496661012020746865206d696e74207072696365206973207365742c207468656e2069742073686f756c64206265206164646974696f6e616c6c7920636f6e6669726d656420696e2074686520607769746e6573735f64617461602e0051014e6f74653a20746865206465706f7369742077696c6c2062652074616b656e2066726f6d2074686520606f726967696e6020616e64206e6f742074686520606f776e657260206f662074686520606974656d602e0094456d697473206049737375656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f2831296028666f7263655f6d696e74100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011c6d696e745f746f450401504163636f756e7449644c6f6f6b75704f663c543e00012c6974656d5f636f6e666967990501284974656d436f6e66696700043405014d696e7420616e206974656d206f66206120706172746963756c617220636f6c6c656374696f6e2066726f6d20612070726976696c65676564206f726967696e2e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d75737420626520605369676e65646020616e64207468652073656e646572206d757374206265207468656c497373756572206f66207468652060636f6c6c656374696f6e602e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206d696e7465642ea02d20606974656d603a20416e206964656e746966696572206f6620746865206e6577206974656d2ee02d20606d696e745f746f603a204163636f756e7420696e746f20776869636820746865206974656d2077696c6c206265206d696e7465642ea82d20606974656d5f636f6e666967603a204120636f6e666967206f6620746865206e6577206974656d2e0094456d697473206049737375656460206576656e74207768656e207375636365737366756c2e00385765696768743a20604f28312960106275726e080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400052c5844657374726f7920612073696e676c65206974656d2e005d01546865206f726967696e206d75737420636f6e666f726d20746f2060466f7263654f726967696e60206f72206d757374206265205369676e656420616e6420746865207369676e696e67206163636f756e74206d7573746c626520746865206f776e6572206f662074686520606974656d602e00e02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206275726e65642e802d20606974656d603a20546865206974656d20746f206265206275726e65642e003c456d69747320604275726e6564602e00385765696768743a20604f28312960207472616e736665720c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064657374450401504163636f756e7449644c6f6f6b75704f663c543e000638c04d6f766520616e206974656d2066726f6d207468652073656e646572206163636f756e7420746f20616e6f746865722e00f44f726967696e206d757374206265205369676e656420616e6420746865207369676e696e67206163636f756e74206d757374206265206569746865723a682d20746865204f776e6572206f662074686520606974656d603b35012d2074686520617070726f7665642064656c656761746520666f722074686520606974656d602028696e207468697320636173652c2074686520617070726f76616c206973207265736574292e0028417267756d656e74733af42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265207472616e736665727265642e942d20606974656d603a20546865206974656d20746f206265207472616e736665727265642edc2d206064657374603a20546865206163636f756e7420746f2072656365697665206f776e657273686970206f6620746865206974656d2e0050456d69747320605472616e73666572726564602e00385765696768743a20604f283129602472656465706f736974080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146974656d73b90201385665633c543a3a4974656d49643e0007449c52652d6576616c7561746520746865206465706f73697473206f6e20736f6d65206974656d732e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00f82d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d7320746f2062652072656576616c75617465642e29012d20606974656d73603a20546865206974656d73206f662074686520636f6c6c656374696f6e2077686f7365206465706f736974732077696c6c2062652072656576616c75617465642e002d014e4f54453a205468697320657869737473206173206120626573742d6566666f72742066756e6374696f6e2e20416e79206974656d732077686963682061726520756e6b6e6f776e206f723901696e207468652063617365207468617420746865206f776e6572206163636f756e7420646f6573206e6f7420686176652072657365727661626c652066756e647320746f2070617920666f7220614d016465706f73697420696e637265617365206172652069676e6f7265642e2047656e6572616c6c7920746865206f776e65722069736e277420676f696e6720746f2063616c6c2074686973206f6e206974656d73550177686f7365206578697374696e67206465706f736974206973206c657373207468616e2074686520726566726573686564206465706f73697420617320697420776f756c64206f6e6c7920636f7374207468656d2c78736f2069742773206f66206c6974746c6520636f6e73657175656e63652e00590149742077696c6c207374696c6c2072657475726e20616e206572726f7220696e20746865206361736520746861742074686520636f6c6c656374696f6e20697320756e6b6e6f776e206f7220746865207369676e6572706973206e6f74207065726d697474656420746f2063616c6c2069742e00605765696768743a20604f286974656d732e6c656e28292960486c6f636b5f6974656d5f7472616e73666572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000828c8446973616c6c6f77206675727468657220756e70726976696c65676564207472616e73666572206f6620616e206974656d2e003d014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c642062652074686520467265657a6572206f66207468652060636f6c6c656374696f6e602e00e42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206368616e6765642eb82d20606974656d603a20546865206974656d20746f206265636f6d65206e6f6e2d7472616e7366657261626c652e006c456d69747320604974656d5472616e736665724c6f636b6564602e00385765696768743a20604f2831296050756e6c6f636b5f6974656d5f7472616e73666572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964000928a852652d616c6c6f7720756e70726976696c65676564207472616e73666572206f6620616e206974656d2e003d014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c642062652074686520467265657a6572206f66207468652060636f6c6c656374696f6e602e00e42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f206265206368616e6765642ea82d20606974656d603a20546865206974656d20746f206265636f6d65207472616e7366657261626c652e0074456d69747320604974656d5472616e73666572556e6c6f636b6564602e00385765696768743a20604f283129603c6c6f636b5f636f6c6c656374696f6e080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001346c6f636b5f73657474696e677371050148436f6c6c656374696f6e53657474696e6773000a30d8446973616c6c6f7773207370656369666965642073657474696e677320666f72207468652077686f6c6520636f6c6c656374696f6e2e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00b02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e20746f206265206c6f636b65642eb42d20606c6f636b5f73657474696e6773603a205468652073657474696e677320746f206265206c6f636b65642e001d014e6f74653a206974277320706f737369626c6520746f206f6e6c79206c6f636b2873657429207468652073657474696e672c20627574206e6f7420746f20756e7365742069742e0064456d6974732060436f6c6c656374696f6e4c6f636b6564602e00385765696768743a20604f28312960487472616e736665725f6f776e657273686970080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001246e65775f6f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000b2c844368616e676520746865204f776e6572206f66206120636f6c6c656374696f6e2e0035014f726967696e206d757374206265205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468652060636f6c6c656374696f6e602e00f42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2077686f7365206f776e65722073686f756c64206265206368616e6765642e09012d20606f776e6572603a20546865206e6577204f776e6572206f66207468697320636f6c6c656374696f6e2e2054686579206d75737420686176652063616c6c656449012020607365745f6163636570745f6f776e6572736869706020776974682060636f6c6c656374696f6e6020696e206f7264657220666f722074686973206f7065726174696f6e20746f20737563636565642e0054456d69747320604f776e65724368616e676564602e00385765696768743a20604f28312960207365745f7465616d100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001186973737565729d0501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e00011461646d696e9d0501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e00011c667265657a65729d0501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e000c40d44368616e676520746865204973737565722c2041646d696e20616e6420467265657a6572206f66206120636f6c6c656374696f6e2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468653460636f6c6c656374696f6e602e0051014e6f74653a2062792073657474696e672074686520726f6c6520746f20604e6f6e6560206f6e6c79207468652060466f7263654f726967696e602077696c6c2062652061626c6520746f206368616e676520697464616674657220746f2060536f6d65286163636f756e7429602e00f02d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2077686f7365207465616d2073686f756c64206265206368616e6765642eb82d2060697373756572603a20546865206e657720497373756572206f66207468697320636f6c6c656374696f6e2eb02d206061646d696e603a20546865206e65772041646d696e206f66207468697320636f6c6c656374696f6e2ec02d2060667265657a6572603a20546865206e657720467265657a6572206f66207468697320636f6c6c656374696f6e2e0050456d69747320605465616d4368616e676564602e00385765696768743a20604f2831296058666f7263655f636f6c6c656374696f6e5f6f776e6572080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001146f776e6572450401504163636f756e7449644c6f6f6b75704f663c543e000d28844368616e676520746865204f776e6572206f66206120636f6c6c656374696f6e2e00744f726967696e206d7573742062652060466f7263654f726967696e602e00c42d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2eb02d20606f776e6572603a20546865206e6577204f776e6572206f66207468697320636f6c6c656374696f6e2e0054456d69747320604f776e65724368616e676564602e00385765696768743a20604f283129605c666f7263655f636f6c6c656374696f6e5f636f6e666967080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000118636f6e6669676d050164436f6c6c656374696f6e436f6e666967466f723c542c20493e000e28884368616e67652074686520636f6e666967206f66206120636f6c6c656374696f6e2e00744f726967696e206d7573742062652060466f7263654f726967696e602e00c42d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2eb82d2060636f6e666967603a20546865206e657720636f6e666967206f66207468697320636f6c6c656374696f6e2e0080456d6974732060436f6c6c656374696f6e436f6e6669674368616e676564602e00385765696768743a20604f2831296040617070726f76655f7472616e73666572100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e0001386d617962655f646561646c696e65110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000f381501417070726f766520616e206974656d20746f206265207472616e7366657272656420627920612064656c6567617465642074686972642d7061727479206163636f756e742e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66207468651c606974656d602e0045012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f20626520617070726f76656420666f722064656c656761746564207472616e736665722ee42d20606974656d603a20546865206974656d20746f20626520617070726f76656420666f722064656c656761746564207472616e736665722e19012d206064656c6567617465603a20546865206163636f756e7420746f2064656c6567617465207065726d697373696f6e20746f207472616e7366657220746865206974656d2e49012d20606d617962655f646561646c696e65603a204f7074696f6e616c20646561646c696e6520666f722074686520617070726f76616c2e205370656369666965642062792070726f766964696e6720746865d8096e756d626572206f6620626c6f636b732061667465722077686963682074686520617070726f76616c2077696c6c206578706972650090456d69747320605472616e73666572417070726f76656460206f6e20737563636573732e00385765696768743a20604f283129603c63616e63656c5f617070726f76616c0c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e001038e443616e63656c206f6e65206f6620746865207472616e7366657220617070726f76616c7320666f722061207370656369666963206974656d2e00584f726967696e206d757374206265206569746865723a542d207468652060466f72636560206f726967696e3be42d20605369676e656460207769746820746865207369676e6572206265696e6720746865204f776e6572206f662074686520606974656d603b0028417267756d656e74733a3d012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d206f662077686f736520617070726f76616c2077696c6c2062652063616e63656c6c65642e25012d20606974656d603a20546865206974656d206f662074686520636f6c6c656374696f6e206f662077686f736520617070726f76616c2077696c6c2062652063616e63656c6c65642e01012d206064656c6567617465603a20546865206163636f756e74207468617420697320676f696e6720746f206c6f6f736520746865697220617070726f76616c2e0094456d6974732060417070726f76616c43616e63656c6c656460206f6e20737563636573732e00385765696768743a20604f2831296070636c6561725f616c6c5f7472616e736665725f617070726f76616c73080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d4964001134b043616e63656c20616c6c2074686520617070726f76616c73206f662061207370656369666963206974656d2e00584f726967696e206d757374206265206569746865723a542d207468652060466f72636560206f726967696e3be42d20605369676e656460207769746820746865207369676e6572206265696e6720746865204f776e6572206f662074686520606974656d603b0028417267756d656e74733a39012d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d206f662077686f736520617070726f76616c732077696c6c20626520636c65617265642e21012d20606974656d603a20546865206974656d206f662074686520636f6c6c656374696f6e206f662077686f736520617070726f76616c732077696c6c20626520636c65617265642e00a4456d6974732060416c6c417070726f76616c7343616e63656c6c656460206f6e20737563636573732e00385765696768743a20604f28312960506c6f636b5f6974656d5f70726f70657274696573100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001346c6f636b5f6d65746164617461200110626f6f6c00013c6c6f636b5f61747472696275746573200110626f6f6c001244e8446973616c6c6f7773206368616e67696e6720746865206d65746164617461206f722061747472696275746573206f6620746865206974656d2e0041014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e506f66207468652060636f6c6c656374696f6e602e00b42d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e2069662074686520606974656d602e7c2d20606974656d603a20416e206974656d20746f206265206c6f636b65642e0d012d20606c6f636b5f6d65746164617461603a20537065636966696573207768657468657220746865206d657461646174612073686f756c64206265206c6f636b65642e61012d20606c6f636b5f61747472696275746573603a20537065636966696573207768657468657220746865206174747269627574657320696e207468652060436f6c6c656374696f6e4f776e657260206e616d6573706163654c202073686f756c64206265206c6f636b65642e005d014e6f74653a20606c6f636b5f6174747269627574657360206166666563747320746865206174747269627574657320696e207468652060436f6c6c656374696f6e4f776e657260206e616d657370616365206f6e6c792e45015768656e20746865206d65746164617461206f72206174747269627574657320617265206c6f636b65642c20697420776f6e277420626520706f737369626c652074686520756e6c6f636b207468656d2e0074456d69747320604974656d50726f706572746965734c6f636b6564602e00385765696768743a20604f28312960347365745f617474726962757465140128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e001358a853657420616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e0001014f726967696e206d757374206265205369676e656420616e64206d75737420636f6e666f726d20746f20746865206e616d6573706163652072756c657365743a3d012d2060436f6c6c656374696f6e4f776e657260206e616d65737061636520636f756c64206265206d6f646966696564206279207468652060636f6c6c656374696f6e602041646d696e206f6e6c793b59012d20604974656d4f776e657260206e616d65737061636520636f756c64206265206d6f6469666965642062792074686520606d617962655f6974656d60206f776e6572206f6e6c792e20606d617962655f6974656d6074202073686f756c642062652073657420696e207468617420636173653b55012d20604163636f756e74284163636f756e7449642960206e616d65737061636520636f756c64206265206d6f646966696564206f6e6c79207768656e2074686520606f726967696e602077617320676976656e20615820207065726d697373696f6e20746f20646f20736f3b00f05468652066756e6473206f6620606f726967696e6020617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613a2d01604174747269627574654465706f73697442617365202b204465706f73697450657242797465202a20286b65792e6c656e202b2076616c75652e6c656e29602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0039012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742e05012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652ecc2d206076616c7565603a205468652076616c756520746f20776869636820746f2073657420746865206174747269627574652e0054456d6974732060417474726962757465536574602e00385765696768743a20604f283129604c666f7263655f7365745f6174747269627574651801187365745f61739c01504f7074696f6e3c543a3a4163636f756e7449643e000128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00011476616c7565b5020174426f756e6465645665633c75382c20543a3a56616c75654c696d69743e001444c0466f7263652d73657420616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e00744f726967696e206d7573742062652060466f7263654f726967696e602e0039014966207468652061747472696275746520616c72656164792065786973747320616e64206974207761732073657420627920616e6f74686572206163636f756e742c20746865206465706f7369749c77696c6c2062652072657475726e656420746f207468652070726576696f7573206f776e65722e00bc2d20607365745f6173603a20416e206f7074696f6e616c206f776e6572206f6620746865206174747269627574652e39012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742e05012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652ecc2d206076616c7565603a205468652076616c756520746f20776869636820746f2073657420746865206174747269627574652e0054456d6974732060417474726962757465536574602e00385765696768743a20604f283129603c636c6561725f617474726962757465100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d617962655f6974656d110201444f7074696f6e3c543a3a4974656d49643e0001246e616d657370616365c10201804174747269627574654e616d6573706163653c543a3a4163636f756e7449643e00010c6b6579bd02016c426f756e6465645665633c75382c20543a3a4b65794c696d69743e00153cb0436c65617220616e2061747472696275746520666f72206120636f6c6c656374696f6e206f72206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f6620746865286174747269627574652e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0041012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f20636c6561722e0d012d20606d617962655f6974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f20636c6561722e942d20606e616d657370616365603a204174747269627574652773206e616d6573706163652e882d20606b6579603a20546865206b6579206f6620746865206174747269627574652e0064456d6974732060417474726962757465436c6561726564602e00385765696768743a20604f283129605c617070726f76655f6974656d5f617474726962757465730c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e0016242d01417070726f7665206974656d2773206174747269627574657320746f206265206368616e67656420627920612064656c6567617465642074686972642d7061727479206163636f756e742e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00a42d2060636f6c6c656374696f6e603a204120636f6c6c656374696f6e206f6620746865206974656d2ea42d20606974656d603a20546865206974656d207468617420686f6c647320617474726962757465732e49012d206064656c6567617465603a20546865206163636f756e7420746f2064656c6567617465207065726d697373696f6e20746f206368616e67652061747472696275746573206f6620746865206974656d2e00bc456d69747320604974656d41747472696275746573417070726f76616c416464656460206f6e20737563636573732e7c63616e63656c5f6974656d5f617474726962757465735f617070726f76616c100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400012064656c6567617465450401504163636f756e7449644c6f6f6b75704f663c543e00011c7769746e657373a105017c43616e63656c41747472696275746573417070726f76616c5769746e657373001728110143616e63656c207468652070726576696f75736c792070726f766964656420617070726f76616c20746f206368616e6765206974656d277320617474726962757465732e1101416c6c207468652070726576696f75736c7920736574206174747269627574657320627920746865206064656c6567617465602077696c6c2062652072656d6f7665642e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00f42d2060636f6c6c656374696f6e603a20436f6c6c656374696f6e207468617420746865206974656d20697320636f6e7461696e65642077697468696e2ea42d20606974656d603a20546865206974656d207468617420686f6c647320617474726962757465732ee02d206064656c6567617465603a205468652070726576696f75736c7920617070726f766564206163636f756e7420746f2072656d6f76652e00c4456d69747320604974656d41747472696275746573417070726f76616c52656d6f76656460206f6e20737563636573732e307365745f6d657461646174610c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e0018407453657420746865206d6574616461746120666f7220616e206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66207468653460636f6c6c656374696f6e602e005101496620746865206f726967696e206973205369676e65642c207468656e2066756e6473206f66207369676e657220617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613af4604d657461646174614465706f73697442617365202b204465706f73697450657242797465202a20646174612e6c656e602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0039012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f207365742eec2d20606974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207365742e4d012d206064617461603a205468652067656e6572616c20696e666f726d6174696f6e206f662074686973206974656d2e204c696d6974656420696e206c656e6774682062792060537472696e674c696d6974602e0060456d69747320604974656d4d65746164617461536574602e00385765696768743a20604f2831296038636c6561725f6d65746164617461080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640019347c436c65617220746865206d6574616461746120666f7220616e206974656d2e005d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f72205369676e656420616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66207468653460636f6c6c656374696f6e602e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0041012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206974656d2773206d6574616461746120746f20636c6561722ef42d20606974656d603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f20636c6561722e0070456d69747320604974656d4d65746164617461436c6561726564602e00385765696768743a20604f283129605c7365745f636f6c6c656374696f6e5f6d65746164617461080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400011064617461b5020178426f756e6465645665633c75382c20543a3a537472696e674c696d69743e001a3c8853657420746865206d6574616461746120666f72206120636f6c6c656374696f6e2e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66447468652060636f6c6c656374696f6e602e005901496620746865206f726967696e20697320605369676e6564602c207468656e2066756e6473206f66207369676e657220617265207265736572766564206163636f7264696e6720746f2074686520666f726d756c613af4604d657461646174614465706f73697442617365202b204465706f73697450657242797465202a20646174612e6c656e602074616b696e6720696e746f8c6163636f756e7420616e7920616c72656164792072657365727665642066756e64732e0011012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f6620746865206974656d2077686f7365206d6574616461746120746f207570646174652e4d012d206064617461603a205468652067656e6572616c20696e666f726d6174696f6e206f662074686973206974656d2e204c696d6974656420696e206c656e6774682062792060537472696e674c696d6974602e0078456d6974732060436f6c6c656374696f6e4d65746164617461536574602e00385765696768743a20604f2831296064636c6561725f636f6c6c656374696f6e5f6d65746164617461040128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964001b3090436c65617220746865206d6574616461746120666f72206120636f6c6c656374696f6e2e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c64206265207468652041646d696e206f66447468652060636f6c6c656374696f6e602e00c0416e79206465706f73697420697320667265656420666f722074686520636f6c6c656374696f6e2773206f776e65722e0025012d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206d6574616461746120746f20636c6561722e0088456d6974732060436f6c6c656374696f6e4d65746164617461436c6561726564602e00385765696768743a20604f28312960507365745f6163636570745f6f776e6572736869700401406d617962655f636f6c6c656374696f6e1102015c4f7074696f6e3c543a3a436f6c6c656374696f6e49643e001c28110153657420286f72207265736574292074686520616363657074616e6365206f66206f776e65727368697020666f72206120706172746963756c6172206163636f756e742e0061014f726967696e206d75737420626520605369676e65646020616e6420696620606d617962655f636f6c6c656374696f6e602069732060536f6d65602c207468656e20746865207369676e6572206d757374206861766520614c70726f7669646572207265666572656e63652e0051012d20606d617962655f636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e2077686f7365206f776e65727368697020746865207369676e65722069736101202077696c6c696e6720746f206163636570742c206f7220696620604e6f6e65602c20616e20696e6469636174696f6e207468617420746865207369676e65722069732077696c6c696e6720746f20616363657074206e6f5c20206f776e657273686970207472616e73666572616c2e008c456d69747320604f776e657273686970416363657074616e63654368616e676564602e647365745f636f6c6c656374696f6e5f6d61785f737570706c79080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001286d61785f737570706c7910010c753332001d24e053657420746865206d6178696d756d206e756d626572206f66206974656d73206120636f6c6c656374696f6e20636f756c6420686176652e0055014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c6420626520746865204f776e6572206f66447468652060636f6c6c656374696f6e602e00ec2d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f206368616e67652e11012d20606d61785f737570706c79603a20546865206d6178696d756d206e756d626572206f66206974656d73206120636f6c6c656374696f6e20636f756c6420686176652e00d4456d6974732060436f6c6c656374696f6e4d6178537570706c7953657460206576656e74207768656e207375636365737366756c2e507570646174655f6d696e745f73657474696e6773080128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001346d696e745f73657474696e677379050105014d696e7453657474696e67733c42616c616e63654f663c542c20493e2c20426c6f636b4e756d626572466f723c543e2c20543a3a436f6c6c656374696f6e49643e001e2454557064617465206d696e742073657474696e67732e004d014f726967696e206d757374206265206569746865722060466f7263654f726967696e60206f7220605369676e65646020616e64207468652073656e6465722073686f756c642062652074686520497373756572506f66207468652060636f6c6c656374696f6e602e00ec2d2060636f6c6c656374696f6e603a20546865206964656e746966696572206f662074686520636f6c6c656374696f6e20746f206368616e67652ea42d20606d696e745f73657474696e6773603a20546865206e6577206d696e742073657474696e67732e00f0456d6974732060436f6c6c656374696f6e4d696e7453657474696e67735570646174656460206576656e74207768656e207375636365737366756c2e247365745f7072696365100128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d496400011470726963658105015c4f7074696f6e3c4974656d50726963653c542c20493e3e00014477686974656c69737465645f62757965729d0501704f7074696f6e3c4163636f756e7449644c6f6f6b75704f663c543e3e001f2c9453657420286f72207265736574292074686520707269636520666f7220616e206974656d2e00e84f726967696e206d757374206265205369676e656420616e64206d75737420626520746865206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea02d20606974656d603a20546865206974656d20746f207365742074686520707269636520666f722e0d012d20607072696365603a2054686520707269636520666f7220746865206974656d2e205061737320604e6f6e65602c20746f207265736574207468652070726963652ef42d20606275796572603a205265737472696374732074686520627579206f7065726174696f6e20746f2061207370656369666963206163636f756e742e00ec456d69747320604974656d507269636553657460206f6e207375636365737320696620746865207072696365206973206e6f7420604e6f6e65602eec456d69747320604974656d507269636552656d6f76656460206f6e20737563636573732069662074686520707269636520697320604e6f6e65602e206275795f6974656d0c0128636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001106974656d100124543a3a4974656d49640001246269645f707269636518013c4974656d50726963653c542c20493e002024a8416c6c6f777320746f2062757920616e206974656d206966206974277320757020666f722073616c652e00f84f726967696e206d757374206265205369676e656420616e64206d757374206e6f7420626520746865206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2eac2d20606974656d603a20546865206974656d207468652073656e6465722077616e747320746f206275792ed82d20606269645f7072696365603a20546865207072696365207468652073656e6465722069732077696c6c696e6720746f207061792e0078456d69747320604974656d426f7567687460206f6e20737563636573732e207061795f7469707304011074697073a505019c426f756e6465645665633c4974656d5469704f663c542c20493e2c20543a3a4d6178546970733e00211c5c416c6c6f777320746f207061792074686520746970732e00584f726967696e206d757374206265205369676e65642e00542d206074697073603a20546970732061727261792e0098456d697473206054697053656e7460206f6e20657665727920746970207472616e736665722e2c6372656174655f737761701801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d4964000148646573697265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001486d617962655f646573697265645f6974656d110201444f7074696f6e3c543a3a4974656d49643e00012c6d617962655f7072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e0001206475726174696f6e100144426c6f636b4e756d626572466f723c543e002240510152656769737465722061206e65772061746f6d696320737761702c206465636c6172696e6720616e20696e74656e74696f6e20746f2073656e6420616e20606974656d6020696e2065786368616e676520666f72fc60646573697265645f6974656d602066726f6d206f726967696e20746f20746172676574206f6e207468652063757272656e7420626c6f636b636861696e2e4d01546865207461726765742063616e206578656375746520746865207377617020647572696e67207468652073706563696669656420606475726174696f6e60206f6620626c6f636b732028696620736574292ef04164646974696f6e616c6c792c2074686520707269636520636f756c642062652073657420666f7220746865206465736972656420606974656d602e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea82d20606974656d603a20546865206974656d20616e206f776e65722077616e747320746f20676976652eec2d2060646573697265645f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f66207468652064657369726564206974656d2ef42d2060646573697265645f6974656d603a205468652064657369726564206974656d20616e206f776e65722077616e747320746f20726563656976652e61012d20606d617962655f7072696365603a2054686520707269636520616e206f776e65722069732077696c6c696e6720746f20706179206f72207265636569766520666f7220746865206465736972656420606974656d602e49012d20606475726174696f6e603a204120646561646c696e6520666f722074686520737761702e205370656369666965642062792070726f766964696e6720746865206e756d626572206f6620626c6f636b73880961667465722077686963682074686520737761702077696c6c206578706972652e007c456d6974732060537761704372656174656460206f6e20737563636573732e2c63616e63656c5f737761700801486f6666657265645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e49640001306f6666657265645f6974656d100124543a3a4974656d49640023245843616e63656c20616e2061746f6d696320737761702e00584f726967696e206d757374206265205369676e65642e15014f726967696e206d75737420626520616e206f776e6572206f662074686520606974656d602069662074686520646561646c696e65206861736e277420657870697265642e00ac2d2060636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d2ea82d20606974656d603a20546865206974656d20616e206f776e65722077616e747320746f20676976652e0084456d69747320605377617043616e63656c6c656460206f6e20737563636573732e28636c61696d5f7377617014013c73656e645f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e496400012473656e645f6974656d100124543a3a4974656d4964000148726563656976655f636f6c6c656374696f6e10013c543a3a436f6c6c656374696f6e4964000130726563656976655f6974656d100124543a3a4974656d49640001347769746e6573735f7072696365c50201ac4f7074696f6e3c507269636557697468446972656374696f6e3c4974656d50726963653c542c20493e3e3e00243054436c61696d20616e2061746f6d696320737761702e390154686973206d6574686f6420657865637574657320612070656e64696e6720737761702c2074686174207761732063726561746564206279206120636f756e74657270617274206265666f72652e00e44f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f662074686520606974656d602e00ec2d206073656e645f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f2062652073656e742e8c2d206073656e645f6974656d603a20546865206974656d20746f2062652073656e742e09012d2060726563656976655f636f6c6c656374696f6e603a2054686520636f6c6c656374696f6e206f6620746865206974656d20746f2062652072656365697665642ea82d2060726563656976655f6974656d603a20546865206974656d20746f2062652072656365697665642ee42d20607769746e6573735f7072696365603a20412070726963652074686174207761732070726576696f75736c7920616772656564206f6e2e007c456d697473206053776170436c61696d656460206f6e20737563636573732e3c6d696e745f7072655f7369676e65640c01246d696e745f64617461b1050168426f783c5072655369676e65644d696e744f663c542c20493e3e0001247369676e6174757265b5050150543a3a4f6666636861696e5369676e61747572650001187369676e6572000130543a3a4163636f756e744964002534c84d696e7420616e206974656d2062792070726f766964696e6720746865207072652d7369676e656420617070726f76616c2e00584f726967696e206d757374206265205369676e65642e005d012d20606d696e745f64617461603a20546865207072652d7369676e656420617070726f76616c207468617420636f6e7369737473206f662074686520696e666f726d6174696f6e2061626f757420746865206974656d2c51012020697473206d657461646174612c20617474726962757465732c2077686f2063616e206d696e742069742028604e6f6e656020666f7220616e796f6e652920616e6420756e74696c207768617420626c6f636b2420206e756d6265722ec82d20607369676e6174757265603a20546865207369676e6174757265206f662074686520606461746160206f626a6563742e39012d20607369676e6572603a2054686520606461746160206f626a6563742773207369676e65722e2053686f756c6420626520616e20497373756572206f662074686520636f6c6c656374696f6e2e0068456d697473206049737375656460206f6e20737563636573732ed4456d69747320604174747269627574655365746020696620746865206174747269627574657320776572652070726f76696465642ed8456d69747320604974656d4d657461646174615365746020696620746865206d6574616461746120776173206e6f7420656d7074792e647365745f617474726962757465735f7072655f7369676e65640c011064617461c105016c5072655369676e6564417474726962757465734f663c542c20493e0001247369676e6174757265b5050150543a3a4f6666636861696e5369676e61747572650001187369676e6572000130543a3a4163636f756e7449640026340101536574206174747269627574657320666f7220616e206974656d2062792070726f766964696e6720746865207072652d7369676e656420617070726f76616c2e00f84f726967696e206d757374206265205369676e656420616e64206d75737420626520616e206f776e6572206f66207468652060646174612e6974656d602e0049012d206064617461603a20546865207072652d7369676e656420617070726f76616c207468617420636f6e7369737473206f662074686520696e666f726d6174696f6e2061626f757420746865206974656d2ccc20206174747269627574657320746f2075706461746520616e6420756e74696c207768617420626c6f636b206e756d6265722ec82d20607369676e6174757265603a20546865207369676e6174757265206f662074686520606461746160206f626a6563742e51012d20607369676e6572603a2054686520606461746160206f626a6563742773207369676e65722e2053686f756c6420626520616e2041646d696e206f662074686520636f6c6c656374696f6e20666f722074686578202060436f6c6c656374696f6e4f776e657260206e616d6573706163652e00c4456d69747320604174747269627574655365746020666f7220656163682070726f7669646564206174747269627574652e1901456d69747320604974656d41747472696275746573417070726f76616c4164646564602069662074686520617070726f76616c207761736e277420736574206265666f72652ea8456d69747320605072655369676e65644174747269627574657353657460206f6e20737563636573732e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e6d050c2c70616c6c65745f6e66747314747970657340436f6c6c656374696f6e436f6e6669670c14507269636501182c426c6f636b4e756d626572011030436f6c6c656374696f6e49640110000c012073657474696e677371050148436f6c6c656374696f6e53657474696e67730001286d61785f737570706c791102012c4f7074696f6e3c7533323e0001346d696e745f73657474696e6773790501b84d696e7453657474696e67733c50726963652c20426c6f636b4e756d6265722c20436f6c6c656374696f6e49643e000071050c2c70616c6c65745f6e66747314747970657320426974466c616773040454017505000400300144436f6c6c656374696f6e53657474696e67000075050c2c70616c6c65745f6e66747314747970657344436f6c6c656374696f6e53657474696e67000114445472616e7366657261626c654974656d7300010040556e6c6f636b65644d6574616461746100020048556e6c6f636b65644174747269627574657300040044556e6c6f636b65644d6178537570706c790008003c4465706f73697452657175697265640010000079050c2c70616c6c65745f6e667473147479706573304d696e7453657474696e67730c14507269636501182c426c6f636b4e756d626572011030436f6c6c656374696f6e49640110001401246d696e745f747970657d0501584d696e74547970653c436f6c6c656374696f6e49643e0001147072696365810501344f7074696f6e3c50726963653e00012c73746172745f626c6f636b1102014c4f7074696f6e3c426c6f636b4e756d6265723e000124656e645f626c6f636b1102014c4f7074696f6e3c426c6f636b4e756d6265723e00015464656661756c745f6974656d5f73657474696e6773850501304974656d53657474696e677300007d050c2c70616c6c65745f6e667473147479706573204d696e74547970650430436f6c6c656374696f6e49640110010c18497373756572000000185075626c696300010020486f6c6465724f660400100130436f6c6c656374696f6e496400020000810504184f7074696f6e04045401180108104e6f6e6500000010536f6d65040018000001000085050c2c70616c6c65745f6e66747314747970657320426974466c61677304045401890500040030012c4974656d53657474696e67000089050c2c70616c6c65745f6e6674731474797065732c4974656d53657474696e6700010c305472616e7366657261626c6500010040556e6c6f636b65644d6574616461746100020048556e6c6f636b656441747472696275746573000400008d050c2c70616c6c65745f6e6674731474797065733844657374726f795769746e65737300000c01386974656d5f6d6574616461746173cc010c7533320001306974656d5f636f6e66696773cc010c75333200012861747472696275746573cc010c7533320000910504184f7074696f6e0404540195050108104e6f6e6500000010536f6d6504009505000001000095050c2c70616c6c65745f6e6674731474797065732c4d696e745769746e65737308184974656d496401101c42616c616e63650118000801286f776e65645f6974656d110201384f7074696f6e3c4974656d49643e0001286d696e745f70726963658105013c4f7074696f6e3c42616c616e63653e000099050c2c70616c6c65745f6e667473147479706573284974656d436f6e666967000004012073657474696e6773850501304974656d53657474696e677300009d0504184f7074696f6e0404540145040108104e6f6e6500000010536f6d65040045040000010000a1050c2c70616c6c65745f6e6674731474797065737c43616e63656c41747472696275746573417070726f76616c5769746e65737300000401486163636f756e745f6174747269627574657310010c7533320000a5050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401a905045300000400ad0501185665633c543e0000a9050c2c70616c6c65745f6e6674731474797065731c4974656d5469701030436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010018416d6f756e74011800100128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d496400012072656365697665720001244163636f756e744964000118616d6f756e74180118416d6f756e740000ad05000002a90500b1050c2c70616c6c65745f6e667473147479706573345072655369676e65644d696e741430436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010020446561646c696e6501101c42616c616e63650118001c0128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d4964000128617474726962757465731903015c5665633c285665633c75383e2c205665633c75383e293e0001206d6574616461746138011c5665633c75383e0001306f6e6c795f6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000120646561646c696e65100120446561646c696e650001286d696e745f70726963658105013c4f7074696f6e3c42616c616e63653e0000b505082873705f72756e74696d65384d756c74695369676e617475726500010c1c456432353531390400b9050148656432353531393a3a5369676e61747572650000001c537232353531390400b9050148737232353531393a3a5369676e61747572650001001445636473610400bd05014065636473613a3a5369676e617475726500020000b905000003400000000800bd05000003410000000800c1050c2c70616c6c65745f6e6674731474797065734c5072655369676e6564417474726962757465731030436f6c6c656374696f6e49640110184974656d49640110244163636f756e744964010020446561646c696e65011000140128636f6c6c656374696f6e100130436f6c6c656374696f6e49640001106974656d1001184974656d4964000128617474726962757465731903015c5665633c285665633c75383e2c205665633c75383e293e0001246e616d657370616365c10201744174747269627574654e616d6573706163653c4163636f756e7449643e000120646561646c696e65100120446561646c696e650000c5050c4470616c6c65745f706172616d65746572731870616c6c65741043616c6c040454000104347365745f706172616d657465720401246b65795f76616c7565c9050150543a3a52756e74696d65506172616d657465727300001074536574207468652076616c7565206f66206120706172616d657465722e005501546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265206041646d696e4f726967696e6020666f722074686520676976656e20606b6579602e2056616c7565732062658864656c657465642062792073657474696e67207468656d20746f20604e6f6e65602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec905104c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d734452756e74696d65506172616d65746572730001043452756e74696d65436f6e6669670400cd0501a864796e616d69635f706172616d733a3a72756e74696d655f636f6e6669673a3a506172616d657465727300000000cd05184c73746f726167655f6875625f72756e74696d651c636f6e666967733872756e74696d655f706172616d733864796e616d69635f706172616d733872756e74696d655f636f6e66696728506172616d657465727300011064536c617368416d6f756e745065724d617846696c6553697a650800e1020164536c617368416d6f756e745065724d617846696c6553697a6500008105013c4f7074696f6e3c42616c616e63653e000000585374616b65546f4368616c6c656e6765506572696f640800e50201585374616b65546f4368616c6c656e6765506572696f6400008105013c4f7074696f6e3c42616c616e63653e00010064436865636b706f696e744368616c6c656e6765506572696f640800e9020164436865636b706f696e744368616c6c656e6765506572696f6400001102014c4f7074696f6e3c426c6f636b4e756d6265723e000200484d696e4368616c6c656e6765506572696f640800ed0201484d696e4368616c6c656e6765506572696f6400001102014c4f7074696f6e3c426c6f636b4e756d6265723e00030000d1050c2c70616c6c65745f7375646f1870616c6c6574144572726f720404540001042c526571756972655375646f0000048053656e646572206d75737420626520746865205375646f206163636f756e742e04684572726f7220666f7220746865205375646f2070616c6c65742ed5050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400a401185665633c543e0000d9050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401dd05045300000400e10501185665633c543e0000dd050c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65743443616e646964617465496e666f08244163636f756e74496401001c42616c616e636501180008010c77686f0001244163636f756e74496400011c6465706f73697418011c42616c616e63650000e105000002dd0500e5050c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144572726f7204045400014444546f6f4d616e7943616e646964617465730000048c5468652070616c6c65742068617320746f6f206d616e792063616e646964617465732e5c546f6f466577456c696769626c65436f6c6c61746f7273000104ac4c656176696e6720776f756c6420726573756c7420696e20746f6f206665772063616e646964617465732e40416c726561647943616e6469646174650002047c4163636f756e7420697320616c726561647920612063616e6469646174652e304e6f7443616e6469646174650003046c4163636f756e74206973206e6f7420612063616e6469646174652e50546f6f4d616e79496e76756c6e657261626c65730004048454686572652061726520746f6f206d616e7920496e76756c6e657261626c65732e4c416c7265616479496e76756c6e657261626c650005048c4163636f756e7420697320616c726561647920616e20496e76756c6e657261626c652e3c4e6f74496e76756c6e657261626c650006047c4163636f756e74206973206e6f7420616e20496e76756c6e657261626c652e5c4e6f4173736f63696174656456616c696461746f7249640007049c4163636f756e7420686173206e6f206173736f6369617465642076616c696461746f722049442e5856616c696461746f724e6f74526567697374657265640008048c56616c696461746f72204944206973206e6f742079657420726567697374657265642e6c496e73657274546f43616e6469646174654c6973744661696c65640009049c436f756c64206e6f7420696e7365727420696e207468652063616e646964617465206c6973742e7452656d6f766546726f6d43616e6469646174654c6973744661696c6564000a04a4436f756c64206e6f742072656d6f76652066726f6d207468652063616e646964617465206c6973742e344465706f736974546f6f4c6f77000b04f44e6577206465706f73697420616d6f756e7420776f756c642062652062656c6f7720746865206d696e696d756d2063616e64696461637920626f6e642e6455706461746543616e6469646174654c6973744661696c6564000c0490436f756c64206e6f7420757064617465207468652063616e646964617465206c6973742e40496e73756666696369656e74426f6e64000d0429014465706f73697420616d6f756e7420697320746f6f206c6f7720746f2074616b652074686520746172676574277320736c6f7420696e207468652063616e646964617465206c6973742e5054617267657449734e6f7443616e646964617465000e042d0154686520746172676574206163636f756e7420746f206265207265706c6163656420696e207468652063616e646964617465206c697374206973206e6f7420612063616e6469646174652e404964656e746963616c4465706f736974000f040d015468652075706461746564206465706f73697420616d6f756e7420697320657175616c20746f2074686520616d6f756e7420616c72656164792072657365727665642e40496e76616c6964556e726573657276650010043d0143616e6e6f74206c6f7765722063616e64696461637920626f6e64207768696c65206f6363757079696e6720612066757475726520636f6c6c61746f7220736c6f7420696e20746865206c6973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ee905000002ed0500ed050000040800690400f10500000408f5053800f5050c1c73705f636f72651863727970746f244b65795479706549640000040048011c5b75383b20345d0000f9050c3870616c6c65745f73657373696f6e1870616c6c6574144572726f7204045400011430496e76616c696450726f6f6600000460496e76616c6964206f776e6572736869702070726f6f662e5c4e6f4173736f63696174656456616c696461746f7249640001049c4e6f206173736f6369617465642076616c696461746f7220494420666f72206163636f756e742e344475706c6963617465644b65790002046452656769737465726564206475706c6963617465206b65792e184e6f4b657973000304a44e6f206b65797320617265206173736f63696174656420776974682074686973206163636f756e742e244e6f4163636f756e7400040419014b65792073657474696e67206163636f756e74206973206e6f74206c6976652c20736f206974277320696d706f737369626c6520746f206173736f6369617465206b6579732e04744572726f7220666f72207468652073657373696f6e2070616c6c65742efd050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454016d04045300000400010601185665633c543e000001060000026d04000506084873705f636f6e73656e7375735f736c6f747310536c6f740000040030010c7536340000090600000408050610000d060c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401e9010453000004001106012c42547265655365743c543e000011060420425472656553657404045401e90100040015060000001506000002e9010019060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011d06045300000400250601185665633c543e00001d06086463756d756c75735f70616c6c65745f78636d705f7175657565584f7574626f756e644368616e6e656c44657461696c730000140124726563697069656e74e90101185061726149640001147374617465210601344f7574626f756e6453746174650001347369676e616c735f6578697374200110626f6f6c00012c66697273745f696e6465784d03010c7531360001286c6173745f696e6465784d03010c75313600002106086463756d756c75735f70616c6c65745f78636d705f7175657565344f7574626f756e645374617465000108084f6b0000002453757370656e6465640001000025060000021d0600290600000408e9014d03002d060c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401080453000004003801185665633c543e00003106086463756d756c75735f70616c6c65745f78636d705f71756575653c5175657565436f6e6669674461746100000c014473757370656e645f7468726573686f6c6410010c75333200013864726f705f7468726573686f6c6410010c753332000140726573756d655f7468726573686f6c6410010c753332000035060c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144572726f72040454000114384261645175657565436f6e666967000004110153657474696e672074686520717565756520636f6e666967206661696c65642073696e6365206f6e65206f66206974732076616c7565732077617320696e76616c69642e40416c726561647953757370656e6465640001048c54686520657865637574696f6e20697320616c72656164792073757370656e6465642e38416c7265616479526573756d65640002048454686520657865637574696f6e20697320616c726561647920726573756d65642e74546f6f4d616e794163746976654f7574626f756e644368616e6e656c73000304b054686572652061726520746f6f206d616e7920616374697665206f7574626f756e64206368616e6e656c732e18546f6f4269670004045c546865206d65737361676520697320746f6f206269672e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e39060c2870616c6c65745f78636d1870616c6c65742c5175657279537461747573042c426c6f636b4e756d6265720110010c1c50656e64696e67100124726573706f6e646572d901014456657273696f6e65644c6f636174696f6e00014c6d617962655f6d617463685f717565726965723d0601644f7074696f6e3c56657273696f6e65644c6f636174696f6e3e0001306d617962655f6e6f74696679410601404f7074696f6e3c2875382c207538293e00011c74696d656f757410012c426c6f636b4e756d6265720000003c56657273696f6e4e6f7469666965720801186f726967696ed901014456657273696f6e65644c6f636174696f6e00012469735f616374697665200110626f6f6c000100145265616479080120726573706f6e73654906014456657273696f6e6564526573706f6e7365000108617410012c426c6f636b4e756d626572000200003d0604184f7074696f6e04045401d9010108104e6f6e6500000010536f6d650400d9010000010000410604184f7074696f6e0404540145060108104e6f6e6500000010536f6d650400450600000100004506000004080808004906080c78636d4456657273696f6e6564526573706f6e736500010c08563204008904013076323a3a526573706f6e73650002000856330400b904013076333a3a526573706f6e736500030008563404003101013076343a3a526573706f6e7365000400004d060000040810d9010051060000040c3028100055060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540159060453000004005d0601185665633c543e0000590600000408d90110005d0600000259060061060c2870616c6c65745f78636d1870616c6c65745456657273696f6e4d6967726174696f6e53746167650001105c4d696772617465537570706f7274656456657273696f6e0000005c4d69677261746556657273696f6e4e6f74696669657273000100504e6f7469667943757272656e745461726765747304006506013c4f7074696f6e3c5665633c75383e3e000200684d696772617465416e644e6f746966794f6c645461726765747300030000650604184f7074696f6e04045401380108104e6f6e6500000010536f6d65040038000001000069060000040c10001105006d060c2870616c6c65745f78636d1870616c6c65746852656d6f74654c6f636b656446756e6769626c655265636f72640848436f6e73756d65724964656e7469666965720198304d6178436f6e73756d6572730000100118616d6f756e74180110753132380001146f776e6572d901014456657273696f6e65644c6f636174696f6e0001186c6f636b6572d901014456657273696f6e65644c6f636174696f6e000124636f6e73756d657273710601d0426f756e6465645665633c28436f6e73756d65724964656e7469666965722c2075313238292c204d6178436f6e73756d6572733e000071060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017506045300000400790601185665633c543e000075060000040898180079060000027506007d060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454018106045300000400850601185665633c543e000081060000040818d90100850600000281060089060c2870616c6c65745f78636d1870616c6c6574144572726f720404540001602c556e726561636861626c650000085d0154686520646573697265642064657374696e6174696f6e2077617320756e726561636861626c652c2067656e6572616c6c7920626563617573652074686572652069732061206e6f20776179206f6620726f7574696e6718746f2069742e2c53656e644661696c757265000108410154686572652077617320736f6d65206f746865722069737375652028692e652e206e6f7420746f20646f207769746820726f7574696e672920696e2073656e64696e6720746865206d6573736167652ec8506572686170732061206c61636b206f6620737061636520666f7220627566666572696e6720746865206d6573736167652e2046696c74657265640002049c546865206d65737361676520657865637574696f6e206661696c73207468652066696c7465722e48556e776569676861626c654d657373616765000304b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e6044657374696e6174696f6e4e6f74496e7665727469626c65000404dc5468652064657374696e6174696f6e20604c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e14456d707479000504805468652061737365747320746f2062652073656e742061726520656d7074792e3843616e6e6f745265616e63686f720006043501436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468652064657374696e6174696f6e20636861696e2e34546f6f4d616e79417373657473000704c4546f6f206d616e79206173736574732068617665206265656e20617474656d7074656420666f72207472616e736665722e34496e76616c69644f726967696e000804784f726967696e20697320696e76616c696420666f722073656e64696e672e2842616456657273696f6e00090421015468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626520696e7465727072657465642e2c4261644c6f636174696f6e000a08410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e384e6f537562736372697074696f6e000b04bc546865207265666572656e63656420737562736372697074696f6e20636f756c64206e6f7420626520666f756e642e44416c726561647953756273637269626564000c041101546865206c6f636174696f6e20697320696e76616c69642073696e636520697420616c726561647920686173206120737562736372697074696f6e2066726f6d2075732e5843616e6e6f74436865636b4f757454656c65706f7274000d042901436f756c64206e6f7420636865636b2d6f7574207468652061737365747320666f722074656c65706f72746174696f6e20746f207468652064657374696e6174696f6e20636861696e2e284c6f7742616c616e6365000e044101546865206f776e657220646f6573206e6f74206f776e2028616c6c29206f662074686520617373657420746861742074686579207769736820746f20646f20746865206f7065726174696f6e206f6e2e30546f6f4d616e794c6f636b73000f04c0546865206173736574206f776e65722068617320746f6f206d616e79206c6f636b73206f6e207468652061737365742e4c4163636f756e744e6f74536f7665726569676e001004310154686520676976656e206163636f756e74206973206e6f7420616e206964656e7469666961626c6520736f7665726569676e206163636f756e7420666f7220616e79206c6f636174696f6e2e28466565734e6f744d65740011042901546865206f7065726174696f6e207265717569726564206665657320746f20626520706169642077686963682074686520696e69746961746f7220636f756c64206e6f74206d6565742e304c6f636b4e6f74466f756e64001204f4412072656d6f7465206c6f636b20776974682074686520636f72726573706f6e64696e67206461746120636f756c64206e6f7420626520666f756e642e14496e557365001304490154686520756e6c6f636b206f7065726174696f6e2063616e6e6f742073756363656564206265636175736520746865726520617265207374696c6c20636f6e73756d657273206f6620746865206c6f636b2e68496e76616c69644173736574556e6b6e6f776e52657365727665001504f0496e76616c69642061737365742c207265736572766520636861696e20636f756c64206e6f742062652064657465726d696e656420666f722069742e78496e76616c69644173736574556e737570706f72746564526573657276650016044501496e76616c69642061737365742c20646f206e6f7420737570706f72742072656d6f7465206173736574207265736572766573207769746820646966666572656e7420666565732072657365727665732e3c546f6f4d616e7952657365727665730017044901546f6f206d616e7920617373657473207769746820646966666572656e742072657365727665206c6f636174696f6e732068617665206265656e20617474656d7074656420666f72207472616e736665722e604c6f63616c457865637574696f6e496e636f6d706c6574650018047c4c6f63616c2058434d20657865637574696f6e20696e636f6d706c6574652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8d06085070616c6c65745f6d6573736167655f717565756524426f6f6b537461746504344d6573736167654f726967696e01e50100180114626567696e10012450616765496e64657800010c656e6410012450616765496e646578000114636f756e7410012450616765496e64657800014072656164795f6e65696768626f757273910601844f7074696f6e3c4e65696768626f7572733c4d6573736167654f726967696e3e3e0001346d6573736167655f636f756e7430010c75363400011073697a6530010c7536340000910604184f7074696f6e0404540195060108104e6f6e6500000010536f6d650400950600000100009506085070616c6c65745f6d6573736167655f7175657565284e65696768626f75727304344d6573736167654f726967696e01e5010008011070726576e50101344d6573736167654f726967696e0001106e657874e50101344d6573736167654f726967696e0000990600000408e50110009d06085070616c6c65745f6d6573736167655f71756575651050616765081053697a650110204865617053697a65000018012472656d61696e696e6710011053697a6500013872656d61696e696e675f73697a6510011053697a6500012c66697273745f696e64657810011053697a65000114666972737410011053697a650001106c61737410011053697a6500011068656170a106019c426f756e6465645665633c75382c20496e746f5533323c4865617053697a652c2053697a653e3e0000a1060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003801185665633c543e0000a5060c5070616c6c65745f6d6573736167655f71756575651870616c6c6574144572726f720404540001242c4e6f745265617061626c65000008490150616765206973206e6f74207265617061626c65206265636175736520697420686173206974656d732072656d61696e696e6720746f2062652070726f63657373656420616e64206973206e6f74206f6c641c656e6f7567682e184e6f50616765000104845061676520746f2062652072656170656420646f6573206e6f742065786973742e244e6f4d657373616765000204a8546865207265666572656e636564206d65737361676520636f756c64206e6f7420626520666f756e642e40416c726561647950726f6365737365640003040101546865206d6573736167652077617320616c72656164792070726f63657373656420616e642063616e6e6f742062652070726f63657373656420616761696e2e18517565756564000404ac546865206d6573736167652069732071756575656420666f722066757475726520657865637574696f6e2e48496e73756666696369656e74576569676874000504190154686572652069732074656d706f726172696c79206e6f7420656e6f7567682077656967687420746f20636f6e74696e756520736572766963696e67206d657373616765732e6054656d706f726172696c79556e70726f6365737361626c65000610a854686973206d6573736167652069732074656d706f726172696c7920756e70726f6365737361626c652e00590153756368206572726f7273206172652065787065637465642c20627574206e6f742067756172616e746565642c20746f207265736f6c7665207468656d73656c766573206576656e7475616c6c79207468726f756768247265747279696e672e2c517565756550617573656400070cec5468652071756575652069732070617573656420616e64206e6f206d6573736167652063616e2062652065786563757465642066726f6d2069742e001d01546869732063616e206368616e676520617420616e792074696d6520616e64206d6179207265736f6c766520696e20746865206675747572652062792072652d747279696e672e4c526563757273697665446973616c6c6f7765640008043101416e6f746865722063616c6c20697320696e2070726f677265737320616e64206e6565647320746f2066696e697368206265666f726520746869732063616c6c2063616e2068617070656e2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ea90600000408ad061000ad060c6070616c6c65745f73746f726167655f70726f7669646572731474797065733c53746f7261676550726f7669646572040454000108544261636b757053746f7261676550726f76696465720400b10601604261636b757053746f7261676550726f76696465723c543e0000004c4d61696e53746f7261676550726f76696465720400b50601584d61696e53746f7261676550726f76696465723c543e00010000b1060c6070616c6c65745f73746f726167655f70726f766964657273147479706573544261636b757053746f7261676550726f76696465720404540000200120636170616369747930014853746f7261676544617461556e69743c543e00013463617061636974795f7573656430014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e000110726f6f743401544d65726b6c655061747269636961526f6f743c543e0001506c6173745f63617061636974795f6368616e6765100144426c6f636b4e756d626572466f723c543e0001346f776e65725f6163636f756e74000130543a3a4163636f756e74496400013c7061796d656e745f6163636f756e74000130543a3a4163636f756e74496400014472657075746174696f6e5f77656967687410015c52657075746174696f6e576569676874547970653c543e0000b5060c6070616c6c65745f73746f726167655f70726f7669646572731474797065734c4d61696e53746f7261676550726f7669646572040454000020011c6275636b657473b90601284275636b6574733c543e000120636170616369747930014853746f7261676544617461556e69743c543e00013463617061636974795f7573656430014853746f7261676544617461556e69743c543e0001386d756c7469616464726573736573f50101d4426f756e6465645665633c4d756c7469416464726573733c543e2c204d61784d756c746941646472657373416d6f756e743c543e3e00012876616c75655f70726f700102014c56616c756550726f706f736974696f6e3c543e0001506c6173745f63617061636974795f6368616e6765100144426c6f636b4e756d626572466f723c543e0001346f776e65725f6163636f756e74000130543a3a4163636f756e74496400013c7061796d656e745f6163636f756e74000130543a3a4163636f756e7449640000b9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401bd06045300000400c10601185665633c543e0000bd060c6070616c6c65745f73746f726167655f70726f766964657273147479706573184275636b65740404540000180110726f6f743401544d65726b6c655061747269636961526f6f743c543e00011c757365725f6964000130543a3a4163636f756e7449640001186d73705f69643401604d61696e53746f7261676550726f766964657249643c543e00011c70726976617465200110626f6f6c000150726561645f6163636573735f67726f75705f6964110201704f7074696f6e3c543a3a5265616441636365737347726f757049643e00011073697a6530014853746f7261676544617461556e69743c543e0000c106000002bd0600c5060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000c9060c6070616c6c65745f73746f726167655f70726f7669646572731870616c6c6574144572726f7204045400016c44416c72656164795265676973746572656400000471014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020617320612053502062757420697320616c726561647920726567697374657265642061732061204d5350206f72204253502e485369676e55704e6f7452657175657374656400010459014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e207570207468617420776173206e6f74207265717565737465642070726576696f75736c792e505369676e55705265717565737450656e64696e670002048d014572726f72207468726f776e207768656e2061207573657220747269657320746f207265717565737420746f207369676e207570207768656e20697420616c7265616479206861732061207369676e20757020726571756573742070656e64696e672e384e6f4d756c7469416464726573730003040d014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020776974686f757420616e79206d756c7469616464726573732e4c496e76616c69644d756c74694164647265737300040491014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e20757020617320612053502062757420616e79206f66207468652070726f7669646564206d756c746961646472657373657320697320696e76616c69642e3453746f72616765546f6f4c6f7700050401024572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e207570206f72206368616e67652069747320636170616369747920746f2073746f7265206c6573732073746f72616765207468616e20746865206d696e696d756d207265717569726564206279207468652072756e74696d652e404e6f74456e6f75676842616c616e636500060431024572726f72207468726f776e207768656e2061207573657220646f6573206e6f74206861766520656e6f7567682062616c616e636520746f2070617920746865206465706f736974207468617420697420776f756c6420696e637572206279207369676e696e672075702061732061205350206f72206368616e67696e67206974732063617061636974792e4443616e6e6f74486f6c644465706f736974000704f9014572726f72207468726f776e207768656e207468652072756e74696d652063616e6e6f7420686f6c6420746865207265717569726564206465706f7369742066726f6d20746865206163636f756e7420746f2072656769737465722069742061732061205350206f72206368616e6765206974732063617061636974792e4453746f726167655374696c6c496e55736500080439014572726f72207468726f776e207768656e2061207573657220747269657320746f207369676e206f6666206173206120535020627574207374696c6c2068617320757365642073746f726167652e5452616e646f6d6e6573734e6f7456616c69645965740009048d014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e20757020627574207468652072616e646f6d6e65737320697320746f6f20667265736820746f2062652075736564207965742e505369676e55705265717565737445787069726564000a048d014572726f72207468726f776e207768656e2061207573657220747269657320746f20636f6e6669726d2061207369676e2075702062757420746f6f206d7563682074696d6520686173207061737365642073696e63652074686520726571756573742e784e657743617061636974794c6573735468616e5573656453746f72616765000b0451014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f206c657373207468616e2069747320757365642073746f726167652e804e65774361706163697479457175616c7343757272656e744361706163697479000c045d014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f207468652073616d652076616c756520697420616c7265616479206861732e544e6577436170616369747943616e7442655a65726f000d04c9014572726f72207468726f776e207768656e2061207573657220747269657320746f206368616e67652069747320636170616369747920746f207a65726f20287468657265206172652073706563696669632065787472696e7369637320746f207369676e206f66662061732061205350292e4c4e6f74456e6f75676854696d65506173736564000e04d9014572726f72207468726f776e207768656e206120535020747269657320746f206368616e6765206974732063617061636974792062757420697420686173206e6f74206265656e20656e6f7567682074696d652073696e636520746865206c6173742074696d65206974206368616e6765642069742e944e65775573656443617061636974794578636565647353746f726167654361706163697479000f04c5014572726f72207468726f776e207768656e206120535020747269657320746f206368616e6765206974732063617061636974792062757420746865206e6577206361706163697479206973206e6f7420656e6f75676820746f2073746f72652074686520757365642073746f726167652e344e6f745265676973746572656400100465014572726f72207468726f776e207768656e2061207573657220747269657320746f20696e746572616374206173206120535020627574206973206e6f7420726567697374657265642061732061204d5350206f72204253502e204e6f55736572496400110431014572726f72207468726f776e207768656e20747279696e6720746f20676574206120726f6f742066726f6d2061204d535020776974686f75742070617373696e67206120557365722049442e284e6f4275636b6574496400120439014572726f72207468726f776e207768656e20747279696e6720746f20676574206120726f6f742066726f6d2061204d535020776974686f75742070617373696e672061204275636b65742049442e6c537052656769737465726564427574446174614e6f74466f756e64001304d1014572726f72207468726f776e207768656e206120757365722068617320612053502049442061737369676e656420746f2069742062757420746865205350206461746120646f6573206e6f7420657869737420696e2073746f726167652028496e636f6e73697374656e6379206572726f72292e384275636b65744e6f74466f756e64001404d84572726f72207468726f776e207768656e2061206275636b6574204944206973206e6f7420666f756e6420696e2073746f726167652e4c4275636b6574416c7265616479457869737473001504e04572726f72207468726f776e207768656e2061206275636b657420494420616c72656164792065786973747320696e2073746f726167652e5c417070656e644275636b6574546f4d73704661696c656400160445014572726f72207468726f776e207768656e2061206275636b657420494420636f756c64206e6f7420626520616464656420746f20746865206c697374206f66206275636b657473206f662061204d53502e5050726f76696465724e6f74536c61736861626c650017043d014572726f72207468726f776e207768656e20616e20617474656d707420776173206d61646520746f20736c61736820616e20756e736c61736861626c652053746f726167652050726f76696465722e68496e76616c6964456e636f64656446696c654d6574616461746100180475014572726f72207468726f776e207768656e206661696c696e6720746f206465636f646520746865206d657461646174612066726f6d206120726563656976656420747269652076616c75652074686174207761732072656d6f7665642e5c496e76616c6964456e636f6465644163636f756e74496400190451014572726f72207468726f776e207768656e206661696c696e6720746f206465636f646520746865206f776e6572204163636f756e742049442066726f6d20746865207265636569766564206d657461646174612e545061796d656e7453747265616d4e6f74466f756e64001a0421014572726f72207468726f776e207768656e20747279696e6720746f207570646174652061207061796d656e742073747265616d207468617420646f6573206e6f742065786973742e044901546865206572726f727320746861742063616e206265207468726f776e20627920746869732070616c6c657420746f20696e666f726d2075736572732061626f757420776861742077656e742077726f6e67cd060c4870616c6c65745f66696c655f73797374656d1474797065735853746f72616765526571756573744d6574616461746104045400003001307265717565737465645f61741001345469636b4e756d6265723c543e0001146f776e6572000130543a3a4163636f756e7449640001246275636b65745f69643401384275636b65744964466f723c543e0001206c6f636174696f6e1502013c46696c654c6f636174696f6e3c543e00012c66696e6765727072696e7434013846696e6765727072696e743c543e00011073697a6530013853746f72616765446174613c543e00010c6d7370d10601804f7074696f6e3c2850726f76696465724964466f723c543e2c20626f6f6c293e000134757365725f706565725f696473f5010128506565724964733c543e00013c646174615f7365727665725f737073d90601e4426f756e6465645665633c50726f76696465724964466f723c543e2c204d61784273707350657253746f72616765526571756573743c543e3e000134627370735f72657175697265641001605265706c69636174696f6e546172676574547970653c543e000138627370735f636f6e6669726d65641001605265706c69636174696f6e546172676574547970653c543e000140627370735f766f6c756e7465657265641001605265706c69636174696f6e546172676574547970653c543e0000d10604184f7074696f6e04045401d5060108104e6f6e6500000010536f6d650400d5060000010000d50600000408342000d9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000dd0600000408343400e1060c4870616c6c65745f66696c655f73797374656d1474797065736853746f7261676552657175657374427370734d657461646174610404540000040124636f6e6669726d6564200110626f6f6c0000e5060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e0000e9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401ed06045300000400f10601185665633c543e0000ed0600000408003400f106000002ed0600f5060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401dd06045300000400f90601185665633c543e0000f906000002dd0600fd060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401dd06045300000400f90601185665633c543e000001070000040810300005070c4870616c6c65745f66696c655f73797374656d147479706573644d6f76654275636b6574526571756573744d657461646174610404540000040124726571756573746572000130543a3a4163636f756e744964000009070c4870616c6c65745f66696c655f73797374656d1870616c6c6574144572726f720404540001f47c53746f7261676552657175657374416c726561647952656769737465726564000004d853746f72616765207265717565737420616c7265616479207265676973746572656420666f722074686520676976656e2066696c652e5853746f72616765526571756573744e6f74466f756e64000104c853746f726167652072657175657374206e6f74207265676973746572656420666f722074686520676976656e2066696c652e6053746f72616765526571756573744e6f745265766f6b656400020415014f7065726174696f6e206e6f7420616c6c6f776564207768696c65207468652073746f726167652072657175657374206973206e6f74206265696e67207265766f6b65642e5053746f7261676552657175657374457869737473000304dc4f7065726174696f6e206e6f7420616c6c6f776564207768696c65207468652073746f726167652072657175657374206578697374732e745265706c69636174696f6e54617267657443616e6e6f7442655a65726f000404885265706c69636174696f6e207461726765742063616e6e6f74206265207a65726f2e644273707352657175697265644578636565647354617267657400050411014253507320726571756972656420666f722073746f7261676520726571756573742063616e6e6f742065786365656420746865206d6178696d756d20616c6c6f7765642e1c4e6f7441427370000604544163636f756e74206973206e6f742061204253502e1c4e6f74414d7370000704544163636f756e74206973206e6f742061204d53502e184e6f74415370000804504163636f756e74206973206e6f7420612053502e444273704e6f74566f6c756e746565726564000904c042535020686173206e6f7420766f6c756e74656572656420746f2073746f72652074686520676976656e2066696c652e3c4273704e6f74436f6e6669726d6564000a04b442535020686173206e6f7420636f6e6669726d65642073746f72696e672074686520676976656e2066696c652e4c427370416c7265616479436f6e6669726d6564000b04c44253502068617320616c726561647920636f6e6669726d65642073746f72696e672074686520676976656e2066696c652e8c53746f726167655265717565737442737073526571756972656446756c66696c6c6564000c04f44e756d626572206f66204253507320726571756972656420666f722073746f72616765207265717565737420686173206265656e20726561636865642e54427370416c7265616479566f6c756e746565726564000d04c042535020616c726561647920766f6c756e74656572656420746f2073746f72652074686520676976656e2066696c652e74496e73756666696369656e74417661696c61626c654361706163697479000e04ec535020646f6573206e6f74206861766520656e6f7567682073746f7261676520636170616369747920746f2073746f7265207468652066696c652ea0556e65787065637465644e756d6265724f6652656d6f766564566f6c756e74656572656442737073000f0485014e756d626572206f662072656d6f766564204253507320766f6c756e7465657265642066726f6d2073746f7261676520726571756573742070726566697820646964206e6f74206d6174636820746865206578706563746564206e756d6265722e9053746f7261676552657175657374457870697265644e6f536c6f74417661696c61626c6500100431014e6f20736c6f7420617661696c61626c6520666f756e6420696e20626c6f636b7320746f20696e736572742073746f7261676520726571756573742065787069726174696f6e2074696d652e6c53746f72616765526571756573744e6f74417574686f72697a6564001104b44e6f7420617574686f72697a656420746f2064656c657465207468652073746f7261676520726571756573742e544d6178426c6f636b4e756d6265725265616368656400120859014572726f72206372656174656420696e20323032342e20496620796f752073656520746869732c20796f75206172652077656c6c206265796f6e64207468652073696e67756c617269747920616e642073686f756c648070726f6261626c792073746f70207573696e6720746869732070616c6c65742e444661696c6564546f456e636f6465427370001304844661696c656420746f20656e636f64652042535020696420617320736c6963652e644661696c6564546f456e636f646546696e6765727072696e74001404984661696c656420746f20656e636f64652066696e6765727072696e7420617320736c6963652e5c4661696c6564546f4465636f64655468726573686f6c640015046c4661696c656420746f206465636f6465207468726573686f6c642e3841626f76655468726573686f6c640016049042535020646964206e6f742073756363656564207468726573686f6c6420636865636b2e605468726573686f6c6441726974686d657469634572726f72001704a841726974686d65746963206572726f7220696e207468726573686f6c642063616c63756c6174696f6e2e504661696c656454797065436f6e76657273696f6e001804904661696c656420746f20636f6e7665727420746f207072696d697469766520747970652e344469766964656442795a65726f0019043044697669646564206279203068496d706f737369626c654661696c6564546f47657456616c7565001a04c44661696c656420746f206765742076616c7565207768656e206a75737420636865636b656420697420657869737465642e484275636b657449734e6f7450726976617465001b041d014275636b6574206973206e6f7420707269766174652e2043616c6c20607570646174655f6275636b65745f707269766163796020746f206d616b6520697420707269766174652e384275636b65744e6f74466f756e64001c04544275636b657420646f6573206e6f74206578697374384e6f744275636b65744f776e6572001d0411014f7065726174696f6e206661696c6564206265636175736520746865206163636f756e74206973206e6f7420746865206f776e6572206f6620746865206275636b65742e5050726f7669646572526f6f744e6f74466f756e64001e047c526f6f74206f66207468652070726f7669646572206e6f7420666f756e642e6445787065637465644e6f6e496e636c7573696f6e50726f6f66001f0415014661696c656420746f207665726966792070726f6f663a20726571756972656420746f2070726f7669646520612070726f6f66206f66206e6f6e2d696e636c7573696f6e2e584578706563746564496e636c7573696f6e50726f6f6600200405014661696c656420746f207665726966792070726f6f663a20726571756972656420746f2070726f7669646520612070726f6f66206f6620696e636c7573696f6e2e58496e76616c696446696c654b65794d65746164617461002104c84d6574616461746120646f6573206e6f7420636f72726573706f6e6420746f2065787065637465642066696c65206b65792e5c5468726573686f6c6442656c6f774173796d70746f7465002204d0425350732061737369676e6d656e74207468726573686f6c642063616e6e6f742062652062656c6f77206173796d70746f74652e304e6f7446696c654f776e6572002304d4556e617574686f72697a6564206f7065726174696f6e2c207369676e657220646f6573206e6f74206f776e207468652066696c652e7446696c654b6579416c726561647950656e64696e6744656c6574696f6e0024048846696c65206b657920616c72656164792070656e64696e672064656c6574696f6e2e944d61785573657250656e64696e6744656c6574696f6e526571756573747352656163686564002504d44d6178206e756d626572206f6620757365722070656e64696e672064656c6574696f6e20726571756573747320726561636865642e4c4d73704e6f7453746f72696e674275636b6574002604f8556e617574686f72697a6564206f7065726174696f6e2c207369676e6572206973206e6f7420616e204d5350206f6620746865206275636b65742069642e6446696c654b65794e6f7450656e64696e6744656c6574696f6e002704c046696c65206b6579206e6f7420666f756e6420696e2070656e64696e672064656c6574696f6e2072657175657374732e5046696c6553697a6543616e6e6f7442655a65726f0028046446696c652073697a652063616e6e6f74206265207a65726f2e6c4e6f476c6f62616c52657075746174696f6e576569676874536574002904804e6f20676c6f62616c2072657075746174696f6e20776569676874207365742e704d6178696d756d5468726573686f6c6443616e6e6f7442655a65726f002a04844d6178696d756d207468726573686f6c642063616e6e6f74206265207a65726f2e9c5469636b52616e6765546f4d6178696d756d5468726573686f6c6443616e6e6f7442655a65726f002b04bc5469636b2072616e676520746f206d6178696d756d207468726573686f6c642063616e6e6f74206265207a65726f2e8450656e64696e6753746f7053746f72696e67526571756573744e6f74466f756e64002c049c50656e64696e672073746f702073746f72696e672072657175657374206e6f7420666f756e642e7c4d696e57616974466f7253746f7053746f72696e674e6f7452656163686564002d0479014d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e207468652072657175657374206f70656e696e6720616e64206265696e672061626c6520746f20636f6e6669726d206974206e6f7420726561636865642e9850656e64696e6753746f7053746f72696e6752657175657374416c7265616479457869737473002e04b050656e64696e672073746f702073746f72696e67207265717565737420616c7265616479206578697374732e40557365724e6f74496e736f6c76656e74002f04a9014120535020747269656420746f2073746f702073746f72696e672066696c65732066726f6d2061207573657220746861742077617320737570706f7365646c7920696e736f6c76656e742c20627574207468652075736572206973206e6f7420696e736f6c76656e742e384e6f7453656c65637465644d7370003004a101546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f726167652072657175657374206973206e6f7420746865206f6e652073656c656374656420746f2073746f72652069742e4c4d7370416c7265616479436f6e6669726d6564003104ad01546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f72616765207265717565737420746861742069742068617320616c726561647920636f6e6669726d656420746f2073746f72652e4452657175657374576974686f75744d73700032049901546865204d535020697320747279696e6720746f20636f6e6669726d20746f2073746f726520612066696c652066726f6d20612073746f726167652072657175657374207468617420646f6573206e6f7420686176652061204d53502061737369676e65642e5c4d7370416c726561647953746f72696e674275636b657400330498546865204d535020697320616c72656164792073746f72696e6720746865206275636b65742e644d6f76654275636b6574526571756573744e6f74466f756e64003404a44d6f7665206275636b65742072657175657374206e6f7420666f756e6420696e2073746f726167652e484275636b657449734265696e674d6f766564003504cc416374696f6e206e6f7420616c6c6f776564207768696c6520746865206275636b6574206973206265696e67206d6f7665642e50427370416c726561647944617461536572766572003604e442535020697320616c7265616479206120646174612073657276657220666f7220746865206d6f7665206275636b657420726571756573742e5842737044617461536572766572734578636565646564003704f4546f6f206d616e7920726567697374657265642064617461207365727665727320666f7220746865206d6f7665206275636b657420726571756573742e7c46696c654d6574616461746150726f63657373696e67517565756546756c6c003804910154686520626f756e64656420766563746f72207468617420686f6c64732066696c65206d6574616461746120746f2070726f636573732069742069732066756c6c206275742074686572652773207374696c6c206d6f726520746f2070726f636573732e54546f6f4d616e794261746368526573706f6e73657300390490546f6f206d616e7920626174636820726573706f6e73657320746f2070726f636573732e78546f6f4d616e7953746f7261676552657175657374526573706f6e736573003a048c546f6f206d616e792073746f72616765207265717565737420726573706f6e7365732e68496e76616c69644275636b6574496446696c654b657950616972003b049c4275636b657420696420616e642066696c65206b6579207061697220697320696e76616c69642e84496e636f6e73697374656e7453746174654b6579416c7265616479457869737473003c04c44b657920616c72656164792065786973747320696e206d617070696e67207768656e2069742073686f756c64206e6f742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e0d070000040810340011070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401340453000004002d0201185665633c543e000015070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454018d02045300000400990201185665633c543e000019070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401340453000004001d07012c42547265655365743c543e00001d070420425472656553657404045401340004002d0200000021070c3473705f61726974686d65746963287065725f7468696e67731c50657262696c6c0000040010010c753332000025070c5070616c6c65745f70726f6f66735f6465616c65721870616c6c6574144572726f720404540001582c4e6f7450726f76696465720000083847656e6572616c206572726f7273c45468652070726f6f66207375626d6974746572206973206e6f74206120726567697374657265642050726f76696465722e5c4368616c6c656e67657351756575654f766572666c6f7700010c70606368616c6c656e6765602065787472696e736963206572726f72730901546865204368616c6c656e67657351756575652069732066756c6c2e204e6f206d6f7265206d616e75616c206368616c6c656e6765732063616e206265206d616465e4756e74696c20736f6d65206f6620746865206368616c6c656e67657320696e207468652071756575652061726520646973706174636865642e7c5072696f726974794368616c6c656e67657351756575654f766572666c6f770002083101546865205072696f726974794368616c6c656e67657351756575652069732066756c6c2e204e6f206d6f7265207072696f72697479206368616c6c656e6765732063616e206265206d616465e4756e74696c20736f6d65206f6620746865206368616c6c656e67657320696e207468652071756575652061726520646973706174636865642e3c4665654368617267654661696c6564000304e05468652066656520666f72207375626d697474696e672061206368616c6c656e676520636f756c64206e6f7420626520636861726765642e38456d7074794b657950726f6f66730004087c607375626d69745f70726f6f66602065787472696e736963206572726f727388546865726520617265206e6f206b65792070726f6f6673207375626d69747465642e5050726f7669646572526f6f744e6f74466f756e64000504b454686520726f6f7420666f72207468652050726f766964657220636f756c64206e6f7420626520666f756e642e205a65726f526f6f7400060ce850726f7669646572206973207375626d697474696e6720612070726f6f66207768656e207468657920686176652061207a65726f20726f6f742e390150726f7669646572732077697468207a65726f20726f6f747320617265206e6f742070726f766964696e6720616e7920736572766963652c20736f20746865792073686f756c64206e6f74206265487375626d697474696e672070726f6f66732e704e6f5265636f72644f664c6173745375626d697474656450726f6f660007142d0150726f7669646572206973207375626d697474696e6720612070726f6f6620627574207468657265206973206e6f207265636f7264206f6620746865206c617374207469636b2074686579587375626d697474656420612070726f6f6620666f722e390150726f7669646572732077686f2061726520726571756972656420746f207375626d69742070726f6f66732073686f756c6420616c7761797320686176652061207265636f7264206f662074686535016c617374207469636b2074686579207375626d697474656420612070726f6f6620666f722c206f7468657277697365206974206d65616e73207468657920686176656e277420737461727465648c70726f766964696e67207365727669636520666f7220616e792075736572207965742e5450726f76696465725374616b654e6f74466f756e64000804985468652070726f7669646572207374616b6520636f756c64206e6f7420626520666f756e642e245a65726f5374616b65000904dc50726f7669646572206973207375626d697474696e6720612070726f6f6620627574207468656972207374616b65206973207a65726f2e605374616b65436f756c644e6f744265436f6e766572746564000a081101546865207374616b65642062616c616e6365206f66207468652050726f766964657220636f756c64206e6f7420626520636f6e76657274656420746f206075313238602e5901546869732073686f756c64206e6f7420626520706f737369626c652c20617320746865206042616c616e63656020747970652073686f756c6420626520616e20756e7369676e656420696e746567657220747970652e604368616c6c656e6765735469636b4e6f7452656163686564000b04e050726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b20696e20746865206675747572652e504368616c6c656e6765735469636b546f6f4f6c64000c08510150726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b206265666f726520746865206c617374207469636b20746869732070616c6c6574207265676973746572733c6368616c6c656e67657320666f722e544368616c6c656e6765735469636b546f6f4c617465000d08450150726f7669646572206973207375626d697474696e6720612070726f6f6620666f722061207469636b20746f6f206c6174652c20692e652e207468617420746865206368616c6c656e676573207469636b450169732067726561746572206f7220657175616c207468616e20606368616c6c656e6765735f7469636b60202b2060543a3a4368616c6c656e67655469636b73546f6c6572616e63653a3a6765742829602e30536565644e6f74466f756e64000e0ca4546865207365656420666f7220746865207469636b20636f756c64206e6f7420626520666f756e642e5101546869732073686f756c64206e6f7420626520706f737369626c6520666f722061207469636b2077697468696e2074686520604368616c6c656e6765486973746f72794c656e677468602072616e67652c20617301017365656473206172652067656e65726174656420666f7220616c6c207469636b732c20616e642073746f7265642077697468696e20746869732072616e67652e70436865636b706f696e744368616c6c656e6765734e6f74466f756e64000f0ca4436865636b706f696e74206368616c6c656e676573206e6f7420666f756e6420696e20626c6f636b2e5d01546869732073686f756c64206f6e6c7920626520706f737369626c6520696620605469636b546f436865636b706f696e744368616c6c656e676573602069732064657265666572656e63656420666f722061207469636b7874686174206973206e6f74206120636865636b706f696e74207469636b2e74466f7265737450726f6f66566572696669636174696f6e4661696c656400100cd854686520666f726573742070726f6f66207375626d6974746564206279207468652050726f766964657220697320696e76616c69642e49015468697320636f756c642062652062656361757365207468652070726f6f66206973206e6f742076616c696420666f722074686520726f6f742c206f722062656361757365207468652070726f6f662069739c6e6f742073756666696369656e7420666f7220746865206368616c6c656e676573206d6164652e404b657950726f6f664e6f74466f756e6400110861015468657265206973206174206c65617374206f6e65206b65792070726f76656e20696e2074686520666f726573742070726f6f662c207468617420646f6573206e6f742068617665206120636f72726573706f6e64696e67286b65792070726f6f662e684b657950726f6f66566572696669636174696f6e4661696c656400120cc441206b65792070726f6f66207375626d6974746564206279207468652050726f766964657220697320696e76616c69642e6d015468697320636f756c642062652062656361757365207468652070726f6f66206973206e6f742076616c696420666f722074686520726f6f74206f662074686174206b65792c206f722062656361757365207468652070726f6f66a86973206e6f742073756666696369656e7420666f7220746865206368616c6c656e676573206d6164652e484661696c6564546f4170706c7944656c7461001304dc4661696c656420746f206170706c792064656c746120746f2074686520666f726573742070726f6f66207061727469616c20747269652e944661696c6564546f55706461746550726f766964657241667465724b657952656d6f76616c001404ec4661696c656420746f20757064617465207468652070726f76696465722061667465722061206b65792072656d6f76616c206d75746174696f6e2e6c546f6f4d616e7956616c696450726f6f665375626d6974746572730015044501546865206c696d6974206f662050726f76696465727320746861742063616e207375626d697420612070726f6f6620696e20612073696e676c65207469636b20686173206265656e20726561636865642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2907000004083410002d070000040834000031070c5870616c6c65745f7061796d656e745f73747265616d73147479706573584669786564526174655061796d656e7453747265616d04045400001001107261746518013042616c616e63654f663c543e0001446c6173745f636861726765645f7469636b100144426c6f636b4e756d626572466f723c543e000130757365725f6465706f73697418013042616c616e63654f663c543e0001446f75745f6f665f66756e64735f7469636b110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000035070c5870616c6c65745f7061796d656e745f73747265616d731474797065736044796e616d6963526174655061796d656e7453747265616d040454000010013c616d6f756e745f70726f766964656430014c556e69747350726f7669646564466f723c543e00017470726963655f696e6465785f7768656e5f6c6173745f6368617267656418013042616c616e63654f663c543e000130757365725f6465706f73697418013042616c616e63654f663c543e0001446f75745f6f665f66756e64735f7469636b110201644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000039070c5870616c6c65745f7061796d656e745f73747265616d731474797065736850726f76696465724c61737443686172676561626c65496e666f04045400000801506c6173745f63686172676561626c655f7469636b100144426c6f636b4e756d626572466f723c543e00012c70726963655f696e64657818013042616c616e63654f663c543e00003d070c5870616c6c65745f7061796d656e745f73747265616d731870616c6c6574144572726f72040454000140685061796d656e7453747265616d416c726561647945786973747300000469014572726f72207468726f776e207768656e20612075736572206f6620746869732070616c6c657420747269657320746f206164642061207061796d656e742073747265616d207468617420616c7265616479206578697374732e545061796d656e7453747265616d4e6f74466f756e64000104bd014572726f72207468726f776e207768656e20612075736572206f6620746869732070616c6c657420747269657320746f207570646174652c2072656d6f7665206f72206368617267652061207061796d656e742073747265616d207468617420646f6573206e6f742065786973742e304e6f744150726f766964657200020471014572726f72207468726f776e207768656e2061207573657220747269657320746f206368617267652061207061796d656e742073747265616d20616e642069742773206e6f74206120726567697374657265642050726f76696465726850726f7669646572496e636f6e73697374656e63794572726f7200030435014572726f72207468726f776e207768656e206661696c696e6720746f2067657420746865207061796d656e74206163636f756e74206f66206120726567697374657265642050726f76696465724443616e6e6f74486f6c644465706f736974000404a9014572726f72207468726f776e207768656e207468652073797374656d2063616e277420686f6c642066756e64732066726f6d2074686520557365722061732061206465706f73697420666f72206372656174696e672061206e6577207061796d656e742073747265616d5055706461746552617465546f53616d655261746500050495014572726f72207468726f776e207768656e20747279696e6720746f20757064617465207468652072617465206f6620612066697865642d72617465207061796d656e742073747265616d20746f207468652073616d652072617465206173206265666f726560557064617465416d6f756e74546f53616d65416d6f756e74000604d1014572726f72207468726f776e207768656e20747279696e6720746f207570646174652074686520616d6f756e742070726f7669646564206f6620612064796e616d69632d72617465207061796d656e742073747265616d20746f207468652073616d6520616d6f756e74206173206265666f7265385261746543616e7442655a65726f000704bd024572726f72207468726f776e207768656e20747279696e6720746f206372656174652061206e65772066697865642d72617465207061796d656e742073747265616d207769746820726174652030206f7220757064617465207468652072617465206f6620616e206578697374696e67206f6e6520746f2030202873686f756c64207573652072656d6f76655f66697865645f726174655f7061796d656e745f73747265616d20696e73746561642960416d6f756e7450726f766964656443616e7442655a65726f00080425034572726f72207468726f776e207768656e20747279696e6720746f206372656174652061206e65772064796e616d69632d72617465207061796d656e742073747265616d207769746820616d6f756e742070726f76696465642030206f72207570646174652074686520616d6f756e742070726f7669646564206f6620616e206578697374696e67206f6e6520746f2030202873686f756c64207573652072656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d20696e737465616429904c61737443686172676564477265617465725468616e4c61737443686172676561626c6500090425024572726f72207468726f776e207768656e20746865207469636b206e756d626572206f66207768656e20746865207061796d656e742073747265616d20776173206c61737420636861726765642069732067726561746572207468616e20746865207469636b206e756d626572206f6620746865206c6173742063686172676561626c65207469636b80496e76616c69644c61737443686172676561626c65426c6f636b4e756d626572000a04bd024572726f72207468726f776e207768656e20746865206e6577206c6173742063686172676561626c65207469636b206e756d626572207468617420697320747279696e6720746f206265207365742069732067726561746572207468616e207468652063757272656e74207469636b206e756d626572206f7220736d616c6c6572207468616e207468652070726576696f7573206c6173742063686172676561626c65207469636b206e756d6265727c496e76616c69644c61737443686172676561626c655072696365496e646578000b04bd024572726f72207468726f776e207768656e20746865206e6577206c6173742063686172676561626c6520707269636520696e646578207468617420697320747279696e6720746f206265207365742069732067726561746572207468616e207468652063757272656e7420707269636520696e646578206f7220736d616c6c6572207468616e207468652070726576696f7573206c6173742063686172676561626c6520707269636520696e646578384368617267654f766572666c6f77000c046d014572726f72207468726f776e207768656e206368617267696e672061207061796d656e742073747265616d20776f756c6420726573756c7420696e20616e206f766572666c6f77206f66207468652062616c616e636520747970654055736572576974686f757446756e6473000d047d014572726f72207468726f776e207768656e20747279696e6720746f206f706572617465207768656e20746865205573657220686173206265656e20666c616767656420666f72206e6f7420686176696e6720656e6f7567682066756e64732e70557365724e6f74466c61676765644173576974686f757446756e6473000e04e9014572726f72207468726f776e207768656e20612075736572207468617420686173206e6f74206265656e20666c616767656420617320776974686f75742066756e647320747269657320746f20757365207468652065787472696e73696320746f2070617920697473206f75747374616e64696e6720646562745c436f6f6c646f776e506572696f644e6f74506173736564000f04b5014572726f72207468726f776e207768656e2061207573657220747269657320746f20636c6561722074686520666c6167206f66206265696e6720776974686f75742066756e6473206265666f72652074686520636f6f6c646f776e20706572696f642068617320706173736564044901546865206572726f727320746861742063616e206265207468726f776e20627920746869732070616c6c657420746f20696e666f726d2075736572732061626f757420776861742077656e742077726f6e6741070c4870616c6c65745f6275636b65745f6e6674731870616c6c6574144572726f72040454000110484275636b657449734e6f74507269766174650000048d014275636b6574206973206e6f7420707269766174652e2043616c6c20607570646174655f6275636b65745f70726976616379602066726f6d207468652066696c652073797374656d2070616c6c657420746f206d616b6520697420707269766174652e384e6f744275636b65744f776e65720001049c4163636f756e74206973206e6f7420746865206f776e6572206f6620746865206275636b65742e644e6f436f72726573706f6e64696e67436f6c6c656374696f6e000204dd014e6f20636f6c6c656374696f6e20636f72726573706f6e64696e6720746f20746865206275636b65742e2043616c6c20607570646174655f6275636b65745f70726976616379602066726f6d207468652066696c652073797374656d2070616c6c657420746f206d616b6520697420707269766174652e60436f6e766572744279746573546f426f756e6465645665630003049c4661696c656420746f20636f6e7665727420627974657320746f2060426f756e64656456656360048054686520604572726f726020656e756d206f6620746869732070616c6c65742e45070c2c70616c6c65745f6e66747314747970657344436f6c6c656374696f6e44657461696c7308244163636f756e7449640100384465706f73697442616c616e63650118001801146f776e65720001244163636f756e7449640001346f776e65725f6465706f7369741801384465706f73697442616c616e63650001146974656d7310010c7533320001386974656d5f6d657461646174617310010c7533320001306974656d5f636f6e6669677310010c7533320001286174747269627574657310010c753332000049070000040c001010004d070000040800100051070000040810000055070c2c70616c6c65745f6e66747314747970657320426974466c616773040454015907000400080138436f6c6c656374696f6e526f6c65000059070c2c70616c6c65745f6e66747314747970657338436f6c6c656374696f6e526f6c6500010c184973737565720001001c467265657a65720002001441646d696e000400005d070c2c70616c6c65745f6e6674731474797065732c4974656d44657461696c730c244163636f756e74496401001c4465706f73697401610724417070726f76616c73016507000c01146f776e65720001244163636f756e744964000124617070726f76616c7365070124417070726f76616c7300011c6465706f7369746107011c4465706f736974000061070c2c70616c6c65745f6e6674731474797065732c4974656d4465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e740001244163636f756e744964000118616d6f756e741801384465706f73697442616c616e6365000065070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b010004560111020453000004006907013842547265654d61703c4b2c20563e00006907042042547265654d617008044b010004560111020004006d070000006d070000027107007107000004080011020075070c2c70616c6c65745f6e66747314747970657348436f6c6c656374696f6e4d65746164617461081c4465706f73697401182c537472696e674c696d6974000008011c6465706f73697418011c4465706f73697400011064617461b502016c426f756e6465645665633c75382c20537472696e674c696d69743e000079070c2c70616c6c65745f6e667473147479706573304974656d4d65746164617461081c4465706f736974017d072c537472696e674c696d6974000008011c6465706f7369747d07011c4465706f73697400011064617461b502016c426f756e6465645665633c75382c20537472696e674c696d69743e00007d070c2c70616c6c65745f6e6674731474797065734c4974656d4d657461646174614465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000118616d6f756e741801384465706f73697442616c616e63650000810700000410101102c102bd0200850700000408b50289070089070c2c70616c6c65745f6e667473147479706573404174747269627574654465706f73697408384465706f73697442616c616e63650118244163636f756e74496401000008011c6163636f756e749c01444f7074696f6e3c4163636f756e7449643e000118616d6f756e741801384465706f73697442616c616e636500008d0700000408189c0091070c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e646564425472656553657408045401000453000004009507012c42547265655365743c543e00009507042042547265655365740404540100000400a400000099070c2c70616c6c65745f6e6674731474797065732c50656e64696e67537761701030436f6c6c656374696f6e49640110184974656d49640110584974656d507269636557697468446972656374696f6e01c90220446561646c696e65011000100148646573697265645f636f6c6c656374696f6e100130436f6c6c656374696f6e4964000130646573697265645f6974656d110201384f7074696f6e3c4974656d49643e0001147072696365c50201784f7074696f6e3c4974656d507269636557697468446972656374696f6e3e000120646561646c696e65100120446561646c696e6500009d070c2c70616c6c65745f6e66747314747970657320426974466c61677304045401a10700040030013450616c6c6574466561747572650000a1070c2c70616c6c65745f6e6674731474797065733450616c6c6574466561747572650001101c54726164696e67000100284174747269627574657300020024417070726f76616c7300040014537761707300080000a5070c2c70616c6c65745f6e6674731870616c6c6574144572726f720804540004490001b4304e6f5065726d697373696f6e000004e8546865207369676e696e67206163636f756e7420686173206e6f207065726d697373696f6e20746f20646f20746865206f7065726174696f6e2e44556e6b6e6f776e436f6c6c656374696f6e0001047454686520676976656e206974656d20494420697320756e6b6e6f776e2e34416c7265616479457869737473000204b8546865206974656d2049442068617320616c7265616479206265656e207573656420666f7220616e206974656d2e3c417070726f76616c45787069726564000304390154686520617070726f76616c20686164206120646561646c696e65207468617420657870697265642c20736f2074686520617070726f76616c2069736e27742076616c696420616e796d6f72652e2857726f6e674f776e6572000404e8546865206f776e6572207475726e6564206f757420746f20626520646966666572656e7420746f2077686174207761732065787065637465642e284261645769746e6573730005041501546865207769746e657373206461746120676976656e20646f6573206e6f74206d61746368207468652063757272656e74207374617465206f662074686520636861696e2e44436f6c6c656374696f6e4964496e5573650006047c436f6c6c656374696f6e20494420697320616c72656164792074616b656e2e504974656d734e6f6e5472616e7366657261626c65000704c84974656d732077697468696e207468617420636f6c6c656374696f6e20617265206e6f6e2d7472616e7366657261626c652e2c4e6f7444656c65676174650008049c5468652070726f7669646564206163636f756e74206973206e6f7420612064656c65676174652e3457726f6e6744656c6567617465000904f45468652064656c6567617465207475726e6564206f757420746f20626520646966666572656e7420746f2077686174207761732065787065637465642e28556e617070726f766564000a04c44e6f20617070726f76616c20657869737473207468617420776f756c6420616c6c6f7720746865207472616e736665722e28556e6163636570746564000b041901546865206e616d6564206f776e657220686173206e6f74207369676e6564206f776e65727368697020616363657074616e6365206f662074686520636f6c6c656374696f6e2e284974656d4c6f636b6564000c0498546865206974656d206973206c6f636b656420286e6f6e2d7472616e7366657261626c65292e504c6f636b65644974656d41747472696275746573000d04744974656d2773206174747269627574657320617265206c6f636b65642e684c6f636b6564436f6c6c656374696f6e41747472696275746573000e048c436f6c6c656374696f6e2773206174747269627574657320617265206c6f636b65642e484c6f636b65644974656d4d65746164617461000f04684974656d2773206d65746164617461206973206c6f636b65642e604c6f636b6564436f6c6c656374696f6e4d6574616461746100100480436f6c6c656374696f6e2773206d65746164617461206973206c6f636b65642e404d6178537570706c79526561636865640011046c416c6c206974656d732068617665206265656e206d696e7465642e3c4d6178537570706c794c6f636b6564001204b8546865206d617820737570706c79206973206c6f636b656420616e642063616e2774206265206368616e6765642e444d6178537570706c79546f6f536d616c6c00130449015468652070726f7669646564206d617820737570706c79206973206c657373207468616e20746865206e756d626572206f66206974656d73206120636f6c6c656374696f6e20616c7265616479206861732e2c556e6b6e6f776e4974656d0014047454686520676976656e206974656d20494420697320756e6b6e6f776e2e2c556e6b6e6f776e537761700015044c5377617020646f65736e27742065786973742e404d657461646174614e6f74466f756e640016048c54686520676976656e206974656d20686173206e6f206d65746164617461207365742e444174747269627574654e6f74466f756e64001704985468652070726f7669646564206174747269627574652063616e277420626520666f756e642e284e6f74466f7253616c65001804544974656d206973206e6f7420666f722073616c652e24426964546f6f4c6f77001904705468652070726f76696465642062696420697320746f6f206c6f772e5052656163686564417070726f76616c4c696d6974001a04a0546865206974656d2068617320726561636865642069747320617070726f76616c206c696d69742e3c446561646c696e6545787069726564001b048454686520646561646c696e652068617320616c726561647920657870697265642e3457726f6e674475726174696f6e001c043101546865206475726174696f6e2070726f76696465642073686f756c64206265206c657373207468616e206f7220657175616c20746f20604d6178446561646c696e654475726174696f6e602e384d6574686f6444697361626c6564001d04a8546865206d6574686f642069732064697361626c65642062792073797374656d2073657474696e67732e3057726f6e6753657474696e67001e04885468652070726f76696465642073657474696e672063616e2774206265207365742e58496e636f6e73697374656e744974656d436f6e666967001f0415014974656d277320636f6e66696720616c72656164792065786973747320616e642073686f756c6420626520657175616c20746f207468652070726f7669646564206f6e652e204e6f436f6e666967002004c8436f6e66696720666f72206120636f6c6c656374696f6e206f7220616e206974656d2063616e277420626520666f756e642e3c526f6c65734e6f74436c656172656400210470536f6d6520726f6c65732077657265206e6f7420636c65617265642e384d696e744e6f7453746172746564002204644d696e7420686173206e6f742073746172746564207965742e244d696e74456e6465640023045c4d696e742068617320616c726561647920656e6465642e38416c7265616479436c61696d6564002404c05468652070726f7669646564204974656d2077617320616c7265616479207573656420666f7220636c61696d696e672e34496e636f7272656374446174610025047c5468652070726f7669646564206461746120697320696e636f72726563742e2c57726f6e674f726967696e002604ac5468652065787472696e736963207761732073656e74206279207468652077726f6e67206f726967696e2e3857726f6e675369676e6174757265002704905468652070726f7669646564207369676e617475726520697320696e636f72726563742e44496e636f72726563744d65746164617461002804a05468652070726f7669646564206d65746164617461206d6967687420626520746f6f206c6f6e672e644d6178417474726962757465734c696d6974526561636865640029049c43616e277420736574206d6f7265206174747269627574657320706572206f6e652063616c6c2e3857726f6e674e616d657370616365002a04d05468652070726f7669646564206e616d6573706163652069736e277420737570706f7274656420696e20746869732063616c6c2e48436f6c6c656374696f6e4e6f74456d707479002b048c43616e27742064656c657465206e6f6e2d656d70747920636f6c6c656374696f6e732e3c5769746e6573735265717569726564002c0490546865207769746e65737320646174612073686f756c642062652070726f76696465642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ea907102873705f72756e74696d651c67656e657269634c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c416464726573730145041043616c6c015d04245369676e617475726501b50514457874726101ad0700040038000000ad0700000428b107b507b907bd07c107c907cd07d107d507d90700b10710306672616d655f73797374656d28657874656e73696f6e7354636865636b5f6e6f6e5f7a65726f5f73656e64657248436865636b4e6f6e5a65726f53656e64657204045400000000b50710306672616d655f73797374656d28657874656e73696f6e7348636865636b5f737065635f76657273696f6e40436865636b5370656356657273696f6e04045400000000b90710306672616d655f73797374656d28657874656e73696f6e7340636865636b5f74785f76657273696f6e38436865636b547856657273696f6e04045400000000bd0710306672616d655f73797374656d28657874656e73696f6e7334636865636b5f67656e6573697330436865636b47656e6573697304045400000000c10710306672616d655f73797374656d28657874656e73696f6e733c636865636b5f6d6f7274616c69747938436865636b4d6f7274616c69747904045400000400c507010c4572610000c507102873705f72756e74696d651c67656e657269630c6572610c4572610001010420496d6d6f7274616c0000001c4d6f7274616c31040008000001001c4d6f7274616c32040008000002001c4d6f7274616c33040008000003001c4d6f7274616c34040008000004001c4d6f7274616c35040008000005001c4d6f7274616c36040008000006001c4d6f7274616c37040008000007001c4d6f7274616c38040008000008001c4d6f7274616c3904000800000900204d6f7274616c313004000800000a00204d6f7274616c313104000800000b00204d6f7274616c313204000800000c00204d6f7274616c313304000800000d00204d6f7274616c313404000800000e00204d6f7274616c313504000800000f00204d6f7274616c313604000800001000204d6f7274616c313704000800001100204d6f7274616c313804000800001200204d6f7274616c313904000800001300204d6f7274616c323004000800001400204d6f7274616c323104000800001500204d6f7274616c323204000800001600204d6f7274616c323304000800001700204d6f7274616c323404000800001800204d6f7274616c323504000800001900204d6f7274616c323604000800001a00204d6f7274616c323704000800001b00204d6f7274616c323804000800001c00204d6f7274616c323904000800001d00204d6f7274616c333004000800001e00204d6f7274616c333104000800001f00204d6f7274616c333204000800002000204d6f7274616c333304000800002100204d6f7274616c333404000800002200204d6f7274616c333504000800002300204d6f7274616c333604000800002400204d6f7274616c333704000800002500204d6f7274616c333804000800002600204d6f7274616c333904000800002700204d6f7274616c343004000800002800204d6f7274616c343104000800002900204d6f7274616c343204000800002a00204d6f7274616c343304000800002b00204d6f7274616c343404000800002c00204d6f7274616c343504000800002d00204d6f7274616c343604000800002e00204d6f7274616c343704000800002f00204d6f7274616c343804000800003000204d6f7274616c343904000800003100204d6f7274616c353004000800003200204d6f7274616c353104000800003300204d6f7274616c353204000800003400204d6f7274616c353304000800003500204d6f7274616c353404000800003600204d6f7274616c353504000800003700204d6f7274616c353604000800003800204d6f7274616c353704000800003900204d6f7274616c353804000800003a00204d6f7274616c353904000800003b00204d6f7274616c363004000800003c00204d6f7274616c363104000800003d00204d6f7274616c363204000800003e00204d6f7274616c363304000800003f00204d6f7274616c363404000800004000204d6f7274616c363504000800004100204d6f7274616c363604000800004200204d6f7274616c363704000800004300204d6f7274616c363804000800004400204d6f7274616c363904000800004500204d6f7274616c373004000800004600204d6f7274616c373104000800004700204d6f7274616c373204000800004800204d6f7274616c373304000800004900204d6f7274616c373404000800004a00204d6f7274616c373504000800004b00204d6f7274616c373604000800004c00204d6f7274616c373704000800004d00204d6f7274616c373804000800004e00204d6f7274616c373904000800004f00204d6f7274616c383004000800005000204d6f7274616c383104000800005100204d6f7274616c383204000800005200204d6f7274616c383304000800005300204d6f7274616c383404000800005400204d6f7274616c383504000800005500204d6f7274616c383604000800005600204d6f7274616c383704000800005700204d6f7274616c383804000800005800204d6f7274616c383904000800005900204d6f7274616c393004000800005a00204d6f7274616c393104000800005b00204d6f7274616c393204000800005c00204d6f7274616c393304000800005d00204d6f7274616c393404000800005e00204d6f7274616c393504000800005f00204d6f7274616c393604000800006000204d6f7274616c393704000800006100204d6f7274616c393804000800006200204d6f7274616c393904000800006300244d6f7274616c31303004000800006400244d6f7274616c31303104000800006500244d6f7274616c31303204000800006600244d6f7274616c31303304000800006700244d6f7274616c31303404000800006800244d6f7274616c31303504000800006900244d6f7274616c31303604000800006a00244d6f7274616c31303704000800006b00244d6f7274616c31303804000800006c00244d6f7274616c31303904000800006d00244d6f7274616c31313004000800006e00244d6f7274616c31313104000800006f00244d6f7274616c31313204000800007000244d6f7274616c31313304000800007100244d6f7274616c31313404000800007200244d6f7274616c31313504000800007300244d6f7274616c31313604000800007400244d6f7274616c31313704000800007500244d6f7274616c31313804000800007600244d6f7274616c31313904000800007700244d6f7274616c31323004000800007800244d6f7274616c31323104000800007900244d6f7274616c31323204000800007a00244d6f7274616c31323304000800007b00244d6f7274616c31323404000800007c00244d6f7274616c31323504000800007d00244d6f7274616c31323604000800007e00244d6f7274616c31323704000800007f00244d6f7274616c31323804000800008000244d6f7274616c31323904000800008100244d6f7274616c31333004000800008200244d6f7274616c31333104000800008300244d6f7274616c31333204000800008400244d6f7274616c31333304000800008500244d6f7274616c31333404000800008600244d6f7274616c31333504000800008700244d6f7274616c31333604000800008800244d6f7274616c31333704000800008900244d6f7274616c31333804000800008a00244d6f7274616c31333904000800008b00244d6f7274616c31343004000800008c00244d6f7274616c31343104000800008d00244d6f7274616c31343204000800008e00244d6f7274616c31343304000800008f00244d6f7274616c31343404000800009000244d6f7274616c31343504000800009100244d6f7274616c31343604000800009200244d6f7274616c31343704000800009300244d6f7274616c31343804000800009400244d6f7274616c31343904000800009500244d6f7274616c31353004000800009600244d6f7274616c31353104000800009700244d6f7274616c31353204000800009800244d6f7274616c31353304000800009900244d6f7274616c31353404000800009a00244d6f7274616c31353504000800009b00244d6f7274616c31353604000800009c00244d6f7274616c31353704000800009d00244d6f7274616c31353804000800009e00244d6f7274616c31353904000800009f00244d6f7274616c3136300400080000a000244d6f7274616c3136310400080000a100244d6f7274616c3136320400080000a200244d6f7274616c3136330400080000a300244d6f7274616c3136340400080000a400244d6f7274616c3136350400080000a500244d6f7274616c3136360400080000a600244d6f7274616c3136370400080000a700244d6f7274616c3136380400080000a800244d6f7274616c3136390400080000a900244d6f7274616c3137300400080000aa00244d6f7274616c3137310400080000ab00244d6f7274616c3137320400080000ac00244d6f7274616c3137330400080000ad00244d6f7274616c3137340400080000ae00244d6f7274616c3137350400080000af00244d6f7274616c3137360400080000b000244d6f7274616c3137370400080000b100244d6f7274616c3137380400080000b200244d6f7274616c3137390400080000b300244d6f7274616c3138300400080000b400244d6f7274616c3138310400080000b500244d6f7274616c3138320400080000b600244d6f7274616c3138330400080000b700244d6f7274616c3138340400080000b800244d6f7274616c3138350400080000b900244d6f7274616c3138360400080000ba00244d6f7274616c3138370400080000bb00244d6f7274616c3138380400080000bc00244d6f7274616c3138390400080000bd00244d6f7274616c3139300400080000be00244d6f7274616c3139310400080000bf00244d6f7274616c3139320400080000c000244d6f7274616c3139330400080000c100244d6f7274616c3139340400080000c200244d6f7274616c3139350400080000c300244d6f7274616c3139360400080000c400244d6f7274616c3139370400080000c500244d6f7274616c3139380400080000c600244d6f7274616c3139390400080000c700244d6f7274616c3230300400080000c800244d6f7274616c3230310400080000c900244d6f7274616c3230320400080000ca00244d6f7274616c3230330400080000cb00244d6f7274616c3230340400080000cc00244d6f7274616c3230350400080000cd00244d6f7274616c3230360400080000ce00244d6f7274616c3230370400080000cf00244d6f7274616c3230380400080000d000244d6f7274616c3230390400080000d100244d6f7274616c3231300400080000d200244d6f7274616c3231310400080000d300244d6f7274616c3231320400080000d400244d6f7274616c3231330400080000d500244d6f7274616c3231340400080000d600244d6f7274616c3231350400080000d700244d6f7274616c3231360400080000d800244d6f7274616c3231370400080000d900244d6f7274616c3231380400080000da00244d6f7274616c3231390400080000db00244d6f7274616c3232300400080000dc00244d6f7274616c3232310400080000dd00244d6f7274616c3232320400080000de00244d6f7274616c3232330400080000df00244d6f7274616c3232340400080000e000244d6f7274616c3232350400080000e100244d6f7274616c3232360400080000e200244d6f7274616c3232370400080000e300244d6f7274616c3232380400080000e400244d6f7274616c3232390400080000e500244d6f7274616c3233300400080000e600244d6f7274616c3233310400080000e700244d6f7274616c3233320400080000e800244d6f7274616c3233330400080000e900244d6f7274616c3233340400080000ea00244d6f7274616c3233350400080000eb00244d6f7274616c3233360400080000ec00244d6f7274616c3233370400080000ed00244d6f7274616c3233380400080000ee00244d6f7274616c3233390400080000ef00244d6f7274616c3234300400080000f000244d6f7274616c3234310400080000f100244d6f7274616c3234320400080000f200244d6f7274616c3234330400080000f300244d6f7274616c3234340400080000f400244d6f7274616c3234350400080000f500244d6f7274616c3234360400080000f600244d6f7274616c3234370400080000f700244d6f7274616c3234380400080000f800244d6f7274616c3234390400080000f900244d6f7274616c3235300400080000fa00244d6f7274616c3235310400080000fb00244d6f7274616c3235320400080000fc00244d6f7274616c3235330400080000fd00244d6f7274616c3235340400080000fe00244d6f7274616c3235350400080000ff0000c90710306672616d655f73797374656d28657874656e73696f6e732c636865636b5f6e6f6e636528436865636b4e6f6e636504045400000400cc0120543a3a4e6f6e63650000cd0710306672616d655f73797374656d28657874656e73696f6e7330636865636b5f7765696768742c436865636b57656967687404045400000000d107086870616c6c65745f7472616e73616374696f6e5f7061796d656e74604368617267655472616e73616374696f6e5061796d656e7404045400000400dc013042616c616e63654f663c543e0000d50708a463756d756c75735f7072696d6974697665735f73746f726167655f7765696768745f7265636c61696d5053746f726167655765696768745265636c61696d04045400000000d90708746672616d655f6d657461646174615f686173685f657874656e73696f6e44436865636b4d657461646174614861736804045400000401106d6f6465dd0701104d6f64650000dd0708746672616d655f6d657461646174615f686173685f657874656e73696f6e104d6f64650001082044697361626c65640000001c456e61626c656400010000e107084c73746f726167655f6875625f72756e74696d651c52756e74696d6500000000601853797374656d011853797374656d481c4163636f756e7401010402000c4101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004e8205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e3845787472696e736963436f756e74000010040004b820546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e40496e686572656e74734170706c696564010020040004a4205768657468657220616c6c20696e686572656e74732068617665206265656e206170706c6965642e2c426c6f636b576569676874010024180000000000000488205468652063757272656e742077656967687420666f722074686520626c6f636b2e40416c6c45787472696e736963734c656e000010040004410120546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e24426c6f636b486173680101040510348000000000000000000000000000000000000000000000000000000000000000000498204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e3445787472696e736963446174610101040510380400043d012045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e184e756d6265720100101000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801003480000000000000000000000000000000000000000000000000000000000000000004702048617368206f66207468652070726576696f757320626c6f636b2e1844696765737401003c040004f020446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e184576656e747301004c04001ca0204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e001d01204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2ed020497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e002d01204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279fc206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e284576656e74436f756e74010010100000000004b820546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e2c4576656e74546f70696373010104023401030400282501204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e646578657394206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e00510120416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e2054686973450120616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e64e420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e005901205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573744d012074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b0101206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e484c61737452756e74696d65557067726164650000090304000455012053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e545570677261646564546f553332526566436f756e740100200400044d012054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e605570677261646564546f547269706c65526566436f756e740100200400085d012054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c736548202864656661756c7429206966206e6f742e38457865637574696f6e50686173650000fd02040004882054686520657865637574696f6e207068617365206f662074686520626c6f636b2e44417574686f72697a65645570677261646500001103040004b82060536f6d6560206966206120636f6465207570677261646520686173206265656e20617574686f72697a65642e01150301581830426c6f636b57656967687473210389010700f2052a01000b00204aa9d101020040010265cd1d00010bc0560def45010200e000010b0098f73e5d010200f0000100000265cd1d00010bc0de5f59ba0102003001010b00204aa9d1010200400101070088526a74020050000265cd1d0000000004d020426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e2c426c6f636b4c656e67746831033000003c00000050000000500004a820546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e38426c6f636b48617368436f756e74101000100000045501204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e20446257656967687439034040787d010000000000e1f505000000000409012054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e1c56657273696f6e3d0319044c73746f726167652d6875622d72756e74696d654c73746f726167652d6875622d72756e74696d6501000000010000000000000044d7bdd8a272ca0d6501000000dd718d5cc53262d401000000df6acb689907609b0500000037e397fc7c91f5e40200000040fe3ad401f8959a06000000d2bc9897eed08f1503000000f78b278be53f454c02000000ab3c0572291feb8b01000000bc9d89904f5b923f0100000037c8bb1350a9a2a804000000f3ff14d5ab52705903000000ea93e3f16f3d696202000000fbc577b9d747efd601000000b9e7717ace5b45cd010000001078d7ac24a07b0e010000000be7208954c7c6c901000000966604ffe78eb0920100000001000000010484204765742074686520636861696e277320696e2d636f64652076657273696f6e2e28535335385072656669784d03082a0014a8205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e0039012054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e20697331012074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f662069742061737020616e206964656e746966696572206f662074686520636861696e2e015103003c50617261636861696e53797374656d013c50617261636861696e53797374656d6044556e696e636c756465645365676d656e74010055030400184901204c617465737420696e636c7564656420626c6f636b2064657363656e64616e7473207468652072756e74696d652061636365707465642e20496e206f7468657220776f7264732c20746865736520617265610120616e636573746f7273206f66207468652063757272656e746c7920657865637574696e6720626c6f636b2077686963682068617665206e6f74206265656e20696e636c7564656420696e20746865206f627365727665644c2072656c61792d636861696e2073746174652e00750120546865207365676d656e74206c656e677468206973206c696d69746564206279207468652063617061636974792072657475726e65642066726f6d20746865205b60436f6e73656e737573486f6f6b605d20636f6e666967757265643c20696e207468652070616c6c65742e6c41676772656761746564556e696e636c756465645365676d656e7400007d0304000c69012053746f72616765206669656c642074686174206b6565707320747261636b206f662062616e64776964746820757365642062792074686520756e696e636c75646564207365676d656e7420616c6f6e672077697468207468652d01206c61746573742048524d502077617465726d61726b2e205573656420666f72206c696d6974696e672074686520616363657074616e6365206f66206e657720626c6f636b73207769746890207265737065637420746f2072656c617920636861696e20636f6e73747261696e74732e5450656e64696e6756616c69646174696f6e436f6465010038040018590120496e2063617365206f662061207363686564756c656420757067726164652c20746869732073746f72616765206669656c6420636f6e7461696e73207468652076616c69646174696f6e20636f646520746f20626524206170706c6965642e003d0120417320736f6f6e206173207468652072656c617920636861696e2067697665732075732074686520676f2d6168656164207369676e616c2c2077652077696c6c206f7665727772697465207468657101205b603a636f6465605d5b73705f636f72653a3a73746f726167653a3a77656c6c5f6b6e6f776e5f6b6579733a3a434f44455d2077686963682077696c6c20726573756c7420746865206e65787420626c6f636b2070726f636573730901207769746820746865206e65772076616c69646174696f6e20636f64652e205468697320636f6e636c756465732074686520757067726164652070726f636573732e444e657756616c69646174696f6e436f64650000380400145d012056616c69646174696f6e20636f6465207468617420697320736574206279207468652070617261636861696e20616e6420697320746f20626520636f6d6d756e69636174656420746f20636f6c6c61746f7220616e647820636f6e73657175656e746c79207468652072656c61792d636861696e2e00650120546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b206966206e6f206f746865722070616c6c657420616c7265616479207365742c207468652076616c75652e3856616c69646174696f6e446174610000810304000cd020546865205b6050657273697374656456616c69646174696f6e44617461605d2073657420666f72207468697320626c6f636b2e2d0120546869732076616c756520697320657870656374656420746f20626520736574206f6e6c79206f6e63652070657220626c6f636b20616e642069742773206e657665722073746f7265643420696e2074686520747269652e5044696453657456616c69646174696f6e436f6465010020040004e02057657265207468652076616c69646174696f6e20646174612073657420746f206e6f74696679207468652072656c617920636861696e3f644c61737452656c6179436861696e426c6f636b4e756d62657201001010000000000c1d01205468652072656c617920636861696e20626c6f636b206e756d626572206173736f636961746564207769746820746865206c6173742070617261636861696e20626c6f636b2e00882054686973206973207570646174656420696e20606f6e5f66696e616c697a65602e60557067726164655265737472696374696f6e5369676e616c0100890304001c750120416e206f7074696f6e20776869636820696e64696361746573206966207468652072656c61792d636861696e20726573747269637473207369676e616c6c696e6720612076616c69646174696f6e20636f646520757067726164652e610120496e206f7468657220776f7264732c20696620746869732069732060536f6d656020616e64205b604e657756616c69646174696f6e436f6465605d2069732060536f6d6560207468656e207468652070726f64756365646c2063616e6469646174652077696c6c20626520696e76616c69642e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3855706772616465476f416865616401007503040014dc204f7074696f6e616c207570677261646520676f2d6168656164207369676e616c2066726f6d207468652072656c61792d636861696e2e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3c52656c6179537461746550726f6f6600009103040018c4205468652073746174652070726f6f6620666f7220746865206c6173742072656c617920706172656e7420626c6f636b2e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e5852656c6576616e744d6573736167696e6753746174650000990304001c65012054686520736e617073686f74206f6620736f6d652073746174652072656c6174656420746f206d6573736167696e672072656c6576616e7420746f207468652063757272656e742070617261636861696e2061732070657248207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e44486f7374436f6e66696775726174696f6e0000ad030400182901205468652070617261636861696e20686f737420636f6e66696775726174696f6e207468617420776173206f627461696e65642066726f6d207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e384c617374446d714d7163486561640100b50380000000000000000000000000000000000000000000000000000000000000000010f420546865206c61737420646f776e77617264206d65737361676520717565756520636861696e20686561642077652068617665206f627365727665642e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e404c61737448726d704d716348656164730100b903040010490120546865206d65737361676520717565756520636861696e2068656164732077652068617665206f62736572766564207065722065616368206368616e6e656c20696e636f6d696e67206368616e6e656c2e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e6450726f636573736564446f776e776172644d6573736167657301001010000000000cc8204e756d626572206f6620646f776e77617264206d657373616765732070726f63657373656420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e3448726d7057617465726d61726b01001010000000000ca02048524d502077617465726d61726b2074686174207761732073657420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5048726d704f7574626f756e644d657373616765730100c50304000ca42048524d50206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e385570776172644d657373616765730100690204000cac20557077617264206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5450656e64696e675570776172644d6573736167657301006902040004310120557077617264206d65737361676573207468617420617265207374696c6c2070656e64696e6720616e64206e6f74207965742073656e6420746f207468652072656c617920636861696e2e5c55707761726444656c6976657279466565466163746f720100cd0340000064a7b3b6e00d000000000000000004e42054686520666163746f7220746f206d756c7469706c792074686520626173652064656c69766572792066656520627920666f7220554d502e84416e6e6f756e63656448726d704d6573736167657350657243616e646964617465010010100000000008650120546865206e756d626572206f662048524d50206d65737361676573207765206f6273657276656420696e20606f6e5f696e697469616c697a656020616e64207468757320757365642074686174206e756d62657220666f72f020616e6e6f756e63696e672074686520776569676874206f6620606f6e5f696e697469616c697a656020616e6420606f6e5f66696e616c697a65602e68526573657276656458636d705765696768744f766572726964650000280400085d01205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e672058434d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e645265736572766564446d705765696768744f766572726964650000280400085901205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e6720444d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e60437573746f6d56616c69646174696f6e486561644461746100003804000c2901204120637573746f6d2068656164206461746120746861742073686f756c642062652072657475726e656420617320726573756c74206f66206076616c69646174655f626c6f636b602e00110120536565206050616c6c65743a3a7365745f637573746f6d5f76616c69646174696f6e5f686561645f646174616020666f72206d6f726520696e666f726d6174696f6e2e01d103017c042853656c66506172614964e90110e803000004b82052657475726e73207468652070617261636861696e204944207765206172652072756e6e696e6720776974682e01f503012454696d657374616d70012454696d657374616d70080c4e6f7701003020000000000000000004a0205468652063757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e24446964557064617465010020040010d82057686574686572207468652074696d657374616d7020686173206265656e207570646174656420696e207468697320626c6f636b2e00550120546869732076616c7565206973207570646174656420746f206074727565602075706f6e207375636365737366756c207375626d697373696f6e206f6620612074696d657374616d702062792061206e6f64652e4501204974206973207468656e20636865636b65642061742074686520656e64206f66206561636820626c6f636b20657865637574696f6e20696e2074686520606f6e5f66696e616c697a656020686f6f6b2e01f9030004344d696e696d756d506572696f6430200000000000000000188c20546865206d696e696d756d20706572696f64206265747765656e20626c6f636b732e004d012042652061776172652074686174207468697320697320646966666572656e7420746f20746865202a65787065637465642a20706572696f6420746861742074686520626c6f636b2070726f64756374696f6e4901206170706172617475732070726f76696465732e20596f75722063686f73656e20636f6e73656e7375732073797374656d2077696c6c2067656e6572616c6c7920776f726b2077697468207468697320746f61012064657465726d696e6520612073656e7369626c6520626c6f636b2074696d652e20466f72206578616d706c652c20696e2074686520417572612070616c6c65742069742077696c6c20626520646f75626c6520746869737020706572696f64206f6e2064656661756c742073657474696e67732e00023450617261636861696e496e666f013450617261636861696e496e666f042c50617261636861696e49640100e90110640000000001fd03000000032042616c616e636573012042616c616e6365731c34546f74616c49737375616e6365010018400000000000000000000000000000000004982054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e40496e61637469766549737375616e636501001840000000000000000000000000000000000409012054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e1c4163636f756e74010104020014010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080600901205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b19022020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e0c20207d102060606000150120596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b7420202074797065204163636f756e7453746f7265203d2053797374656d0c20207d102060606000510120427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f7265736d0120606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e207468652901206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e4101204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e144c6f636b7301010402000104040010b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e00ad0120557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f602052657365727665730101040200110404000ca4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e00b10120557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f6014486f6c647301010402001d040400046c20486f6c6473206f6e206163636f756e742062616c616e6365732e1c467265657a6573010104020035040400048820467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e014104018410484578697374656e7469616c4465706f736974184000ca9a3b00000000000000000000000020410120546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2100590120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f72610120746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e590120496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e65787065637465648c206265686176696f757220696620796f7520736574207468697320746f207a65726f2e00f020426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e6521204d61784c6f636b7310103200000010f420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742edc204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e00ad0120557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f602c4d617852657365727665731010320000000c0d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e00b10120557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f60284d6178467265657a657310100000000004610120546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e0151040a485472616e73616374696f6e5061796d656e7401485472616e73616374696f6e5061796d656e7408444e6578744665654d756c7469706c6965720100cd0340000064a7b3b6e00d0000000000000000003853746f7261676556657273696f6e0100550404000000018c04604f7065726174696f6e616c4665654d756c7469706c696572080405545901204120666565206d756c7469706c69657220666f7220604f7065726174696f6e616c602065787472696e7369637320746f20636f6d7075746520227669727475616c207469702220746f20626f6f73742074686569722c20607072696f726974796000510120546869732076616c7565206973206d756c7469706c69656420627920746865206066696e616c5f6665656020746f206f627461696e206120227669727475616c20746970222074686174206973206c61746572f420616464656420746f20612074697020636f6d706f6e656e7420696e20726567756c617220607072696f72697479602063616c63756c6174696f6e732e4d01204974206d65616e732074686174206120604e6f726d616c60207472616e73616374696f6e2063616e2066726f6e742d72756e20612073696d696c61726c792d73697a656420604f7065726174696f6e616c6041012065787472696e736963202877697468206e6f20746970292c20627920696e636c7564696e672061207469702076616c75652067726561746572207468616e20746865207669727475616c207469702e003c20606060727573742c69676e6f726540202f2f20466f7220604e6f726d616c608c206c6574207072696f72697479203d207072696f726974795f63616c6328746970293b0054202f2f20466f7220604f7065726174696f6e616c601101206c6574207669727475616c5f746970203d2028696e636c7573696f6e5f666565202b2074697029202a204f7065726174696f6e616c4665654d756c7469706c6965723bc4206c6574207072696f72697479203d207072696f726974795f63616c6328746970202b207669727475616c5f746970293b1020606060005101204e6f746520746861742073696e636520776520757365206066696e616c5f6665656020746865206d756c7469706c696572206170706c69657320616c736f20746f2074686520726567756c61722060746970605d012073656e74207769746820746865207472616e73616374696f6e2e20536f2c206e6f74206f6e6c7920646f657320746865207472616e73616374696f6e206765742061207072696f726974792062756d702062617365646101206f6e207468652060696e636c7573696f6e5f666565602c2062757420776520616c736f20616d706c6966792074686520696d70616374206f662074697073206170706c69656420746f20604f7065726174696f6e616c6038207472616e73616374696f6e732e000b105375646f01105375646f040c4b6579000000040004842054686520604163636f756e74496460206f6620746865207375646f206b65792e01590401900001d1050f28417574686f72736869700128417574686f72736869700418417574686f720000000400046420417574686f72206f662063757272656e7420626c6f636b2e000000001444436f6c6c61746f7253656c656374696f6e0144436f6c6c61746f7253656c656374696f6e1434496e76756c6e657261626c65730100d50504000411012054686520696e76756c6e657261626c652c207065726d697373696f6e656420636f6c6c61746f72732e2054686973206c697374206d75737420626520736f727465642e3443616e6469646174654c6973740100d9050400146901205468652028636f6d6d756e6974792c206c696d697465642920636f6c6c6174696f6e2063616e646964617465732e206043616e646964617465736020616e642060496e76756c6e657261626c6573602073686f756c6420626550206d757475616c6c79206578636c75736976652e0075012054686973206c69737420697320736f7274656420696e20617363656e64696e67206f72646572206279206465706f73697420616e64207768656e20746865206465706f736974732061726520657175616c2c20746865206c65617374a020726563656e746c79207570646174656420697320636f6e7369646572656420677265617465722e444c617374417574686f726564426c6f636b01010405001010000000000484204c61737420626c6f636b20617574686f72656420627920636f6c6c61746f722e444465736972656443616e6469646174657301001010000000000c782044657369726564206e756d626572206f662063616e646964617465732e00750120546869732073686f756c6420696465616c6c7920616c77617973206265206c657373207468616e205b60436f6e6669673a3a4d617843616e64696461746573605d20666f72207765696768747320746f20626520636f72726563742e3443616e646964616379426f6e6401001840000000000000000000000000000000000cb820466978656420616d6f756e7420746f206465706f73697420746f206265636f6d65206120636f6c6c61746f722e004101205768656e206120636f6c6c61746f722063616c6c7320606c656176655f696e74656e7460207468657920696d6d6564696174656c79207265636569766520746865206465706f736974206261636b2e01610401a00001e505151c53657373696f6e011c53657373696f6e1c2856616c696461746f72730100a40400047c205468652063757272656e7420736574206f662076616c696461746f72732e3043757272656e74496e646578010010100000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e345175657565644368616e676564010020040008390120547275652069662074686520756e6465726c79696e672065636f6e6f6d6963206964656e746974696573206f7220776569676874696e6720626568696e64207468652076616c696461746f7273a420686173206368616e67656420696e20746865207175657565642076616c696461746f72207365742e285175657565644b6579730100e9050400083d012054686520717565756564206b65797320666f7220746865206e6578742073657373696f6e2e205768656e20746865206e6578742073657373696f6e20626567696e732c207468657365206b657973e02077696c6c206265207573656420746f2064657465726d696e65207468652076616c696461746f7227732073657373696f6e206b6579732e4844697361626c656456616c696461746f72730100b9020400148020496e6469636573206f662064697361626c65642076616c696461746f72732e003d01205468652076656320697320616c77617973206b65707420736f7274656420736f20746861742077652063616e2066696e642077686574686572206120676976656e2076616c696461746f722069733d012064697361626c6564207573696e672062696e617279207365617263682e204974206765747320636c6561726564207768656e20606f6e5f73657373696f6e5f656e64696e67602072657475726e73642061206e657720736574206f66206964656e7469746965732e204e6578744b657973000104050069040400049c20546865206e6578742073657373696f6e206b65797320666f7220612076616c696461746f722e204b65794f776e657200010405f10500040004090120546865206f776e6572206f662061206b65792e20546865206b65792069732074686520604b657954797065496460202b2074686520656e636f646564206b65792e01650401a80001f905161041757261011041757261082c417574686f7269746965730100fd050400046c205468652063757272656e7420617574686f72697479207365742e2c43757272656e74536c6f74010005062000000000000000000c80205468652063757272656e7420736c6f74206f66207468697320626c6f636b2e009420546869732077696c6c2062652073657420696e20606f6e5f696e697469616c697a65602e00000430536c6f744475726174696f6e30207017000000000000100d012054686520736c6f74206475726174696f6e20417572612073686f756c642072756e20776974682c2065787072657373656420696e206d696c6c697365636f6e64732e3d0120546865206566666563746976652076616c7565206f66207468697320747970652073686f756c64206e6f74206368616e6765207768696c652074686520636861696e2069732072756e6e696e672e00350120466f72206261636b776172647320636f6d7061746962696c6974792065697468657220757365205b604d696e696d756d506572696f6454696d657354776f605d206f72206120636f6e73742e00171c41757261457874011c41757261457874082c417574686f7269746965730100fd05040014942053657276657320617320636163686520666f722074686520617574686f7269746965732e0071012054686520617574686f72697469657320696e204175526120617265206f7665727772697474656e20696e20606f6e5f696e697469616c697a6560207768656e2077652073776974636820746f2061206e65772073657373696f6e2c5d0120627574207765207265717569726520746865206f6c6420617574686f72697469657320746f2076657269667920746865207365616c207768656e2076616c69646174696e67206120506f562e20546869732077696c6c0d0120616c77617973206265207570646174656420746f20746865206c6174657374204175526120617574686f72697469657320696e20606f6e5f66696e616c697a65602e20536c6f74496e666f0000090604000cd82043757272656e7420736c6f742070616972656420776974682061206e756d626572206f6620617574686f72656420626c6f636b732e00982055706461746564206f6e206561636820626c6f636b20696e697469616c697a6174696f6e2e00000000182458636d705175657565012458636d7051756575651c50496e626f756e6458636d7053757370656e64656401000d060400200d01205468652073757370656e64656420696e626f756e642058434d50206368616e6e656c732e20416c6c206f746865727320617265206e6f742073757370656e6465642e00710120546869732069732061206053746f7261676556616c75656020696e7374656164206f662061206053746f726167654d6170602073696e636520776520657870656374206d756c7469706c652072656164732070657220626c6f636b690120746f20646966666572656e74206b65797320776974682061206f6e652062797465207061796c6f61642e205468652061636365737320746f2060426f756e6465644254726565536574602077696c6c2062652063616368656415012077697468696e2074686520626c6f636b20616e64207468657265666f7265206f6e6c7920696e636c75646564206f6e636520696e207468652070726f6f662073697a652e006501204e4f54453a2054686520506f562062656e63686d61726b696e672063616e6e6f74206b6e6f77207468697320616e642077696c6c206f7665722d657374696d6174652c20627574207468652061637475616c2070726f6f66442077696c6c20626520736d616c6c65722e484f7574626f756e6458636d70537461747573010019060400185d0120546865206e6f6e2d656d7074792058434d50206368616e6e656c7320696e206f72646572206f66206265636f6d696e67206e6f6e2d656d7074792c20616e642074686520696e646578206f6620746865206669727374510120616e64206c617374206f7574626f756e64206d6573736167652e204966207468652074776f20696e64696365732061726520657175616c2c207468656e20697420696e6469636174657320616e20656d707479590120717565756520616e64207468657265206d7573742062652061206e6f6e2d604f6b6020604f7574626f756e64537461747573602e20576520617373756d65207175657565732067726f77206e6f20677265617465725901207468616e203635353335206974656d732e20517565756520696e646963657320666f72206e6f726d616c206d6573736167657320626567696e206174206f6e653b207a65726f20697320726573657276656420696e11012063617365206f6620746865206e65656420746f2073656e64206120686967682d7072696f72697479207369676e616c206d657373616765207468697320626c6f636b2e09012054686520626f6f6c20697320747275652069662074686572652069732061207369676e616c206d6573736167652077616974696e6720746f2062652073656e742e504f7574626f756e6458636d704d65737361676573010108020529062d06040004bc20546865206d65737361676573206f7574626f756e6420696e206120676976656e2058434d50206368616e6e656c2e385369676e616c4d6573736167657301010402e9012d06040004a020416e79207369676e616c206d657373616765732077616974696e6720746f2062652073656e742e2c5175657565436f6e66696701003106302000000030000000080000000415012054686520636f6e66696775726174696f6e20776869636820636f6e74726f6c73207468652064796e616d696373206f6620746865206f7574626f756e642071756575652e38517565756553757370656e64656401002004000441012057686574686572206f72206e6f74207468652058434d502071756575652069732073757370656e6465642066726f6d20657865637574696e6720696e636f6d696e672058434d73206f72206e6f742e4444656c6976657279466565466163746f7201010405e901cd0340000064a7b3b6e00d000000000000000004c42054686520666163746f7220746f206d756c7469706c792074686520626173652064656c6976657279206665652062792e01710401ac0c4c4d6178496e626f756e6453757370656e6465641010e803000014490120546865206d6178696d756d206e756d626572206f6620696e626f756e642058434d50206368616e6e656c7320746861742063616e2062652073757370656e6465642073696d756c74616e656f75736c792e005d0120416e792066757274686572206368616e6e656c2073757370656e73696f6e732077696c6c206661696c20616e64206d65737361676573206d6179206765742064726f7070656420776974686f757420667572746865724501206e6f746963652e2043686f6f73696e67206120686967682076616c756520283130303029206973206f6b61793b207468652074726164652d6f666620746861742069732064657363726962656420696ed8205b60496e626f756e6458636d7053757370656e646564605d207374696c6c206170706c6965732061742074686174207363616c652e644d61784163746976654f7574626f756e644368616e6e656c73101080000000206501204d6178696d616c206e756d626572206f66206f7574626f756e642058434d50206368616e6e656c7320746861742063616e2068617665206d6573736167657320717565756564206174207468652073616d652074696d652e005501204966207468697320697320726561636865642c207468656e206e6f2066757274686572206d657373616765732063616e2062652073656e7420746f206368616e6e656c73207468617420646f206e6f74207965745d0120686176652061206d657373616765207175657565642e20546869732073686f756c642062652073657420746f20746865206578706563746564206d6178696d756d206f66206f7574626f756e64206368616e6e656c7361012077686963682069732064657465726d696e6564206279205b6053656c663a3a4368616e6e656c496e666f605d2e20497420697320696d706f7274616e7420746f207365742074686973206c6172676520656e6f7567682c5d012073696e6365206f74686572776973652074686520636f6e67657374696f6e20636f6e74726f6c2070726f746f636f6c2077696c6c206e6f7420776f726b20617320696e74656e64656420616e64206d657373616765735101206d61792062652064726f707065642e20546869732076616c756520696e637265617365732074686520506f5620616e642073686f756c64207468657265666f7265206e6f74206265207069636b656420746f6f4d0120686967682e20476f7665726e616e6365206e6565647320746f2070617920617474656e74696f6e20746f206e6f74206f70656e206d6f7265206368616e6e656c73207468616e20746869732076616c75652e2c4d61785061676553697a6510100000010014b820546865206d6178696d616c20706167652073697a6520666f722048524d50206d6573736167652070616765732e0061012041206c6f776572206c696d69742063616e206265207365742064796e616d6963616c6c792c2062757420746869732069732074686520686172642d6c696d697420666f722074686520506f5620776f727374206361736555012062656e63686d61726b696e672e20546865206c696d697420666f72207468652073697a65206f662061206d65737361676520697320736c696768746c792062656c6f7720746869732c2073696e636520736f6d65b8206f7665726865616420697320696e63757272656420666f7220656e636f64696e672074686520666f726d61742e0135061e2c506f6c6b61646f7458636d012c506f6c6b61646f7458636d38305175657279436f756e746572010030200000000000000000048820546865206c617465737420617661696c61626c6520717565727920696e6465782e1c51756572696573000104023039060400045420546865206f6e676f696e6720717565726965732e28417373657454726170730101040634101000000000106820546865206578697374696e672061737365742074726170732e006101204b65792069732074686520626c616b6532203235362068617368206f6620286f726967696e2c2076657273696f6e65642060417373657473602920706169722e2056616c756520697320746865206e756d626572206f661d012074696d65732074686973207061697220686173206265656e20747261707065642028757375616c6c79206a75737420312069662069742065786973747320617420616c6c292e385361666558636d56657273696f6e00001004000861012044656661756c742076657273696f6e20746f20656e636f64652058434d207768656e206c61746573742076657273696f6e206f662064657374696e6174696f6e20697320756e6b6e6f776e2e20496620604e6f6e65602c3d01207468656e207468652064657374696e6174696f6e732077686f73652058434d2076657273696f6e20697320756e6b6e6f776e2061726520636f6e7369646572656420756e726561636861626c652e40537570706f7274656456657273696f6e00010805024d0610040004f020546865204c61746573742076657273696f6e732074686174207765206b6e6f7720766172696f7573206c6f636174696f6e7320737570706f72742e4056657273696f6e4e6f7469666965727300010805024d0630040004050120416c6c206c6f636174696f6e7320746861742077652068617665207265717565737465642076657273696f6e206e6f74696669636174696f6e732066726f6d2e5056657273696f6e4e6f746966795461726765747300010805024d06510604000871012054686520746172676574206c6f636174696f6e73207468617420617265207375627363726962656420746f206f75722076657273696f6e206368616e6765732c2061732077656c6c20617320746865206d6f737420726563656e7494206f66206f75722076657273696f6e7320776520696e666f726d6564207468656d206f662e5456657273696f6e446973636f7665727951756575650100550604000c65012044657374696e6174696f6e732077686f7365206c61746573742058434d2076657273696f6e20776520776f756c64206c696b6520746f206b6e6f772e204475706c696361746573206e6f7420616c6c6f7765642c20616e6471012074686520607533326020636f756e74657220697320746865206e756d626572206f662074696d6573207468617420612073656e6420746f207468652064657374696e6174696f6e20686173206265656e20617474656d707465642c8c20776869636820697320757365642061732061207072696f726974697a6174696f6e2e4043757272656e744d6967726174696f6e000061060400049c205468652063757272656e74206d6967726174696f6e27732073746167652c20696620616e792e5452656d6f74654c6f636b656446756e6769626c657300010c05020269066d06040004f02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e20612072656d6f746520636861696e2e3c4c6f636b656446756e6769626c657300010402007d06040004e02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e207468697320636861696e2e5458636d457865637574696f6e53757370656e646564010020040004b420476c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e3c53686f756c645265636f726458636d01002004001c59012057686574686572206f72206e6f7420696e636f6d696e672058434d732028626f7468206578656375746564206c6f63616c6c7920616e64207265636569766564292073686f756c64206265207265636f726465642ec4204f6e6c79206f6e652058434d2070726f6772616d2077696c6c206265207265636f7264656420617420612074696d652e29012054686973206973206d65616e7420746f206265207573656420696e2072756e74696d6520415049732c20616e64206974277320616476697365642069742073746179732066616c73650d0120666f7220616c6c206f74686572207573652063617365732c20736f20617320746f206e6f74206465677261646520726567756c617220706572666f726d616e63652e005901204f6e6c792072656c6576616e7420696620746869732070616c6c6574206973206265696e67207573656420617320746865205b6078636d5f6578656375746f723a3a7472616974733a3a5265636f726458636d605dc820696d706c656d656e746174696f6e20696e207468652058434d206578656375746f7220636f6e66696775726174696f6e2e2c5265636f7264656458636d000005010400184901204966205b6053686f756c645265636f726458636d605d2069732073657420746f20747275652c207468656e20746865206c6173742058434d2070726f6772616d206578656375746564206c6f63616c6c79542077696c6c2062652073746f72656420686572652e29012052756e74696d6520415049732063616e206665746368207468652058434d20746861742077617320657865637574656420627920616363657373696e6720746869732076616c75652e005901204f6e6c792072656c6576616e7420696620746869732070616c6c6574206973206265696e67207573656420617320746865205b6078636d5f6578656375746f723a3a7472616974733a3a5265636f726458636d605dc820696d706c656d656e746174696f6e20696e207468652058434d206578656375746f7220636f6e66696775726174696f6e2e01750401b0000189061f2843756d756c757358636d0001150501dd01000020304d657373616765517565756501304d65737361676551756575650c30426f6f6b5374617465466f7201010405e5018d0674000000000000000000000000000000000000000000000000000000000004cc2054686520696e646578206f662074686520666972737420616e64206c61737420286e6f6e2d656d707479292070616765732e2c53657276696365486561640000e501040004bc20546865206f726967696e2061742077686963682077652073686f756c6420626567696e20736572766963696e672e145061676573000108050599069d060400048820546865206d6170206f66207061676520696e646963657320746f2070616765732e01190501e10110204865617053697a651010009c0100143d01205468652073697a65206f662074686520706167653b207468697320696d706c69657320746865206d6178696d756d206d6573736167652073697a652077686963682063616e2062652073656e742e005901204120676f6f642076616c756520646570656e6473206f6e20746865206578706563746564206d6573736167652073697a65732c20746865697220776569676874732c207468652077656967687420746861742069735d0120617661696c61626c6520666f722070726f63657373696e67207468656d20616e6420746865206d6178696d616c206e6565646564206d6573736167652073697a652e20546865206d6178696d616c206d65737361676511012073697a6520697320736c696768746c79206c6f776572207468616e207468697320617320646566696e6564206279205b604d61784d6573736167654c656e4f66605d2e204d61785374616c651010080000000c5d0120546865206d6178696d756d206e756d626572206f66207374616c652070616765732028692e652e206f66206f766572776569676874206d657373616765732920616c6c6f776564206265666f72652063756c6c696e6751012063616e2068617070656e2e204f6e636520746865726520617265206d6f7265207374616c65207061676573207468616e20746869732c207468656e20686973746f726963616c207061676573206d6179206265fc2064726f707065642c206576656e206966207468657920636f6e7461696e20756e70726f636573736564206f766572776569676874206d657373616765732e34536572766963655765696768742d032c0107005840fba2020070001841012054686520616d6f756e74206f66207765696768742028696620616e79292077686963682073686f756c642062652070726f766964656420746f20746865206d65737361676520717565756520666f72a820736572766963696e6720656e717565756564206974656d7320606f6e5f696e697469616c697a65602e00fc2054686973206d6179206265206c65676974696d6174656c7920604e6f6e656020696e207468652063617365207468617420796f752077696c6c2063616c6c5d012060536572766963655175657565733a3a736572766963655f71756575657360206d616e75616c6c79206f7220736574205b6053656c663a3a49646c654d617853657276696365576569676874605d20746f2068617665542069742072756e20696e20606f6e5f69646c65602e5049646c654d6178536572766963655765696768742d030400145d0120546865206d6178696d756d20616d6f756e74206f66207765696768742028696620616e792920746f20626520757365642066726f6d2072656d61696e696e672077656967687420606f6e5f69646c656020776869636841012073686f756c642062652070726f766964656420746f20746865206d65737361676520717565756520666f7220736572766963696e6720656e717565756564206974656d7320606f6e5f69646c65602e3d012055736566756c20666f722070617261636861696e7320746f2070726f63657373206d65737361676573206174207468652073616d6520626c6f636b2074686579206172652072656365697665642e00290120496620604e6f6e65602c2069742077696c6c206e6f742063616c6c2060536572766963655175657565733a3a736572766963655f7175657565736020696e20606f6e5f69646c65602e01a506212450726f766964657273012450726f76696465727330385369676e557052657175657374730001040200a906040028ed0120546865206d617070696e672066726f6d20616e204163636f756e74496420746861742072657175657374656420746f207369676e20757020746f2061207475706c65206f6620746865206d6574616461746120776974682074797065206f662074686520726571756573742c20616e642074686520626c6f636b88206e756d626572207768656e20746865207265717565737420776173206d6164652e00d1012054686973206973207573656420666f72207468652074776f2d737465702070726f63657373206f66207265676973746572696e673a207768656e2061207573657220726571756573747320746f20726567697374657220617320612053502028656974686572204d5350206f7220425350292cb10120746861742072657175657374207769746820746865206d6574616461746120616e6420746865206465706f7369742068656c642069732073746f72656420686572652e205768656e20746865207573657220636f6e6669726d7320746865207369676e2075702c20746865250120726571756573742069732072656d6f7665642066726f6d20746869732073746f7261676520616e64207468652075736572206973207265676973746572656420617320612053502e007020546869732073746f72616765206973207570646174656420696e3aad02202d205b726571756573745f6d73705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6d73705f7369676e5f75702920616e64205b726571756573745f6273705f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a726571756573745f6273705f7369676e5f7570292c207768696368206164642061206e657720656e74727920746f20746865206d61702e9102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f75702920616e64205b63616e63656c5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a63616e63656c5f7369676e5f7570292c2077686963682072656d6f766520616e206578697374696e6720656e7472792066726f6d20746865206d61702e804163636f756e744964546f4d61696e53746f7261676550726f7669646572496400010402003404001ce820546865206d617070696e672066726f6d20616e204163636f756e74496420746f2061204d61696e53746f7261676550726f766964657249642e007d012054686973206973207573656420746f206765742061204d61696e2053746f726167652050726f7669646572277320756e69717565206964656e746966696572206e656564656420746f2061636365737320697473206d657461646174612e007020546869732073746f72616765206973207570646174656420696e3a5102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722ea901202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e504d61696e53746f7261676550726f7669646572730001040234b5060400280d0120546865206d617070696e672066726f6d2061204d61696e53746f7261676550726f7669646572496420746f2061204d61696e53746f7261676550726f76696465722e00e02054686973206973207573656420746f206765742061204d61696e2053746f726167652050726f76696465722773206d657461646174612ebd012049742072657475726e7320604e6f6e656020696620746865204d61696e2053746f726167652050726f766964657220494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564204d61696e2053746f726167652050726f76696465722e007020546869732073746f72616765206973207570646174656420696e3a5102202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722ea901202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e8901202d205b6368616e67655f63617061636974795d2863726174653a3a646973706174636861626c65733a3a6368616e67655f6361706163697479292c207768696368206368616e6765732074686520656e747279277320606361706163697479602e5502202d205b6164645f76616c75655f70726f705d2863726174653a3a646973706174636861626c65733a3a6164645f76616c75655f70726f70292c20776869636820617070656e64732061206e65772076616c75652070726f706f736974696f6e20746f2074686520656e7472792773206578697374696e67206076616c75655f70726f706020626f756e64656420766563746f722e1c4275636b6574730001040234bd06040024dc20546865206d617070696e672066726f6d2061204275636b6574496420746f2074686174206275636b65742773206d657461646174612e0031012054686973206973207573656420746f206765742061206275636b65742773206d657461646174612c207375636820617320726f6f742c20757365722049442c20616e64204d53502049442e45012049742072657475726e7320604e6f6e656020696620746865204275636b657420494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564206275636b65742e007020546869732073746f72616765206973207570646174656420696e3a9501202d205b6164645f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6164645f6275636b6574292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e0502202d205b6368616e67655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6368616e67655f726f6f745f6275636b6574292c207768696368206368616e6765732074686520636f72726573706f6e64696e67206275636b6574277320726f6f742e1d02202d205b72656d6f76655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a72656d6f76655f726f6f745f6275636b6574292c2077686963682072656d6f7665732074686520656e747279206f662074686520636f72726573706f6e64696e67206275636b65742e7c4d61696e53746f7261676550726f7669646572496473546f4275636b6574730001040234c50604001c0d0120546865206d617070696e672066726f6d2061204d61696e53746f7261676550726f7669646572496420746f206120766563746f72206f66204275636b65744964732e00b1012054686973206973207573656420746f20656666696369656e746c7920726574726965766520746865206c697374206f66206275636b65747320746861742061204d61696e2053746f726167652050726f76696465722069732063757272656e746c792073746f72696e672e007020546869732073746f72616765206973207570646174656420696e3a0501202d205b6164645f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a6164645f6275636b6574294501202d205b72656d6f76655f726f6f745f6275636b65745d287368705f7472616974733a3a4d757461746550726f766964657273496e746572666163653a3a72656d6f76655f726f6f745f6275636b657429884163636f756e744964546f4261636b757053746f7261676550726f76696465724964000104020034040020f020546865206d617070696e672066726f6d20616e204163636f756e74496420746f2061204261636b757053746f7261676550726f766964657249642e0085012054686973206973207573656420746f206765742061204261636b75702053746f726167652050726f7669646572277320756e69717565206964656e746966696572206e656564656420746f2061636365737320697473206d657461646174612e007020546869732073746f72616765206973207570646174656420696e3a005902202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722ea901202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e584261636b757053746f7261676550726f7669646572730001040234b1060400241d0120546865206d617070696e672066726f6d2061204261636b757053746f7261676550726f7669646572496420746f2061204261636b757053746f7261676550726f76696465722e00e82054686973206973207573656420746f206765742061204261636b75702053746f726167652050726f76696465722773206d657461646174612ecd012049742072657475726e7320604e6f6e656020696620746865204261636b75702053746f726167652050726f766964657220494420646f6573206e6f7420636f72726573706f6e6420746f20616e792072656769737465726564204261636b75702053746f726167652050726f76696465722e007020546869732073746f72616765206973207570646174656420696e3a5902202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c20776869636820616464732061206e657720656e74727920746f20746865206d617020696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722ea901202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e8901202d205b6368616e67655f63617061636974795d2863726174653a3a646973706174636861626c65733a3a6368616e67655f6361706163697479292c207768696368206368616e6765732074686520656e747279277320606361706163697479602e204d7370436f756e7401001010000000001c4d012054686520616d6f756e74206f66204d61696e2053746f726167652050726f7669646572732074686174206172652063757272656e746c79207265676973746572656420696e207468652072756e74696d652e0019012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f66204d53507320696e207468652073797374656d2e007020546869732073746f72616765206973207570646174656420696e3a4502202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c2077686963682061646473206f6e6520746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204d61696e2053746f726167652050726f76696465722e7501202d205b6d73705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6d73705f7369676e5f6f6666292c20776869636820737562747261637473206f6e652066726f6d20746869732073746f726167652e20427370436f756e7401001010000000001c55012054686520616d6f756e74206f66204261636b75702053746f726167652050726f7669646572732074686174206172652063757272656e746c79207265676973746572656420696e207468652072756e74696d652e0019012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f66204253507320696e207468652073797374656d2e007020546869732073746f72616765206973207570646174656420696e3a4d02202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c2077686963682061646473206f6e6520746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722e7501202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c20776869636820737562747261637473206f6e652066726f6d20746869732073746f726167652e44546f74616c42737073436170616369747901003020000000000000000020d02054686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920616c6c204253507320686176652e00c9012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920616c6c2042535073206861766520696e207468652073797374656d2c20776869636820697320616c736f207468658d0120746f74616c20616d6f756e74206f662073746f7261676520636170616369747920746861742063616e206265207573656420627920757365727320696620776520666163746f7220696e20746865207265706c69636174696f6e20666163746f722e007020546869732073746f72616765206973207570646174656420696e3afd02202d205b636f6e6669726d5f7369676e5f75705d2863726174653a3a646973706174636861626c65733a3a636f6e6669726d5f7369676e5f7570292c207768696368206164647320746865206361706163697479206f662074686520726567697374657265642053746f726167652050726f766964657220746f20746869732073746f7261676520696620746865206163636f756e7420746f20636f6e6669726d2069732061204261636b75702053746f726167652050726f76696465722e4502202d205b6273705f7369676e5f6f66665d2863726174653a3a646973706174636861626c65733a3a6273705f7369676e5f6f6666292c2077686963682073756274726163747320746865206361706163697479206f6620746865204261636b75702053746f726167652050726f766964657220746f207369676e206f66662066726f6d20746869732073746f726167652e4055736564427370734361706163697479010030200000000000000000101d012054686520746f74616c20616d6f756e74206f662073746f72616765206361706163697479206f66204253507320746861742069732063757272656e746c7920696e207573652e00e9012054686973206973207573656420746f206b65657020747261636b206f662074686520746f74616c20616d6f756e74206f662073746f7261676520636170616369747920746861742069732063757272656e746c7920696e207573652062792075736572732c2077686963682069732075736566756c20666f720d012073797374656d206d65747269637320616e6420616c736f20746f2063616c63756c617465207468652063757272656e74207072696365206f662073746f726167652e68476c6f62616c4273707352657075746174696f6e576569676874010010100000000004c02054686520746f74616c20676c6f62616c2072657075746174696f6e20776569676874206f6620616c6c20425350732e011d0501f1014020547265617375727900800000000000000000000000000000000000000000000000000000000000000000106020546865205472656173757279204163636f756e7449642e5820546865206163636f756e7420746f2077686963683adc202d20546865206665657320666f72207375626d697474696e672061206368616c6c656e676520617265207472616e736665727265642e94202d2054686520736c61736865642066756e647320617265207472616e736665727265642e3053704d696e4465706f736974184000407a10f35a0000000000000000000004410120546865206d696e696d756d20616d6f756e74207468617420616e206163636f756e742068617320746f206465706f73697420746f206265636f6d6520612073746f726167652070726f76696465722e3453704d696e4361706163697479302002000000000000000481012054686520616d6f756e74207468617420612042535020726563656976657320617320616c6c6f636174696f6e206f662073746f72616765206361706163697479207768656e206974206465706f736974732053704d696e4465706f7369742e384465706f736974506572446174611840020000000000000000000000000000000411032054686520736c6f7065206f662074686520636f6c6c61746572616c2076732073746f726167652063617061636974792063757276652e20496e206f74686572207465726d732c20686f77206d616e7920746f6b656e7320612053746f726167652050726f76696465722073686f756c642061646420617320636f6c6c61746572616c20746f20696e637265617365206974732073746f7261676520636170616369747920696e206f6e6520756e6974206f662053746f7261676544617461556e69742e2c4d617846696c6553697a653020ffffffffffffffff0cbc2054686520657374696d61746564206d6178696d756d2073697a65206f6620616e20756e6b6e6f776e2066696c652e00b9012055736564207072696d6172696c7920746f20736c61736820612053746f726167652050726f7669646572207768656e206974206661696c7320746f2070726f766964652061206368756e6b206f66206461746120666f7220616e20756e6b6e6f776e2066696c652073697a652e4c4d61784d756c74694164647265737353697a65101064000000049020546865206d6178696d756d2073697a65206f662061206d756c7469616464726573732e544d61784d756c746941646472657373416d6f756e74101005000000041d0120546865206d6178696d756d20616d6f756e74206f66206d756c7469616464726573736573207468617420612053746f726167652050726f76696465722063616e20686176652e304d617850726f746f636f6c73101064000000044d0120546865206d6178696d756d206e756d626572206f662070726f746f636f6c7320746865204d53502063616e20737570706f727420286174206c656173742077697468696e207468652072756e74696d65292e284d61784275636b65747310101027000004cc20546865206d6178696d756d20616d6f756e74206f66204275636b65747320746861742061204d53502063616e20686176652e344275636b65744465706f736974184000407a10f35a0000000000000000000004f82054686520616d6f756e74207468617420616e206163636f756e742068617320746f206465706f73697420746f206372656174652061206275636b65742e3c4275636b65744e616d654c696d697410106400000004d82054797065207468617420726570726573656e7473207468652062797465206c696d6974206f662061206275636b6574206e616d652e584d6178426c6f636b73466f7252616e646f6d6e6573731010b004000004990120546865206d6178696d756d20616d6f756e74206f6620626c6f636b732061667465722077686963682061207369676e2075702072657175657374206578706972657320736f207468652072616e646f6d6e6573732063616e6e6f742062652063686f73656e7c4d696e426c6f636b734265747765656e43617061636974794368616e67657310100a00000004fc20546865206d696e696d756d20616d6f756e74206f6620626c6f636b73206265747765656e206361706163697479206368616e67657320666f7220612053504444656661756c744d65726b6c65526f6f74348003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314042501205468652064656661756c742076616c7565206f662074686520726f6f74206f6620746865204d65726b6c652050617472696369612054726965206f66207468652072756e74696d6564536c617368416d6f756e745065724d617846696c6553697a6518400040e59c30120000000000000000000004c5012054686520736c61736820666163746f722064656475637465642066726f6d20612053746f726167652050726f76696465722773206465706f73697420666f722065766572792073696e676c652073746f726167652070726f6f662074686579206661696c20746f2070726f766964652e605374617274696e6752657075746174696f6e57656967687410100100000004dc205374617274696e672072657075746174696f6e2077656967687420666f722061206e65776c792072656769737465726564204253502e01c906282846696c6553797374656d012846696c6553797374656d443c53746f7261676552657175657374730001040234cd060400004853746f7261676552657175657374427370730001080202dd06e1060400186901204120646f75626c65206d61702066726f6d2073746f72616765207265717565737420746f2042535020604163636f756e7449646073207468617420766f6c756e74656572656420746f2073746f7265207468652066696c652e009d0120416e792042535020756e64657220612073746f7261676520726571756573742070726566697820697320636f6e7369646572656420746f206265206120766f6c756e7465657220616e642063616e2062652072656d6f76656420617420616e792074696d652e8102204f6e6365206120425350207375626d69747320612076616c69642070726f6f6620746f20746865207669612074686520606273705f636f6e6669726d5f73746f72696e67602065787472696e7369632c207468652060636f6e6669726d656460206669656c6420696e205b6053746f7261676552657175657374427370734d65746164617461605d2077696c6c2062652073657420746f206074727565602e00bd01205768656e20612073746f7261676520726571756573742069732065787069726564206f722072656d6f7665642c2074686520636f72726573706f6e64696e672073746f7261676520726571756573742070726566697820696e2074686973206d61702069732072656d6f7665642e684275636b6574735769746853746f7261676552657175657374730001080202dd0698040004f420426f6f6b6b656570696e67206f6620746865206275636b65747320636f6e7461696e696e67206f70656e2073746f726167652072657175657374732e6453746f726167655265717565737445787069726174696f6e730101040210e506040004b42041206d6170206f6620626c6f636b7320746f20657870697265642073746f726167652072657175657374732e7846696c6544656c6574696f6e5265717565737445787069726174696f6e730101040210e906040004cc2041206d6170206f6620626c6f636b7320746f20657870697265642066696c652064656c6574696f6e2072657175657374732e704d6f76654275636b65745265717565737445787069726174696f6e730101040210f506040004c42041206d6170206f6620626c6f636b7320746f2065787069726564206d6f7665206275636b65742072657175657374732ea84e657874417661696c61626c6553746f726167655265717565737445787069726174696f6e426c6f636b01001010000000000c5901204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e65772073746f7261676520726571756573742065787069726174696f6e2e006d0120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a53746f726167655265717565737454746c605d2ebc4e657874417661696c61626c6546696c6544656c6574696f6e5265717565737445787069726174696f6e426c6f636b01001010000000000c7101204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e65772066696c652064656c6574696f6e20726571756573742065787069726174696f6e2e009d0120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a50656e64696e6746696c6544656c6574696f6e5265717565737454746c605d2eb44e657874417661696c61626c654d6f76654275636b65745265717565737445787069726174696f6e426c6f636b01001010000000000c6901204120706f696e74657220746f20746865206561726c6965737420617661696c61626c6520626c6f636b20746f20696e736572742061206e6577206d6f7665206275636b657420726571756573742065787069726174696f6e2e00790120546869732073686f756c6420616c776179732062652067726561746572206f7220657175616c207468616e2063757272656e7420626c6f636b202b205b60436f6e6669673a3a4d6f76654275636b65745265717565737454746c605d2e684e6578745374617274696e67426c6f636b546f436c65616e55700100101000000000141901204120706f696e74657220746f20746865207374617274696e6720626c6f636b20746f20636c65616e20757020657870697265642073746f726167652072657175657374732e006901204966207468697320626c6f636b20697320626568696e64207468652063757272656e7420626c6f636b206e756d6265722c2074686520636c65616e757020616c676f726974686d20696e20606f6e5f69646c65602077696c6c750120617474656d707420746f20616363656c6572617465207468697320626c6f636b20706f696e74657220617320636c6f736520746f206f7220757020746f207468652063757272656e7420626c6f636b206e756d6265722e205468697319012077696c6c20657865637574652070726f7669646564207468617420746865726520697320656e6f7567682072656d61696e696e672077656967687420746f20646f20736f2e6c50656e64696e6746696c6544656c6574696f6e52657175657374730101040200fd0604000c802050656e64696e672066696c652064656c6574696f6e2072657175657374732e00f5012041206d617070696e672066726f6d20612075736572206163636f756e7420696420746f2061206c697374206f662070656e64696e672066696c652064656c6574696f6e2072657175657374732c20686f6c64696e672061207475706c65206f66207468652066696c65206b657920616e64206275636b65742069642e6850656e64696e6753746f7053746f72696e6752657175657374730001080202dd060107040018902050656e64696e672066696c652073746f702073746f72696e672072657175657374732e001102204120646f75626c65206d617070696e672066726f6d204253502049447320746f2061206c697374206f662066696c65206b6579732070656e64696e672073746f702073746f72696e6720726571756573747320746f2074686520626c6f636b20696e2077686963682074686f73652072657175657374732077657265206f70656e65648420616e64207468652070726f76656e2073697a65206f66207468652066696c652efd012054686520626c6f636b206e756d626572206973207573656420746f2061766f69642042535073206265696e672061626c6520746f2073746f702073746f72696e672066696c657320696d6d6564696174656c7920776869636820776f756c6420616c6c6f77207468656d20746f2061766f6964206368616c6c656e676573d501206f66206d697373696e672066696c65732e205468652073697a6520697320746f2062652061626c6520746f2064656372656173652074686569722075736564206361706163697479207768656e207468657920636f6e6669726d20746f2073746f702073746f72696e67207468652066696c652e6450656e64696e674d6f76654275636b657452657175657374730001080202dd060507040010782050656e64696e67206d6f7665206275636b65742072657175657374732e009501204120646f75626c65206d617070696e672066726f6d204d53502049447320746f2061206c697374206f66206275636b65742049447320776869636820746865792063616e20616363657074206f72206465636c696e6520746f2074616b65206f7665722eb8205468652076616c75652069732074686520757365722077686f2072657175657374656420746865206d6f76652e604461746153657276657273466f724d6f76654275636b65740001080202dd0698040004ac204253502064617461207365727665727320666f72206d6f7665206275636b65742072657175657374732e5050656e64696e674275636b657473546f4d6f76650101040234980004090120426f6f6b6b656570696e67206f66206275636b6574732074686174206172652070656e64696e6720746f206265206d6f76656420746f2061206e6577204d53502e445265706c69636174696f6e54617267657401001010000000000cd4204e756d626572206f66204253507320726571756972656420746f2066756c66696c6c20612073746f72616765207265717565737400b501205468697320697320616c736f207573656420617320612064656661756c742076616c756520696620746865204253507320726571756972656420617265206e6f7420737065636966696564207768656e206372656174696e6720612073746f7261676520726571756573742e6c5469636b52616e6765546f4d6178696d756d5468726573686f6c64010010100000000004ed01204e756d626572206f66207469636b7320756e74696c20616c6c204253507320776f756c6420726561636820746865205b60436f6e6669673a3a4d6178696d756d5468726573686f6c64605d20746f20656e73757265207468617420616c6c2042535073206172652061626c6520746f20766f6c756e746565722e012105010d0238604d61784273707350657253746f726167655265717565737410100500000010e8204d6178696d756d206e756d626572206f662053507320284d5350202b20425350732920746861742063616e2073746f726520612066696c652e0055012054686973206973207573656420746f206c696d697420746865206e756d626572206f6620425350732073746f72696e6720612066696c6520616e6420636c61696d696e67207265776172647320666f722069742ead012049662074686973206e756d62657220697320746f6f20686967682c207468656e207468652072657761726420666f722073746f72696e6720612066696c65206d6967687420626520746f2064696c7574656420616e6420706f696e746c65737320746f2073746f72652e784d61784261746368436f6e6669726d53746f72616765526571756573747310100a000000049101204d6178696d756d206261746368206f662073746f7261676520726571756573747320746861742063616e20626520636f6e6669726d6564206174206f6e6365207768656e2063616c6c696e6720606273705f636f6e6669726d5f73746f72696e67602e844d617842617463684d7370526573706f6e6453746f72616765526571756573747310100a000000040502204d6178696d756d206261746368206f662073746f7261676520726571756573747320746861742063616e20626520726573706f6e64656420746f206174206f6e6365207768656e2063616c6c696e6720606d73705f726573706f6e645f73746f726167655f72657175657374735f6d756c7469706c655f6275636b657473602e3c4d617846696c655061746853697a651010000200000488204d6178696d756d20627974652073697a65206f6620612066696c6520706174682e344d617850656572496453697a651010640000000480204d6178696d756d20627974652073697a65206f66206120706565722069642e484d61784e756d6265724f665065657249647310100500000004c8204d6178696d756d206e756d626572206f6620706565722069647320666f7220612073746f7261676520726571756573742e6c4d6178446174615365727665724d756c746941646472657373657310100a00000004e0204d6178696d756d206e756d626572206f66206d756c746961646472657373657320666f7220612073746f7261676520726571756573742e584d6178457870697265644974656d73496e426c6f636b101064000000042901204d6178696d756d206e756d626572206f662065787069726564206974656d73202870657220747970652920746f20636c65616e20757020696e20612073696e676c6520626c6f636b2e4453746f726167655265717565737454746c10102800000004902054696d652d746f2d6c69766520666f7220612073746f7261676520726571756573742e7450656e64696e6746696c6544656c6574696f6e5265717565737454746c10102800000004e1012054696d652d746f2d6c69766520666f7220612070656e64696e672066696c652064656c6574696f6e20726571756573742c2061667465722077686963682061207072696f72697479206368616c6c656e67652069732073656e74206f757420746f20656e666f726365207468652064656c6574696f6e2e504d6f76654275636b65745265717565737454746c101028000000045d012054696d652d746f2d6c69766520666f722061206d6f7665206275636b657420726571756573742c20616674657220776869636820746865207265717565737420697320636f6e7369646572656420657870697265642e784d61785573657250656e64696e6744656c6574696f6e526571756573747310100a000000040901204d6178696d756d206e756d626572206f662066696c652064656c6574696f6e207265717565737473206120757365722063616e20686176652070656e64696e672e804d61785573657250656e64696e674d6f76654275636b6574526571756573747310100a000000040101204d6178696d756d206e756d626572206f66206d6f7665206275636b6574207265717565737473206120757365722063616e20686176652070656e64696e672e544d696e57616974466f7253746f7053746f72696e6710100a000000040d02204e756d626572206f6620626c6f636b7320726571756972656420746f2070617373206265747765656e2061204253502072657175657374696e6720746f2073746f702073746f72696e6720612066696c6520616e64206974206265696e672061626c6520746f20636f6e6669726d20746f2073746f702073746f72696e672069742e010907293050726f6f66734465616c6572013050726f6f66734465616c657238505469636b546f4368616c6c656e6765735365656400010402103404001085012041206d617070696e672066726f6d206368616c6c656e676573207469636b20746f20612072616e646f6d2073656564207573656420666f722067656e65726174696e6720746865206368616c6c656e67657320696e2074686174207469636b2e0001012054686973206973207573656420746f206b65657020747261636b206f6620746865206368616c6c656e67657327207365656420696e2074686520706173742e8d012054686973206d617070696e6720676f6573206261636b206f6e6c79205b604368616c6c656e6765486973746f72794c656e677468466f72605d20626c6f636b732e2050726576696f7573206368616c6c656e676573206172652072656d6f7665642e685469636b546f436865636b706f696e744368616c6c656e6765730001040210890204001851012041206d617070696e672066726f6d206368616c6c656e676573207469636b20746f206120766563746f72206f6620637573746f6d206368616c6c656e676564206b65797320666f722074686174207469636b2e006d012054686973206973207573656420746f206b65657020747261636b206f6620746865206368616c6c656e67657320746861742068617665206265656e206d61646520696e2074686520706173742c207370656369666963616c6c799020696e2074686520636865636b706f696e74206368616c6c656e676520726f756e64732ef42054686520766563746f7220697320626f756e646564206279205b604d6178437573746f6d4368616c6c656e676573506572426c6f636b466f72605d2e89012054686973206d617070696e6720676f6573206261636b206f6e6c79205b604368616c6c656e6765486973746f72794c656e677468466f72605d207469636b732e2050726576696f7573206368616c6c656e676573206172652072656d6f7665642e484c617374436865636b706f696e745469636b010010100000000018ec20546865206368616c6c656e6765207469636b206f6620746865206c61737420636865636b706f696e74206368616c6c656e676520726f756e642e0069012054686973206973207573656420746f2064657465726d696e65207768656e20746f20696e636c75646520746865206368616c6c656e6765732066726f6d20746865205b604368616c6c656e6765735175657565605d20616e647d01205b605072696f726974794368616c6c656e6765735175657565605d20696e20746865205b605469636b546f436865636b706f696e744368616c6c656e676573605d2053746f726167654d61702e20546865736520636865636b706f696e744d01206368616c6c656e676520726f756e6473206861766520746f20626520616e73776572656420627920414c4c2050726f7669646572732c20616e64207468697320697320656e666f72636564206279207468656820607375626d69745f70726f6f66602065787472696e7369632e605469636b546f50726f766964657273446561646c696e657300010802020d079804002045012041206d617070696e672066726f6d206368616c6c656e6765207469636b20746f206120766563746f72206f66206368616c6c656e6765642050726f76696465727320666f722074686174207469636b2e0049012054686973206973207573656420746f206b65657020747261636b206f66207468652050726f76696465727320746861742068617665206265656e206368616c6c656e6765642c20616e642073686f756c644901207375626d697420612070726f6f66206279207468652074696d65206f6620746865205b604368616c6c656e6765735469636b6572605d207265616368657320746865206e756d62657220757365642061735901206b657920696e20746865206d617070696e672e2050726f7669646572732077686f20646f207375626d697420612070726f6f66206172652072656d6f7665642066726f6d20746865697220726573706563746976653d0120656e74727920616e642070757368656420666f727761726420746f20746865206e657874207469636b20696e20776869636820746865792073686f756c64207375626d697420612070726f6f662e4d012054686f73652077686f20617265207374696c6c20696e2074686520656e747279206279207468652074696d6520746865207469636b20697320726561636865642061726520636f6e7369646572656420746fdc2068617665206661696c656420746f207375626d697420612070726f6f6620616e64207375626a65637420746f20736c617368696e672e884c6173745469636b50726f76696465725375626d69747465644150726f6f66466f7200010402341004002465012041206d617070696e672066726f6d20612050726f766964657220746f20746865206c617374207469636b20666f7220776869636820746865792053484f554c442068617665207375626d697474656420612070726f6f662e410120496620666f7220612050726f7669646572206070602c20604c6173745469636b50726f76696465725375626d69747465644150726f6f66466f725b705d6020697320606e602c207468656e2074686531012050726f76696465722073686f756c64207375626d697420612070726f6f6620666f72207469636b20606e202b207374616b655f746f5f6368616c6c656e67655f706572696f64287029602e007101205468697320676574732075706461746564207768656e20612050726f7669646572207375626d69747320612070726f6f66207375636365737366756c6c7920616e64206973207573656420746f2064657465726d696e65207468652d01206e657874207469636b20666f72207768696368207468652050726f76696465722073686f756c64207375626d697420612070726f6f662c20616e64206974277320646561646c696e652e007101204966207468652050726f7669646572206661696c7320746f207375626d697420612070726f6f6620696e2074696d6520616e6420697320736c61736865642c20746869732077696c6c207374696c6c206765742075706461746564c820746f20746865207469636b2069742073686f756c642068617665207375626d697474656420612070726f6f6620666f722e3c4368616c6c656e676573517565756501001107040018d02041207175657565206f66206b65797320746861742068617665206265656e206368616c6c656e676564206d616e75616c6c792e0011012054686520656c656d656e747320696e20746869732071756575652077696c6c206265206368616c6c656e67656420696e2074686520636f6d696e6720626c6f636b732c450120616c7761797320656e737572696e67207468617420746865206d6178696d756d206e756d626572206f66206368616c6c656e6765732070657220626c6f636b206973206e6f742065786365656465642e350120412060426f756e64656456656360206973207573656420626563617573652074686520607061726974795f7363616c655f636f6465633a3a4d6178456e636f6465644c656e6020747261697455012069732072657175697265642c20627574207573696e672061206056656344657175656020776f756c64206265206d6f726520656666696369656e7420617320746869732069732061204649464f2071756575652e5c5072696f726974794368616c6c656e676573517565756501001507040028f42041207072696f72697479207175657565206f66206b65797320746861742068617665206265656e206368616c6c656e676564206d616e75616c6c792e0025012054686520646966666572656e6365206265747765656e207468697320616e6420604368616c6c656e676573517565756560206973207468617420746865206368616c6c656e676573290120696e20746869732071756575652061726520676976656e207072696f72697479206f76657220746865206f74686572732e20536f20746869732071756575652073686f756c64206265390120656d7074696564206265666f726520616e79206f6620746865206368616c6c656e67657320696e2074686520604368616c6c656e6765735175657565602061726520646973706174636865642ecc20546869732071756575652073686f756c64206e6f742062652061636365737369626c6520746f20746865207075626c69632e11012054686520656c656d656e747320696e20746869732071756575652077696c6c206265206368616c6c656e67656420696e2074686520636f6d696e6720626c6f636b732c450120616c7761797320656e737572696e67207468617420746865206d6178696d756d206e756d626572206f66206368616c6c656e6765732070657220626c6f636b206973206e6f742065786365656465642e350120412060426f756e64656456656360206973207573656420626563617573652074686520607061726974795f7363616c655f636f6465633a3a4d6178456e636f6465644c656e6020747261697455012069732072657175697265642c20627574207573696e672061206056656344657175656020776f756c64206265206d6f726520656666696369656e7420617320746869732069732061204649464f2071756575652e404368616c6c656e6765735469636b6572010010100000000018e8204120636f756e746572206f6620626c6f636b7320696e207768696368206368616c6c656e67657320776572652064697374726962757465642e004101205468697320636f756e746572206973206e6f74206e65636573736172696c79207468652073616d652061732074686520626c6f636b206e756d6265722c206173206368616c6c656e67657320617265490120646973747269627574656420696e2074686520606f6e5f706f6c6c6020686f6f6b2c2077686963682068617070656e732061742074686520626567696e6e696e67206f6620657665727920626c6f636b2ced0120736f206c6f6e672061732074686520626c6f636b206973206e6f742070617274206f662061205b4d756c74692d426c6f636b2d4d6967726174696f6e5d2868747470733a2f2f6769746875622e636f6d2f706172697479746563682f706f6c6b61646f742d73646b2f70756c6c2f313738312920284d424d292e350120447572696e67204d424d736d2c2074686520626c6f636b206e756d62657220696e637265617365732c20627574205b604368616c6c656e6765735469636b6572605d20646f6573206e6f742e48536c61736861626c6550726f7669646572730001040234100400007456616c696450726f6f665375626d6974746572734c6173745469636b730001040210190704001495012041206d617070696e672066726f6d207469636b20746f2050726f7669646572732c20776869636820697320736574206966207468652050726f7669646572207375626d697474656420612076616c69642070726f6f6620696e2074686174207469636b2e005d012054686973206973207573656420746f206b65657020747261636b206f66207468652050726f76696465727320746861742068617665207375626d69747465642070726f6f667320696e20746865206c61737420666577b901207469636b732c20776865726520617661696c6162696c697479206f6e6c7920757020746f20746865206c617374205b60436f6e6669673a3a5461726765745469636b7353746f726167654f665375626d697474657273605d207469636b732069732067756172616e746565642e950120546869732073746f72616765206973207468656e206d61646520617661696c61626c6520666f72206f746865722070616c6c65747320746f20757365207468726f75676820746865206050726f6f665375626d697474657273496e74657266616365602e3c4c61737444656c657465645469636b010010100000000010b90120412076616c7565207468617420726570726573656e747320746865206c617374207469636b2074686174207761732064656c657465642066726f6d20746865205b6056616c696450726f6f665375626d6974746572734c6173745469636b73605d2053746f726167654d61702e00a5012054686973206973207573656420746f206b6e6f77207768696368207469636b20746f2064656c6574652066726f6d20746865205b6056616c696450726f6f665375626d6974746572734c6173745469636b73605d2053746f726167654d6170207768656e207468656820606f6e5f69646c656020686f6f6b2069732063616c6c65642e584368616c6c656e6765735469636b65725061757365640000980400201901204120626f6f6c65616e207468617420726570726573656e7473207768657468657220746865205b604368616c6c656e6765735469636b6572605d206973207061757365642e00c9012042792064656661756c742c2074686973206973206066616c7365602c206d65616e696e67207468617420746865205b604368616c6c656e6765735469636b6572605d20697320696e6372656d656e7465642065766572792074696d6520606f6e5f706f6c6c602069732063616c6c65642ec10120546869732063616e2062652073657420746f2060747275656020776869636820776f756c6420706175736520746865205b604368616c6c656e6765735469636b6572605d2c2070726576656e74696e672060646f5f6e65775f6368616c6c656e6765735f726f756e64602066726f6d6c206265696e672065786563757465642e205468657265666f72653a4d01202d204e6f206e65772072616e646f6d206368616c6c656e67657320776f756c6420626520656d697474656420616e6420616464656420746f205b605469636b546f4368616c6c656e67657353656564605d2e7501202d204e6f206e657720636865636b706f696e74206368616c6c656e67657320776f756c6420626520656d697474656420616e6420616464656420746f205b605469636b546f436865636b706f696e744368616c6c656e676573605d2ef8202d20446561646c696e657320666f722070726f6f66207375626d697373696f6e732061726520696e646566696e6974656c7920706f7374706f6e65642e4050617374426c6f636b73576569676874000104021028040010f82041206d617070696e672066726f6d20626c6f636b206e756d62657220746f2074686520776569676874207573656420696e207468617420626c6f636b2e0021012054686973206973207573656420746f20636865636b20696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e750120497420697320636c656172656420666f7220626c6f636b73206f6c646572207468616e206063757272656e745f626c6f636b60202d20285b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d202b2031292e484e6f7446756c6c426c6f636b73436f756e7401001010000000000c9d0120546865206e756d626572206f6620626c6f636b7320746861742068617665206265656e20636f6e73696465726564205f6e6f745f2066756c6c20696e20746865206c617374205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d2e0021012054686973206973207573656420746f20636865636b20696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e015505015d023c6052616e646f6d4368616c6c656e676573506572426c6f636b10100a00000008510120546865206e756d626572206f662072616e646f6d206368616c6c656e6765732074686174206172652067656e6572617465642070657220626c6f636b2c207573696e67207468652072616e646f6d2073656564682067656e65726174656420666f72207468617420626c6f636b2e6c4d6178437573746f6d4368616c6c656e676573506572426c6f636b10100a000000045d0120546865206d6178696d756d206e756d626572206f6620637573746f6d206368616c6c656e67657320746861742063616e206265206d61646520696e20612073696e676c6520636865636b706f696e7420626c6f636b2e584368616c6c656e6765486973746f72794c656e6774681010640000000ce420546865206e756d626572206f66207469636b732074686174206368616c6c656e67657320686973746f7279206973206b65707420666f722e61012041667465722074686973206d616e79207469636b732c206368616c6c656e676573206172652072656d6f7665642066726f6d205b605469636b546f4368616c6c656e67657353656564605d2053746f726167654d61702e4501204120227469636b2220697320757375616c6c79206f6e6520626c6f636b2c2062757420736f6d6520626c6f636b73206d617920626520736b69707065642064756520746f206d6967726174696f6e732e544368616c6c656e67657351756575654c656e6774681010640000000cc820546865206c656e677468206f662074686520604368616c6c656e6765735175657565602053746f7261676556616c75652e1101205468697320697320746f206c696d6974207468652073697a65206f66207468652071756575652c20616e64207468657265666f726520746865206e756d626572206f6690206d616e75616c206368616c6c656e67657320746861742063616e206265206d6164652e64436865636b706f696e744368616c6c656e6765506572696f641010b80b000024750120546865206e756d626572206f6620626c6f636b7320696e206265747765656e206120636865636b706f696e74206368616c6c656e67657320726f756e642028692e652e207769746820637573746f6d206368616c6c656e676573292e61012054686973206973207573656420746f2064657465726d696e65207768656e20746f20696e636c75646520746865206368616c6c656e6765732066726f6d2074686520604368616c6c656e67657351756575656020616e64710120605072696f726974794368616c6c656e67657351756575656020696e207468652060426c6f636b546f4368616c6c656e676573602053746f726167654d61702e20546865736520636865636b706f696e74206368616c6c656e6765610120726f756e6473206861766520746f20626520616e73776572656420627920414c4c2050726f7669646572732c20616e64207468697320697320656e666f726365642062792074686520607375626d69745f70726f6f66602c2065787472696e7369632e006d01205741524e494e473a205468697320706572696f64206e6565647320746f20626520657175616c206f72206c6172676572207468616e20746865206368616c6c656e676520706572696f64206f662074686520736d616c6c6573747d012050726f766964657220696e20746865206e6574776f726b2e2049662074686520736d616c6c6573742050726f7669646572206861732061206368616c6c656e676520706572696f64206f66203130207469636b732028626c6f636b73292c1101207468656e2074686520636865636b706f696e74206368616c6c656e676520706572696f64206e6565647320746f206265206174206c65617374203130207469636b732e585374616b65546f4368616c6c656e6765506572696f64184000009e1869d0290400000000000000000cd42054686520726174696f20746f20636f6e76657274207374616b65642062616c616e636520746f20626c6f636b20706572696f642e65012054686973206973207573656420746f2064657465726d696e652074686520706572696f6420696e20776869636820612050726f76696465722073686f756c64207375626d697420612070726f6f662c206261736564206f6ee501207468656972207374616b652e2054686520706572696f642069732063616c63756c6174656420617320605374616b65546f4368616c6c656e6765506572696f64202f207374616b65602c2073617475726174696e67206174205b60436f6e6669673a3a4d696e4368616c6c656e6765506572696f64605d2e484d696e4368616c6c656e6765506572696f6410101e00000004550120546865206d696e696d756d20706572696f6420696e20776869636820612050726f76696465722063616e206265206368616c6c656e6765642c207265676172646c657373206f66207468656972207374616b652e5c4368616c6c656e67655469636b73546f6c6572616e63651010320000001c59012054686520746f6c6572616e636520696e206e756d626572206f66207469636b732028616c6d6f7374206571756976616c656e7420746f20626c6f636b732c2062757420736b697070696e67204d424d292074686174590120612050726f76696465722068617320746f207375626d697420612070726f6f662c20636f756e74696e672066726f6d20746865207469636b20746865206368616c6c656e676520697320656d697474656420666f723c20746861742050726f76696465722e00650120466f72206578616d706c652c20696620612050726f766964657220697320737570706f73656420746f207375626d697420612070726f6f6620666f72207469636b20606e602c20616e642074686520746f6c6572616e636569012069732073657420746f206074602c207468656e207468652050726f76696465722068617320746f207375626d697420612070726f6f6620666f72206368616c6c656e67657320696e207469636b20606e602c206265666f72652420606e202b2074602e344368616c6c656e67657346656518400010a5d4e800000000000000000000000cb02054686520666565206368617267656420666f72207375626d697474696e672061206368616c6c656e67652e550120546869732066656520676f657320746f207468652054726561737572792c20616e64206973207573656420746f2070726576656e74207370616d2e20526567697374657265642050726f7669646572732061726558206578656d70742066726f6d2074686973206665652e785461726765745469636b7353746f726167654f665375626d6974746572731010030000000c89012054686520746172676574206e756d626572206f66207469636b7320666f7220776869636820746f2073746f726520746865207375626d6974746572732074686174207375626d69747465642076616c69642070726f6f667320696e207468656d2cad012073746f72656420696e20746865206056616c696450726f6f665375626d6974746572734c6173745469636b73602053746f726167654d61702e20546861742073746f726167652077696c6c206265207472696d6d656420646f776e20746f2074686973206e756d6265723101206f66207469636b7320696e2074686520606f6e5f69646c656020686f6f6b206f6620746869732070616c6c65742c20746f2061766f696420626c6f6174696e67207468652073746174652e504d61785375626d6974746572735065725469636b1010e8030000102d0120546865206d6178696d756d20616d6f756e74206f662050726f76696465727320746861742063616e207375626d697420612070726f6f6620696e20612073696e676c6520626c6f636b2e410120416c74686f75676820746869732063616e206265207365656e20617320616e20617262697472617279206c696d69742c2069662073657420746f2074686520616c7265616479206578697374696e67410120696d706c69636974206c696d697420746861742069732022686f77206d616e7920607375626d69745f70726f6f66602065787472696e736963732066697420696e2074686520776569676874206f66f0206120626c6f636b2c207468697320776f756c646e27742061646420616e79206164646974696f6e616c206172746966696369616c206c696d69742e20547265617375727900800000000000000000000000000000000000000000000000000000000000000000106020546865205472656173757279204163636f756e7449642e5820546865206163636f756e7420746f2077686963683adc202d20546865206665657320666f72207375626d697474696e672061206368616c6c656e676520617265207472616e736665727265642e94202d2054686520736c61736865642066756e647320617265207472616e736665727265642e4c426c6f636b46756c6c6e657373506572696f6410103200000024f82054686520706572696f64206f6620626c6f636b7320666f722077686963682074686520626c6f636b2066756c6c6e65737320697320636865636b65642e00690120546869732069732074686520616d6f756e74206f6620626c6f636b732066726f6d2074686520706173742c20666f722077686963682074686520626c6f636b2066756c6c6e65737320686173206265656e20636865636b65645d0120616e642069732073746f7265642e20426c6f636b73206f6c646572207468616e206063757272656e745f626c6f636b60202d205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d206172655820636c65617265642066726f6d2073746f726167652e008101205468697320636f6e7374616e742073686f756c6420626520657175616c206f7220736d616c6c6572207468616e20746865205b60436f6e6669673a3a4368616c6c656e67655469636b73546f6c6572616e6365605d20636f6e7374616e742c7d012069662074686520676f616c20697320746f2070726576656e74207370616d6d696e672061747461636b73207468617420776f756c642070726576656e7420686f6e6573742050726f7669646572732066726f6d207375626d697474696e67582074686569722070726f6f667320696e2074696d652e54426c6f636b46756c6c6e65737348656164726f6f6d2818a291e146d10414390120546865206d696e696d756d20756e75736564207765696768742074686174206120626c6f636b206d757374206861766520746f20626520636f6e73696465726564205f6e6f745f2066756c6c2e009101205468697320697320757365642061732070617274206f662074686520637269746572696120666f7220636865636b696e6720696620746865206e6574776f726b2069732070726573756d61626c7920756e6465722061207370616d2061747461636b2e890120466f72206578616d706c652c20746869732063616e2062652073657420746f207468652062656e63686d61726b656420776569676874206f66206120607375626d69745f70726f6f66602065787472696e7369632c20776869636820776f756c648d01206d65616e2074686174206120626c6f636b206973206e6f7420636f6e736964657265642066756c6c206966206120607375626d69745f70726f6f66602065787472696e73696320636f756c642068617665207374696c6c2066697420696e2069742e544d696e4e6f7446756c6c426c6f636b73526174696f2107100065cd1d24710120546865206d696e696d756d20726174696f20286f722070657263656e7461676520696620796f752077696c6c29206f6620626c6f636b732074686174206d75737420626520636f6e73696465726564205f6e6f745f2066756c6c2c51012066726f6d2074686520746f74616c206e756d626572206f66205b60436f6e6669673a3a426c6f636b46756c6c6e657373506572696f64605d20626c6f636b732074616b656e20696e746f206163636f756e742e008501204966206c657373207468616e20746869732070657263656e74616765206f6620626c6f636b7320617265206e6f742066756c6c2c20746865206e6574776f726b7320697320636f6e7369646572656420746f2062652070726573756d61626c795420756e6465722061207370616d2061747461636b2e890120546869732063616e20616c736f2062652074686f75676874206f6620617320746865206d6178696d756d20726174696f206f66206d69736265686176696e6720636f6c6c61746f727320746f6c6572617465642e20466f72206578616d706c652ca10120696620746869732069732073657420746f206050657262696c6c3a3a66726f6d5f70657263656e7428353029602c207468656e206966206d6f7265207468616e2068616c66206f6620746865206c6173742060426c6f636b46756c6c6e657373506572696f6460850120626c6f636b7320617265206e6f742066756c6c2c207468656e206f6e65206f662074686f736520626c6f636b7320737572656c79207761732070726f647563656420627920616e20686f6e65737420636f6c6c61746f722c206d65616e696e677501207468617420746865726520776173206174206c65617374206f6e65207472756c79205f6e6f745f2066756c6c20626c6f636b20696e20746865206c6173742060426c6f636b46756c6c6e657373506572696f646020626c6f636b732e0125072a2852616e646f6d6e657373012852616e646f6d6e657373106c4c61746573744f6e6545706f636841676f52616e646f6d6e657373000029070400042d02204c61746573742072616e646f6d2073656564206f627461696e65642066726f6d20746865206f6e652065706f63682061676f2072616e646f6d6e6573732066726f6d20424142452c20616e6420746865206c617465737420626c6f636b20746861742069742063616e2070726f636573732072616e646f6d6e6573732072657175657374732066726f6d6c4c6174657374506172656e74426c6f636b52616e646f6d6e657373000029070400042902204c61746573742072616e646f6d2073656564206f627461696e65642066726f6d2074686520706172656e7420626c6f636b2072616e646f6d6e6573732066726f6d20424142452c20616e6420746865206c617465737420626c6f636b20746861742069742063616e2070726f636573732072616e646f6d6e6573732072657175657374732066726f6d2852656c617945706f636801003020000000000000000004502043757272656e742072656c61792065706f636840496e686572656e74496e636c75646564000098040004e420456e737572657320746865206d616e6461746f727920696e686572656e742077617320696e636c7564656420696e2074686520626c6f636b01590501a50200002b385061796d656e7453747265616d7301385061796d656e7453747265616d7320304f6e506f6c6c5469636b65720100101000000000180901204120636f756e746572206f6620626c6f636b7320666f722077686963682050726f7669646572732063616e206368617267652074686569722073747265616d732e009d01205468697320636f756e746572206973206e6f74206e65636573736172696c79207468652073616d652061732074686520626c6f636b206e756d6265722c20617320746865206c6173742063686172676561626c6520696e666f206f662050726f766964657273bd012028616e642074686520676c6f62616c20707269636520696e6465782920617265207570646174656420696e2074686520606f6e5f706f6c6c6020686f6f6b2c2077686963682068617070656e732061742074686520626567696e6e696e67206f6620657665727920626c6f636b2ced0120736f206c6f6e672061732074686520626c6f636b206973206e6f742070617274206f662061205b4d756c74692d426c6f636b2d4d6967726174696f6e5d2868747470733a2f2f6769746875622e636f6d2f706172697479746563682f706f6c6b61646f742d73646b2f70756c6c2f313738312920284d424d292e190120447572696e67204d424d732c2074686520626c6f636b206e756d62657220696e637265617365732c2062757420604f6e506f6c6c5469636b65726020646f6573206e6f742e5c4669786564526174655061796d656e7453747265616d7300010802022d07310704002481012054686520646f75626c65206d617070696e672066726f6d20612050726f76696465722c20746f206974732070726f76696465642055736572732c20746f2074686569722066697865642d72617465207061796d656e742073747265616d732e0065012054686973206973207573656420746f2073746f726520616e64206d616e6167652066697865642d72617465207061796d656e742073747265616d73206265747765656e20557365727320616e642050726f7669646572732e007020546869732073746f72616765206973207570646174656420696e3aed01202d205b6164645f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e4902202d205b64656c6574655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a64656c6574655f66697865645f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e0102202d205b7570646174655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a7570646174655f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820757064617465732074686520656e7472792773206072617465602ee501202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820757064617465732074686520656e747279277320606c6173745f636861726765645f7469636b602e6444796e616d6963526174655061796d656e7453747265616d7300010802022d07350704002489012054686520646f75626c65206d617070696e672066726f6d20612050726f76696465722c20746f206974732070726f76696465642055736572732c20746f2074686569722064796e616d69632d72617465207061796d656e742073747265616d732e006d012054686973206973207573656420746f2073746f726520616e64206d616e6167652064796e616d69632d72617465207061796d656e742073747265616d73206265747765656e20557365727320616e642050726f7669646572732e007020546869732073746f72616765206973207570646174656420696e3afd01202d205b6164645f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820616464732061206e657720656e74727920746f20746865206d61702e5902202d205b64656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a64656c6574655f64796e616d69635f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f7665732074686520636f72726573706f6e64696e6720656e7472792066726f6d20746865206d61702e3d02202d205b7570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a7570646174655f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820757064617465732074686520656e74727927732060616d6f756e745f70726f7669646564602e1502202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820757064617465732074686520656e7472792773206070726963655f696e6465785f7768656e5f6c6173745f63686172676564602e484c61737443686172676561626c65496e666f0101040234390750000000000000000000000000000000000000000018750220546865206d617070696e672066726f6d20612050726f766964657220746f20697473206c6173742063686172676561626c6520707269636520696e6465782028666f722064796e616d69632d72617465207061796d656e742073747265616d732920616e64206c6173742063686172676561626c65207469636b2028666f722066697865642d72617465207061796d656e742073747265616d73292e0069022054686973206973207573656420746f206b65657020747261636b206f6620746865206c6173742063686172676561626c6520707269636520696e64657820616e64207469636b206e756d62657220666f7220656163682050726f76696465722c20736f20746869732070616c6c65742063616e2063686172676520746865207061796d656e742073747265616d7320636f72726563746c792e007020546869732073746f72616765206973207570646174656420696e3a6502202d205b7570646174655f6c6173745f63686172676561626c655f696e666f5d2863726174653a3a5061796d656e744d616e616765723a3a7570646174655f6c6173745f63686172676561626c655f696e666f292c20776869636820757064617465732074686520656e747279277320606c6173745f63686172676561626c655f7469636b6020616e64206070726963655f696e646578602e445573657273576974686f757446756e6473000104020010040020c50120546865206d617070696e672066726f6d2061207573657220746f20696620697420686173206265656e20666c616767656420666f72206e6f7420686176696e6720656e6f7567682066756e647320746f2070617920666f7220697473207265717565737465642073657276696365732e00c5012054686973206973207573656420746f20666c6167207573657273207468617420646f206e6f74206861766520656e6f7567682066756e647320746f2070617920666f72207468656972207265717565737465642073657276696365732c20736f206f746865722050726f766964657273942063616e2073746f702070726f766964696e6720736572766963657320746f207468656d2e007020546869732073746f72616765206973207570646174656420696e3a9902202d205b6368617267655f7061796d656e745f73747265616d735d2863726174653a3a646973706174636861626c65733a3a6368617267655f7061796d656e745f73747265616d73292c20776869636820656d6974732061206055736572576974686f757446756e647360206576656e7420616e642073657473207468652075736572277320656e74727920696e2074686973206d617020696620697420646f6573206e6f745501206861766520656e6f7567682066756e64732c20616e6420636c656172732074686520656e747279206966206974207761732073657420616e642074686520757365722068617320656e6f7567682066756e64732e3c526567697374657265645573657273010104020010100000000028b90120546865206d617070696e672066726f6d2061207573657220746f20696620697420686173206265656e207265676973746572656420746f20746865206e6574776f726b20616e642074686520616d6f756e74206f66207061796d656e742073747265616d73206974206861732e0029022053696e6365207573657273206861766520746f2070726f766964652061206465706f73697420746f2062652061626c6520746f206f70656e2065616368207061796d656e742073747265616d2c2074686973206973207573656420746f206b65657020747261636b206f662074686520616d6f756e74206f66207061796d656e742073747265616d7361012074686174206120757365722068617320616e6420697420697320616c736f2075736566756c20746f20636865636b2069662061207573657220686173207265676973746572656420746f20746865206e6574776f726b2e007020546869732073746f72616765206973207570646174656420696e3a6902202d205b6164645f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f66697865645f726174655f7061796d656e745f73747265616d292c20776869636820686f6c647320746865206465706f736974206f6620746865207573657220616e642061646473206f6e6520746f20746869732073746f726167652e7902202d205b6164645f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a6164645f64796e616d69635f726174655f7061796d656e745f73747265616d292c20776869636820686f6c647320746865206465706f736974206f6620746865207573657220616e642061646473206f6e6520746f20746869732073746f726167652e7102202d205b72656d6f76655f66697865645f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a72656d6f76655f66697865645f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f766573206f6e652066726f6d20746869732073746f7261676520616e642072656c656173657320746865206465706f7369742e8102202d205b72656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d5d2863726174653a3a646973706174636861626c65733a3a72656d6f76655f64796e616d69635f726174655f7061796d656e745f73747265616d292c2077686963682072656d6f766573206f6e652066726f6d20746869732073746f7261676520616e642072656c656173657320746865206465706f7369742e6843757272656e745072696365506572556e69745065725469636b0100184000000000000000000000000000000000181902205468652063757272656e742070726963652070657220756e697420706572207469636b206f66207468652070726f766964656420736572766963652c207573656420746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732e009102205468697320697320757064617465642065616368207469636b207573696e672074686520666f726d756c61207468617420636f6e7369646572732063757272656e742073797374656d2063617061636974792028746f74616c2073746f72616765206f66207468652073797374656d2920616e642073797374656d20617661696c6162696c6974792028746f74616c2073746f7261676520617661696c61626c65292e007020546869732073746f72616765206973207570646174656420696e3a6d02202d205b646f5f7570646174655f63757272656e745f70726963655f7065725f756e69745f7065725f7469636b5d2863726174653a3a7574696c733a3a646f5f7570646174655f63757272656e745f70726963655f7065725f756e69745f7065725f7469636b292c2077686963682075706461746573207468652063757272656e742070726963652070657220756e697420706572207469636b2e54416363756d756c617465645072696365496e646578010018400000000000000000000000000000000020d1012054686520616363756d756c6174656420707269636520696e6465782073696e63652067656e657369732c207573656420746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732e00f1012054686973206973206571756976616c656e7420746f207768617420697420776f756c64206861766520636f737420746f2073746f7265206f6e6520756e6974206f66207468652070726f766964656420736572766963652073696e63652074686520626567696e6e696e67206f6620746865206e6574776f726b2e050220576520757365207468697320746f2063616c63756c6174652074686520616d6f756e7420746f2063686172676520666f722064796e616d69632d72617465207061796d656e742073747265616d732c20627920636865636b696e67206f75742074686520646966666572656e6365206265747765656e2074686520696e6465785501207768656e20746865207061796d656e742073747265616d20776173206c61737420636861726765642c20616e642074686520696e64657820617420746865206c6173742063686172676561626c65207469636b2e007020546869732073746f72616765206973207570646174656420696e3a2d02202d205b646f5f7570646174655f70726963655f696e6465785d2863726174653a3a7574696c733a3a646f5f7570646174655f70726963655f696e646578292c20776869636820757064617465732074686520616363756d756c6174656420707269636520696e6465782c20616464696e6720746f206974207468652063757272656e742070726963652e015d0501a90208404e657753747265616d4465706f73697410100a00000014910120546865206e756d626572206f66207469636b73207468617420636f72726573706f6e6420746f20746865206465706f7369742074686174206120557365722068617320746f2070617920746f206f70656e2061207061796d656e742073747265616d2ea1012054686973206d65616e7320746861742c2066726f6d207468652062616c616e6365206f6620746865205573657220666f7220776869636820746865207061796d656e742073747265616d206973206265696e6720637265617465642c2074686520616d6f756e74d420604e657753747265616d4465706f736974202a2072617465602077696c6c2062652068656c642061732061206465706f7369742e250220496e207468652063617365206f662064796e616d69632d72617465207061796d656e742073747265616d732c206072617465602077696c6c2062652060616d6f756e745f70726f7669646564202a2063757272656e745f736572766963655f7072696365602c207768657265206063757272656e745f736572766963655f70726963656020686173310120746f2062652070726f7669646564206279207468652070616c6c6574207573696e672074686520605061796d656e7453747265616d73496e746572666163656020696e746572666163652e6055736572576974686f757446756e6473436f6f6c646f776e10106400000008ed0120546865206e756d626572206f66207469636b732074686174206120757365722077696c6c206861766520746f207761697420616674657220697420686173206265656e20666c616767656420617320776974686f75742066756e647320746f2062652061626c6520746f20636c656172207468617420666c6167c90120616e642062652061626c6520746f2070617920666f7220736572766963657320616761696e2e204966207468657265277320616e79206f75747374616e64696e672064656274207768656e2074686520666c616720697320636c65617265642c2069742077696c6c20626520706169642e013d072c284275636b65744e6674730001610501ad02000141072d104e66747301104e6674733c28436f6c6c656374696f6e00010402104507040004642044657461696c73206f66206120636f6c6c656374696f6e2e4c4f776e657273686970416363657074616e636500010402001004000429012054686520636f6c6c656374696f6e2c20696620616e792c206f6620776869636820616e206163636f756e742069732077696c6c696e6720746f2074616b65206f776e6572736869702e1c4163636f756e7400010c020202490798040008590120546865206974656d732068656c6420627920616e7920676976656e206163636f756e743b20736574206f757420746869732077617920736f2074686174206974656d73206f776e656420627920612073696e676c656c206163636f756e742063616e20626520656e756d6572617465642e44436f6c6c656374696f6e4163636f756e7400010802024d079804000869012054686520636f6c6c656374696f6e73206f776e656420627920616e7920676976656e206163636f756e743b20736574206f757420746869732077617920736f207468617420636f6c6c656374696f6e73206f776e65642062799020612073696e676c65206163636f756e742063616e20626520656e756d6572617465642e40436f6c6c656374696f6e526f6c654f66000108020251075507040008d020546865206974656d7320696e206578697374656e636520616e64207468656972206f776e6572736869702064657461696c732ea02053746f72657320636f6c6c656374696f6e20726f6c657320617320706572206163636f756e742e104974656d000108020205035d07040004d020546865206974656d7320696e206578697374656e636520616e64207468656972206f776e6572736869702064657461696c732e50436f6c6c656374696f6e4d657461646174614f660001040210750704000468204d65746164617461206f66206120636f6c6c656374696f6e2e384974656d4d657461646174614f6600010802020503790704000454204d65746164617461206f6620616e206974656d2e244174747269627574650001100202020281078507040004702041747472696275746573206f66206120636f6c6c656374696f6e2e2c4974656d50726963654f66000108020205038d07040004502041207072696365206f6620616e206974656d2e644974656d41747472696275746573417070726f76616c734f6601010802020503910704000468204974656d2061747472696275746520617070726f76616c732e404e657874436f6c6c656374696f6e496400001004000831012053746f726573207468652060436f6c6c656374696f6e496460207468617420697320676f696e6720746f206265207573656420666f7220746865206e65787420636f6c6c656374696f6e2ef02054686973206765747320696e6372656d656e746564207768656e657665722061206e657720636f6c6c656374696f6e20697320637265617465642e3450656e64696e67537761704f660001080202050399070400047c2048616e646c657320616c6c207468652070656e64696e672073776170732e48436f6c6c656374696f6e436f6e6669674f6600010402106d050400046020436f6e666967206f66206120636f6c6c656374696f6e2e304974656d436f6e6669674f660001080202050399050400044c20436f6e666967206f6620616e206974656d2e01690501b1023844436f6c6c656374696f6e4465706f736974184000407a10f35a000000000000000000000401012054686520626173696320616d6f756e74206f662066756e64732074686174206d75737420626520726573657276656420666f7220636f6c6c656374696f6e2e2c4974656d4465706f73697418400010a5d4e8000000000000000000000004f42054686520626173696320616d6f756e74206f662066756e64732074686174206d75737420626520726573657276656420666f7220616e206974656d2e4c4d657461646174614465706f73697442617365184000a0724e180900000000000000000000044d012054686520626173696320616d6f756e74206f662066756e64732074686174206d757374206265207265736572766564207768656e20616464696e67206d6574616461746120746f20796f7572206974656d2e504174747269627574654465706f73697442617365184000a0724e1809000000000000000000000455012054686520626173696320616d6f756e74206f662066756e64732074686174206d757374206265207265736572766564207768656e20616464696e6720616e2061747472696275746520746f20616e206974656d2e384465706f7369745065724279746518400010a5d4e8000000000000000000000008590120546865206164646974696f6e616c2066756e64732074686174206d75737420626520726573657276656420666f7220746865206e756d626572206f662062797465732073746f726520696e206d657461646174612cc02065697468657220226e6f726d616c22206d65746164617461206f7220617474726962757465206d657461646174612e2c537472696e674c696d697410100001000004b020546865206d6178696d756d206c656e677468206f6620646174612073746f726564206f6e2d636861696e2e204b65794c696d697410104000000004a020546865206d6178696d756d206c656e677468206f6620616e20617474726962757465206b65792e2856616c75654c696d697410100001000004a820546865206d6178696d756d206c656e677468206f6620616e206174747269627574652076616c75652e38417070726f76616c734c696d697410101400000004a820546865206d6178696d756d20617070726f76616c7320616e206974656d20636f756c6420686176652e704974656d41747472696275746573417070726f76616c734c696d697410101400000004d420546865206d6178696d756d206174747269627574657320617070726f76616c7320616e206974656d20636f756c6420686176652e1c4d61785469707310100a00000004a820546865206d6178206e756d626572206f6620746970732061207573657220636f756c642073656e642e4c4d6178446561646c696e654475726174696f6e1010001a4f0004a820546865206d6178206475726174696f6e20696e20626c6f636b7320666f7220646561646c696e65732e504d61784174747269627574657350657243616c6c10100a00000004e020546865206d6178206e756d626572206f6620617474726962757465732061207573657220636f756c6420736574207065722063616c6c2e2046656174757265739d0720000000000000000004902044697361626c657320736f6d65206f662070616c6c657427732066656174757265732e01a5073228506172616d65746572730128506172616d65746572730428506172616d657465727300010402d902f5020400044c2053746f72656420706172616d65746572732e01c50501d502000033a907042848436865636b4e6f6e5a65726f53656e646572b1079840436865636b5370656356657273696f6eb5071038436865636b547856657273696f6eb9071030436865636b47656e65736973bd073438436865636b4d6f7274616c697479c1073428436865636b4e6f6e6365c907982c436865636b576569676874cd0798604368617267655472616e73616374696f6e5061796d656e74d107985053746f726167655765696768745265636c61696dd5079844436865636b4d6574616461746148617368d90780e107","id":"1"} \ No newline at end of file diff --git a/api-augment/tsconfig.json b/api-augment/tsconfig.json index 2c33c76bc..d3c4bd779 100644 --- a/api-augment/tsconfig.json +++ b/api-augment/tsconfig.json @@ -6,27 +6,17 @@ "outDir": "dist", "baseUrl": "./", "paths": { - "@storagehub/api-augment/*": [ - "src/*" - ], - "@polkadot/api/augment": [ - "src/interfaces/augment-api.ts" - ], - "@polkadot/types/augment": [ - "src/interfaces/augment-types.ts" - ], - "@polkadot/types/lookup": [ - "src/interfaces/types-lookup.ts" - ] + "@storagehub/api-augment/*": ["src/*"], + "@polkadot/api/augment": ["src/interfaces/augment-api.ts"], + "@polkadot/types/augment": ["src/interfaces/augment-types.ts"], + "@polkadot/types/lookup": ["src/interfaces/types-lookup.ts"] }, "noEmit": false, "declaration": true, "declarationDir": "dist/types", "allowImportingTsExtensions": false, + // TODO: This should not be needed but for some reason in `augment-api-query.ts` it is importing the `StagingXcmV4Xcm` type which is never used in that file + "noUnusedLocals": false }, - "exclude": [ - "node_modules", - "dist", - "scripts" - ] -} \ No newline at end of file + "exclude": ["node_modules", "dist", "scripts"] +} diff --git a/client/blockchain-service/src/commands.rs b/client/blockchain-service/src/commands.rs index 94bada61b..ddeaaa61b 100644 --- a/client/blockchain-service/src/commands.rs +++ b/client/blockchain-service/src/commands.rs @@ -1,775 +1,775 @@ -use anyhow::Result; -use async_trait::async_trait; -use log::warn; -use serde_json::Number; - -use pallet_file_system_runtime_api::{ - QueryBspConfirmChunksToProveForFileError, QueryFileEarliestVolunteerTickError, - QueryMspConfirmChunksToProveForFileError, -}; -use pallet_payment_streams_runtime_api::GetUsersWithDebtOverThresholdError; -use pallet_proofs_dealer_runtime_api::{ - GetChallengePeriodError, GetCheckpointChallengesError, GetLastTickProviderSubmittedProofError, -}; -use pallet_storage_providers_runtime_api::{ - GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, - QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, -}; -use shc_actors_framework::actor::ActorHandle; -use shc_common::types::{ - BlockNumber, BucketId, ChunkId, ForestLeaf, MainStorageProviderId, ProviderId, - RandomnessOutput, StorageProviderId, TickNumber, TrieRemoveMutation, -}; -use sp_api::ApiError; -use sp_core::H256; -use storage_hub_runtime::{AccountId, Balance, StorageDataUnit}; - -use super::{ - handler::BlockchainService, - transaction::SubmittedTransaction, - types::{ - ConfirmStoringRequest, Extrinsic, ExtrinsicResult, RespondStorageRequest, RetryStrategy, - StopStoringForInsolventUserRequest, SubmitProofRequest, Tip, - }, -}; - -const LOG_TARGET: &str = "blockchain-service-interface"; - -/// Commands that can be sent to the BlockchainService actor. -pub enum BlockchainServiceCommand { - SendExtrinsic { - call: storage_hub_runtime::RuntimeCall, - tip: Tip, - callback: tokio::sync::oneshot::Sender>, - }, - GetExtrinsicFromBlock { - block_hash: H256, - extrinsic_hash: H256, - callback: tokio::sync::oneshot::Sender>, - }, - UnwatchExtrinsic { - subscription_id: Number, - callback: tokio::sync::oneshot::Sender>, - }, - WaitForBlock { - block_number: BlockNumber, - callback: tokio::sync::oneshot::Sender>, - }, - WaitForTick { - tick_number: TickNumber, - callback: - tokio::sync::oneshot::Sender>>, - }, - QueryFileEarliestVolunteerTick { - bsp_id: ProviderId, - file_key: H256, - callback: - tokio::sync::oneshot::Sender>, - }, - QueryEarliestChangeCapacityBlock { - bsp_id: ProviderId, - callback: tokio::sync::oneshot::Sender< - Result, - >, - }, - GetNodePublicKey { - callback: tokio::sync::oneshot::Sender, - }, - QueryBspConfirmChunksToProveForFile { - bsp_id: ProviderId, - file_key: H256, - callback: tokio::sync::oneshot::Sender< - Result, QueryBspConfirmChunksToProveForFileError>, - >, - }, - QueryMspConfirmChunksToProveForFile { - msp_id: ProviderId, - file_key: H256, - callback: tokio::sync::oneshot::Sender< - Result, QueryMspConfirmChunksToProveForFileError>, - >, - }, - QueueSubmitProofRequest { - request: SubmitProofRequest, - callback: tokio::sync::oneshot::Sender>, - }, - QueueConfirmBspRequest { - request: ConfirmStoringRequest, - callback: tokio::sync::oneshot::Sender>, - }, - QueueMspRespondStorageRequest { - request: RespondStorageRequest, - callback: tokio::sync::oneshot::Sender>, - }, - QueueStopStoringForInsolventUserRequest { - request: StopStoringForInsolventUserRequest, - callback: tokio::sync::oneshot::Sender>, - }, - QueryChallengesFromSeed { - seed: RandomnessOutput, - provider_id: ProviderId, - count: u32, - callback: tokio::sync::oneshot::Sender, ApiError>>, - }, - QueryForestChallengesFromSeed { - seed: RandomnessOutput, - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender, ApiError>>, - }, - QueryLastTickProviderSubmittedProof { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender< - Result, - >, - }, - QueryChallengePeriod { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender>, - }, - QueryNextChallengeTickForProvider { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender>, - }, - QueryLastCheckpointChallengeTick { - callback: tokio::sync::oneshot::Sender>, - }, - QueryLastCheckpointChallenges { - tick: BlockNumber, - callback: tokio::sync::oneshot::Sender< - Result)>, GetCheckpointChallengesError>, - >, - }, - QueryProviderForestRoot { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender>, - }, - QueryStorageProviderCapacity { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender< - Result, - >, - }, - QueryAvailableStorageCapacity { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender< - Result, - >, - }, - QueryStorageProviderId { - maybe_node_pub_key: Option, - callback: tokio::sync::oneshot::Sender>>, - }, - QueryUsersWithDebt { - provider_id: ProviderId, - min_debt: Balance, - callback: tokio::sync::oneshot::Sender< - Result, GetUsersWithDebtOverThresholdError>, - >, - }, - QueryWorstCaseScenarioSlashableAmount { - provider_id: ProviderId, - callback: tokio::sync::oneshot::Sender>>, - }, - QuerySlashAmountPerMaxFileSize { - callback: tokio::sync::oneshot::Sender>, - }, - QueryMspIdOfBucketId { - bucket_id: BucketId, - callback: - tokio::sync::oneshot::Sender>, - }, -} - -/// Interface for interacting with the BlockchainService actor. -#[async_trait] -pub trait BlockchainServiceInterface { - /// Send an extrinsic to the runtime. - async fn send_extrinsic( - &self, - call: impl Into + Send, - tip: Tip, - ) -> Result; - - /// Get an extrinsic from a block. - async fn get_extrinsic_from_block( - &self, - block_hash: H256, - extrinsic_hash: H256, - ) -> Result; - - /// Unwatch an extrinsic. - async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result<()>; - - /// Wait for a block number. - async fn wait_for_block(&self, block_number: BlockNumber) -> Result<()>; - - /// Wait for a tick number. - async fn wait_for_tick(&self, tick_number: TickNumber) -> Result<(), ApiError>; - - /// Query the earliest tick number that a file was volunteered for storage. - async fn query_file_earliest_volunteer_tick( - &self, - bsp_id: ProviderId, - file_key: H256, - ) -> Result; - - async fn query_earliest_change_capacity_block( - &self, - bsp_id: ProviderId, - ) -> Result; - - /// Get the node's public key. - async fn get_node_public_key(&self) -> sp_core::sr25519::Public; - - /// Query the chunks that a BSP needs to confirm for a file. - async fn query_bsp_confirm_chunks_to_prove_for_file( - &self, - bsp_id: ProviderId, - file_key: H256, - ) -> Result, QueryBspConfirmChunksToProveForFileError>; - - /// Query the chunks that a MSP needs to confirm for a file. - async fn query_msp_confirm_chunks_to_prove_for_file( - &self, - msp_id: ProviderId, - file_key: H256, - ) -> Result, QueryMspConfirmChunksToProveForFileError>; - - /// Queue a SubmitProofRequest to be processed. - async fn queue_submit_proof_request(&self, request: SubmitProofRequest) -> Result<()>; - - /// Queue a ConfirmBspRequest to be processed. - async fn queue_confirm_bsp_request(&self, request: ConfirmStoringRequest) -> Result<()>; - - // Queue a BspStopStoringForInsolventUserRequest to be processed. - async fn queue_stop_storing_for_insolvent_user_request( - &self, - request: StopStoringForInsolventUserRequest, - ) -> Result<()>; - - /// Queue a RespondStoringRequest to be processed. - async fn queue_msp_respond_storage_request(&self, request: RespondStorageRequest) - -> Result<()>; - - /// Query the challenges that a Provider needs to submit for a given seed. - async fn query_challenges_from_seed( - &self, - seed: RandomnessOutput, - provider_id: ProviderId, - count: u32, - ) -> Result, ApiError>; - - /// Query the forest challenges that a Provider needs to submit for a given seed. - /// This is the same as the `query_challenges_from_seed` method, but it does not - /// require specifying the `count`, as the runtime will know how many challenges - /// to generate. - async fn query_forest_challenges_from_seed( - &self, - seed: RandomnessOutput, - provider_id: ProviderId, - ) -> Result, ApiError>; - - /// Query the last tick that a Provider submitted a proof for. - async fn query_last_tick_provider_submitted_proof( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the challenge period for a given Provider. - async fn query_challenge_period( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the next challenge tick for a given Provider. - async fn get_next_challenge_tick_for_provider( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the last checkpoint tick. - async fn query_last_checkpoint_challenge_tick(&self) -> Result; - - /// Query the checkpoint challenges for a given tick. - async fn query_last_checkpoint_challenges( - &self, - tick: BlockNumber, - ) -> Result)>, GetCheckpointChallengesError>; - - /// Query the Merkle Patricia Forest root for a given Provider. - async fn query_provider_forest_root( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the storage capacity for a Provider. - async fn query_storage_provider_capacity( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the available storage capacity for a Provider. - async fn query_available_storage_capacity( - &self, - provider_id: ProviderId, - ) -> Result; - - /// Query the ProviderId for a given account. If no account is provided, the node's account is - /// used. - async fn query_storage_provider_id( - &self, - maybe_node_pub_key: Option, - ) -> Result>; - - async fn query_users_with_debt( - &self, - provider_id: ProviderId, - min_debt: Balance, - ) -> Result, GetUsersWithDebtOverThresholdError>; - - async fn query_worst_case_scenario_slashable_amount( - &self, - provider_id: ProviderId, - ) -> Result>; - - async fn query_slash_amount_per_max_file_size(&self) -> Result; - - /// Helper function to check if an extrinsic failed or succeeded in a block. - fn extrinsic_result(extrinsic: Extrinsic) -> Result; - - /// Helper function to submit an extrinsic with a retry strategy. Returns when the extrinsic is - /// included in a block or when the retry strategy is exhausted. - async fn submit_extrinsic_with_retry( - &self, - call: impl Into + Send, - retry_strategy: RetryStrategy, - ) -> Result<()>; - - /// Helper function to get the MSP ID of a bucket ID. - async fn query_msp_id_of_bucket_id( - &self, - bucket_id: BucketId, - ) -> Result; -} - -/// Implement the BlockchainServiceInterface for the ActorHandle. -#[async_trait] -impl BlockchainServiceInterface for ActorHandle { - async fn send_extrinsic( - &self, - call: impl Into + Send, - tip: Tip, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::SendExtrinsic { - call: call.into(), - tip, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn get_extrinsic_from_block( - &self, - block_hash: H256, - extrinsic_hash: H256, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::GetExtrinsicFromBlock { - block_hash, - extrinsic_hash, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::UnwatchExtrinsic { - subscription_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn wait_for_block(&self, block_number: BlockNumber) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::WaitForBlock { - block_number, - callback, - }; - self.send(message).await; - let rx = rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed."); - rx.await.expect("Failed to wait for block"); - Ok(()) - } - - async fn wait_for_tick(&self, tick_number: TickNumber) -> Result<(), ApiError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::WaitForTick { - tick_number, - callback, - }; - self.send(message).await; - let rx = rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed."); - rx.await.expect("Failed to wait for tick") - } - - async fn query_file_earliest_volunteer_tick( - &self, - bsp_id: ProviderId, - file_key: H256, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::QueryFileEarliestVolunteerTick { - bsp_id, - file_key, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_earliest_change_capacity_block( - &self, - bsp_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = - BlockchainServiceCommand::QueryEarliestChangeCapacityBlock { bsp_id, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - /// Get the node's public key. - async fn get_node_public_key(&self) -> sp_core::sr25519::Public { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::GetNodePublicKey { callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_bsp_confirm_chunks_to_prove_for_file( - &self, - bsp_id: ProviderId, - file_key: H256, - ) -> Result, QueryBspConfirmChunksToProveForFileError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::QueryBspConfirmChunksToProveForFile { - bsp_id, - file_key, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_msp_confirm_chunks_to_prove_for_file( - &self, - msp_id: ProviderId, - file_key: H256, - ) -> Result, QueryMspConfirmChunksToProveForFileError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::QueryMspConfirmChunksToProveForFile { - msp_id, - file_key, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn queue_submit_proof_request(&self, request: SubmitProofRequest) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueueSubmitProofRequest { request, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn queue_confirm_bsp_request(&self, request: ConfirmStoringRequest) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueueConfirmBspRequest { request, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn queue_msp_respond_storage_request( - &self, - request: RespondStorageRequest, - ) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueueMspRespondStorageRequest { request, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn queue_stop_storing_for_insolvent_user_request( - &self, - request: StopStoringForInsolventUserRequest, - ) -> Result<()> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = - BlockchainServiceCommand::QueueStopStoringForInsolventUserRequest { request, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_challenges_from_seed( - &self, - seed: RandomnessOutput, - provider_id: ProviderId, - count: u32, - ) -> Result, ApiError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - // Build command to send to blockchain service. - let message = BlockchainServiceCommand::QueryChallengesFromSeed { - seed, - provider_id, - count, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_forest_challenges_from_seed( - &self, - seed: RandomnessOutput, - provider_id: ProviderId, - ) -> Result, ApiError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryForestChallengesFromSeed { - seed, - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_last_tick_provider_submitted_proof( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryLastTickProviderSubmittedProof { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_challenge_period( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryChallengePeriod { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn get_next_challenge_tick_for_provider( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryNextChallengeTickForProvider { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_last_checkpoint_challenge_tick(&self) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryLastCheckpointChallengeTick { callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_last_checkpoint_challenges( - &self, - tick: BlockNumber, - ) -> Result)>, GetCheckpointChallengesError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryLastCheckpointChallenges { tick, callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_provider_forest_root( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryProviderForestRoot { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_storage_provider_id( - &self, - maybe_node_pub_key: Option, - ) -> Result> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryStorageProviderId { - maybe_node_pub_key, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_storage_provider_capacity( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryStorageProviderCapacity { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_available_storage_capacity( - &self, - provider_id: ProviderId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryAvailableStorageCapacity { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_users_with_debt( - &self, - provider_id: ProviderId, - min_debt: Balance, - ) -> Result, GetUsersWithDebtOverThresholdError> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryUsersWithDebt { - provider_id, - min_debt, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_worst_case_scenario_slashable_amount( - &self, - provider_id: ProviderId, - ) -> Result> { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryWorstCaseScenarioSlashableAmount { - provider_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - async fn query_slash_amount_per_max_file_size(&self) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QuerySlashAmountPerMaxFileSize { callback }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } - - fn extrinsic_result(extrinsic: Extrinsic) -> Result { - for ev in extrinsic.events { - match ev.event { - storage_hub_runtime::RuntimeEvent::System( - frame_system::Event::ExtrinsicFailed { - dispatch_error, - dispatch_info, - }, - ) => { - return Ok(ExtrinsicResult::Failure { - dispatch_info, - dispatch_error, - }); - } - storage_hub_runtime::RuntimeEvent::System( - frame_system::Event::ExtrinsicSuccess { dispatch_info }, - ) => { - return Ok(ExtrinsicResult::Success { dispatch_info }); - } - _ => {} - } - } - - Err(anyhow::anyhow!( - "Extrinsic does not contain an ExtrinsicFailed event." - )) - } - - async fn submit_extrinsic_with_retry( - &self, - call: impl Into + Send, - retry_strategy: RetryStrategy, - ) -> Result<()> { - let call = call.into(); - - for retry_count in 0..=retry_strategy.max_retries { - let tip = retry_strategy.compute_tip(retry_count); - let mut transaction = self - .send_extrinsic(call.clone(), Tip::from(tip as u128)) - .await? - .with_timeout(retry_strategy.timeout); - - if transaction.watch_for_success(&self).await.is_ok() { - return Ok(()); - } - - if let Some(ref should_retry) = retry_strategy.should_retry { - if !should_retry().await { - return Err(anyhow::anyhow!("Exhausted retry strategy")); - } - } - - warn!(target: LOG_TARGET, "Failed to submit transaction, attempt #{}", retry_count + 1); - } - - Ok(()) - } - - async fn query_msp_id_of_bucket_id( - &self, - bucket_id: BucketId, - ) -> Result { - let (callback, rx) = tokio::sync::oneshot::channel(); - let message = BlockchainServiceCommand::QueryMspIdOfBucketId { - bucket_id, - callback, - }; - self.send(message).await; - rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") - } -} +use anyhow::Result; +use async_trait::async_trait; +use log::warn; +use serde_json::Number; + +use pallet_file_system_runtime_api::{ + QueryBspConfirmChunksToProveForFileError, QueryFileEarliestVolunteerTickError, + QueryMspConfirmChunksToProveForFileError, +}; +use pallet_payment_streams_runtime_api::GetUsersWithDebtOverThresholdError; +use pallet_proofs_dealer_runtime_api::{ + GetChallengePeriodError, GetCheckpointChallengesError, GetLastTickProviderSubmittedProofError, +}; +use pallet_storage_providers_runtime_api::{ + GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, + QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, +}; +use shc_actors_framework::actor::ActorHandle; +use shc_common::types::{ + BlockNumber, BucketId, ChunkId, ForestLeaf, MainStorageProviderId, ProviderId, + RandomnessOutput, StorageProviderId, TickNumber, TrieRemoveMutation, +}; +use sp_api::ApiError; +use sp_core::H256; +use storage_hub_runtime::{AccountId, Balance, StorageDataUnit}; + +use super::{ + handler::BlockchainService, + transaction::SubmittedTransaction, + types::{ + ConfirmStoringRequest, Extrinsic, ExtrinsicResult, RespondStorageRequest, RetryStrategy, + StopStoringForInsolventUserRequest, SubmitProofRequest, Tip, + }, +}; + +const LOG_TARGET: &str = "blockchain-service-interface"; + +/// Commands that can be sent to the BlockchainService actor. +pub enum BlockchainServiceCommand { + SendExtrinsic { + call: storage_hub_runtime::RuntimeCall, + tip: Tip, + callback: tokio::sync::oneshot::Sender>, + }, + GetExtrinsicFromBlock { + block_hash: H256, + extrinsic_hash: H256, + callback: tokio::sync::oneshot::Sender>, + }, + UnwatchExtrinsic { + subscription_id: Number, + callback: tokio::sync::oneshot::Sender>, + }, + WaitForBlock { + block_number: BlockNumber, + callback: tokio::sync::oneshot::Sender>, + }, + WaitForTick { + tick_number: TickNumber, + callback: + tokio::sync::oneshot::Sender>>, + }, + QueryFileEarliestVolunteerTick { + bsp_id: ProviderId, + file_key: H256, + callback: + tokio::sync::oneshot::Sender>, + }, + QueryEarliestChangeCapacityBlock { + bsp_id: ProviderId, + callback: tokio::sync::oneshot::Sender< + Result, + >, + }, + GetNodePublicKey { + callback: tokio::sync::oneshot::Sender, + }, + QueryBspConfirmChunksToProveForFile { + bsp_id: ProviderId, + file_key: H256, + callback: tokio::sync::oneshot::Sender< + Result, QueryBspConfirmChunksToProveForFileError>, + >, + }, + QueryMspConfirmChunksToProveForFile { + msp_id: ProviderId, + file_key: H256, + callback: tokio::sync::oneshot::Sender< + Result, QueryMspConfirmChunksToProveForFileError>, + >, + }, + QueueSubmitProofRequest { + request: SubmitProofRequest, + callback: tokio::sync::oneshot::Sender>, + }, + QueueConfirmBspRequest { + request: ConfirmStoringRequest, + callback: tokio::sync::oneshot::Sender>, + }, + QueueMspRespondStorageRequest { + request: RespondStorageRequest, + callback: tokio::sync::oneshot::Sender>, + }, + QueueStopStoringForInsolventUserRequest { + request: StopStoringForInsolventUserRequest, + callback: tokio::sync::oneshot::Sender>, + }, + QueryChallengesFromSeed { + seed: RandomnessOutput, + provider_id: ProviderId, + count: u32, + callback: tokio::sync::oneshot::Sender, ApiError>>, + }, + QueryForestChallengesFromSeed { + seed: RandomnessOutput, + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender, ApiError>>, + }, + QueryLastTickProviderSubmittedProof { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender< + Result, + >, + }, + QueryChallengePeriod { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender>, + }, + QueryNextChallengeTickForProvider { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender>, + }, + QueryLastCheckpointChallengeTick { + callback: tokio::sync::oneshot::Sender>, + }, + QueryLastCheckpointChallenges { + tick: BlockNumber, + callback: tokio::sync::oneshot::Sender< + Result)>, GetCheckpointChallengesError>, + >, + }, + QueryProviderForestRoot { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender>, + }, + QueryStorageProviderCapacity { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender< + Result, + >, + }, + QueryAvailableStorageCapacity { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender< + Result, + >, + }, + QueryStorageProviderId { + maybe_node_pub_key: Option, + callback: tokio::sync::oneshot::Sender>>, + }, + QueryUsersWithDebt { + provider_id: ProviderId, + min_debt: Balance, + callback: tokio::sync::oneshot::Sender< + Result, GetUsersWithDebtOverThresholdError>, + >, + }, + QueryWorstCaseScenarioSlashableAmount { + provider_id: ProviderId, + callback: tokio::sync::oneshot::Sender>>, + }, + QuerySlashAmountPerMaxFileSize { + callback: tokio::sync::oneshot::Sender>, + }, + QueryMspIdOfBucketId { + bucket_id: BucketId, + callback: + tokio::sync::oneshot::Sender>, + }, +} + +/// Interface for interacting with the BlockchainService actor. +#[async_trait] +pub trait BlockchainServiceInterface { + /// Send an extrinsic to the runtime. + async fn send_extrinsic( + &self, + call: impl Into + Send, + tip: Tip, + ) -> Result; + + /// Get an extrinsic from a block. + async fn get_extrinsic_from_block( + &self, + block_hash: H256, + extrinsic_hash: H256, + ) -> Result; + + /// Unwatch an extrinsic. + async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result<()>; + + /// Wait for a block number. + async fn wait_for_block(&self, block_number: BlockNumber) -> Result<()>; + + /// Wait for a tick number. + async fn wait_for_tick(&self, tick_number: TickNumber) -> Result<(), ApiError>; + + /// Query the earliest tick number that a file was volunteered for storage. + async fn query_file_earliest_volunteer_tick( + &self, + bsp_id: ProviderId, + file_key: H256, + ) -> Result; + + async fn query_earliest_change_capacity_block( + &self, + bsp_id: ProviderId, + ) -> Result; + + /// Get the node's public key. + async fn get_node_public_key(&self) -> sp_core::sr25519::Public; + + /// Query the chunks that a BSP needs to confirm for a file. + async fn query_bsp_confirm_chunks_to_prove_for_file( + &self, + bsp_id: ProviderId, + file_key: H256, + ) -> Result, QueryBspConfirmChunksToProveForFileError>; + + /// Query the chunks that a MSP needs to confirm for a file. + async fn query_msp_confirm_chunks_to_prove_for_file( + &self, + msp_id: ProviderId, + file_key: H256, + ) -> Result, QueryMspConfirmChunksToProveForFileError>; + + /// Queue a SubmitProofRequest to be processed. + async fn queue_submit_proof_request(&self, request: SubmitProofRequest) -> Result<()>; + + /// Queue a ConfirmBspRequest to be processed. + async fn queue_confirm_bsp_request(&self, request: ConfirmStoringRequest) -> Result<()>; + + // Queue a BspStopStoringForInsolventUserRequest to be processed. + async fn queue_stop_storing_for_insolvent_user_request( + &self, + request: StopStoringForInsolventUserRequest, + ) -> Result<()>; + + /// Queue a RespondStoringRequest to be processed. + async fn queue_msp_respond_storage_request(&self, request: RespondStorageRequest) + -> Result<()>; + + /// Query the challenges that a Provider needs to submit for a given seed. + async fn query_challenges_from_seed( + &self, + seed: RandomnessOutput, + provider_id: ProviderId, + count: u32, + ) -> Result, ApiError>; + + /// Query the forest challenges that a Provider needs to submit for a given seed. + /// This is the same as the `query_challenges_from_seed` method, but it does not + /// require specifying the `count`, as the runtime will know how many challenges + /// to generate. + async fn query_forest_challenges_from_seed( + &self, + seed: RandomnessOutput, + provider_id: ProviderId, + ) -> Result, ApiError>; + + /// Query the last tick that a Provider submitted a proof for. + async fn query_last_tick_provider_submitted_proof( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the challenge period for a given Provider. + async fn query_challenge_period( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the next challenge tick for a given Provider. + async fn get_next_challenge_tick_for_provider( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the last checkpoint tick. + async fn query_last_checkpoint_challenge_tick(&self) -> Result; + + /// Query the checkpoint challenges for a given tick. + async fn query_last_checkpoint_challenges( + &self, + tick: BlockNumber, + ) -> Result)>, GetCheckpointChallengesError>; + + /// Query the Merkle Patricia Forest root for a given Provider. + async fn query_provider_forest_root( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the storage capacity for a Provider. + async fn query_storage_provider_capacity( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the available storage capacity for a Provider. + async fn query_available_storage_capacity( + &self, + provider_id: ProviderId, + ) -> Result; + + /// Query the ProviderId for a given account. If no account is provided, the node's account is + /// used. + async fn query_storage_provider_id( + &self, + maybe_node_pub_key: Option, + ) -> Result>; + + async fn query_users_with_debt( + &self, + provider_id: ProviderId, + min_debt: Balance, + ) -> Result, GetUsersWithDebtOverThresholdError>; + + async fn query_worst_case_scenario_slashable_amount( + &self, + provider_id: ProviderId, + ) -> Result>; + + async fn query_slash_amount_per_max_file_size(&self) -> Result; + + /// Helper function to check if an extrinsic failed or succeeded in a block. + fn extrinsic_result(extrinsic: Extrinsic) -> Result; + + /// Helper function to submit an extrinsic with a retry strategy. Returns when the extrinsic is + /// included in a block or when the retry strategy is exhausted. + async fn submit_extrinsic_with_retry( + &self, + call: impl Into + Send, + retry_strategy: RetryStrategy, + ) -> Result<()>; + + /// Helper function to get the MSP ID of a bucket ID. + async fn query_msp_id_of_bucket_id( + &self, + bucket_id: BucketId, + ) -> Result; +} + +/// Implement the BlockchainServiceInterface for the ActorHandle. +#[async_trait] +impl BlockchainServiceInterface for ActorHandle { + async fn send_extrinsic( + &self, + call: impl Into + Send, + tip: Tip, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::SendExtrinsic { + call: call.into(), + tip, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn get_extrinsic_from_block( + &self, + block_hash: H256, + extrinsic_hash: H256, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::GetExtrinsicFromBlock { + block_hash, + extrinsic_hash, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::UnwatchExtrinsic { + subscription_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn wait_for_block(&self, block_number: BlockNumber) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::WaitForBlock { + block_number, + callback, + }; + self.send(message).await; + let rx = rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed."); + rx.await.expect("Failed to wait for block"); + Ok(()) + } + + async fn wait_for_tick(&self, tick_number: TickNumber) -> Result<(), ApiError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::WaitForTick { + tick_number, + callback, + }; + self.send(message).await; + let rx = rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed."); + rx.await.expect("Failed to wait for tick") + } + + async fn query_file_earliest_volunteer_tick( + &self, + bsp_id: ProviderId, + file_key: H256, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::QueryFileEarliestVolunteerTick { + bsp_id, + file_key, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_earliest_change_capacity_block( + &self, + bsp_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = + BlockchainServiceCommand::QueryEarliestChangeCapacityBlock { bsp_id, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + /// Get the node's public key. + async fn get_node_public_key(&self) -> sp_core::sr25519::Public { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::GetNodePublicKey { callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_bsp_confirm_chunks_to_prove_for_file( + &self, + bsp_id: ProviderId, + file_key: H256, + ) -> Result, QueryBspConfirmChunksToProveForFileError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::QueryBspConfirmChunksToProveForFile { + bsp_id, + file_key, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_msp_confirm_chunks_to_prove_for_file( + &self, + msp_id: ProviderId, + file_key: H256, + ) -> Result, QueryMspConfirmChunksToProveForFileError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::QueryMspConfirmChunksToProveForFile { + msp_id, + file_key, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn queue_submit_proof_request(&self, request: SubmitProofRequest) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueueSubmitProofRequest { request, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn queue_confirm_bsp_request(&self, request: ConfirmStoringRequest) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueueConfirmBspRequest { request, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn queue_msp_respond_storage_request( + &self, + request: RespondStorageRequest, + ) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueueMspRespondStorageRequest { request, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn queue_stop_storing_for_insolvent_user_request( + &self, + request: StopStoringForInsolventUserRequest, + ) -> Result<()> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = + BlockchainServiceCommand::QueueStopStoringForInsolventUserRequest { request, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_challenges_from_seed( + &self, + seed: RandomnessOutput, + provider_id: ProviderId, + count: u32, + ) -> Result, ApiError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + // Build command to send to blockchain service. + let message = BlockchainServiceCommand::QueryChallengesFromSeed { + seed, + provider_id, + count, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_forest_challenges_from_seed( + &self, + seed: RandomnessOutput, + provider_id: ProviderId, + ) -> Result, ApiError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryForestChallengesFromSeed { + seed, + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_last_tick_provider_submitted_proof( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryLastTickProviderSubmittedProof { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_challenge_period( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryChallengePeriod { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn get_next_challenge_tick_for_provider( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryNextChallengeTickForProvider { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_last_checkpoint_challenge_tick(&self) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryLastCheckpointChallengeTick { callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_last_checkpoint_challenges( + &self, + tick: BlockNumber, + ) -> Result)>, GetCheckpointChallengesError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryLastCheckpointChallenges { tick, callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_provider_forest_root( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryProviderForestRoot { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_storage_provider_id( + &self, + maybe_node_pub_key: Option, + ) -> Result> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryStorageProviderId { + maybe_node_pub_key, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_storage_provider_capacity( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryStorageProviderCapacity { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_available_storage_capacity( + &self, + provider_id: ProviderId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryAvailableStorageCapacity { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_users_with_debt( + &self, + provider_id: ProviderId, + min_debt: Balance, + ) -> Result, GetUsersWithDebtOverThresholdError> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryUsersWithDebt { + provider_id, + min_debt, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_worst_case_scenario_slashable_amount( + &self, + provider_id: ProviderId, + ) -> Result> { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryWorstCaseScenarioSlashableAmount { + provider_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + async fn query_slash_amount_per_max_file_size(&self) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QuerySlashAmountPerMaxFileSize { callback }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } + + fn extrinsic_result(extrinsic: Extrinsic) -> Result { + for ev in extrinsic.events { + match ev.event { + storage_hub_runtime::RuntimeEvent::System( + frame_system::Event::ExtrinsicFailed { + dispatch_error, + dispatch_info, + }, + ) => { + return Ok(ExtrinsicResult::Failure { + dispatch_info, + dispatch_error, + }); + } + storage_hub_runtime::RuntimeEvent::System( + frame_system::Event::ExtrinsicSuccess { dispatch_info }, + ) => { + return Ok(ExtrinsicResult::Success { dispatch_info }); + } + _ => {} + } + } + + Err(anyhow::anyhow!( + "Extrinsic does not contain an ExtrinsicFailed event." + )) + } + + async fn submit_extrinsic_with_retry( + &self, + call: impl Into + Send, + retry_strategy: RetryStrategy, + ) -> Result<()> { + let call = call.into(); + + for retry_count in 0..=retry_strategy.max_retries { + let tip = retry_strategy.compute_tip(retry_count); + let mut transaction = self + .send_extrinsic(call.clone(), Tip::from(tip as u128)) + .await? + .with_timeout(retry_strategy.timeout); + + if transaction.watch_for_success(&self).await.is_ok() { + return Ok(()); + } + + if let Some(ref should_retry) = retry_strategy.should_retry { + if !should_retry().await { + return Err(anyhow::anyhow!("Exhausted retry strategy")); + } + } + + warn!(target: LOG_TARGET, "Failed to submit transaction, attempt #{}", retry_count + 1); + } + + Ok(()) + } + + async fn query_msp_id_of_bucket_id( + &self, + bucket_id: BucketId, + ) -> Result { + let (callback, rx) = tokio::sync::oneshot::channel(); + let message = BlockchainServiceCommand::QueryMspIdOfBucketId { + bucket_id, + callback, + }; + self.send(message).await; + rx.await.expect("Failed to receive response from BlockchainService. Probably means BlockchainService has crashed.") + } +} diff --git a/client/blockchain-service/src/events.rs b/client/blockchain-service/src/events.rs index fa8b0cb3b..5c9ce85bc 100644 --- a/client/blockchain-service/src/events.rs +++ b/client/blockchain-service/src/events.rs @@ -1,363 +1,363 @@ -use codec::{Decode, Encode}; -use sc_network::Multiaddr; -use shc_actors_framework::event_bus::{EventBus, EventBusMessage, ProvidesEventBus}; -use shc_common::types::{ - Balance, BlockNumber, BucketId, FileKey, FileLocation, Fingerprint, ForestRoot, KeyProofs, - PeerIds, ProviderId, RandomnessOutput, StorageData, TrieRemoveMutation, -}; -use sp_core::H256; -use sp_runtime::AccountId32; -use std::sync::Arc; -use tokio::sync::{oneshot, Mutex}; - -use crate::types::{ConfirmStoringRequest, RespondStorageRequest}; - -/// New random challenge emitted by the StorageHub runtime. -/// -/// This event is emitted when there's a new random challenge seed that affects this -/// BSP. In other words, it only pays attention to the random seeds in the challenge -/// period of this BSP. -#[derive(Debug, Clone, Encode, Decode)] -pub struct NewChallengeSeed { - pub provider_id: ProviderId, - pub tick: BlockNumber, - pub seed: RandomnessOutput, -} - -impl EventBusMessage for NewChallengeSeed {} - -/// Multiple new challenge seeds that have to be responded in order. -/// -/// This event is emitted when catching up to proof submissions, and there are multiple -/// new challenge seeds that have to be responded in order. -/// The `seeds` vector is expected to be sorted in ascending order, where the first element -/// is the seed that should be responded to first, and the last element is the seed that -/// should be responded to last. -#[derive(Debug, Clone, Encode, Decode)] -pub struct MultipleNewChallengeSeeds { - pub provider_id: ProviderId, - pub seeds: Vec<(BlockNumber, RandomnessOutput)>, -} - -impl EventBusMessage for MultipleNewChallengeSeeds {} - -/// New storage request event. -/// -/// This event is emitted when a new storage request is created on-chain. -#[derive(Debug, Clone)] -pub struct NewStorageRequest { - /// Account ID of the requester. - pub who: AccountId32, - /// Computed file key for the file. - pub file_key: FileKey, - /// Bucket ID of the file. - pub bucket_id: BucketId, - /// Location of the file (as a file path). - pub location: FileLocation, - /// Fingerprint of the file (root hash of the merkelized file). - pub fingerprint: Fingerprint, - /// Size of the file. - pub size: StorageData, - /// libp2p peer IDs from where the user would send the file. - pub user_peer_ids: PeerIds, -} - -impl EventBusMessage for NewStorageRequest {} - -/// Accepted BSP volunteer event. -/// -/// This event is emitted when a BSP volunteer is accepted to store a file. -#[derive(Debug, Clone)] -pub struct AcceptedBspVolunteer { - pub bsp_id: H256, - pub bucket_id: BucketId, - pub location: FileLocation, - pub fingerprint: Fingerprint, - pub multiaddresses: Vec, - pub owner: AccountId32, - pub size: StorageData, -} - -impl EventBusMessage for AcceptedBspVolunteer {} - -#[derive(Debug, Clone, Encode, Decode)] -pub enum ForestWriteLockTaskData { - SubmitProofRequest(ProcessSubmitProofRequestData), - ConfirmStoringRequest(ProcessConfirmStoringRequestData), - MspRespondStorageRequest(ProcessMspRespondStoringRequestData), - StopStoringForInsolventUserRequest(ProcessStopStoringForInsolventUserRequestData), -} - -impl From for ForestWriteLockTaskData { - fn from(data: ProcessSubmitProofRequestData) -> Self { - Self::SubmitProofRequest(data) - } -} - -impl From for ForestWriteLockTaskData { - fn from(data: ProcessConfirmStoringRequestData) -> Self { - Self::ConfirmStoringRequest(data) - } -} - -impl From for ForestWriteLockTaskData { - fn from(data: ProcessMspRespondStoringRequestData) -> Self { - Self::MspRespondStorageRequest(data) - } -} - -impl From for ForestWriteLockTaskData { - fn from(data: ProcessStopStoringForInsolventUserRequestData) -> Self { - Self::StopStoringForInsolventUserRequest(data) - } -} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct ProcessSubmitProofRequestData { - pub provider_id: ProviderId, - pub tick: BlockNumber, - pub seed: RandomnessOutput, - pub forest_challenges: Vec, - pub checkpoint_challenges: Vec<(H256, Option)>, -} - -#[derive(Debug, Clone)] -pub struct ProcessSubmitProofRequest { - pub data: ProcessSubmitProofRequestData, - pub forest_root_write_tx: Arc>>>, -} - -impl EventBusMessage for ProcessSubmitProofRequest {} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct ProcessConfirmStoringRequestData { - pub confirm_storing_requests: Vec, -} - -#[derive(Debug, Clone)] -pub struct ProcessConfirmStoringRequest { - pub data: ProcessConfirmStoringRequestData, - pub forest_root_write_tx: Arc>>>, -} - -impl EventBusMessage for ProcessConfirmStoringRequest {} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct ProcessMspRespondStoringRequestData { - pub respond_storing_requests: Vec, -} - -#[derive(Debug, Clone)] -pub struct ProcessMspRespondStoringRequest { - pub data: crate::events::ProcessMspRespondStoringRequestData, - pub forest_root_write_tx: Arc>>>, -} - -impl EventBusMessage for ProcessMspRespondStoringRequest {} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct ProcessStopStoringForInsolventUserRequestData { - pub who: AccountId32, -} - -#[derive(Debug, Clone)] -pub struct ProcessStopStoringForInsolventUserRequest { - pub data: ProcessStopStoringForInsolventUserRequestData, - pub forest_root_write_tx: Arc>>>, -} - -impl EventBusMessage for ProcessStopStoringForInsolventUserRequest {} - -/// Slashable Provider event. -/// -/// This event is emitted when a provider is marked as slashable by the runtime. -#[derive(Debug, Clone)] -pub struct SlashableProvider { - pub provider: ProviderId, - pub next_challenge_deadline: BlockNumber, -} - -impl EventBusMessage for SlashableProvider {} - -/// Mutations applied event in a finalised block. -/// -/// This event is emitted when a finalised block is received by the Blockchain service, -/// in which there is a `MutationsApplied` event for one of the providers that this node is tracking. -#[derive(Debug, Clone)] -pub struct FinalisedTrieRemoveMutationsApplied { - pub provider_id: ProviderId, - pub mutations: Vec<(ForestRoot, TrieRemoveMutation)>, - pub new_root: H256, -} - -impl EventBusMessage for FinalisedTrieRemoveMutationsApplied {} - -#[derive(Debug, Clone)] -pub struct ProofAccepted { - pub provider_id: ProviderId, - pub proofs: KeyProofs, -} - -impl EventBusMessage for ProofAccepted {} - -#[derive(Debug, Clone)] -pub struct LastChargeableInfoUpdated { - pub provider_id: ProviderId, - pub last_chargeable_tick: BlockNumber, - pub last_chargeable_price_index: Balance, -} - -impl EventBusMessage for LastChargeableInfoUpdated {} - -/// User without funds event. -/// -/// This event is emitted when a User has been determined as insolvent by the Payment Streams pallet for -/// being unable to pay for their payment streams for a prolonged period of time. -#[derive(Debug, Clone)] -pub struct UserWithoutFunds { - pub who: AccountId32, -} -impl EventBusMessage for UserWithoutFunds {} - -/// Provider stopped storing for insolvent user event. -/// -/// This event is emitted when a provider has stopped storing a file for an insolvent user. -#[derive(Debug, Clone)] -pub struct SpStopStoringInsolventUser { - pub sp_id: ProviderId, - pub file_key: FileKey, - pub owner: AccountId32, - pub location: FileLocation, - pub new_root: H256, -} -impl EventBusMessage for SpStopStoringInsolventUser {} - -/// The event bus provider for the BlockchainService actor. -/// -/// It holds the event buses for the different events that the BlockchainService actor -/// can emit. -#[derive(Clone, Default)] -pub struct BlockchainServiceEventBusProvider { - new_challenge_seed_event_bus: EventBus, - multiple_new_challenge_seeds_event_bus: EventBus, - new_storage_request_event_bus: EventBus, - accepted_bsp_volunteer_event_bus: EventBus, - process_submit_proof_request_event_bus: EventBus, - process_confirm_storage_request_event_bus: EventBus, - process_msp_respond_storing_request_event_bus: EventBus, - process_stop_storing_for_insolvent_user_request_event_bus: - EventBus, - slashable_provider_event_bus: EventBus, - finalised_mutations_applied_event_bus: EventBus, - proof_accepted_event_bus: EventBus, - last_chargeable_info_updated_event_bus: EventBus, - user_without_funds_event_bus: EventBus, - sp_stop_storing_insolvent_user_event_bus: EventBus, -} - -impl BlockchainServiceEventBusProvider { - pub fn new() -> Self { - Self { - new_challenge_seed_event_bus: EventBus::new(), - multiple_new_challenge_seeds_event_bus: EventBus::new(), - new_storage_request_event_bus: EventBus::new(), - accepted_bsp_volunteer_event_bus: EventBus::new(), - process_submit_proof_request_event_bus: EventBus::new(), - process_confirm_storage_request_event_bus: EventBus::new(), - process_msp_respond_storing_request_event_bus: EventBus::new(), - process_stop_storing_for_insolvent_user_request_event_bus: EventBus::new(), - slashable_provider_event_bus: EventBus::new(), - finalised_mutations_applied_event_bus: EventBus::new(), - proof_accepted_event_bus: EventBus::new(), - last_chargeable_info_updated_event_bus: EventBus::new(), - user_without_funds_event_bus: EventBus::new(), - sp_stop_storing_insolvent_user_event_bus: EventBus::new(), - } - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.new_challenge_seed_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.multiple_new_challenge_seeds_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.new_storage_request_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.accepted_bsp_volunteer_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.process_submit_proof_request_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.process_confirm_storage_request_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.process_msp_respond_storing_request_event_bus - } -} - -impl ProvidesEventBus - for BlockchainServiceEventBusProvider -{ - fn event_bus(&self) -> &EventBus { - &self.process_stop_storing_for_insolvent_user_request_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.slashable_provider_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.finalised_mutations_applied_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.proof_accepted_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.last_chargeable_info_updated_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.user_without_funds_event_bus - } -} - -impl ProvidesEventBus for BlockchainServiceEventBusProvider { - fn event_bus(&self) -> &EventBus { - &self.sp_stop_storing_insolvent_user_event_bus - } -} +use codec::{Decode, Encode}; +use sc_network::Multiaddr; +use shc_actors_framework::event_bus::{EventBus, EventBusMessage, ProvidesEventBus}; +use shc_common::types::{ + Balance, BlockNumber, BucketId, FileKey, FileLocation, Fingerprint, ForestRoot, KeyProofs, + PeerIds, ProviderId, RandomnessOutput, StorageData, TrieRemoveMutation, +}; +use sp_core::H256; +use sp_runtime::AccountId32; +use std::sync::Arc; +use tokio::sync::{oneshot, Mutex}; + +use crate::types::{ConfirmStoringRequest, RespondStorageRequest}; + +/// New random challenge emitted by the StorageHub runtime. +/// +/// This event is emitted when there's a new random challenge seed that affects this +/// BSP. In other words, it only pays attention to the random seeds in the challenge +/// period of this BSP. +#[derive(Debug, Clone, Encode, Decode)] +pub struct NewChallengeSeed { + pub provider_id: ProviderId, + pub tick: BlockNumber, + pub seed: RandomnessOutput, +} + +impl EventBusMessage for NewChallengeSeed {} + +/// Multiple new challenge seeds that have to be responded in order. +/// +/// This event is emitted when catching up to proof submissions, and there are multiple +/// new challenge seeds that have to be responded in order. +/// The `seeds` vector is expected to be sorted in ascending order, where the first element +/// is the seed that should be responded to first, and the last element is the seed that +/// should be responded to last. +#[derive(Debug, Clone, Encode, Decode)] +pub struct MultipleNewChallengeSeeds { + pub provider_id: ProviderId, + pub seeds: Vec<(BlockNumber, RandomnessOutput)>, +} + +impl EventBusMessage for MultipleNewChallengeSeeds {} + +/// New storage request event. +/// +/// This event is emitted when a new storage request is created on-chain. +#[derive(Debug, Clone)] +pub struct NewStorageRequest { + /// Account ID of the requester. + pub who: AccountId32, + /// Computed file key for the file. + pub file_key: FileKey, + /// Bucket ID of the file. + pub bucket_id: BucketId, + /// Location of the file (as a file path). + pub location: FileLocation, + /// Fingerprint of the file (root hash of the merkelized file). + pub fingerprint: Fingerprint, + /// Size of the file. + pub size: StorageData, + /// libp2p peer IDs from where the user would send the file. + pub user_peer_ids: PeerIds, +} + +impl EventBusMessage for NewStorageRequest {} + +/// Accepted BSP volunteer event. +/// +/// This event is emitted when a BSP volunteer is accepted to store a file. +#[derive(Debug, Clone)] +pub struct AcceptedBspVolunteer { + pub bsp_id: H256, + pub bucket_id: BucketId, + pub location: FileLocation, + pub fingerprint: Fingerprint, + pub multiaddresses: Vec, + pub owner: AccountId32, + pub size: StorageData, +} + +impl EventBusMessage for AcceptedBspVolunteer {} + +#[derive(Debug, Clone, Encode, Decode)] +pub enum ForestWriteLockTaskData { + SubmitProofRequest(ProcessSubmitProofRequestData), + ConfirmStoringRequest(ProcessConfirmStoringRequestData), + MspRespondStorageRequest(ProcessMspRespondStoringRequestData), + StopStoringForInsolventUserRequest(ProcessStopStoringForInsolventUserRequestData), +} + +impl From for ForestWriteLockTaskData { + fn from(data: ProcessSubmitProofRequestData) -> Self { + Self::SubmitProofRequest(data) + } +} + +impl From for ForestWriteLockTaskData { + fn from(data: ProcessConfirmStoringRequestData) -> Self { + Self::ConfirmStoringRequest(data) + } +} + +impl From for ForestWriteLockTaskData { + fn from(data: ProcessMspRespondStoringRequestData) -> Self { + Self::MspRespondStorageRequest(data) + } +} + +impl From for ForestWriteLockTaskData { + fn from(data: ProcessStopStoringForInsolventUserRequestData) -> Self { + Self::StopStoringForInsolventUserRequest(data) + } +} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ProcessSubmitProofRequestData { + pub provider_id: ProviderId, + pub tick: BlockNumber, + pub seed: RandomnessOutput, + pub forest_challenges: Vec, + pub checkpoint_challenges: Vec<(H256, Option)>, +} + +#[derive(Debug, Clone)] +pub struct ProcessSubmitProofRequest { + pub data: ProcessSubmitProofRequestData, + pub forest_root_write_tx: Arc>>>, +} + +impl EventBusMessage for ProcessSubmitProofRequest {} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ProcessConfirmStoringRequestData { + pub confirm_storing_requests: Vec, +} + +#[derive(Debug, Clone)] +pub struct ProcessConfirmStoringRequest { + pub data: ProcessConfirmStoringRequestData, + pub forest_root_write_tx: Arc>>>, +} + +impl EventBusMessage for ProcessConfirmStoringRequest {} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ProcessMspRespondStoringRequestData { + pub respond_storing_requests: Vec, +} + +#[derive(Debug, Clone)] +pub struct ProcessMspRespondStoringRequest { + pub data: crate::events::ProcessMspRespondStoringRequestData, + pub forest_root_write_tx: Arc>>>, +} + +impl EventBusMessage for ProcessMspRespondStoringRequest {} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ProcessStopStoringForInsolventUserRequestData { + pub who: AccountId32, +} + +#[derive(Debug, Clone)] +pub struct ProcessStopStoringForInsolventUserRequest { + pub data: ProcessStopStoringForInsolventUserRequestData, + pub forest_root_write_tx: Arc>>>, +} + +impl EventBusMessage for ProcessStopStoringForInsolventUserRequest {} + +/// Slashable Provider event. +/// +/// This event is emitted when a provider is marked as slashable by the runtime. +#[derive(Debug, Clone)] +pub struct SlashableProvider { + pub provider: ProviderId, + pub next_challenge_deadline: BlockNumber, +} + +impl EventBusMessage for SlashableProvider {} + +/// Mutations applied event in a finalised block. +/// +/// This event is emitted when a finalised block is received by the Blockchain service, +/// in which there is a `MutationsApplied` event for one of the providers that this node is tracking. +#[derive(Debug, Clone)] +pub struct FinalisedTrieRemoveMutationsApplied { + pub provider_id: ProviderId, + pub mutations: Vec<(ForestRoot, TrieRemoveMutation)>, + pub new_root: H256, +} + +impl EventBusMessage for FinalisedTrieRemoveMutationsApplied {} + +#[derive(Debug, Clone)] +pub struct ProofAccepted { + pub provider_id: ProviderId, + pub proofs: KeyProofs, +} + +impl EventBusMessage for ProofAccepted {} + +#[derive(Debug, Clone)] +pub struct LastChargeableInfoUpdated { + pub provider_id: ProviderId, + pub last_chargeable_tick: BlockNumber, + pub last_chargeable_price_index: Balance, +} + +impl EventBusMessage for LastChargeableInfoUpdated {} + +/// User without funds event. +/// +/// This event is emitted when a User has been determined as insolvent by the Payment Streams pallet for +/// being unable to pay for their payment streams for a prolonged period of time. +#[derive(Debug, Clone)] +pub struct UserWithoutFunds { + pub who: AccountId32, +} +impl EventBusMessage for UserWithoutFunds {} + +/// Provider stopped storing for insolvent user event. +/// +/// This event is emitted when a provider has stopped storing a file for an insolvent user. +#[derive(Debug, Clone)] +pub struct SpStopStoringInsolventUser { + pub sp_id: ProviderId, + pub file_key: FileKey, + pub owner: AccountId32, + pub location: FileLocation, + pub new_root: H256, +} +impl EventBusMessage for SpStopStoringInsolventUser {} + +/// The event bus provider for the BlockchainService actor. +/// +/// It holds the event buses for the different events that the BlockchainService actor +/// can emit. +#[derive(Clone, Default)] +pub struct BlockchainServiceEventBusProvider { + new_challenge_seed_event_bus: EventBus, + multiple_new_challenge_seeds_event_bus: EventBus, + new_storage_request_event_bus: EventBus, + accepted_bsp_volunteer_event_bus: EventBus, + process_submit_proof_request_event_bus: EventBus, + process_confirm_storage_request_event_bus: EventBus, + process_msp_respond_storing_request_event_bus: EventBus, + process_stop_storing_for_insolvent_user_request_event_bus: + EventBus, + slashable_provider_event_bus: EventBus, + finalised_mutations_applied_event_bus: EventBus, + proof_accepted_event_bus: EventBus, + last_chargeable_info_updated_event_bus: EventBus, + user_without_funds_event_bus: EventBus, + sp_stop_storing_insolvent_user_event_bus: EventBus, +} + +impl BlockchainServiceEventBusProvider { + pub fn new() -> Self { + Self { + new_challenge_seed_event_bus: EventBus::new(), + multiple_new_challenge_seeds_event_bus: EventBus::new(), + new_storage_request_event_bus: EventBus::new(), + accepted_bsp_volunteer_event_bus: EventBus::new(), + process_submit_proof_request_event_bus: EventBus::new(), + process_confirm_storage_request_event_bus: EventBus::new(), + process_msp_respond_storing_request_event_bus: EventBus::new(), + process_stop_storing_for_insolvent_user_request_event_bus: EventBus::new(), + slashable_provider_event_bus: EventBus::new(), + finalised_mutations_applied_event_bus: EventBus::new(), + proof_accepted_event_bus: EventBus::new(), + last_chargeable_info_updated_event_bus: EventBus::new(), + user_without_funds_event_bus: EventBus::new(), + sp_stop_storing_insolvent_user_event_bus: EventBus::new(), + } + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.new_challenge_seed_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.multiple_new_challenge_seeds_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.new_storage_request_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.accepted_bsp_volunteer_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.process_submit_proof_request_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.process_confirm_storage_request_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.process_msp_respond_storing_request_event_bus + } +} + +impl ProvidesEventBus + for BlockchainServiceEventBusProvider +{ + fn event_bus(&self) -> &EventBus { + &self.process_stop_storing_for_insolvent_user_request_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.slashable_provider_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.finalised_mutations_applied_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.proof_accepted_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.last_chargeable_info_updated_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.user_without_funds_event_bus + } +} + +impl ProvidesEventBus for BlockchainServiceEventBusProvider { + fn event_bus(&self) -> &EventBus { + &self.sp_stop_storing_insolvent_user_event_bus + } +} diff --git a/client/blockchain-service/src/handler.rs b/client/blockchain-service/src/handler.rs index 9b320f5f3..30883750b 100644 --- a/client/blockchain-service/src/handler.rs +++ b/client/blockchain-service/src/handler.rs @@ -1,1249 +1,1249 @@ -use anyhow::anyhow; -use futures::prelude::*; -use log::{debug, trace, warn}; -use std::{ - collections::{BTreeMap, BTreeSet}, - path::PathBuf, - str::FromStr, - sync::Arc, -}; - -use sc_client_api::{ - BlockImportNotification, BlockchainEvents, FinalityNotification, HeaderBackend, -}; -use sc_network::Multiaddr; -use sc_service::RpcHandlers; -use sc_tracing::tracing::{error, info}; -use sp_api::{ApiError, ProvideRuntimeApi}; -use sp_core::H256; -use sp_keystore::{Keystore, KeystorePtr}; -use sp_runtime::{ - traits::{Header, Zero}, - AccountId32, SaturatedConversion, -}; - -use pallet_file_system_runtime_api::{ - FileSystemApi, QueryBspConfirmChunksToProveForFileError, QueryFileEarliestVolunteerTickError, - QueryMspConfirmChunksToProveForFileError, -}; -use pallet_payment_streams_runtime_api::{GetUsersWithDebtOverThresholdError, PaymentStreamsApi}; -use pallet_proofs_dealer_runtime_api::{ - GetChallengePeriodError, GetCheckpointChallengesError, GetLastTickProviderSubmittedProofError, - ProofsDealerApi, -}; -use pallet_storage_providers_runtime_api::{ - GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, - QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, StorageProvidersApi, -}; -use shc_actors_framework::actor::{Actor, ActorEventLoop}; -use shc_common::types::{BlockNumber, ParachainClient, ProviderId}; -use shc_common::{ - blockchain_utils::get_events_at_block, - types::{Fingerprint, TickNumber, BCSV_KEY_TYPE}, -}; -use shp_file_metadata::FileKey; -use storage_hub_runtime::RuntimeEvent; - -use crate::state::OngoingProcessMspRespondStorageRequestCf; -use crate::{ - commands::BlockchainServiceCommand, - events::{ - AcceptedBspVolunteer, BlockchainServiceEventBusProvider, - FinalisedTrieRemoveMutationsApplied, LastChargeableInfoUpdated, NewStorageRequest, - SlashableProvider, SpStopStoringInsolventUser, UserWithoutFunds, - }, - state::{ - BlockchainServiceStateStore, LastProcessedBlockNumberCf, - OngoingProcessConfirmStoringRequestCf, OngoingProcessStopStoringForInsolventUserRequestCf, - }, - transaction::SubmittedTransaction, - typed_store::{CFDequeAPI, ProvidesTypedDbSingleAccess}, - types::{StopStoringForInsolventUserRequest, SubmitProofRequest}, -}; - -pub(crate) const LOG_TARGET: &str = "blockchain-service"; -pub(crate) const SYNC_MODE_MIN_BLOCKS_BEHIND: BlockNumber = 5; - -/// The BlockchainService actor. -/// -/// This actor is responsible for sending extrinsics to the runtime and handling block import notifications. -/// For such purposes, it uses the [`ParachainClient`] to interact with the runtime, the [`RpcHandlers`] to send -/// extrinsics, and the [`Keystore`] to sign the extrinsics. -pub struct BlockchainService { - /// The event bus provider. - pub(crate) event_bus_provider: BlockchainServiceEventBusProvider, - /// The parachain client. Used to interact with the runtime. - pub(crate) client: Arc, - /// The keystore. Used to sign extrinsics. - pub(crate) keystore: KeystorePtr, - /// The RPC handlers. Used to send extrinsics. - pub(crate) rpc_handlers: Arc, - /// Nonce counter for the extrinsics. - pub(crate) nonce_counter: u32, - /// A registry of waiters for a block number. - pub(crate) wait_for_block_request_by_number: - BTreeMap>>, - /// A registry of waiters for a tick number. - pub(crate) wait_for_tick_request_by_number: - BTreeMap>>>, - /// A list of Provider IDs that this node has to pay attention to submit proofs for. - /// This could be a BSP or a list of buckets that an MSP has. - pub(crate) provider_ids: BTreeSet, - /// A lock to prevent multiple tasks from writing to the runtime forest root (send transactions) at the same time. - /// This is a oneshot channel instead of a regular mutex because we want to "lock" in 1 - /// thread (blockchain service) and unlock it at the end of the spawned task. The alternative - /// would be to send a [`MutexGuard`]. - pub(crate) forest_root_write_lock: Option>, - /// The last block number that was processed by the BlockchainService. - /// This is used to detect when the BlockchainService gets out of syncing mode and should therefore - /// run some initialisation tasks. - pub(crate) last_block_processed: BlockNumber, - /// A persistent state store for the BlockchainService actor. - pub(crate) persistent_state: BlockchainServiceStateStore, - /// Pending submit proof requests. Note: this is not kept in the persistent state because of - /// various edge cases when restarting the node, all originating from the "dynamic" way of - /// computing the next challenges tick. This case is handled separately. - pub(crate) pending_submit_proof_requests: BTreeSet, -} - -/// Event loop for the BlockchainService actor. -pub struct BlockchainServiceEventLoop { - receiver: sc_utils::mpsc::TracingUnboundedReceiver, - actor: BlockchainService, -} - -/// Merged event loop message for the BlockchainService actor. -enum MergedEventLoopMessage -where - Block: cumulus_primitives_core::BlockT, -{ - Command(BlockchainServiceCommand), - BlockImportNotification(BlockImportNotification), - FinalityNotification(FinalityNotification), -} - -/// Implement the ActorEventLoop trait for the BlockchainServiceEventLoop. -impl ActorEventLoop for BlockchainServiceEventLoop { - fn new( - actor: BlockchainService, - receiver: sc_utils::mpsc::TracingUnboundedReceiver, - ) -> Self { - Self { actor, receiver } - } - - async fn run(mut self) { - info!(target: LOG_TARGET, "BlockchainService starting up!"); - - // Import notification stream to be notified of new blocks. - // The behaviour of this stream is: - // 1. While the node is syncing to the tip of the chain (initial sync, i.e. it just started - // or got behind due to connectivity issues), it will only notify us of re-orgs. - // 2. Once the node is synced, it will notify us of every new block. - let block_import_notification_stream = self.actor.client.import_notification_stream(); - - // Finality notification stream to be notified of blocks being finalised. - let finality_notification_stream = self.actor.client.finality_notification_stream(); - - // Merging notification streams with command stream. - let mut merged_stream = stream::select_all(vec![ - self.receiver.map(MergedEventLoopMessage::Command).boxed(), - block_import_notification_stream - .map(MergedEventLoopMessage::BlockImportNotification) - .boxed(), - finality_notification_stream - .map(MergedEventLoopMessage::FinalityNotification) - .boxed(), - ]); - - // Process incoming messages. - while let Some(notification) = merged_stream.next().await { - match notification { - MergedEventLoopMessage::Command(command) => { - self.actor.handle_message(command).await; - } - MergedEventLoopMessage::BlockImportNotification(notification) => { - self.actor - .handle_block_import_notification(notification) - .await; - } - MergedEventLoopMessage::FinalityNotification(notification) => { - self.actor.handle_finality_notification(notification).await; - } - }; - } - } -} - -/// Implement the Actor trait for the BlockchainService actor. -impl Actor for BlockchainService { - type Message = BlockchainServiceCommand; - type EventLoop = BlockchainServiceEventLoop; - type EventBusProvider = BlockchainServiceEventBusProvider; - - fn handle_message( - &mut self, - message: Self::Message, - ) -> impl std::future::Future + Send { - async { - match message { - BlockchainServiceCommand::SendExtrinsic { - call, - tip, - callback, - } => match self.send_extrinsic(call, tip).await { - Ok(output) => { - debug!(target: LOG_TARGET, "Extrinsic sent successfully: {:?}", output); - match callback - .send(Ok(SubmittedTransaction::new(output.receiver, output.hash))) - { - Ok(_) => { - trace!(target: LOG_TARGET, "Receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - Err(e) => { - warn!(target: LOG_TARGET, "Failed to send extrinsic: {:?}", e); - - match callback.send(Err(e)) { - Ok(_) => { - trace!(target: LOG_TARGET, "RPC error sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send error message through channel: {:?}", e); - } - } - } - }, - BlockchainServiceCommand::GetExtrinsicFromBlock { - block_hash, - extrinsic_hash, - callback, - } => { - match self - .get_extrinsic_from_block(block_hash, extrinsic_hash) - .await - { - Ok(extrinsic) => { - debug!(target: LOG_TARGET, "Extrinsic retrieved successfully: {:?}", extrinsic); - match callback.send(Ok(extrinsic)) { - Ok(_) => { - trace!(target: LOG_TARGET, "Receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - Err(e) => { - warn!(target: LOG_TARGET, "Failed to retrieve extrinsic: {:?}", e); - match callback.send(Err(e)) { - Ok(_) => { - trace!(target: LOG_TARGET, "Receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - } - } - BlockchainServiceCommand::UnwatchExtrinsic { - subscription_id, - callback, - } => match self.unwatch_extrinsic(subscription_id).await { - Ok(output) => { - debug!(target: LOG_TARGET, "Extrinsic unwatched successfully: {:?}", output); - match callback.send(Ok(())) { - Ok(_) => { - trace!(target: LOG_TARGET, "Receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - Err(e) => { - warn!(target: LOG_TARGET, "Failed to unwatch extrinsic: {:?}", e); - match callback.send(Err(e)) { - Ok(_) => { - trace!(target: LOG_TARGET, "Receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - }, - BlockchainServiceCommand::WaitForBlock { - block_number, - callback, - } => { - let current_block_number = self.client.info().best_number; - - let (tx, rx) = tokio::sync::oneshot::channel(); - - if current_block_number >= block_number { - match tx.send(()) { - Ok(_) => {} - Err(_) => { - error!(target: LOG_TARGET, "Failed to notify task about waiting block number. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message."); - } - } - } else { - self.wait_for_block_request_by_number - .entry(block_number) - .or_insert_with(Vec::new) - .push(tx); - } - - match callback.send(rx) { - Ok(_) => { - trace!(target: LOG_TARGET, "Block message receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send block message receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::WaitForTick { - tick_number, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - // Current Tick should always return a value, unless there's an internal API error. - let current_tick_result = self - .client - .runtime_api() - .get_current_tick(current_block_hash); - - let (tx, rx) = tokio::sync::oneshot::channel(); - - match current_tick_result { - Ok(current_tick) => { - // If there is no API error, and the current tick is greater than or equal to the tick number - // we are waiting for, we notify the task that the tick has been reached. - if current_tick >= tick_number { - match tx.send(Ok(())) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to notify task about tick reached: {:?}. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message.", e); - } - } - } else { - // If the current tick is less than the tick number we are waiting for, we insert it in - // the waiting queue. - self.wait_for_tick_request_by_number - .entry(tick_number) - .or_insert_with(Vec::new) - .push(tx); - } - } - Err(e) => { - // If there is an API error, we notify the task about it immediately. - match tx.send(Err(e)) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to notify API error to task querying current tick: {:?}. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message.", e); - } - } - } - } - - match callback.send(rx) { - Ok(_) => { - trace!(target: LOG_TARGET, "Tick message receiver sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send tick message receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryEarliestChangeCapacityBlock { bsp_id, callback } => { - let current_block_hash = self.client.info().best_hash; - - let earliest_block_to_change_capacity = self - .client - .runtime_api() - .query_earliest_change_capacity_block(current_block_hash, &bsp_id) - .unwrap_or_else(|_| { - error!(target: LOG_TARGET, "Failed to query earliest block to change capacity"); - Err(QueryEarliestChangeCapacityBlockError::InternalError) - }); - - match callback.send(earliest_block_to_change_capacity) { - Ok(_) => { - trace!(target: LOG_TARGET, "Earliest block to change capacity result sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send earliest block to change capacity: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryFileEarliestVolunteerTick { - bsp_id, - file_key, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let earliest_block_to_volunteer = self - .client - .runtime_api() - .query_earliest_file_volunteer_tick( - current_block_hash, - bsp_id.into(), - file_key, - ) - .unwrap_or_else(|_| { - Err(QueryFileEarliestVolunteerTickError::InternalError) - }); - - match callback.send(earliest_block_to_volunteer) { - Ok(_) => { - trace!(target: LOG_TARGET, "Earliest block to volunteer result sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send earliest block to volunteer: {:?}", e); - } - } - } - BlockchainServiceCommand::GetNodePublicKey { callback } => { - let pub_key = Self::caller_pub_key(self.keystore.clone()); - match callback.send(pub_key) { - Ok(_) => { - trace!(target: LOG_TARGET, "Node's public key sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send node's public key: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryBspConfirmChunksToProveForFile { - bsp_id, - file_key, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let chunks_to_prove = self - .client - .runtime_api() - .query_bsp_confirm_chunks_to_prove_for_file( - current_block_hash, - bsp_id.into(), - file_key, - ) - .unwrap_or_else(|_| { - Err(QueryBspConfirmChunksToProveForFileError::InternalError) - }); - - match callback.send(chunks_to_prove) { - Ok(_) => { - trace!(target: LOG_TARGET, "Chunks to prove file sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send chunks to prove file: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryMspConfirmChunksToProveForFile { - msp_id, - file_key, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let chunks_to_prove = self - .client - .runtime_api() - .query_msp_confirm_chunks_to_prove_for_file( - current_block_hash, - msp_id.into(), - file_key, - ) - .unwrap_or_else(|_| { - Err(QueryMspConfirmChunksToProveForFileError::InternalError) - }); - - match callback.send(chunks_to_prove) { - Ok(_) => { - trace!(target: LOG_TARGET, "Chunks to prove file sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send chunks to prove file: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryChallengesFromSeed { - seed, - provider_id, - count, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let challenges = self.client.runtime_api().get_challenges_from_seed( - current_block_hash, - &seed, - &provider_id, - count, - ); - - match callback.send(challenges) { - Ok(_) => { - trace!(target: LOG_TARGET, "Challenges sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send challenges: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryForestChallengesFromSeed { - seed, - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let challenges = self.client.runtime_api().get_forest_challenges_from_seed( - current_block_hash, - &seed, - &provider_id, - ); - - match callback.send(challenges) { - Ok(_) => { - trace!(target: LOG_TARGET, "Challenges sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send challenges: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryLastTickProviderSubmittedProof { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let last_tick = self - .client - .runtime_api() - .get_last_tick_provider_submitted_proof(current_block_hash, &provider_id) - .unwrap_or_else(|_| { - Err(GetLastTickProviderSubmittedProofError::InternalApiError) - }); - - match callback.send(last_tick) { - Ok(_) => { - trace!(target: LOG_TARGET, "Last tick sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send last tick provider submitted proof: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryChallengePeriod { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let challenge_period = self - .client - .runtime_api() - .get_challenge_period(current_block_hash, &provider_id) - .unwrap_or_else(|_| { - error!(target: LOG_TARGET, "Failed to query challenge period for provider [{:?}]", provider_id); - Err(GetChallengePeriodError::InternalApiError) - }); - - match callback.send(challenge_period) { - Ok(_) => { - trace!(target: LOG_TARGET, "Challenge period sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send challenge period: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryNextChallengeTickForProvider { - provider_id, - callback, - } => { - let next_challenge_tick = - self.get_next_challenge_tick_for_provider(&provider_id); - - match callback.send(next_challenge_tick) { - Ok(_) => { - trace!(target: LOG_TARGET, "Next challenge tick sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send next challenge tick: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryLastCheckpointChallengeTick { callback } => { - let current_block_hash = self.client.info().best_hash; - - let last_checkpoint_tick = self - .client - .runtime_api() - .get_last_checkpoint_challenge_tick(current_block_hash); - - match callback.send(last_checkpoint_tick) { - Ok(_) => { - trace!(target: LOG_TARGET, "Last checkpoint tick sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send last checkpoint challenge tick: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryLastCheckpointChallenges { tick, callback } => { - let current_block_hash = self.client.info().best_hash; - - let checkpoint_challenges = self - .client - .runtime_api() - .get_checkpoint_challenges(current_block_hash, tick) - .unwrap_or_else(|_| Err(GetCheckpointChallengesError::InternalApiError)); - - match callback.send(checkpoint_challenges) { - Ok(_) => { - trace!(target: LOG_TARGET, "Checkpoint challenges sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send checkpoint challenges: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryProviderForestRoot { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let bsp_info = self - .client - .runtime_api() - .get_bsp_info(current_block_hash, &provider_id) - .unwrap_or_else(|_| Err(GetBspInfoError::InternalApiError)); - - let root = bsp_info.map(|bsp_info| bsp_info.root); - - match callback.send(root) { - Ok(_) => { - trace!(target: LOG_TARGET, "BSP root sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send BSP root: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryStorageProviderCapacity { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let capacity = self - .client - .runtime_api() - .query_storage_provider_capacity(current_block_hash, &provider_id) - .unwrap_or_else(|_| Err(QueryStorageProviderCapacityError::InternalError)); - - match callback.send(capacity) { - Ok(_) => { - trace!(target: LOG_TARGET, "Storage provider capacity sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send storage provider capacity: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryAvailableStorageCapacity { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let capacity = self - .client - .runtime_api() - .query_available_storage_capacity(current_block_hash, &provider_id) - .unwrap_or_else(|_| Err(QueryAvailableStorageCapacityError::InternalError)); - - match callback.send(capacity) { - Ok(_) => { - trace!(target: LOG_TARGET, "Available storage capacity sent successfully"); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to send available storage capacity: {:?}", e); - } - } - } - BlockchainServiceCommand::QueueConfirmBspRequest { request, callback } => { - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .pending_confirm_storing_request_deque() - .push_back(request); - state_store_context.commit(); - // We check right away if we can process the request so we don't waste time. - self.check_pending_forest_root_writes(); - match callback.send(Ok(())) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::QueueMspRespondStorageRequest { request, callback } => { - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .pending_msp_respond_storage_request_deque() - .push_back(request); - state_store_context.commit(); - // We check right away if we can process the request so we don't waste time. - self.check_pending_forest_root_writes(); - match callback.send(Ok(())) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::QueueSubmitProofRequest { request, callback } => { - // The strategy used here is to replace the request in the set with the new request. - // This is because new insertions are presumed to be done with more information of the current state of the chain, - // so we want to make sure that the request is the most up-to-date one. - if let Some(replaced_request) = - self.pending_submit_proof_requests.replace(request.clone()) - { - trace!(target: LOG_TARGET, "Replacing pending submit proof request {:?} with {:?}", replaced_request, request); - } - - // We check right away if we can process the request so we don't waste time. - self.check_pending_forest_root_writes(); - match callback.send(Ok(())) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::QueueStopStoringForInsolventUserRequest { - request, - callback, - } => { - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .pending_stop_storing_for_insolvent_user_request_deque() - .push_back(request); - state_store_context.commit(); - // We check right away if we can process the request so we don't waste time. - self.check_pending_forest_root_writes(); - match callback.send(Ok(())) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryStorageProviderId { - maybe_node_pub_key, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let node_pub_key = maybe_node_pub_key - .unwrap_or_else(|| Self::caller_pub_key(self.keystore.clone())); - - let provider_id = self - .client - .runtime_api() - .get_storage_provider_id(current_block_hash, &node_pub_key.into()) - .map_err(|_| anyhow!("Internal API error")); - - match callback.send(provider_id) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send storage provider ID: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryUsersWithDebt { - provider_id, - min_debt, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let users_with_debt = self - .client - .runtime_api() - .get_users_with_debt_over_threshold( - current_block_hash, - &provider_id, - min_debt, - ) - .unwrap_or_else(|e| { - error!(target: LOG_TARGET, "{}", e); - Err(GetUsersWithDebtOverThresholdError::InternalApiError) - }); - - match callback.send(users_with_debt) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send back users with debt: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryWorstCaseScenarioSlashableAmount { - provider_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let worst_case_scenario_slashable_amount = self - .client - .runtime_api() - .get_worst_case_scenario_slashable_amount(current_block_hash, provider_id) - .map_err(|_| anyhow!("Internal API error")); - - match callback.send(worst_case_scenario_slashable_amount) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send back slashable amount: {:?}", e); - } - } - } - BlockchainServiceCommand::QuerySlashAmountPerMaxFileSize { callback } => { - // Get the current block hash. - let current_block_hash = self.client.info().best_hash; - - let slash_amount_per_max_file_size = self - .client - .runtime_api() - .get_slash_amount_per_max_file_size(current_block_hash) - .map_err(|_| anyhow!("Internal API error")); - - match callback.send(slash_amount_per_max_file_size) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send back `SlashAmountPerMaxFileSize`: {:?}", e); - } - } - } - BlockchainServiceCommand::QueryMspIdOfBucketId { - bucket_id, - callback, - } => { - let current_block_hash = self.client.info().best_hash; - - let msp_id = self - .client - .runtime_api() - .query_msp_id_of_bucket_id(current_block_hash, &bucket_id) - .unwrap_or_else(|e| { - error!(target: LOG_TARGET, "{}", e); - Err(QueryMspIdOfBucketIdError::BucketNotFound) - }); - - match callback.send(msp_id) { - Ok(_) => {} - Err(e) => { - error!(target: LOG_TARGET, "Failed to send back MSP ID: {:?}", e); - } - } - } - } - } - } - - fn get_event_bus_provider(&self) -> &Self::EventBusProvider { - &self.event_bus_provider - } -} - -impl BlockchainService { - /// Create a new [`BlockchainService`]. - pub fn new( - client: Arc, - rpc_handlers: Arc, - keystore: KeystorePtr, - rocksdb_root_path: impl Into, - ) -> Self { - Self { - client, - rpc_handlers, - keystore, - event_bus_provider: BlockchainServiceEventBusProvider::new(), - nonce_counter: 0, - wait_for_block_request_by_number: BTreeMap::new(), - wait_for_tick_request_by_number: BTreeMap::new(), - provider_ids: BTreeSet::new(), - forest_root_write_lock: None, - last_block_processed: Zero::zero(), - persistent_state: BlockchainServiceStateStore::new(rocksdb_root_path.into()), - pending_submit_proof_requests: BTreeSet::new(), - } - } - - async fn handle_block_import_notification( - &mut self, - notification: BlockImportNotification, - ) where - Block: cumulus_primitives_core::BlockT, - { - let block_hash: H256 = notification.hash; - let block_number: BlockNumber = (*notification.header.number()).saturated_into(); - - // If this is the first block import notification, we might need to catch up. - info!(target: LOG_TARGET, "Block import notification (#{}): {}", block_number, block_hash); - - // Get provider IDs linked to keys in this node's keystore and update the nonce. - self.pre_block_processing_checks(&block_hash); - - // Check if we just came out of syncing mode. - if block_number - self.last_block_processed < SYNC_MODE_MIN_BLOCKS_BEHIND { - self.handle_initial_sync(notification).await; - } - - self.process_block_import(&block_hash, &block_number).await; - } - - fn pre_block_processing_checks(&mut self, block_hash: &H256) { - // We query the [`BlockchainService`] account nonce at this height - // and update our internal counter if it's smaller than the result. - self.check_nonce(&block_hash); - - // Get provider IDs linked to keys in this node's keystore. - self.get_provider_ids(&block_hash); - } - - /// Handle the first time this node syncs with the chain. - async fn handle_initial_sync(&mut self, notification: BlockImportNotification) - where - Block: cumulus_primitives_core::BlockT, - { - let block_hash: H256 = notification.hash; - let block_number: BlockNumber = (*notification.header.number()).saturated_into(); - - // If this is the first block import notification, we might need to catch up. - info!(target: LOG_TARGET, "First block import notification (synced to #{}): {}", block_number, block_hash); - - // Check if there was an ongoing process confirm storing task. - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - - // Check if there was an ongoing process confirm storing task. - // Note: This would only exist if the node was running as a BSP. - let maybe_ongoing_process_confirm_storing_request = state_store_context - .access_value(&OngoingProcessConfirmStoringRequestCf) - .read(); - - // If there was an ongoing process confirm storing task, we need to re-queue the requests. - if let Some(process_confirm_storing_request) = maybe_ongoing_process_confirm_storing_request - { - for request in process_confirm_storing_request.confirm_storing_requests { - state_store_context - .pending_confirm_storing_request_deque() - .push_back(request); - } - } - - // Check if there was an ongoing process msp respond storage request task. - // Note: This would only exist if the node was running as an MSP. - let maybe_ongoing_process_msp_respond_storage_request = state_store_context - .access_value(&OngoingProcessMspRespondStorageRequestCf) - .read(); - - // If there was an ongoing process msp respond storage request task, we need to re-queue the requests. - if let Some(process_msp_respond_storage_request) = - maybe_ongoing_process_msp_respond_storage_request - { - for request in process_msp_respond_storage_request.respond_storing_requests { - state_store_context - .pending_msp_respond_storage_request_deque() - .push_back(request); - } - } - - // Check if there was an ongoing process stop storing task. - let maybe_ongoing_process_stop_storing_for_insolvent_user_request = state_store_context - .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) - .read(); - - // If there was an ongoing process stop storing task, we need to re-queue the requests. - if let Some(process_stop_storing_for_insolvent_user_request) = - maybe_ongoing_process_stop_storing_for_insolvent_user_request - { - state_store_context - .pending_stop_storing_for_insolvent_user_request_deque() - .push_back(StopStoringForInsolventUserRequest::new( - process_stop_storing_for_insolvent_user_request.who, - )); - } - - state_store_context.commit(); - - // Catch up to proofs that this node might have missed. - for provider_id in self.provider_ids.clone() { - self.proof_submission_catch_up(&block_hash, &provider_id); - } - } - - async fn process_block_import(&mut self, block_hash: &H256, block_number: &BlockNumber) { - info!(target: LOG_TARGET, "Processing block import #{}: {}", block_number, block_hash); - - // Notify all tasks waiting for this block number (or lower). - self.notify_import_block_number(&block_number); - - // Notify all tasks waiting for this tick number (or lower). - // It is not guaranteed that the tick number will increase at every block import. - self.notify_tick_number(&block_hash); - - // Process pending requests that update the forest root. - self.check_pending_forest_root_writes(); - - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - // Get events from storage. - match get_events_at_block(&self.client, block_hash) { - Ok(block_events) => { - // Process the events. - for ev in block_events { - match ev.event.clone() { - // New storage request event coming from pallet-file-system. - RuntimeEvent::FileSystem( - pallet_file_system::Event::NewStorageRequest { - who, - file_key, - bucket_id, - location, - fingerprint, - size, - peer_ids, - }, - ) => self.emit(NewStorageRequest { - who, - file_key: FileKey::from(file_key.as_ref()), - bucket_id, - location, - fingerprint: fingerprint.as_ref().into(), - size, - user_peer_ids: peer_ids, - }), - // A Provider's challenge cycle has been initialised. - RuntimeEvent::ProofsDealer( - pallet_proofs_dealer::Event::NewChallengeCycleInitialised { - current_tick: _, - next_challenge_deadline: _, - provider: provider_id, - maybe_provider_account, - }, - ) => { - // This node only cares if the Provider account matches one of the accounts in the keystore. - if let Some(account) = maybe_provider_account { - let account: Vec = - >::as_ref(&account) - .to_vec(); - if self.keystore.has_keys(&[(account.clone(), BCSV_KEY_TYPE)]) { - // If so, add the Provider ID to the list of Providers that this node is monitoring. - info!(target: LOG_TARGET, "New Provider ID to monitor [{:?}] for account [{:?}]", provider_id, account); - self.provider_ids.insert(provider_id); - } - } - } - // New challenge seed event coming from pallet-proofs-dealer. - RuntimeEvent::ProofsDealer( - pallet_proofs_dealer::Event::NewChallengeSeed { - challenges_ticker, - seed: _, - }, - ) => { - // For each Provider ID this node monitors... - for provider_id in &self.provider_ids { - // ...check if the challenges tick is one that this provider has to submit a proof for. - if self.should_provider_submit_proof( - &block_hash, - provider_id, - &challenges_ticker, - ) { - self.proof_submission_catch_up(&block_hash, provider_id); - } else { - trace!(target: LOG_TARGET, "Challenges tick is not the next one to be submitted for Provider [{:?}]", provider_id); - } - } - } - // A provider has been marked as slashable. - RuntimeEvent::ProofsDealer( - pallet_proofs_dealer::Event::SlashableProvider { - provider, - next_challenge_deadline, - }, - ) => self.emit(SlashableProvider { - provider, - next_challenge_deadline, - }), - // The last chargeable info of a provider has been updated - RuntimeEvent::PaymentStreams( - pallet_payment_streams::Event::LastChargeableInfoUpdated { - provider_id, - last_chargeable_tick, - last_chargeable_price_index, - }, - ) => { - if self.provider_ids.contains(&provider_id) { - self.emit(LastChargeableInfoUpdated { - provider_id: provider_id, - last_chargeable_tick: last_chargeable_tick, - last_chargeable_price_index: last_chargeable_price_index, - }) - } - } - // A user has been flagged as without funds in the runtime - RuntimeEvent::PaymentStreams( - pallet_payment_streams::Event::UserWithoutFunds { who }, - ) => { - self.emit(UserWithoutFunds { who }); - } - // A file was correctly deleted from a user without funds - RuntimeEvent::FileSystem( - pallet_file_system::Event::SpStopStoringInsolventUser { - sp_id, - file_key, - owner, - location, - new_root, - }, - ) => { - if self.provider_ids.contains(&sp_id) { - self.emit(SpStopStoringInsolventUser { - sp_id, - file_key: file_key.into(), - owner, - location, - new_root, - }) - } - } - // This event should only be of any use if a node is run by as a user. - RuntimeEvent::FileSystem( - pallet_file_system::Event::AcceptedBspVolunteer { - bsp_id, - bucket_id, - location, - fingerprint, - multiaddresses, - owner, - size, - }, - ) if owner - == AccountId32::from(Self::caller_pub_key(self.keystore.clone())) => - { - // We try to convert the types coming from the runtime into our expected types. - let fingerprint: Fingerprint = fingerprint.as_bytes().into(); - // Here the Multiaddresses come as a BoundedVec of BoundedVecs of bytes, - // and we need to convert them. Returns if any of the provided multiaddresses are invalid. - let mut multiaddress_vec: Vec = Vec::new(); - for raw_multiaddr in multiaddresses.into_iter() { - let multiaddress = match std::str::from_utf8(&raw_multiaddr) { - Ok(s) => match Multiaddr::from_str(s) { - Ok(multiaddr) => multiaddr, - Err(e) => { - error!(target: LOG_TARGET, "Failed to parse Multiaddress from string in AcceptedBspVolunteer event. bsp: {:?}, file owner: {:?}, file fingerprint: {:?}\n Error: {:?}", bsp_id, owner, fingerprint, e); - return; - } - }, - Err(e) => { - error!(target: LOG_TARGET, "Failed to parse Multiaddress from bytes in AcceptedBspVolunteer event. bsp: {:?}, file owner: {:?}, file fingerprint: {:?}\n Error: {:?}", bsp_id, owner, fingerprint, e); - return; - } - }; - - multiaddress_vec.push(multiaddress); - } - - self.emit(AcceptedBspVolunteer { - bsp_id, - bucket_id, - location, - fingerprint, - multiaddresses: multiaddress_vec, - owner, - size, - }) - } - // Ignore all other events. - _ => {} - } - } - } - Err(e) => { - // TODO: Handle case where the storage cannot be decoded. - // TODO: This would happen if we're parsing a block authored with an older version of the runtime, using - // TODO: a node that has a newer version of the runtime, therefore the EventsVec type is different. - // TODO: Consider using runtime APIs for getting old data of previous blocks, and this just for current blocks. - error!(target: LOG_TARGET, "Failed to get events storage element: {:?}", e); - } - } - state_store_context - .access_value(&LastProcessedBlockNumberCf) - .write(block_number); - state_store_context.commit(); - } - - /// Handle a finality notification. - async fn handle_finality_notification( - &mut self, - notification: FinalityNotification, - ) where - Block: cumulus_primitives_core::BlockT, - { - let block_hash: H256 = notification.hash; - let block_number: BlockNumber = (*notification.header.number()).saturated_into(); - - debug!(target: LOG_TARGET, "Finality notification #{}: {}", block_number, block_hash); - - // Get events from storage. - match get_events_at_block(&self.client, &block_hash) { - Ok(block_events) => { - // Process the events. - for ev in block_events { - match ev.event.clone() { - // New storage request event coming from pallet-file-system. - RuntimeEvent::ProofsDealer( - pallet_proofs_dealer::Event::MutationsApplied { - provider, - mutations, - new_root, - }, - ) => { - // Check if the provider ID is one of the provider IDs this node is tracking. - if self.provider_ids.contains(&provider) { - self.emit(FinalisedTrieRemoveMutationsApplied { - provider_id: provider, - mutations: mutations.clone(), - new_root, - }) - } - } - // Ignore all other events. - _ => {} - } - } - } - Err(e) => { - // TODO: Handle case where the storage cannot be decoded. - // TODO: This would happen if we're parsing a block authored with an older version of the runtime, using - // TODO: a node that has a newer version of the runtime, therefore the EventsVec type is different. - // TODO: Consider using runtime APIs for getting old data of previous blocks, and this just for current blocks. - error!(target: LOG_TARGET, "Failed to get events storage element: {:?}", e); - } - } - } -} +use anyhow::anyhow; +use futures::prelude::*; +use log::{debug, trace, warn}; +use std::{ + collections::{BTreeMap, BTreeSet}, + path::PathBuf, + str::FromStr, + sync::Arc, +}; + +use sc_client_api::{ + BlockImportNotification, BlockchainEvents, FinalityNotification, HeaderBackend, +}; +use sc_network::Multiaddr; +use sc_service::RpcHandlers; +use sc_tracing::tracing::{error, info}; +use sp_api::{ApiError, ProvideRuntimeApi}; +use sp_core::H256; +use sp_keystore::{Keystore, KeystorePtr}; +use sp_runtime::{ + traits::{Header, Zero}, + AccountId32, SaturatedConversion, +}; + +use pallet_file_system_runtime_api::{ + FileSystemApi, QueryBspConfirmChunksToProveForFileError, QueryFileEarliestVolunteerTickError, + QueryMspConfirmChunksToProveForFileError, +}; +use pallet_payment_streams_runtime_api::{GetUsersWithDebtOverThresholdError, PaymentStreamsApi}; +use pallet_proofs_dealer_runtime_api::{ + GetChallengePeriodError, GetCheckpointChallengesError, GetLastTickProviderSubmittedProofError, + ProofsDealerApi, +}; +use pallet_storage_providers_runtime_api::{ + GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, + QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, StorageProvidersApi, +}; +use shc_actors_framework::actor::{Actor, ActorEventLoop}; +use shc_common::types::{BlockNumber, ParachainClient, ProviderId}; +use shc_common::{ + blockchain_utils::get_events_at_block, + types::{Fingerprint, TickNumber, BCSV_KEY_TYPE}, +}; +use shp_file_metadata::FileKey; +use storage_hub_runtime::RuntimeEvent; + +use crate::state::OngoingProcessMspRespondStorageRequestCf; +use crate::{ + commands::BlockchainServiceCommand, + events::{ + AcceptedBspVolunteer, BlockchainServiceEventBusProvider, + FinalisedTrieRemoveMutationsApplied, LastChargeableInfoUpdated, NewStorageRequest, + SlashableProvider, SpStopStoringInsolventUser, UserWithoutFunds, + }, + state::{ + BlockchainServiceStateStore, LastProcessedBlockNumberCf, + OngoingProcessConfirmStoringRequestCf, OngoingProcessStopStoringForInsolventUserRequestCf, + }, + transaction::SubmittedTransaction, + typed_store::{CFDequeAPI, ProvidesTypedDbSingleAccess}, + types::{StopStoringForInsolventUserRequest, SubmitProofRequest}, +}; + +pub(crate) const LOG_TARGET: &str = "blockchain-service"; +pub(crate) const SYNC_MODE_MIN_BLOCKS_BEHIND: BlockNumber = 5; + +/// The BlockchainService actor. +/// +/// This actor is responsible for sending extrinsics to the runtime and handling block import notifications. +/// For such purposes, it uses the [`ParachainClient`] to interact with the runtime, the [`RpcHandlers`] to send +/// extrinsics, and the [`Keystore`] to sign the extrinsics. +pub struct BlockchainService { + /// The event bus provider. + pub(crate) event_bus_provider: BlockchainServiceEventBusProvider, + /// The parachain client. Used to interact with the runtime. + pub(crate) client: Arc, + /// The keystore. Used to sign extrinsics. + pub(crate) keystore: KeystorePtr, + /// The RPC handlers. Used to send extrinsics. + pub(crate) rpc_handlers: Arc, + /// Nonce counter for the extrinsics. + pub(crate) nonce_counter: u32, + /// A registry of waiters for a block number. + pub(crate) wait_for_block_request_by_number: + BTreeMap>>, + /// A registry of waiters for a tick number. + pub(crate) wait_for_tick_request_by_number: + BTreeMap>>>, + /// A list of Provider IDs that this node has to pay attention to submit proofs for. + /// This could be a BSP or a list of buckets that an MSP has. + pub(crate) provider_ids: BTreeSet, + /// A lock to prevent multiple tasks from writing to the runtime forest root (send transactions) at the same time. + /// This is a oneshot channel instead of a regular mutex because we want to "lock" in 1 + /// thread (blockchain service) and unlock it at the end of the spawned task. The alternative + /// would be to send a [`MutexGuard`]. + pub(crate) forest_root_write_lock: Option>, + /// The last block number that was processed by the BlockchainService. + /// This is used to detect when the BlockchainService gets out of syncing mode and should therefore + /// run some initialisation tasks. + pub(crate) last_block_processed: BlockNumber, + /// A persistent state store for the BlockchainService actor. + pub(crate) persistent_state: BlockchainServiceStateStore, + /// Pending submit proof requests. Note: this is not kept in the persistent state because of + /// various edge cases when restarting the node, all originating from the "dynamic" way of + /// computing the next challenges tick. This case is handled separately. + pub(crate) pending_submit_proof_requests: BTreeSet, +} + +/// Event loop for the BlockchainService actor. +pub struct BlockchainServiceEventLoop { + receiver: sc_utils::mpsc::TracingUnboundedReceiver, + actor: BlockchainService, +} + +/// Merged event loop message for the BlockchainService actor. +enum MergedEventLoopMessage +where + Block: cumulus_primitives_core::BlockT, +{ + Command(BlockchainServiceCommand), + BlockImportNotification(BlockImportNotification), + FinalityNotification(FinalityNotification), +} + +/// Implement the ActorEventLoop trait for the BlockchainServiceEventLoop. +impl ActorEventLoop for BlockchainServiceEventLoop { + fn new( + actor: BlockchainService, + receiver: sc_utils::mpsc::TracingUnboundedReceiver, + ) -> Self { + Self { actor, receiver } + } + + async fn run(mut self) { + info!(target: LOG_TARGET, "BlockchainService starting up!"); + + // Import notification stream to be notified of new blocks. + // The behaviour of this stream is: + // 1. While the node is syncing to the tip of the chain (initial sync, i.e. it just started + // or got behind due to connectivity issues), it will only notify us of re-orgs. + // 2. Once the node is synced, it will notify us of every new block. + let block_import_notification_stream = self.actor.client.import_notification_stream(); + + // Finality notification stream to be notified of blocks being finalised. + let finality_notification_stream = self.actor.client.finality_notification_stream(); + + // Merging notification streams with command stream. + let mut merged_stream = stream::select_all(vec![ + self.receiver.map(MergedEventLoopMessage::Command).boxed(), + block_import_notification_stream + .map(MergedEventLoopMessage::BlockImportNotification) + .boxed(), + finality_notification_stream + .map(MergedEventLoopMessage::FinalityNotification) + .boxed(), + ]); + + // Process incoming messages. + while let Some(notification) = merged_stream.next().await { + match notification { + MergedEventLoopMessage::Command(command) => { + self.actor.handle_message(command).await; + } + MergedEventLoopMessage::BlockImportNotification(notification) => { + self.actor + .handle_block_import_notification(notification) + .await; + } + MergedEventLoopMessage::FinalityNotification(notification) => { + self.actor.handle_finality_notification(notification).await; + } + }; + } + } +} + +/// Implement the Actor trait for the BlockchainService actor. +impl Actor for BlockchainService { + type Message = BlockchainServiceCommand; + type EventLoop = BlockchainServiceEventLoop; + type EventBusProvider = BlockchainServiceEventBusProvider; + + fn handle_message( + &mut self, + message: Self::Message, + ) -> impl std::future::Future + Send { + async { + match message { + BlockchainServiceCommand::SendExtrinsic { + call, + tip, + callback, + } => match self.send_extrinsic(call, tip).await { + Ok(output) => { + debug!(target: LOG_TARGET, "Extrinsic sent successfully: {:?}", output); + match callback + .send(Ok(SubmittedTransaction::new(output.receiver, output.hash))) + { + Ok(_) => { + trace!(target: LOG_TARGET, "Receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + Err(e) => { + warn!(target: LOG_TARGET, "Failed to send extrinsic: {:?}", e); + + match callback.send(Err(e)) { + Ok(_) => { + trace!(target: LOG_TARGET, "RPC error sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send error message through channel: {:?}", e); + } + } + } + }, + BlockchainServiceCommand::GetExtrinsicFromBlock { + block_hash, + extrinsic_hash, + callback, + } => { + match self + .get_extrinsic_from_block(block_hash, extrinsic_hash) + .await + { + Ok(extrinsic) => { + debug!(target: LOG_TARGET, "Extrinsic retrieved successfully: {:?}", extrinsic); + match callback.send(Ok(extrinsic)) { + Ok(_) => { + trace!(target: LOG_TARGET, "Receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + Err(e) => { + warn!(target: LOG_TARGET, "Failed to retrieve extrinsic: {:?}", e); + match callback.send(Err(e)) { + Ok(_) => { + trace!(target: LOG_TARGET, "Receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + } + } + BlockchainServiceCommand::UnwatchExtrinsic { + subscription_id, + callback, + } => match self.unwatch_extrinsic(subscription_id).await { + Ok(output) => { + debug!(target: LOG_TARGET, "Extrinsic unwatched successfully: {:?}", output); + match callback.send(Ok(())) { + Ok(_) => { + trace!(target: LOG_TARGET, "Receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + Err(e) => { + warn!(target: LOG_TARGET, "Failed to unwatch extrinsic: {:?}", e); + match callback.send(Err(e)) { + Ok(_) => { + trace!(target: LOG_TARGET, "Receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + }, + BlockchainServiceCommand::WaitForBlock { + block_number, + callback, + } => { + let current_block_number = self.client.info().best_number; + + let (tx, rx) = tokio::sync::oneshot::channel(); + + if current_block_number >= block_number { + match tx.send(()) { + Ok(_) => {} + Err(_) => { + error!(target: LOG_TARGET, "Failed to notify task about waiting block number. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message."); + } + } + } else { + self.wait_for_block_request_by_number + .entry(block_number) + .or_insert_with(Vec::new) + .push(tx); + } + + match callback.send(rx) { + Ok(_) => { + trace!(target: LOG_TARGET, "Block message receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send block message receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::WaitForTick { + tick_number, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + // Current Tick should always return a value, unless there's an internal API error. + let current_tick_result = self + .client + .runtime_api() + .get_current_tick(current_block_hash); + + let (tx, rx) = tokio::sync::oneshot::channel(); + + match current_tick_result { + Ok(current_tick) => { + // If there is no API error, and the current tick is greater than or equal to the tick number + // we are waiting for, we notify the task that the tick has been reached. + if current_tick >= tick_number { + match tx.send(Ok(())) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to notify task about tick reached: {:?}. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message.", e); + } + } + } else { + // If the current tick is less than the tick number we are waiting for, we insert it in + // the waiting queue. + self.wait_for_tick_request_by_number + .entry(tick_number) + .or_insert_with(Vec::new) + .push(tx); + } + } + Err(e) => { + // If there is an API error, we notify the task about it immediately. + match tx.send(Err(e)) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to notify API error to task querying current tick: {:?}. \nThis should never happen, in this same code we have both the sender and receiver of the oneshot channel, so it should always be possible to send the message.", e); + } + } + } + } + + match callback.send(rx) { + Ok(_) => { + trace!(target: LOG_TARGET, "Tick message receiver sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send tick message receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryEarliestChangeCapacityBlock { bsp_id, callback } => { + let current_block_hash = self.client.info().best_hash; + + let earliest_block_to_change_capacity = self + .client + .runtime_api() + .query_earliest_change_capacity_block(current_block_hash, &bsp_id) + .unwrap_or_else(|_| { + error!(target: LOG_TARGET, "Failed to query earliest block to change capacity"); + Err(QueryEarliestChangeCapacityBlockError::InternalError) + }); + + match callback.send(earliest_block_to_change_capacity) { + Ok(_) => { + trace!(target: LOG_TARGET, "Earliest block to change capacity result sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send earliest block to change capacity: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryFileEarliestVolunteerTick { + bsp_id, + file_key, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let earliest_block_to_volunteer = self + .client + .runtime_api() + .query_earliest_file_volunteer_tick( + current_block_hash, + bsp_id.into(), + file_key, + ) + .unwrap_or_else(|_| { + Err(QueryFileEarliestVolunteerTickError::InternalError) + }); + + match callback.send(earliest_block_to_volunteer) { + Ok(_) => { + trace!(target: LOG_TARGET, "Earliest block to volunteer result sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send earliest block to volunteer: {:?}", e); + } + } + } + BlockchainServiceCommand::GetNodePublicKey { callback } => { + let pub_key = Self::caller_pub_key(self.keystore.clone()); + match callback.send(pub_key) { + Ok(_) => { + trace!(target: LOG_TARGET, "Node's public key sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send node's public key: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryBspConfirmChunksToProveForFile { + bsp_id, + file_key, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let chunks_to_prove = self + .client + .runtime_api() + .query_bsp_confirm_chunks_to_prove_for_file( + current_block_hash, + bsp_id.into(), + file_key, + ) + .unwrap_or_else(|_| { + Err(QueryBspConfirmChunksToProveForFileError::InternalError) + }); + + match callback.send(chunks_to_prove) { + Ok(_) => { + trace!(target: LOG_TARGET, "Chunks to prove file sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send chunks to prove file: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryMspConfirmChunksToProveForFile { + msp_id, + file_key, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let chunks_to_prove = self + .client + .runtime_api() + .query_msp_confirm_chunks_to_prove_for_file( + current_block_hash, + msp_id.into(), + file_key, + ) + .unwrap_or_else(|_| { + Err(QueryMspConfirmChunksToProveForFileError::InternalError) + }); + + match callback.send(chunks_to_prove) { + Ok(_) => { + trace!(target: LOG_TARGET, "Chunks to prove file sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send chunks to prove file: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryChallengesFromSeed { + seed, + provider_id, + count, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let challenges = self.client.runtime_api().get_challenges_from_seed( + current_block_hash, + &seed, + &provider_id, + count, + ); + + match callback.send(challenges) { + Ok(_) => { + trace!(target: LOG_TARGET, "Challenges sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send challenges: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryForestChallengesFromSeed { + seed, + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let challenges = self.client.runtime_api().get_forest_challenges_from_seed( + current_block_hash, + &seed, + &provider_id, + ); + + match callback.send(challenges) { + Ok(_) => { + trace!(target: LOG_TARGET, "Challenges sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send challenges: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryLastTickProviderSubmittedProof { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let last_tick = self + .client + .runtime_api() + .get_last_tick_provider_submitted_proof(current_block_hash, &provider_id) + .unwrap_or_else(|_| { + Err(GetLastTickProviderSubmittedProofError::InternalApiError) + }); + + match callback.send(last_tick) { + Ok(_) => { + trace!(target: LOG_TARGET, "Last tick sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send last tick provider submitted proof: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryChallengePeriod { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let challenge_period = self + .client + .runtime_api() + .get_challenge_period(current_block_hash, &provider_id) + .unwrap_or_else(|_| { + error!(target: LOG_TARGET, "Failed to query challenge period for provider [{:?}]", provider_id); + Err(GetChallengePeriodError::InternalApiError) + }); + + match callback.send(challenge_period) { + Ok(_) => { + trace!(target: LOG_TARGET, "Challenge period sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send challenge period: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryNextChallengeTickForProvider { + provider_id, + callback, + } => { + let next_challenge_tick = + self.get_next_challenge_tick_for_provider(&provider_id); + + match callback.send(next_challenge_tick) { + Ok(_) => { + trace!(target: LOG_TARGET, "Next challenge tick sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send next challenge tick: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryLastCheckpointChallengeTick { callback } => { + let current_block_hash = self.client.info().best_hash; + + let last_checkpoint_tick = self + .client + .runtime_api() + .get_last_checkpoint_challenge_tick(current_block_hash); + + match callback.send(last_checkpoint_tick) { + Ok(_) => { + trace!(target: LOG_TARGET, "Last checkpoint tick sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send last checkpoint challenge tick: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryLastCheckpointChallenges { tick, callback } => { + let current_block_hash = self.client.info().best_hash; + + let checkpoint_challenges = self + .client + .runtime_api() + .get_checkpoint_challenges(current_block_hash, tick) + .unwrap_or_else(|_| Err(GetCheckpointChallengesError::InternalApiError)); + + match callback.send(checkpoint_challenges) { + Ok(_) => { + trace!(target: LOG_TARGET, "Checkpoint challenges sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send checkpoint challenges: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryProviderForestRoot { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let bsp_info = self + .client + .runtime_api() + .get_bsp_info(current_block_hash, &provider_id) + .unwrap_or_else(|_| Err(GetBspInfoError::InternalApiError)); + + let root = bsp_info.map(|bsp_info| bsp_info.root); + + match callback.send(root) { + Ok(_) => { + trace!(target: LOG_TARGET, "BSP root sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send BSP root: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryStorageProviderCapacity { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let capacity = self + .client + .runtime_api() + .query_storage_provider_capacity(current_block_hash, &provider_id) + .unwrap_or_else(|_| Err(QueryStorageProviderCapacityError::InternalError)); + + match callback.send(capacity) { + Ok(_) => { + trace!(target: LOG_TARGET, "Storage provider capacity sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send storage provider capacity: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryAvailableStorageCapacity { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let capacity = self + .client + .runtime_api() + .query_available_storage_capacity(current_block_hash, &provider_id) + .unwrap_or_else(|_| Err(QueryAvailableStorageCapacityError::InternalError)); + + match callback.send(capacity) { + Ok(_) => { + trace!(target: LOG_TARGET, "Available storage capacity sent successfully"); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to send available storage capacity: {:?}", e); + } + } + } + BlockchainServiceCommand::QueueConfirmBspRequest { request, callback } => { + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .pending_confirm_storing_request_deque() + .push_back(request); + state_store_context.commit(); + // We check right away if we can process the request so we don't waste time. + self.check_pending_forest_root_writes(); + match callback.send(Ok(())) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::QueueMspRespondStorageRequest { request, callback } => { + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .pending_msp_respond_storage_request_deque() + .push_back(request); + state_store_context.commit(); + // We check right away if we can process the request so we don't waste time. + self.check_pending_forest_root_writes(); + match callback.send(Ok(())) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::QueueSubmitProofRequest { request, callback } => { + // The strategy used here is to replace the request in the set with the new request. + // This is because new insertions are presumed to be done with more information of the current state of the chain, + // so we want to make sure that the request is the most up-to-date one. + if let Some(replaced_request) = + self.pending_submit_proof_requests.replace(request.clone()) + { + trace!(target: LOG_TARGET, "Replacing pending submit proof request {:?} with {:?}", replaced_request, request); + } + + // We check right away if we can process the request so we don't waste time. + self.check_pending_forest_root_writes(); + match callback.send(Ok(())) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::QueueStopStoringForInsolventUserRequest { + request, + callback, + } => { + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .pending_stop_storing_for_insolvent_user_request_deque() + .push_back(request); + state_store_context.commit(); + // We check right away if we can process the request so we don't waste time. + self.check_pending_forest_root_writes(); + match callback.send(Ok(())) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send receiver: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryStorageProviderId { + maybe_node_pub_key, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let node_pub_key = maybe_node_pub_key + .unwrap_or_else(|| Self::caller_pub_key(self.keystore.clone())); + + let provider_id = self + .client + .runtime_api() + .get_storage_provider_id(current_block_hash, &node_pub_key.into()) + .map_err(|_| anyhow!("Internal API error")); + + match callback.send(provider_id) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send storage provider ID: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryUsersWithDebt { + provider_id, + min_debt, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let users_with_debt = self + .client + .runtime_api() + .get_users_with_debt_over_threshold( + current_block_hash, + &provider_id, + min_debt, + ) + .unwrap_or_else(|e| { + error!(target: LOG_TARGET, "{}", e); + Err(GetUsersWithDebtOverThresholdError::InternalApiError) + }); + + match callback.send(users_with_debt) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send back users with debt: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryWorstCaseScenarioSlashableAmount { + provider_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let worst_case_scenario_slashable_amount = self + .client + .runtime_api() + .get_worst_case_scenario_slashable_amount(current_block_hash, provider_id) + .map_err(|_| anyhow!("Internal API error")); + + match callback.send(worst_case_scenario_slashable_amount) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send back slashable amount: {:?}", e); + } + } + } + BlockchainServiceCommand::QuerySlashAmountPerMaxFileSize { callback } => { + // Get the current block hash. + let current_block_hash = self.client.info().best_hash; + + let slash_amount_per_max_file_size = self + .client + .runtime_api() + .get_slash_amount_per_max_file_size(current_block_hash) + .map_err(|_| anyhow!("Internal API error")); + + match callback.send(slash_amount_per_max_file_size) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send back `SlashAmountPerMaxFileSize`: {:?}", e); + } + } + } + BlockchainServiceCommand::QueryMspIdOfBucketId { + bucket_id, + callback, + } => { + let current_block_hash = self.client.info().best_hash; + + let msp_id = self + .client + .runtime_api() + .query_msp_id_of_bucket_id(current_block_hash, &bucket_id) + .unwrap_or_else(|e| { + error!(target: LOG_TARGET, "{}", e); + Err(QueryMspIdOfBucketIdError::BucketNotFound) + }); + + match callback.send(msp_id) { + Ok(_) => {} + Err(e) => { + error!(target: LOG_TARGET, "Failed to send back MSP ID: {:?}", e); + } + } + } + } + } + } + + fn get_event_bus_provider(&self) -> &Self::EventBusProvider { + &self.event_bus_provider + } +} + +impl BlockchainService { + /// Create a new [`BlockchainService`]. + pub fn new( + client: Arc, + rpc_handlers: Arc, + keystore: KeystorePtr, + rocksdb_root_path: impl Into, + ) -> Self { + Self { + client, + rpc_handlers, + keystore, + event_bus_provider: BlockchainServiceEventBusProvider::new(), + nonce_counter: 0, + wait_for_block_request_by_number: BTreeMap::new(), + wait_for_tick_request_by_number: BTreeMap::new(), + provider_ids: BTreeSet::new(), + forest_root_write_lock: None, + last_block_processed: Zero::zero(), + persistent_state: BlockchainServiceStateStore::new(rocksdb_root_path.into()), + pending_submit_proof_requests: BTreeSet::new(), + } + } + + async fn handle_block_import_notification( + &mut self, + notification: BlockImportNotification, + ) where + Block: cumulus_primitives_core::BlockT, + { + let block_hash: H256 = notification.hash; + let block_number: BlockNumber = (*notification.header.number()).saturated_into(); + + // If this is the first block import notification, we might need to catch up. + info!(target: LOG_TARGET, "Block import notification (#{}): {}", block_number, block_hash); + + // Get provider IDs linked to keys in this node's keystore and update the nonce. + self.pre_block_processing_checks(&block_hash); + + // Check if we just came out of syncing mode. + if block_number - self.last_block_processed < SYNC_MODE_MIN_BLOCKS_BEHIND { + self.handle_initial_sync(notification).await; + } + + self.process_block_import(&block_hash, &block_number).await; + } + + fn pre_block_processing_checks(&mut self, block_hash: &H256) { + // We query the [`BlockchainService`] account nonce at this height + // and update our internal counter if it's smaller than the result. + self.check_nonce(&block_hash); + + // Get provider IDs linked to keys in this node's keystore. + self.get_provider_ids(&block_hash); + } + + /// Handle the first time this node syncs with the chain. + async fn handle_initial_sync(&mut self, notification: BlockImportNotification) + where + Block: cumulus_primitives_core::BlockT, + { + let block_hash: H256 = notification.hash; + let block_number: BlockNumber = (*notification.header.number()).saturated_into(); + + // If this is the first block import notification, we might need to catch up. + info!(target: LOG_TARGET, "First block import notification (synced to #{}): {}", block_number, block_hash); + + // Check if there was an ongoing process confirm storing task. + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + + // Check if there was an ongoing process confirm storing task. + // Note: This would only exist if the node was running as a BSP. + let maybe_ongoing_process_confirm_storing_request = state_store_context + .access_value(&OngoingProcessConfirmStoringRequestCf) + .read(); + + // If there was an ongoing process confirm storing task, we need to re-queue the requests. + if let Some(process_confirm_storing_request) = maybe_ongoing_process_confirm_storing_request + { + for request in process_confirm_storing_request.confirm_storing_requests { + state_store_context + .pending_confirm_storing_request_deque() + .push_back(request); + } + } + + // Check if there was an ongoing process msp respond storage request task. + // Note: This would only exist if the node was running as an MSP. + let maybe_ongoing_process_msp_respond_storage_request = state_store_context + .access_value(&OngoingProcessMspRespondStorageRequestCf) + .read(); + + // If there was an ongoing process msp respond storage request task, we need to re-queue the requests. + if let Some(process_msp_respond_storage_request) = + maybe_ongoing_process_msp_respond_storage_request + { + for request in process_msp_respond_storage_request.respond_storing_requests { + state_store_context + .pending_msp_respond_storage_request_deque() + .push_back(request); + } + } + + // Check if there was an ongoing process stop storing task. + let maybe_ongoing_process_stop_storing_for_insolvent_user_request = state_store_context + .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) + .read(); + + // If there was an ongoing process stop storing task, we need to re-queue the requests. + if let Some(process_stop_storing_for_insolvent_user_request) = + maybe_ongoing_process_stop_storing_for_insolvent_user_request + { + state_store_context + .pending_stop_storing_for_insolvent_user_request_deque() + .push_back(StopStoringForInsolventUserRequest::new( + process_stop_storing_for_insolvent_user_request.who, + )); + } + + state_store_context.commit(); + + // Catch up to proofs that this node might have missed. + for provider_id in self.provider_ids.clone() { + self.proof_submission_catch_up(&block_hash, &provider_id); + } + } + + async fn process_block_import(&mut self, block_hash: &H256, block_number: &BlockNumber) { + info!(target: LOG_TARGET, "Processing block import #{}: {}", block_number, block_hash); + + // Notify all tasks waiting for this block number (or lower). + self.notify_import_block_number(&block_number); + + // Notify all tasks waiting for this tick number (or lower). + // It is not guaranteed that the tick number will increase at every block import. + self.notify_tick_number(&block_hash); + + // Process pending requests that update the forest root. + self.check_pending_forest_root_writes(); + + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + // Get events from storage. + match get_events_at_block(&self.client, block_hash) { + Ok(block_events) => { + // Process the events. + for ev in block_events { + match ev.event.clone() { + // New storage request event coming from pallet-file-system. + RuntimeEvent::FileSystem( + pallet_file_system::Event::NewStorageRequest { + who, + file_key, + bucket_id, + location, + fingerprint, + size, + peer_ids, + }, + ) => self.emit(NewStorageRequest { + who, + file_key: FileKey::from(file_key.as_ref()), + bucket_id, + location, + fingerprint: fingerprint.as_ref().into(), + size, + user_peer_ids: peer_ids, + }), + // A Provider's challenge cycle has been initialised. + RuntimeEvent::ProofsDealer( + pallet_proofs_dealer::Event::NewChallengeCycleInitialised { + current_tick: _, + next_challenge_deadline: _, + provider: provider_id, + maybe_provider_account, + }, + ) => { + // This node only cares if the Provider account matches one of the accounts in the keystore. + if let Some(account) = maybe_provider_account { + let account: Vec = + >::as_ref(&account) + .to_vec(); + if self.keystore.has_keys(&[(account.clone(), BCSV_KEY_TYPE)]) { + // If so, add the Provider ID to the list of Providers that this node is monitoring. + info!(target: LOG_TARGET, "New Provider ID to monitor [{:?}] for account [{:?}]", provider_id, account); + self.provider_ids.insert(provider_id); + } + } + } + // New challenge seed event coming from pallet-proofs-dealer. + RuntimeEvent::ProofsDealer( + pallet_proofs_dealer::Event::NewChallengeSeed { + challenges_ticker, + seed: _, + }, + ) => { + // For each Provider ID this node monitors... + for provider_id in &self.provider_ids { + // ...check if the challenges tick is one that this provider has to submit a proof for. + if self.should_provider_submit_proof( + &block_hash, + provider_id, + &challenges_ticker, + ) { + self.proof_submission_catch_up(&block_hash, provider_id); + } else { + trace!(target: LOG_TARGET, "Challenges tick is not the next one to be submitted for Provider [{:?}]", provider_id); + } + } + } + // A provider has been marked as slashable. + RuntimeEvent::ProofsDealer( + pallet_proofs_dealer::Event::SlashableProvider { + provider, + next_challenge_deadline, + }, + ) => self.emit(SlashableProvider { + provider, + next_challenge_deadline, + }), + // The last chargeable info of a provider has been updated + RuntimeEvent::PaymentStreams( + pallet_payment_streams::Event::LastChargeableInfoUpdated { + provider_id, + last_chargeable_tick, + last_chargeable_price_index, + }, + ) => { + if self.provider_ids.contains(&provider_id) { + self.emit(LastChargeableInfoUpdated { + provider_id: provider_id, + last_chargeable_tick: last_chargeable_tick, + last_chargeable_price_index: last_chargeable_price_index, + }) + } + } + // A user has been flagged as without funds in the runtime + RuntimeEvent::PaymentStreams( + pallet_payment_streams::Event::UserWithoutFunds { who }, + ) => { + self.emit(UserWithoutFunds { who }); + } + // A file was correctly deleted from a user without funds + RuntimeEvent::FileSystem( + pallet_file_system::Event::SpStopStoringInsolventUser { + sp_id, + file_key, + owner, + location, + new_root, + }, + ) => { + if self.provider_ids.contains(&sp_id) { + self.emit(SpStopStoringInsolventUser { + sp_id, + file_key: file_key.into(), + owner, + location, + new_root, + }) + } + } + // This event should only be of any use if a node is run by as a user. + RuntimeEvent::FileSystem( + pallet_file_system::Event::AcceptedBspVolunteer { + bsp_id, + bucket_id, + location, + fingerprint, + multiaddresses, + owner, + size, + }, + ) if owner + == AccountId32::from(Self::caller_pub_key(self.keystore.clone())) => + { + // We try to convert the types coming from the runtime into our expected types. + let fingerprint: Fingerprint = fingerprint.as_bytes().into(); + // Here the Multiaddresses come as a BoundedVec of BoundedVecs of bytes, + // and we need to convert them. Returns if any of the provided multiaddresses are invalid. + let mut multiaddress_vec: Vec = Vec::new(); + for raw_multiaddr in multiaddresses.into_iter() { + let multiaddress = match std::str::from_utf8(&raw_multiaddr) { + Ok(s) => match Multiaddr::from_str(s) { + Ok(multiaddr) => multiaddr, + Err(e) => { + error!(target: LOG_TARGET, "Failed to parse Multiaddress from string in AcceptedBspVolunteer event. bsp: {:?}, file owner: {:?}, file fingerprint: {:?}\n Error: {:?}", bsp_id, owner, fingerprint, e); + return; + } + }, + Err(e) => { + error!(target: LOG_TARGET, "Failed to parse Multiaddress from bytes in AcceptedBspVolunteer event. bsp: {:?}, file owner: {:?}, file fingerprint: {:?}\n Error: {:?}", bsp_id, owner, fingerprint, e); + return; + } + }; + + multiaddress_vec.push(multiaddress); + } + + self.emit(AcceptedBspVolunteer { + bsp_id, + bucket_id, + location, + fingerprint, + multiaddresses: multiaddress_vec, + owner, + size, + }) + } + // Ignore all other events. + _ => {} + } + } + } + Err(e) => { + // TODO: Handle case where the storage cannot be decoded. + // TODO: This would happen if we're parsing a block authored with an older version of the runtime, using + // TODO: a node that has a newer version of the runtime, therefore the EventsVec type is different. + // TODO: Consider using runtime APIs for getting old data of previous blocks, and this just for current blocks. + error!(target: LOG_TARGET, "Failed to get events storage element: {:?}", e); + } + } + state_store_context + .access_value(&LastProcessedBlockNumberCf) + .write(block_number); + state_store_context.commit(); + } + + /// Handle a finality notification. + async fn handle_finality_notification( + &mut self, + notification: FinalityNotification, + ) where + Block: cumulus_primitives_core::BlockT, + { + let block_hash: H256 = notification.hash; + let block_number: BlockNumber = (*notification.header.number()).saturated_into(); + + debug!(target: LOG_TARGET, "Finality notification #{}: {}", block_number, block_hash); + + // Get events from storage. + match get_events_at_block(&self.client, &block_hash) { + Ok(block_events) => { + // Process the events. + for ev in block_events { + match ev.event.clone() { + // New storage request event coming from pallet-file-system. + RuntimeEvent::ProofsDealer( + pallet_proofs_dealer::Event::MutationsApplied { + provider, + mutations, + new_root, + }, + ) => { + // Check if the provider ID is one of the provider IDs this node is tracking. + if self.provider_ids.contains(&provider) { + self.emit(FinalisedTrieRemoveMutationsApplied { + provider_id: provider, + mutations: mutations.clone(), + new_root, + }) + } + } + // Ignore all other events. + _ => {} + } + } + } + Err(e) => { + // TODO: Handle case where the storage cannot be decoded. + // TODO: This would happen if we're parsing a block authored with an older version of the runtime, using + // TODO: a node that has a newer version of the runtime, therefore the EventsVec type is different. + // TODO: Consider using runtime APIs for getting old data of previous blocks, and this just for current blocks. + error!(target: LOG_TARGET, "Failed to get events storage element: {:?}", e); + } + } + } +} diff --git a/client/blockchain-service/src/state.rs b/client/blockchain-service/src/state.rs index 711037202..426f172cf 100644 --- a/client/blockchain-service/src/state.rs +++ b/client/blockchain-service/src/state.rs @@ -1,306 +1,306 @@ -use std::path::PathBuf; - -use log::info; -use rocksdb::{ColumnFamilyDescriptor, Options, DB}; -use shc_common::types::BlockNumber; - -use crate::events::ProcessMspRespondStoringRequestData; -use crate::{ - events::{ProcessConfirmStoringRequestData, ProcessStopStoringForInsolventUserRequestData}, - typed_store::{ - BufferedWriteSupport, CFDequeAPI, ProvidesDbContext, ProvidesTypedDbAccess, - ProvidesTypedDbSingleAccess, ScaleEncodedCf, SingleScaleEncodedValueCf, TypedCf, - TypedDbContext, TypedRocksDB, - }, - types::{ - StopStoringForInsolventUserRequest, {ConfirmStoringRequest, RespondStorageRequest}, - }, -}; - -/// Last processed block number. -pub struct LastProcessedBlockNumberCf; -impl SingleScaleEncodedValueCf for LastProcessedBlockNumberCf { - type Value = BlockNumber; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = "last_processed_block_number"; -} - -/// Current ongoing task which requires a forest write lock. -pub struct OngoingProcessConfirmStoringRequestCf; -impl SingleScaleEncodedValueCf for OngoingProcessConfirmStoringRequestCf { - type Value = ProcessConfirmStoringRequestData; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = "ongoing_process_confirm_storing_request"; -} - -/// Current ongoing task which requires a forest write lock. -pub struct OngoingProcessStopStoringForInsolventUserRequestCf; -impl SingleScaleEncodedValueCf for OngoingProcessStopStoringForInsolventUserRequestCf { - type Value = ProcessStopStoringForInsolventUserRequestData; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "ongoing_process_stop_storing_for_insolvent_user_request"; -} - -/// Pending confirm storing requests. -#[derive(Default)] -pub struct PendingConfirmStoringRequestCf; -impl ScaleEncodedCf for PendingConfirmStoringRequestCf { - type Key = u64; - type Value = ConfirmStoringRequest; - - const SCALE_ENCODED_NAME: &'static str = "pending_confirm_storing_request"; -} - -/// Pending stop storing requests. -#[derive(Default)] -pub struct PendingStopStoringForInsolventUserRequestCf; -impl ScaleEncodedCf for PendingStopStoringForInsolventUserRequestCf { - type Key = u64; - type Value = StopStoringForInsolventUserRequest; - - const SCALE_ENCODED_NAME: &'static str = "pending_stop_storing_for_insolvent_user_request"; -} - -/// Pending submit proof requests left side (inclusive) index for the [`PendingConfirmStoringRequestCf`] CF. -#[derive(Default)] -pub struct PendingConfirmStoringRequestLeftIndexCf; -impl SingleScaleEncodedValueCf for PendingConfirmStoringRequestLeftIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_confirm_storing_request_left_index"; -} - -/// Pending submit proof requests right side (exclusive) index for the [`PendingConfirmStoringRequestCf`] CF. -#[derive(Default)] -pub struct PendingConfirmStoringRequestRightIndexCf; -impl SingleScaleEncodedValueCf for PendingConfirmStoringRequestRightIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_confirm_storing_request_right_index"; -} - -pub struct OngoingProcessMspRespondStorageRequestCf; -impl SingleScaleEncodedValueCf for OngoingProcessMspRespondStorageRequestCf { - type Value = ProcessMspRespondStoringRequestData; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "ongoing_process_msp_respond_storage_request"; -} - -/// Pending respond storage requests. -#[derive(Default)] -pub struct PendingMspRespondStorageRequestCf; -impl ScaleEncodedCf for PendingMspRespondStorageRequestCf { - type Key = u64; - type Value = RespondStorageRequest; - - const SCALE_ENCODED_NAME: &'static str = "pending_msp_respond_storage_request"; -} - -/// Pending respond storage requests left side (inclusive) index for the [`PendingMspRespondStorageRequestCf`] CF. -#[derive(Default)] -pub struct PendingMspRespondStorageRequestLeftIndexCf; -impl SingleScaleEncodedValueCf for PendingMspRespondStorageRequestLeftIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_msp_respond_storage_request_left_index"; -} - -/// Pending respond storage requests right side (exclusive) index for the [`PendingMspRespondStorageRequestCf`] CF. -#[derive(Default)] -pub struct PendingMspRespondStorageRequestRightIndexCf; -impl SingleScaleEncodedValueCf for PendingMspRespondStorageRequestRightIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_msp_respond_storage_request_right_index"; -} - -/// Pending submit proof requests left side (inclusive) index for the [`PendingStopStoringForInsolventUserRequestCf`] CF. -#[derive(Default)] -pub struct PendingStopStoringForInsolventUserRequestLeftIndexCf; -impl SingleScaleEncodedValueCf for PendingStopStoringForInsolventUserRequestLeftIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_stop_storing_for_insolvent_user_request_left_index"; -} - -/// Pending submit proof requests right side (exclusive) index for the [`PendingStopStoringForInsolventUserRequestCf`] CF. -#[derive(Default)] -pub struct PendingStopStoringForInsolventUserRequestRightIndexCf; -impl SingleScaleEncodedValueCf for PendingStopStoringForInsolventUserRequestRightIndexCf { - type Value = u64; - - const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = - "pending_stop_storing_for_insolvent_user_request_right_index"; -} - -const ALL_COLUMN_FAMILIES: [&str; 13] = [ - LastProcessedBlockNumberCf::NAME, - OngoingProcessConfirmStoringRequestCf::NAME, - PendingConfirmStoringRequestLeftIndexCf::NAME, - PendingConfirmStoringRequestRightIndexCf::NAME, - PendingConfirmStoringRequestCf::NAME, - OngoingProcessMspRespondStorageRequestCf::NAME, - PendingMspRespondStorageRequestLeftIndexCf::NAME, - PendingMspRespondStorageRequestRightIndexCf::NAME, - PendingMspRespondStorageRequestCf::NAME, - OngoingProcessStopStoringForInsolventUserRequestCf::NAME, - PendingStopStoringForInsolventUserRequestLeftIndexCf::NAME, - PendingStopStoringForInsolventUserRequestRightIndexCf::NAME, - PendingStopStoringForInsolventUserRequestCf::NAME, -]; - -/// A persistent blockchain service state store. -pub struct BlockchainServiceStateStore { - /// The RocksDB database. - rocks: TypedRocksDB, -} - -impl BlockchainServiceStateStore { - pub fn new(root_path: PathBuf) -> Self { - let mut path = root_path; - path.push("storagehub/blockchain_service/"); - - let db_path_str = path.to_str().expect("Failed to convert path to string"); - info!("Blockchain service state store path: {}", db_path_str); - std::fs::create_dir_all(&db_path_str).expect("Failed to create directory"); - - let mut db_opts = Options::default(); - db_opts.create_if_missing(true); - db_opts.create_missing_column_families(true); - - let column_families: Vec = ALL_COLUMN_FAMILIES - .iter() - .map(|cf| ColumnFamilyDescriptor::new(cf.to_string(), Options::default())) - .collect(); - - let db = DB::open_cf_descriptors(&db_opts, db_path_str, column_families).unwrap(); - - BlockchainServiceStateStore { - rocks: TypedRocksDB { db }, - } - } - - /// Starts a read/buffered-write interaction with the DB through per-CF type-safe APIs. - pub fn open_rw_context_with_overlay(&self) -> BlockchainServiceStateStoreRwContext<'_> { - BlockchainServiceStateStoreRwContext::new(TypedDbContext::new( - &self.rocks, - BufferedWriteSupport::new(&self.rocks), - )) - } -} - -pub struct BlockchainServiceStateStoreRwContext<'a> { - /// The RocksDB database. - db_context: TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, -} - -impl<'a> BlockchainServiceStateStoreRwContext<'a> { - pub fn new( - db_context: TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, - ) -> Self { - BlockchainServiceStateStoreRwContext { db_context } - } - - pub fn pending_confirm_storing_request_deque( - &'a self, - ) -> PendingConfirmStoringRequestDequeAPI<'a> { - PendingConfirmStoringRequestDequeAPI { - db_context: &self.db_context, - } - } - - pub fn pending_msp_respond_storage_request_deque( - &'a self, - ) -> PendingMspRespondStorageRequestDequeAPI<'a> { - PendingMspRespondStorageRequestDequeAPI { - db_context: &self.db_context, - } - } - - pub fn pending_stop_storing_for_insolvent_user_request_deque( - &'a self, - ) -> PendingStopStoringForInsolventUserRequestDequeAPI<'a> { - PendingStopStoringForInsolventUserRequestDequeAPI { - db_context: &self.db_context, - } - } - - /// Flushes the buffered writes to the DB. - pub fn commit(self) { - self.db_context.flush(); - } -} - -impl<'a> ProvidesDbContext for BlockchainServiceStateStoreRwContext<'a> { - fn db_context(&self) -> &TypedDbContext> { - &self.db_context - } -} - -impl<'a> ProvidesTypedDbSingleAccess for BlockchainServiceStateStoreRwContext<'a> {} - -impl<'a> ProvidesTypedDbAccess for BlockchainServiceStateStoreRwContext<'a> {} - -pub struct PendingConfirmStoringRequestDequeAPI<'a> { - db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, -} - -impl<'a> ProvidesDbContext for PendingConfirmStoringRequestDequeAPI<'a> { - fn db_context(&self) -> &TypedDbContext> { - &self.db_context - } -} - -impl<'a> ProvidesTypedDbSingleAccess for PendingConfirmStoringRequestDequeAPI<'a> {} - -impl<'a> CFDequeAPI for PendingConfirmStoringRequestDequeAPI<'a> { - type Value = ConfirmStoringRequest; - type LeftIndexCF = PendingConfirmStoringRequestLeftIndexCf; - type RightIndexCF = PendingConfirmStoringRequestRightIndexCf; - type DataCF = PendingConfirmStoringRequestCf; -} - -pub struct PendingMspRespondStorageRequestDequeAPI<'a> { - db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, -} - -impl<'a> ProvidesDbContext for PendingMspRespondStorageRequestDequeAPI<'a> { - fn db_context(&self) -> &TypedDbContext> { - &self.db_context - } -} - -impl<'a> ProvidesTypedDbSingleAccess for PendingMspRespondStorageRequestDequeAPI<'a> {} - -impl<'a> CFDequeAPI for PendingMspRespondStorageRequestDequeAPI<'a> { - type Value = RespondStorageRequest; - type LeftIndexCF = PendingMspRespondStorageRequestLeftIndexCf; - type RightIndexCF = PendingMspRespondStorageRequestRightIndexCf; - type DataCF = PendingMspRespondStorageRequestCf; -} - -pub struct PendingStopStoringForInsolventUserRequestDequeAPI<'a> { - db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, -} - -impl<'a> ProvidesDbContext for PendingStopStoringForInsolventUserRequestDequeAPI<'a> { - fn db_context(&self) -> &TypedDbContext> { - &self.db_context - } -} - -impl<'a> ProvidesTypedDbSingleAccess for PendingStopStoringForInsolventUserRequestDequeAPI<'a> {} - -impl<'a> CFDequeAPI for PendingStopStoringForInsolventUserRequestDequeAPI<'a> { - type Value = StopStoringForInsolventUserRequest; - type LeftIndexCF = PendingStopStoringForInsolventUserRequestLeftIndexCf; - type RightIndexCF = PendingStopStoringForInsolventUserRequestRightIndexCf; - type DataCF = PendingStopStoringForInsolventUserRequestCf; -} +use std::path::PathBuf; + +use log::info; +use rocksdb::{ColumnFamilyDescriptor, Options, DB}; +use shc_common::types::BlockNumber; + +use crate::events::ProcessMspRespondStoringRequestData; +use crate::{ + events::{ProcessConfirmStoringRequestData, ProcessStopStoringForInsolventUserRequestData}, + typed_store::{ + BufferedWriteSupport, CFDequeAPI, ProvidesDbContext, ProvidesTypedDbAccess, + ProvidesTypedDbSingleAccess, ScaleEncodedCf, SingleScaleEncodedValueCf, TypedCf, + TypedDbContext, TypedRocksDB, + }, + types::{ + StopStoringForInsolventUserRequest, {ConfirmStoringRequest, RespondStorageRequest}, + }, +}; + +/// Last processed block number. +pub struct LastProcessedBlockNumberCf; +impl SingleScaleEncodedValueCf for LastProcessedBlockNumberCf { + type Value = BlockNumber; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = "last_processed_block_number"; +} + +/// Current ongoing task which requires a forest write lock. +pub struct OngoingProcessConfirmStoringRequestCf; +impl SingleScaleEncodedValueCf for OngoingProcessConfirmStoringRequestCf { + type Value = ProcessConfirmStoringRequestData; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = "ongoing_process_confirm_storing_request"; +} + +/// Current ongoing task which requires a forest write lock. +pub struct OngoingProcessStopStoringForInsolventUserRequestCf; +impl SingleScaleEncodedValueCf for OngoingProcessStopStoringForInsolventUserRequestCf { + type Value = ProcessStopStoringForInsolventUserRequestData; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "ongoing_process_stop_storing_for_insolvent_user_request"; +} + +/// Pending confirm storing requests. +#[derive(Default)] +pub struct PendingConfirmStoringRequestCf; +impl ScaleEncodedCf for PendingConfirmStoringRequestCf { + type Key = u64; + type Value = ConfirmStoringRequest; + + const SCALE_ENCODED_NAME: &'static str = "pending_confirm_storing_request"; +} + +/// Pending stop storing requests. +#[derive(Default)] +pub struct PendingStopStoringForInsolventUserRequestCf; +impl ScaleEncodedCf for PendingStopStoringForInsolventUserRequestCf { + type Key = u64; + type Value = StopStoringForInsolventUserRequest; + + const SCALE_ENCODED_NAME: &'static str = "pending_stop_storing_for_insolvent_user_request"; +} + +/// Pending submit proof requests left side (inclusive) index for the [`PendingConfirmStoringRequestCf`] CF. +#[derive(Default)] +pub struct PendingConfirmStoringRequestLeftIndexCf; +impl SingleScaleEncodedValueCf for PendingConfirmStoringRequestLeftIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_confirm_storing_request_left_index"; +} + +/// Pending submit proof requests right side (exclusive) index for the [`PendingConfirmStoringRequestCf`] CF. +#[derive(Default)] +pub struct PendingConfirmStoringRequestRightIndexCf; +impl SingleScaleEncodedValueCf for PendingConfirmStoringRequestRightIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_confirm_storing_request_right_index"; +} + +pub struct OngoingProcessMspRespondStorageRequestCf; +impl SingleScaleEncodedValueCf for OngoingProcessMspRespondStorageRequestCf { + type Value = ProcessMspRespondStoringRequestData; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "ongoing_process_msp_respond_storage_request"; +} + +/// Pending respond storage requests. +#[derive(Default)] +pub struct PendingMspRespondStorageRequestCf; +impl ScaleEncodedCf for PendingMspRespondStorageRequestCf { + type Key = u64; + type Value = RespondStorageRequest; + + const SCALE_ENCODED_NAME: &'static str = "pending_msp_respond_storage_request"; +} + +/// Pending respond storage requests left side (inclusive) index for the [`PendingMspRespondStorageRequestCf`] CF. +#[derive(Default)] +pub struct PendingMspRespondStorageRequestLeftIndexCf; +impl SingleScaleEncodedValueCf for PendingMspRespondStorageRequestLeftIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_msp_respond_storage_request_left_index"; +} + +/// Pending respond storage requests right side (exclusive) index for the [`PendingMspRespondStorageRequestCf`] CF. +#[derive(Default)] +pub struct PendingMspRespondStorageRequestRightIndexCf; +impl SingleScaleEncodedValueCf for PendingMspRespondStorageRequestRightIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_msp_respond_storage_request_right_index"; +} + +/// Pending submit proof requests left side (inclusive) index for the [`PendingStopStoringForInsolventUserRequestCf`] CF. +#[derive(Default)] +pub struct PendingStopStoringForInsolventUserRequestLeftIndexCf; +impl SingleScaleEncodedValueCf for PendingStopStoringForInsolventUserRequestLeftIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_stop_storing_for_insolvent_user_request_left_index"; +} + +/// Pending submit proof requests right side (exclusive) index for the [`PendingStopStoringForInsolventUserRequestCf`] CF. +#[derive(Default)] +pub struct PendingStopStoringForInsolventUserRequestRightIndexCf; +impl SingleScaleEncodedValueCf for PendingStopStoringForInsolventUserRequestRightIndexCf { + type Value = u64; + + const SINGLE_SCALE_ENCODED_VALUE_NAME: &'static str = + "pending_stop_storing_for_insolvent_user_request_right_index"; +} + +const ALL_COLUMN_FAMILIES: [&str; 13] = [ + LastProcessedBlockNumberCf::NAME, + OngoingProcessConfirmStoringRequestCf::NAME, + PendingConfirmStoringRequestLeftIndexCf::NAME, + PendingConfirmStoringRequestRightIndexCf::NAME, + PendingConfirmStoringRequestCf::NAME, + OngoingProcessMspRespondStorageRequestCf::NAME, + PendingMspRespondStorageRequestLeftIndexCf::NAME, + PendingMspRespondStorageRequestRightIndexCf::NAME, + PendingMspRespondStorageRequestCf::NAME, + OngoingProcessStopStoringForInsolventUserRequestCf::NAME, + PendingStopStoringForInsolventUserRequestLeftIndexCf::NAME, + PendingStopStoringForInsolventUserRequestRightIndexCf::NAME, + PendingStopStoringForInsolventUserRequestCf::NAME, +]; + +/// A persistent blockchain service state store. +pub struct BlockchainServiceStateStore { + /// The RocksDB database. + rocks: TypedRocksDB, +} + +impl BlockchainServiceStateStore { + pub fn new(root_path: PathBuf) -> Self { + let mut path = root_path; + path.push("storagehub/blockchain_service/"); + + let db_path_str = path.to_str().expect("Failed to convert path to string"); + info!("Blockchain service state store path: {}", db_path_str); + std::fs::create_dir_all(&db_path_str).expect("Failed to create directory"); + + let mut db_opts = Options::default(); + db_opts.create_if_missing(true); + db_opts.create_missing_column_families(true); + + let column_families: Vec = ALL_COLUMN_FAMILIES + .iter() + .map(|cf| ColumnFamilyDescriptor::new(cf.to_string(), Options::default())) + .collect(); + + let db = DB::open_cf_descriptors(&db_opts, db_path_str, column_families).unwrap(); + + BlockchainServiceStateStore { + rocks: TypedRocksDB { db }, + } + } + + /// Starts a read/buffered-write interaction with the DB through per-CF type-safe APIs. + pub fn open_rw_context_with_overlay(&self) -> BlockchainServiceStateStoreRwContext<'_> { + BlockchainServiceStateStoreRwContext::new(TypedDbContext::new( + &self.rocks, + BufferedWriteSupport::new(&self.rocks), + )) + } +} + +pub struct BlockchainServiceStateStoreRwContext<'a> { + /// The RocksDB database. + db_context: TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, +} + +impl<'a> BlockchainServiceStateStoreRwContext<'a> { + pub fn new( + db_context: TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, + ) -> Self { + BlockchainServiceStateStoreRwContext { db_context } + } + + pub fn pending_confirm_storing_request_deque( + &'a self, + ) -> PendingConfirmStoringRequestDequeAPI<'a> { + PendingConfirmStoringRequestDequeAPI { + db_context: &self.db_context, + } + } + + pub fn pending_msp_respond_storage_request_deque( + &'a self, + ) -> PendingMspRespondStorageRequestDequeAPI<'a> { + PendingMspRespondStorageRequestDequeAPI { + db_context: &self.db_context, + } + } + + pub fn pending_stop_storing_for_insolvent_user_request_deque( + &'a self, + ) -> PendingStopStoringForInsolventUserRequestDequeAPI<'a> { + PendingStopStoringForInsolventUserRequestDequeAPI { + db_context: &self.db_context, + } + } + + /// Flushes the buffered writes to the DB. + pub fn commit(self) { + self.db_context.flush(); + } +} + +impl<'a> ProvidesDbContext for BlockchainServiceStateStoreRwContext<'a> { + fn db_context(&self) -> &TypedDbContext> { + &self.db_context + } +} + +impl<'a> ProvidesTypedDbSingleAccess for BlockchainServiceStateStoreRwContext<'a> {} + +impl<'a> ProvidesTypedDbAccess for BlockchainServiceStateStoreRwContext<'a> {} + +pub struct PendingConfirmStoringRequestDequeAPI<'a> { + db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, +} + +impl<'a> ProvidesDbContext for PendingConfirmStoringRequestDequeAPI<'a> { + fn db_context(&self) -> &TypedDbContext> { + &self.db_context + } +} + +impl<'a> ProvidesTypedDbSingleAccess for PendingConfirmStoringRequestDequeAPI<'a> {} + +impl<'a> CFDequeAPI for PendingConfirmStoringRequestDequeAPI<'a> { + type Value = ConfirmStoringRequest; + type LeftIndexCF = PendingConfirmStoringRequestLeftIndexCf; + type RightIndexCF = PendingConfirmStoringRequestRightIndexCf; + type DataCF = PendingConfirmStoringRequestCf; +} + +pub struct PendingMspRespondStorageRequestDequeAPI<'a> { + db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, +} + +impl<'a> ProvidesDbContext for PendingMspRespondStorageRequestDequeAPI<'a> { + fn db_context(&self) -> &TypedDbContext> { + &self.db_context + } +} + +impl<'a> ProvidesTypedDbSingleAccess for PendingMspRespondStorageRequestDequeAPI<'a> {} + +impl<'a> CFDequeAPI for PendingMspRespondStorageRequestDequeAPI<'a> { + type Value = RespondStorageRequest; + type LeftIndexCF = PendingMspRespondStorageRequestLeftIndexCf; + type RightIndexCF = PendingMspRespondStorageRequestRightIndexCf; + type DataCF = PendingMspRespondStorageRequestCf; +} + +pub struct PendingStopStoringForInsolventUserRequestDequeAPI<'a> { + db_context: &'a TypedDbContext<'a, TypedRocksDB, BufferedWriteSupport<'a, TypedRocksDB>>, +} + +impl<'a> ProvidesDbContext for PendingStopStoringForInsolventUserRequestDequeAPI<'a> { + fn db_context(&self) -> &TypedDbContext> { + &self.db_context + } +} + +impl<'a> ProvidesTypedDbSingleAccess for PendingStopStoringForInsolventUserRequestDequeAPI<'a> {} + +impl<'a> CFDequeAPI for PendingStopStoringForInsolventUserRequestDequeAPI<'a> { + type Value = StopStoringForInsolventUserRequest; + type LeftIndexCF = PendingStopStoringForInsolventUserRequestLeftIndexCf; + type RightIndexCF = PendingStopStoringForInsolventUserRequestRightIndexCf; + type DataCF = PendingStopStoringForInsolventUserRequestCf; +} diff --git a/client/blockchain-service/src/types.rs b/client/blockchain-service/src/types.rs index cb46b0340..ea60dcc8b 100644 --- a/client/blockchain-service/src/types.rs +++ b/client/blockchain-service/src/types.rs @@ -1,272 +1,272 @@ -use std::{ - cmp::{min, Ordering}, - future::Future, - pin::Pin, - time::Duration, -}; - -use codec::{Decode, Encode}; -use frame_support::dispatch::DispatchInfo; -use sp_core::H256; -use sp_runtime::{AccountId32, DispatchError}; - -use shc_common::types::{ - BlockNumber, ProviderId, RandomnessOutput, RejectedStorageRequestReason, StorageHubEventsVec, - TrieRemoveMutation, -}; - -/// A struct that holds the information to submit a storage proof. -/// -/// This struct is used as an item in the `pending_submit_proof_requests` queue. -#[derive(Debug, Clone, Encode, Decode)] -pub struct SubmitProofRequest { - pub provider_id: ProviderId, - pub tick: BlockNumber, - pub seed: RandomnessOutput, - pub forest_challenges: Vec, - pub checkpoint_challenges: Vec<(H256, Option)>, -} - -impl SubmitProofRequest { - pub fn new( - provider_id: ProviderId, - tick: BlockNumber, - seed: RandomnessOutput, - forest_challenges: Vec, - checkpoint_challenges: Vec<(H256, Option)>, - ) -> Self { - Self { - provider_id, - tick, - seed, - forest_challenges, - checkpoint_challenges, - } - } -} - -impl Ord for SubmitProofRequest { - fn cmp(&self, other: &Self) -> Ordering { - self.tick.cmp(&other.tick) - } -} - -impl PartialOrd for SubmitProofRequest { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -// Two `SubmitProofRequest`s are considered equal if they have the same `tick` and `provider_id`. -// This helps to identify and remove duplicate requests from the queue. -impl PartialEq for SubmitProofRequest { - fn eq(&self, other: &Self) -> bool { - self.tick == other.tick && self.provider_id == other.provider_id - } -} - -impl Eq for SubmitProofRequest {} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct ConfirmStoringRequest { - pub file_key: H256, - pub try_count: u32, -} - -impl ConfirmStoringRequest { - pub fn new(file_key: H256) -> Self { - Self { - file_key, - try_count: 0, - } - } - - pub fn increment_try_count(&mut self) { - self.try_count += 1; - } -} - -#[derive(Debug, Clone, Encode, Decode)] -pub enum MspRespondStorageRequest { - Accept, - Reject(RejectedStorageRequestReason), -} - -#[derive(Debug, Clone, Encode, Decode)] -pub struct RespondStorageRequest { - pub file_key: H256, - pub response: MspRespondStorageRequest, - pub try_count: u32, -} - -impl RespondStorageRequest { - pub fn new(file_key: H256, response: MspRespondStorageRequest) -> Self { - Self { - file_key, - response, - try_count: 0, - } - } - - pub fn increment_try_count(&mut self) { - self.try_count += 1; - } -} - -/// A struct that holds the information to stop storing all files from an insolvent user. -/// (Which is only the user's account ID). -/// -/// This struct is used as an item in the `pending_stop_storing_for_insolvent_user_requests` queue. -#[derive(Debug, Clone, Encode, Decode)] -pub struct StopStoringForInsolventUserRequest { - pub user: AccountId32, -} - -impl StopStoringForInsolventUserRequest { - pub fn new(user: AccountId32) -> Self { - Self { user } - } -} - -/// Extrinsic struct. -/// -/// This struct represents an extrinsic in the blockchain. -#[derive(Debug, Clone)] -pub struct Extrinsic { - /// Extrinsic hash. - pub hash: H256, - /// Block hash. - pub block_hash: H256, - /// Events vector. - pub events: StorageHubEventsVec, -} - -/// ExtrinsicResult enum. -/// -/// This enum represents the result of an extrinsic execution. It can be either a success or a failure. -pub enum ExtrinsicResult { - /// Success variant. - /// - /// This variant represents a successful extrinsic execution. - Success { - /// Dispatch info. - dispatch_info: DispatchInfo, - }, - /// Failure variant. - /// - /// This variant represents a failed extrinsic execution. - Failure { - /// Dispatch error. - dispatch_error: DispatchError, - /// Dispatch info. - dispatch_info: DispatchInfo, - }, -} - -/// Type alias for the extrinsic hash. -pub type ExtrinsicHash = H256; - -/// Type alias for the tip. -pub type Tip = pallet_transaction_payment::ChargeTransactionPayment; - -/// A struct which defines a submit extrinsic retry strategy. This defines a simple strategy when -/// sending and extrinsic. It will retry a maximum number of times ([Self::max_retries]). -/// If the extrinsic is not included in a block within a certain time frame [`Self::timeout`] it is -/// considered a failure. -/// The tip will increase with each retry, up to a maximum tip of [`Self::max_tip`]. -/// The tip series (with the exception of the first try which is 0) is a geometric progression with -/// a multiplier of [`Self::base_multiplier`]. -/// The final tip for each retry is calculated as: -/// [`Self::max_tip`] * (([`Self::base_multiplier`] ^ (retry_count / [`Self::max_retries`]) - 1) / -/// ([`Self::base_multiplier`] - 1)). -/// An optional check function can be provided to determine if the extrinsic should be retried, -/// aborting early if the function returns false. -pub struct RetryStrategy { - /// Maximum number of retries after which the extrinsic submission will be considered failed. - pub max_retries: u32, - /// Maximum time to wait for a response before assuming the extrinsic submission has failed. - pub timeout: Duration, - /// Maximum tip to be paid for the extrinsic submission. The progression follows an exponential - /// backoff strategy. - pub max_tip: f64, - /// Base multiplier for the tip calculation. This is the base of the geometric progression. - /// A higher value will make tips grow faster. - pub base_multiplier: f64, - /// An optional check function to determine if the extrinsic should be retried. - /// If this is provided, the function will be called before each retry to determine if the - /// extrinsic should be retried or the submission should be considered failed. If this is not - /// provided, the extrinsic will be retried until [`Self::max_retries`] is reached. - pub should_retry: Option Pin + Send>> + Send>>, -} - -impl RetryStrategy { - /// Creates a new `RetryStrategy` instance. - pub fn new(max_retries: u32, timeout: Duration, max_tip: f64, base_multiplier: f64) -> Self { - Self { - max_retries, - timeout, - max_tip, - base_multiplier, - should_retry: None, - } - } - - pub fn with_max_retries(mut self, max_retries: u32) -> Self { - self.max_retries = max_retries; - self - } - - pub fn with_timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - - pub fn with_max_tip(mut self, max_tip: f64) -> Self { - self.max_tip = max_tip; - self - } - - pub fn with_base_multiplier(mut self, base_multiplier: f64) -> Self { - self.base_multiplier = base_multiplier; - self - } - - pub fn with_should_retry( - mut self, - should_retry: Option Pin + Send>> + Send>>, - ) -> Self { - self.should_retry = should_retry; - self - } - - /// Computes the tip for the given retry count. - /// The formula for the tip is: - /// [`Self::max_tip`] * (([`Self::base_multiplier`] ^ (retry_count / [`Self::max_retries`]) - 1) / - /// ([`Self::base_multiplier`] - 1)). - pub fn compute_tip(&self, retry_count: u32) -> f64 { - // Ensure the retry_count is within the bounds of max_retries - let retry_count = min(retry_count, self.max_retries); - - // Calculate the geometric progression factor for this retry_count - let factor = (self - .base_multiplier - .powf(retry_count as f64 / self.max_retries as f64) - - 1.0) - / (self.base_multiplier - 1.0); - - // Final tip formula for each retry, scaled to max_tip - self.max_tip * factor - } -} - -impl Default for RetryStrategy { - fn default() -> Self { - Self { - max_retries: 5, - timeout: Duration::from_secs(30), - max_tip: 0.0, - base_multiplier: 2.0, - should_retry: None, - } - } -} +use std::{ + cmp::{min, Ordering}, + future::Future, + pin::Pin, + time::Duration, +}; + +use codec::{Decode, Encode}; +use frame_support::dispatch::DispatchInfo; +use sp_core::H256; +use sp_runtime::{AccountId32, DispatchError}; + +use shc_common::types::{ + BlockNumber, ProviderId, RandomnessOutput, RejectedStorageRequestReason, StorageHubEventsVec, + TrieRemoveMutation, +}; + +/// A struct that holds the information to submit a storage proof. +/// +/// This struct is used as an item in the `pending_submit_proof_requests` queue. +#[derive(Debug, Clone, Encode, Decode)] +pub struct SubmitProofRequest { + pub provider_id: ProviderId, + pub tick: BlockNumber, + pub seed: RandomnessOutput, + pub forest_challenges: Vec, + pub checkpoint_challenges: Vec<(H256, Option)>, +} + +impl SubmitProofRequest { + pub fn new( + provider_id: ProviderId, + tick: BlockNumber, + seed: RandomnessOutput, + forest_challenges: Vec, + checkpoint_challenges: Vec<(H256, Option)>, + ) -> Self { + Self { + provider_id, + tick, + seed, + forest_challenges, + checkpoint_challenges, + } + } +} + +impl Ord for SubmitProofRequest { + fn cmp(&self, other: &Self) -> Ordering { + self.tick.cmp(&other.tick) + } +} + +impl PartialOrd for SubmitProofRequest { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// Two `SubmitProofRequest`s are considered equal if they have the same `tick` and `provider_id`. +// This helps to identify and remove duplicate requests from the queue. +impl PartialEq for SubmitProofRequest { + fn eq(&self, other: &Self) -> bool { + self.tick == other.tick && self.provider_id == other.provider_id + } +} + +impl Eq for SubmitProofRequest {} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ConfirmStoringRequest { + pub file_key: H256, + pub try_count: u32, +} + +impl ConfirmStoringRequest { + pub fn new(file_key: H256) -> Self { + Self { + file_key, + try_count: 0, + } + } + + pub fn increment_try_count(&mut self) { + self.try_count += 1; + } +} + +#[derive(Debug, Clone, Encode, Decode)] +pub enum MspRespondStorageRequest { + Accept, + Reject(RejectedStorageRequestReason), +} + +#[derive(Debug, Clone, Encode, Decode)] +pub struct RespondStorageRequest { + pub file_key: H256, + pub response: MspRespondStorageRequest, + pub try_count: u32, +} + +impl RespondStorageRequest { + pub fn new(file_key: H256, response: MspRespondStorageRequest) -> Self { + Self { + file_key, + response, + try_count: 0, + } + } + + pub fn increment_try_count(&mut self) { + self.try_count += 1; + } +} + +/// A struct that holds the information to stop storing all files from an insolvent user. +/// (Which is only the user's account ID). +/// +/// This struct is used as an item in the `pending_stop_storing_for_insolvent_user_requests` queue. +#[derive(Debug, Clone, Encode, Decode)] +pub struct StopStoringForInsolventUserRequest { + pub user: AccountId32, +} + +impl StopStoringForInsolventUserRequest { + pub fn new(user: AccountId32) -> Self { + Self { user } + } +} + +/// Extrinsic struct. +/// +/// This struct represents an extrinsic in the blockchain. +#[derive(Debug, Clone)] +pub struct Extrinsic { + /// Extrinsic hash. + pub hash: H256, + /// Block hash. + pub block_hash: H256, + /// Events vector. + pub events: StorageHubEventsVec, +} + +/// ExtrinsicResult enum. +/// +/// This enum represents the result of an extrinsic execution. It can be either a success or a failure. +pub enum ExtrinsicResult { + /// Success variant. + /// + /// This variant represents a successful extrinsic execution. + Success { + /// Dispatch info. + dispatch_info: DispatchInfo, + }, + /// Failure variant. + /// + /// This variant represents a failed extrinsic execution. + Failure { + /// Dispatch error. + dispatch_error: DispatchError, + /// Dispatch info. + dispatch_info: DispatchInfo, + }, +} + +/// Type alias for the extrinsic hash. +pub type ExtrinsicHash = H256; + +/// Type alias for the tip. +pub type Tip = pallet_transaction_payment::ChargeTransactionPayment; + +/// A struct which defines a submit extrinsic retry strategy. This defines a simple strategy when +/// sending and extrinsic. It will retry a maximum number of times ([Self::max_retries]). +/// If the extrinsic is not included in a block within a certain time frame [`Self::timeout`] it is +/// considered a failure. +/// The tip will increase with each retry, up to a maximum tip of [`Self::max_tip`]. +/// The tip series (with the exception of the first try which is 0) is a geometric progression with +/// a multiplier of [`Self::base_multiplier`]. +/// The final tip for each retry is calculated as: +/// [`Self::max_tip`] * (([`Self::base_multiplier`] ^ (retry_count / [`Self::max_retries`]) - 1) / +/// ([`Self::base_multiplier`] - 1)). +/// An optional check function can be provided to determine if the extrinsic should be retried, +/// aborting early if the function returns false. +pub struct RetryStrategy { + /// Maximum number of retries after which the extrinsic submission will be considered failed. + pub max_retries: u32, + /// Maximum time to wait for a response before assuming the extrinsic submission has failed. + pub timeout: Duration, + /// Maximum tip to be paid for the extrinsic submission. The progression follows an exponential + /// backoff strategy. + pub max_tip: f64, + /// Base multiplier for the tip calculation. This is the base of the geometric progression. + /// A higher value will make tips grow faster. + pub base_multiplier: f64, + /// An optional check function to determine if the extrinsic should be retried. + /// If this is provided, the function will be called before each retry to determine if the + /// extrinsic should be retried or the submission should be considered failed. If this is not + /// provided, the extrinsic will be retried until [`Self::max_retries`] is reached. + pub should_retry: Option Pin + Send>> + Send>>, +} + +impl RetryStrategy { + /// Creates a new `RetryStrategy` instance. + pub fn new(max_retries: u32, timeout: Duration, max_tip: f64, base_multiplier: f64) -> Self { + Self { + max_retries, + timeout, + max_tip, + base_multiplier, + should_retry: None, + } + } + + pub fn with_max_retries(mut self, max_retries: u32) -> Self { + self.max_retries = max_retries; + self + } + + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub fn with_max_tip(mut self, max_tip: f64) -> Self { + self.max_tip = max_tip; + self + } + + pub fn with_base_multiplier(mut self, base_multiplier: f64) -> Self { + self.base_multiplier = base_multiplier; + self + } + + pub fn with_should_retry( + mut self, + should_retry: Option Pin + Send>> + Send>>, + ) -> Self { + self.should_retry = should_retry; + self + } + + /// Computes the tip for the given retry count. + /// The formula for the tip is: + /// [`Self::max_tip`] * (([`Self::base_multiplier`] ^ (retry_count / [`Self::max_retries`]) - 1) / + /// ([`Self::base_multiplier`] - 1)). + pub fn compute_tip(&self, retry_count: u32) -> f64 { + // Ensure the retry_count is within the bounds of max_retries + let retry_count = min(retry_count, self.max_retries); + + // Calculate the geometric progression factor for this retry_count + let factor = (self + .base_multiplier + .powf(retry_count as f64 / self.max_retries as f64) + - 1.0) + / (self.base_multiplier - 1.0); + + // Final tip formula for each retry, scaled to max_tip + self.max_tip * factor + } +} + +impl Default for RetryStrategy { + fn default() -> Self { + Self { + max_retries: 5, + timeout: Duration::from_secs(30), + max_tip: 0.0, + base_multiplier: 2.0, + should_retry: None, + } + } +} diff --git a/client/blockchain-service/src/utils.rs b/client/blockchain-service/src/utils.rs index 68a1eb1c4..887842c41 100644 --- a/client/blockchain-service/src/utils.rs +++ b/client/blockchain-service/src/utils.rs @@ -1,862 +1,862 @@ -use std::sync::Arc; - -use anyhow::{anyhow, Result}; -use codec::Encode; -use cumulus_primitives_core::BlockT; -use log::{debug, error, trace, warn}; -use pallet_proofs_dealer_runtime_api::{ - GetChallengePeriodError, GetChallengeSeedError, GetLastTickProviderSubmittedProofError, - ProofsDealerApi, -}; -use pallet_storage_providers::types::StorageProviderId; -use pallet_storage_providers_runtime_api::StorageProvidersApi; -use polkadot_runtime_common::BlockHashCount; -use sc_client_api::{BlockBackend, HeaderBackend}; -use serde_json::Number; -use shc_actors_framework::actor::Actor; -use shc_common::{ - blockchain_utils::get_events_at_block, - types::{BlockNumber, ParachainClient, ProviderId, BCSV_KEY_TYPE}, -}; -use sp_api::ProvideRuntimeApi; -use sp_core::{Blake2Hasher, Get, Hasher, H256}; -use sp_keystore::KeystorePtr; -use sp_runtime::{ - generic::{self, SignedPayload}, - SaturatedConversion, -}; -use storage_hub_runtime::{Runtime, SignedExtra, UncheckedExtrinsic}; -use substrate_frame_rpc_system::AccountNonceApi; -use tokio::sync::{oneshot::error::TryRecvError, Mutex}; - -use crate::{ - events::{ - ForestWriteLockTaskData, MultipleNewChallengeSeeds, ProcessConfirmStoringRequest, - ProcessConfirmStoringRequestData, ProcessMspRespondStoringRequest, - ProcessStopStoringForInsolventUserRequest, ProcessStopStoringForInsolventUserRequestData, - ProcessSubmitProofRequest, ProcessSubmitProofRequestData, - }, - handler::LOG_TARGET, - state::{ - OngoingProcessConfirmStoringRequestCf, OngoingProcessMspRespondStorageRequestCf, - OngoingProcessStopStoringForInsolventUserRequestCf, - }, - typed_store::{CFDequeAPI, ProvidesTypedDbSingleAccess}, - types::{Extrinsic, Tip}, - BlockchainService, -}; - -impl BlockchainService { - /// Notify tasks waiting for a block number. - pub(crate) fn notify_import_block_number(&mut self, block_number: &BlockNumber) { - let mut keys_to_remove = Vec::new(); - - for (block_number, waiters) in self - .wait_for_block_request_by_number - .range_mut(..=block_number) - { - keys_to_remove.push(*block_number); - for waiter in waiters.drain(..) { - match waiter.send(()) { - Ok(_) => {} - Err(_) => { - error!(target: LOG_TARGET, "Failed to notify task about block number."); - } - } - } - } - - for key in keys_to_remove { - self.wait_for_block_request_by_number.remove(&key); - } - } - - /// Notify tasks waiting for a tick number. - pub(crate) fn notify_tick_number(&mut self, block_hash: &H256) { - // Get the current tick number. - let tick_number = match self.client.runtime_api().get_current_tick(*block_hash) { - Ok(current_tick) => current_tick, - Err(_) => { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to query current tick from runtime in block hash {:?} and block number {:?}. This should not happen.", block_hash, self.client.info().best_number); - return; - } - }; - - let mut keys_to_remove = Vec::new(); - - for (tick_number, waiters) in self - .wait_for_tick_request_by_number - .range_mut(..=tick_number) - { - keys_to_remove.push(*tick_number); - for waiter in waiters.drain(..) { - match waiter.send(Ok(())) { - Ok(_) => {} - Err(_) => { - error!(target: LOG_TARGET, "Failed to notify task about tick number."); - } - } - } - } - - for key in keys_to_remove { - self.wait_for_tick_request_by_number.remove(&key); - } - } - - /// Checks if the account nonce on-chain is higher than the nonce in the [`BlockchainService`]. - /// - /// If the nonce is higher, the account nonce is updated in the [`BlockchainService`]. - pub(crate) fn check_nonce(&mut self, block_hash: &H256) { - let pub_key = Self::caller_pub_key(self.keystore.clone()); - let latest_nonce = self - .client - .runtime_api() - .account_nonce(*block_hash, pub_key.into()) - .expect("Fetching account nonce works; qed"); - if latest_nonce > self.nonce_counter { - self.nonce_counter = latest_nonce - } - } - - /// Get all the provider IDs linked to keys in this node's keystore. - /// - /// The provider IDs found are added to the [`BlockchainService`]'s list of provider IDs. - pub(crate) fn get_provider_ids(&mut self, block_hash: &H256) { - for key in self.keystore.sr25519_public_keys(BCSV_KEY_TYPE) { - self.client - .runtime_api() - .get_storage_provider_id(*block_hash, &key.into()) - .map(|provider_id| { - if let Some(provider_id) = provider_id { - match provider_id { - StorageProviderId::BackupStorageProvider(bsp_id) => { - self.provider_ids.insert(bsp_id); - } - StorageProviderId::MainStorageProvider(msp_id) => { - self.provider_ids.insert(msp_id); - } - } - } else { - warn!(target: LOG_TARGET, "There is no provider ID for key: {:?}. This means that the node has a BCSV key in the keystore for which there is no provider ID.", key); - } - }) - .unwrap_or_else(|_| { - warn!(target: LOG_TARGET, "Failed to get provider ID for key: {:?}.", key); - }); - } - } - - /// Send an extrinsic to this node using an RPC call. - pub(crate) async fn send_extrinsic( - &mut self, - call: impl Into, - tip: Tip, - ) -> Result { - debug!(target: LOG_TARGET, "Sending extrinsic to the runtime"); - - // Get the nonce for the caller and increment it for the next transaction. - // TODO: Handle nonce overflow. - let nonce = self.nonce_counter; - - // Construct the extrinsic. - let extrinsic = self.construct_extrinsic(self.client.clone(), call, nonce, tip); - - // Generate a unique ID for this query. - let id_hash = Blake2Hasher::hash(&extrinsic.encode()); - // TODO: Consider storing the ID in a hashmap if later retrieval is needed. - - let (result, rx) = self - .rpc_handlers - .rpc_query(&format!( - r#"{{ - "jsonrpc": "2.0", - "method": "author_submitAndWatchExtrinsic", - "params": ["0x{}"], - "id": {:?} - }}"#, - array_bytes::bytes2hex("", &extrinsic.encode()), - array_bytes::bytes2hex("", &id_hash.as_bytes()) - )) - .await - .expect("Sending query failed even when it is correctly formatted as JSON-RPC; qed"); - - let json: serde_json::Value = - serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed"); - let error = json - .as_object() - .expect("JSON result is always an object; qed") - .get("error"); - - if let Some(error) = error { - // TODO: Consider how to handle a low nonce error, and retry. - return Err(anyhow::anyhow!("Error in RPC call: {}", error.to_string())); - } - - // Only update nonce after we are sure no errors - // occurred submitting the extrinsic. - self.nonce_counter += 1; - - Ok(RpcExtrinsicOutput { - hash: id_hash, - result, - receiver: rx, - }) - } - - /// Construct an extrinsic that can be applied to the runtime. - pub fn construct_extrinsic( - &self, - client: Arc, - function: impl Into, - nonce: u32, - tip: Tip, - ) -> UncheckedExtrinsic { - let function = function.into(); - let current_block_hash = client.info().best_hash; - let current_block = client.info().best_number.saturated_into(); - let genesis_block = client - .hash(0) - .expect("Failed to get genesis block hash, always present; qed") - .expect("Genesis block hash should never not be on-chain; qed"); - let period = BlockHashCount::get() - .checked_next_power_of_two() - .map(|c| c / 2) - .unwrap_or(2) as u64; - let extra: SignedExtra = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal( - period, - current_block, - )), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - tip, - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::< - storage_hub_runtime::Runtime, - >::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - let raw_payload = SignedPayload::from_raw( - function.clone(), - extra.clone(), - ( - (), - storage_hub_runtime::VERSION.spec_version, - storage_hub_runtime::VERSION.transaction_version, - genesis_block, - current_block_hash, - (), - (), - (), - (), - None, - ), - ); - - let caller_pub_key = Self::caller_pub_key(self.keystore.clone()); - - // Sign the payload. - let signature = raw_payload - .using_encoded(|e| self.keystore.sr25519_sign(BCSV_KEY_TYPE, &caller_pub_key, e)) - .expect("The payload is always valid and should be possible to sign; qed") - .expect("They key type and public key are valid because we just extracted them from the keystore; qed"); - - // Construct the extrinsic. - UncheckedExtrinsic::new_signed( - function.clone(), - storage_hub_runtime::Address::Id(>::into(caller_pub_key)), - polkadot_primitives::Signature::Sr25519(signature), - extra.clone(), - ) - } - - // Getting signer public key. - pub fn caller_pub_key(keystore: KeystorePtr) -> sp_core::sr25519::Public { - let caller_pub_key = keystore.sr25519_public_keys(BCSV_KEY_TYPE).pop().expect( - format!( - "There should be at least one sr25519 key in the keystore with key type '{:?}' ; qed", - BCSV_KEY_TYPE - ) - .as_str(), - ); - caller_pub_key - } - - /// Get an extrinsic from a block. - pub(crate) async fn get_extrinsic_from_block( - &self, - block_hash: H256, - extrinsic_hash: H256, - ) -> Result { - // Get the block. - let block = self - .client - .block(block_hash) - .expect("Failed to get block. This shouldn't be possible for known existing block hash; qed") - .expect("Block returned None for known existing block hash. This shouldn't be the case for a block known to have at least one transaction; qed"); - - // Get the extrinsics. - let extrinsics = block.block.extrinsics(); - - // Find the extrinsic index in the block. - let extrinsic_index = extrinsics - .iter() - .position(|e| { - let hash = Blake2Hasher::hash(&e.encode()); - hash == extrinsic_hash - }) - .expect("Extrinsic not found in block. This shouldn't be possible if we're looking into a block for which we got confirmation that the extrinsic was included; qed"); - - // Get the events from storage. - let events_in_block = get_events_at_block(&self.client, &block_hash)?; - - // Filter the events for the extrinsic. - // Each event record is composed of the `phase`, `event` and `topics` fields. - // We are interested in those events whose `phase` is equal to `ApplyExtrinsic` with the index of the extrinsic. - // For more information see: https://polkadot.js.org/docs/api/cookbook/blocks/#how-do-i-map-extrinsics-to-their-events - let events = events_in_block - .into_iter() - .filter(|ev| ev.phase == frame_system::Phase::ApplyExtrinsic(extrinsic_index as u32)) - .collect(); - - // Construct the extrinsic. - Ok(Extrinsic { - hash: extrinsic_hash, - block_hash, - events, - }) - } - - /// Unwatch an extrinsic. - pub(crate) async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result { - let (result, _rx) = self - .rpc_handlers - .rpc_query(&format!( - r#"{{ - "jsonrpc": "2.0", - "method": "author_unwatchExtrinsic", - "params": [{}], - "id": {} - }}"#, - subscription_id, subscription_id - )) - .await - .expect("Sending query failed even when it is correctly formatted as JSON-RPC; qed"); - - let json: serde_json::Value = - serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed"); - let unwatch_result = json - .as_object() - .expect("JSON result is always an object; qed") - .get("result"); - - if let Some(unwatch_result) = unwatch_result { - if unwatch_result - .as_bool() - .expect("Result is always a boolean; qed") - { - debug!(target: LOG_TARGET, "Extrinsic unwatched successfully"); - } else { - return Err(anyhow::anyhow!("Failed to unwatch extrinsic")); - } - } else { - return Err(anyhow::anyhow!("Failed to unwatch extrinsic")); - } - - Ok(result) - } - - /// Check if the challenges tick is one that this provider has to submit a proof for, - /// and if so, return true. - pub(crate) fn should_provider_submit_proof( - &self, - block_hash: &H256, - provider_id: &ProviderId, - current_tick: &BlockNumber, - ) -> bool { - // Get the last tick for which the BSP submitted a proof. - let last_tick_provided = match self - .client - .runtime_api() - .get_last_tick_provider_submitted_proof(*block_hash, provider_id) - { - Ok(last_tick_provided_result) => match last_tick_provided_result { - Ok(last_tick_provided) => last_tick_provided, - Err(e) => match e { - GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { - debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); - return false; - } - GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { - debug!(target: LOG_TARGET, "Provider [{:?}] does not have an initialised challenge cycle", provider_id); - return false; - } - GetLastTickProviderSubmittedProofError::InternalApiError => { - error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); - return false; - } - }, - }, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); - return false; - } - }; - - // Get the challenge period for the provider. - let provider_challenge_period = match self - .client - .runtime_api() - .get_challenge_period(*block_hash, provider_id) - { - Ok(provider_challenge_period_result) => match provider_challenge_period_result { - Ok(provider_challenge_period) => provider_challenge_period, - Err(e) => match e { - GetChallengePeriodError::ProviderNotRegistered => { - debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); - return false; - } - GetChallengePeriodError::InternalApiError => { - error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge period for Provider [{:?}]", provider_id); - return false; - } - }, - }, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", provider_id, e); - return false; - } - }; - - // Check if the current tick is a tick this provider should submit a proof for. - let current_tick_minus_last_submission = match current_tick.checked_sub(last_tick_provided) - { - Some(tick) => tick, - None => { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Current tick is smaller than the last tick this provider submitted a proof for. This should not happen. \nThis is a bug. Please report it to the StorageHub team."); - return false; - } - }; - - (current_tick_minus_last_submission % provider_challenge_period) == 0 - } - - /// Check if there are any pending requests to update the forest root on the runtime, and process them. - /// Takes care of prioritizing requests, favouring `SubmitProofRequest` over `ConfirmStoringRequest` over `StopStoringForInsolventUserRequest`. - /// This function is called every time a new block is imported and after each request is queued. - pub(crate) fn check_pending_forest_root_writes(&mut self) { - if let Some(mut rx) = self.forest_root_write_lock.take() { - // Note: tasks that get ownership of the lock are responsible for sending a message back when done processing. - match rx.try_recv() { - // If the channel is empty, means we still need to wait for the current task to finish. - Err(TryRecvError::Empty) => { - // If we have a task writing to the runtime, we don't want to start another one. - self.forest_root_write_lock = Some(rx); - trace!(target: LOG_TARGET, "Waiting for current forest root write task to finish"); - return; - } - Ok(_) => { - trace!(target: LOG_TARGET, "Forest root write task finished, lock is released!"); - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .access_value(&OngoingProcessConfirmStoringRequestCf) - .delete(); - state_store_context - .access_value(&OngoingProcessMspRespondStorageRequestCf) - .delete(); - state_store_context - .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) - .delete(); - state_store_context.commit(); - } - Err(TryRecvError::Closed) => { - error!(target: LOG_TARGET, "Forest root write task channel closed unexpectedly. Lock is released anyway!"); - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .access_value(&OngoingProcessConfirmStoringRequestCf) - .delete(); - state_store_context - .access_value(&OngoingProcessMspRespondStorageRequestCf) - .delete(); - state_store_context - .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) - .delete(); - state_store_context.commit(); - } - } - } - - // At this point we know that the lock is released and we can start processing new requests. - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - let mut next_event_data = None; - - // If we have a submit proof request, prioritise it. - while let Some(request) = self.pending_submit_proof_requests.pop_first() { - // Check if the proof is still the next one to be submitted. - let provider_id = request.provider_id; - let next_challenge_tick = match self.get_next_challenge_tick_for_provider(&provider_id) - { - Ok(next_challenge_tick) => next_challenge_tick, - Err(e) => { - error!(target: LOG_TARGET, "Failed to get next challenge tick for provider [{:?}]: {:?}", provider_id, e); - break; - } - }; - - // This is to avoid starting a new task if the proof is not the next one to be submitted. - if next_challenge_tick == request.tick { - // If the proof is still the next one to be submitted, we can process it. - next_event_data = Some(ForestWriteLockTaskData::SubmitProofRequest( - ProcessSubmitProofRequestData { - seed: request.seed, - provider_id: request.provider_id, - tick: request.tick, - forest_challenges: request.forest_challenges, - checkpoint_challenges: request.checkpoint_challenges, - }, - )); - break; - } else { - // If the proof is not the next one to be submitted, we can remove it from the list of pending submit proof requests. - trace!(target: LOG_TARGET, "Proof for tick [{:?}] is not the next one to be submitted. Removing it from the list of pending submit proof requests.", request.tick); - self.pending_submit_proof_requests.remove(&request); - } - } - - // If we have no pending submit proof requests, we can also check for pending confirm storing requests. - if next_event_data.is_none() { - let max_batch_confirm = - <::MaxBatchConfirmStorageRequests as Get< - u32, - >>::get(); - - // Batch multiple confirm file storing taking the runtime maximum. - let mut confirm_storing_requests = Vec::new(); - for _ in 0..max_batch_confirm { - if let Some(request) = state_store_context - .pending_confirm_storing_request_deque() - .pop_front() - { - confirm_storing_requests.push(request); - } else { - break; - } - } - - // If we have at least 1 confirm storing request, send the process event. - if confirm_storing_requests.len() > 0 { - next_event_data = Some( - ProcessConfirmStoringRequestData { - confirm_storing_requests, - } - .into(), - ); - } - } - - // If we have no pending submit proof requests nor pending confirm storing requests, we can also check for pending stop storing for insolvent user requests. - if next_event_data.is_none() { - if let Some(request) = state_store_context - .pending_stop_storing_for_insolvent_user_request_deque() - .pop_front() - { - next_event_data = Some( - ProcessStopStoringForInsolventUserRequestData { who: request.user }.into(), - ); - } - } - state_store_context.commit(); - - if let Some(event_data) = next_event_data { - self.emit_forest_write_event(event_data); - } - } - - pub(crate) fn emit_forest_write_event(&mut self, data: impl Into) { - let (tx, rx) = tokio::sync::oneshot::channel(); - self.forest_root_write_lock = Some(rx); - - let data = data.into(); - - // If this is a confirm storing request or a stop storing for insolvent user request, we need to store it in the state store. - match &data { - ForestWriteLockTaskData::ConfirmStoringRequest(data) => { - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .access_value(&OngoingProcessConfirmStoringRequestCf) - .write(data); - state_store_context.commit(); - } - ForestWriteLockTaskData::StopStoringForInsolventUserRequest(data) => { - let state_store_context = self.persistent_state.open_rw_context_with_overlay(); - state_store_context - .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) - .write(data); - state_store_context.commit(); - } - _ => {} - } - - // This is an [`Arc>>`] (in this case [`oneshot::Sender<()>`]) instead of just - // T so that we can keep using the current actors event bus (emit) which requires Clone on the - // event. Clone is required because there is no constraint on the number of listeners that can - // subscribe to the event (and each is guaranteed to receive all emitted events). - let forest_root_write_tx = Arc::new(Mutex::new(Some(tx))); - match data.into() { - ForestWriteLockTaskData::SubmitProofRequest(data) => { - self.emit(ProcessSubmitProofRequest { - data, - forest_root_write_tx, - }); - } - ForestWriteLockTaskData::ConfirmStoringRequest(data) => { - self.emit(ProcessConfirmStoringRequest { - data, - forest_root_write_tx, - }); - } - ForestWriteLockTaskData::MspRespondStorageRequest(data) => { - self.emit(ProcessMspRespondStoringRequest { - data, - forest_root_write_tx, - }); - } - ForestWriteLockTaskData::StopStoringForInsolventUserRequest(data) => { - self.emit(ProcessStopStoringForInsolventUserRequest { - data, - forest_root_write_tx, - }); - } - } - } - - /// Emits a `MultipleNewChallengeSeeds` event with all the pending proof submissions for this provider. - /// This is used to catch up to the latest proof submissions that were missed due to a node restart. - /// Also, it can help to catch up to proofs in case there is a change in the BSP's stake (therefore - /// also a change in it's challenge period). - /// - /// IMPORTANT: This function takes into account whether a proof should be submitted for the current tick. - pub(crate) fn proof_submission_catch_up( - &self, - current_block_hash: &H256, - provider_id: &ProviderId, - ) { - // Get the last tick for which the BSP submitted a proof, according to the runtime right now. - let last_tick_provider_submitted_proof = match self - .client - .runtime_api() - .get_last_tick_provider_submitted_proof(*current_block_hash, provider_id) - { - Ok(last_tick_provided_result) => match last_tick_provided_result { - Ok(last_tick_provided) => last_tick_provided, - Err(e) => match e { - GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { - debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); - return; - } - GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { - debug!(target: LOG_TARGET, "Provider [{:?}] does not have an initialised challenge cycle", provider_id); - return; - } - GetLastTickProviderSubmittedProofError::InternalApiError => { - error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); - return; - } - }, - }, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); - return; - } - }; - trace!(target: LOG_TARGET, "Last tick Provider [{:?}] submitted a proof for: {}", provider_id, last_tick_provider_submitted_proof); - - // Get the current challenge period for this provider. - let challenge_period = match self - .client - .runtime_api() - .get_challenge_period(*current_block_hash, provider_id) - { - Ok(challenge_period_result) => match challenge_period_result { - Ok(challenge_period) => challenge_period, - Err(e) => match e { - GetChallengePeriodError::ProviderNotRegistered => { - debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); - return; - } - GetChallengePeriodError::InternalApiError => { - error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge period for Provider [{:?}]", provider_id); - return; - } - }, - }, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", provider_id, e); - return; - } - }; - - // Get the current tick. - let current_tick = match self - .client - .runtime_api() - .get_current_tick(*current_block_hash) - { - Ok(current_tick) => current_tick, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting current tick for Provider [{:?}]: {:?}", provider_id, e); - return; - } - }; - - // Advance by `challenge_period` ticks and add the seed to the list of challenge seeds. - let mut challenge_seeds = Vec::new(); - let mut next_challenge_tick = last_tick_provider_submitted_proof + challenge_period; - while next_challenge_tick <= current_tick { - // Get the seed for the challenge tick. - let seed = match self - .client - .runtime_api() - .get_challenge_seed(*current_block_hash, next_challenge_tick) - { - Ok(seed_result) => match seed_result { - Ok(seed) => seed, - Err(e) => match e { - GetChallengeSeedError::TickBeyondLastSeedStored => { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Tick [{:?}] is beyond last seed stored and this provider needs to submit a proof for it.", next_challenge_tick); - return; - } - GetChallengeSeedError::TickIsInTheFuture => { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Tick [{:?}] is in the future. This should never happen. \nThis is a bug. Please report it to the StorageHub team.", next_challenge_tick); - return; - } - GetChallengeSeedError::InternalApiError => { - error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge seed for challenge tick [{:?}]: {:?}", next_challenge_tick, e); - return; - } - }, - }, - Err(e) => { - error!(target: LOG_TARGET, "Runtime API error while getting challenges from seed for challenge tick [{:?}]: {:?}", next_challenge_tick, e); - return; - } - }; - challenge_seeds.push((next_challenge_tick, seed)); - next_challenge_tick += challenge_period; - } - - // Emit the `MultiNewChallengeSeeds` event. - if challenge_seeds.len() > 0 { - trace!(target: LOG_TARGET, "Emitting MultipleNewChallengeSeeds event for provider [{:?}] with challenge seeds: {:?}", provider_id, challenge_seeds); - self.emit(MultipleNewChallengeSeeds { - provider_id: *provider_id, - seeds: challenge_seeds, - }); - } - } - - pub(crate) fn get_next_challenge_tick_for_provider( - &self, - provider_id: &ProviderId, - ) -> Result { - // Get the current block hash. - let current_block_hash = self.client.info().best_hash; - - // Get the last tick for which the provider submitted a proof. - let last_tick_provider_submitted_proof = match self - .client - .runtime_api() - .get_last_tick_provider_submitted_proof(current_block_hash, provider_id) - { - Ok(last_tick_provided_result) => match last_tick_provided_result { - Ok(last_tick_provided) => last_tick_provided, - Err(e) => match e { - GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { - return Err(anyhow!("Provider [{:?}] is not registered", provider_id)); - } - GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { - return Err(anyhow!( - "Provider [{:?}] does not have an initialised challenge cycle", - provider_id - )); - } - GetLastTickProviderSubmittedProofError::InternalApiError => { - return Err(anyhow!( - "Internal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", - provider_id, e - )); - } - }, - }, - Err(e) => { - return Err(anyhow!( - "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", - provider_id, - e - )); - } - }; - - // Get the challenge period for the provider. - let challenge_period = match self - .client - .runtime_api() - .get_challenge_period(current_block_hash, provider_id) - { - Ok(challenge_period_result) => match challenge_period_result { - Ok(challenge_period) => challenge_period, - Err(e) => match e { - GetChallengePeriodError::ProviderNotRegistered => { - return Err(anyhow!("Provider [{:?}] is not registered", provider_id)); - } - GetChallengePeriodError::InternalApiError => { - return Err(anyhow!( - "Internal API error while getting challenge period for Provider [{:?}]", - provider_id - )); - } - }, - }, - Err(e) => { - return Err(anyhow!( - "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", - provider_id, - e - )); - } - }; - - // Calculate the next challenge tick. - let next_challenge_tick = last_tick_provider_submitted_proof + challenge_period; - - // Check if the current tick is a tick this provider should submit a proof for. - Ok(next_challenge_tick) - } -} - -/// The output of an RPC transaction. -pub struct RpcExtrinsicOutput { - /// Hash of the extrinsic. - pub hash: H256, - /// The output string of the transaction if any. - pub result: String, - /// An async receiver if data will be returned via a callback. - pub receiver: tokio::sync::mpsc::Receiver, -} - -impl std::fmt::Debug for RpcExtrinsicOutput { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "RpcExtrinsicOutput {{ hash: {:?}, result: {:?}, receiver }}", - self.hash, self.result - ) - } -} +use std::sync::Arc; + +use anyhow::{anyhow, Result}; +use codec::Encode; +use cumulus_primitives_core::BlockT; +use log::{debug, error, trace, warn}; +use pallet_proofs_dealer_runtime_api::{ + GetChallengePeriodError, GetChallengeSeedError, GetLastTickProviderSubmittedProofError, + ProofsDealerApi, +}; +use pallet_storage_providers::types::StorageProviderId; +use pallet_storage_providers_runtime_api::StorageProvidersApi; +use polkadot_runtime_common::BlockHashCount; +use sc_client_api::{BlockBackend, HeaderBackend}; +use serde_json::Number; +use shc_actors_framework::actor::Actor; +use shc_common::{ + blockchain_utils::get_events_at_block, + types::{BlockNumber, ParachainClient, ProviderId, BCSV_KEY_TYPE}, +}; +use sp_api::ProvideRuntimeApi; +use sp_core::{Blake2Hasher, Get, Hasher, H256}; +use sp_keystore::KeystorePtr; +use sp_runtime::{ + generic::{self, SignedPayload}, + SaturatedConversion, +}; +use storage_hub_runtime::{Runtime, SignedExtra, UncheckedExtrinsic}; +use substrate_frame_rpc_system::AccountNonceApi; +use tokio::sync::{oneshot::error::TryRecvError, Mutex}; + +use crate::{ + events::{ + ForestWriteLockTaskData, MultipleNewChallengeSeeds, ProcessConfirmStoringRequest, + ProcessConfirmStoringRequestData, ProcessMspRespondStoringRequest, + ProcessStopStoringForInsolventUserRequest, ProcessStopStoringForInsolventUserRequestData, + ProcessSubmitProofRequest, ProcessSubmitProofRequestData, + }, + handler::LOG_TARGET, + state::{ + OngoingProcessConfirmStoringRequestCf, OngoingProcessMspRespondStorageRequestCf, + OngoingProcessStopStoringForInsolventUserRequestCf, + }, + typed_store::{CFDequeAPI, ProvidesTypedDbSingleAccess}, + types::{Extrinsic, Tip}, + BlockchainService, +}; + +impl BlockchainService { + /// Notify tasks waiting for a block number. + pub(crate) fn notify_import_block_number(&mut self, block_number: &BlockNumber) { + let mut keys_to_remove = Vec::new(); + + for (block_number, waiters) in self + .wait_for_block_request_by_number + .range_mut(..=block_number) + { + keys_to_remove.push(*block_number); + for waiter in waiters.drain(..) { + match waiter.send(()) { + Ok(_) => {} + Err(_) => { + error!(target: LOG_TARGET, "Failed to notify task about block number."); + } + } + } + } + + for key in keys_to_remove { + self.wait_for_block_request_by_number.remove(&key); + } + } + + /// Notify tasks waiting for a tick number. + pub(crate) fn notify_tick_number(&mut self, block_hash: &H256) { + // Get the current tick number. + let tick_number = match self.client.runtime_api().get_current_tick(*block_hash) { + Ok(current_tick) => current_tick, + Err(_) => { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to query current tick from runtime in block hash {:?} and block number {:?}. This should not happen.", block_hash, self.client.info().best_number); + return; + } + }; + + let mut keys_to_remove = Vec::new(); + + for (tick_number, waiters) in self + .wait_for_tick_request_by_number + .range_mut(..=tick_number) + { + keys_to_remove.push(*tick_number); + for waiter in waiters.drain(..) { + match waiter.send(Ok(())) { + Ok(_) => {} + Err(_) => { + error!(target: LOG_TARGET, "Failed to notify task about tick number."); + } + } + } + } + + for key in keys_to_remove { + self.wait_for_tick_request_by_number.remove(&key); + } + } + + /// Checks if the account nonce on-chain is higher than the nonce in the [`BlockchainService`]. + /// + /// If the nonce is higher, the account nonce is updated in the [`BlockchainService`]. + pub(crate) fn check_nonce(&mut self, block_hash: &H256) { + let pub_key = Self::caller_pub_key(self.keystore.clone()); + let latest_nonce = self + .client + .runtime_api() + .account_nonce(*block_hash, pub_key.into()) + .expect("Fetching account nonce works; qed"); + if latest_nonce > self.nonce_counter { + self.nonce_counter = latest_nonce + } + } + + /// Get all the provider IDs linked to keys in this node's keystore. + /// + /// The provider IDs found are added to the [`BlockchainService`]'s list of provider IDs. + pub(crate) fn get_provider_ids(&mut self, block_hash: &H256) { + for key in self.keystore.sr25519_public_keys(BCSV_KEY_TYPE) { + self.client + .runtime_api() + .get_storage_provider_id(*block_hash, &key.into()) + .map(|provider_id| { + if let Some(provider_id) = provider_id { + match provider_id { + StorageProviderId::BackupStorageProvider(bsp_id) => { + self.provider_ids.insert(bsp_id); + } + StorageProviderId::MainStorageProvider(msp_id) => { + self.provider_ids.insert(msp_id); + } + } + } else { + warn!(target: LOG_TARGET, "There is no provider ID for key: {:?}. This means that the node has a BCSV key in the keystore for which there is no provider ID.", key); + } + }) + .unwrap_or_else(|_| { + warn!(target: LOG_TARGET, "Failed to get provider ID for key: {:?}.", key); + }); + } + } + + /// Send an extrinsic to this node using an RPC call. + pub(crate) async fn send_extrinsic( + &mut self, + call: impl Into, + tip: Tip, + ) -> Result { + debug!(target: LOG_TARGET, "Sending extrinsic to the runtime"); + + // Get the nonce for the caller and increment it for the next transaction. + // TODO: Handle nonce overflow. + let nonce = self.nonce_counter; + + // Construct the extrinsic. + let extrinsic = self.construct_extrinsic(self.client.clone(), call, nonce, tip); + + // Generate a unique ID for this query. + let id_hash = Blake2Hasher::hash(&extrinsic.encode()); + // TODO: Consider storing the ID in a hashmap if later retrieval is needed. + + let (result, rx) = self + .rpc_handlers + .rpc_query(&format!( + r#"{{ + "jsonrpc": "2.0", + "method": "author_submitAndWatchExtrinsic", + "params": ["0x{}"], + "id": {:?} + }}"#, + array_bytes::bytes2hex("", &extrinsic.encode()), + array_bytes::bytes2hex("", &id_hash.as_bytes()) + )) + .await + .expect("Sending query failed even when it is correctly formatted as JSON-RPC; qed"); + + let json: serde_json::Value = + serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed"); + let error = json + .as_object() + .expect("JSON result is always an object; qed") + .get("error"); + + if let Some(error) = error { + // TODO: Consider how to handle a low nonce error, and retry. + return Err(anyhow::anyhow!("Error in RPC call: {}", error.to_string())); + } + + // Only update nonce after we are sure no errors + // occurred submitting the extrinsic. + self.nonce_counter += 1; + + Ok(RpcExtrinsicOutput { + hash: id_hash, + result, + receiver: rx, + }) + } + + /// Construct an extrinsic that can be applied to the runtime. + pub fn construct_extrinsic( + &self, + client: Arc, + function: impl Into, + nonce: u32, + tip: Tip, + ) -> UncheckedExtrinsic { + let function = function.into(); + let current_block_hash = client.info().best_hash; + let current_block = client.info().best_number.saturated_into(); + let genesis_block = client + .hash(0) + .expect("Failed to get genesis block hash, always present; qed") + .expect("Genesis block hash should never not be on-chain; qed"); + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal( + period, + current_block, + )), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + tip, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::< + storage_hub_runtime::Runtime, + >::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + let raw_payload = SignedPayload::from_raw( + function.clone(), + extra.clone(), + ( + (), + storage_hub_runtime::VERSION.spec_version, + storage_hub_runtime::VERSION.transaction_version, + genesis_block, + current_block_hash, + (), + (), + (), + (), + None, + ), + ); + + let caller_pub_key = Self::caller_pub_key(self.keystore.clone()); + + // Sign the payload. + let signature = raw_payload + .using_encoded(|e| self.keystore.sr25519_sign(BCSV_KEY_TYPE, &caller_pub_key, e)) + .expect("The payload is always valid and should be possible to sign; qed") + .expect("They key type and public key are valid because we just extracted them from the keystore; qed"); + + // Construct the extrinsic. + UncheckedExtrinsic::new_signed( + function.clone(), + storage_hub_runtime::Address::Id(>::into(caller_pub_key)), + polkadot_primitives::Signature::Sr25519(signature), + extra.clone(), + ) + } + + // Getting signer public key. + pub fn caller_pub_key(keystore: KeystorePtr) -> sp_core::sr25519::Public { + let caller_pub_key = keystore.sr25519_public_keys(BCSV_KEY_TYPE).pop().expect( + format!( + "There should be at least one sr25519 key in the keystore with key type '{:?}' ; qed", + BCSV_KEY_TYPE + ) + .as_str(), + ); + caller_pub_key + } + + /// Get an extrinsic from a block. + pub(crate) async fn get_extrinsic_from_block( + &self, + block_hash: H256, + extrinsic_hash: H256, + ) -> Result { + // Get the block. + let block = self + .client + .block(block_hash) + .expect("Failed to get block. This shouldn't be possible for known existing block hash; qed") + .expect("Block returned None for known existing block hash. This shouldn't be the case for a block known to have at least one transaction; qed"); + + // Get the extrinsics. + let extrinsics = block.block.extrinsics(); + + // Find the extrinsic index in the block. + let extrinsic_index = extrinsics + .iter() + .position(|e| { + let hash = Blake2Hasher::hash(&e.encode()); + hash == extrinsic_hash + }) + .expect("Extrinsic not found in block. This shouldn't be possible if we're looking into a block for which we got confirmation that the extrinsic was included; qed"); + + // Get the events from storage. + let events_in_block = get_events_at_block(&self.client, &block_hash)?; + + // Filter the events for the extrinsic. + // Each event record is composed of the `phase`, `event` and `topics` fields. + // We are interested in those events whose `phase` is equal to `ApplyExtrinsic` with the index of the extrinsic. + // For more information see: https://polkadot.js.org/docs/api/cookbook/blocks/#how-do-i-map-extrinsics-to-their-events + let events = events_in_block + .into_iter() + .filter(|ev| ev.phase == frame_system::Phase::ApplyExtrinsic(extrinsic_index as u32)) + .collect(); + + // Construct the extrinsic. + Ok(Extrinsic { + hash: extrinsic_hash, + block_hash, + events, + }) + } + + /// Unwatch an extrinsic. + pub(crate) async fn unwatch_extrinsic(&self, subscription_id: Number) -> Result { + let (result, _rx) = self + .rpc_handlers + .rpc_query(&format!( + r#"{{ + "jsonrpc": "2.0", + "method": "author_unwatchExtrinsic", + "params": [{}], + "id": {} + }}"#, + subscription_id, subscription_id + )) + .await + .expect("Sending query failed even when it is correctly formatted as JSON-RPC; qed"); + + let json: serde_json::Value = + serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed"); + let unwatch_result = json + .as_object() + .expect("JSON result is always an object; qed") + .get("result"); + + if let Some(unwatch_result) = unwatch_result { + if unwatch_result + .as_bool() + .expect("Result is always a boolean; qed") + { + debug!(target: LOG_TARGET, "Extrinsic unwatched successfully"); + } else { + return Err(anyhow::anyhow!("Failed to unwatch extrinsic")); + } + } else { + return Err(anyhow::anyhow!("Failed to unwatch extrinsic")); + } + + Ok(result) + } + + /// Check if the challenges tick is one that this provider has to submit a proof for, + /// and if so, return true. + pub(crate) fn should_provider_submit_proof( + &self, + block_hash: &H256, + provider_id: &ProviderId, + current_tick: &BlockNumber, + ) -> bool { + // Get the last tick for which the BSP submitted a proof. + let last_tick_provided = match self + .client + .runtime_api() + .get_last_tick_provider_submitted_proof(*block_hash, provider_id) + { + Ok(last_tick_provided_result) => match last_tick_provided_result { + Ok(last_tick_provided) => last_tick_provided, + Err(e) => match e { + GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { + debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); + return false; + } + GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { + debug!(target: LOG_TARGET, "Provider [{:?}] does not have an initialised challenge cycle", provider_id); + return false; + } + GetLastTickProviderSubmittedProofError::InternalApiError => { + error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); + return false; + } + }, + }, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); + return false; + } + }; + + // Get the challenge period for the provider. + let provider_challenge_period = match self + .client + .runtime_api() + .get_challenge_period(*block_hash, provider_id) + { + Ok(provider_challenge_period_result) => match provider_challenge_period_result { + Ok(provider_challenge_period) => provider_challenge_period, + Err(e) => match e { + GetChallengePeriodError::ProviderNotRegistered => { + debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); + return false; + } + GetChallengePeriodError::InternalApiError => { + error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge period for Provider [{:?}]", provider_id); + return false; + } + }, + }, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", provider_id, e); + return false; + } + }; + + // Check if the current tick is a tick this provider should submit a proof for. + let current_tick_minus_last_submission = match current_tick.checked_sub(last_tick_provided) + { + Some(tick) => tick, + None => { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Current tick is smaller than the last tick this provider submitted a proof for. This should not happen. \nThis is a bug. Please report it to the StorageHub team."); + return false; + } + }; + + (current_tick_minus_last_submission % provider_challenge_period) == 0 + } + + /// Check if there are any pending requests to update the forest root on the runtime, and process them. + /// Takes care of prioritizing requests, favouring `SubmitProofRequest` over `ConfirmStoringRequest` over `StopStoringForInsolventUserRequest`. + /// This function is called every time a new block is imported and after each request is queued. + pub(crate) fn check_pending_forest_root_writes(&mut self) { + if let Some(mut rx) = self.forest_root_write_lock.take() { + // Note: tasks that get ownership of the lock are responsible for sending a message back when done processing. + match rx.try_recv() { + // If the channel is empty, means we still need to wait for the current task to finish. + Err(TryRecvError::Empty) => { + // If we have a task writing to the runtime, we don't want to start another one. + self.forest_root_write_lock = Some(rx); + trace!(target: LOG_TARGET, "Waiting for current forest root write task to finish"); + return; + } + Ok(_) => { + trace!(target: LOG_TARGET, "Forest root write task finished, lock is released!"); + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .access_value(&OngoingProcessConfirmStoringRequestCf) + .delete(); + state_store_context + .access_value(&OngoingProcessMspRespondStorageRequestCf) + .delete(); + state_store_context + .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) + .delete(); + state_store_context.commit(); + } + Err(TryRecvError::Closed) => { + error!(target: LOG_TARGET, "Forest root write task channel closed unexpectedly. Lock is released anyway!"); + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .access_value(&OngoingProcessConfirmStoringRequestCf) + .delete(); + state_store_context + .access_value(&OngoingProcessMspRespondStorageRequestCf) + .delete(); + state_store_context + .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) + .delete(); + state_store_context.commit(); + } + } + } + + // At this point we know that the lock is released and we can start processing new requests. + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + let mut next_event_data = None; + + // If we have a submit proof request, prioritise it. + while let Some(request) = self.pending_submit_proof_requests.pop_first() { + // Check if the proof is still the next one to be submitted. + let provider_id = request.provider_id; + let next_challenge_tick = match self.get_next_challenge_tick_for_provider(&provider_id) + { + Ok(next_challenge_tick) => next_challenge_tick, + Err(e) => { + error!(target: LOG_TARGET, "Failed to get next challenge tick for provider [{:?}]: {:?}", provider_id, e); + break; + } + }; + + // This is to avoid starting a new task if the proof is not the next one to be submitted. + if next_challenge_tick == request.tick { + // If the proof is still the next one to be submitted, we can process it. + next_event_data = Some(ForestWriteLockTaskData::SubmitProofRequest( + ProcessSubmitProofRequestData { + seed: request.seed, + provider_id: request.provider_id, + tick: request.tick, + forest_challenges: request.forest_challenges, + checkpoint_challenges: request.checkpoint_challenges, + }, + )); + break; + } else { + // If the proof is not the next one to be submitted, we can remove it from the list of pending submit proof requests. + trace!(target: LOG_TARGET, "Proof for tick [{:?}] is not the next one to be submitted. Removing it from the list of pending submit proof requests.", request.tick); + self.pending_submit_proof_requests.remove(&request); + } + } + + // If we have no pending submit proof requests, we can also check for pending confirm storing requests. + if next_event_data.is_none() { + let max_batch_confirm = + <::MaxBatchConfirmStorageRequests as Get< + u32, + >>::get(); + + // Batch multiple confirm file storing taking the runtime maximum. + let mut confirm_storing_requests = Vec::new(); + for _ in 0..max_batch_confirm { + if let Some(request) = state_store_context + .pending_confirm_storing_request_deque() + .pop_front() + { + confirm_storing_requests.push(request); + } else { + break; + } + } + + // If we have at least 1 confirm storing request, send the process event. + if confirm_storing_requests.len() > 0 { + next_event_data = Some( + ProcessConfirmStoringRequestData { + confirm_storing_requests, + } + .into(), + ); + } + } + + // If we have no pending submit proof requests nor pending confirm storing requests, we can also check for pending stop storing for insolvent user requests. + if next_event_data.is_none() { + if let Some(request) = state_store_context + .pending_stop_storing_for_insolvent_user_request_deque() + .pop_front() + { + next_event_data = Some( + ProcessStopStoringForInsolventUserRequestData { who: request.user }.into(), + ); + } + } + state_store_context.commit(); + + if let Some(event_data) = next_event_data { + self.emit_forest_write_event(event_data); + } + } + + pub(crate) fn emit_forest_write_event(&mut self, data: impl Into) { + let (tx, rx) = tokio::sync::oneshot::channel(); + self.forest_root_write_lock = Some(rx); + + let data = data.into(); + + // If this is a confirm storing request or a stop storing for insolvent user request, we need to store it in the state store. + match &data { + ForestWriteLockTaskData::ConfirmStoringRequest(data) => { + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .access_value(&OngoingProcessConfirmStoringRequestCf) + .write(data); + state_store_context.commit(); + } + ForestWriteLockTaskData::StopStoringForInsolventUserRequest(data) => { + let state_store_context = self.persistent_state.open_rw_context_with_overlay(); + state_store_context + .access_value(&OngoingProcessStopStoringForInsolventUserRequestCf) + .write(data); + state_store_context.commit(); + } + _ => {} + } + + // This is an [`Arc>>`] (in this case [`oneshot::Sender<()>`]) instead of just + // T so that we can keep using the current actors event bus (emit) which requires Clone on the + // event. Clone is required because there is no constraint on the number of listeners that can + // subscribe to the event (and each is guaranteed to receive all emitted events). + let forest_root_write_tx = Arc::new(Mutex::new(Some(tx))); + match data.into() { + ForestWriteLockTaskData::SubmitProofRequest(data) => { + self.emit(ProcessSubmitProofRequest { + data, + forest_root_write_tx, + }); + } + ForestWriteLockTaskData::ConfirmStoringRequest(data) => { + self.emit(ProcessConfirmStoringRequest { + data, + forest_root_write_tx, + }); + } + ForestWriteLockTaskData::MspRespondStorageRequest(data) => { + self.emit(ProcessMspRespondStoringRequest { + data, + forest_root_write_tx, + }); + } + ForestWriteLockTaskData::StopStoringForInsolventUserRequest(data) => { + self.emit(ProcessStopStoringForInsolventUserRequest { + data, + forest_root_write_tx, + }); + } + } + } + + /// Emits a `MultipleNewChallengeSeeds` event with all the pending proof submissions for this provider. + /// This is used to catch up to the latest proof submissions that were missed due to a node restart. + /// Also, it can help to catch up to proofs in case there is a change in the BSP's stake (therefore + /// also a change in it's challenge period). + /// + /// IMPORTANT: This function takes into account whether a proof should be submitted for the current tick. + pub(crate) fn proof_submission_catch_up( + &self, + current_block_hash: &H256, + provider_id: &ProviderId, + ) { + // Get the last tick for which the BSP submitted a proof, according to the runtime right now. + let last_tick_provider_submitted_proof = match self + .client + .runtime_api() + .get_last_tick_provider_submitted_proof(*current_block_hash, provider_id) + { + Ok(last_tick_provided_result) => match last_tick_provided_result { + Ok(last_tick_provided) => last_tick_provided, + Err(e) => match e { + GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { + debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); + return; + } + GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { + debug!(target: LOG_TARGET, "Provider [{:?}] does not have an initialised challenge cycle", provider_id); + return; + } + GetLastTickProviderSubmittedProofError::InternalApiError => { + error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); + return; + } + }, + }, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", provider_id, e); + return; + } + }; + trace!(target: LOG_TARGET, "Last tick Provider [{:?}] submitted a proof for: {}", provider_id, last_tick_provider_submitted_proof); + + // Get the current challenge period for this provider. + let challenge_period = match self + .client + .runtime_api() + .get_challenge_period(*current_block_hash, provider_id) + { + Ok(challenge_period_result) => match challenge_period_result { + Ok(challenge_period) => challenge_period, + Err(e) => match e { + GetChallengePeriodError::ProviderNotRegistered => { + debug!(target: LOG_TARGET, "Provider [{:?}] is not registered", provider_id); + return; + } + GetChallengePeriodError::InternalApiError => { + error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge period for Provider [{:?}]", provider_id); + return; + } + }, + }, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", provider_id, e); + return; + } + }; + + // Get the current tick. + let current_tick = match self + .client + .runtime_api() + .get_current_tick(*current_block_hash) + { + Ok(current_tick) => current_tick, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting current tick for Provider [{:?}]: {:?}", provider_id, e); + return; + } + }; + + // Advance by `challenge_period` ticks and add the seed to the list of challenge seeds. + let mut challenge_seeds = Vec::new(); + let mut next_challenge_tick = last_tick_provider_submitted_proof + challenge_period; + while next_challenge_tick <= current_tick { + // Get the seed for the challenge tick. + let seed = match self + .client + .runtime_api() + .get_challenge_seed(*current_block_hash, next_challenge_tick) + { + Ok(seed_result) => match seed_result { + Ok(seed) => seed, + Err(e) => match e { + GetChallengeSeedError::TickBeyondLastSeedStored => { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Tick [{:?}] is beyond last seed stored and this provider needs to submit a proof for it.", next_challenge_tick); + return; + } + GetChallengeSeedError::TickIsInTheFuture => { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Tick [{:?}] is in the future. This should never happen. \nThis is a bug. Please report it to the StorageHub team.", next_challenge_tick); + return; + } + GetChallengeSeedError::InternalApiError => { + error!(target: LOG_TARGET, "This should be impossible, we just checked the API error. \nInternal API error while getting challenge seed for challenge tick [{:?}]: {:?}", next_challenge_tick, e); + return; + } + }, + }, + Err(e) => { + error!(target: LOG_TARGET, "Runtime API error while getting challenges from seed for challenge tick [{:?}]: {:?}", next_challenge_tick, e); + return; + } + }; + challenge_seeds.push((next_challenge_tick, seed)); + next_challenge_tick += challenge_period; + } + + // Emit the `MultiNewChallengeSeeds` event. + if challenge_seeds.len() > 0 { + trace!(target: LOG_TARGET, "Emitting MultipleNewChallengeSeeds event for provider [{:?}] with challenge seeds: {:?}", provider_id, challenge_seeds); + self.emit(MultipleNewChallengeSeeds { + provider_id: *provider_id, + seeds: challenge_seeds, + }); + } + } + + pub(crate) fn get_next_challenge_tick_for_provider( + &self, + provider_id: &ProviderId, + ) -> Result { + // Get the current block hash. + let current_block_hash = self.client.info().best_hash; + + // Get the last tick for which the provider submitted a proof. + let last_tick_provider_submitted_proof = match self + .client + .runtime_api() + .get_last_tick_provider_submitted_proof(current_block_hash, provider_id) + { + Ok(last_tick_provided_result) => match last_tick_provided_result { + Ok(last_tick_provided) => last_tick_provided, + Err(e) => match e { + GetLastTickProviderSubmittedProofError::ProviderNotRegistered => { + return Err(anyhow!("Provider [{:?}] is not registered", provider_id)); + } + GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof => { + return Err(anyhow!( + "Provider [{:?}] does not have an initialised challenge cycle", + provider_id + )); + } + GetLastTickProviderSubmittedProofError::InternalApiError => { + return Err(anyhow!( + "Internal API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", + provider_id, e + )); + } + }, + }, + Err(e) => { + return Err(anyhow!( + "Runtime API error while getting last tick Provider [{:?}] submitted a proof for: {:?}", + provider_id, + e + )); + } + }; + + // Get the challenge period for the provider. + let challenge_period = match self + .client + .runtime_api() + .get_challenge_period(current_block_hash, provider_id) + { + Ok(challenge_period_result) => match challenge_period_result { + Ok(challenge_period) => challenge_period, + Err(e) => match e { + GetChallengePeriodError::ProviderNotRegistered => { + return Err(anyhow!("Provider [{:?}] is not registered", provider_id)); + } + GetChallengePeriodError::InternalApiError => { + return Err(anyhow!( + "Internal API error while getting challenge period for Provider [{:?}]", + provider_id + )); + } + }, + }, + Err(e) => { + return Err(anyhow!( + "Runtime API error while getting challenge period for Provider [{:?}]: {:?}", + provider_id, + e + )); + } + }; + + // Calculate the next challenge tick. + let next_challenge_tick = last_tick_provider_submitted_proof + challenge_period; + + // Check if the current tick is a tick this provider should submit a proof for. + Ok(next_challenge_tick) + } +} + +/// The output of an RPC transaction. +pub struct RpcExtrinsicOutput { + /// Hash of the extrinsic. + pub hash: H256, + /// The output string of the transaction if any. + pub result: String, + /// An async receiver if data will be returned via a callback. + pub receiver: tokio::sync::mpsc::Receiver, +} + +impl std::fmt::Debug for RpcExtrinsicOutput { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "RpcExtrinsicOutput {{ hash: {:?}, result: {:?}, receiver }}", + self.hash, self.result + ) + } +} diff --git a/client/common/src/types.rs b/client/common/src/types.rs index 943ba4541..0ba515631 100644 --- a/client/common/src/types.rs +++ b/client/common/src/types.rs @@ -1,156 +1,156 @@ -use std::fmt::Debug; - -use codec::{Decode, Encode}; -use frame_system::EventRecord; -use sc_executor::WasmExecutor; -use sc_service::TFullClient; -pub use shp_constants::{FILE_CHUNK_SIZE, FILE_SIZE_TO_CHALLENGES, H_LENGTH}; -pub use shp_file_metadata::{Chunk, ChunkId, ChunkWithId, Leaf}; -use shp_traits::CommitmentVerifier; -use sp_core::Hasher; -use sp_runtime::{traits::Block as BlockT, KeyTypeId}; -use sp_std::collections::btree_map::BTreeMap; -use sp_trie::CompactProof; -use storage_hub_runtime::{apis::RuntimeApi, opaque::Block, Runtime}; -use trie_db::TrieLayout; - -/// The hash type of trie node keys -pub type HashT = ::Hash; -pub type HasherOutT = <::Hash as Hasher>::Out; - -/// Following types are shared between the client and the runtime. -/// They are defined as generic types in the runtime and made concrete using the runtime config -/// here to be used by the node/client. -pub type FileKeyVerifier = ::KeyVerifier; -pub type FileKeyProof = ::Proof; -pub type Hash = shp_file_metadata::Hash; -pub type Fingerprint = shp_file_metadata::Fingerprint; -pub type FileMetadata = - shp_file_metadata::FileMetadata; -pub type FileKey = shp_file_metadata::FileKey; -pub type BlockNumber = frame_system::pallet_prelude::BlockNumberFor; -pub type TickNumber = pallet_file_system::types::TickNumber; -pub type StorageData = pallet_file_system::types::StorageData; -pub type FileLocation = pallet_file_system::types::FileLocation; -pub type FileKeyResponsesInput = pallet_file_system::types::FileKeyResponsesInput; -pub type MspStorageRequestResponse = pallet_file_system::types::MspStorageRequestResponse; -pub type AcceptedStorageRequestParameters = - pallet_file_system::types::AcceptedStorageRequestParameters; -pub type RejectedStorageRequestReason = pallet_file_system::types::RejectedStorageRequestReason; -pub type PeerIds = pallet_file_system::types::PeerIds; -pub type BucketId = pallet_storage_providers::types::MerklePatriciaRoot; -pub type StorageProviderId = pallet_storage_providers::types::StorageProviderId; -pub type MainStorageProviderId = pallet_storage_providers::types::ProviderId; -pub type ProviderId = pallet_proofs_dealer::types::ProviderIdFor; -pub type RandomnessOutput = pallet_proofs_dealer::types::RandomnessOutputFor; -pub type ForestLeaf = pallet_proofs_dealer::types::KeyFor; -pub type ForestRoot = pallet_proofs_dealer::types::ForestRootFor; -pub type TrieMutation = shp_traits::TrieMutation; -pub type TrieRemoveMutation = shp_traits::TrieRemoveMutation; -pub type StorageProofsMerkleTrieLayout = storage_hub_runtime::StorageProofsMerkleTrieLayout; -pub type StorageProof = pallet_proofs_dealer::types::Proof; -pub type ForestVerifierProof = pallet_proofs_dealer::types::ForestVerifierProofFor; -pub type KeyProof = pallet_proofs_dealer::types::KeyProof; -pub type KeyProofs = BTreeMap; -pub type Balance = pallet_storage_providers::types::BalanceOf; -pub type OpaqueBlock = storage_hub_runtime::opaque::Block; -pub type BlockHash = ::Hash; - -#[cfg(not(feature = "runtime-benchmarks"))] -type HostFunctions = cumulus_client_service::ParachainHostFunctions; - -#[cfg(feature = "runtime-benchmarks")] -type HostFunctions = ( - cumulus_client_service::ParachainHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, -); - -pub type ParachainExecutor = WasmExecutor; -pub type ParachainClient = TFullClient; - -/// The type of key used for [`BlockchainService`]` operations. -pub const BCSV_KEY_TYPE: KeyTypeId = KeyTypeId(*b"bcsv"); - -/// Proving either the exact key or the neighbour keys of the challenged key. -#[derive(Clone, Debug)] -pub enum Proven { - Empty, - ExactKey(Leaf), - NeighbourKeys((Option>, Option>)), -} - -impl Proven { - pub fn new_exact_key(key: K, data: D) -> Self { - Proven::ExactKey(Leaf { key, data }) - } - - pub fn new_neighbour_keys( - left: Option>, - right: Option>, - ) -> Result { - match (left, right) { - (None, None) => Err("Both left and right leaves cannot be None"), - (left, right) => Ok(Proven::NeighbourKeys((left, right))), - } - } -} - -/// Proof of file key(s) in the forest trie. -#[derive(Clone, Encode, Decode, Debug)] -pub struct ForestProof { - /// The file keys that were proven. - #[codec(skip)] - pub proven: Vec, ()>>, - /// The compact proof. - pub proof: CompactProof, - /// The root hash of the trie. - pub root: HasherOutT, -} - -#[derive(Clone, Encode, Decode)] -pub struct FileProof { - /// The compact proof. - pub proof: CompactProof, - /// The root hash of the trie, also known as the fingerprint of the file. - pub fingerprint: Fingerprint, -} - -impl FileProof { - pub fn to_file_key_proof(&self, file_metadata: FileMetadata) -> FileKeyProof { - FileKeyProof::new( - file_metadata.owner.clone(), - file_metadata.bucket_id.clone(), - file_metadata.location.clone(), - file_metadata.file_size, - file_metadata.fingerprint, - self.proof.clone(), - ) - } -} - -#[derive(Clone, Eq, Hash, PartialEq, Debug)] -pub struct DownloadRequestId(u64); - -impl DownloadRequestId { - pub fn new(id: u64) -> Self { - DownloadRequestId(id) - } - - pub fn next(&self) -> Self { - let next = self.0 + 1; - DownloadRequestId(next) - } -} - -/// Type alias for the events vector. -/// -/// The events vector is a storage element in the FRAME system pallet, which stores all the events -/// that have occurred in a block. This is syntactic sugar to make the code more readable. -pub type StorageHubEventsVec = Vec< - Box< - EventRecord< - ::RuntimeEvent, - ::Hash, - >, - >, ->; +use std::fmt::Debug; + +use codec::{Decode, Encode}; +use frame_system::EventRecord; +use sc_executor::WasmExecutor; +use sc_service::TFullClient; +pub use shp_constants::{FILE_CHUNK_SIZE, FILE_SIZE_TO_CHALLENGES, H_LENGTH}; +pub use shp_file_metadata::{Chunk, ChunkId, ChunkWithId, Leaf}; +use shp_traits::CommitmentVerifier; +use sp_core::Hasher; +use sp_runtime::{traits::Block as BlockT, KeyTypeId}; +use sp_std::collections::btree_map::BTreeMap; +use sp_trie::CompactProof; +use storage_hub_runtime::{apis::RuntimeApi, opaque::Block, Runtime}; +use trie_db::TrieLayout; + +/// The hash type of trie node keys +pub type HashT = ::Hash; +pub type HasherOutT = <::Hash as Hasher>::Out; + +/// Following types are shared between the client and the runtime. +/// They are defined as generic types in the runtime and made concrete using the runtime config +/// here to be used by the node/client. +pub type FileKeyVerifier = ::KeyVerifier; +pub type FileKeyProof = ::Proof; +pub type Hash = shp_file_metadata::Hash; +pub type Fingerprint = shp_file_metadata::Fingerprint; +pub type FileMetadata = + shp_file_metadata::FileMetadata; +pub type FileKey = shp_file_metadata::FileKey; +pub type BlockNumber = frame_system::pallet_prelude::BlockNumberFor; +pub type TickNumber = pallet_file_system::types::TickNumber; +pub type StorageData = pallet_file_system::types::StorageData; +pub type FileLocation = pallet_file_system::types::FileLocation; +pub type FileKeyResponsesInput = pallet_file_system::types::FileKeyResponsesInput; +pub type MspStorageRequestResponse = pallet_file_system::types::MspStorageRequestResponse; +pub type AcceptedStorageRequestParameters = + pallet_file_system::types::AcceptedStorageRequestParameters; +pub type RejectedStorageRequestReason = pallet_file_system::types::RejectedStorageRequestReason; +pub type PeerIds = pallet_file_system::types::PeerIds; +pub type BucketId = pallet_storage_providers::types::MerklePatriciaRoot; +pub type StorageProviderId = pallet_storage_providers::types::StorageProviderId; +pub type MainStorageProviderId = pallet_storage_providers::types::ProviderId; +pub type ProviderId = pallet_proofs_dealer::types::ProviderIdFor; +pub type RandomnessOutput = pallet_proofs_dealer::types::RandomnessOutputFor; +pub type ForestLeaf = pallet_proofs_dealer::types::KeyFor; +pub type ForestRoot = pallet_proofs_dealer::types::ForestRootFor; +pub type TrieMutation = shp_traits::TrieMutation; +pub type TrieRemoveMutation = shp_traits::TrieRemoveMutation; +pub type StorageProofsMerkleTrieLayout = storage_hub_runtime::StorageProofsMerkleTrieLayout; +pub type StorageProof = pallet_proofs_dealer::types::Proof; +pub type ForestVerifierProof = pallet_proofs_dealer::types::ForestVerifierProofFor; +pub type KeyProof = pallet_proofs_dealer::types::KeyProof; +pub type KeyProofs = BTreeMap; +pub type Balance = pallet_storage_providers::types::BalanceOf; +pub type OpaqueBlock = storage_hub_runtime::opaque::Block; +pub type BlockHash = ::Hash; + +#[cfg(not(feature = "runtime-benchmarks"))] +type HostFunctions = cumulus_client_service::ParachainHostFunctions; + +#[cfg(feature = "runtime-benchmarks")] +type HostFunctions = ( + cumulus_client_service::ParachainHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); + +pub type ParachainExecutor = WasmExecutor; +pub type ParachainClient = TFullClient; + +/// The type of key used for [`BlockchainService`]` operations. +pub const BCSV_KEY_TYPE: KeyTypeId = KeyTypeId(*b"bcsv"); + +/// Proving either the exact key or the neighbour keys of the challenged key. +#[derive(Clone, Debug)] +pub enum Proven { + Empty, + ExactKey(Leaf), + NeighbourKeys((Option>, Option>)), +} + +impl Proven { + pub fn new_exact_key(key: K, data: D) -> Self { + Proven::ExactKey(Leaf { key, data }) + } + + pub fn new_neighbour_keys( + left: Option>, + right: Option>, + ) -> Result { + match (left, right) { + (None, None) => Err("Both left and right leaves cannot be None"), + (left, right) => Ok(Proven::NeighbourKeys((left, right))), + } + } +} + +/// Proof of file key(s) in the forest trie. +#[derive(Clone, Encode, Decode, Debug)] +pub struct ForestProof { + /// The file keys that were proven. + #[codec(skip)] + pub proven: Vec, ()>>, + /// The compact proof. + pub proof: CompactProof, + /// The root hash of the trie. + pub root: HasherOutT, +} + +#[derive(Clone, Encode, Decode)] +pub struct FileProof { + /// The compact proof. + pub proof: CompactProof, + /// The root hash of the trie, also known as the fingerprint of the file. + pub fingerprint: Fingerprint, +} + +impl FileProof { + pub fn to_file_key_proof(&self, file_metadata: FileMetadata) -> FileKeyProof { + FileKeyProof::new( + file_metadata.owner.clone(), + file_metadata.bucket_id.clone(), + file_metadata.location.clone(), + file_metadata.file_size, + file_metadata.fingerprint, + self.proof.clone(), + ) + } +} + +#[derive(Clone, Eq, Hash, PartialEq, Debug)] +pub struct DownloadRequestId(u64); + +impl DownloadRequestId { + pub fn new(id: u64) -> Self { + DownloadRequestId(id) + } + + pub fn next(&self) -> Self { + let next = self.0 + 1; + DownloadRequestId(next) + } +} + +/// Type alias for the events vector. +/// +/// The events vector is a storage element in the FRAME system pallet, which stores all the events +/// that have occurred in a block. This is syntactic sugar to make the code more readable. +pub type StorageHubEventsVec = Vec< + Box< + EventRecord< + ::RuntimeEvent, + ::Hash, + >, + >, +>; diff --git a/client/file-transfer-service/Cargo.toml b/client/file-transfer-service/Cargo.toml index afb4a2859..2a3b57178 100644 --- a/client/file-transfer-service/Cargo.toml +++ b/client/file-transfer-service/Cargo.toml @@ -19,7 +19,6 @@ workspace = true prost = { workspace = true } async-channel = { workspace = true } tokio = { workspace = true } -libp2p-identity = { workspace = true, features = ["peerid"] } thiserror = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } @@ -31,6 +30,7 @@ codec = { workspace = true } sc-client-api = { workspace = true } sc-service = { workspace = true } sc-network = { workspace = true } +sc-network-types = { workspace = true } sc-utils = { workspace = true } sc-tracing = { workspace = true } diff --git a/client/file-transfer-service/src/handler.rs b/client/file-transfer-service/src/handler.rs index abb2913ca..23b7ef3ff 100644 --- a/client/file-transfer-service/src/handler.rs +++ b/client/file-transfer-service/src/handler.rs @@ -30,13 +30,13 @@ use std::{ use codec::{Decode, Encode}; use futures::prelude::*; use futures::stream::select; -use libp2p_identity::PeerId; use prost::Message; use sc_network::{ request_responses::{IncomingRequest, OutgoingResponse}, service::traits::NetworkService, IfDisconnected, NetworkPeers, NetworkRequest, ProtocolName, ReputationChange, }; +use sc_network_types::PeerId; use sc_tracing::tracing::{debug, error, info, warn}; use shc_actors_framework::actor::{Actor, ActorEventLoop}; use shc_common::types::{DownloadRequestId, FileKey, FileKeyProof}; diff --git a/client/indexer-service/src/handler.rs b/client/indexer-service/src/handler.rs index e49538e41..f9fc93079 100644 --- a/client/indexer-service/src/handler.rs +++ b/client/indexer-service/src/handler.rs @@ -1,458 +1,458 @@ -use diesel_async::AsyncConnection; -use futures::prelude::*; -use log::{error, info}; -use shc_common::types::StorageProviderId; -use std::sync::Arc; -use thiserror::Error; - -use sc_client_api::{BlockBackend, BlockchainEvents}; -use sp_core::H256; -use sp_runtime::traits::Header; - -use shc_actors_framework::actor::{Actor, ActorEventLoop}; -use shc_common::blockchain_utils::EventsRetrievalError; -use shc_common::{ - blockchain_utils::get_events_at_block, - types::{BlockNumber, ParachainClient}, -}; -use shc_indexer_db::{models::*, DbConnection, DbPool}; -use storage_hub_runtime::RuntimeEvent; - -pub(crate) const LOG_TARGET: &str = "indexer-service"; - -// Since the indexed data should be used directly from the database, -// we don't need to implement commands. -#[derive(Debug)] -pub enum IndexerServiceCommand {} - -// The IndexerService actor -pub struct IndexerService { - client: Arc, - db_pool: DbPool, -} - -// Implement the Actor trait for IndexerService -impl Actor for IndexerService { - type Message = IndexerServiceCommand; - type EventLoop = IndexerServiceEventLoop; - type EventBusProvider = (); // We're not using an event bus for now - - fn handle_message( - &mut self, - message: Self::Message, - ) -> impl std::future::Future + Send { - async move { - match message { - // No commands for now - } - } - } - - fn get_event_bus_provider(&self) -> &Self::EventBusProvider { - &() - } -} - -// Implement methods for IndexerService -impl IndexerService { - pub fn new(client: Arc, db_pool: DbPool) -> Self { - Self { client, db_pool } - } - - async fn handle_finality_notification( - &mut self, - notification: sc_client_api::FinalityNotification, - ) -> Result<(), HandleFinalityNotificationError> - where - Block: sp_runtime::traits::Block, - Block::Header: Header, - { - let finalized_block_hash = notification.hash; - let finalized_block_number = *notification.header.number(); - - info!(target: LOG_TARGET, "Finality notification (#{}): {}", finalized_block_number, finalized_block_hash); - - let mut db_conn = self.db_pool.get().await?; - - let service_state = ServiceState::get(&mut db_conn).await?; - - for block_number in - (service_state.last_processed_block as BlockNumber + 1)..=finalized_block_number - { - let block_hash = self - .client - .block_hash(block_number)? - .ok_or(HandleFinalityNotificationError::BlockHashNotFound)?; - self.index_block(&mut db_conn, block_number as BlockNumber, block_hash) - .await?; - } - - Ok(()) - } - - async fn index_block<'a, 'b: 'a>( - &'b self, - conn: &mut DbConnection<'a>, - block_number: BlockNumber, - block_hash: H256, - ) -> Result<(), IndexBlockError> { - info!(target: LOG_TARGET, "Indexing block #{}: {}", block_number, block_hash); - - let block_events = get_events_at_block(&self.client, &block_hash)?; - - conn.transaction::<(), IndexBlockError, _>(move |conn| { - Box::pin(async move { - ServiceState::update(conn, block_number as i64).await?; - - for ev in block_events { - self.index_event(conn, &ev.event).await?; - } - - Ok(()) - }) - }) - .await?; - - Ok(()) - } - - async fn index_event<'a, 'b: 'a>( - &'b self, - conn: &mut DbConnection<'a>, - event: &RuntimeEvent, - ) -> Result<(), diesel::result::Error> { - match event { - RuntimeEvent::BucketNfts(event) => self.index_bucket_nfts_event(conn, event).await?, - RuntimeEvent::FileSystem(event) => self.index_file_system_event(conn, event).await?, - RuntimeEvent::PaymentStreams(event) => { - self.index_payment_streams_event(conn, event).await? - } - RuntimeEvent::ProofsDealer(event) => { - self.index_proofs_dealer_event(conn, event).await? - } - RuntimeEvent::Providers(event) => self.index_providers_event(conn, event).await?, - RuntimeEvent::Randomness(event) => self.index_randomness_event(conn, event).await?, - // Runtime events that we're not interested in. - // We add them here instead of directly matching (_ => {}) - // to ensure the compiler will let us know to treat future events when added. - RuntimeEvent::System(_) => {} - RuntimeEvent::ParachainSystem(_) => {} - RuntimeEvent::Balances(_) => {} - RuntimeEvent::TransactionPayment(_) => {} - RuntimeEvent::Sudo(_) => {} - RuntimeEvent::CollatorSelection(_) => {} - RuntimeEvent::Session(_) => {} - RuntimeEvent::XcmpQueue(_) => {} - RuntimeEvent::PolkadotXcm(_) => {} - RuntimeEvent::CumulusXcm(_) => {} - RuntimeEvent::MessageQueue(_) => {} - RuntimeEvent::Nfts(_) => {} - RuntimeEvent::Parameters(_) => {} - } - - Ok(()) - } - - async fn index_bucket_nfts_event<'a, 'b: 'a>( - &'b self, - _conn: &mut DbConnection<'a>, - event: &pallet_bucket_nfts::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_bucket_nfts::Event::AccessShared { .. } => {} - pallet_bucket_nfts::Event::ItemReadAccessUpdated { .. } => {} - pallet_bucket_nfts::Event::ItemBurned { .. } => {} - pallet_bucket_nfts::Event::__Ignore(_, _) => {} - } - Ok(()) - } - - async fn index_file_system_event<'a, 'b: 'a>( - &'b self, - conn: &mut DbConnection<'a>, - event: &pallet_file_system::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_file_system::Event::NewBucket { - who, - msp_id, - bucket_id, - name, - collection_id, - private, - } => { - let msp = Msp::get_by_onchain_msp_id(conn, msp_id.to_string()).await?; - Bucket::create( - conn, - msp.id, - who.to_string(), - bucket_id.to_string(), - name.to_vec(), - collection_id.map(|id| id.to_string()), - *private, - ) - .await?; - } - pallet_file_system::Event::MoveBucketAccepted { msp_id, bucket_id } => { - let msp = Msp::get_by_onchain_msp_id(conn, msp_id.to_string()).await?; - Bucket::update_msp(conn, bucket_id.to_string(), msp.id).await?; - } - pallet_file_system::Event::BucketPrivacyUpdated { - who, - bucket_id, - collection_id, - private, - } => { - Bucket::update_privacy( - conn, - who.to_string(), - bucket_id.to_string(), - collection_id.map(|id| id.to_string()), - *private, - ) - .await?; - } - pallet_file_system::Event::BspConfirmStoppedStoring { .. } => {} - pallet_file_system::Event::BspConfirmedStoring { .. } => {} - pallet_file_system::Event::MspRespondedToStorageRequests { .. } => {} - pallet_file_system::Event::NewStorageRequest { .. } => {} - pallet_file_system::Event::MoveBucketRequested { .. } => {} - pallet_file_system::Event::NewCollectionAndAssociation { .. } => {} - pallet_file_system::Event::AcceptedBspVolunteer { .. } => {} - pallet_file_system::Event::StorageRequestFulfilled { .. } => {} - pallet_file_system::Event::StorageRequestExpired { .. } => {} - pallet_file_system::Event::StorageRequestRevoked { .. } => {} - pallet_file_system::Event::BspRequestedToStopStoring { .. } => {} - pallet_file_system::Event::PriorityChallengeForFileDeletionQueued { .. } => {} - pallet_file_system::Event::SpStopStoringInsolventUser { .. } => {} - pallet_file_system::Event::FailedToQueuePriorityChallenge { .. } => {} - pallet_file_system::Event::FileDeletionRequest { .. } => {} - pallet_file_system::Event::ProofSubmittedForPendingFileDeletionRequest { .. } => {} - pallet_file_system::Event::BspChallengeCycleInitialised { .. } => {} - pallet_file_system::Event::MoveBucketRequestExpired { .. } => {} - pallet_file_system::Event::MoveBucketRejected { .. } => {} - pallet_file_system::Event::DataServerRegisteredForMoveBucket { .. } => {} - pallet_file_system::Event::__Ignore(_, _) => {} - } - Ok(()) - } - - async fn index_payment_streams_event<'a, 'b: 'a>( - &'b self, - _conn: &mut DbConnection<'a>, - event: &pallet_payment_streams::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_payment_streams::Event::DynamicRatePaymentStreamCreated { .. } => {} - pallet_payment_streams::Event::DynamicRatePaymentStreamUpdated { .. } => {} - pallet_payment_streams::Event::DynamicRatePaymentStreamDeleted { .. } => {} - pallet_payment_streams::Event::FixedRatePaymentStreamCreated { .. } => {} - pallet_payment_streams::Event::FixedRatePaymentStreamUpdated { .. } => {} - pallet_payment_streams::Event::FixedRatePaymentStreamDeleted { .. } => {} - pallet_payment_streams::Event::PaymentStreamCharged { .. } => {} - pallet_payment_streams::Event::LastChargeableInfoUpdated { .. } => {} - pallet_payment_streams::Event::UserWithoutFunds { .. } => {} - pallet_payment_streams::Event::UserPaidDebts { .. } => {} - pallet_payment_streams::Event::UserSolvent { .. } => {} - pallet_payment_streams::Event::__Ignore(_, _) => {} - } - Ok(()) - } - - async fn index_proofs_dealer_event<'a, 'b: 'a>( - &'b self, - _conn: &mut DbConnection<'a>, - event: &pallet_proofs_dealer::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_proofs_dealer::Event::MutationsApplied { .. } => {} - pallet_proofs_dealer::Event::NewChallenge { .. } => {} - pallet_proofs_dealer::Event::ProofAccepted { .. } => {} - pallet_proofs_dealer::Event::NewChallengeSeed { .. } => {} - pallet_proofs_dealer::Event::NewCheckpointChallenge { .. } => {} - pallet_proofs_dealer::Event::SlashableProvider { .. } => {} - pallet_proofs_dealer::Event::NoRecordOfLastSubmittedProof { .. } => {} - pallet_proofs_dealer::Event::NewChallengeCycleInitialised { .. } => {} - pallet_proofs_dealer::Event::ChallengesTickerSet { .. } => {} - pallet_proofs_dealer::Event::__Ignore(_, _) => {} - } - Ok(()) - } - - async fn index_providers_event<'a, 'b: 'a>( - &'b self, - conn: &mut DbConnection<'a>, - event: &pallet_storage_providers::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_storage_providers::Event::BspRequestSignUpSuccess { .. } => {} - pallet_storage_providers::Event::BspSignUpSuccess { - who, - bsp_id, - multiaddresses, - capacity, - } => { - let mut sql_multiaddresses = Vec::new(); - for multiaddress in multiaddresses { - let multiaddress_str = - String::from_utf8(multiaddress.to_vec()).expect("Invalid multiaddress"); - sql_multiaddresses.push(MultiAddress::create(conn, multiaddress_str).await?); - } - - Bsp::create( - conn, - who.to_string(), - capacity.into(), - sql_multiaddresses, - bsp_id.to_string(), - ) - .await?; - } - pallet_storage_providers::Event::BspSignOffSuccess { - who, - bsp_id: _bsp_id, - } => { - Bsp::delete(conn, who.to_string()).await?; - } - pallet_storage_providers::Event::CapacityChanged { - who, - new_capacity, - provider_id, - old_capacity: _old_capacity, - next_block_when_change_allowed: _next_block_when_change_allowed, - } => match provider_id { - StorageProviderId::BackupStorageProvider(_) => { - Bsp::update_capacity(conn, who.to_string(), new_capacity.into()).await?; - } - StorageProviderId::MainStorageProvider(_) => { - Bsp::update_capacity(conn, who.to_string(), new_capacity.into()).await?; - } - }, - pallet_storage_providers::Event::SignUpRequestCanceled { .. } => {} - pallet_storage_providers::Event::MspRequestSignUpSuccess { .. } => {} - pallet_storage_providers::Event::MspSignUpSuccess { - who, - msp_id, - multiaddresses, - capacity, - value_prop, - } => { - let mut sql_multiaddresses = Vec::new(); - for multiaddress in multiaddresses { - let multiaddress_str = - String::from_utf8(multiaddress.to_vec()).expect("Invalid multiaddress"); - sql_multiaddresses.push(MultiAddress::create(conn, multiaddress_str).await?); - } - - // TODO: update value prop after properly defined in runtime - let value_prop = format!("{value_prop:?}"); - - Msp::create( - conn, - who.to_string(), - capacity.into(), - value_prop, - sql_multiaddresses, - msp_id.to_string(), - ) - .await?; - } - pallet_storage_providers::Event::MspSignOffSuccess { - who, - msp_id: _msp_id, - } => { - Msp::delete(conn, who.to_string()).await?; - } - pallet_storage_providers::Event::Slashed { .. } => {} - pallet_storage_providers::Event::__Ignore(_, _) => {} - } - Ok(()) - } - - async fn index_randomness_event<'a, 'b: 'a>( - &'b self, - _conn: &mut DbConnection<'a>, - event: &pallet_randomness::Event, - ) -> Result<(), diesel::result::Error> { - match event { - pallet_randomness::Event::NewOneEpochAgoRandomnessAvailable { .. } => {} - pallet_randomness::Event::__Ignore(_, _) => {} - } - Ok(()) - } -} - -// Define the EventLoop for IndexerService -pub struct IndexerServiceEventLoop { - receiver: sc_utils::mpsc::TracingUnboundedReceiver, - actor: IndexerService, -} - -enum MergedEventLoopMessage -where - Block: sp_runtime::traits::Block, -{ - Command(IndexerServiceCommand), - FinalityNotification(sc_client_api::FinalityNotification), -} - -// Implement ActorEventLoop for IndexerServiceEventLoop -impl ActorEventLoop for IndexerServiceEventLoop { - fn new( - actor: IndexerService, - receiver: sc_utils::mpsc::TracingUnboundedReceiver, - ) -> Self { - Self { actor, receiver } - } - - async fn run(mut self) { - info!(target: LOG_TARGET, "IndexerService starting up!"); - - let finality_notification_stream = self.actor.client.finality_notification_stream(); - - let mut merged_stream = stream::select( - self.receiver.map(MergedEventLoopMessage::Command), - finality_notification_stream.map(MergedEventLoopMessage::FinalityNotification), - ); - - while let Some(message) = merged_stream.next().await { - match message { - MergedEventLoopMessage::Command(command) => { - self.actor.handle_message(command).await; - } - MergedEventLoopMessage::FinalityNotification(notification) => { - self.actor - .handle_finality_notification(notification) - .await - .unwrap_or_else(|e| { - error!(target: LOG_TARGET, "Failed to handle finality notification: {}", e); - }); - } - } - } - - info!(target: LOG_TARGET, "IndexerService shutting down."); - } -} - -#[derive(Error, Debug)] -pub enum IndexBlockError { - #[error("Database error: {0}")] - DatabaseError(#[from] diesel::result::Error), - #[error("Failed to retrieve or decode events: {0}")] - EventsRetrievalError(#[from] EventsRetrievalError), -} - -#[derive(Error, Debug)] -pub enum HandleFinalityNotificationError { - #[error("Database error: {0}")] - DatabaseError(#[from] diesel::result::Error), - #[error("Block hash not found")] - BlockHashNotFound, - #[error("Index block error: {0}")] - IndexBlockError(#[from] IndexBlockError), - #[error("Client error: {0}")] - ClientError(#[from] sp_blockchain::Error), - #[error("Pool run error: {0}")] - PoolRunError(#[from] diesel_async::pooled_connection::bb8::RunError), -} +use diesel_async::AsyncConnection; +use futures::prelude::*; +use log::{error, info}; +use shc_common::types::StorageProviderId; +use std::sync::Arc; +use thiserror::Error; + +use sc_client_api::{BlockBackend, BlockchainEvents}; +use sp_core::H256; +use sp_runtime::traits::Header; + +use shc_actors_framework::actor::{Actor, ActorEventLoop}; +use shc_common::blockchain_utils::EventsRetrievalError; +use shc_common::{ + blockchain_utils::get_events_at_block, + types::{BlockNumber, ParachainClient}, +}; +use shc_indexer_db::{models::*, DbConnection, DbPool}; +use storage_hub_runtime::RuntimeEvent; + +pub(crate) const LOG_TARGET: &str = "indexer-service"; + +// Since the indexed data should be used directly from the database, +// we don't need to implement commands. +#[derive(Debug)] +pub enum IndexerServiceCommand {} + +// The IndexerService actor +pub struct IndexerService { + client: Arc, + db_pool: DbPool, +} + +// Implement the Actor trait for IndexerService +impl Actor for IndexerService { + type Message = IndexerServiceCommand; + type EventLoop = IndexerServiceEventLoop; + type EventBusProvider = (); // We're not using an event bus for now + + fn handle_message( + &mut self, + message: Self::Message, + ) -> impl std::future::Future + Send { + async move { + match message { + // No commands for now + } + } + } + + fn get_event_bus_provider(&self) -> &Self::EventBusProvider { + &() + } +} + +// Implement methods for IndexerService +impl IndexerService { + pub fn new(client: Arc, db_pool: DbPool) -> Self { + Self { client, db_pool } + } + + async fn handle_finality_notification( + &mut self, + notification: sc_client_api::FinalityNotification, + ) -> Result<(), HandleFinalityNotificationError> + where + Block: sp_runtime::traits::Block, + Block::Header: Header, + { + let finalized_block_hash = notification.hash; + let finalized_block_number = *notification.header.number(); + + info!(target: LOG_TARGET, "Finality notification (#{}): {}", finalized_block_number, finalized_block_hash); + + let mut db_conn = self.db_pool.get().await?; + + let service_state = ServiceState::get(&mut db_conn).await?; + + for block_number in + (service_state.last_processed_block as BlockNumber + 1)..=finalized_block_number + { + let block_hash = self + .client + .block_hash(block_number)? + .ok_or(HandleFinalityNotificationError::BlockHashNotFound)?; + self.index_block(&mut db_conn, block_number as BlockNumber, block_hash) + .await?; + } + + Ok(()) + } + + async fn index_block<'a, 'b: 'a>( + &'b self, + conn: &mut DbConnection<'a>, + block_number: BlockNumber, + block_hash: H256, + ) -> Result<(), IndexBlockError> { + info!(target: LOG_TARGET, "Indexing block #{}: {}", block_number, block_hash); + + let block_events = get_events_at_block(&self.client, &block_hash)?; + + conn.transaction::<(), IndexBlockError, _>(move |conn| { + Box::pin(async move { + ServiceState::update(conn, block_number as i64).await?; + + for ev in block_events { + self.index_event(conn, &ev.event).await?; + } + + Ok(()) + }) + }) + .await?; + + Ok(()) + } + + async fn index_event<'a, 'b: 'a>( + &'b self, + conn: &mut DbConnection<'a>, + event: &RuntimeEvent, + ) -> Result<(), diesel::result::Error> { + match event { + RuntimeEvent::BucketNfts(event) => self.index_bucket_nfts_event(conn, event).await?, + RuntimeEvent::FileSystem(event) => self.index_file_system_event(conn, event).await?, + RuntimeEvent::PaymentStreams(event) => { + self.index_payment_streams_event(conn, event).await? + } + RuntimeEvent::ProofsDealer(event) => { + self.index_proofs_dealer_event(conn, event).await? + } + RuntimeEvent::Providers(event) => self.index_providers_event(conn, event).await?, + RuntimeEvent::Randomness(event) => self.index_randomness_event(conn, event).await?, + // Runtime events that we're not interested in. + // We add them here instead of directly matching (_ => {}) + // to ensure the compiler will let us know to treat future events when added. + RuntimeEvent::System(_) => {} + RuntimeEvent::ParachainSystem(_) => {} + RuntimeEvent::Balances(_) => {} + RuntimeEvent::TransactionPayment(_) => {} + RuntimeEvent::Sudo(_) => {} + RuntimeEvent::CollatorSelection(_) => {} + RuntimeEvent::Session(_) => {} + RuntimeEvent::XcmpQueue(_) => {} + RuntimeEvent::PolkadotXcm(_) => {} + RuntimeEvent::CumulusXcm(_) => {} + RuntimeEvent::MessageQueue(_) => {} + RuntimeEvent::Nfts(_) => {} + RuntimeEvent::Parameters(_) => {} + } + + Ok(()) + } + + async fn index_bucket_nfts_event<'a, 'b: 'a>( + &'b self, + _conn: &mut DbConnection<'a>, + event: &pallet_bucket_nfts::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_bucket_nfts::Event::AccessShared { .. } => {} + pallet_bucket_nfts::Event::ItemReadAccessUpdated { .. } => {} + pallet_bucket_nfts::Event::ItemBurned { .. } => {} + pallet_bucket_nfts::Event::__Ignore(_, _) => {} + } + Ok(()) + } + + async fn index_file_system_event<'a, 'b: 'a>( + &'b self, + conn: &mut DbConnection<'a>, + event: &pallet_file_system::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_file_system::Event::NewBucket { + who, + msp_id, + bucket_id, + name, + collection_id, + private, + } => { + let msp = Msp::get_by_onchain_msp_id(conn, msp_id.to_string()).await?; + Bucket::create( + conn, + msp.id, + who.to_string(), + bucket_id.to_string(), + name.to_vec(), + collection_id.map(|id| id.to_string()), + *private, + ) + .await?; + } + pallet_file_system::Event::MoveBucketAccepted { msp_id, bucket_id } => { + let msp = Msp::get_by_onchain_msp_id(conn, msp_id.to_string()).await?; + Bucket::update_msp(conn, bucket_id.to_string(), msp.id).await?; + } + pallet_file_system::Event::BucketPrivacyUpdated { + who, + bucket_id, + collection_id, + private, + } => { + Bucket::update_privacy( + conn, + who.to_string(), + bucket_id.to_string(), + collection_id.map(|id| id.to_string()), + *private, + ) + .await?; + } + pallet_file_system::Event::BspConfirmStoppedStoring { .. } => {} + pallet_file_system::Event::BspConfirmedStoring { .. } => {} + pallet_file_system::Event::MspRespondedToStorageRequests { .. } => {} + pallet_file_system::Event::NewStorageRequest { .. } => {} + pallet_file_system::Event::MoveBucketRequested { .. } => {} + pallet_file_system::Event::NewCollectionAndAssociation { .. } => {} + pallet_file_system::Event::AcceptedBspVolunteer { .. } => {} + pallet_file_system::Event::StorageRequestFulfilled { .. } => {} + pallet_file_system::Event::StorageRequestExpired { .. } => {} + pallet_file_system::Event::StorageRequestRevoked { .. } => {} + pallet_file_system::Event::BspRequestedToStopStoring { .. } => {} + pallet_file_system::Event::PriorityChallengeForFileDeletionQueued { .. } => {} + pallet_file_system::Event::SpStopStoringInsolventUser { .. } => {} + pallet_file_system::Event::FailedToQueuePriorityChallenge { .. } => {} + pallet_file_system::Event::FileDeletionRequest { .. } => {} + pallet_file_system::Event::ProofSubmittedForPendingFileDeletionRequest { .. } => {} + pallet_file_system::Event::BspChallengeCycleInitialised { .. } => {} + pallet_file_system::Event::MoveBucketRequestExpired { .. } => {} + pallet_file_system::Event::MoveBucketRejected { .. } => {} + pallet_file_system::Event::DataServerRegisteredForMoveBucket { .. } => {} + pallet_file_system::Event::__Ignore(_, _) => {} + } + Ok(()) + } + + async fn index_payment_streams_event<'a, 'b: 'a>( + &'b self, + _conn: &mut DbConnection<'a>, + event: &pallet_payment_streams::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_payment_streams::Event::DynamicRatePaymentStreamCreated { .. } => {} + pallet_payment_streams::Event::DynamicRatePaymentStreamUpdated { .. } => {} + pallet_payment_streams::Event::DynamicRatePaymentStreamDeleted { .. } => {} + pallet_payment_streams::Event::FixedRatePaymentStreamCreated { .. } => {} + pallet_payment_streams::Event::FixedRatePaymentStreamUpdated { .. } => {} + pallet_payment_streams::Event::FixedRatePaymentStreamDeleted { .. } => {} + pallet_payment_streams::Event::PaymentStreamCharged { .. } => {} + pallet_payment_streams::Event::LastChargeableInfoUpdated { .. } => {} + pallet_payment_streams::Event::UserWithoutFunds { .. } => {} + pallet_payment_streams::Event::UserPaidDebts { .. } => {} + pallet_payment_streams::Event::UserSolvent { .. } => {} + pallet_payment_streams::Event::__Ignore(_, _) => {} + } + Ok(()) + } + + async fn index_proofs_dealer_event<'a, 'b: 'a>( + &'b self, + _conn: &mut DbConnection<'a>, + event: &pallet_proofs_dealer::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_proofs_dealer::Event::MutationsApplied { .. } => {} + pallet_proofs_dealer::Event::NewChallenge { .. } => {} + pallet_proofs_dealer::Event::ProofAccepted { .. } => {} + pallet_proofs_dealer::Event::NewChallengeSeed { .. } => {} + pallet_proofs_dealer::Event::NewCheckpointChallenge { .. } => {} + pallet_proofs_dealer::Event::SlashableProvider { .. } => {} + pallet_proofs_dealer::Event::NoRecordOfLastSubmittedProof { .. } => {} + pallet_proofs_dealer::Event::NewChallengeCycleInitialised { .. } => {} + pallet_proofs_dealer::Event::ChallengesTickerSet { .. } => {} + pallet_proofs_dealer::Event::__Ignore(_, _) => {} + } + Ok(()) + } + + async fn index_providers_event<'a, 'b: 'a>( + &'b self, + conn: &mut DbConnection<'a>, + event: &pallet_storage_providers::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_storage_providers::Event::BspRequestSignUpSuccess { .. } => {} + pallet_storage_providers::Event::BspSignUpSuccess { + who, + bsp_id, + multiaddresses, + capacity, + } => { + let mut sql_multiaddresses = Vec::new(); + for multiaddress in multiaddresses { + let multiaddress_str = + String::from_utf8(multiaddress.to_vec()).expect("Invalid multiaddress"); + sql_multiaddresses.push(MultiAddress::create(conn, multiaddress_str).await?); + } + + Bsp::create( + conn, + who.to_string(), + capacity.into(), + sql_multiaddresses, + bsp_id.to_string(), + ) + .await?; + } + pallet_storage_providers::Event::BspSignOffSuccess { + who, + bsp_id: _bsp_id, + } => { + Bsp::delete(conn, who.to_string()).await?; + } + pallet_storage_providers::Event::CapacityChanged { + who, + new_capacity, + provider_id, + old_capacity: _old_capacity, + next_block_when_change_allowed: _next_block_when_change_allowed, + } => match provider_id { + StorageProviderId::BackupStorageProvider(_) => { + Bsp::update_capacity(conn, who.to_string(), new_capacity.into()).await?; + } + StorageProviderId::MainStorageProvider(_) => { + Bsp::update_capacity(conn, who.to_string(), new_capacity.into()).await?; + } + }, + pallet_storage_providers::Event::SignUpRequestCanceled { .. } => {} + pallet_storage_providers::Event::MspRequestSignUpSuccess { .. } => {} + pallet_storage_providers::Event::MspSignUpSuccess { + who, + msp_id, + multiaddresses, + capacity, + value_prop, + } => { + let mut sql_multiaddresses = Vec::new(); + for multiaddress in multiaddresses { + let multiaddress_str = + String::from_utf8(multiaddress.to_vec()).expect("Invalid multiaddress"); + sql_multiaddresses.push(MultiAddress::create(conn, multiaddress_str).await?); + } + + // TODO: update value prop after properly defined in runtime + let value_prop = format!("{value_prop:?}"); + + Msp::create( + conn, + who.to_string(), + capacity.into(), + value_prop, + sql_multiaddresses, + msp_id.to_string(), + ) + .await?; + } + pallet_storage_providers::Event::MspSignOffSuccess { + who, + msp_id: _msp_id, + } => { + Msp::delete(conn, who.to_string()).await?; + } + pallet_storage_providers::Event::Slashed { .. } => {} + pallet_storage_providers::Event::__Ignore(_, _) => {} + } + Ok(()) + } + + async fn index_randomness_event<'a, 'b: 'a>( + &'b self, + _conn: &mut DbConnection<'a>, + event: &pallet_randomness::Event, + ) -> Result<(), diesel::result::Error> { + match event { + pallet_randomness::Event::NewOneEpochAgoRandomnessAvailable { .. } => {} + pallet_randomness::Event::__Ignore(_, _) => {} + } + Ok(()) + } +} + +// Define the EventLoop for IndexerService +pub struct IndexerServiceEventLoop { + receiver: sc_utils::mpsc::TracingUnboundedReceiver, + actor: IndexerService, +} + +enum MergedEventLoopMessage +where + Block: sp_runtime::traits::Block, +{ + Command(IndexerServiceCommand), + FinalityNotification(sc_client_api::FinalityNotification), +} + +// Implement ActorEventLoop for IndexerServiceEventLoop +impl ActorEventLoop for IndexerServiceEventLoop { + fn new( + actor: IndexerService, + receiver: sc_utils::mpsc::TracingUnboundedReceiver, + ) -> Self { + Self { actor, receiver } + } + + async fn run(mut self) { + info!(target: LOG_TARGET, "IndexerService starting up!"); + + let finality_notification_stream = self.actor.client.finality_notification_stream(); + + let mut merged_stream = stream::select( + self.receiver.map(MergedEventLoopMessage::Command), + finality_notification_stream.map(MergedEventLoopMessage::FinalityNotification), + ); + + while let Some(message) = merged_stream.next().await { + match message { + MergedEventLoopMessage::Command(command) => { + self.actor.handle_message(command).await; + } + MergedEventLoopMessage::FinalityNotification(notification) => { + self.actor + .handle_finality_notification(notification) + .await + .unwrap_or_else(|e| { + error!(target: LOG_TARGET, "Failed to handle finality notification: {}", e); + }); + } + } + } + + info!(target: LOG_TARGET, "IndexerService shutting down."); + } +} + +#[derive(Error, Debug)] +pub enum IndexBlockError { + #[error("Database error: {0}")] + DatabaseError(#[from] diesel::result::Error), + #[error("Failed to retrieve or decode events: {0}")] + EventsRetrievalError(#[from] EventsRetrievalError), +} + +#[derive(Error, Debug)] +pub enum HandleFinalityNotificationError { + #[error("Database error: {0}")] + DatabaseError(#[from] diesel::result::Error), + #[error("Block hash not found")] + BlockHashNotFound, + #[error("Index block error: {0}")] + IndexBlockError(#[from] IndexBlockError), + #[error("Client error: {0}")] + ClientError(#[from] sp_blockchain::Error), + #[error("Pool run error: {0}")] + PoolRunError(#[from] diesel_async::pooled_connection::bb8::RunError), +} diff --git a/docker/local-dev-bsp-compose.yml b/docker/local-dev-bsp-compose.yml index 7dfcd7b47..ccb89340c 100644 --- a/docker/local-dev-bsp-compose.yml +++ b/docker/local-dev-bsp-compose.yml @@ -1,55 +1,55 @@ -services: - sh-bsp: - image: storage-hub:local - container_name: docker-sh-bsp-1 - platform: linux/amd64 - ports: - - "9666:9944" - - "30350:30350" - volumes: - - ./dev-keystores/bsp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=bsp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--name=sh-bsp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-user: - image: storage-hub:local - platform: linux/amd64 - container_name: docker-sh-user-1 - ports: - - "9888:9944" - - "30444:30444" - volumes: - - ./dev-keystores/user:/keystore:rw - - ./resource:/res:ro - command: - [ - "--dev", - "--name=sh-user", - "--provider", - "--provider-type=user", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30444", - "--rpc-cors=all", - "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] +services: + sh-bsp: + image: storage-hub:local + container_name: docker-sh-bsp-1 + platform: linux/amd64 + ports: + - "9666:9944" + - "30350:30350" + volumes: + - ./dev-keystores/bsp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=bsp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--name=sh-bsp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-user: + image: storage-hub:local + platform: linux/amd64 + container_name: docker-sh-user-1 + ports: + - "9888:9944" + - "30444:30444" + volumes: + - ./dev-keystores/user:/keystore:rw + - ./resource:/res:ro + command: + [ + "--dev", + "--name=sh-user", + "--provider", + "--provider-type=user", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30444", + "--rpc-cors=all", + "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] diff --git a/docker/local-dev-bsp-rocksdb-compose.yml b/docker/local-dev-bsp-rocksdb-compose.yml index fd186cc72..630e6a57f 100644 --- a/docker/local-dev-bsp-rocksdb-compose.yml +++ b/docker/local-dev-bsp-rocksdb-compose.yml @@ -1,59 +1,59 @@ -services: - sh-bsp: - image: storage-hub:local - container_name: docker-sh-bsp-1 - platform: linux/amd64 - ports: - - "9666:9944" - - "30350:30350" - volumes: - - ./dev-keystores/bsp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=bsp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--storage-layer=rocks-db", - "--storage-path=/tmp/bsp/${BSP_IP:-default_bsp_ip}", - "--name=sh-bsp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-user: - image: storage-hub:local - platform: linux/amd64 - container_name: docker-sh-user-1 - ports: - - "9888:9944" - - "30444:30444" - volumes: - - ./dev-keystores/user:/keystore:rw - - ./resource:/res:ro - command: - [ - "--dev", - "--name=sh-user", - "--provider", - "--provider-type=user", - "--storage-layer=rocks-db", - "--storage-path=/tmp/user/${BSP_IP:-default_bsp_ip}", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30444", - "--rpc-cors=all", - "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] +services: + sh-bsp: + image: storage-hub:local + container_name: docker-sh-bsp-1 + platform: linux/amd64 + ports: + - "9666:9944" + - "30350:30350" + volumes: + - ./dev-keystores/bsp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=bsp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--storage-layer=rocks-db", + "--storage-path=/tmp/bsp/${BSP_IP:-default_bsp_ip}", + "--name=sh-bsp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-user: + image: storage-hub:local + platform: linux/amd64 + container_name: docker-sh-user-1 + ports: + - "9888:9944" + - "30444:30444" + volumes: + - ./dev-keystores/user:/keystore:rw + - ./resource:/res:ro + command: + [ + "--dev", + "--name=sh-user", + "--provider", + "--provider-type=user", + "--storage-layer=rocks-db", + "--storage-path=/tmp/user/${BSP_IP:-default_bsp_ip}", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30444", + "--rpc-cors=all", + "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] diff --git a/docker/local-dev-full-compose.yml b/docker/local-dev-full-compose.yml index fe32be6b6..bbb283f77 100644 --- a/docker/local-dev-full-compose.yml +++ b/docker/local-dev-full-compose.yml @@ -1,83 +1,83 @@ -services: - sh-bsp: - image: storage-hub:local - container_name: docker-sh-bsp-1 - platform: linux/amd64 - ports: - - "9666:9944" - - "30350:30350" - volumes: - - ./dev-keystores/bsp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=bsp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--name=sh-bsp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-msp: - image: storage-hub:local - container_name: docker-sh-msp-1 - platform: linux/amd64 - ports: - - "9777:9944" - - "30555:30350" - volumes: - - ./dev-keystores/msp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=msp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--name=sh-msp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=${NODE_KEY}", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-user: - image: storage-hub:local - platform: linux/amd64 - container_name: docker-sh-user-1 - ports: - - "9888:9944" - - "30444:30444" - volumes: - - ./dev-keystores/user:/keystore:rw - - ./resource:/res:ro - command: - [ - "--dev", - "--name=sh-user", - "--provider", - "--provider-type=user", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30444", - "--rpc-cors=all", - "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] +services: + sh-bsp: + image: storage-hub:local + container_name: docker-sh-bsp-1 + platform: linux/amd64 + ports: + - "9666:9944" + - "30350:30350" + volumes: + - ./dev-keystores/bsp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=bsp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--name=sh-bsp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-msp: + image: storage-hub:local + container_name: docker-sh-msp-1 + platform: linux/amd64 + ports: + - "9777:9944" + - "30555:30350" + volumes: + - ./dev-keystores/msp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=msp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--name=sh-msp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=${NODE_KEY}", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-user: + image: storage-hub:local + platform: linux/amd64 + container_name: docker-sh-user-1 + ports: + - "9888:9944" + - "30444:30444" + volumes: + - ./dev-keystores/user:/keystore:rw + - ./resource:/res:ro + command: + [ + "--dev", + "--name=sh-user", + "--provider", + "--provider-type=user", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30444", + "--rpc-cors=all", + "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] diff --git a/docker/local-dev-full-rocksdb-compose.yml b/docker/local-dev-full-rocksdb-compose.yml index da561e2f8..d73a5b919 100644 --- a/docker/local-dev-full-rocksdb-compose.yml +++ b/docker/local-dev-full-rocksdb-compose.yml @@ -1,89 +1,89 @@ -services: - sh-bsp: - image: storage-hub:local - container_name: docker-sh-bsp-1 - platform: linux/amd64 - ports: - - "9666:9944" - - "30350:30350" - volumes: - - ./dev-keystores/bsp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=bsp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--storage-layer=rocks-db", - "--storage-path=/tmp/bsp/${BSP_IP:-default_bsp_ip}", - "--name=sh-bsp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-msp: - image: storage-hub:local - container_name: docker-sh-msp-1 - platform: linux/amd64 - ports: - - "9777:9944" - - "30555:30350" - volumes: - - ./dev-keystores/msp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=msp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--storage-layer=rocks-db", - "--storage-path=/tmp/msp/${BSP_IP:-default_bsp_ip}", - "--name=sh-msp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=${NODE_KEY}", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - sh-user: - image: storage-hub:local - platform: linux/amd64 - container_name: docker-sh-user-1 - ports: - - "9888:9944" - - "30444:30444" - volumes: - - ./dev-keystores/user:/keystore:rw - - ./resource:/res:ro - command: - [ - "--dev", - "--name=sh-user", - "--provider", - "--provider-type=user", - "--storage-layer=rocks-db", - "--storage-path=/tmp/user/${BSP_IP:-default_bsp_ip}", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30444", - "--rpc-cors=all", - "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] +services: + sh-bsp: + image: storage-hub:local + container_name: docker-sh-bsp-1 + platform: linux/amd64 + ports: + - "9666:9944" + - "30350:30350" + volumes: + - ./dev-keystores/bsp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=bsp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--storage-layer=rocks-db", + "--storage-path=/tmp/bsp/${BSP_IP:-default_bsp_ip}", + "--name=sh-bsp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-msp: + image: storage-hub:local + container_name: docker-sh-msp-1 + platform: linux/amd64 + ports: + - "9777:9944" + - "30555:30350" + volumes: + - ./dev-keystores/msp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=msp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--storage-layer=rocks-db", + "--storage-path=/tmp/msp/${BSP_IP:-default_bsp_ip}", + "--name=sh-msp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=${NODE_KEY}", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + sh-user: + image: storage-hub:local + platform: linux/amd64 + container_name: docker-sh-user-1 + ports: + - "9888:9944" + - "30444:30444" + volumes: + - ./dev-keystores/user:/keystore:rw + - ./resource:/res:ro + command: + [ + "--dev", + "--name=sh-user", + "--provider", + "--provider-type=user", + "--storage-layer=rocks-db", + "--storage-path=/tmp/user/${BSP_IP:-default_bsp_ip}", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30444", + "--rpc-cors=all", + "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] diff --git a/docker/noisy-bsp-compose.yml b/docker/noisy-bsp-compose.yml index be32c9354..919919902 100644 --- a/docker/noisy-bsp-compose.yml +++ b/docker/noisy-bsp-compose.yml @@ -1,80 +1,80 @@ -services: - sh-bsp: - image: storage-hub:local - container_name: docker-sh-bsp-1 - platform: linux/amd64 - ports: - - "9666:9944" - volumes: - - ./dev-keystores/bsp:/keystore:rw - command: - [ - "--dev", - "--provider", - "--provider-type=bsp", - "--max-storage-capacity=4294967295", - "--jump-capacity=1073741824", - "--name=sh-bsp", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30350", - "--rpc-cors=all", - "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - networks: - storage-hub-network: - aliases: - - sh-bsp - - sh-user: - image: storage-hub:local - platform: linux/amd64 - container_name: docker-sh-user-1 - ports: - - "9888:9944" - volumes: - - ./dev-keystores/user:/keystore:rw - - ./resource:/res:ro - command: - [ - "--dev", - "--name=sh-user", - "--provider", - "--provider-type=user", - "--no-hardware-benchmarks", - "--unsafe-rpc-external", - "--rpc-methods=unsafe", - "--port=30444", - "--rpc-cors=all", - "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", - "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", - "--keystore-path=/keystore", - "--sealing=manual", - "--base-path=/data", - ] - networks: - - storage-hub-network - depends_on: - - toxiproxy - - toxiproxy: - image: shopify/toxiproxy - container_name: toxiproxy - ports: - - "8474:8474" - - "30350:30350" - volumes: - - ./toxiproxy.json:/etc/toxiproxy.json - command: -config /etc/toxiproxy.json -host=0.0.0.0 - networks: - storage-hub-network: - aliases: - - toxiproxy - -networks: - storage-hub-network: +services: + sh-bsp: + image: storage-hub:local + container_name: docker-sh-bsp-1 + platform: linux/amd64 + ports: + - "9666:9944" + volumes: + - ./dev-keystores/bsp:/keystore:rw + command: + [ + "--dev", + "--provider", + "--provider-type=bsp", + "--max-storage-capacity=4294967295", + "--jump-capacity=1073741824", + "--name=sh-bsp", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30350", + "--rpc-cors=all", + "--node-key=0x2e6e3670c96202a2d6f5a58b7ac9092c5a51e0250f324eec2111ca94f5e568be", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + networks: + storage-hub-network: + aliases: + - sh-bsp + + sh-user: + image: storage-hub:local + platform: linux/amd64 + container_name: docker-sh-user-1 + ports: + - "9888:9944" + volumes: + - ./dev-keystores/user:/keystore:rw + - ./resource:/res:ro + command: + [ + "--dev", + "--name=sh-user", + "--provider", + "--provider-type=user", + "--no-hardware-benchmarks", + "--unsafe-rpc-external", + "--rpc-methods=unsafe", + "--port=30444", + "--rpc-cors=all", + "--node-key=0x13b3b1c917dda506f152816aad4685eefa54fe57792165b31141ac893610b314", + "--bootnodes=/ip4/${BSP_IP:-default_bsp_ip}/tcp/30350/p2p/${BSP_PEER_ID:-default_bsp_peer_id}", + "--keystore-path=/keystore", + "--sealing=manual", + "--base-path=/data", + ] + networks: + - storage-hub-network + depends_on: + - toxiproxy + + toxiproxy: + image: shopify/toxiproxy + container_name: toxiproxy + ports: + - "8474:8474" + - "30350:30350" + volumes: + - ./toxiproxy.json:/etc/toxiproxy.json + command: -config /etc/toxiproxy.json -host=0.0.0.0 + networks: + storage-hub-network: + aliases: + - toxiproxy + +networks: + storage-hub-network: driver: bridge \ No newline at end of file diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 69b748a82..c2558f5d9 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -8,7 +8,7 @@ use sp_runtime::traits::{IdentifyAccount, Verify}; use storage_hub_runtime as runtime; /// Specialized `ChainSpec` for the normal parachain runtime. -pub type ChainSpec = sc_service::GenericChainSpec<(), Extensions>; +pub type ChainSpec = sc_service::GenericChainSpec; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; diff --git a/node/src/service.rs b/node/src/service.rs index b4b87b94b..5a17b5d8a 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -12,7 +12,7 @@ use codec::Encode; use cumulus_client_cli::CollatorOptions; use cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}; -use polkadot_primitives::{BlakeTwo256, HashT, HeadData, ValidationCode}; +use polkadot_primitives::{BlakeTwo256, HashT, HeadData}; use sc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider; use shc_actors_framework::actor::TaskSpawner; use shc_common::types::{BlockHash, OpaqueBlock, BCSV_KEY_TYPE}; @@ -34,7 +34,7 @@ use cumulus_client_service::{ BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, }; use cumulus_primitives_core::{ - relay_chain::{well_known_keys as RelayChainWellKnownKeys, CollatorPair}, + relay_chain::{well_known_keys as RelayChainWellKnownKeys, CollatorPair, ValidationCode}, ParaId, }; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; @@ -592,21 +592,22 @@ where ))), create_inherent_data_providers: move |block: Hash, ()| { let current_para_block = client_for_cidp - .number(block) - .expect("Header lookup should succeed") - .expect("Header passed in as parent should be present in backend."); + .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_header = client + .expect_header(hash) + .expect("Expected parachain header should exist") + .encode(); - let para_head_data = HeadData(para_header).encode(); + let raw_para_head_data = HeadData(para_header); + let para_head_data = raw_para_head_data.encode(); let client_for_xcm = client_for_cidp.clone(); @@ -642,6 +643,8 @@ where let mocked_parachain = { MockValidationDataInherentDataProvider { current_para_block, + para_id, + current_para_block_head: Some(raw_para_head_data), relay_offset: 1000, relay_blocks_per_para_block: 2, para_blocks_per_relay_epoch: 0, @@ -650,7 +653,6 @@ where &*client_for_xcm, block, Default::default(), - Default::default(), ), raw_downward_messages: vec![], raw_horizontal_messages: vec![], diff --git a/node/src/services/handler.rs b/node/src/services/handler.rs index ef8674ea6..18c20edb6 100644 --- a/node/src/services/handler.rs +++ b/node/src/services/handler.rs @@ -1,275 +1,275 @@ -use std::sync::Arc; -use storage_hub_runtime::StorageDataUnit; -use tokio::sync::RwLock; - -use shc_actors_framework::{ - actor::{ActorHandle, TaskSpawner}, - event_bus::{EventBusListener, EventHandler}, -}; -use shc_blockchain_service::{ - events::{ - AcceptedBspVolunteer, LastChargeableInfoUpdated, MultipleNewChallengeSeeds, - NewStorageRequest, ProcessConfirmStoringRequest, ProcessMspRespondStoringRequest, - ProcessStopStoringForInsolventUserRequest, ProcessSubmitProofRequest, SlashableProvider, - SpStopStoringInsolventUser, UserWithoutFunds, - }, - BlockchainService, -}; -use shc_file_transfer_service::{ - events::{RemoteDownloadRequest, RemoteUploadRequest}, - FileTransferService, -}; -use shc_forest_manager::traits::ForestStorageHandler; - -use crate::tasks::{ - bsp_charge_fees::BspChargeFeesTask, bsp_download_file::BspDownloadFileTask, - bsp_submit_proof::BspSubmitProofTask, bsp_upload_file::BspUploadFileTask, - msp_upload_file::MspUploadFileTask, sp_slash_provider::SlashProviderTask, - user_sends_file::UserSendsFileTask, BspForestStorageHandlerT, FileStorageT, - MspForestStorageHandlerT, -}; - -/// Configuration paramaters for Storage Providers. -#[derive(Clone)] -pub struct ProviderConfig { - /// Maximum storage capacity of the provider (bytes). - /// - /// The Storage Provider will not request to increase its storage capacity beyond this value. - pub max_storage_capacity: StorageDataUnit, - /// Jump capacity (bytes). - /// - /// Storage capacity increases in jumps of this size. - pub jump_capacity: StorageDataUnit, - /// The time in seconds to wait before retrying an extrinsic. - pub extrinsic_retry_timeout: u64, -} - -/// Represents the handler for the Storage Hub service. -pub struct StorageHubHandler -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - /// The task spawner for spawning asynchronous tasks. - pub task_spawner: TaskSpawner, - /// The actor handle for the file transfer service. - pub file_transfer: ActorHandle, - /// The actor handle for the blockchain service. - pub blockchain: ActorHandle, - /// The file storage layer which stores all files in chunks. - pub file_storage: Arc>, - /// The forest storage layer which tracks all complete files stored in the file storage layer. - pub forest_storage_handler: FSH, - /// The configuration parameters for the provider. - pub provider_config: ProviderConfig, -} - -impl Clone for StorageHubHandler -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - fn clone(&self) -> StorageHubHandler { - Self { - task_spawner: self.task_spawner.clone(), - file_transfer: self.file_transfer.clone(), - blockchain: self.blockchain.clone(), - file_storage: self.file_storage.clone(), - forest_storage_handler: self.forest_storage_handler.clone(), - provider_config: self.provider_config.clone(), - } - } -} - -impl StorageHubHandler -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - pub fn new( - task_spawner: TaskSpawner, - file_transfer: ActorHandle, - blockchain: ActorHandle, - file_storage: Arc>, - forest_storage_handler: FSH, - provider_config: ProviderConfig, - ) -> Self { - Self { - task_spawner, - file_transfer, - blockchain, - file_storage, - forest_storage_handler, - provider_config, - } - } - - pub fn start_user_tasks(&self) { - log::info!("Starting User tasks."); - - let user_sends_file_task = UserSendsFileTask::new(self.clone()); - - // Subscribing to NewStorageRequest event from the BlockchainService. - let new_storage_request_event_bus_listener: EventBusListener = - user_sends_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - new_storage_request_event_bus_listener.start(); - - let accepted_bsp_volunteer_event_bus_listener: EventBusListener = - user_sends_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - accepted_bsp_volunteer_event_bus_listener.start(); - } -} - -impl StorageHubHandler -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - pub fn start_msp_tasks(&self) { - log::info!("Starting MSP tasks"); - - // MspUploadFileTask is triggered by a NewStorageRequest event which registers the user's peer address for - // an upcoming RemoteUploadRequest events, which happens when the user connects to the MSP and submits chunks of the file, - // along with a proof of storage, which is then queued to batch accept many storage requests at once. - // Finally once the ProcessMspRespondStoringRequest event is emitted, the MSP will respond to the user with a confirmation. - let msp_upload_file_task = MspUploadFileTask::new(self.clone()); - // Subscribing to NewStorageRequest event from the BlockchainService. - let new_storage_request_event_bus_listener: EventBusListener = - msp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - new_storage_request_event_bus_listener.start(); - // Subscribing to RemoteUploadRequest event from the FileTransferService. - let remote_upload_request_event_bus_listener: EventBusListener = - msp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.file_transfer); - remote_upload_request_event_bus_listener.start(); - // Subscribing to ProcessMspRespondStoringRequest event from the BlockchainService. - let process_confirm_storing_request_event_bus_listener: EventBusListener< - ProcessMspRespondStoringRequest, - _, - > = msp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - process_confirm_storing_request_event_bus_listener.start(); - } -} - -impl StorageHubHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn start_bsp_tasks(&self) { - log::info!("Starting BSP tasks"); - - // BspUploadFileTask is triggered by a NewStorageRequest event, to which it responds by - // volunteering to store the file. Then it waits for RemoteUploadRequest events, which - // happens when the user, now aware of the BSP volunteering, submits chunks of the file, - // along with a proof of storage. - let bsp_upload_file_task = BspUploadFileTask::new(self.clone()); - // Subscribing to NewStorageRequest event from the BlockchainService. - let new_storage_request_event_bus_listener: EventBusListener = - bsp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - new_storage_request_event_bus_listener.start(); - // Subscribing to RemoteUploadRequest event from the FileTransferService. - let remote_upload_request_event_bus_listener: EventBusListener = - bsp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.file_transfer); - remote_upload_request_event_bus_listener.start(); - // Subscribing to ProcessConfirmStoringRequest event from the BlockchainService. - let process_confirm_storing_request_event_bus_listener: EventBusListener< - ProcessConfirmStoringRequest, - _, - > = bsp_upload_file_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - process_confirm_storing_request_event_bus_listener.start(); - - // The BspDownloadFileTask - let bsp_download_file_task = BspDownloadFileTask::new(self.clone()); - // Subscribing to RemoteDownloadRequest event from the FileTransferService. - let remote_download_request_event_bus_listener: EventBusListener = - bsp_download_file_task.subscribe_to(&self.task_spawner, &self.file_transfer); - remote_download_request_event_bus_listener.start(); - - // BspSubmitProofTask is triggered by a MultipleNewChallengeSeeds event emitted by the BlockchainService. - // It responds by computing challenges derived from the seeds, taking also into account - // the custom challenges in checkpoint challenge rounds and enqueuing them in BlockchainService. - // BspSubmitProofTask also listens to ProcessSubmitProofRequest events, which are emitted by the - // BlockchainService when it is time to actually submit the proof of storage. - // Additionally, it handles file deletions as a consequence of inclusion proofs in custom challenges. - let bsp_submit_proof_task = BspSubmitProofTask::new(self.clone()); - // Subscribing to MultipleNewChallengeSeeds event from the BlockchainService. - let multiple_new_challenge_seeds_event_bus_listener: EventBusListener< - MultipleNewChallengeSeeds, - _, - > = bsp_submit_proof_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - multiple_new_challenge_seeds_event_bus_listener.start(); - // Subscribing to ProcessSubmitProofRequest event from the BlockchainService. - let process_submit_proof_request_event_bus_listener: EventBusListener< - ProcessSubmitProofRequest, - _, - > = bsp_submit_proof_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - process_submit_proof_request_event_bus_listener.start(); - - // Slash your own kin or potentially commit seppuku on your own stake. - // Running this is as a BSP is very honourable and shows a great sense of justice. - let bsp_slash_provider_task = SlashProviderTask::new(self.clone()); - // Subscribing to SlashableProvider event from the BlockchainService. - let slashable_provider_event_bus_listener: EventBusListener = - bsp_slash_provider_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - slashable_provider_event_bus_listener.start(); - - // Collect debt from users after a BSP proof is accepted. - let bsp_charge_fees_task = BspChargeFeesTask::new(self.clone()); - let last_chargeable_info_updated_event_bus_listener: EventBusListener< - LastChargeableInfoUpdated, - _, - > = bsp_charge_fees_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - last_chargeable_info_updated_event_bus_listener.start(); - - // Subscribing to ProcessStopStoringForInsolventUserRequest event from the BlockchainService. - let process_stop_storing_for_insolvent_user_request_event_bus_listener: EventBusListener< - ProcessStopStoringForInsolventUserRequest, - _, - > = bsp_charge_fees_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - process_stop_storing_for_insolvent_user_request_event_bus_listener.start(); - - // Start deletion process for stored files owned by a user that has been declared as without funds and charge - // its payment stream afterwards, getting the owed tokens and deleting it. - let user_without_funds_event_bus_listener: EventBusListener = - bsp_charge_fees_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - user_without_funds_event_bus_listener.start(); - - // Continue deletion process for stored files owned by a user that has been declared as without funds. - // Once the last file has been deleted, get the owed tokens and delete the payment stream. - let sp_stop_storing_insolvent_user_event_bus_listener: EventBusListener< - SpStopStoringInsolventUser, - _, - > = bsp_charge_fees_task - .clone() - .subscribe_to(&self.task_spawner, &self.blockchain); - sp_stop_storing_insolvent_user_event_bus_listener.start(); - } -} +use std::sync::Arc; +use storage_hub_runtime::StorageDataUnit; +use tokio::sync::RwLock; + +use shc_actors_framework::{ + actor::{ActorHandle, TaskSpawner}, + event_bus::{EventBusListener, EventHandler}, +}; +use shc_blockchain_service::{ + events::{ + AcceptedBspVolunteer, LastChargeableInfoUpdated, MultipleNewChallengeSeeds, + NewStorageRequest, ProcessConfirmStoringRequest, ProcessMspRespondStoringRequest, + ProcessStopStoringForInsolventUserRequest, ProcessSubmitProofRequest, SlashableProvider, + SpStopStoringInsolventUser, UserWithoutFunds, + }, + BlockchainService, +}; +use shc_file_transfer_service::{ + events::{RemoteDownloadRequest, RemoteUploadRequest}, + FileTransferService, +}; +use shc_forest_manager::traits::ForestStorageHandler; + +use crate::tasks::{ + bsp_charge_fees::BspChargeFeesTask, bsp_download_file::BspDownloadFileTask, + bsp_submit_proof::BspSubmitProofTask, bsp_upload_file::BspUploadFileTask, + msp_upload_file::MspUploadFileTask, sp_slash_provider::SlashProviderTask, + user_sends_file::UserSendsFileTask, BspForestStorageHandlerT, FileStorageT, + MspForestStorageHandlerT, +}; + +/// Configuration paramaters for Storage Providers. +#[derive(Clone)] +pub struct ProviderConfig { + /// Maximum storage capacity of the provider (bytes). + /// + /// The Storage Provider will not request to increase its storage capacity beyond this value. + pub max_storage_capacity: StorageDataUnit, + /// Jump capacity (bytes). + /// + /// Storage capacity increases in jumps of this size. + pub jump_capacity: StorageDataUnit, + /// The time in seconds to wait before retrying an extrinsic. + pub extrinsic_retry_timeout: u64, +} + +/// Represents the handler for the Storage Hub service. +pub struct StorageHubHandler +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + /// The task spawner for spawning asynchronous tasks. + pub task_spawner: TaskSpawner, + /// The actor handle for the file transfer service. + pub file_transfer: ActorHandle, + /// The actor handle for the blockchain service. + pub blockchain: ActorHandle, + /// The file storage layer which stores all files in chunks. + pub file_storage: Arc>, + /// The forest storage layer which tracks all complete files stored in the file storage layer. + pub forest_storage_handler: FSH, + /// The configuration parameters for the provider. + pub provider_config: ProviderConfig, +} + +impl Clone for StorageHubHandler +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + fn clone(&self) -> StorageHubHandler { + Self { + task_spawner: self.task_spawner.clone(), + file_transfer: self.file_transfer.clone(), + blockchain: self.blockchain.clone(), + file_storage: self.file_storage.clone(), + forest_storage_handler: self.forest_storage_handler.clone(), + provider_config: self.provider_config.clone(), + } + } +} + +impl StorageHubHandler +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + pub fn new( + task_spawner: TaskSpawner, + file_transfer: ActorHandle, + blockchain: ActorHandle, + file_storage: Arc>, + forest_storage_handler: FSH, + provider_config: ProviderConfig, + ) -> Self { + Self { + task_spawner, + file_transfer, + blockchain, + file_storage, + forest_storage_handler, + provider_config, + } + } + + pub fn start_user_tasks(&self) { + log::info!("Starting User tasks."); + + let user_sends_file_task = UserSendsFileTask::new(self.clone()); + + // Subscribing to NewStorageRequest event from the BlockchainService. + let new_storage_request_event_bus_listener: EventBusListener = + user_sends_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + new_storage_request_event_bus_listener.start(); + + let accepted_bsp_volunteer_event_bus_listener: EventBusListener = + user_sends_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + accepted_bsp_volunteer_event_bus_listener.start(); + } +} + +impl StorageHubHandler +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + pub fn start_msp_tasks(&self) { + log::info!("Starting MSP tasks"); + + // MspUploadFileTask is triggered by a NewStorageRequest event which registers the user's peer address for + // an upcoming RemoteUploadRequest events, which happens when the user connects to the MSP and submits chunks of the file, + // along with a proof of storage, which is then queued to batch accept many storage requests at once. + // Finally once the ProcessMspRespondStoringRequest event is emitted, the MSP will respond to the user with a confirmation. + let msp_upload_file_task = MspUploadFileTask::new(self.clone()); + // Subscribing to NewStorageRequest event from the BlockchainService. + let new_storage_request_event_bus_listener: EventBusListener = + msp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + new_storage_request_event_bus_listener.start(); + // Subscribing to RemoteUploadRequest event from the FileTransferService. + let remote_upload_request_event_bus_listener: EventBusListener = + msp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.file_transfer); + remote_upload_request_event_bus_listener.start(); + // Subscribing to ProcessMspRespondStoringRequest event from the BlockchainService. + let process_confirm_storing_request_event_bus_listener: EventBusListener< + ProcessMspRespondStoringRequest, + _, + > = msp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + process_confirm_storing_request_event_bus_listener.start(); + } +} + +impl StorageHubHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn start_bsp_tasks(&self) { + log::info!("Starting BSP tasks"); + + // BspUploadFileTask is triggered by a NewStorageRequest event, to which it responds by + // volunteering to store the file. Then it waits for RemoteUploadRequest events, which + // happens when the user, now aware of the BSP volunteering, submits chunks of the file, + // along with a proof of storage. + let bsp_upload_file_task = BspUploadFileTask::new(self.clone()); + // Subscribing to NewStorageRequest event from the BlockchainService. + let new_storage_request_event_bus_listener: EventBusListener = + bsp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + new_storage_request_event_bus_listener.start(); + // Subscribing to RemoteUploadRequest event from the FileTransferService. + let remote_upload_request_event_bus_listener: EventBusListener = + bsp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.file_transfer); + remote_upload_request_event_bus_listener.start(); + // Subscribing to ProcessConfirmStoringRequest event from the BlockchainService. + let process_confirm_storing_request_event_bus_listener: EventBusListener< + ProcessConfirmStoringRequest, + _, + > = bsp_upload_file_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + process_confirm_storing_request_event_bus_listener.start(); + + // The BspDownloadFileTask + let bsp_download_file_task = BspDownloadFileTask::new(self.clone()); + // Subscribing to RemoteDownloadRequest event from the FileTransferService. + let remote_download_request_event_bus_listener: EventBusListener = + bsp_download_file_task.subscribe_to(&self.task_spawner, &self.file_transfer); + remote_download_request_event_bus_listener.start(); + + // BspSubmitProofTask is triggered by a MultipleNewChallengeSeeds event emitted by the BlockchainService. + // It responds by computing challenges derived from the seeds, taking also into account + // the custom challenges in checkpoint challenge rounds and enqueuing them in BlockchainService. + // BspSubmitProofTask also listens to ProcessSubmitProofRequest events, which are emitted by the + // BlockchainService when it is time to actually submit the proof of storage. + // Additionally, it handles file deletions as a consequence of inclusion proofs in custom challenges. + let bsp_submit_proof_task = BspSubmitProofTask::new(self.clone()); + // Subscribing to MultipleNewChallengeSeeds event from the BlockchainService. + let multiple_new_challenge_seeds_event_bus_listener: EventBusListener< + MultipleNewChallengeSeeds, + _, + > = bsp_submit_proof_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + multiple_new_challenge_seeds_event_bus_listener.start(); + // Subscribing to ProcessSubmitProofRequest event from the BlockchainService. + let process_submit_proof_request_event_bus_listener: EventBusListener< + ProcessSubmitProofRequest, + _, + > = bsp_submit_proof_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + process_submit_proof_request_event_bus_listener.start(); + + // Slash your own kin or potentially commit seppuku on your own stake. + // Running this is as a BSP is very honourable and shows a great sense of justice. + let bsp_slash_provider_task = SlashProviderTask::new(self.clone()); + // Subscribing to SlashableProvider event from the BlockchainService. + let slashable_provider_event_bus_listener: EventBusListener = + bsp_slash_provider_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + slashable_provider_event_bus_listener.start(); + + // Collect debt from users after a BSP proof is accepted. + let bsp_charge_fees_task = BspChargeFeesTask::new(self.clone()); + let last_chargeable_info_updated_event_bus_listener: EventBusListener< + LastChargeableInfoUpdated, + _, + > = bsp_charge_fees_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + last_chargeable_info_updated_event_bus_listener.start(); + + // Subscribing to ProcessStopStoringForInsolventUserRequest event from the BlockchainService. + let process_stop_storing_for_insolvent_user_request_event_bus_listener: EventBusListener< + ProcessStopStoringForInsolventUserRequest, + _, + > = bsp_charge_fees_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + process_stop_storing_for_insolvent_user_request_event_bus_listener.start(); + + // Start deletion process for stored files owned by a user that has been declared as without funds and charge + // its payment stream afterwards, getting the owed tokens and deleting it. + let user_without_funds_event_bus_listener: EventBusListener = + bsp_charge_fees_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + user_without_funds_event_bus_listener.start(); + + // Continue deletion process for stored files owned by a user that has been declared as without funds. + // Once the last file has been deleted, get the owed tokens and delete the payment stream. + let sp_stop_storing_insolvent_user_event_bus_listener: EventBusListener< + SpStopStoringInsolventUser, + _, + > = bsp_charge_fees_task + .clone() + .subscribe_to(&self.task_spawner, &self.blockchain); + sp_stop_storing_insolvent_user_event_bus_listener.start(); + } +} diff --git a/node/src/tasks/bsp_submit_proof.rs b/node/src/tasks/bsp_submit_proof.rs index 87ea650f2..430874dab 100644 --- a/node/src/tasks/bsp_submit_proof.rs +++ b/node/src/tasks/bsp_submit_proof.rs @@ -1,605 +1,605 @@ -use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; - -use anyhow::anyhow; -use sc_tracing::tracing::*; -use shp_file_metadata::ChunkId; -use sp_core::H256; - -use shc_actors_framework::{actor::ActorHandle, event_bus::EventHandler}; -use shc_blockchain_service::{ - commands::BlockchainServiceInterface, - events::{ - FinalisedTrieRemoveMutationsApplied, MultipleNewChallengeSeeds, ProcessSubmitProofRequest, - }, - types::{RetryStrategy, SubmitProofRequest}, - BlockchainService, -}; -use shc_common::types::{ - BlockNumber, FileKey, KeyProof, KeyProofs, Proven, ProviderId, RandomnessOutput, StorageProof, - TrieRemoveMutation, -}; -use shc_forest_manager::traits::ForestStorage; - -use crate::services::{forest_storage::NoKey, handler::StorageHubHandler}; -use crate::tasks::{BspForestStorageHandlerT, FileStorageT}; - -const LOG_TARGET: &str = "bsp-submit-proof-task"; -const MAX_PROOF_SUBMISSION_ATTEMPTS: u32 = 3; - -/// BSP Submit Proof Task: Handles the submission of proof for BSP (Backup Storage Provider) to the runtime. -/// -/// The flow includes the following steps: -/// - **MultipleNewChallengeSeeds Event:** -/// - Triggered by the on-chain generation of a new challenge seed. -/// - For each seed: -/// - Derives forest challenges from the seed. -/// - Checks for any checkpoint challenges and adds them to the forest challenges. -/// - Queues the challenges for submission to the runtime, to be processed when the Forest write lock is released. -/// -/// - **ProcessSubmitProofRequest Event:** -/// - Triggered when the Blockchain Service detects that the Forest write lock has been released. -/// - Generates proofs for the queued challenges derived from the seed in the [`MultipleNewChallengeSeeds`] event. -/// - Constructs key proofs for each file key involved in the challenges. -/// - Submits the proofs to the runtime, with up to [`MAX_PROOF_SUBMISSION_ATTEMPTS`] retries on failure. -/// - Applies any necessary mutations to the Forest Storage (but not the File Storage). -/// - Verifies that the new Forest root matches the one recorded on-chain to ensure consistency. -/// -/// - **FinalisedTrieRemoveMutationsApplied Event:** -/// - Triggered when mutations applied to the Merkle Trie have been finalized, indicating that certain keys should be removed. -/// - Iterates over each file key that was part of the finalised mutations. -/// - Checks if the file key is still present in the Forest Storage: -/// - If the key is still present, logs a warning, as this may indicate that the key was re-added after deletion. -/// - If the key is absent from the Forest Storage, safely removes the corresponding file from the File Storage. -/// - Ensures that no residual file keys remain in the File Storage when they should have been deleted. -pub struct BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - storage_hub_handler: StorageHubHandler, -} - -impl Clone for BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - fn clone(&self) -> BspSubmitProofTask { - Self { - storage_hub_handler: self.storage_hub_handler.clone(), - } - } -} - -impl BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - storage_hub_handler, - } - } -} - -/// Handles the `MultipleNewChallengeSeeds` event. -/// -/// This event is triggered when catching up to proof submissions, and there are multiple new challenge seeds -/// that have to be responded in order. It queues the proof submissions for the given seeds. -/// The task performs the following actions for each seed: -/// - Derives forest challenges from the seed. -/// - Checks for checkpoint challenges and adds them to the forest challenges. -/// - Queues the challenges for submission to the runtime, for when the Forest write lock is released. -impl EventHandler for BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: MultipleNewChallengeSeeds) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Initiating BSP multiple proof submissions for BSP ID: {:?}, with seeds: {:?}", - event.provider_id, - event.seeds - ); - - for seed in event.seeds { - let provider_id = event.provider_id; - let tick = seed.0; - let seed = seed.1; - self.queue_submit_proof_request(provider_id, tick, seed) - .await?; - } - - Ok(()) - } -} - -/// Handles the `ProcessSubmitProofRequest` event. -/// -/// This event is triggered when the Blockchain Service realises that the Forest write lock has been released, -/// giving this task the opportunity to generate proofs and submit them to the runtime. -/// -/// This task performs the following actions: -/// - Generates proofs for the challenges. -/// - Constructs key proofs and submits the proof to the runtime. -/// - Retries up to [`MAX_PROOF_SUBMISSION_ATTEMPTS`] times if the submission fails. -/// - Applies any necessary mutations to the Forest Storage (not the File Storage). -/// - Ensures the new Forest root matches the one on-chain. -impl EventHandler for BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: ProcessSubmitProofRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Processing SubmitProofRequest {:?}", - event.data - ); - - // Check if this proof is the next one to be submitted. - // This is, for example, in case that this provider is trying to submit a proof for a tick that is not the next one to be submitted. - // Exiting early in this case is important so that the provider doesn't get stuck trying to submit an outdated proof. - Self::check_if_proof_is_outdated(&self.storage_hub_handler.blockchain, &event).await?; - - let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { - Some(tx) => tx, - None => { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."); - return Err(anyhow!( - "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken!" - )); - } - }; - - let proven_file_keys = { - let fs = self - .storage_hub_handler - .forest_storage_handler - .get(&NoKey) - .await - .ok_or_else(|| anyhow!("Failed to get forest storage."))?; - - let p = fs - .read() - .await - .generate_proof(event.data.forest_challenges.clone()) - .map_err(|e| anyhow!("Failed to generate forest proof: {:?}", e))?; - - p - }; - - // Get the keys that were proven. - let mut proven_keys = Vec::new(); - for key in proven_file_keys.proven { - match key { - Proven::ExactKey(leaf) => proven_keys.push(leaf.key), - Proven::NeighbourKeys((left, right)) => match (left, right) { - (Some(left), Some(right)) => { - proven_keys.push(left.key); - proven_keys.push(right.key); - } - (Some(left), None) => proven_keys.push(left.key), - (None, Some(right)) => proven_keys.push(right.key), - (None, None) => { - error!(target: LOG_TARGET, "Both left and right leaves in forest proof are None. This should not be possible."); - } - }, - Proven::Empty => { - error!(target: LOG_TARGET, "Forest proof generated with empty forest. This should not be possible, as this provider shouldn't have been challenged with an empty forest."); - } - } - } - - // Construct key challenges and generate key proofs for them. - let mut key_proofs = KeyProofs::new(); - for file_key in &proven_keys { - // Generate the key proof for each file key. - let key_proof = self - .generate_key_proof(*file_key, event.data.seed, event.data.provider_id) - .await?; - - key_proofs.insert(*file_key, key_proof); - } - - // Construct full proof. - let proof = StorageProof { - forest_proof: proven_file_keys.proof, - key_proofs, - }; - - // Submit proof to the runtime. - // Provider is `None` since we're submitting with the account linked to the BSP. - let call = storage_hub_runtime::RuntimeCall::ProofsDealer( - pallet_proofs_dealer::Call::submit_proof { - proof, - provider: None, - }, - ); - - // We consider that the maximum tip we're willing to pay for the submission of the proof is - // equal to the amount that this BSP would be slashed for, if the proof cannot be submitted. - let max_tip = self - .storage_hub_handler - .blockchain - .query_slash_amount_per_max_file_size() - .await? - .saturating_mul(event.data.forest_challenges.len() as u128) - .saturating_mul(2u32.into()); - - let cloned_blockchain = Arc::new(self.storage_hub_handler.blockchain.clone()); - let cloned_event = Arc::new(event.clone()); - - let should_retry = move || { - let cloned_blockchain = Arc::clone(&cloned_blockchain); - let cloned_event = Arc::clone(&cloned_event); - - Box::pin(async move { - Self::check_if_proof_is_outdated(&cloned_blockchain, &cloned_event) - .await - .is_ok() - }) as Pin + Send>> - }; - - // Attempt to submit the extrinsic with retries and tip increase. - self.storage_hub_handler - .blockchain - .submit_extrinsic_with_retry( - call, - RetryStrategy::default() - .with_max_retries(MAX_PROOF_SUBMISSION_ATTEMPTS) - .with_max_tip(max_tip as f64) - .with_timeout(Duration::from_secs(self.storage_hub_handler.provider_config.extrinsic_retry_timeout)) - .with_should_retry(Some(Box::new(should_retry))), - ) - .await - .map_err(|e| { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to submit proof after {} attempts: {}", MAX_PROOF_SUBMISSION_ATTEMPTS, e); - anyhow!( - "Failed to submit proof after {} attempts", - MAX_PROOF_SUBMISSION_ATTEMPTS - ) - })?; - - trace!(target: LOG_TARGET, "Proof submitted successfully"); - - // Apply mutations, if any. - let mut mutations_applied = false; - for (file_key, maybe_mutation) in &event.data.checkpoint_challenges { - if proven_keys.contains(file_key) { - // If the file key is proven, it means that this provider had an exact match for a checkpoint challenge. - trace!(target: LOG_TARGET, "Checkpoint challenge proven with exact match for file key: {:?}", file_key); - - if let Some(mutation) = maybe_mutation { - // If the mutation (which is a remove mutation) is Some and the file key was proven exactly, - // then the mutation needs to be applied (i.e. the file key is removed from the Forest). - trace!(target: LOG_TARGET, "Applying mutation: {:?}", mutation); - - // At this point, we only remove the file and its metadata from the Forest of this BSP. - // This is because if in a future block built on top of this one, the BSP needs to provide - // a proof, it will be against the Forest root with this change applied. - // We will remove the file from the File Storage only after finality is reached. - // This gives us the opportunity to put the file back in the Forest if this block is re-orged. - self.remove_file_from_forest(file_key).await?; - mutations_applied = true; - } - } - } - - if mutations_applied { - trace!(target: LOG_TARGET, "Mutations applied successfully"); - - // Check that the new Forest root matches the one on-chain. - self.check_provider_root(event.data.provider_id).await?; - } - - // Release the forest root write "lock". - let forest_root_write_result = forest_root_write_tx.send(()); - if forest_root_write_result.is_err() { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock. This is a critical bug. Please report it to the StorageHub team."); - return Err(anyhow!( - "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock." - )); - } - - Ok(()) - } -} - -/// Handles the `FinalisedTrieRemoveMutationsApplied` event. -/// -/// This event is triggered when mutations applied to the Forest of this BSP have been finalised, -/// signalling that certain keys (representing files) should be removed from the File Storage if they are -/// not present in the Forest Storage. If the key is still present in the Forest Storage, it sends out -/// a warning, since it could indicate that the key has been re-added after being deleted. -/// -/// This task performs the following actions: -/// - Iterates over each removed file key. -/// - Checks if the file key is present in the Forest Storage. -/// - If the key is still present, it logs a warning, -/// since this could indicate that the key has been re-added after being deleted. -/// - If the key is not present in the Forest Storage, it safely removes the key from the File Storage. -impl EventHandler for BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event( - &mut self, - event: FinalisedTrieRemoveMutationsApplied, - ) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Processing finalised mutations applied for provider [{:?}] with mutations: {:?}", - event.provider_id, - event.mutations - ); - - // For each mutation... - for mutation in event.mutations { - let file_key = FileKey::from(mutation.0); - - // Check that the file_key is not in the Forest. - let read_fs = self - .storage_hub_handler - .forest_storage_handler - .get(&NoKey) - .await - .ok_or_else(|| anyhow!("Failed to get forest storage."))?; - if read_fs.read().await.contains_file_key(&file_key.into())? { - warn!( - target: LOG_TARGET, - "TrieRemoveMutation applied and finalised for file key {:?}, but file key is still in Forest. This can only happen if the same file key was added again after deleted by the user.\n Mutation: {:?}", - file_key, - mutation - ); - } else { - // If file key is not in Forest, we can now safely remove it from the File Storage. - self.remove_file_from_file_storage(&file_key.into()).await?; - } - } - - Ok(()) - } -} - -impl BspSubmitProofTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn queue_submit_proof_request( - &self, - provider_id: ProviderId, - tick: BlockNumber, - seed: RandomnessOutput, - ) -> anyhow::Result<()> { - // Derive forest challenges from seed. - let mut forest_challenges = self - .derive_forest_challenges_from_seed(seed, provider_id) - .await?; - trace!(target: LOG_TARGET, "Forest challenges to respond to: {:?}", forest_challenges); - - // Check if there are checkpoint challenges since last tick this provider submitted a proof for. - // If so, this will add them to the forest challenges. - let checkpoint_challenges = self - .add_checkpoint_challenges_to_forest_challenges(provider_id, &mut forest_challenges) - .await?; - trace!(target: LOG_TARGET, "Checkpoint challenges to respond to: {:?}", checkpoint_challenges); - - self.storage_hub_handler - .blockchain - .queue_submit_proof_request(SubmitProofRequest::new( - provider_id, - tick, - seed, - forest_challenges, - checkpoint_challenges, - )) - .await?; - - Ok(()) - } - - async fn derive_forest_challenges_from_seed( - &self, - seed: RandomnessOutput, - provider_id: ProviderId, - ) -> anyhow::Result> { - Ok(self - .storage_hub_handler - .blockchain - .query_forest_challenges_from_seed(seed, provider_id) - .await?) - } - - async fn add_checkpoint_challenges_to_forest_challenges( - &self, - provider_id: ProviderId, - forest_challenges: &mut Vec, - ) -> anyhow::Result)>> { - let last_tick_provided_submitted_proof = self - .storage_hub_handler - .blockchain - .query_last_tick_provider_submitted_proof(provider_id) - .await - .map_err(|e| { - anyhow!( - "Failed to query last tick provider submitted proof: {:?}", - e - ) - })?; - let last_checkpoint_tick = self - .storage_hub_handler - .blockchain - .query_last_checkpoint_challenge_tick() - .await?; - - // If there were checkpoint challenges since the last tick this provider submitted a proof for, - // get the checkpoint challenges. - if last_tick_provided_submitted_proof <= last_checkpoint_tick { - let checkpoint_challenges = self - .storage_hub_handler - .blockchain - .query_last_checkpoint_challenges(last_checkpoint_tick) - .await - .map_err(|e| anyhow!("Failed to query last checkpoint challenges: {:?}", e))?; - - // Add the checkpoint challenges to the forest challenges. - forest_challenges.extend(checkpoint_challenges.iter().map(|(key, _)| *key)); - - // Return the checkpoint challenges. - Ok(checkpoint_challenges) - } else { - // Else, return an empty checkpoint challenges vector. - Ok(Vec::new()) - } - } - - async fn check_if_proof_is_outdated( - blockchain: &ActorHandle, - event: &ProcessSubmitProofRequest, - ) -> anyhow::Result<()> { - // Get the next challenge tick for this provider. - let next_challenge_tick = blockchain - .get_next_challenge_tick_for_provider(event.data.provider_id) - .await?; - - if next_challenge_tick != event.data.tick { - warn!(target: LOG_TARGET, "The proof for tick [{:?}] is not the next one to be submitted. Next challenge tick is [{:?}]", event.data.tick, next_challenge_tick); - return Err(anyhow!( - "The proof for tick [{:?}] is not the next one to be submitted.", - event.data.tick, - )); - } - - Ok(()) - } - - async fn generate_key_proof( - &self, - file_key: H256, - seed: RandomnessOutput, - provider_id: ProviderId, - ) -> anyhow::Result { - // Get the metadata for the file. - let read_file_storage = self.storage_hub_handler.file_storage.read().await; - let metadata = read_file_storage - .get_metadata(&file_key) - .map_err(|e| anyhow!("Error retrieving file metadata: {:?}", e))? - .ok_or(anyhow!("File metadata not found!"))?; - // Release the file storage read lock as soon as possible. - drop(read_file_storage); - - // Calculate the number of challenges for this file. - let challenge_count = metadata.chunks_to_check(); - - // Generate the challenges for this file. - let file_key_challenges = self - .storage_hub_handler - .blockchain - .query_challenges_from_seed(seed, provider_id, challenge_count) - .await?; - - // Convert the challenges to chunk IDs. - let chunks_count = metadata.chunks_count(); - let chunks_to_prove = file_key_challenges - .iter() - .map(|challenge| ChunkId::from_challenge(challenge.as_ref(), chunks_count)) - .collect::>(); - - // Construct file key proofs for the challenges. - let read_file_storage = self.storage_hub_handler.file_storage.read().await; - let file_key_proof = read_file_storage - .generate_proof(&file_key, &chunks_to_prove) - .map_err(|e| anyhow!("File is not in storage, or proof does not exist: {:?}", e))?; - // Release the file storage read lock as soon as possible. - drop(read_file_storage); - - // Return the key proof. - Ok(KeyProof { - proof: file_key_proof, - challenge_count, - }) - } - - async fn remove_file_from_forest(&self, file_key: &H256) -> anyhow::Result<()> { - // Remove the file key from the Forest. - // Check that the new Forest root matches the one on-chain. - { - let fs = self - .storage_hub_handler - .forest_storage_handler - .get(&NoKey) - .await - .ok_or_else(|| anyhow!("Failed to get forest storage."))?; - - fs.write().await.delete_file_key(file_key).map_err(|e| { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to apply mutation to Forest storage. This may result in a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team. \nError: {:?}", e); - anyhow!( - "Failed to remove file key from Forest storage: {:?}", - e - ) - })?; - }; - - Ok(()) - } - - async fn remove_file_from_file_storage(&self, file_key: &H256) -> anyhow::Result<()> { - // Remove the file from the File Storage. - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - write_file_storage.delete_file(file_key).map_err(|e| { - error!(target: LOG_TARGET, "Failed to remove file from File Storage after it was removed from the Forest. \nError: {:?}", e); - anyhow!( - "Failed to delete file from File Storage after it was removed from the Forest: {:?}", - e - ) - })?; - // Release the file storage write lock. - drop(write_file_storage); - - Ok(()) - } - - async fn check_provider_root(&self, provider_id: ProviderId) -> anyhow::Result<()> { - // Get root for this provider according to the runtime. - let onchain_root = self - .storage_hub_handler - .blockchain - .query_provider_forest_root(provider_id) - .await - .map_err(|e| { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to query provider root from runtime after successfully submitting proof. This may result in a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team. \nError: {:?}", e); - anyhow!( - "Failed to query provider root from runtime after successfully submitting proof: {:?}", - e - ) - })?; - - trace!(target: LOG_TARGET, "Provider root according to runtime: {:?}", onchain_root); - - // Check that the new Forest root matches the one on-chain. - let fs = self - .storage_hub_handler - .forest_storage_handler - .get(&NoKey) - .await - .ok_or_else(|| anyhow!("Failed to get forest storage."))?; - - let root = { fs.read().await.root() }; - - trace!(target: LOG_TARGET, "Provider root according to Forest Storage: {:?}", root); - - if root != onchain_root { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ Applying mutations yielded different root than the one on-chain. This means that there is a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team."); - return Err(anyhow!( - "Applying mutations yielded different root than the one on-chain." - )); - } - - Ok(()) - } -} +use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; + +use anyhow::anyhow; +use sc_tracing::tracing::*; +use shp_file_metadata::ChunkId; +use sp_core::H256; + +use shc_actors_framework::{actor::ActorHandle, event_bus::EventHandler}; +use shc_blockchain_service::{ + commands::BlockchainServiceInterface, + events::{ + FinalisedTrieRemoveMutationsApplied, MultipleNewChallengeSeeds, ProcessSubmitProofRequest, + }, + types::{RetryStrategy, SubmitProofRequest}, + BlockchainService, +}; +use shc_common::types::{ + BlockNumber, FileKey, KeyProof, KeyProofs, Proven, ProviderId, RandomnessOutput, StorageProof, + TrieRemoveMutation, +}; +use shc_forest_manager::traits::ForestStorage; + +use crate::services::{forest_storage::NoKey, handler::StorageHubHandler}; +use crate::tasks::{BspForestStorageHandlerT, FileStorageT}; + +const LOG_TARGET: &str = "bsp-submit-proof-task"; +const MAX_PROOF_SUBMISSION_ATTEMPTS: u32 = 3; + +/// BSP Submit Proof Task: Handles the submission of proof for BSP (Backup Storage Provider) to the runtime. +/// +/// The flow includes the following steps: +/// - **MultipleNewChallengeSeeds Event:** +/// - Triggered by the on-chain generation of a new challenge seed. +/// - For each seed: +/// - Derives forest challenges from the seed. +/// - Checks for any checkpoint challenges and adds them to the forest challenges. +/// - Queues the challenges for submission to the runtime, to be processed when the Forest write lock is released. +/// +/// - **ProcessSubmitProofRequest Event:** +/// - Triggered when the Blockchain Service detects that the Forest write lock has been released. +/// - Generates proofs for the queued challenges derived from the seed in the [`MultipleNewChallengeSeeds`] event. +/// - Constructs key proofs for each file key involved in the challenges. +/// - Submits the proofs to the runtime, with up to [`MAX_PROOF_SUBMISSION_ATTEMPTS`] retries on failure. +/// - Applies any necessary mutations to the Forest Storage (but not the File Storage). +/// - Verifies that the new Forest root matches the one recorded on-chain to ensure consistency. +/// +/// - **FinalisedTrieRemoveMutationsApplied Event:** +/// - Triggered when mutations applied to the Merkle Trie have been finalized, indicating that certain keys should be removed. +/// - Iterates over each file key that was part of the finalised mutations. +/// - Checks if the file key is still present in the Forest Storage: +/// - If the key is still present, logs a warning, as this may indicate that the key was re-added after deletion. +/// - If the key is absent from the Forest Storage, safely removes the corresponding file from the File Storage. +/// - Ensures that no residual file keys remain in the File Storage when they should have been deleted. +pub struct BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + storage_hub_handler: StorageHubHandler, +} + +impl Clone for BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + fn clone(&self) -> BspSubmitProofTask { + Self { + storage_hub_handler: self.storage_hub_handler.clone(), + } + } +} + +impl BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + storage_hub_handler, + } + } +} + +/// Handles the `MultipleNewChallengeSeeds` event. +/// +/// This event is triggered when catching up to proof submissions, and there are multiple new challenge seeds +/// that have to be responded in order. It queues the proof submissions for the given seeds. +/// The task performs the following actions for each seed: +/// - Derives forest challenges from the seed. +/// - Checks for checkpoint challenges and adds them to the forest challenges. +/// - Queues the challenges for submission to the runtime, for when the Forest write lock is released. +impl EventHandler for BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: MultipleNewChallengeSeeds) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Initiating BSP multiple proof submissions for BSP ID: {:?}, with seeds: {:?}", + event.provider_id, + event.seeds + ); + + for seed in event.seeds { + let provider_id = event.provider_id; + let tick = seed.0; + let seed = seed.1; + self.queue_submit_proof_request(provider_id, tick, seed) + .await?; + } + + Ok(()) + } +} + +/// Handles the `ProcessSubmitProofRequest` event. +/// +/// This event is triggered when the Blockchain Service realises that the Forest write lock has been released, +/// giving this task the opportunity to generate proofs and submit them to the runtime. +/// +/// This task performs the following actions: +/// - Generates proofs for the challenges. +/// - Constructs key proofs and submits the proof to the runtime. +/// - Retries up to [`MAX_PROOF_SUBMISSION_ATTEMPTS`] times if the submission fails. +/// - Applies any necessary mutations to the Forest Storage (not the File Storage). +/// - Ensures the new Forest root matches the one on-chain. +impl EventHandler for BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: ProcessSubmitProofRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Processing SubmitProofRequest {:?}", + event.data + ); + + // Check if this proof is the next one to be submitted. + // This is, for example, in case that this provider is trying to submit a proof for a tick that is not the next one to be submitted. + // Exiting early in this case is important so that the provider doesn't get stuck trying to submit an outdated proof. + Self::check_if_proof_is_outdated(&self.storage_hub_handler.blockchain, &event).await?; + + let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { + Some(tx) => tx, + None => { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."); + return Err(anyhow!( + "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken!" + )); + } + }; + + let proven_file_keys = { + let fs = self + .storage_hub_handler + .forest_storage_handler + .get(&NoKey) + .await + .ok_or_else(|| anyhow!("Failed to get forest storage."))?; + + let p = fs + .read() + .await + .generate_proof(event.data.forest_challenges.clone()) + .map_err(|e| anyhow!("Failed to generate forest proof: {:?}", e))?; + + p + }; + + // Get the keys that were proven. + let mut proven_keys = Vec::new(); + for key in proven_file_keys.proven { + match key { + Proven::ExactKey(leaf) => proven_keys.push(leaf.key), + Proven::NeighbourKeys((left, right)) => match (left, right) { + (Some(left), Some(right)) => { + proven_keys.push(left.key); + proven_keys.push(right.key); + } + (Some(left), None) => proven_keys.push(left.key), + (None, Some(right)) => proven_keys.push(right.key), + (None, None) => { + error!(target: LOG_TARGET, "Both left and right leaves in forest proof are None. This should not be possible."); + } + }, + Proven::Empty => { + error!(target: LOG_TARGET, "Forest proof generated with empty forest. This should not be possible, as this provider shouldn't have been challenged with an empty forest."); + } + } + } + + // Construct key challenges and generate key proofs for them. + let mut key_proofs = KeyProofs::new(); + for file_key in &proven_keys { + // Generate the key proof for each file key. + let key_proof = self + .generate_key_proof(*file_key, event.data.seed, event.data.provider_id) + .await?; + + key_proofs.insert(*file_key, key_proof); + } + + // Construct full proof. + let proof = StorageProof { + forest_proof: proven_file_keys.proof, + key_proofs, + }; + + // Submit proof to the runtime. + // Provider is `None` since we're submitting with the account linked to the BSP. + let call = storage_hub_runtime::RuntimeCall::ProofsDealer( + pallet_proofs_dealer::Call::submit_proof { + proof, + provider: None, + }, + ); + + // We consider that the maximum tip we're willing to pay for the submission of the proof is + // equal to the amount that this BSP would be slashed for, if the proof cannot be submitted. + let max_tip = self + .storage_hub_handler + .blockchain + .query_slash_amount_per_max_file_size() + .await? + .saturating_mul(event.data.forest_challenges.len() as u128) + .saturating_mul(2u32.into()); + + let cloned_blockchain = Arc::new(self.storage_hub_handler.blockchain.clone()); + let cloned_event = Arc::new(event.clone()); + + let should_retry = move || { + let cloned_blockchain = Arc::clone(&cloned_blockchain); + let cloned_event = Arc::clone(&cloned_event); + + Box::pin(async move { + Self::check_if_proof_is_outdated(&cloned_blockchain, &cloned_event) + .await + .is_ok() + }) as Pin + Send>> + }; + + // Attempt to submit the extrinsic with retries and tip increase. + self.storage_hub_handler + .blockchain + .submit_extrinsic_with_retry( + call, + RetryStrategy::default() + .with_max_retries(MAX_PROOF_SUBMISSION_ATTEMPTS) + .with_max_tip(max_tip as f64) + .with_timeout(Duration::from_secs(self.storage_hub_handler.provider_config.extrinsic_retry_timeout)) + .with_should_retry(Some(Box::new(should_retry))), + ) + .await + .map_err(|e| { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to submit proof after {} attempts: {}", MAX_PROOF_SUBMISSION_ATTEMPTS, e); + anyhow!( + "Failed to submit proof after {} attempts", + MAX_PROOF_SUBMISSION_ATTEMPTS + ) + })?; + + trace!(target: LOG_TARGET, "Proof submitted successfully"); + + // Apply mutations, if any. + let mut mutations_applied = false; + for (file_key, maybe_mutation) in &event.data.checkpoint_challenges { + if proven_keys.contains(file_key) { + // If the file key is proven, it means that this provider had an exact match for a checkpoint challenge. + trace!(target: LOG_TARGET, "Checkpoint challenge proven with exact match for file key: {:?}", file_key); + + if let Some(mutation) = maybe_mutation { + // If the mutation (which is a remove mutation) is Some and the file key was proven exactly, + // then the mutation needs to be applied (i.e. the file key is removed from the Forest). + trace!(target: LOG_TARGET, "Applying mutation: {:?}", mutation); + + // At this point, we only remove the file and its metadata from the Forest of this BSP. + // This is because if in a future block built on top of this one, the BSP needs to provide + // a proof, it will be against the Forest root with this change applied. + // We will remove the file from the File Storage only after finality is reached. + // This gives us the opportunity to put the file back in the Forest if this block is re-orged. + self.remove_file_from_forest(file_key).await?; + mutations_applied = true; + } + } + } + + if mutations_applied { + trace!(target: LOG_TARGET, "Mutations applied successfully"); + + // Check that the new Forest root matches the one on-chain. + self.check_provider_root(event.data.provider_id).await?; + } + + // Release the forest root write "lock". + let forest_root_write_result = forest_root_write_tx.send(()); + if forest_root_write_result.is_err() { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock. This is a critical bug. Please report it to the StorageHub team."); + return Err(anyhow!( + "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock." + )); + } + + Ok(()) + } +} + +/// Handles the `FinalisedTrieRemoveMutationsApplied` event. +/// +/// This event is triggered when mutations applied to the Forest of this BSP have been finalised, +/// signalling that certain keys (representing files) should be removed from the File Storage if they are +/// not present in the Forest Storage. If the key is still present in the Forest Storage, it sends out +/// a warning, since it could indicate that the key has been re-added after being deleted. +/// +/// This task performs the following actions: +/// - Iterates over each removed file key. +/// - Checks if the file key is present in the Forest Storage. +/// - If the key is still present, it logs a warning, +/// since this could indicate that the key has been re-added after being deleted. +/// - If the key is not present in the Forest Storage, it safely removes the key from the File Storage. +impl EventHandler for BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event( + &mut self, + event: FinalisedTrieRemoveMutationsApplied, + ) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Processing finalised mutations applied for provider [{:?}] with mutations: {:?}", + event.provider_id, + event.mutations + ); + + // For each mutation... + for mutation in event.mutations { + let file_key = FileKey::from(mutation.0); + + // Check that the file_key is not in the Forest. + let read_fs = self + .storage_hub_handler + .forest_storage_handler + .get(&NoKey) + .await + .ok_or_else(|| anyhow!("Failed to get forest storage."))?; + if read_fs.read().await.contains_file_key(&file_key.into())? { + warn!( + target: LOG_TARGET, + "TrieRemoveMutation applied and finalised for file key {:?}, but file key is still in Forest. This can only happen if the same file key was added again after deleted by the user.\n Mutation: {:?}", + file_key, + mutation + ); + } else { + // If file key is not in Forest, we can now safely remove it from the File Storage. + self.remove_file_from_file_storage(&file_key.into()).await?; + } + } + + Ok(()) + } +} + +impl BspSubmitProofTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn queue_submit_proof_request( + &self, + provider_id: ProviderId, + tick: BlockNumber, + seed: RandomnessOutput, + ) -> anyhow::Result<()> { + // Derive forest challenges from seed. + let mut forest_challenges = self + .derive_forest_challenges_from_seed(seed, provider_id) + .await?; + trace!(target: LOG_TARGET, "Forest challenges to respond to: {:?}", forest_challenges); + + // Check if there are checkpoint challenges since last tick this provider submitted a proof for. + // If so, this will add them to the forest challenges. + let checkpoint_challenges = self + .add_checkpoint_challenges_to_forest_challenges(provider_id, &mut forest_challenges) + .await?; + trace!(target: LOG_TARGET, "Checkpoint challenges to respond to: {:?}", checkpoint_challenges); + + self.storage_hub_handler + .blockchain + .queue_submit_proof_request(SubmitProofRequest::new( + provider_id, + tick, + seed, + forest_challenges, + checkpoint_challenges, + )) + .await?; + + Ok(()) + } + + async fn derive_forest_challenges_from_seed( + &self, + seed: RandomnessOutput, + provider_id: ProviderId, + ) -> anyhow::Result> { + Ok(self + .storage_hub_handler + .blockchain + .query_forest_challenges_from_seed(seed, provider_id) + .await?) + } + + async fn add_checkpoint_challenges_to_forest_challenges( + &self, + provider_id: ProviderId, + forest_challenges: &mut Vec, + ) -> anyhow::Result)>> { + let last_tick_provided_submitted_proof = self + .storage_hub_handler + .blockchain + .query_last_tick_provider_submitted_proof(provider_id) + .await + .map_err(|e| { + anyhow!( + "Failed to query last tick provider submitted proof: {:?}", + e + ) + })?; + let last_checkpoint_tick = self + .storage_hub_handler + .blockchain + .query_last_checkpoint_challenge_tick() + .await?; + + // If there were checkpoint challenges since the last tick this provider submitted a proof for, + // get the checkpoint challenges. + if last_tick_provided_submitted_proof <= last_checkpoint_tick { + let checkpoint_challenges = self + .storage_hub_handler + .blockchain + .query_last_checkpoint_challenges(last_checkpoint_tick) + .await + .map_err(|e| anyhow!("Failed to query last checkpoint challenges: {:?}", e))?; + + // Add the checkpoint challenges to the forest challenges. + forest_challenges.extend(checkpoint_challenges.iter().map(|(key, _)| *key)); + + // Return the checkpoint challenges. + Ok(checkpoint_challenges) + } else { + // Else, return an empty checkpoint challenges vector. + Ok(Vec::new()) + } + } + + async fn check_if_proof_is_outdated( + blockchain: &ActorHandle, + event: &ProcessSubmitProofRequest, + ) -> anyhow::Result<()> { + // Get the next challenge tick for this provider. + let next_challenge_tick = blockchain + .get_next_challenge_tick_for_provider(event.data.provider_id) + .await?; + + if next_challenge_tick != event.data.tick { + warn!(target: LOG_TARGET, "The proof for tick [{:?}] is not the next one to be submitted. Next challenge tick is [{:?}]", event.data.tick, next_challenge_tick); + return Err(anyhow!( + "The proof for tick [{:?}] is not the next one to be submitted.", + event.data.tick, + )); + } + + Ok(()) + } + + async fn generate_key_proof( + &self, + file_key: H256, + seed: RandomnessOutput, + provider_id: ProviderId, + ) -> anyhow::Result { + // Get the metadata for the file. + let read_file_storage = self.storage_hub_handler.file_storage.read().await; + let metadata = read_file_storage + .get_metadata(&file_key) + .map_err(|e| anyhow!("Error retrieving file metadata: {:?}", e))? + .ok_or(anyhow!("File metadata not found!"))?; + // Release the file storage read lock as soon as possible. + drop(read_file_storage); + + // Calculate the number of challenges for this file. + let challenge_count = metadata.chunks_to_check(); + + // Generate the challenges for this file. + let file_key_challenges = self + .storage_hub_handler + .blockchain + .query_challenges_from_seed(seed, provider_id, challenge_count) + .await?; + + // Convert the challenges to chunk IDs. + let chunks_count = metadata.chunks_count(); + let chunks_to_prove = file_key_challenges + .iter() + .map(|challenge| ChunkId::from_challenge(challenge.as_ref(), chunks_count)) + .collect::>(); + + // Construct file key proofs for the challenges. + let read_file_storage = self.storage_hub_handler.file_storage.read().await; + let file_key_proof = read_file_storage + .generate_proof(&file_key, &chunks_to_prove) + .map_err(|e| anyhow!("File is not in storage, or proof does not exist: {:?}", e))?; + // Release the file storage read lock as soon as possible. + drop(read_file_storage); + + // Return the key proof. + Ok(KeyProof { + proof: file_key_proof, + challenge_count, + }) + } + + async fn remove_file_from_forest(&self, file_key: &H256) -> anyhow::Result<()> { + // Remove the file key from the Forest. + // Check that the new Forest root matches the one on-chain. + { + let fs = self + .storage_hub_handler + .forest_storage_handler + .get(&NoKey) + .await + .ok_or_else(|| anyhow!("Failed to get forest storage."))?; + + fs.write().await.delete_file_key(file_key).map_err(|e| { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to apply mutation to Forest storage. This may result in a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team. \nError: {:?}", e); + anyhow!( + "Failed to remove file key from Forest storage: {:?}", + e + ) + })?; + }; + + Ok(()) + } + + async fn remove_file_from_file_storage(&self, file_key: &H256) -> anyhow::Result<()> { + // Remove the file from the File Storage. + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + write_file_storage.delete_file(file_key).map_err(|e| { + error!(target: LOG_TARGET, "Failed to remove file from File Storage after it was removed from the Forest. \nError: {:?}", e); + anyhow!( + "Failed to delete file from File Storage after it was removed from the Forest: {:?}", + e + ) + })?; + // Release the file storage write lock. + drop(write_file_storage); + + Ok(()) + } + + async fn check_provider_root(&self, provider_id: ProviderId) -> anyhow::Result<()> { + // Get root for this provider according to the runtime. + let onchain_root = self + .storage_hub_handler + .blockchain + .query_provider_forest_root(provider_id) + .await + .map_err(|e| { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Failed to query provider root from runtime after successfully submitting proof. This may result in a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team. \nError: {:?}", e); + anyhow!( + "Failed to query provider root from runtime after successfully submitting proof: {:?}", + e + ) + })?; + + trace!(target: LOG_TARGET, "Provider root according to runtime: {:?}", onchain_root); + + // Check that the new Forest root matches the one on-chain. + let fs = self + .storage_hub_handler + .forest_storage_handler + .get(&NoKey) + .await + .ok_or_else(|| anyhow!("Failed to get forest storage."))?; + + let root = { fs.read().await.root() }; + + trace!(target: LOG_TARGET, "Provider root according to Forest Storage: {:?}", root); + + if root != onchain_root { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ Applying mutations yielded different root than the one on-chain. This means that there is a mismatch between the Forest root on-chain and in this node. \nThis is a critical bug. Please report it to the StorageHub team."); + return Err(anyhow!( + "Applying mutations yielded different root than the one on-chain." + )); + } + + Ok(()) + } +} diff --git a/node/src/tasks/bsp_upload_file.rs b/node/src/tasks/bsp_upload_file.rs index cd1777854..f9bde6e6f 100644 --- a/node/src/tasks/bsp_upload_file.rs +++ b/node/src/tasks/bsp_upload_file.rs @@ -1,724 +1,724 @@ -use std::{cmp::max, str::FromStr, time::Duration}; - -use anyhow::anyhow; -use frame_support::BoundedVec; -use sc_network::PeerId; -use sc_tracing::tracing::*; -use sp_core::H256; -use sp_runtime::AccountId32; - -use shc_actors_framework::event_bus::EventHandler; -use shc_blockchain_service::{ - commands::BlockchainServiceInterface, - events::{NewStorageRequest, ProcessConfirmStoringRequest}, - types::{ConfirmStoringRequest, RetryStrategy, Tip}, -}; -use shc_common::types::{ - Balance, FileKey, FileMetadata, HashT, StorageProofsMerkleTrieLayout, StorageProviderId, -}; -use shc_file_manager::traits::{FileStorageWriteError, FileStorageWriteOutcome}; -use shc_file_transfer_service::{ - commands::FileTransferServiceInterface, events::RemoteUploadRequest, -}; -use shc_forest_manager::traits::ForestStorage; -use storage_hub_runtime::{StorageDataUnit, MILLIUNIT}; - -use crate::services::{forest_storage::NoKey, handler::StorageHubHandler}; -use crate::tasks::{BspForestStorageHandlerT, FileStorageT}; - -const LOG_TARGET: &str = "bsp-upload-file-task"; - -const MAX_CONFIRM_STORING_REQUEST_TRY_COUNT: u32 = 3; -const MAX_CONFIRM_STORING_REQUEST_TIP: Balance = 500 * MILLIUNIT; - -/// BSP Upload File Task: Handles the whole flow of a file being uploaded to a BSP, from -/// the BSP's perspective. -/// -/// The flow is split into three parts, which are represented here as 3 handlers for 3 -/// different events: -/// - [`NewStorageRequest`] event: The first part of the flow. It is triggered by an -/// on-chain event of a user submitting a storage request to StorageHub. It responds -/// by sending a volunteer transaction and registering the interest of this BSP in -/// receiving the file. -/// - [`RemoteUploadRequest`] event: The second part of the flow. It is triggered by a -/// user sending a chunk of the file to the BSP. It checks the proof for the chunk -/// and if it is valid, stores it, until the whole file is stored. -/// - [`ProcessConfirmStoringRequest`] event: The third part of the flow. It is triggered by the -/// runtime when the BSP should construct a proof for the new file(s) and submit a confirm storing -/// before updating it's local Forest storage root. -pub struct BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - storage_hub_handler: StorageHubHandler, - file_key_cleanup: Option, -} - -impl Clone for BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - fn clone(&self) -> BspUploadFileTask { - Self { - storage_hub_handler: self.storage_hub_handler.clone(), - file_key_cleanup: self.file_key_cleanup, - } - } -} - -impl BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - storage_hub_handler, - file_key_cleanup: None, - } - } -} - -/// Handles the `NewStorageRequest` event. -/// -/// This event is triggered by an on-chain event of a user submitting a storage request to StorageHub. -/// It responds by sending a volunteer transaction and registering the interest of this BSP in -/// receiving the file. This task optimistically assumes the transaction will succeed, and registers -/// the user and file key in the registry of the File Transfer Service, which handles incoming p2p -/// upload requests. -impl EventHandler for BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Initiating BSP volunteer for file_key {:?}, location {:?}, fingerprint {:?}", - event.file_key, - event.location, - event.fingerprint - ); - - let result = self.handle_new_storage_request_event(event).await; - if result.is_err() { - if let Some(file_key) = &self.file_key_cleanup { - self.unvolunteer_file(*file_key).await?; - } - } - result - } -} - -/// Handles the `RemoteUploadRequest` event. -/// -/// This event is triggered by a user sending a chunk of the file to the BSP. It checks the proof -/// for the chunk and if it is valid, stores it, until the whole file is stored. -impl EventHandler for BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { - info!(target: LOG_TARGET, "Received remote upload request for file {:?} and peer {:?}", event.file_key, event.peer); - - let proven = match event - .file_key_proof - .proven::() - { - Ok(proven) => { - if proven.len() != 1 { - Err(anyhow::anyhow!("Expected exactly one proven chunk.")) - } else { - Ok(proven[0].clone()) - } - } - Err(e) => Err(anyhow::anyhow!( - "Failed to verify and get proven file key chunks: {:?}", - e - )), - }; - - let proven = match proven { - Ok(proven) => proven, - Err(e) => { - warn!(target: LOG_TARGET, "{}", e); - - // Unvolunteer the file. - self.unvolunteer_file(event.file_key.into()).await?; - return Err(e); - } - }; - - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - let write_chunk_result = - write_file_storage.write_chunk(&event.file_key.into(), &proven.key, &proven.data); - // Release the file storage write lock as soon as possible. - drop(write_file_storage); - - match write_chunk_result { - Ok(outcome) => match outcome { - FileStorageWriteOutcome::FileComplete => { - self.on_file_complete(&event.file_key.into()).await? - } - FileStorageWriteOutcome::FileIncomplete => {} - }, - Err(error) => match error { - FileStorageWriteError::FileChunkAlreadyExists => { - warn!( - target: LOG_TARGET, - "Received duplicate chunk with key: {:?}", - proven.key - ); - - // TODO: Consider informing this to the file transfer service so that it can handle reputation for this peer id. - } - FileStorageWriteError::FileDoesNotExist => { - // Unvolunteer the file. - self.unvolunteer_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key))); - } - FileStorageWriteError::FailedToGetFileChunk - | FileStorageWriteError::FailedToInsertFileChunk - | FileStorageWriteError::FailedToDeleteChunk - | FileStorageWriteError::FailedToPersistChanges - | FileStorageWriteError::FailedToParseFileMetadata - | FileStorageWriteError::FailedToParseFingerprint - | FileStorageWriteError::FailedToReadStorage - | FileStorageWriteError::FailedToUpdatePartialRoot - | FileStorageWriteError::FailedToParsePartialRoot - | FileStorageWriteError::FailedToGetStoredChunksCount => { - // This internal error should not happen. - - // Unvolunteer the file. - self.unvolunteer_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "Internal trie read/write error {:?}:{:?}", - event.file_key, proven.key - ))); - } - FileStorageWriteError::FingerprintAndStoredFileMismatch => { - // This should never happen, given that the first check in the handler is verifying the proof. - // This means that something is seriously wrong, so we error out the whole task. - - // Unvolunteer the file. - self.unvolunteer_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "Invariant broken! This is a bug! Fingerprint and stored file mismatch for key {:?}.", - event.file_key - ))); - } - FileStorageWriteError::FailedToConstructTrieIter => { - // This should never happen for a well constructed trie. - // This means that something is seriously wrong, so we error out the whole task. - - // Unvolunteer the file. - self.unvolunteer_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "This is a bug! Failed to construct trie iter for key {:?}.", - event.file_key - ))); - } - }, - } - - Ok(()) - } -} - -/// Handles the `ProcessConfirmStoringRequest` event. -/// -/// This event is triggered by the runtime when it decides it is the right time to submit a confirm -/// storing extrinsic (and update the local forest root). -impl EventHandler for BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: ProcessConfirmStoringRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Processing ConfirmStoringRequest: {:?}", - event.data.confirm_storing_requests, - ); - - let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { - Some(tx) => tx, - None => { - let err_msg = "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }; - - let own_provider_id = self - .storage_hub_handler - .blockchain - .query_storage_provider_id(None) - .await?; - - let own_bsp_id = match own_provider_id { - Some(id) => match id { - StorageProviderId::MainStorageProvider(_) => { - let err_msg = "Current node account is a Main Storage Provider. Expected a Backup Storage Provider ID."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - StorageProviderId::BackupStorageProvider(id) => id, - }, - None => { - error!(target: LOG_TARGET, "Failed to get own BSP ID."); - return Err(anyhow!("Failed to get own BSP ID.")); - } - }; - - // Query runtime for the chunks to prove for the file. - let mut confirm_storing_requests_with_chunks_to_prove = Vec::new(); - for confirm_storing_request in event.data.confirm_storing_requests.iter() { - match self - .storage_hub_handler - .blockchain - .query_bsp_confirm_chunks_to_prove_for_file( - own_bsp_id, - confirm_storing_request.file_key, - ) - .await - { - Ok(chunks_to_prove) => { - confirm_storing_requests_with_chunks_to_prove - .push((confirm_storing_request, chunks_to_prove)); - } - Err(e) => { - let mut confirm_storing_request = confirm_storing_request.clone(); - confirm_storing_request.increment_try_count(); - if confirm_storing_request.try_count > MAX_CONFIRM_STORING_REQUEST_TRY_COUNT { - error!(target: LOG_TARGET, "Failed to query chunks to prove for file {:?}: {:?}\nMax try count exceeded! Dropping request!", confirm_storing_request.file_key, e); - } else { - error!(target: LOG_TARGET, "Failed to query chunks to prove for file {:?}: {:?}\nEnqueuing file key again! (retry {}/{})", confirm_storing_request.file_key, e, confirm_storing_request.try_count, MAX_CONFIRM_STORING_REQUEST_TRY_COUNT); - self.storage_hub_handler - .blockchain - .queue_confirm_bsp_request(confirm_storing_request) - .await?; - } - } - } - } - - // Generate the proof for the files and get metadatas. - let read_file_storage = self.storage_hub_handler.file_storage.read().await; - let mut file_keys_and_proofs = Vec::new(); - let mut file_metadatas = Vec::new(); - for (confirm_storing_request, chunks_to_prove) in - confirm_storing_requests_with_chunks_to_prove.into_iter() - { - match ( - read_file_storage - .generate_proof(&confirm_storing_request.file_key, &chunks_to_prove), - read_file_storage.get_metadata(&confirm_storing_request.file_key), - ) { - (Ok(proof), Ok(Some(metadata))) => { - file_keys_and_proofs.push((confirm_storing_request.file_key, proof)); - file_metadatas.push(metadata); - } - _ => { - let mut confirm_storing_request = confirm_storing_request.clone(); - confirm_storing_request.increment_try_count(); - if confirm_storing_request.try_count > MAX_CONFIRM_STORING_REQUEST_TRY_COUNT { - error!(target: LOG_TARGET, "Failed to generate proof or get metadatas for file {:?}.\nMax try count exceeded! Dropping request!", confirm_storing_request.file_key); - } else { - error!(target: LOG_TARGET, "Failed to generate proof or get metadatas for file {:?}.\nEnqueuing file key again! (retry {}/{})", confirm_storing_request.file_key, confirm_storing_request.try_count, MAX_CONFIRM_STORING_REQUEST_TRY_COUNT); - self.storage_hub_handler - .blockchain - .queue_confirm_bsp_request(confirm_storing_request) - .await?; - } - } - } - } - // Release the file storage read lock as soon as possible. - drop(read_file_storage); - - if file_keys_and_proofs.is_empty() { - error!(target: LOG_TARGET, "Failed to generate proofs for ALL the requested files.\n"); - return Err(anyhow!( - "Failed to generate proofs for ALL the requested files." - )); - } - - let file_keys = file_keys_and_proofs - .iter() - .map(|(file_key, _)| *file_key) - .collect::>(); - - let fs = self - .storage_hub_handler - .forest_storage_handler - .get(&NoKey) - .await - .ok_or_else(|| anyhow!("Failed to get forest storage."))?; - - // Generate a proof of non-inclusion (executed in closure to drop the read lock on the forest storage). - let non_inclusion_forest_proof = { fs.read().await.generate_proof(file_keys)? }; - - // Build extrinsic. - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::bsp_confirm_storing { - non_inclusion_forest_proof: non_inclusion_forest_proof.proof, - file_keys_and_proofs: BoundedVec::try_from(file_keys_and_proofs) - .map_err(|_| { - error!("CRITICAL❗️❗️ This is a bug! Failed to convert file keys and proofs to BoundedVec. Please report it to the StorageHub team."); - anyhow!("Failed to convert file keys and proofs to BoundedVec.") - })?, - }, - ); - - // Send the confirmation transaction and wait for it to be included in the block and - // continue only if it is successful. - self.storage_hub_handler - .blockchain - .submit_extrinsic_with_retry( - call, - RetryStrategy::default() - .with_max_retries(MAX_CONFIRM_STORING_REQUEST_TRY_COUNT) - .with_max_tip(MAX_CONFIRM_STORING_REQUEST_TIP as f64) - .with_timeout(Duration::from_secs( - self.storage_hub_handler - .provider_config - .extrinsic_retry_timeout, - )), - ) - .await?; - - // Save `FileMetadata` of the successfully retrieved stored files in the forest storage (executed in closure to drop the read lock on the forest storage). - { - fs.write() - .await - .insert_files_metadata(file_metadatas.as_slice())?; - } - - // Release the forest root write "lock". - let forest_root_write_result = forest_root_write_tx.send(()); - if forest_root_write_result.is_err() { - error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock. This is a critical bug. Please report it to the StorageHub team."); - return Err(anyhow!( - "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock." - )); - } - - Ok(()) - } -} - -impl BspUploadFileTask -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_new_storage_request_event( - &mut self, - event: NewStorageRequest, - ) -> anyhow::Result<()> { - // Construct file metadata. - let metadata = FileMetadata { - owner: >::as_ref(&event.who).to_vec(), - bucket_id: event.bucket_id.as_ref().to_vec(), - file_size: event.size as u64, - fingerprint: event.fingerprint, - location: event.location.to_vec(), - }; - - let own_provider_id = self - .storage_hub_handler - .blockchain - .query_storage_provider_id(None) - .await?; - - let own_bsp_id = match own_provider_id { - Some(id) => match id { - StorageProviderId::MainStorageProvider(_) => { - let err_msg = "Current node account is a Main Storage Provider. Expected a Backup Storage Provider ID."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - StorageProviderId::BackupStorageProvider(id) => id, - }, - None => { - let err_msg = "Failed to get own BSP ID."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }; - - let available_capacity = self - .storage_hub_handler - .blockchain - .query_available_storage_capacity(own_bsp_id) - .await - .map_err(|e| { - let err_msg = format!("Failed to query available storage capacity: {:?}", e); - error!( - target: LOG_TARGET, - err_msg - ); - anyhow::anyhow!(err_msg) - })?; - - // Increase storage capacity if the available capacity is less than the file size. - if available_capacity < event.size { - warn!( - target: LOG_TARGET, - "Insufficient storage capacity to volunteer for file key: {:?}", - event.file_key - ); - - let current_capacity = self - .storage_hub_handler - .blockchain - .query_storage_provider_capacity(own_bsp_id) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to query storage provider capacity: {:?}", e - ); - anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) - })?; - - let max_storage_capacity = self - .storage_hub_handler - .provider_config - .max_storage_capacity; - - if max_storage_capacity == current_capacity { - let err_msg = "Reached maximum storage capacity limit. Unable to add more more storage capacity."; - warn!( - target: LOG_TARGET, "{}", err_msg - ); - return Err(anyhow::anyhow!(err_msg)); - } - - let new_capacity = self.calculate_capacity(&event, current_capacity)?; - - let call = storage_hub_runtime::RuntimeCall::Providers( - pallet_storage_providers::Call::change_capacity { new_capacity }, - ); - - let earliest_change_capacity_block = self - .storage_hub_handler - .blockchain - .query_earliest_change_capacity_block(own_bsp_id) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to query storage provider capacity: {:?}", e - ); - anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) - })?; - - // Wait for the earliest block where the capacity can be changed. - self.storage_hub_handler - .blockchain - .wait_for_block(earliest_change_capacity_block) - .await?; - - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs( - self.storage_hub_handler - .provider_config - .extrinsic_retry_timeout, - )) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - info!( - target: LOG_TARGET, - "Increased storage capacity to {:?} bytes", - new_capacity - ); - - let available_capacity = self - .storage_hub_handler - .blockchain - .query_available_storage_capacity(own_bsp_id) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to query available storage capacity: {:?}", e - ); - anyhow::anyhow!("Failed to query available storage capacity: {:?}", e) - })?; - - // Skip volunteering if the new available capacity is still less than the file size. - if available_capacity < event.size { - let err_msg = "Increased storage capacity is still insufficient to volunteer for file. Skipping volunteering."; - warn!( - target: LOG_TARGET, "{}", err_msg - ); - return Err(anyhow::anyhow!(err_msg)); - } - } - - // Get the file key. - let file_key: FileKey = metadata - .file_key::>() - .as_ref() - .try_into()?; - - self.file_key_cleanup = Some(file_key.into()); - - // Query runtime for the earliest block where the BSP can volunteer for the file. - let earliest_volunteer_tick = self - .storage_hub_handler - .blockchain - .query_file_earliest_volunteer_tick(own_bsp_id, file_key.into()) - .await - .map_err(|e| anyhow!("Failed to query file earliest volunteer block: {:?}", e))?; - - info!( - target: LOG_TARGET, - "Waiting for tick {:?} to volunteer for file {:?}", - earliest_volunteer_tick, - file_key - ); - - // TODO: if the earliest tick is too far away, we should drop the task. - // TODO: based on the limit above, also add a timeout for the task. - self.storage_hub_handler - .blockchain - .wait_for_tick(earliest_volunteer_tick) - .await?; - - // Optimistically register the file for upload in the file transfer service. - // This solves the race condition between the user and the BSP, where the user could react faster - // to the BSP volunteering than the BSP, and therefore initiate a new upload request before the - // BSP has registered the file and peer ID in the file transfer service. - for peer_id in event.user_peer_ids.iter() { - let peer_id = match std::str::from_utf8(&peer_id.as_slice()) { - Ok(str_slice) => PeerId::from_str(str_slice).map_err(|e| { - error!(target: LOG_TARGET, "Failed to convert peer ID to PeerId: {}", e); - e - })?, - Err(e) => return Err(anyhow!("Failed to convert peer ID to a string: {}", e)), - }; - self.storage_hub_handler - .file_transfer - .register_new_file_peer(peer_id, file_key) - .await - .map_err(|e| anyhow!("Failed to register new file peer: {:?}", e))?; - } - - // Also optimistically create file in file storage so we can write uploaded chunks as soon as possible. - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - write_file_storage - .insert_file( - metadata.file_key::>(), - metadata, - ) - .map_err(|e| anyhow!("Failed to insert file in file storage: {:?}", e))?; - drop(write_file_storage); - - // Build extrinsic. - let call = - storage_hub_runtime::RuntimeCall::FileSystem(pallet_file_system::Call::bsp_volunteer { - file_key: H256(file_key.into()), - }); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs( - self.storage_hub_handler - .provider_config - .extrinsic_retry_timeout, - )) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - Ok(()) - } - - /// Calculate the new capacity after adding the required capacity for the file. - /// - /// The new storage capacity will be increased by the jump capacity until it reaches the - /// `max_storage_capacity`. - /// - /// The `max_storage_capacity` is returned if the new capacity exceeds it. - fn calculate_capacity( - &mut self, - event: &NewStorageRequest, - current_capacity: StorageDataUnit, - ) -> Result { - let jump_capacity = self.storage_hub_handler.provider_config.jump_capacity; - let jumps_needed = (event.size + jump_capacity - 1) / jump_capacity; - let jumps = max(jumps_needed, 1); - let bytes_to_add = jumps * jump_capacity; - let required_capacity = current_capacity.checked_add(bytes_to_add).ok_or_else(|| { - anyhow::anyhow!( - "Reached maximum storage capacity limit. Skipping volunteering for file." - ) - })?; - - let max_storage_capacity = self - .storage_hub_handler - .provider_config - .max_storage_capacity; - - let new_capacity = std::cmp::min(required_capacity, max_storage_capacity); - - Ok(new_capacity) - } - - async fn unvolunteer_file(&self, file_key: H256) -> anyhow::Result<()> { - warn!(target: LOG_TARGET, "Unvolunteering file {:?}", file_key); - - // Unregister the file from the file transfer service. - // The error is ignored, as the file might already be unregistered. - let _ = self - .storage_hub_handler - .file_transfer - .unregister_file(file_key.as_ref().into()) - .await; - - // TODO: Send transaction to runtime to unvolunteer the file. - - // Delete the file from the file storage. - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - - // TODO: Handle error - let _ = write_file_storage.delete_file(&file_key); - - Ok(()) - } - - async fn on_file_complete(&self, file_key: &H256) -> anyhow::Result<()> { - info!(target: LOG_TARGET, "File upload complete ({:?})", file_key); - - // Unregister the file from the file transfer service. - self.storage_hub_handler - .file_transfer - .unregister_file((*file_key).into()) - .await - .map_err(|e| anyhow!("File is not registered. This should not happen!: {:?}", e))?; - - // Queue a request to confirm the storing of the file. - self.storage_hub_handler - .blockchain - .queue_confirm_bsp_request(ConfirmStoringRequest::new(*file_key)) - .await?; - - Ok(()) - } -} +use std::{cmp::max, str::FromStr, time::Duration}; + +use anyhow::anyhow; +use frame_support::BoundedVec; +use sc_network::PeerId; +use sc_tracing::tracing::*; +use sp_core::H256; +use sp_runtime::AccountId32; + +use shc_actors_framework::event_bus::EventHandler; +use shc_blockchain_service::{ + commands::BlockchainServiceInterface, + events::{NewStorageRequest, ProcessConfirmStoringRequest}, + types::{ConfirmStoringRequest, RetryStrategy, Tip}, +}; +use shc_common::types::{ + Balance, FileKey, FileMetadata, HashT, StorageProofsMerkleTrieLayout, StorageProviderId, +}; +use shc_file_manager::traits::{FileStorageWriteError, FileStorageWriteOutcome}; +use shc_file_transfer_service::{ + commands::FileTransferServiceInterface, events::RemoteUploadRequest, +}; +use shc_forest_manager::traits::ForestStorage; +use storage_hub_runtime::{StorageDataUnit, MILLIUNIT}; + +use crate::services::{forest_storage::NoKey, handler::StorageHubHandler}; +use crate::tasks::{BspForestStorageHandlerT, FileStorageT}; + +const LOG_TARGET: &str = "bsp-upload-file-task"; + +const MAX_CONFIRM_STORING_REQUEST_TRY_COUNT: u32 = 3; +const MAX_CONFIRM_STORING_REQUEST_TIP: Balance = 500 * MILLIUNIT; + +/// BSP Upload File Task: Handles the whole flow of a file being uploaded to a BSP, from +/// the BSP's perspective. +/// +/// The flow is split into three parts, which are represented here as 3 handlers for 3 +/// different events: +/// - [`NewStorageRequest`] event: The first part of the flow. It is triggered by an +/// on-chain event of a user submitting a storage request to StorageHub. It responds +/// by sending a volunteer transaction and registering the interest of this BSP in +/// receiving the file. +/// - [`RemoteUploadRequest`] event: The second part of the flow. It is triggered by a +/// user sending a chunk of the file to the BSP. It checks the proof for the chunk +/// and if it is valid, stores it, until the whole file is stored. +/// - [`ProcessConfirmStoringRequest`] event: The third part of the flow. It is triggered by the +/// runtime when the BSP should construct a proof for the new file(s) and submit a confirm storing +/// before updating it's local Forest storage root. +pub struct BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + storage_hub_handler: StorageHubHandler, + file_key_cleanup: Option, +} + +impl Clone for BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + fn clone(&self) -> BspUploadFileTask { + Self { + storage_hub_handler: self.storage_hub_handler.clone(), + file_key_cleanup: self.file_key_cleanup, + } + } +} + +impl BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + storage_hub_handler, + file_key_cleanup: None, + } + } +} + +/// Handles the `NewStorageRequest` event. +/// +/// This event is triggered by an on-chain event of a user submitting a storage request to StorageHub. +/// It responds by sending a volunteer transaction and registering the interest of this BSP in +/// receiving the file. This task optimistically assumes the transaction will succeed, and registers +/// the user and file key in the registry of the File Transfer Service, which handles incoming p2p +/// upload requests. +impl EventHandler for BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Initiating BSP volunteer for file_key {:?}, location {:?}, fingerprint {:?}", + event.file_key, + event.location, + event.fingerprint + ); + + let result = self.handle_new_storage_request_event(event).await; + if result.is_err() { + if let Some(file_key) = &self.file_key_cleanup { + self.unvolunteer_file(*file_key).await?; + } + } + result + } +} + +/// Handles the `RemoteUploadRequest` event. +/// +/// This event is triggered by a user sending a chunk of the file to the BSP. It checks the proof +/// for the chunk and if it is valid, stores it, until the whole file is stored. +impl EventHandler for BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { + info!(target: LOG_TARGET, "Received remote upload request for file {:?} and peer {:?}", event.file_key, event.peer); + + let proven = match event + .file_key_proof + .proven::() + { + Ok(proven) => { + if proven.len() != 1 { + Err(anyhow::anyhow!("Expected exactly one proven chunk.")) + } else { + Ok(proven[0].clone()) + } + } + Err(e) => Err(anyhow::anyhow!( + "Failed to verify and get proven file key chunks: {:?}", + e + )), + }; + + let proven = match proven { + Ok(proven) => proven, + Err(e) => { + warn!(target: LOG_TARGET, "{}", e); + + // Unvolunteer the file. + self.unvolunteer_file(event.file_key.into()).await?; + return Err(e); + } + }; + + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + let write_chunk_result = + write_file_storage.write_chunk(&event.file_key.into(), &proven.key, &proven.data); + // Release the file storage write lock as soon as possible. + drop(write_file_storage); + + match write_chunk_result { + Ok(outcome) => match outcome { + FileStorageWriteOutcome::FileComplete => { + self.on_file_complete(&event.file_key.into()).await? + } + FileStorageWriteOutcome::FileIncomplete => {} + }, + Err(error) => match error { + FileStorageWriteError::FileChunkAlreadyExists => { + warn!( + target: LOG_TARGET, + "Received duplicate chunk with key: {:?}", + proven.key + ); + + // TODO: Consider informing this to the file transfer service so that it can handle reputation for this peer id. + } + FileStorageWriteError::FileDoesNotExist => { + // Unvolunteer the file. + self.unvolunteer_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key))); + } + FileStorageWriteError::FailedToGetFileChunk + | FileStorageWriteError::FailedToInsertFileChunk + | FileStorageWriteError::FailedToDeleteChunk + | FileStorageWriteError::FailedToPersistChanges + | FileStorageWriteError::FailedToParseFileMetadata + | FileStorageWriteError::FailedToParseFingerprint + | FileStorageWriteError::FailedToReadStorage + | FileStorageWriteError::FailedToUpdatePartialRoot + | FileStorageWriteError::FailedToParsePartialRoot + | FileStorageWriteError::FailedToGetStoredChunksCount => { + // This internal error should not happen. + + // Unvolunteer the file. + self.unvolunteer_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "Internal trie read/write error {:?}:{:?}", + event.file_key, proven.key + ))); + } + FileStorageWriteError::FingerprintAndStoredFileMismatch => { + // This should never happen, given that the first check in the handler is verifying the proof. + // This means that something is seriously wrong, so we error out the whole task. + + // Unvolunteer the file. + self.unvolunteer_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "Invariant broken! This is a bug! Fingerprint and stored file mismatch for key {:?}.", + event.file_key + ))); + } + FileStorageWriteError::FailedToConstructTrieIter => { + // This should never happen for a well constructed trie. + // This means that something is seriously wrong, so we error out the whole task. + + // Unvolunteer the file. + self.unvolunteer_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "This is a bug! Failed to construct trie iter for key {:?}.", + event.file_key + ))); + } + }, + } + + Ok(()) + } +} + +/// Handles the `ProcessConfirmStoringRequest` event. +/// +/// This event is triggered by the runtime when it decides it is the right time to submit a confirm +/// storing extrinsic (and update the local forest root). +impl EventHandler for BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: ProcessConfirmStoringRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Processing ConfirmStoringRequest: {:?}", + event.data.confirm_storing_requests, + ); + + let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { + Some(tx) => tx, + None => { + let err_msg = "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }; + + let own_provider_id = self + .storage_hub_handler + .blockchain + .query_storage_provider_id(None) + .await?; + + let own_bsp_id = match own_provider_id { + Some(id) => match id { + StorageProviderId::MainStorageProvider(_) => { + let err_msg = "Current node account is a Main Storage Provider. Expected a Backup Storage Provider ID."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + StorageProviderId::BackupStorageProvider(id) => id, + }, + None => { + error!(target: LOG_TARGET, "Failed to get own BSP ID."); + return Err(anyhow!("Failed to get own BSP ID.")); + } + }; + + // Query runtime for the chunks to prove for the file. + let mut confirm_storing_requests_with_chunks_to_prove = Vec::new(); + for confirm_storing_request in event.data.confirm_storing_requests.iter() { + match self + .storage_hub_handler + .blockchain + .query_bsp_confirm_chunks_to_prove_for_file( + own_bsp_id, + confirm_storing_request.file_key, + ) + .await + { + Ok(chunks_to_prove) => { + confirm_storing_requests_with_chunks_to_prove + .push((confirm_storing_request, chunks_to_prove)); + } + Err(e) => { + let mut confirm_storing_request = confirm_storing_request.clone(); + confirm_storing_request.increment_try_count(); + if confirm_storing_request.try_count > MAX_CONFIRM_STORING_REQUEST_TRY_COUNT { + error!(target: LOG_TARGET, "Failed to query chunks to prove for file {:?}: {:?}\nMax try count exceeded! Dropping request!", confirm_storing_request.file_key, e); + } else { + error!(target: LOG_TARGET, "Failed to query chunks to prove for file {:?}: {:?}\nEnqueuing file key again! (retry {}/{})", confirm_storing_request.file_key, e, confirm_storing_request.try_count, MAX_CONFIRM_STORING_REQUEST_TRY_COUNT); + self.storage_hub_handler + .blockchain + .queue_confirm_bsp_request(confirm_storing_request) + .await?; + } + } + } + } + + // Generate the proof for the files and get metadatas. + let read_file_storage = self.storage_hub_handler.file_storage.read().await; + let mut file_keys_and_proofs = Vec::new(); + let mut file_metadatas = Vec::new(); + for (confirm_storing_request, chunks_to_prove) in + confirm_storing_requests_with_chunks_to_prove.into_iter() + { + match ( + read_file_storage + .generate_proof(&confirm_storing_request.file_key, &chunks_to_prove), + read_file_storage.get_metadata(&confirm_storing_request.file_key), + ) { + (Ok(proof), Ok(Some(metadata))) => { + file_keys_and_proofs.push((confirm_storing_request.file_key, proof)); + file_metadatas.push(metadata); + } + _ => { + let mut confirm_storing_request = confirm_storing_request.clone(); + confirm_storing_request.increment_try_count(); + if confirm_storing_request.try_count > MAX_CONFIRM_STORING_REQUEST_TRY_COUNT { + error!(target: LOG_TARGET, "Failed to generate proof or get metadatas for file {:?}.\nMax try count exceeded! Dropping request!", confirm_storing_request.file_key); + } else { + error!(target: LOG_TARGET, "Failed to generate proof or get metadatas for file {:?}.\nEnqueuing file key again! (retry {}/{})", confirm_storing_request.file_key, confirm_storing_request.try_count, MAX_CONFIRM_STORING_REQUEST_TRY_COUNT); + self.storage_hub_handler + .blockchain + .queue_confirm_bsp_request(confirm_storing_request) + .await?; + } + } + } + } + // Release the file storage read lock as soon as possible. + drop(read_file_storage); + + if file_keys_and_proofs.is_empty() { + error!(target: LOG_TARGET, "Failed to generate proofs for ALL the requested files.\n"); + return Err(anyhow!( + "Failed to generate proofs for ALL the requested files." + )); + } + + let file_keys = file_keys_and_proofs + .iter() + .map(|(file_key, _)| *file_key) + .collect::>(); + + let fs = self + .storage_hub_handler + .forest_storage_handler + .get(&NoKey) + .await + .ok_or_else(|| anyhow!("Failed to get forest storage."))?; + + // Generate a proof of non-inclusion (executed in closure to drop the read lock on the forest storage). + let non_inclusion_forest_proof = { fs.read().await.generate_proof(file_keys)? }; + + // Build extrinsic. + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::bsp_confirm_storing { + non_inclusion_forest_proof: non_inclusion_forest_proof.proof, + file_keys_and_proofs: BoundedVec::try_from(file_keys_and_proofs) + .map_err(|_| { + error!("CRITICAL❗️❗️ This is a bug! Failed to convert file keys and proofs to BoundedVec. Please report it to the StorageHub team."); + anyhow!("Failed to convert file keys and proofs to BoundedVec.") + })?, + }, + ); + + // Send the confirmation transaction and wait for it to be included in the block and + // continue only if it is successful. + self.storage_hub_handler + .blockchain + .submit_extrinsic_with_retry( + call, + RetryStrategy::default() + .with_max_retries(MAX_CONFIRM_STORING_REQUEST_TRY_COUNT) + .with_max_tip(MAX_CONFIRM_STORING_REQUEST_TIP as f64) + .with_timeout(Duration::from_secs( + self.storage_hub_handler + .provider_config + .extrinsic_retry_timeout, + )), + ) + .await?; + + // Save `FileMetadata` of the successfully retrieved stored files in the forest storage (executed in closure to drop the read lock on the forest storage). + { + fs.write() + .await + .insert_files_metadata(file_metadatas.as_slice())?; + } + + // Release the forest root write "lock". + let forest_root_write_result = forest_root_write_tx.send(()); + if forest_root_write_result.is_err() { + error!(target: LOG_TARGET, "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock. This is a critical bug. Please report it to the StorageHub team."); + return Err(anyhow!( + "CRITICAL❗️❗️ This is a bug! Failed to release forest root write lock." + )); + } + + Ok(()) + } +} + +impl BspUploadFileTask +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_new_storage_request_event( + &mut self, + event: NewStorageRequest, + ) -> anyhow::Result<()> { + // Construct file metadata. + let metadata = FileMetadata { + owner: >::as_ref(&event.who).to_vec(), + bucket_id: event.bucket_id.as_ref().to_vec(), + file_size: event.size as u64, + fingerprint: event.fingerprint, + location: event.location.to_vec(), + }; + + let own_provider_id = self + .storage_hub_handler + .blockchain + .query_storage_provider_id(None) + .await?; + + let own_bsp_id = match own_provider_id { + Some(id) => match id { + StorageProviderId::MainStorageProvider(_) => { + let err_msg = "Current node account is a Main Storage Provider. Expected a Backup Storage Provider ID."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + StorageProviderId::BackupStorageProvider(id) => id, + }, + None => { + let err_msg = "Failed to get own BSP ID."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }; + + let available_capacity = self + .storage_hub_handler + .blockchain + .query_available_storage_capacity(own_bsp_id) + .await + .map_err(|e| { + let err_msg = format!("Failed to query available storage capacity: {:?}", e); + error!( + target: LOG_TARGET, + err_msg + ); + anyhow::anyhow!(err_msg) + })?; + + // Increase storage capacity if the available capacity is less than the file size. + if available_capacity < event.size { + warn!( + target: LOG_TARGET, + "Insufficient storage capacity to volunteer for file key: {:?}", + event.file_key + ); + + let current_capacity = self + .storage_hub_handler + .blockchain + .query_storage_provider_capacity(own_bsp_id) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Failed to query storage provider capacity: {:?}", e + ); + anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) + })?; + + let max_storage_capacity = self + .storage_hub_handler + .provider_config + .max_storage_capacity; + + if max_storage_capacity == current_capacity { + let err_msg = "Reached maximum storage capacity limit. Unable to add more more storage capacity."; + warn!( + target: LOG_TARGET, "{}", err_msg + ); + return Err(anyhow::anyhow!(err_msg)); + } + + let new_capacity = self.calculate_capacity(&event, current_capacity)?; + + let call = storage_hub_runtime::RuntimeCall::Providers( + pallet_storage_providers::Call::change_capacity { new_capacity }, + ); + + let earliest_change_capacity_block = self + .storage_hub_handler + .blockchain + .query_earliest_change_capacity_block(own_bsp_id) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Failed to query storage provider capacity: {:?}", e + ); + anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) + })?; + + // Wait for the earliest block where the capacity can be changed. + self.storage_hub_handler + .blockchain + .wait_for_block(earliest_change_capacity_block) + .await?; + + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs( + self.storage_hub_handler + .provider_config + .extrinsic_retry_timeout, + )) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + info!( + target: LOG_TARGET, + "Increased storage capacity to {:?} bytes", + new_capacity + ); + + let available_capacity = self + .storage_hub_handler + .blockchain + .query_available_storage_capacity(own_bsp_id) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Failed to query available storage capacity: {:?}", e + ); + anyhow::anyhow!("Failed to query available storage capacity: {:?}", e) + })?; + + // Skip volunteering if the new available capacity is still less than the file size. + if available_capacity < event.size { + let err_msg = "Increased storage capacity is still insufficient to volunteer for file. Skipping volunteering."; + warn!( + target: LOG_TARGET, "{}", err_msg + ); + return Err(anyhow::anyhow!(err_msg)); + } + } + + // Get the file key. + let file_key: FileKey = metadata + .file_key::>() + .as_ref() + .try_into()?; + + self.file_key_cleanup = Some(file_key.into()); + + // Query runtime for the earliest block where the BSP can volunteer for the file. + let earliest_volunteer_tick = self + .storage_hub_handler + .blockchain + .query_file_earliest_volunteer_tick(own_bsp_id, file_key.into()) + .await + .map_err(|e| anyhow!("Failed to query file earliest volunteer block: {:?}", e))?; + + info!( + target: LOG_TARGET, + "Waiting for tick {:?} to volunteer for file {:?}", + earliest_volunteer_tick, + file_key + ); + + // TODO: if the earliest tick is too far away, we should drop the task. + // TODO: based on the limit above, also add a timeout for the task. + self.storage_hub_handler + .blockchain + .wait_for_tick(earliest_volunteer_tick) + .await?; + + // Optimistically register the file for upload in the file transfer service. + // This solves the race condition between the user and the BSP, where the user could react faster + // to the BSP volunteering than the BSP, and therefore initiate a new upload request before the + // BSP has registered the file and peer ID in the file transfer service. + for peer_id in event.user_peer_ids.iter() { + let peer_id = match std::str::from_utf8(&peer_id.as_slice()) { + Ok(str_slice) => PeerId::from_str(str_slice).map_err(|e| { + error!(target: LOG_TARGET, "Failed to convert peer ID to PeerId: {}", e); + e + })?, + Err(e) => return Err(anyhow!("Failed to convert peer ID to a string: {}", e)), + }; + self.storage_hub_handler + .file_transfer + .register_new_file_peer(peer_id, file_key) + .await + .map_err(|e| anyhow!("Failed to register new file peer: {:?}", e))?; + } + + // Also optimistically create file in file storage so we can write uploaded chunks as soon as possible. + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + write_file_storage + .insert_file( + metadata.file_key::>(), + metadata, + ) + .map_err(|e| anyhow!("Failed to insert file in file storage: {:?}", e))?; + drop(write_file_storage); + + // Build extrinsic. + let call = + storage_hub_runtime::RuntimeCall::FileSystem(pallet_file_system::Call::bsp_volunteer { + file_key: H256(file_key.into()), + }); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs( + self.storage_hub_handler + .provider_config + .extrinsic_retry_timeout, + )) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + Ok(()) + } + + /// Calculate the new capacity after adding the required capacity for the file. + /// + /// The new storage capacity will be increased by the jump capacity until it reaches the + /// `max_storage_capacity`. + /// + /// The `max_storage_capacity` is returned if the new capacity exceeds it. + fn calculate_capacity( + &mut self, + event: &NewStorageRequest, + current_capacity: StorageDataUnit, + ) -> Result { + let jump_capacity = self.storage_hub_handler.provider_config.jump_capacity; + let jumps_needed = (event.size + jump_capacity - 1) / jump_capacity; + let jumps = max(jumps_needed, 1); + let bytes_to_add = jumps * jump_capacity; + let required_capacity = current_capacity.checked_add(bytes_to_add).ok_or_else(|| { + anyhow::anyhow!( + "Reached maximum storage capacity limit. Skipping volunteering for file." + ) + })?; + + let max_storage_capacity = self + .storage_hub_handler + .provider_config + .max_storage_capacity; + + let new_capacity = std::cmp::min(required_capacity, max_storage_capacity); + + Ok(new_capacity) + } + + async fn unvolunteer_file(&self, file_key: H256) -> anyhow::Result<()> { + warn!(target: LOG_TARGET, "Unvolunteering file {:?}", file_key); + + // Unregister the file from the file transfer service. + // The error is ignored, as the file might already be unregistered. + let _ = self + .storage_hub_handler + .file_transfer + .unregister_file(file_key.as_ref().into()) + .await; + + // TODO: Send transaction to runtime to unvolunteer the file. + + // Delete the file from the file storage. + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + + // TODO: Handle error + let _ = write_file_storage.delete_file(&file_key); + + Ok(()) + } + + async fn on_file_complete(&self, file_key: &H256) -> anyhow::Result<()> { + info!(target: LOG_TARGET, "File upload complete ({:?})", file_key); + + // Unregister the file from the file transfer service. + self.storage_hub_handler + .file_transfer + .unregister_file((*file_key).into()) + .await + .map_err(|e| anyhow!("File is not registered. This should not happen!: {:?}", e))?; + + // Queue a request to confirm the storing of the file. + self.storage_hub_handler + .blockchain + .queue_confirm_bsp_request(ConfirmStoringRequest::new(*file_key)) + .await?; + + Ok(()) + } +} diff --git a/node/src/tasks/mod.rs b/node/src/tasks/mod.rs index 759463415..e9e8c2e0c 100644 --- a/node/src/tasks/mod.rs +++ b/node/src/tasks/mod.rs @@ -1,192 +1,192 @@ -// TODO: Remove this once we don't need the examples in this file -#![allow(dead_code)] - -pub mod bsp_charge_fees; -pub mod bsp_download_file; -pub mod bsp_submit_proof; -pub mod bsp_upload_file; -pub mod mock_bsp_volunteer; -pub mod mock_sp_react_to_event; -pub mod msp_upload_file; -pub mod sp_slash_provider; -pub mod user_sends_file; - -use crate::services::forest_storage::{ForestStorageCaching, ForestStorageSingle, NoKey}; -use crate::services::handler::StorageHubHandler; -use kvdb::KeyValueDB; -use sc_tracing::tracing::info; -use shc_actors_framework::event_bus::EventHandler; -use shc_blockchain_service::events::{AcceptedBspVolunteer, NewStorageRequest}; -use shc_common::types::StorageProofsMerkleTrieLayout; -use shc_file_manager::in_memory::InMemoryFileStorage; -use shc_file_manager::rocksdb::RocksDbFileStorage; -use shc_file_manager::traits::FileStorage; -use shc_file_transfer_service::events::RemoteUploadRequest; -use shc_forest_manager::traits::{ForestStorage, ForestStorageHandler}; - -pub trait FileStorageT: FileStorage + Send + Sync {} -impl FileStorageT for InMemoryFileStorage {} -impl FileStorageT for RocksDbFileStorage where - DB: KeyValueDB + 'static -{ -} - -pub trait BspForestStorageHandlerT: - ForestStorageHandler + Clone + Send + Sync + 'static -{ -} -impl BspForestStorageHandlerT for ForestStorageSingle where - FS: ForestStorage + Send + Sync + 'static -{ -} - -pub trait MspForestStorageHandlerT: - ForestStorageHandler> + Clone + Send + Sync + 'static -{ -} -impl MspForestStorageHandlerT for ForestStorageCaching, FS> where - FS: ForestStorage + Send + Sync + 'static -{ -} - -// ! The following are examples of task definitions. -pub struct ResolveRemoteUploadRequest -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - _storage_hub_handler: StorageHubHandler, -} - -impl Clone for ResolveRemoteUploadRequest -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - fn clone(&self) -> ResolveRemoteUploadRequest { - Self { - _storage_hub_handler: self._storage_hub_handler.clone(), - } - } -} - -impl ResolveRemoteUploadRequest -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - _storage_hub_handler: storage_hub_handler, - } - } -} - -impl EventHandler for ResolveRemoteUploadRequest -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { - info!( - "[ResolveRemoteUploadRequest] - file location: {:?}", - event.file_key - ); - - // self.storage_hub_handler.storage.store_chunk().await?; - - Ok(()) - } -} - -pub struct NewStorageRequestHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - _storage_hub_handler: StorageHubHandler, -} - -impl NewStorageRequestHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - _storage_hub_handler: storage_hub_handler, - } - } -} - -impl Clone for NewStorageRequestHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - fn clone(&self) -> NewStorageRequestHandler { - Self { - _storage_hub_handler: self._storage_hub_handler.clone(), - } - } -} - -impl EventHandler for NewStorageRequestHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { - info!("[NewStorageRequestHandler] - received event: {:?}", event); - - // TODO: implement - - Ok(()) - } -} - -pub struct AcceptedBspVolunteerHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - _storage_hub_handler: StorageHubHandler, -} - -impl Clone for AcceptedBspVolunteerHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - fn clone(&self) -> AcceptedBspVolunteerHandler { - Self { - _storage_hub_handler: self._storage_hub_handler.clone(), - } - } -} - -impl AcceptedBspVolunteerHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - _storage_hub_handler: storage_hub_handler, - } - } -} - -impl EventHandler for AcceptedBspVolunteerHandler -where - FL: FileStorageT, - FSH: BspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: AcceptedBspVolunteer) -> anyhow::Result<()> { - info!("[NewStorageRequestHandler] - received event: {:?}", event); - - // TODO: implement - - Ok(()) - } -} +// TODO: Remove this once we don't need the examples in this file +#![allow(dead_code)] + +pub mod bsp_charge_fees; +pub mod bsp_download_file; +pub mod bsp_submit_proof; +pub mod bsp_upload_file; +pub mod mock_bsp_volunteer; +pub mod mock_sp_react_to_event; +pub mod msp_upload_file; +pub mod sp_slash_provider; +pub mod user_sends_file; + +use crate::services::forest_storage::{ForestStorageCaching, ForestStorageSingle, NoKey}; +use crate::services::handler::StorageHubHandler; +use kvdb::KeyValueDB; +use sc_tracing::tracing::info; +use shc_actors_framework::event_bus::EventHandler; +use shc_blockchain_service::events::{AcceptedBspVolunteer, NewStorageRequest}; +use shc_common::types::StorageProofsMerkleTrieLayout; +use shc_file_manager::in_memory::InMemoryFileStorage; +use shc_file_manager::rocksdb::RocksDbFileStorage; +use shc_file_manager::traits::FileStorage; +use shc_file_transfer_service::events::RemoteUploadRequest; +use shc_forest_manager::traits::{ForestStorage, ForestStorageHandler}; + +pub trait FileStorageT: FileStorage + Send + Sync {} +impl FileStorageT for InMemoryFileStorage {} +impl FileStorageT for RocksDbFileStorage where + DB: KeyValueDB + 'static +{ +} + +pub trait BspForestStorageHandlerT: + ForestStorageHandler + Clone + Send + Sync + 'static +{ +} +impl BspForestStorageHandlerT for ForestStorageSingle where + FS: ForestStorage + Send + Sync + 'static +{ +} + +pub trait MspForestStorageHandlerT: + ForestStorageHandler> + Clone + Send + Sync + 'static +{ +} +impl MspForestStorageHandlerT for ForestStorageCaching, FS> where + FS: ForestStorage + Send + Sync + 'static +{ +} + +// ! The following are examples of task definitions. +pub struct ResolveRemoteUploadRequest +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + _storage_hub_handler: StorageHubHandler, +} + +impl Clone for ResolveRemoteUploadRequest +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + fn clone(&self) -> ResolveRemoteUploadRequest { + Self { + _storage_hub_handler: self._storage_hub_handler.clone(), + } + } +} + +impl ResolveRemoteUploadRequest +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + _storage_hub_handler: storage_hub_handler, + } + } +} + +impl EventHandler for ResolveRemoteUploadRequest +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { + info!( + "[ResolveRemoteUploadRequest] - file location: {:?}", + event.file_key + ); + + // self.storage_hub_handler.storage.store_chunk().await?; + + Ok(()) + } +} + +pub struct NewStorageRequestHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + _storage_hub_handler: StorageHubHandler, +} + +impl NewStorageRequestHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + _storage_hub_handler: storage_hub_handler, + } + } +} + +impl Clone for NewStorageRequestHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + fn clone(&self) -> NewStorageRequestHandler { + Self { + _storage_hub_handler: self._storage_hub_handler.clone(), + } + } +} + +impl EventHandler for NewStorageRequestHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { + info!("[NewStorageRequestHandler] - received event: {:?}", event); + + // TODO: implement + + Ok(()) + } +} + +pub struct AcceptedBspVolunteerHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + _storage_hub_handler: StorageHubHandler, +} + +impl Clone for AcceptedBspVolunteerHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + fn clone(&self) -> AcceptedBspVolunteerHandler { + Self { + _storage_hub_handler: self._storage_hub_handler.clone(), + } + } +} + +impl AcceptedBspVolunteerHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + _storage_hub_handler: storage_hub_handler, + } + } +} + +impl EventHandler for AcceptedBspVolunteerHandler +where + FL: FileStorageT, + FSH: BspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: AcceptedBspVolunteer) -> anyhow::Result<()> { + info!("[NewStorageRequestHandler] - received event: {:?}", event); + + // TODO: implement + + Ok(()) + } +} diff --git a/node/src/tasks/msp_upload_file.rs b/node/src/tasks/msp_upload_file.rs index fd5818969..508029b97 100644 --- a/node/src/tasks/msp_upload_file.rs +++ b/node/src/tasks/msp_upload_file.rs @@ -1,869 +1,869 @@ -use std::collections::HashMap; -use std::{cmp::max, str::FromStr, time::Duration}; - -use anyhow::anyhow; -use sc_network::PeerId; -use sc_tracing::tracing::*; -use shc_blockchain_service::types::{MspRespondStorageRequest, RespondStorageRequest, Tip}; -use sp_core::{bounded_vec, H256}; -use sp_runtime::AccountId32; - -use crate::services::handler::StorageHubHandler; -use crate::tasks::{FileStorageT, MspForestStorageHandlerT}; -use shc_actors_framework::event_bus::EventHandler; -use shc_blockchain_service::events::ProcessMspRespondStoringRequest; -use shc_blockchain_service::{commands::BlockchainServiceInterface, events::NewStorageRequest}; -use shc_common::types::{ - AcceptedStorageRequestParameters, FileKey, FileMetadata, HashT, MspStorageRequestResponse, - RejectedStorageRequestReason, StorageProofsMerkleTrieLayout, StorageProviderId, -}; -use shc_file_manager::traits::{FileStorageWriteError, FileStorageWriteOutcome}; -use shc_file_transfer_service::{ - commands::FileTransferServiceInterface, events::RemoteUploadRequest, -}; -use shc_forest_manager::traits::ForestStorage; -use storage_hub_runtime::StorageDataUnit; - -const LOG_TARGET: &str = "msp-upload-file-task"; - -const MAX_CONFIRM_STORING_REQUEST_TRY_COUNT: u32 = 3; - -/// MSP Upload File Task: Handles the whole flow of a file being uploaded to a MSP, from -/// the MSP's perspective. -/// -/// The flow is split into three parts, which are represented here as 3 handlers for 3 -/// different events: -/// - [`NewStorageRequest`] event: The first part of the flow. It is triggered by a user -/// submitting a storage request to StorageHub. The MSP will check if it has enough -/// storage capacity to store the file and increase it if necessary (up to a maximum). -/// If the MSP does not have enough capacity still, it will reject the storage request. -/// It will register the user and file key in the registry of the File Transfer Service, -/// which handles incoming p2p upload requests. Finally, it will create a file in the -/// file storage so that it can write uploaded chunks as soon as possible. -/// - [`RemoteUploadRequest`] event: The second part of the flow. It is triggered by a -/// user sending a chunk of the file to the MSP. It checks the proof for the chunk -/// and if it is valid, stores it, until the whole file is stored. Finally the MSP will -/// queue a response to accept storing the file. -/// - [`ProcessMspRespondStoringRequest`] event: The third part of the flow. It is triggered -/// when there are new storage request(s) to respond to. The batch of storage requests -/// will be responded to in a single call to the FileSystem pallet `msp_respond_storage_requests_multiple_buckets` extrinsic -/// which will emit an event that describes the final result of the batch response (i.e. all accepted, -/// rejected and/or failed file keys). The MSP will then apply the necessary deltas to each one of the bucket's -/// forest storage to reflect the result. -pub struct MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - storage_hub_handler: StorageHubHandler, - file_key_cleanup: Option, -} - -impl Clone for MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - fn clone(&self) -> MspUploadFileTask { - Self { - storage_hub_handler: self.storage_hub_handler.clone(), - file_key_cleanup: self.file_key_cleanup, - } - } -} - -impl MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - storage_hub_handler, - file_key_cleanup: None, - } - } -} - -/// Handles the [`NewStorageRequest`] event. -/// -/// This event is triggered by an on-chain event of a user submitting a storage request to StorageHub. -/// -/// This task will: -/// - Check if the MSP has enough storage capacity to store the file and increase it if necessary (up to a maximum). -/// - Register the user and file key in the registry of the File Transfer Service, which handles incoming p2p -/// upload requests. -impl EventHandler for MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Registering user peer for file_key {:?}, location {:?}, fingerprint {:?}", - event.file_key, - event.location, - event.fingerprint - ); - - let result = self.handle_new_storage_request_event(event).await; - if result.is_err() { - if let Some(file_key) = &self.file_key_cleanup { - self.unregister_file(*file_key).await?; - } - } - result - } -} - -/// Handles the `RemoteUploadRequest` event. -/// -/// This event is triggered by a user sending a chunk of the file to the MSP. It checks the proof -/// for the chunk and if it is valid, stores it, until the whole file is stored. -impl EventHandler for MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { - info!(target: LOG_TARGET, "Received remote upload request for file {:?} and peer {:?}", event.file_key, event.peer); - - let proven = match event - .file_key_proof - .proven::() - { - Ok(proven) => { - if proven.len() != 1 { - Err(anyhow::anyhow!("Expected exactly one proven chunk.")) - } else { - Ok(proven[0].clone()) - } - } - Err(e) => Err(anyhow::anyhow!( - "Failed to verify and get proven file key chunks: {:?}", - e - )), - }; - - let bucket_id = match self - .storage_hub_handler - .file_storage - .read() - .await - .get_metadata(&event.file_key.into()) - { - Ok(metadata) => match metadata { - Some(metadata) => H256(metadata.bucket_id.try_into().unwrap()), - None => { - let err_msg = format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key); - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }, - Err(e) => { - let err_msg = format!("Failed to get file metadata: {:?}", e); - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }; - - // Reject storage request if the proof is invalid. - let proven = match proven { - Ok(proven) => proven, - Err(e) => { - warn!(target: LOG_TARGET, "{}", e); - - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::ReceivedInvalidProof, - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - // Unregister the file. - self.unregister_file(event.file_key.into()).await?; - return Err(e); - } - }; - - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - let write_chunk_result = - write_file_storage.write_chunk(&event.file_key.into(), &proven.key, &proven.data); - // Release the file storage write lock as soon as possible. - drop(write_file_storage); - - match write_chunk_result { - Ok(outcome) => match outcome { - FileStorageWriteOutcome::FileComplete => { - self.on_file_complete(&event.file_key.into()).await?; - } - FileStorageWriteOutcome::FileIncomplete => {} - }, - Err(error) => match error { - FileStorageWriteError::FileChunkAlreadyExists => { - warn!( - target: LOG_TARGET, - "Received duplicate chunk with key: {:?}", - proven.key - ); - - // TODO: Consider informing this to the file transfer service so that it can handle reputation for this peer id. - } - FileStorageWriteError::FileDoesNotExist => { - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::InternalError - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - // Unregister the file. - self.unregister_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key))); - } - FileStorageWriteError::FailedToGetFileChunk - | FileStorageWriteError::FailedToInsertFileChunk - | FileStorageWriteError::FailedToDeleteChunk - | FileStorageWriteError::FailedToPersistChanges - | FileStorageWriteError::FailedToParseFileMetadata - | FileStorageWriteError::FailedToParseFingerprint - | FileStorageWriteError::FailedToReadStorage - | FileStorageWriteError::FailedToUpdatePartialRoot - | FileStorageWriteError::FailedToParsePartialRoot - | FileStorageWriteError::FailedToGetStoredChunksCount => { - // This internal error should not happen. - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::InternalError - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - // Unregister the file. - self.unregister_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "Internal trie read/write error {:?}:{:?}", - event.file_key, proven.key - ))); - } - FileStorageWriteError::FingerprintAndStoredFileMismatch => { - // This should never happen, given that the first check in the handler is verifying the proof. - // This means that something is seriously wrong, so we error out the whole task. - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::InternalError - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - // Unregister the file. - self.unregister_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "Invariant broken! This is a bug! Fingerprint and stored file mismatch for key {:?}.", - event.file_key - ))); - } - FileStorageWriteError::FailedToConstructTrieIter => { - // This should never happen for a well constructed trie. - // This means that something is seriously wrong, so we error out the whole task. - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::InternalError - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - // Unregister the file. - self.unregister_file(event.file_key.into()).await?; - - return Err(anyhow::anyhow!(format!( - "This is a bug! Failed to construct trie iter for key {:?}.", - event.file_key - ))); - } - }, - } - - Ok(()) - } -} - -/// Handles the `ProcessMspRespondStoringRequest` event. -/// -/// Triggered when there are new storage request(s) to respond to. Normally, storage requests are -/// immidiately rejected if the MSP cannot store the file (e.g. not enough capacity). However, this event -/// is able to respond to storage requests that are either being accepted or rejected either way. -/// -/// The MSP will call the `msp_respond_storage_requests_multiple_buckets` extrinsic on the FileSystem pallet to respond to the -/// storage requests. -impl EventHandler for MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - async fn handle_event(&mut self, event: ProcessMspRespondStoringRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Processing ProcessMspRespondStoringRequest: {:?}", - event.data.respond_storing_requests, - ); - - let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { - Some(tx) => tx, - None => { - let err_msg = "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }; - - let own_provider_id = self - .storage_hub_handler - .blockchain - .query_storage_provider_id(None) - .await?; - - let own_msp_id = match own_provider_id { - Some(StorageProviderId::MainStorageProvider(id)) => id, - Some(StorageProviderId::BackupStorageProvider(_)) => { - return Err(anyhow!("Current node account is a Backup Storage Provider. Expected a Main Storage Provider ID.")); - } - None => { - return Err(anyhow!("Failed to get own MSP ID.")); - } - }; - - let mut file_key_responses: HashMap< - H256, - (Vec<(H256, _)>, Vec<(H256, RejectedStorageRequestReason)>), - > = HashMap::new(); - let read_file_storage = self.storage_hub_handler.file_storage.read().await; - - for respond in &event.data.respond_storing_requests { - let bucket_id = match read_file_storage.get_metadata(&respond.file_key) { - Ok(Some(metadata)) => H256(metadata.bucket_id.try_into().unwrap()), - Ok(None) => { - error!(target: LOG_TARGET, "File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", respond.file_key); - continue; - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to get file metadata: {:?}", e); - continue; - } - }; - - let entry = file_key_responses - .entry(bucket_id) - .or_insert_with(|| (Vec::new(), Vec::new())); - - match &respond.response { - MspRespondStorageRequest::Accept => { - let chunks_to_prove = match self - .storage_hub_handler - .blockchain - .query_msp_confirm_chunks_to_prove_for_file(own_msp_id, respond.file_key) - .await - { - Ok(chunks) => chunks, - Err(e) => { - error!(target: LOG_TARGET, "Failed to get chunks to prove: {:?}", e); - continue; - } - }; - - let proof = match read_file_storage - .generate_proof(&respond.file_key, &chunks_to_prove) - { - Ok(p) => p, - Err(e) => { - error!(target: LOG_TARGET, "Failed to generate proof: {:?}", e); - continue; - } - }; - - entry.0.push((respond.file_key, proof)); - } - MspRespondStorageRequest::Reject(reason) => { - entry.1.push((respond.file_key, reason.clone())); - } - } - } - - drop(read_file_storage); - - let mut final_responses: HashMap = HashMap::new(); - - for (bucket_id, (accepts, rejects)) in file_key_responses.iter_mut() { - let fs = match self - .storage_hub_handler - .forest_storage_handler - .get(&bucket_id.as_ref().to_vec()) - .await - { - Some(fs) => fs, - None => { - error!(target: LOG_TARGET, "Failed to get forest storage for bucket {:?}", bucket_id); - continue; - } - }; - - let file_keys: Vec<_> = accepts.iter().map(|(file_key, _)| *file_key).collect(); - - let non_inclusion_forest_proof = match fs.read().await.generate_proof(file_keys) { - Ok(proof) => proof, - Err(e) => { - error!(target: LOG_TARGET, "Failed to generate non-inclusion forest proof: {:?}", e); - continue; - } - }; - - let file_metadatas: Vec<_> = { - let read_file_storage = self.storage_hub_handler.file_storage.read().await; - accepts - .iter() - .filter_map(|(file_key, _)| { - read_file_storage.get_metadata(file_key).ok().flatten() - }) - .collect() - }; - - if let Err(e) = fs.write().await.insert_files_metadata(&file_metadatas) { - error!(target: LOG_TARGET, "Failed to insert file metadata: {:?}", e); - continue; - } - - let response = MspStorageRequestResponse { - accept: if !accepts.is_empty() { - Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: accepts - .clone() - .try_into() - .map_err(|_| anyhow!("Failed to convert accepts to bounded vec"))?, - non_inclusion_forest_proof: non_inclusion_forest_proof.proof, - }) - } else { - None - }, - reject: if !rejects.is_empty() { - Some( - rejects - .clone() - .try_into() - .map_err(|_| anyhow!("Failed to convert rejects to bounded vec"))?, - ) - } else { - None - }, - }; - - final_responses.insert(*bucket_id, response); - } - - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: final_responses - .into_iter() - .collect::>() - .try_into() - .map_err(|_| anyhow!("Failed to convert file key responses to bounded vec"))?, - }, - ); - - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - let _ = forest_root_write_tx.send(()); - - Ok(()) - } -} - -impl MspUploadFileTask -where - FL: FileStorageT, - FSH: MspForestStorageHandlerT, -{ - async fn handle_new_storage_request_event( - &mut self, - event: NewStorageRequest, - ) -> anyhow::Result<()> { - // Construct file metadata. - let metadata = FileMetadata { - owner: >::as_ref(&event.who).to_vec(), - bucket_id: event.bucket_id.as_ref().to_vec(), - file_size: event.size as u64, - fingerprint: event.fingerprint, - location: event.location.to_vec(), - }; - - let own_provider_id = self - .storage_hub_handler - .blockchain - .query_storage_provider_id(None) - .await?; - - let own_msp_id = match own_provider_id { - Some(id) => match id { - StorageProviderId::MainStorageProvider(id) => id, - StorageProviderId::BackupStorageProvider(_) => { - let err_msg = "Current node account is a Backup Storage Provider. Expected a Main Storage Provider ID."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }, - None => { - let err_msg = "Failed to get own MSP ID."; - error!(target: LOG_TARGET, err_msg); - return Err(anyhow!(err_msg)); - } - }; - - let available_capacity = self - .storage_hub_handler - .blockchain - .query_available_storage_capacity(own_msp_id) - .await - .map_err(|e| { - let err_msg = format!("Failed to query available storage capacity: {:?}", e); - error!( - target: LOG_TARGET, - err_msg - ); - anyhow::anyhow!(err_msg) - })?; - - // Increase storage capacity if the available capacity is less than the file size. - if available_capacity < event.size { - warn!( - target: LOG_TARGET, - "Insufficient storage capacity to accept file: {:?}", - event.file_key - ); - - let current_capacity = self - .storage_hub_handler - .blockchain - .query_storage_provider_capacity(own_msp_id) - .await - .map_err(|e| { - let err_msg = format!("Failed to query storage provider capacity: {:?}", e); - error!( - target: LOG_TARGET, - err_msg - ); - anyhow::anyhow!(err_msg) - })?; - - let max_storage_capacity = self - .storage_hub_handler - .provider_config - .max_storage_capacity; - - if max_storage_capacity == current_capacity { - let err_msg = "Reached maximum storage capacity limit. Unable to add more more storage capacity."; - warn!( - target: LOG_TARGET, err_msg - ); - return Err(anyhow::anyhow!(err_msg)); - } - - let new_capacity = self.calculate_capacity(&event, current_capacity)?; - - let call = storage_hub_runtime::RuntimeCall::Providers( - pallet_storage_providers::Call::change_capacity { new_capacity }, - ); - - let earliest_change_capacity_block = self - .storage_hub_handler - .blockchain - .query_earliest_change_capacity_block(own_msp_id) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to query storage provider capacity: {:?}", e - ); - anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) - })?; - - // Wait for the earliest block where the capacity can be changed. - self.storage_hub_handler - .blockchain - .wait_for_block(earliest_change_capacity_block) - .await?; - - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - info!( - target: LOG_TARGET, - "Increased storage capacity to {:?} bytes", - new_capacity - ); - - let available_capacity = self - .storage_hub_handler - .blockchain - .query_available_storage_capacity(own_msp_id) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Failed to query available storage capacity: {:?}", e - ); - anyhow::anyhow!("Failed to query available storage capacity: {:?}", e) - })?; - - // Reject storage request if the new available capacity is still less than the file size. - if available_capacity < event.size { - let err_msg = "Increased storage capacity is still insufficient to volunteer for file. Rejecting storage request."; - warn!( - target: LOG_TARGET, "{}", err_msg - ); - - // Build extrinsic. - let call = storage_hub_runtime::RuntimeCall::FileSystem( - pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { - file_key_responses_input: bounded_vec![( - H256(metadata.bucket_id.try_into().map_err(|e| { - let err_msg = - format!("Failed to convert bucket ID to [u8; 32]: {:?}", e); - error!( - target: LOG_TARGET, - err_msg - ); - anyhow::anyhow!(err_msg) - })?), - MspStorageRequestResponse { - accept: None, - reject: Some(bounded_vec![( - H256(event.file_key.into()), - RejectedStorageRequestReason::ReachedMaximumCapacity, - )]) - } - )], - }, - ); - - // Send extrinsic and wait for it to be included in the block. - self.storage_hub_handler - .blockchain - .send_extrinsic(call, Tip::from(0)) - .await? - .with_timeout(Duration::from_secs(60)) - .watch_for_success(&self.storage_hub_handler.blockchain) - .await?; - - return Err(anyhow::anyhow!(err_msg)); - } - } - - // Get the file key. - let file_key: FileKey = metadata - .file_key::>() - .as_ref() - .try_into()?; - - self.file_key_cleanup = Some(file_key.into()); - - // Register the file for upload in the file transfer service. - for peer_id in event.user_peer_ids.iter() { - let peer_id = match std::str::from_utf8(&peer_id.as_slice()) { - Ok(str_slice) => PeerId::from_str(str_slice).map_err(|e| { - error!(target: LOG_TARGET, "Failed to convert peer ID to PeerId: {}", e); - e - })?, - Err(e) => return Err(anyhow!("Failed to convert peer ID to a string: {}", e)), - }; - self.storage_hub_handler - .file_transfer - .register_new_file_peer(peer_id, file_key) - .await - .map_err(|e| anyhow!("Failed to register new file peer: {:?}", e))?; - } - - // Create file in file storage so we can write uploaded chunks as soon as possible. - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - write_file_storage - .insert_file( - metadata.file_key::>(), - metadata, - ) - .map_err(|e| anyhow!("Failed to insert file in file storage: {:?}", e))?; - drop(write_file_storage); - - Ok(()) - } - - /// Calculate the new capacity after adding the required capacity for the file. - /// - /// The new storage capacity will be increased by the jump capacity until it reaches the - /// `max_storage_capacity`. - /// - /// The `max_storage_capacity` is returned if the new capacity exceeds it. - fn calculate_capacity( - &mut self, - event: &NewStorageRequest, - current_capacity: StorageDataUnit, - ) -> Result { - let jump_capacity = self.storage_hub_handler.provider_config.jump_capacity; - let jumps_needed = (event.size + jump_capacity - 1) / jump_capacity; - let jumps = max(jumps_needed, 1); - let bytes_to_add = jumps * jump_capacity; - let required_capacity = current_capacity.checked_add(bytes_to_add).ok_or_else(|| { - anyhow::anyhow!( - "Reached maximum storage capacity limit. Skipping volunteering for file." - ) - })?; - - let max_storage_capacity = self - .storage_hub_handler - .provider_config - .max_storage_capacity; - - let new_capacity = std::cmp::min(required_capacity, max_storage_capacity); - - Ok(new_capacity) - } - - async fn unregister_file(&self, file_key: H256) -> anyhow::Result<()> { - warn!(target: LOG_TARGET, "Unregistering file {:?}", file_key); - - // Unregister the file from the file transfer service. - // The error is ignored, as the file might already be unregistered. - let _ = self - .storage_hub_handler - .file_transfer - .unregister_file(file_key.as_ref().into()) - .await; - - // Delete the file from the file storage. - let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; - - // TODO: Handle error - let _ = write_file_storage.delete_file(&file_key); - - Ok(()) - } - - async fn on_file_complete(&self, file_key: &H256) -> anyhow::Result<()> { - info!(target: LOG_TARGET, "File upload complete ({:?})", file_key); - - // Unregister the file from the file transfer service. - self.storage_hub_handler - .file_transfer - .unregister_file((*file_key).into()) - .await - .map_err(|e| anyhow!("File is not registered. This should not happen!: {:?}", e))?; - - // Queue a request to confirm the storing of the file. - self.storage_hub_handler - .blockchain - .queue_msp_respond_storage_request(RespondStorageRequest::new( - *file_key, - MspRespondStorageRequest::Accept, - )) - .await?; - - Ok(()) - } -} +use std::collections::HashMap; +use std::{cmp::max, str::FromStr, time::Duration}; + +use anyhow::anyhow; +use sc_network::PeerId; +use sc_tracing::tracing::*; +use shc_blockchain_service::types::{MspRespondStorageRequest, RespondStorageRequest, Tip}; +use sp_core::{bounded_vec, H256}; +use sp_runtime::AccountId32; + +use crate::services::handler::StorageHubHandler; +use crate::tasks::{FileStorageT, MspForestStorageHandlerT}; +use shc_actors_framework::event_bus::EventHandler; +use shc_blockchain_service::events::ProcessMspRespondStoringRequest; +use shc_blockchain_service::{commands::BlockchainServiceInterface, events::NewStorageRequest}; +use shc_common::types::{ + AcceptedStorageRequestParameters, FileKey, FileMetadata, HashT, MspStorageRequestResponse, + RejectedStorageRequestReason, StorageProofsMerkleTrieLayout, StorageProviderId, +}; +use shc_file_manager::traits::{FileStorageWriteError, FileStorageWriteOutcome}; +use shc_file_transfer_service::{ + commands::FileTransferServiceInterface, events::RemoteUploadRequest, +}; +use shc_forest_manager::traits::ForestStorage; +use storage_hub_runtime::StorageDataUnit; + +const LOG_TARGET: &str = "msp-upload-file-task"; + +const MAX_CONFIRM_STORING_REQUEST_TRY_COUNT: u32 = 3; + +/// MSP Upload File Task: Handles the whole flow of a file being uploaded to a MSP, from +/// the MSP's perspective. +/// +/// The flow is split into three parts, which are represented here as 3 handlers for 3 +/// different events: +/// - [`NewStorageRequest`] event: The first part of the flow. It is triggered by a user +/// submitting a storage request to StorageHub. The MSP will check if it has enough +/// storage capacity to store the file and increase it if necessary (up to a maximum). +/// If the MSP does not have enough capacity still, it will reject the storage request. +/// It will register the user and file key in the registry of the File Transfer Service, +/// which handles incoming p2p upload requests. Finally, it will create a file in the +/// file storage so that it can write uploaded chunks as soon as possible. +/// - [`RemoteUploadRequest`] event: The second part of the flow. It is triggered by a +/// user sending a chunk of the file to the MSP. It checks the proof for the chunk +/// and if it is valid, stores it, until the whole file is stored. Finally the MSP will +/// queue a response to accept storing the file. +/// - [`ProcessMspRespondStoringRequest`] event: The third part of the flow. It is triggered +/// when there are new storage request(s) to respond to. The batch of storage requests +/// will be responded to in a single call to the FileSystem pallet `msp_respond_storage_requests_multiple_buckets` extrinsic +/// which will emit an event that describes the final result of the batch response (i.e. all accepted, +/// rejected and/or failed file keys). The MSP will then apply the necessary deltas to each one of the bucket's +/// forest storage to reflect the result. +pub struct MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + storage_hub_handler: StorageHubHandler, + file_key_cleanup: Option, +} + +impl Clone for MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + fn clone(&self) -> MspUploadFileTask { + Self { + storage_hub_handler: self.storage_hub_handler.clone(), + file_key_cleanup: self.file_key_cleanup, + } + } +} + +impl MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + storage_hub_handler, + file_key_cleanup: None, + } + } +} + +/// Handles the [`NewStorageRequest`] event. +/// +/// This event is triggered by an on-chain event of a user submitting a storage request to StorageHub. +/// +/// This task will: +/// - Check if the MSP has enough storage capacity to store the file and increase it if necessary (up to a maximum). +/// - Register the user and file key in the registry of the File Transfer Service, which handles incoming p2p +/// upload requests. +impl EventHandler for MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Registering user peer for file_key {:?}, location {:?}, fingerprint {:?}", + event.file_key, + event.location, + event.fingerprint + ); + + let result = self.handle_new_storage_request_event(event).await; + if result.is_err() { + if let Some(file_key) = &self.file_key_cleanup { + self.unregister_file(*file_key).await?; + } + } + result + } +} + +/// Handles the `RemoteUploadRequest` event. +/// +/// This event is triggered by a user sending a chunk of the file to the MSP. It checks the proof +/// for the chunk and if it is valid, stores it, until the whole file is stored. +impl EventHandler for MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: RemoteUploadRequest) -> anyhow::Result<()> { + info!(target: LOG_TARGET, "Received remote upload request for file {:?} and peer {:?}", event.file_key, event.peer); + + let proven = match event + .file_key_proof + .proven::() + { + Ok(proven) => { + if proven.len() != 1 { + Err(anyhow::anyhow!("Expected exactly one proven chunk.")) + } else { + Ok(proven[0].clone()) + } + } + Err(e) => Err(anyhow::anyhow!( + "Failed to verify and get proven file key chunks: {:?}", + e + )), + }; + + let bucket_id = match self + .storage_hub_handler + .file_storage + .read() + .await + .get_metadata(&event.file_key.into()) + { + Ok(metadata) => match metadata { + Some(metadata) => H256(metadata.bucket_id.try_into().unwrap()), + None => { + let err_msg = format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key); + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }, + Err(e) => { + let err_msg = format!("Failed to get file metadata: {:?}", e); + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }; + + // Reject storage request if the proof is invalid. + let proven = match proven { + Ok(proven) => proven, + Err(e) => { + warn!(target: LOG_TARGET, "{}", e); + + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::ReceivedInvalidProof, + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + // Unregister the file. + self.unregister_file(event.file_key.into()).await?; + return Err(e); + } + }; + + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + let write_chunk_result = + write_file_storage.write_chunk(&event.file_key.into(), &proven.key, &proven.data); + // Release the file storage write lock as soon as possible. + drop(write_file_storage); + + match write_chunk_result { + Ok(outcome) => match outcome { + FileStorageWriteOutcome::FileComplete => { + self.on_file_complete(&event.file_key.into()).await?; + } + FileStorageWriteOutcome::FileIncomplete => {} + }, + Err(error) => match error { + FileStorageWriteError::FileChunkAlreadyExists => { + warn!( + target: LOG_TARGET, + "Received duplicate chunk with key: {:?}", + proven.key + ); + + // TODO: Consider informing this to the file transfer service so that it can handle reputation for this peer id. + } + FileStorageWriteError::FileDoesNotExist => { + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::InternalError + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + // Unregister the file. + self.unregister_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!("File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", event.file_key))); + } + FileStorageWriteError::FailedToGetFileChunk + | FileStorageWriteError::FailedToInsertFileChunk + | FileStorageWriteError::FailedToDeleteChunk + | FileStorageWriteError::FailedToPersistChanges + | FileStorageWriteError::FailedToParseFileMetadata + | FileStorageWriteError::FailedToParseFingerprint + | FileStorageWriteError::FailedToReadStorage + | FileStorageWriteError::FailedToUpdatePartialRoot + | FileStorageWriteError::FailedToParsePartialRoot + | FileStorageWriteError::FailedToGetStoredChunksCount => { + // This internal error should not happen. + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::InternalError + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + // Unregister the file. + self.unregister_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "Internal trie read/write error {:?}:{:?}", + event.file_key, proven.key + ))); + } + FileStorageWriteError::FingerprintAndStoredFileMismatch => { + // This should never happen, given that the first check in the handler is verifying the proof. + // This means that something is seriously wrong, so we error out the whole task. + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::InternalError + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + // Unregister the file. + self.unregister_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "Invariant broken! This is a bug! Fingerprint and stored file mismatch for key {:?}.", + event.file_key + ))); + } + FileStorageWriteError::FailedToConstructTrieIter => { + // This should never happen for a well constructed trie. + // This means that something is seriously wrong, so we error out the whole task. + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::InternalError + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + // Unregister the file. + self.unregister_file(event.file_key.into()).await?; + + return Err(anyhow::anyhow!(format!( + "This is a bug! Failed to construct trie iter for key {:?}.", + event.file_key + ))); + } + }, + } + + Ok(()) + } +} + +/// Handles the `ProcessMspRespondStoringRequest` event. +/// +/// Triggered when there are new storage request(s) to respond to. Normally, storage requests are +/// immidiately rejected if the MSP cannot store the file (e.g. not enough capacity). However, this event +/// is able to respond to storage requests that are either being accepted or rejected either way. +/// +/// The MSP will call the `msp_respond_storage_requests_multiple_buckets` extrinsic on the FileSystem pallet to respond to the +/// storage requests. +impl EventHandler for MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + async fn handle_event(&mut self, event: ProcessMspRespondStoringRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Processing ProcessMspRespondStoringRequest: {:?}", + event.data.respond_storing_requests, + ); + + let forest_root_write_tx = match event.forest_root_write_tx.lock().await.take() { + Some(tx) => tx, + None => { + let err_msg = "CRITICAL❗️❗️ This is a bug! Forest root write tx already taken. This is a critical bug. Please report it to the StorageHub team."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }; + + let own_provider_id = self + .storage_hub_handler + .blockchain + .query_storage_provider_id(None) + .await?; + + let own_msp_id = match own_provider_id { + Some(StorageProviderId::MainStorageProvider(id)) => id, + Some(StorageProviderId::BackupStorageProvider(_)) => { + return Err(anyhow!("Current node account is a Backup Storage Provider. Expected a Main Storage Provider ID.")); + } + None => { + return Err(anyhow!("Failed to get own MSP ID.")); + } + }; + + let mut file_key_responses: HashMap< + H256, + (Vec<(H256, _)>, Vec<(H256, RejectedStorageRequestReason)>), + > = HashMap::new(); + let read_file_storage = self.storage_hub_handler.file_storage.read().await; + + for respond in &event.data.respond_storing_requests { + let bucket_id = match read_file_storage.get_metadata(&respond.file_key) { + Ok(Some(metadata)) => H256(metadata.bucket_id.try_into().unwrap()), + Ok(None) => { + error!(target: LOG_TARGET, "File does not exist for key {:?}. Maybe we forgot to unregister before deleting?", respond.file_key); + continue; + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to get file metadata: {:?}", e); + continue; + } + }; + + let entry = file_key_responses + .entry(bucket_id) + .or_insert_with(|| (Vec::new(), Vec::new())); + + match &respond.response { + MspRespondStorageRequest::Accept => { + let chunks_to_prove = match self + .storage_hub_handler + .blockchain + .query_msp_confirm_chunks_to_prove_for_file(own_msp_id, respond.file_key) + .await + { + Ok(chunks) => chunks, + Err(e) => { + error!(target: LOG_TARGET, "Failed to get chunks to prove: {:?}", e); + continue; + } + }; + + let proof = match read_file_storage + .generate_proof(&respond.file_key, &chunks_to_prove) + { + Ok(p) => p, + Err(e) => { + error!(target: LOG_TARGET, "Failed to generate proof: {:?}", e); + continue; + } + }; + + entry.0.push((respond.file_key, proof)); + } + MspRespondStorageRequest::Reject(reason) => { + entry.1.push((respond.file_key, reason.clone())); + } + } + } + + drop(read_file_storage); + + let mut final_responses: HashMap = HashMap::new(); + + for (bucket_id, (accepts, rejects)) in file_key_responses.iter_mut() { + let fs = match self + .storage_hub_handler + .forest_storage_handler + .get(&bucket_id.as_ref().to_vec()) + .await + { + Some(fs) => fs, + None => { + error!(target: LOG_TARGET, "Failed to get forest storage for bucket {:?}", bucket_id); + continue; + } + }; + + let file_keys: Vec<_> = accepts.iter().map(|(file_key, _)| *file_key).collect(); + + let non_inclusion_forest_proof = match fs.read().await.generate_proof(file_keys) { + Ok(proof) => proof, + Err(e) => { + error!(target: LOG_TARGET, "Failed to generate non-inclusion forest proof: {:?}", e); + continue; + } + }; + + let file_metadatas: Vec<_> = { + let read_file_storage = self.storage_hub_handler.file_storage.read().await; + accepts + .iter() + .filter_map(|(file_key, _)| { + read_file_storage.get_metadata(file_key).ok().flatten() + }) + .collect() + }; + + if let Err(e) = fs.write().await.insert_files_metadata(&file_metadatas) { + error!(target: LOG_TARGET, "Failed to insert file metadata: {:?}", e); + continue; + } + + let response = MspStorageRequestResponse { + accept: if !accepts.is_empty() { + Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: accepts + .clone() + .try_into() + .map_err(|_| anyhow!("Failed to convert accepts to bounded vec"))?, + non_inclusion_forest_proof: non_inclusion_forest_proof.proof, + }) + } else { + None + }, + reject: if !rejects.is_empty() { + Some( + rejects + .clone() + .try_into() + .map_err(|_| anyhow!("Failed to convert rejects to bounded vec"))?, + ) + } else { + None + }, + }; + + final_responses.insert(*bucket_id, response); + } + + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: final_responses + .into_iter() + .collect::>() + .try_into() + .map_err(|_| anyhow!("Failed to convert file key responses to bounded vec"))?, + }, + ); + + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + let _ = forest_root_write_tx.send(()); + + Ok(()) + } +} + +impl MspUploadFileTask +where + FL: FileStorageT, + FSH: MspForestStorageHandlerT, +{ + async fn handle_new_storage_request_event( + &mut self, + event: NewStorageRequest, + ) -> anyhow::Result<()> { + // Construct file metadata. + let metadata = FileMetadata { + owner: >::as_ref(&event.who).to_vec(), + bucket_id: event.bucket_id.as_ref().to_vec(), + file_size: event.size as u64, + fingerprint: event.fingerprint, + location: event.location.to_vec(), + }; + + let own_provider_id = self + .storage_hub_handler + .blockchain + .query_storage_provider_id(None) + .await?; + + let own_msp_id = match own_provider_id { + Some(id) => match id { + StorageProviderId::MainStorageProvider(id) => id, + StorageProviderId::BackupStorageProvider(_) => { + let err_msg = "Current node account is a Backup Storage Provider. Expected a Main Storage Provider ID."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }, + None => { + let err_msg = "Failed to get own MSP ID."; + error!(target: LOG_TARGET, err_msg); + return Err(anyhow!(err_msg)); + } + }; + + let available_capacity = self + .storage_hub_handler + .blockchain + .query_available_storage_capacity(own_msp_id) + .await + .map_err(|e| { + let err_msg = format!("Failed to query available storage capacity: {:?}", e); + error!( + target: LOG_TARGET, + err_msg + ); + anyhow::anyhow!(err_msg) + })?; + + // Increase storage capacity if the available capacity is less than the file size. + if available_capacity < event.size { + warn!( + target: LOG_TARGET, + "Insufficient storage capacity to accept file: {:?}", + event.file_key + ); + + let current_capacity = self + .storage_hub_handler + .blockchain + .query_storage_provider_capacity(own_msp_id) + .await + .map_err(|e| { + let err_msg = format!("Failed to query storage provider capacity: {:?}", e); + error!( + target: LOG_TARGET, + err_msg + ); + anyhow::anyhow!(err_msg) + })?; + + let max_storage_capacity = self + .storage_hub_handler + .provider_config + .max_storage_capacity; + + if max_storage_capacity == current_capacity { + let err_msg = "Reached maximum storage capacity limit. Unable to add more more storage capacity."; + warn!( + target: LOG_TARGET, err_msg + ); + return Err(anyhow::anyhow!(err_msg)); + } + + let new_capacity = self.calculate_capacity(&event, current_capacity)?; + + let call = storage_hub_runtime::RuntimeCall::Providers( + pallet_storage_providers::Call::change_capacity { new_capacity }, + ); + + let earliest_change_capacity_block = self + .storage_hub_handler + .blockchain + .query_earliest_change_capacity_block(own_msp_id) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Failed to query storage provider capacity: {:?}", e + ); + anyhow::anyhow!("Failed to query storage provider capacity: {:?}", e) + })?; + + // Wait for the earliest block where the capacity can be changed. + self.storage_hub_handler + .blockchain + .wait_for_block(earliest_change_capacity_block) + .await?; + + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + info!( + target: LOG_TARGET, + "Increased storage capacity to {:?} bytes", + new_capacity + ); + + let available_capacity = self + .storage_hub_handler + .blockchain + .query_available_storage_capacity(own_msp_id) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Failed to query available storage capacity: {:?}", e + ); + anyhow::anyhow!("Failed to query available storage capacity: {:?}", e) + })?; + + // Reject storage request if the new available capacity is still less than the file size. + if available_capacity < event.size { + let err_msg = "Increased storage capacity is still insufficient to volunteer for file. Rejecting storage request."; + warn!( + target: LOG_TARGET, "{}", err_msg + ); + + // Build extrinsic. + let call = storage_hub_runtime::RuntimeCall::FileSystem( + pallet_file_system::Call::msp_respond_storage_requests_multiple_buckets { + file_key_responses_input: bounded_vec![( + H256(metadata.bucket_id.try_into().map_err(|e| { + let err_msg = + format!("Failed to convert bucket ID to [u8; 32]: {:?}", e); + error!( + target: LOG_TARGET, + err_msg + ); + anyhow::anyhow!(err_msg) + })?), + MspStorageRequestResponse { + accept: None, + reject: Some(bounded_vec![( + H256(event.file_key.into()), + RejectedStorageRequestReason::ReachedMaximumCapacity, + )]) + } + )], + }, + ); + + // Send extrinsic and wait for it to be included in the block. + self.storage_hub_handler + .blockchain + .send_extrinsic(call, Tip::from(0)) + .await? + .with_timeout(Duration::from_secs(60)) + .watch_for_success(&self.storage_hub_handler.blockchain) + .await?; + + return Err(anyhow::anyhow!(err_msg)); + } + } + + // Get the file key. + let file_key: FileKey = metadata + .file_key::>() + .as_ref() + .try_into()?; + + self.file_key_cleanup = Some(file_key.into()); + + // Register the file for upload in the file transfer service. + for peer_id in event.user_peer_ids.iter() { + let peer_id = match std::str::from_utf8(&peer_id.as_slice()) { + Ok(str_slice) => PeerId::from_str(str_slice).map_err(|e| { + error!(target: LOG_TARGET, "Failed to convert peer ID to PeerId: {}", e); + e + })?, + Err(e) => return Err(anyhow!("Failed to convert peer ID to a string: {}", e)), + }; + self.storage_hub_handler + .file_transfer + .register_new_file_peer(peer_id, file_key) + .await + .map_err(|e| anyhow!("Failed to register new file peer: {:?}", e))?; + } + + // Create file in file storage so we can write uploaded chunks as soon as possible. + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + write_file_storage + .insert_file( + metadata.file_key::>(), + metadata, + ) + .map_err(|e| anyhow!("Failed to insert file in file storage: {:?}", e))?; + drop(write_file_storage); + + Ok(()) + } + + /// Calculate the new capacity after adding the required capacity for the file. + /// + /// The new storage capacity will be increased by the jump capacity until it reaches the + /// `max_storage_capacity`. + /// + /// The `max_storage_capacity` is returned if the new capacity exceeds it. + fn calculate_capacity( + &mut self, + event: &NewStorageRequest, + current_capacity: StorageDataUnit, + ) -> Result { + let jump_capacity = self.storage_hub_handler.provider_config.jump_capacity; + let jumps_needed = (event.size + jump_capacity - 1) / jump_capacity; + let jumps = max(jumps_needed, 1); + let bytes_to_add = jumps * jump_capacity; + let required_capacity = current_capacity.checked_add(bytes_to_add).ok_or_else(|| { + anyhow::anyhow!( + "Reached maximum storage capacity limit. Skipping volunteering for file." + ) + })?; + + let max_storage_capacity = self + .storage_hub_handler + .provider_config + .max_storage_capacity; + + let new_capacity = std::cmp::min(required_capacity, max_storage_capacity); + + Ok(new_capacity) + } + + async fn unregister_file(&self, file_key: H256) -> anyhow::Result<()> { + warn!(target: LOG_TARGET, "Unregistering file {:?}", file_key); + + // Unregister the file from the file transfer service. + // The error is ignored, as the file might already be unregistered. + let _ = self + .storage_hub_handler + .file_transfer + .unregister_file(file_key.as_ref().into()) + .await; + + // Delete the file from the file storage. + let mut write_file_storage = self.storage_hub_handler.file_storage.write().await; + + // TODO: Handle error + let _ = write_file_storage.delete_file(&file_key); + + Ok(()) + } + + async fn on_file_complete(&self, file_key: &H256) -> anyhow::Result<()> { + info!(target: LOG_TARGET, "File upload complete ({:?})", file_key); + + // Unregister the file from the file transfer service. + self.storage_hub_handler + .file_transfer + .unregister_file((*file_key).into()) + .await + .map_err(|e| anyhow!("File is not registered. This should not happen!: {:?}", e))?; + + // Queue a request to confirm the storing of the file. + self.storage_hub_handler + .blockchain + .queue_msp_respond_storage_request(RespondStorageRequest::new( + *file_key, + MspRespondStorageRequest::Accept, + )) + .await?; + + Ok(()) + } +} diff --git a/node/src/tasks/user_sends_file.rs b/node/src/tasks/user_sends_file.rs index ab974f5b3..f3d175670 100644 --- a/node/src/tasks/user_sends_file.rs +++ b/node/src/tasks/user_sends_file.rs @@ -1,204 +1,204 @@ -use crate::tasks::{FileStorageT, StorageHubHandler}; -use log::{debug, error, info}; -use sc_network::PeerId; -use shc_actors_framework::event_bus::EventHandler; -use shc_blockchain_service::{ - commands::BlockchainServiceInterface, - events::{AcceptedBspVolunteer, NewStorageRequest}, -}; -use shc_common::types::{FileMetadata, HashT, StorageProofsMerkleTrieLayout}; -use shc_file_transfer_service::commands::FileTransferServiceInterface; -use shc_forest_manager::traits::ForestStorageHandler; -use shp_file_metadata::ChunkId; -use sp_runtime::AccountId32; - -const LOG_TARGET: &str = "user-sends-file-task"; - -/// Handles the events related to users sending a file to be stored by BSPs -/// volunteering for that file. -/// It can serve multiple BSPs volunteering to store each file, since -/// it reacts to every `AcceptedBspVolunteer` from the runtime. -pub struct UserSendsFileTask -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - storage_hub_handler: StorageHubHandler, -} - -impl Clone for UserSendsFileTask -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - fn clone(&self) -> Self { - Self { - storage_hub_handler: self.storage_hub_handler.clone(), - } - } -} - -impl UserSendsFileTask -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - pub fn new(storage_hub_handler: StorageHubHandler) -> Self { - Self { - storage_hub_handler, - } - } -} - -impl EventHandler for UserSendsFileTask -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - /// Reacts to a new storage request from the runtime, which is triggered by a user sending a file to be stored. - /// It generates the file metadata and sends it to the BSPs volunteering to store the file. - async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Handling new storage request from user [{:?}], with location [{:?}]", - event.who, - event.location, - ); - - let file_metadata = FileMetadata { - owner: >::as_ref(&event.who).to_vec(), - bucket_id: event.bucket_id.as_ref().to_vec(), - file_size: event.size.into(), - fingerprint: event.fingerprint, - location: event.location.into_inner(), - }; - - let msp_id = self - .storage_hub_handler - .blockchain - .query_msp_id_of_bucket_id(event.bucket_id) - .await - .map_err(|e| { - anyhow::anyhow!( - "Failed to query MSP ID of bucket ID {:?}\n Error: {:?}", - event.bucket_id, - e - ) - })?; - - info!( - target: LOG_TARGET, - "Successfully sent file metadata to MSP ({}) to store the file [{:?}]", - msp_id, file_metadata.fingerprint, - ); - - Ok(()) - } -} - -impl EventHandler for UserSendsFileTask -where - FL: FileStorageT, - FSH: ForestStorageHandler + Clone + Send + Sync + 'static, -{ - /// Reacts to BSPs volunteering (`AcceptedBspVolunteer` from the runtime) to store the user's file, - /// establishes a connection to each BSPs through the p2p network and sends the file. - /// At this point we assume that the file is merkleised and already in file storage, and - /// for this reason the file transfer to the BSP should not fail unless the p2p connection fails. - async fn handle_event(&mut self, event: AcceptedBspVolunteer) -> anyhow::Result<()> { - info!( - target: LOG_TARGET, - "Handling BSP volunteering to store a file from user [{:?}], with location [{:?}]", - event.owner, - event.location, - ); - - let file_metadata = FileMetadata { - owner: >::as_ref(&event.owner).to_vec(), - bucket_id: event.bucket_id.as_ref().to_vec(), - file_size: event.size.into(), - fingerprint: event.fingerprint, - location: event.location.into_inner(), - }; - - let chunk_count = file_metadata.chunks_count(); - let file_key = file_metadata.file_key::>(); - - // Adds the multiaddresses of the BSP volunteering to store the file to the known addresses of the file transfer service. - // This is required to establish a connection to the BSP. - let mut peer_ids = Vec::new(); - for multiaddress in &event.multiaddresses { - if let Some(peer_id) = PeerId::try_from_multiaddr(&multiaddress) { - if let Err(error) = self - .storage_hub_handler - .file_transfer - .add_known_address(peer_id, multiaddress.clone()) - .await - { - error!(target: LOG_TARGET, "Failed to add known address {:?} for peer {:?} due to {:?}", multiaddress, peer_id, error); - } - peer_ids.push(peer_id); - } - } - - // TODO: Check how we can improve this. - // We could either make sure this scenario doesn't happen beforehand, - // by implementing formatting checks for multiaddresses in the runtime, - // or try to fetch new peer ids from the runtime at this point. - if peer_ids.is_empty() { - info!(target: LOG_TARGET, "No peers were found to receive file {:?}", file_metadata.fingerprint); - } - - // Iterates and tries to send file to peer. - // Breaks loop after first successful attempt, - // since all peer ids belong to the same BSP. - for peer_id in peer_ids { - for chunk_id in 0..chunk_count { - debug!(target: LOG_TARGET, "Trying to send chunk id {:?} of file {:?} to peer {:?}", chunk_id, file_key, peer_id); - let proof = match self - .storage_hub_handler - .file_storage - .read() - .await - .generate_proof(&file_key, &vec![ChunkId::new(chunk_id)]) - { - Ok(proof) => proof, - Err(e) => { - return Err(anyhow::anyhow!( - "Failed to generate proof for chunk id {:?} of file {:?}\n Error: {:?}", - chunk_id, - file_key, - e - )); - } - }; - - let upload_response = self - .storage_hub_handler - .file_transfer - .upload_request(peer_id, file_key.as_ref().into(), proof) - .await; - - match upload_response { - Ok(_) => { - debug!(target: LOG_TARGET, "Successfully uploaded chunk id {:?} of file {:?} to peer {:?}", chunk_id, file_metadata.fingerprint, peer_id); - } - Err(e) => { - error!(target: LOG_TARGET, "Failed to upload chunk_id {:?} to peer {:?}\n Error: {:?}", chunk_id, peer_id, e); - // In case of an error, we break the inner loop - // and try to connect to the next peer id. - break; - } - } - } - info!(target: LOG_TARGET, "Successfully sent file {:?} to peer {:?}", file_metadata.fingerprint, peer_id); - return Ok(()); - } - - // If we reach this point, it means that we couldn't send the file to any of the peers. - return Err(anyhow::anyhow!( - "Failed to send file {:?} to any of the peers", - file_metadata.fingerprint - )); - } -} +use crate::tasks::{FileStorageT, StorageHubHandler}; +use log::{debug, error, info}; +use sc_network::PeerId; +use shc_actors_framework::event_bus::EventHandler; +use shc_blockchain_service::{ + commands::BlockchainServiceInterface, + events::{AcceptedBspVolunteer, NewStorageRequest}, +}; +use shc_common::types::{FileMetadata, HashT, StorageProofsMerkleTrieLayout}; +use shc_file_transfer_service::commands::FileTransferServiceInterface; +use shc_forest_manager::traits::ForestStorageHandler; +use shp_file_metadata::ChunkId; +use sp_runtime::AccountId32; + +const LOG_TARGET: &str = "user-sends-file-task"; + +/// Handles the events related to users sending a file to be stored by BSPs +/// volunteering for that file. +/// It can serve multiple BSPs volunteering to store each file, since +/// it reacts to every `AcceptedBspVolunteer` from the runtime. +pub struct UserSendsFileTask +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + storage_hub_handler: StorageHubHandler, +} + +impl Clone for UserSendsFileTask +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + fn clone(&self) -> Self { + Self { + storage_hub_handler: self.storage_hub_handler.clone(), + } + } +} + +impl UserSendsFileTask +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + pub fn new(storage_hub_handler: StorageHubHandler) -> Self { + Self { + storage_hub_handler, + } + } +} + +impl EventHandler for UserSendsFileTask +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + /// Reacts to a new storage request from the runtime, which is triggered by a user sending a file to be stored. + /// It generates the file metadata and sends it to the BSPs volunteering to store the file. + async fn handle_event(&mut self, event: NewStorageRequest) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Handling new storage request from user [{:?}], with location [{:?}]", + event.who, + event.location, + ); + + let file_metadata = FileMetadata { + owner: >::as_ref(&event.who).to_vec(), + bucket_id: event.bucket_id.as_ref().to_vec(), + file_size: event.size.into(), + fingerprint: event.fingerprint, + location: event.location.into_inner(), + }; + + let msp_id = self + .storage_hub_handler + .blockchain + .query_msp_id_of_bucket_id(event.bucket_id) + .await + .map_err(|e| { + anyhow::anyhow!( + "Failed to query MSP ID of bucket ID {:?}\n Error: {:?}", + event.bucket_id, + e + ) + })?; + + info!( + target: LOG_TARGET, + "Successfully sent file metadata to MSP ({}) to store the file [{:?}]", + msp_id, file_metadata.fingerprint, + ); + + Ok(()) + } +} + +impl EventHandler for UserSendsFileTask +where + FL: FileStorageT, + FSH: ForestStorageHandler + Clone + Send + Sync + 'static, +{ + /// Reacts to BSPs volunteering (`AcceptedBspVolunteer` from the runtime) to store the user's file, + /// establishes a connection to each BSPs through the p2p network and sends the file. + /// At this point we assume that the file is merkleised and already in file storage, and + /// for this reason the file transfer to the BSP should not fail unless the p2p connection fails. + async fn handle_event(&mut self, event: AcceptedBspVolunteer) -> anyhow::Result<()> { + info!( + target: LOG_TARGET, + "Handling BSP volunteering to store a file from user [{:?}], with location [{:?}]", + event.owner, + event.location, + ); + + let file_metadata = FileMetadata { + owner: >::as_ref(&event.owner).to_vec(), + bucket_id: event.bucket_id.as_ref().to_vec(), + file_size: event.size.into(), + fingerprint: event.fingerprint, + location: event.location.into_inner(), + }; + + let chunk_count = file_metadata.chunks_count(); + let file_key = file_metadata.file_key::>(); + + // Adds the multiaddresses of the BSP volunteering to store the file to the known addresses of the file transfer service. + // This is required to establish a connection to the BSP. + let mut peer_ids = Vec::new(); + for multiaddress in &event.multiaddresses { + if let Some(peer_id) = PeerId::try_from_multiaddr(&multiaddress) { + if let Err(error) = self + .storage_hub_handler + .file_transfer + .add_known_address(peer_id, multiaddress.clone()) + .await + { + error!(target: LOG_TARGET, "Failed to add known address {:?} for peer {:?} due to {:?}", multiaddress, peer_id, error); + } + peer_ids.push(peer_id); + } + } + + // TODO: Check how we can improve this. + // We could either make sure this scenario doesn't happen beforehand, + // by implementing formatting checks for multiaddresses in the runtime, + // or try to fetch new peer ids from the runtime at this point. + if peer_ids.is_empty() { + info!(target: LOG_TARGET, "No peers were found to receive file {:?}", file_metadata.fingerprint); + } + + // Iterates and tries to send file to peer. + // Breaks loop after first successful attempt, + // since all peer ids belong to the same BSP. + for peer_id in peer_ids { + for chunk_id in 0..chunk_count { + debug!(target: LOG_TARGET, "Trying to send chunk id {:?} of file {:?} to peer {:?}", chunk_id, file_key, peer_id); + let proof = match self + .storage_hub_handler + .file_storage + .read() + .await + .generate_proof(&file_key, &vec![ChunkId::new(chunk_id)]) + { + Ok(proof) => proof, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to generate proof for chunk id {:?} of file {:?}\n Error: {:?}", + chunk_id, + file_key, + e + )); + } + }; + + let upload_response = self + .storage_hub_handler + .file_transfer + .upload_request(peer_id, file_key.as_ref().into(), proof) + .await; + + match upload_response { + Ok(_) => { + debug!(target: LOG_TARGET, "Successfully uploaded chunk id {:?} of file {:?} to peer {:?}", chunk_id, file_metadata.fingerprint, peer_id); + } + Err(e) => { + error!(target: LOG_TARGET, "Failed to upload chunk_id {:?} to peer {:?}\n Error: {:?}", chunk_id, peer_id, e); + // In case of an error, we break the inner loop + // and try to connect to the next peer id. + break; + } + } + } + info!(target: LOG_TARGET, "Successfully sent file {:?} to peer {:?}", file_metadata.fingerprint, peer_id); + return Ok(()); + } + + // If we reach this point, it means that we couldn't send the file to any of the peers. + return Err(anyhow::anyhow!( + "Failed to send file {:?} to any of the peers", + file_metadata.fingerprint + )); + } +} diff --git a/pallets/bucket-nfts/Cargo.toml b/pallets/bucket-nfts/Cargo.toml index d5039e0c6..c865a271b 100644 --- a/pallets/bucket-nfts/Cargo.toml +++ b/pallets/bucket-nfts/Cargo.toml @@ -1,90 +1,90 @@ -[package] -name = "pallet-bucket-nfts" -description = "Pallet for managing non-fungible tokens (NFTs) in a bucket." -version = "0.1.0" -homepage = { workspace = true } -license = { workspace = true } -authors = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { workspace = true } -num-bigint = { workspace = true } -scale-info = { workspace = true } - -# Substrate -frame-benchmarking = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -pallet-nfts = { workspace = true } -sp-core = { workspace = true } -sp-runtime = { workspace = true } -sp-std = { workspace = true } - -# Local -shp-file-metadata = { workspace = true } -shp-traits = { workspace = true } - -[dev-dependencies] -serde = { workspace = true } - -pallet-file-system = { workspace = true } -pallet-storage-providers = { workspace = true } -pallet-payment-streams = { workspace = true } -shp-constants = { workspace = true } - -# Substrate -sp-io = { workspace = true } -sp-keyring = { workspace = true } -sp-trie = { workspace = true } - -# Frame -pallet-balances = { workspace = true, features = ["std"] } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-file-system/runtime-benchmarks", - "pallet-storage-providers/runtime-benchmarks", - "shp-file-metadata/runtime-benchmarks", - "shp-traits/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-file-system/std", - "pallet-nfts/std", - "pallet-storage-providers/std", - "pallet-payment-streams/std", - "scale-info/std", - "shp-file-metadata/std", - "shp-traits/std", - "sp-core/std", - "sp-io/std", - "sp-keyring/std", - "sp-runtime/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-file-system/try-runtime", - "pallet-storage-providers/try-runtime", - "shp-file-metadata/try-runtime", - "shp-traits/try-runtime", - "sp-runtime/try-runtime", -] +[package] +name = "pallet-bucket-nfts" +description = "Pallet for managing non-fungible tokens (NFTs) in a bucket." +version = "0.1.0" +homepage = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +num-bigint = { workspace = true } +scale-info = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +pallet-nfts = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Local +shp-file-metadata = { workspace = true } +shp-traits = { workspace = true } + +[dev-dependencies] +serde = { workspace = true } + +pallet-file-system = { workspace = true } +pallet-storage-providers = { workspace = true } +pallet-payment-streams = { workspace = true } +shp-constants = { workspace = true } + +# Substrate +sp-io = { workspace = true } +sp-keyring = { workspace = true } +sp-trie = { workspace = true } + +# Frame +pallet-balances = { workspace = true, features = ["std"] } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-file-system/runtime-benchmarks", + "pallet-storage-providers/runtime-benchmarks", + "shp-file-metadata/runtime-benchmarks", + "shp-traits/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-file-system/std", + "pallet-nfts/std", + "pallet-storage-providers/std", + "pallet-payment-streams/std", + "scale-info/std", + "shp-file-metadata/std", + "shp-traits/std", + "sp-core/std", + "sp-io/std", + "sp-keyring/std", + "sp-runtime/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-file-system/try-runtime", + "pallet-storage-providers/try-runtime", + "shp-file-metadata/try-runtime", + "shp-traits/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/bucket-nfts/src/mock.rs b/pallets/bucket-nfts/src/mock.rs index 77dcbb4cd..a99c29496 100644 --- a/pallets/bucket-nfts/src/mock.rs +++ b/pallets/bucket-nfts/src/mock.rs @@ -1,463 +1,463 @@ -use core::marker::PhantomData; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Randomness}, - weights::constants::RocksDbWeight, - BoundedBTreeSet, -}; -use frame_system as system; -use num_bigint::BigUint; -use pallet_nfts::PalletFeatures; -use shp_file_metadata::{ChunkId, FileMetadata}; -use shp_traits::{ - ProofSubmittersInterface, ProofsDealerInterface, ReadUserSolvencyInterface, TrieMutation, - TrieRemoveMutation, -}; -use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; -use sp_keyring::sr25519::Keyring; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, ConvertBack, IdentifyAccount, IdentityLookup, Verify}, - BuildStorage, MultiSignature, SaturatedConversion, -}; -use sp_std::collections::btree_set::BTreeSet; -use sp_trie::{LayoutV1, TrieConfiguration, TrieLayout}; -use system::pallet_prelude::BlockNumberFor; - -type Block = frame_system::mocking::MockBlock; -pub(crate) type BlockNumber = u64; -type Balance = u128; -type Signature = MultiSignature; -type AccountPublic = ::Signer; -type AccountId = ::AccountId; - -const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10; - -// We mock the Randomness trait to use a simple randomness function when testing the pallet -const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumber = 3; -pub struct MockRandomness; -impl Randomness for MockRandomness { - fn random(subject: &[u8]) -> (H256, BlockNumber) { - // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks - - // Concatenate the subject with the block number to get a unique hash for each block - let subject_concat_block = [ - subject, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - - let hashed_subject = blake2_256(&subject_concat_block); - - ( - H256::from_slice(&hashed_subject), - frame_system::Pallet::::block_number() - .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), - ) - } -} - -// Configure a mock runtime to test the pallet. -construct_runtime!( - pub enum Test - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, - BucketNfts: crate::{Pallet, Call, Storage, Event}, - Nfts: pallet_nfts::{Pallet, Call, Storage, Event}, - FileSystem: pallet_file_system::{Pallet, Call, Storage, Event}, - PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; - pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::Providers(pallet_storage_providers::HoldReason::StorageProviderDeposit); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_balances::Config for Test { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxLocks = ConstU32<10>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<10>; -} - -pub(crate) type ThresholdType = u32; - -parameter_types! { - pub const MinWaitForStopStoring: BlockNumber = 1; -} - -pub struct MockProofsDealer; -impl ProofsDealerInterface for MockProofsDealer { - type ProviderId = H256; - type ForestProof = u32; - type KeyProof = u32; - type MerkleHash = H256; - type RandomnessOutput = H256; - type MerkleHashing = BlakeTwo256; - type TickNumber = BlockNumber; - - fn challenge(_key_challenged: &Self::MerkleHash) -> frame_support::dispatch::DispatchResult { - Ok(()) - } - - fn challenge_with_priority( - _key_challenged: &Self::MerkleHash, - _mutation: Option, - ) -> frame_support::dispatch::DispatchResult { - Ok(()) - } - - fn verify_forest_proof( - _who: &Self::ProviderId, - _challenges: &[Self::MerkleHash], - _proof: &Self::ForestProof, - ) -> Result, sp_runtime::DispatchError> { - Ok(BTreeSet::new()) - } - - fn verify_generic_forest_proof( - _root: &Self::MerkleHash, - _challenges: &[Self::MerkleHash], - _proof: &Self::ForestProof, - ) -> Result, sp_runtime::DispatchError> { - Ok(BTreeSet::new()) - } - - fn verify_key_proof( - _key: &Self::MerkleHash, - _challenges: &[Self::MerkleHash], - _proof: &Self::KeyProof, - ) -> Result, sp_runtime::DispatchError> { - Ok(BTreeSet::new()) - } - - fn generate_challenges_from_seed( - _seed: Self::RandomnessOutput, - _provider_id: &Self::ProviderId, - _count: u32, - ) -> Vec { - Vec::new() - } - - fn apply_delta( - _commitment: &Self::MerkleHash, - _mutations: &[(Self::MerkleHash, TrieMutation)], - _proof: &Self::ForestProof, - ) -> Result { - Ok(H256::default()) - } - - fn generic_apply_delta( - _root: &Self::MerkleHash, - _mutations: &[(Self::MerkleHash, TrieMutation)], - _proof: &Self::ForestProof, - ) -> Result { - Ok(H256::default()) - } - - fn initialise_challenge_cycle( - _who: &Self::ProviderId, - ) -> frame_support::dispatch::DispatchResult { - Ok(()) - } - - fn get_current_tick() -> Self::TickNumber { - System::block_number() - } -} - -impl pallet_file_system::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Providers = Providers; - type ProofDealer = MockProofsDealer; - type PaymentStreams = PaymentStreams; - type UserSolvency = MockUserSolvency; - type Fingerprint = H256; - type ReplicationTargetType = u32; - type ThresholdType = ThresholdType; - type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; - type HashToThresholdType = HashToThresholdTypeConverter; - type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; - type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; - type Currency = Balances; - type Nfts = Nfts; - type CollectionInspector = BucketNfts; - type MaxBspsPerStorageRequest = ConstU32<5>; - type MaxBatchConfirmStorageRequests = ConstU32<10>; - type MaxBatchMspRespondStorageRequests = ConstU32<10>; - type MaxFilePathSize = ConstU32<512u32>; - type MaxPeerIdSize = ConstU32<100>; - type MaxNumberOfPeerIds = MaxNumberOfPeerIds; - type MaxDataServerMultiAddresses = ConstU32<5>; - type MaxExpiredItemsInBlock = ConstU32<100u32>; - type StorageRequestTtl = ConstU32<40u32>; - type PendingFileDeletionRequestTtl = ConstU32<40u32>; - type MoveBucketRequestTtl = ConstU32<40u32>; - type MaxUserPendingDeletionRequests = ConstU32<5u32>; - type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; - type MinWaitForStopStoring = MinWaitForStopStoring; -} - -pub struct MockUserSolvency; -impl ReadUserSolvencyInterface for MockUserSolvency { - type AccountId = AccountId; - - fn is_user_insolvent(_user_account: &Self::AccountId) -> bool { - false - } -} - -parameter_types! { - pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); -} - -impl pallet_nfts::Config for Test { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u128; - type ItemId = u128; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Locker = (); - type CollectionDeposit = ConstU128<2>; - type ItemDeposit = ConstU128<1>; - type MetadataDepositBase = ConstU128<1>; - type AttributeDepositBase = ConstU128<1>; - type DepositPerByte = ConstU128<1>; - type StringLimit = ConstU32<50>; - type KeyLimit = ConstU32<50>; - type ValueLimit = ConstU32<50>; - type ApprovalsLimit = ConstU32<10>; - type ItemAttributesApprovalsLimit = ConstU32<2>; - type MaxTips = ConstU32<10>; - type MaxDeadlineDuration = ConstU64<10000>; - type MaxAttributesPerCall = ConstU32<2>; - type Features = Features; - type OffchainSignature = Signature; - type OffchainPublic = AccountPublic; - type WeightInfo = (); - pallet_nfts::runtime_benchmarks_enabled! { - type Helper = (); - } -} - -// Payment streams pallet: -impl pallet_payment_streams::Config for Test { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = Providers; - type RuntimeHoldReason = RuntimeHoldReason; - type Units = u64; - type NewStreamDeposit = ConstU64<10>; - type UserWithoutFundsCooldown = ConstU64<100>; - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = MockSubmittingProviders; -} -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; -impl Convert, Balance> for BlockNumberToBalance { - fn convert(block_number: BlockNumberFor) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -parameter_types! { - pub const MaxNumberOfPeerIds: u32 = 100; - pub const MaxMultiAddressSize: u32 = 100; - pub const MaxMultiAddressAmount: u32 = 5; -} -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} - -impl pallet_storage_providers::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = MockRandomness; - type PaymentStreams = PaymentStreams; - type FileMetadataManager = FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = u64; - type SpCount = u32; - type MerklePatriciaRoot = H256; - type ValuePropId = H256; - type ReadAccessGroupId = ::CollectionId; - type ProvidersProofSubmitters = MockSubmittingProviders; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = ConstU128<10>; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = MaxMultiAddressSize; - type MaxMultiAddressAmount = MaxMultiAddressAmount; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = ConstU128<10>; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; - type MinBlocksBetweenCapacityChanges = ConstU64<10>; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = ConstU128<10>; - type StartingReputationWeight = ConstU32<1>; -} - -// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. -pub struct MockSubmittingProviders; -impl ProofSubmittersInterface for MockSubmittingProviders { - type ProviderId = ::Hash; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = ConstU32<1000>; - fn get_proof_submitters_for_tick( - _block_number: &Self::TickNumber, - ) -> Option> { - None - } - - fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { - None - } - - fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} -} - -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId { - AccountId::new([0; 32]) - } -} - -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Buckets = Providers; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (Keyring::Alice.to_account_id(), 1_000_000_000_000_000), - (Keyring::Bob.to_account_id(), 1_000_000_000_000_000), - (Keyring::Charlie.to_account_id(), 1_000_000_000_000_000), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -// Converter from the ThresholdType to the BlockNumber type and vice versa. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct ThresholdTypeToBlockNumberConverter; - -impl Convert> for ThresholdTypeToBlockNumberConverter { - fn convert(threshold: ThresholdType) -> BlockNumberFor { - threshold.saturated_into() - } -} - -impl ConvertBack> for ThresholdTypeToBlockNumberConverter { - fn convert_back(block_number: BlockNumberFor) -> ThresholdType { - block_number.saturated_into() - } -} - -/// Converter from the [`Hash`] type to the [`ThresholdType`]. -pub struct HashToThresholdTypeConverter; -impl Convert<::Hash, ThresholdType> for HashToThresholdTypeConverter { - fn convert(hash: ::Hash) -> ThresholdType { - // Get the hash as bytes - let hash_bytes = hash.as_ref(); - - // Get the 4 least significant bytes of the hash and interpret them as an u32 - let truncated_hash_bytes: [u8; 4] = - hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); - - ThresholdType::from_be_bytes(truncated_hash_bytes) - } -} - -// Converter from the MerkleHash (H256) type to the RandomnessOutput type. -pub struct MerkleHashToRandomnessOutputConverter; - -impl Convert for MerkleHashToRandomnessOutputConverter { - fn convert(hash: H256) -> H256 { - hash - } -} - -// Converter from the ChunkId type to the MerkleHash (H256) type. -pub struct ChunkIdToMerkleHashConverter; - -impl Convert for ChunkIdToMerkleHashConverter { - fn convert(chunk_id: ChunkId) -> H256 { - let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); - let mut bytes = chunk_id_biguint.to_bytes_be(); - - // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros - if bytes.len() < 32 { - let mut padded_bytes = vec![0u8; 32 - bytes.len()]; - padded_bytes.extend(bytes); - bytes = padded_bytes; - } - - H256::from_slice(&bytes) - } -} +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, Everything, Randomness}, + weights::constants::RocksDbWeight, + BoundedBTreeSet, +}; +use frame_system as system; +use num_bigint::BigUint; +use pallet_nfts::PalletFeatures; +use shp_file_metadata::{ChunkId, FileMetadata}; +use shp_traits::{ + ProofSubmittersInterface, ProofsDealerInterface, ReadUserSolvencyInterface, TrieMutation, + TrieRemoveMutation, +}; +use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; +use sp_keyring::sr25519::Keyring; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, ConvertBack, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, SaturatedConversion, +}; +use sp_std::collections::btree_set::BTreeSet; +use sp_trie::{LayoutV1, TrieConfiguration, TrieLayout}; +use system::pallet_prelude::BlockNumberFor; + +type Block = frame_system::mocking::MockBlock; +pub(crate) type BlockNumber = u64; +type Balance = u128; +type Signature = MultiSignature; +type AccountPublic = ::Signer; +type AccountId = ::AccountId; + +const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10; + +// We mock the Randomness trait to use a simple randomness function when testing the pallet +const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumber = 3; +pub struct MockRandomness; +impl Randomness for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumber) { + // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks + + // Concatenate the subject with the block number to get a unique hash for each block + let subject_concat_block = [ + subject, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + + let hashed_subject = blake2_256(&subject_concat_block); + + ( + H256::from_slice(&hashed_subject), + frame_system::Pallet::::block_number() + .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), + ) + } +} + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, + BucketNfts: crate::{Pallet, Call, Storage, Event}, + Nfts: pallet_nfts::{Pallet, Call, Storage, Event}, + FileSystem: pallet_file_system::{Pallet, Call, Storage, Event}, + PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; + pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::Providers(pallet_storage_providers::HoldReason::StorageProviderDeposit); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<10>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<10>; +} + +pub(crate) type ThresholdType = u32; + +parameter_types! { + pub const MinWaitForStopStoring: BlockNumber = 1; +} + +pub struct MockProofsDealer; +impl ProofsDealerInterface for MockProofsDealer { + type ProviderId = H256; + type ForestProof = u32; + type KeyProof = u32; + type MerkleHash = H256; + type RandomnessOutput = H256; + type MerkleHashing = BlakeTwo256; + type TickNumber = BlockNumber; + + fn challenge(_key_challenged: &Self::MerkleHash) -> frame_support::dispatch::DispatchResult { + Ok(()) + } + + fn challenge_with_priority( + _key_challenged: &Self::MerkleHash, + _mutation: Option, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } + + fn verify_forest_proof( + _who: &Self::ProviderId, + _challenges: &[Self::MerkleHash], + _proof: &Self::ForestProof, + ) -> Result, sp_runtime::DispatchError> { + Ok(BTreeSet::new()) + } + + fn verify_generic_forest_proof( + _root: &Self::MerkleHash, + _challenges: &[Self::MerkleHash], + _proof: &Self::ForestProof, + ) -> Result, sp_runtime::DispatchError> { + Ok(BTreeSet::new()) + } + + fn verify_key_proof( + _key: &Self::MerkleHash, + _challenges: &[Self::MerkleHash], + _proof: &Self::KeyProof, + ) -> Result, sp_runtime::DispatchError> { + Ok(BTreeSet::new()) + } + + fn generate_challenges_from_seed( + _seed: Self::RandomnessOutput, + _provider_id: &Self::ProviderId, + _count: u32, + ) -> Vec { + Vec::new() + } + + fn apply_delta( + _commitment: &Self::MerkleHash, + _mutations: &[(Self::MerkleHash, TrieMutation)], + _proof: &Self::ForestProof, + ) -> Result { + Ok(H256::default()) + } + + fn generic_apply_delta( + _root: &Self::MerkleHash, + _mutations: &[(Self::MerkleHash, TrieMutation)], + _proof: &Self::ForestProof, + ) -> Result { + Ok(H256::default()) + } + + fn initialise_challenge_cycle( + _who: &Self::ProviderId, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) + } + + fn get_current_tick() -> Self::TickNumber { + System::block_number() + } +} + +impl pallet_file_system::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Providers = Providers; + type ProofDealer = MockProofsDealer; + type PaymentStreams = PaymentStreams; + type UserSolvency = MockUserSolvency; + type Fingerprint = H256; + type ReplicationTargetType = u32; + type ThresholdType = ThresholdType; + type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; + type HashToThresholdType = HashToThresholdTypeConverter; + type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; + type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; + type Currency = Balances; + type Nfts = Nfts; + type CollectionInspector = BucketNfts; + type MaxBspsPerStorageRequest = ConstU32<5>; + type MaxBatchConfirmStorageRequests = ConstU32<10>; + type MaxBatchMspRespondStorageRequests = ConstU32<10>; + type MaxFilePathSize = ConstU32<512u32>; + type MaxPeerIdSize = ConstU32<100>; + type MaxNumberOfPeerIds = MaxNumberOfPeerIds; + type MaxDataServerMultiAddresses = ConstU32<5>; + type MaxExpiredItemsInBlock = ConstU32<100u32>; + type StorageRequestTtl = ConstU32<40u32>; + type PendingFileDeletionRequestTtl = ConstU32<40u32>; + type MoveBucketRequestTtl = ConstU32<40u32>; + type MaxUserPendingDeletionRequests = ConstU32<5u32>; + type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; + type MinWaitForStopStoring = MinWaitForStopStoring; +} + +pub struct MockUserSolvency; +impl ReadUserSolvencyInterface for MockUserSolvency { + type AccountId = AccountId; + + fn is_user_insolvent(_user_account: &Self::AccountId) -> bool { + false + } +} + +parameter_types! { + pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); +} + +impl pallet_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u128; + type ItemId = u128; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Locker = (); + type CollectionDeposit = ConstU128<2>; + type ItemDeposit = ConstU128<1>; + type MetadataDepositBase = ConstU128<1>; + type AttributeDepositBase = ConstU128<1>; + type DepositPerByte = ConstU128<1>; + type StringLimit = ConstU32<50>; + type KeyLimit = ConstU32<50>; + type ValueLimit = ConstU32<50>; + type ApprovalsLimit = ConstU32<10>; + type ItemAttributesApprovalsLimit = ConstU32<2>; + type MaxTips = ConstU32<10>; + type MaxDeadlineDuration = ConstU64<10000>; + type MaxAttributesPerCall = ConstU32<2>; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; + type WeightInfo = (); + pallet_nfts::runtime_benchmarks_enabled! { + type Helper = (); + } +} + +// Payment streams pallet: +impl pallet_payment_streams::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = Providers; + type RuntimeHoldReason = RuntimeHoldReason; + type Units = u64; + type NewStreamDeposit = ConstU64<10>; + type UserWithoutFundsCooldown = ConstU64<100>; + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = MockSubmittingProviders; +} +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; +impl Convert, Balance> for BlockNumberToBalance { + fn convert(block_number: BlockNumberFor) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +parameter_types! { + pub const MaxNumberOfPeerIds: u32 = 100; + pub const MaxMultiAddressSize: u32 = 100; + pub const MaxMultiAddressAmount: u32 = 5; +} +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} + +impl pallet_storage_providers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = MockRandomness; + type PaymentStreams = PaymentStreams; + type FileMetadataManager = FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = u64; + type SpCount = u32; + type MerklePatriciaRoot = H256; + type ValuePropId = H256; + type ReadAccessGroupId = ::CollectionId; + type ProvidersProofSubmitters = MockSubmittingProviders; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = ConstU128<10>; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = MaxMultiAddressSize; + type MaxMultiAddressAmount = MaxMultiAddressAmount; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = ConstU128<10>; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; + type MinBlocksBetweenCapacityChanges = ConstU64<10>; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = ConstU128<10>; + type StartingReputationWeight = ConstU32<1>; +} + +// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. +pub struct MockSubmittingProviders; +impl ProofSubmittersInterface for MockSubmittingProviders { + type ProviderId = ::Hash; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = ConstU32<1000>; + fn get_proof_submitters_for_tick( + _block_number: &Self::TickNumber, + ) -> Option> { + None + } + + fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { + None + } + + fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} +} + +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId { + AccountId::new([0; 32]) + } +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Buckets = Providers; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (Keyring::Alice.to_account_id(), 1_000_000_000_000_000), + (Keyring::Bob.to_account_id(), 1_000_000_000_000_000), + (Keyring::Charlie.to_account_id(), 1_000_000_000_000_000), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +// Converter from the ThresholdType to the BlockNumber type and vice versa. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct ThresholdTypeToBlockNumberConverter; + +impl Convert> for ThresholdTypeToBlockNumberConverter { + fn convert(threshold: ThresholdType) -> BlockNumberFor { + threshold.saturated_into() + } +} + +impl ConvertBack> for ThresholdTypeToBlockNumberConverter { + fn convert_back(block_number: BlockNumberFor) -> ThresholdType { + block_number.saturated_into() + } +} + +/// Converter from the [`Hash`] type to the [`ThresholdType`]. +pub struct HashToThresholdTypeConverter; +impl Convert<::Hash, ThresholdType> for HashToThresholdTypeConverter { + fn convert(hash: ::Hash) -> ThresholdType { + // Get the hash as bytes + let hash_bytes = hash.as_ref(); + + // Get the 4 least significant bytes of the hash and interpret them as an u32 + let truncated_hash_bytes: [u8; 4] = + hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); + + ThresholdType::from_be_bytes(truncated_hash_bytes) + } +} + +// Converter from the MerkleHash (H256) type to the RandomnessOutput type. +pub struct MerkleHashToRandomnessOutputConverter; + +impl Convert for MerkleHashToRandomnessOutputConverter { + fn convert(hash: H256) -> H256 { + hash + } +} + +// Converter from the ChunkId type to the MerkleHash (H256) type. +pub struct ChunkIdToMerkleHashConverter; + +impl Convert for ChunkIdToMerkleHashConverter { + fn convert(chunk_id: ChunkId) -> H256 { + let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); + let mut bytes = chunk_id_biguint.to_bytes_be(); + + // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros + if bytes.len() < 32 { + let mut padded_bytes = vec![0u8; 32 - bytes.len()]; + padded_bytes.extend(bytes); + bytes = padded_bytes; + } + + H256::from_slice(&bytes) + } +} diff --git a/pallets/file-system/runtime-api/src/lib.rs b/pallets/file-system/runtime-api/src/lib.rs index 7e8e2fac4..15408cb42 100644 --- a/pallets/file-system/runtime-api/src/lib.rs +++ b/pallets/file-system/runtime-api/src/lib.rs @@ -1,54 +1,54 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Codec, Decode, Encode}; -use scale_info::prelude::vec::Vec; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; - -/// Error type for the `query_earliest_file_volunteer_tick` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryFileEarliestVolunteerTickError { - FailedToEncodeFingerprint, - FailedToEncodeBsp, - ThresholdArithmeticError, - StorageRequestNotFound, - InternalError, -} - -/// Error type for the `query_bsp_confirm_chunks_to_prove_for_file` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryBspConfirmChunksToProveForFileError { - StorageRequestNotFound, - ConfirmChunks(QueryConfirmChunksToProveForFileError), - InternalError, -} - -/// Error type for the `query_msp_confirm_chunks_to_prove_for_file` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryMspConfirmChunksToProveForFileError { - StorageRequestNotFound, - ConfirmChunks(QueryConfirmChunksToProveForFileError), - InternalError, -} - -/// Error type for the `query_confirm_chunks_to_prove_for_file`. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryConfirmChunksToProveForFileError { - ChallengedChunkToChunkIdError, -} - -sp_api::decl_runtime_apis! { - #[api_version(1)] - pub trait FileSystemApi - where - BackupStorageProviderId: Codec, - MainStorageProviderId: Codec, - FileKey: Codec, - TickNumber: Codec, - ChunkId: Codec, - { - fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: FileKey) -> Result; - fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: FileKey) -> Result, QueryBspConfirmChunksToProveForFileError>; - fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: FileKey) -> Result, QueryMspConfirmChunksToProveForFileError>; - } -} +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Codec, Decode, Encode}; +use scale_info::prelude::vec::Vec; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; + +/// Error type for the `query_earliest_file_volunteer_tick` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryFileEarliestVolunteerTickError { + FailedToEncodeFingerprint, + FailedToEncodeBsp, + ThresholdArithmeticError, + StorageRequestNotFound, + InternalError, +} + +/// Error type for the `query_bsp_confirm_chunks_to_prove_for_file` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryBspConfirmChunksToProveForFileError { + StorageRequestNotFound, + ConfirmChunks(QueryConfirmChunksToProveForFileError), + InternalError, +} + +/// Error type for the `query_msp_confirm_chunks_to_prove_for_file` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryMspConfirmChunksToProveForFileError { + StorageRequestNotFound, + ConfirmChunks(QueryConfirmChunksToProveForFileError), + InternalError, +} + +/// Error type for the `query_confirm_chunks_to_prove_for_file`. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryConfirmChunksToProveForFileError { + ChallengedChunkToChunkIdError, +} + +sp_api::decl_runtime_apis! { + #[api_version(1)] + pub trait FileSystemApi + where + BackupStorageProviderId: Codec, + MainStorageProviderId: Codec, + FileKey: Codec, + TickNumber: Codec, + ChunkId: Codec, + { + fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: FileKey) -> Result; + fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: FileKey) -> Result, QueryBspConfirmChunksToProveForFileError>; + fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: FileKey) -> Result, QueryMspConfirmChunksToProveForFileError>; + } +} diff --git a/pallets/file-system/src/lib.rs b/pallets/file-system/src/lib.rs index ca7d82573..cd49a4d8f 100644 --- a/pallets/file-system/src/lib.rs +++ b/pallets/file-system/src/lib.rs @@ -1,1234 +1,1234 @@ -//! # File System Pallet -//! -//! - [`Config`] -//! - [`Call`] -//! -//! ## Overview -//! -//! The file system pallet provides the following functionality: -//! -//! - Tracks Merkle Forest roots for every MSP and BSP -//! - Manages storage buckets -//! - Exposes all file related actions a user or storage provider can execute -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! - `issue_storage_request`: Issue a new storage request to store a file. -//! - `volunteer_bsp`: BSP volunteers to store a file for a given storage request. -//! -//! ## Hooks -//! -//! - `on_idle`: Cleanup all expired storage requests. -//! -//! ## Dependencies -//! -//! TODO -#![cfg_attr(not(feature = "std"), no_std)] - -pub use pallet::*; - -pub mod types; -mod utils; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - -#[frame_support::pallet] -pub mod pallet { - use super::types::*; - use codec::HasCompact; - use frame_support::{ - dispatch::DispatchResult, - pallet_prelude::{ValueQuery, *}, - sp_runtime::traits::{CheckEqual, Convert, MaybeDisplay, SimpleBitOps}, - traits::{ - fungible::*, - nonfungibles_v2::{Create, Inspect as NonFungiblesInspect}, - }, - Blake2_128Concat, - }; - use frame_system::pallet_prelude::{BlockNumberFor, *}; - use scale_info::prelude::fmt::Debug; - use shp_file_metadata::ChunkId; - use sp_runtime::{ - traits::{ - Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConvertBack, One, Saturating, - Zero, - }, - BoundedVec, - }; - - #[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>; - - /// The trait for reading and mutating Storage Provider and Bucket data. - type Providers: shp_traits::ReadProvidersInterface - + shp_traits::MutateProvidersInterface< - MerkleHash = ::MerkleHash, - ProviderId = ::ProviderId, - > + shp_traits::ReadStorageProvidersInterface< - ProviderId = ::ProviderId, - > + shp_traits::MutateStorageProvidersInterface< - ProviderId = ::ProviderId, - StorageDataUnit = ::StorageDataUnit, - > + shp_traits::ReadBucketsInterface< - AccountId = Self::AccountId, - BucketId = ::ProviderId, - MerkleHash = ::MerkleHash, - ProviderId = ::ProviderId, - ReadAccessGroupId = CollectionIdFor, - StorageDataUnit = ::StorageDataUnit, - > + shp_traits::MutateBucketsInterface< - AccountId = Self::AccountId, - BucketId = ::BucketId, - MerkleHash = ::MerkleHash, - ProviderId = ::ProviderId, - ReadAccessGroupId = CollectionIdFor, - StorageDataUnit = ::StorageDataUnit, - >; - - /// The trait for issuing challenges and verifying proofs. - type ProofDealer: shp_traits::ProofsDealerInterface< - ProviderId = ::ProviderId, - MerkleHash = ::MerkleHash, - >; - - /// The trait to create, update, delete and inspect payment streams. - type PaymentStreams: shp_traits::PaymentStreamsInterface< - AccountId = Self::AccountId, - ProviderId = ::ProviderId, - Units = ::StorageDataUnit, - >; - - /// The trait for checking user solvency in the system - type UserSolvency: shp_traits::ReadUserSolvencyInterface; - - /// Type for identifying a file, generally a hash. - type Fingerprint: Parameter - + Member - + MaybeSerializeDeserialize - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen; - - /// Type representing the storage request bsps size type. - type ReplicationTargetType: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + From - + Into - + Into - + Copy - + MaxEncodedLen - + HasCompact - + Default - + scale_info::TypeInfo - + MaybeSerializeDeserialize - + CheckedAdd - + One - + Saturating - + PartialOrd - + Zero; - - /// Type representing the threshold a BSP must meet to be eligible to volunteer to store a file. - type ThresholdType: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + Default - + MaybeDisplay - + From - + From<::ReputationWeight> - + From - + Copy - + MaxEncodedLen - + Decode - + Saturating - + CheckedMul - + CheckedDiv - + CheckedAdd - + CheckedSub - + PartialOrd - + Bounded - + One - + Zero; - - /// The type to convert a threshold to a tick number. - /// - /// For more information on what "ticks" are, see the [Proofs Dealer pallet](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/proofs-dealer/README.md). - type ThresholdTypeToTickNumber: ConvertBack< - Self::ThresholdType, - ::TickNumber, - >; - - /// The type to convert a hash to a threshold. - type HashToThresholdType: Convert; - - /// The type to convert a MerkleHash to a RandomnessOutput. - type MerkleHashToRandomnessOutput: Convert< - ::MerkleHash, - ::RandomnessOutput, - >; - - /// The type to convert a ChunkId to a MerkleHash - type ChunkIdToMerkleHash: Convert< - ChunkId, - ::MerkleHash, - >; - - /// The currency mechanism, used for paying for reserves. - type Currency: Inspect - + Mutate - + hold::Balanced - + freeze::Inspect - + freeze::Mutate; - - /// Registry for minted NFTs. - type Nfts: NonFungiblesInspect - + Create>; - - /// Collection inspector - type CollectionInspector: shp_traits::InspectCollections< - CollectionId = CollectionIdFor, - >; - - /// Maximum number of SPs (MSP + BSPs) that can store a file. - /// - /// This is used to limit the number of BSPs storing a file and claiming rewards for it. - /// If this number is too high, then the reward for storing a file might be to diluted and pointless to store. - #[pallet::constant] - type MaxBspsPerStorageRequest: Get; - - /// Maximum batch of storage requests that can be confirmed at once when calling `bsp_confirm_storing`. - #[pallet::constant] - type MaxBatchConfirmStorageRequests: Get; - - /// Maximum batch of storage requests that can be responded to at once when calling `msp_respond_storage_requests_multiple_buckets`. - #[pallet::constant] - type MaxBatchMspRespondStorageRequests: Get; - - /// Maximum byte size of a file path. - #[pallet::constant] - type MaxFilePathSize: Get; - - /// Maximum byte size of a peer id. - #[pallet::constant] - type MaxPeerIdSize: Get; - - /// Maximum number of peer ids for a storage request. - #[pallet::constant] - type MaxNumberOfPeerIds: Get; - - /// Maximum number of multiaddresses for a storage request. - #[pallet::constant] - type MaxDataServerMultiAddresses: Get; - - /// Maximum number of expired items (per type) to clean up in a single block. - #[pallet::constant] - type MaxExpiredItemsInBlock: Get; - - /// Time-to-live for a storage request. - #[pallet::constant] - type StorageRequestTtl: Get; - - /// Time-to-live for a pending file deletion request, after which a priority challenge is sent out to enforce the deletion. - #[pallet::constant] - type PendingFileDeletionRequestTtl: Get; - - /// Time-to-live for a move bucket request, after which the request is considered expired. - #[pallet::constant] - type MoveBucketRequestTtl: Get; - - /// Maximum number of file deletion requests a user can have pending. - #[pallet::constant] - type MaxUserPendingDeletionRequests: Get; - - /// Maximum number of move bucket requests a user can have pending. - #[pallet::constant] - type MaxUserPendingMoveBucketRequests: Get; - - /// Number of blocks required to pass between a BSP requesting to stop storing a file and it being able to confirm to stop storing it. - #[pallet::constant] - type MinWaitForStopStoring: Get>; - } - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - pub type StorageRequests = - StorageMap<_, Blake2_128Concat, MerkleHash, StorageRequestMetadata>; - - /// A double map from storage request to BSP `AccountId`s that volunteered to store the file. - /// - /// Any BSP under a storage request prefix is considered to be a volunteer and can be removed at any time. - /// Once a BSP submits a valid proof to the via the `bsp_confirm_storing` extrinsic, the `confirmed` field in [`StorageRequestBspsMetadata`] will be set to `true`. - /// - /// When a storage request is expired or removed, the corresponding storage request prefix in this map is removed. - #[pallet::storage] - pub type StorageRequestBsps = StorageDoubleMap< - _, - Blake2_128Concat, - MerkleHash, - Blake2_128Concat, - ProviderIdFor, - StorageRequestBspsMetadata, - OptionQuery, - >; - - /// Bookkeeping of the buckets containing open storage requests. - #[pallet::storage] - pub type BucketsWithStorageRequests = StorageDoubleMap< - _, - Blake2_128Concat, - BucketIdFor, - Blake2_128Concat, - MerkleHash, - (), - OptionQuery, - >; - - /// A map of blocks to expired storage requests. - #[pallet::storage] - pub type StorageRequestExpirations = StorageMap< - _, - Blake2_128Concat, - BlockNumberFor, - BoundedVec, T::MaxExpiredItemsInBlock>, - ValueQuery, - >; - - /// A map of blocks to expired file deletion requests. - #[pallet::storage] - pub type FileDeletionRequestExpirations = StorageMap< - _, - Blake2_128Concat, - BlockNumberFor, - BoundedVec, T::MaxExpiredItemsInBlock>, - ValueQuery, - >; - - /// A map of blocks to expired move bucket requests. - #[pallet::storage] - pub type MoveBucketRequestExpirations = StorageMap< - _, - Blake2_128Concat, - BlockNumberFor, - BoundedVec<(ProviderIdFor, BucketIdFor), T::MaxExpiredItemsInBlock>, - ValueQuery, - >; - - /// A pointer to the earliest available block to insert a new storage request expiration. - /// - /// This should always be greater or equal than current block + [`Config::StorageRequestTtl`]. - #[pallet::storage] - pub type NextAvailableStorageRequestExpirationBlock = - StorageValue<_, BlockNumberFor, ValueQuery>; - - /// A pointer to the earliest available block to insert a new file deletion request expiration. - /// - /// This should always be greater or equal than current block + [`Config::PendingFileDeletionRequestTtl`]. - #[pallet::storage] - pub type NextAvailableFileDeletionRequestExpirationBlock = - StorageValue<_, BlockNumberFor, ValueQuery>; - - /// A pointer to the earliest available block to insert a new move bucket request expiration. - /// - /// This should always be greater or equal than current block + [`Config::MoveBucketRequestTtl`]. - #[pallet::storage] - pub type NextAvailableMoveBucketRequestExpirationBlock = - StorageValue<_, BlockNumberFor, ValueQuery>; - - /// A pointer to the starting block to clean up expired storage requests. - /// - /// If this block is behind the current block number, the cleanup algorithm in `on_idle` will - /// attempt to accelerate this block pointer as close to or up to the current block number. This - /// will execute provided that there is enough remaining weight to do so. - #[pallet::storage] - pub type NextStartingBlockToCleanUp = StorageValue<_, BlockNumberFor, ValueQuery>; - - /// Pending file deletion requests. - /// - /// A mapping from a user account id to a list of pending file deletion requests, holding a tuple of the file key and bucket id. - #[pallet::storage] - pub type PendingFileDeletionRequests = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BoundedVec<(MerkleHash, BucketIdFor), T::MaxUserPendingDeletionRequests>, - ValueQuery, - >; - - /// Pending file stop storing requests. - /// - /// A double mapping from BSP IDs to a list of file keys pending stop storing requests to the block in which those requests were opened - /// and the proven size of the file. - /// The block number is used to avoid BSPs being able to stop storing files immediately which would allow them to avoid challenges - /// of missing files. The size is to be able to decrease their used capacity when they confirm to stop storing the file. - #[pallet::storage] - pub type PendingStopStoringRequests = StorageDoubleMap< - _, - Blake2_128Concat, - ProviderIdFor, - Blake2_128Concat, - MerkleHash, - (BlockNumberFor, StorageData), - >; - - /// Pending move bucket requests. - /// - /// A double mapping from MSP IDs to a list of bucket IDs which they can accept or decline to take over. - /// The value is the user who requested the move. - #[pallet::storage] - pub type PendingMoveBucketRequests = StorageDoubleMap< - _, - Blake2_128Concat, - ProviderIdFor, - Blake2_128Concat, - BucketIdFor, - MoveBucketRequestMetadata, - >; - - /// BSP data servers for move bucket requests. - #[pallet::storage] - pub type DataServersForMoveBucket = StorageDoubleMap< - _, - Blake2_128Concat, - BucketIdFor, - Blake2_128Concat, - ProviderIdFor, - (), - >; - - /// Bookkeeping of buckets that are pending to be moved to a new MSP. - #[pallet::storage] - pub type PendingBucketsToMove = - StorageMap<_, Blake2_128Concat, BucketIdFor, (), ValueQuery>; - - /// Number of BSPs required to fulfill a storage request - /// - /// This is also used as a default value if the BSPs required are not specified when creating a storage request. - #[pallet::storage] - pub type ReplicationTarget = StorageValue<_, ReplicationTargetType, ValueQuery>; - - /// Number of ticks until all BSPs would reach the [`Config::MaximumThreshold`] to ensure that all BSPs are able to volunteer. - #[pallet::storage] - pub type TickRangeToMaximumThreshold = StorageValue<_, TickNumber, ValueQuery>; - - #[pallet::genesis_config] - pub struct GenesisConfig { - pub replication_target: ReplicationTargetType, - pub tick_range_to_maximum_threshold: TickNumber, - } - - impl Default for GenesisConfig { - fn default() -> Self { - let replication_target = 1u32.into(); - let tick_range_to_maximum_threshold = 10u32.into(); - - ReplicationTarget::::put(replication_target); - TickRangeToMaximumThreshold::::put(tick_range_to_maximum_threshold); - - Self { - replication_target, - tick_range_to_maximum_threshold, - } - } - } - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - ReplicationTarget::::put(self.replication_target); - TickRangeToMaximumThreshold::::put(self.tick_range_to_maximum_threshold); - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Notifies that a new bucket has been created. - NewBucket { - who: T::AccountId, - msp_id: ProviderIdFor, - bucket_id: BucketIdFor, - name: BucketNameFor, - collection_id: Option>, - private: bool, - }, - /// Notifies that a bucket is being moved to a new MSP. - MoveBucketRequested { - who: T::AccountId, - bucket_id: BucketIdFor, - new_msp_id: ProviderIdFor, - }, - /// Notifies that a bucket's privacy has been updated. - BucketPrivacyUpdated { - who: T::AccountId, - bucket_id: BucketIdFor, - collection_id: Option>, - private: bool, - }, - /// Notifies that a new collection has been created and associated with a bucket. - NewCollectionAndAssociation { - who: T::AccountId, - bucket_id: BucketIdFor, - collection_id: CollectionIdFor, - }, - /// Notifies that a new file has been requested to be stored. - NewStorageRequest { - who: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - location: FileLocation, - fingerprint: Fingerprint, - size: StorageData, - peer_ids: PeerIds, - }, - /// Notifies that a MSP has responded to storage request(s). - MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult, - }, - /// Notifies that a BSP has been accepted to store a given file. - AcceptedBspVolunteer { - bsp_id: ProviderIdFor, - bucket_id: BucketIdFor, - location: FileLocation, - fingerprint: Fingerprint, - multiaddresses: MultiAddresses, - owner: T::AccountId, - size: StorageData, - }, - /// Notifies that a BSP confirmed storing a file(s). - BspConfirmedStoring { - who: T::AccountId, - bsp_id: ProviderIdFor, - file_keys: BoundedVec, T::MaxBatchConfirmStorageRequests>, - new_root: MerkleHash, - }, - /// Notifies that a storage request for a file key has been fulfilled. - StorageRequestFulfilled { file_key: MerkleHash }, - /// Notifies the expiration of a storage request. - StorageRequestExpired { file_key: MerkleHash }, - /// Notifies that a storage request has been revoked by the user who initiated it. - StorageRequestRevoked { file_key: MerkleHash }, - /// Notifies that a BSP has opened a request to stop storing a file. - BspRequestedToStopStoring { - bsp_id: ProviderIdFor, - file_key: MerkleHash, - owner: T::AccountId, - location: FileLocation, - }, - /// Notifies that a BSP has stopped storing a file. - BspConfirmStoppedStoring { - bsp_id: ProviderIdFor, - file_key: MerkleHash, - new_root: MerkleHash, - }, - /// Notifies that a file key has been queued for a priority challenge for file deletion. - PriorityChallengeForFileDeletionQueued { - issuer: EitherAccountIdOrMspId, - file_key: MerkleHash, - }, - /// Notifies that a SP has stopped storing a file because its owner has become insolvent. - SpStopStoringInsolventUser { - sp_id: ProviderIdFor, - file_key: MerkleHash, - owner: T::AccountId, - location: FileLocation, - new_root: MerkleHash, - }, - /// Notifies that a priority challenge failed to be queued for pending file deletion. - FailedToQueuePriorityChallenge { - user: T::AccountId, - file_key: MerkleHash, - }, - /// Notifies that a file will be deleted. - FileDeletionRequest { - user: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - msp_id: ProviderIdFor, - proof_of_inclusion: bool, - }, - /// Notifies that a proof has been submitted for a pending file deletion request. - ProofSubmittedForPendingFileDeletionRequest { - msp_id: ProviderIdFor, - user: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - proof_of_inclusion: bool, - }, - /// Notifies that a BSP's challenge cycle has been initialised, adding the first file - /// key(s) to the BSP's Merkle Patricia Forest. - BspChallengeCycleInitialised { - who: T::AccountId, - bsp_id: ProviderIdFor, - }, - /// Notifies that a move bucket request has expired. - MoveBucketRequestExpired { - msp_id: ProviderIdFor, - bucket_id: BucketIdFor, - }, - /// Notifies that a bucket has been moved to a new MSP. - MoveBucketAccepted { - bucket_id: BucketIdFor, - msp_id: ProviderIdFor, - }, - /// Notifies that a bucket move request has been rejected by the MSP. - MoveBucketRejected { - bucket_id: BucketIdFor, - msp_id: ProviderIdFor, - }, - /// Notifies that a data server has been registered for a move bucket request. - DataServerRegisteredForMoveBucket { - bsp_id: ProviderIdFor, - bucket_id: BucketIdFor, - }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// Storage request already registered for the given file. - StorageRequestAlreadyRegistered, - /// Storage request not registered for the given file. - StorageRequestNotFound, - /// Operation not allowed while the storage request is not being revoked. - StorageRequestNotRevoked, - /// Operation not allowed while the storage request exists. - StorageRequestExists, - /// Replication target cannot be zero. - ReplicationTargetCannotBeZero, - /// BSPs required for storage request cannot exceed the maximum allowed. - BspsRequiredExceedsTarget, - /// Account is not a BSP. - NotABsp, - /// Account is not a MSP. - NotAMsp, - /// Account is not a SP. - NotASp, - /// BSP has not volunteered to store the given file. - BspNotVolunteered, - /// BSP has not confirmed storing the given file. - BspNotConfirmed, - /// BSP has already confirmed storing the given file. - BspAlreadyConfirmed, - /// Number of BSPs required for storage request has been reached. - StorageRequestBspsRequiredFulfilled, - /// BSP already volunteered to store the given file. - BspAlreadyVolunteered, - /// SP does not have enough storage capacity to store the file. - InsufficientAvailableCapacity, - /// Number of removed BSPs volunteered from storage request prefix did not match the expected number. - UnexpectedNumberOfRemovedVolunteeredBsps, - /// No slot available found in blocks to insert storage request expiration time. - StorageRequestExpiredNoSlotAvailable, - /// Not authorized to delete the storage request. - StorageRequestNotAuthorized, - /// Error created in 2024. If you see this, you are well beyond the singularity and should - /// probably stop using this pallet. - MaxBlockNumberReached, - /// Failed to encode BSP id as slice. - FailedToEncodeBsp, - /// Failed to encode fingerprint as slice. - FailedToEncodeFingerprint, - /// Failed to decode threshold. - FailedToDecodeThreshold, - /// BSP did not succeed threshold check. - AboveThreshold, - /// Arithmetic error in threshold calculation. - ThresholdArithmeticError, - /// Failed to convert to primitive type. - FailedTypeConversion, - /// Divided by 0 - DividedByZero, - /// Failed to get value when just checked it existed. - ImpossibleFailedToGetValue, - /// Bucket is not private. Call `update_bucket_privacy` to make it private. - BucketIsNotPrivate, - /// Bucket does not exist - BucketNotFound, - /// Operation failed because the account is not the owner of the bucket. - NotBucketOwner, - /// Root of the provider not found. - ProviderRootNotFound, - /// Failed to verify proof: required to provide a proof of non-inclusion. - ExpectedNonInclusionProof, - /// Failed to verify proof: required to provide a proof of inclusion. - ExpectedInclusionProof, - /// Metadata does not correspond to expected file key. - InvalidFileKeyMetadata, - /// BSPs assignment threshold cannot be below asymptote. - ThresholdBelowAsymptote, - /// Unauthorized operation, signer does not own the file. - NotFileOwner, - /// File key already pending deletion. - FileKeyAlreadyPendingDeletion, - /// Max number of user pending deletion requests reached. - MaxUserPendingDeletionRequestsReached, - /// Unauthorized operation, signer is not an MSP of the bucket id. - MspNotStoringBucket, - /// File key not found in pending deletion requests. - FileKeyNotPendingDeletion, - /// File size cannot be zero. - FileSizeCannotBeZero, - /// No global reputation weight set. - NoGlobalReputationWeightSet, - /// Maximum threshold cannot be zero. - MaximumThresholdCannotBeZero, - /// Tick range to maximum threshold cannot be zero. - TickRangeToMaximumThresholdCannotBeZero, - /// Pending stop storing request not found. - PendingStopStoringRequestNotFound, - /// Minimum amount of blocks between the request opening and being able to confirm it not reached. - MinWaitForStopStoringNotReached, - /// Pending stop storing request already exists. - PendingStopStoringRequestAlreadyExists, - /// A SP tried to stop storing files from a user that was supposedly insolvent, but the user is not insolvent. - UserNotInsolvent, - /// The MSP is trying to confirm to store a file from a storage request is not the one selected to store it. - NotSelectedMsp, - /// The MSP is trying to confirm to store a file from a storage request that it has already confirmed to store. - MspAlreadyConfirmed, - /// The MSP is trying to confirm to store a file from a storage request that does not have a MSP assigned. - RequestWithoutMsp, - /// The MSP is already storing the bucket. - MspAlreadyStoringBucket, - /// Move bucket request not found in storage. - MoveBucketRequestNotFound, - /// Action not allowed while the bucket is being moved. - BucketIsBeingMoved, - /// BSP is already a data server for the move bucket request. - BspAlreadyDataServer, - /// Too many registered data servers for the move bucket request. - BspDataServersExceeded, - /// The bounded vector that holds file metadata to process it is full but there's still more to process. - FileMetadataProcessingQueueFull, - /// Too many batch responses to process. - TooManyBatchResponses, - /// Too many storage request responses. - TooManyStorageRequestResponses, - /// Bucket id and file key pair is invalid. - InvalidBucketIdFileKeyPair, - /// Key already exists in mapping when it should not. - InconsistentStateKeyAlreadyExists, - } - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn create_bucket( - origin: OriginFor, - msp_id: ProviderIdFor, - name: BucketNameFor, - private: bool, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let (bucket_id, maybe_collection_id) = - Self::do_create_bucket(who.clone(), msp_id, name.clone(), private)?; - - Self::deposit_event(Event::NewBucket { - who, - msp_id, - bucket_id, - name, - collection_id: maybe_collection_id, - private, - }); - - Ok(()) - } - - #[pallet::call_index(1)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn request_move_bucket( - origin: OriginFor, - bucket_id: BucketIdFor, - new_msp_id: ProviderIdFor, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - Self::do_request_move_bucket(who.clone(), bucket_id, new_msp_id)?; - - Self::deposit_event(Event::MoveBucketRequested { - who, - bucket_id, - new_msp_id, - }); - - Ok(()) - } - - #[pallet::call_index(2)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn msp_respond_move_bucket_request( - origin: OriginFor, - bucket_id: BucketIdFor, - response: BucketMoveRequestResponse, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let msp_id = - Self::do_msp_respond_move_bucket_request(who.clone(), bucket_id, response.clone())?; - - match response { - BucketMoveRequestResponse::Accepted => { - Self::deposit_event(Event::MoveBucketAccepted { bucket_id, msp_id }); - } - BucketMoveRequestResponse::Rejected => { - Self::deposit_event(Event::MoveBucketRejected { bucket_id, msp_id }); - } - } - - Ok(()) - } - - #[pallet::call_index(3)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn update_bucket_privacy( - origin: OriginFor, - bucket_id: BucketIdFor, - private: bool, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let maybe_collection_id = - Self::do_update_bucket_privacy(who.clone(), bucket_id, private)?; - - Self::deposit_event(Event::BucketPrivacyUpdated { - who, - bucket_id, - private, - collection_id: maybe_collection_id, - }); - - Ok(()) - } - - /// Create and associate a collection with a bucket. - #[pallet::call_index(4)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn create_and_associate_collection_with_bucket( - origin: OriginFor, - bucket_id: BucketIdFor, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let collection_id = - Self::do_create_and_associate_collection_with_bucket(who.clone(), bucket_id)?; - - Self::deposit_event(Event::NewCollectionAndAssociation { - who, - bucket_id, - collection_id, - }); - - Ok(()) - } - - /// Issue a new storage request for a file - #[pallet::call_index(5)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn issue_storage_request( - origin: OriginFor, - bucket_id: BucketIdFor, - location: FileLocation, - fingerprint: Fingerprint, - size: StorageData, - msp_id: ProviderIdFor, - peer_ids: PeerIds, - ) -> DispatchResult { - // Check that the extrinsic was signed and get the signer - let who = ensure_signed(origin)?; - - // Perform validations and register storage request - let file_key = Self::do_request_storage( - who.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - Some(msp_id), - None, - Some(peer_ids.clone()), - Default::default(), - )?; - - // BSPs listen to this event and volunteer to store the file - Self::deposit_event(Event::NewStorageRequest { - who, - file_key, - bucket_id, - location, - fingerprint, - size, - peer_ids, - }); - - Ok(()) - } - - /// Revoke storage request - #[pallet::call_index(6)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn revoke_storage_request( - origin: OriginFor, - file_key: MerkleHash, - ) -> DispatchResult { - // Check that the extrinsic was signed and get the signer - let who = ensure_signed(origin)?; - - // Perform validations and revoke storage request - Self::do_revoke_storage_request(who, file_key)?; - - // Emit event. - Self::deposit_event(Event::StorageRequestRevoked { file_key }); - - Ok(()) - } - - /// Add yourself as a data server for providing the files of the bucket requested to be moved. - #[pallet::call_index(7)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn bsp_add_data_server_for_move_bucket_request( - origin: OriginFor, - bucket_id: BucketIdFor, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let bsp_id = - Self::do_bsp_add_data_server_for_move_bucket_request(who.clone(), bucket_id)?; - - Self::deposit_event(Event::DataServerRegisteredForMoveBucket { bsp_id, bucket_id }); - - Ok(()) - } - - /// Used by a MSP to accept or decline storage requests in batches, grouped by bucket. - /// - /// This follows a best-effort strategy, meaning that all file keys will be processed and declared to have successfully be - /// accepted, rejected or have failed to be processed in the results of the event emitted. - /// - /// The MSP has to provide a file proof for all the file keys that are being accepted and a non-inclusion proof for the file keys - /// in the bucket's Merkle Patricia Forest. The file proofs for the file keys is necessary to verify that - /// the MSP actually has the files, while the non-inclusion proof is necessary to verify that the MSP - /// wasn't storing it before. - #[pallet::call_index(8)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn msp_respond_storage_requests_multiple_buckets( - origin: OriginFor, - file_key_responses_input: FileKeyResponsesInput, - ) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - let results = - Self::do_msp_respond_storage_request(who.clone(), file_key_responses_input)?; - - Self::deposit_event(Event::MspRespondedToStorageRequests { results }); - - Ok(()) - } - - /// Used by a BSP to volunteer for storing a file. - /// - /// The transaction will fail if the XOR between the file ID and the BSP ID is not below the threshold, - /// so a BSP is strongly advised to check beforehand. Another reason for failure is - /// if the maximum number of BSPs has been reached. A successful assignment as BSP means - /// that some of the collateral tokens of that MSP are frozen. - #[pallet::call_index(9)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn bsp_volunteer(origin: OriginFor, file_key: MerkleHash) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Perform validations and register Storage Provider as BSP for file. - let (bsp_id, multiaddresses, storage_request_metadata) = - Self::do_bsp_volunteer(who.clone(), file_key)?; - - // Emit new BSP volunteer event. - Self::deposit_event(Event::AcceptedBspVolunteer { - bsp_id, - multiaddresses, - bucket_id: storage_request_metadata.bucket_id, - location: storage_request_metadata.location, - fingerprint: storage_request_metadata.fingerprint, - owner: storage_request_metadata.owner, - size: storage_request_metadata.size, - }); - - Ok(()) - } - - /// Used by a BSP to confirm they are storing data of a storage request. - #[pallet::call_index(10)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn bsp_confirm_storing( - origin: OriginFor, - non_inclusion_forest_proof: ForestProof, - file_keys_and_proofs: BoundedVec< - (MerkleHash, KeyProof), - T::MaxBatchConfirmStorageRequests, - >, - ) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Perform validations and confirm storage. - Self::do_bsp_confirm_storing( - who.clone(), - non_inclusion_forest_proof.clone(), - file_keys_and_proofs, - ) - } - - /// Executed by a BSP to request to stop storing a file. - /// - /// In the event when a storage request no longer exists for the data the BSP no longer stores, - /// it is required that the BSP still has access to the metadata of the initial storage request. - /// If they do not, they will at least need that metadata to reconstruct the File ID and from wherever - /// the BSP gets that data is up to it. One example could be from the assigned MSP. - /// This metadata is necessary since it is needed to reconstruct the leaf node key in the storage - /// provider's Merkle Forest. - #[pallet::call_index(11)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn bsp_request_stop_storing( - origin: OriginFor, - file_key: MerkleHash, - bucket_id: BucketIdFor, - location: FileLocation, - owner: T::AccountId, - fingerprint: Fingerprint, - size: StorageData, - can_serve: bool, - inclusion_forest_proof: ForestProof, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // Perform validations and open the request to stop storing the file. - let bsp_id = Self::do_bsp_request_stop_storing( - who.clone(), - file_key, - bucket_id, - location.clone(), - owner.clone(), - fingerprint, - size, - can_serve, - inclusion_forest_proof, - )?; - - // Emit event. - Self::deposit_event(Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner, - location, - }); - - Ok(()) - } - - /// Executed by a BSP to confirm to stop storing a file. - /// - /// It has to have previously opened a pending stop storing request using the `bsp_request_stop_storing` extrinsic. - /// The minimum amount of blocks between the request and the confirmation is defined by the runtime, such that the - /// BSP can't immediately stop storing a file it has previously lost when receiving a challenge for it. - #[pallet::call_index(12)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn bsp_confirm_stop_storing( - origin: OriginFor, - file_key: MerkleHash, - inclusion_forest_proof: ForestProof, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // Perform validations and stop storing the file. - let (bsp_id, new_root) = - Self::do_bsp_confirm_stop_storing(who.clone(), file_key, inclusion_forest_proof)?; - - // Emit event. - Self::deposit_event(Event::BspConfirmStoppedStoring { - bsp_id, - file_key, - new_root, - }); - - Ok(()) - } - - /// Executed by a SP to stop storing a file from an insolvent user. - /// - /// This is used when a user has become insolvent and the SP needs to stop storing the files of that user, since - /// it won't be getting paid for it anymore. - /// The validations are similar to the ones in the `bsp_request_stop_storing` and `bsp_confirm_stop_storing` extrinsics, but the SP doesn't need to - /// wait for a minimum amount of blocks to confirm to stop storing the file nor it has to be a BSP. - #[pallet::call_index(13)] - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] - pub fn stop_storing_for_insolvent_user( - origin: OriginFor, - file_key: MerkleHash, - bucket_id: BucketIdFor, - location: FileLocation, - owner: T::AccountId, - fingerprint: Fingerprint, - size: StorageData, - inclusion_forest_proof: ForestProof, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // Perform validations and stop storing the file. - let (sp_id, new_root) = Self::do_sp_stop_storing_for_insolvent_user( - who.clone(), - file_key, - bucket_id, - location.clone(), - owner.clone(), - fingerprint, - size, - inclusion_forest_proof, - )?; - - // Emit event. - Self::deposit_event(Event::SpStopStoringInsolventUser { - sp_id, - file_key, - owner, - location, - new_root, - }); - - Ok(()) - } - - #[pallet::call_index(14)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn delete_file( - origin: OriginFor, - bucket_id: BucketIdFor, - file_key: MerkleHash, - location: FileLocation, - size: StorageData, - fingerprint: Fingerprint, - maybe_inclusion_forest_proof: Option>, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let (proof_of_inclusion, msp_id) = Self::do_delete_file( - who.clone(), - bucket_id, - file_key, - location, - fingerprint, - size, - maybe_inclusion_forest_proof, - )?; - - Self::deposit_event(Event::FileDeletionRequest { - user: who, - file_key, - bucket_id, - msp_id, - proof_of_inclusion, - }); - - Ok(()) - } - - #[pallet::call_index(15)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn pending_file_deletion_request_submit_proof( - origin: OriginFor, - user: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - forest_proof: ForestProof, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let (proof_of_inclusion, msp_id) = Self::do_pending_file_deletion_request_submit_proof( - who.clone(), - user.clone(), - file_key, - bucket_id, - forest_proof, - )?; - - Self::deposit_event(Event::ProofSubmittedForPendingFileDeletionRequest { - msp_id, - user, - file_key, - bucket_id, - proof_of_inclusion, - }); - - Ok(()) - } - - #[pallet::call_index(16)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn set_global_parameters( - origin: OriginFor, - replication_target: Option, - tick_range_to_maximum_threshold: Option>, - ) -> DispatchResult { - // Check that the extrinsic was sent with root origin. - ensure_root(origin)?; - - if let Some(replication_target) = replication_target { - ensure!( - replication_target > T::ReplicationTargetType::zero(), - Error::::ReplicationTargetCannotBeZero - ); - - ReplicationTarget::::put(replication_target); - } - - if let Some(tick_range_to_maximum_threshold) = tick_range_to_maximum_threshold { - ensure!( - tick_range_to_maximum_threshold > TickNumber::::zero(), - Error::::TickRangeToMaximumThresholdCannotBeZero - ); - - TickRangeToMaximumThreshold::::put(tick_range_to_maximum_threshold); - } - - Ok(().into()) - } - } - - #[pallet::hooks] - impl Hooks> for Pallet - where - u32: TryFrom>, - { - fn on_idle(current_block: BlockNumberFor, remaining_weight: Weight) -> Weight { - let mut remaining_weight = remaining_weight; - - Self::do_on_idle(current_block, &mut remaining_weight); - - remaining_weight - } - } -} +//! # File System Pallet +//! +//! - [`Config`] +//! - [`Call`] +//! +//! ## Overview +//! +//! The file system pallet provides the following functionality: +//! +//! - Tracks Merkle Forest roots for every MSP and BSP +//! - Manages storage buckets +//! - Exposes all file related actions a user or storage provider can execute +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - `issue_storage_request`: Issue a new storage request to store a file. +//! - `volunteer_bsp`: BSP volunteers to store a file for a given storage request. +//! +//! ## Hooks +//! +//! - `on_idle`: Cleanup all expired storage requests. +//! +//! ## Dependencies +//! +//! TODO +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +pub mod types; +mod utils; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[frame_support::pallet] +pub mod pallet { + use super::types::*; + use codec::HasCompact; + use frame_support::{ + dispatch::DispatchResult, + pallet_prelude::{ValueQuery, *}, + sp_runtime::traits::{CheckEqual, Convert, MaybeDisplay, SimpleBitOps}, + traits::{ + fungible::*, + nonfungibles_v2::{Create, Inspect as NonFungiblesInspect}, + }, + Blake2_128Concat, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + use scale_info::prelude::fmt::Debug; + use shp_file_metadata::ChunkId; + use sp_runtime::{ + traits::{ + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConvertBack, One, Saturating, + Zero, + }, + BoundedVec, + }; + + #[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>; + + /// The trait for reading and mutating Storage Provider and Bucket data. + type Providers: shp_traits::ReadProvidersInterface + + shp_traits::MutateProvidersInterface< + MerkleHash = ::MerkleHash, + ProviderId = ::ProviderId, + > + shp_traits::ReadStorageProvidersInterface< + ProviderId = ::ProviderId, + > + shp_traits::MutateStorageProvidersInterface< + ProviderId = ::ProviderId, + StorageDataUnit = ::StorageDataUnit, + > + shp_traits::ReadBucketsInterface< + AccountId = Self::AccountId, + BucketId = ::ProviderId, + MerkleHash = ::MerkleHash, + ProviderId = ::ProviderId, + ReadAccessGroupId = CollectionIdFor, + StorageDataUnit = ::StorageDataUnit, + > + shp_traits::MutateBucketsInterface< + AccountId = Self::AccountId, + BucketId = ::BucketId, + MerkleHash = ::MerkleHash, + ProviderId = ::ProviderId, + ReadAccessGroupId = CollectionIdFor, + StorageDataUnit = ::StorageDataUnit, + >; + + /// The trait for issuing challenges and verifying proofs. + type ProofDealer: shp_traits::ProofsDealerInterface< + ProviderId = ::ProviderId, + MerkleHash = ::MerkleHash, + >; + + /// The trait to create, update, delete and inspect payment streams. + type PaymentStreams: shp_traits::PaymentStreamsInterface< + AccountId = Self::AccountId, + ProviderId = ::ProviderId, + Units = ::StorageDataUnit, + >; + + /// The trait for checking user solvency in the system + type UserSolvency: shp_traits::ReadUserSolvencyInterface; + + /// Type for identifying a file, generally a hash. + type Fingerprint: Parameter + + Member + + MaybeSerializeDeserialize + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen; + + /// Type representing the storage request bsps size type. + type ReplicationTargetType: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + From + + Into + + Into + + Copy + + MaxEncodedLen + + HasCompact + + Default + + scale_info::TypeInfo + + MaybeSerializeDeserialize + + CheckedAdd + + One + + Saturating + + PartialOrd + + Zero; + + /// Type representing the threshold a BSP must meet to be eligible to volunteer to store a file. + type ThresholdType: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + Default + + MaybeDisplay + + From + + From<::ReputationWeight> + + From + + Copy + + MaxEncodedLen + + Decode + + Saturating + + CheckedMul + + CheckedDiv + + CheckedAdd + + CheckedSub + + PartialOrd + + Bounded + + One + + Zero; + + /// The type to convert a threshold to a tick number. + /// + /// For more information on what "ticks" are, see the [Proofs Dealer pallet](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/proofs-dealer/README.md). + type ThresholdTypeToTickNumber: ConvertBack< + Self::ThresholdType, + ::TickNumber, + >; + + /// The type to convert a hash to a threshold. + type HashToThresholdType: Convert; + + /// The type to convert a MerkleHash to a RandomnessOutput. + type MerkleHashToRandomnessOutput: Convert< + ::MerkleHash, + ::RandomnessOutput, + >; + + /// The type to convert a ChunkId to a MerkleHash + type ChunkIdToMerkleHash: Convert< + ChunkId, + ::MerkleHash, + >; + + /// The currency mechanism, used for paying for reserves. + type Currency: Inspect + + Mutate + + hold::Balanced + + freeze::Inspect + + freeze::Mutate; + + /// Registry for minted NFTs. + type Nfts: NonFungiblesInspect + + Create>; + + /// Collection inspector + type CollectionInspector: shp_traits::InspectCollections< + CollectionId = CollectionIdFor, + >; + + /// Maximum number of SPs (MSP + BSPs) that can store a file. + /// + /// This is used to limit the number of BSPs storing a file and claiming rewards for it. + /// If this number is too high, then the reward for storing a file might be to diluted and pointless to store. + #[pallet::constant] + type MaxBspsPerStorageRequest: Get; + + /// Maximum batch of storage requests that can be confirmed at once when calling `bsp_confirm_storing`. + #[pallet::constant] + type MaxBatchConfirmStorageRequests: Get; + + /// Maximum batch of storage requests that can be responded to at once when calling `msp_respond_storage_requests_multiple_buckets`. + #[pallet::constant] + type MaxBatchMspRespondStorageRequests: Get; + + /// Maximum byte size of a file path. + #[pallet::constant] + type MaxFilePathSize: Get; + + /// Maximum byte size of a peer id. + #[pallet::constant] + type MaxPeerIdSize: Get; + + /// Maximum number of peer ids for a storage request. + #[pallet::constant] + type MaxNumberOfPeerIds: Get; + + /// Maximum number of multiaddresses for a storage request. + #[pallet::constant] + type MaxDataServerMultiAddresses: Get; + + /// Maximum number of expired items (per type) to clean up in a single block. + #[pallet::constant] + type MaxExpiredItemsInBlock: Get; + + /// Time-to-live for a storage request. + #[pallet::constant] + type StorageRequestTtl: Get; + + /// Time-to-live for a pending file deletion request, after which a priority challenge is sent out to enforce the deletion. + #[pallet::constant] + type PendingFileDeletionRequestTtl: Get; + + /// Time-to-live for a move bucket request, after which the request is considered expired. + #[pallet::constant] + type MoveBucketRequestTtl: Get; + + /// Maximum number of file deletion requests a user can have pending. + #[pallet::constant] + type MaxUserPendingDeletionRequests: Get; + + /// Maximum number of move bucket requests a user can have pending. + #[pallet::constant] + type MaxUserPendingMoveBucketRequests: Get; + + /// Number of blocks required to pass between a BSP requesting to stop storing a file and it being able to confirm to stop storing it. + #[pallet::constant] + type MinWaitForStopStoring: Get>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type StorageRequests = + StorageMap<_, Blake2_128Concat, MerkleHash, StorageRequestMetadata>; + + /// A double map from storage request to BSP `AccountId`s that volunteered to store the file. + /// + /// Any BSP under a storage request prefix is considered to be a volunteer and can be removed at any time. + /// Once a BSP submits a valid proof to the via the `bsp_confirm_storing` extrinsic, the `confirmed` field in [`StorageRequestBspsMetadata`] will be set to `true`. + /// + /// When a storage request is expired or removed, the corresponding storage request prefix in this map is removed. + #[pallet::storage] + pub type StorageRequestBsps = StorageDoubleMap< + _, + Blake2_128Concat, + MerkleHash, + Blake2_128Concat, + ProviderIdFor, + StorageRequestBspsMetadata, + OptionQuery, + >; + + /// Bookkeeping of the buckets containing open storage requests. + #[pallet::storage] + pub type BucketsWithStorageRequests = StorageDoubleMap< + _, + Blake2_128Concat, + BucketIdFor, + Blake2_128Concat, + MerkleHash, + (), + OptionQuery, + >; + + /// A map of blocks to expired storage requests. + #[pallet::storage] + pub type StorageRequestExpirations = StorageMap< + _, + Blake2_128Concat, + BlockNumberFor, + BoundedVec, T::MaxExpiredItemsInBlock>, + ValueQuery, + >; + + /// A map of blocks to expired file deletion requests. + #[pallet::storage] + pub type FileDeletionRequestExpirations = StorageMap< + _, + Blake2_128Concat, + BlockNumberFor, + BoundedVec, T::MaxExpiredItemsInBlock>, + ValueQuery, + >; + + /// A map of blocks to expired move bucket requests. + #[pallet::storage] + pub type MoveBucketRequestExpirations = StorageMap< + _, + Blake2_128Concat, + BlockNumberFor, + BoundedVec<(ProviderIdFor, BucketIdFor), T::MaxExpiredItemsInBlock>, + ValueQuery, + >; + + /// A pointer to the earliest available block to insert a new storage request expiration. + /// + /// This should always be greater or equal than current block + [`Config::StorageRequestTtl`]. + #[pallet::storage] + pub type NextAvailableStorageRequestExpirationBlock = + StorageValue<_, BlockNumberFor, ValueQuery>; + + /// A pointer to the earliest available block to insert a new file deletion request expiration. + /// + /// This should always be greater or equal than current block + [`Config::PendingFileDeletionRequestTtl`]. + #[pallet::storage] + pub type NextAvailableFileDeletionRequestExpirationBlock = + StorageValue<_, BlockNumberFor, ValueQuery>; + + /// A pointer to the earliest available block to insert a new move bucket request expiration. + /// + /// This should always be greater or equal than current block + [`Config::MoveBucketRequestTtl`]. + #[pallet::storage] + pub type NextAvailableMoveBucketRequestExpirationBlock = + StorageValue<_, BlockNumberFor, ValueQuery>; + + /// A pointer to the starting block to clean up expired storage requests. + /// + /// If this block is behind the current block number, the cleanup algorithm in `on_idle` will + /// attempt to accelerate this block pointer as close to or up to the current block number. This + /// will execute provided that there is enough remaining weight to do so. + #[pallet::storage] + pub type NextStartingBlockToCleanUp = StorageValue<_, BlockNumberFor, ValueQuery>; + + /// Pending file deletion requests. + /// + /// A mapping from a user account id to a list of pending file deletion requests, holding a tuple of the file key and bucket id. + #[pallet::storage] + pub type PendingFileDeletionRequests = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec<(MerkleHash, BucketIdFor), T::MaxUserPendingDeletionRequests>, + ValueQuery, + >; + + /// Pending file stop storing requests. + /// + /// A double mapping from BSP IDs to a list of file keys pending stop storing requests to the block in which those requests were opened + /// and the proven size of the file. + /// The block number is used to avoid BSPs being able to stop storing files immediately which would allow them to avoid challenges + /// of missing files. The size is to be able to decrease their used capacity when they confirm to stop storing the file. + #[pallet::storage] + pub type PendingStopStoringRequests = StorageDoubleMap< + _, + Blake2_128Concat, + ProviderIdFor, + Blake2_128Concat, + MerkleHash, + (BlockNumberFor, StorageData), + >; + + /// Pending move bucket requests. + /// + /// A double mapping from MSP IDs to a list of bucket IDs which they can accept or decline to take over. + /// The value is the user who requested the move. + #[pallet::storage] + pub type PendingMoveBucketRequests = StorageDoubleMap< + _, + Blake2_128Concat, + ProviderIdFor, + Blake2_128Concat, + BucketIdFor, + MoveBucketRequestMetadata, + >; + + /// BSP data servers for move bucket requests. + #[pallet::storage] + pub type DataServersForMoveBucket = StorageDoubleMap< + _, + Blake2_128Concat, + BucketIdFor, + Blake2_128Concat, + ProviderIdFor, + (), + >; + + /// Bookkeeping of buckets that are pending to be moved to a new MSP. + #[pallet::storage] + pub type PendingBucketsToMove = + StorageMap<_, Blake2_128Concat, BucketIdFor, (), ValueQuery>; + + /// Number of BSPs required to fulfill a storage request + /// + /// This is also used as a default value if the BSPs required are not specified when creating a storage request. + #[pallet::storage] + pub type ReplicationTarget = StorageValue<_, ReplicationTargetType, ValueQuery>; + + /// Number of ticks until all BSPs would reach the [`Config::MaximumThreshold`] to ensure that all BSPs are able to volunteer. + #[pallet::storage] + pub type TickRangeToMaximumThreshold = StorageValue<_, TickNumber, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub replication_target: ReplicationTargetType, + pub tick_range_to_maximum_threshold: TickNumber, + } + + impl Default for GenesisConfig { + fn default() -> Self { + let replication_target = 1u32.into(); + let tick_range_to_maximum_threshold = 10u32.into(); + + ReplicationTarget::::put(replication_target); + TickRangeToMaximumThreshold::::put(tick_range_to_maximum_threshold); + + Self { + replication_target, + tick_range_to_maximum_threshold, + } + } + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + ReplicationTarget::::put(self.replication_target); + TickRangeToMaximumThreshold::::put(self.tick_range_to_maximum_threshold); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Notifies that a new bucket has been created. + NewBucket { + who: T::AccountId, + msp_id: ProviderIdFor, + bucket_id: BucketIdFor, + name: BucketNameFor, + collection_id: Option>, + private: bool, + }, + /// Notifies that a bucket is being moved to a new MSP. + MoveBucketRequested { + who: T::AccountId, + bucket_id: BucketIdFor, + new_msp_id: ProviderIdFor, + }, + /// Notifies that a bucket's privacy has been updated. + BucketPrivacyUpdated { + who: T::AccountId, + bucket_id: BucketIdFor, + collection_id: Option>, + private: bool, + }, + /// Notifies that a new collection has been created and associated with a bucket. + NewCollectionAndAssociation { + who: T::AccountId, + bucket_id: BucketIdFor, + collection_id: CollectionIdFor, + }, + /// Notifies that a new file has been requested to be stored. + NewStorageRequest { + who: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + location: FileLocation, + fingerprint: Fingerprint, + size: StorageData, + peer_ids: PeerIds, + }, + /// Notifies that a MSP has responded to storage request(s). + MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult, + }, + /// Notifies that a BSP has been accepted to store a given file. + AcceptedBspVolunteer { + bsp_id: ProviderIdFor, + bucket_id: BucketIdFor, + location: FileLocation, + fingerprint: Fingerprint, + multiaddresses: MultiAddresses, + owner: T::AccountId, + size: StorageData, + }, + /// Notifies that a BSP confirmed storing a file(s). + BspConfirmedStoring { + who: T::AccountId, + bsp_id: ProviderIdFor, + file_keys: BoundedVec, T::MaxBatchConfirmStorageRequests>, + new_root: MerkleHash, + }, + /// Notifies that a storage request for a file key has been fulfilled. + StorageRequestFulfilled { file_key: MerkleHash }, + /// Notifies the expiration of a storage request. + StorageRequestExpired { file_key: MerkleHash }, + /// Notifies that a storage request has been revoked by the user who initiated it. + StorageRequestRevoked { file_key: MerkleHash }, + /// Notifies that a BSP has opened a request to stop storing a file. + BspRequestedToStopStoring { + bsp_id: ProviderIdFor, + file_key: MerkleHash, + owner: T::AccountId, + location: FileLocation, + }, + /// Notifies that a BSP has stopped storing a file. + BspConfirmStoppedStoring { + bsp_id: ProviderIdFor, + file_key: MerkleHash, + new_root: MerkleHash, + }, + /// Notifies that a file key has been queued for a priority challenge for file deletion. + PriorityChallengeForFileDeletionQueued { + issuer: EitherAccountIdOrMspId, + file_key: MerkleHash, + }, + /// Notifies that a SP has stopped storing a file because its owner has become insolvent. + SpStopStoringInsolventUser { + sp_id: ProviderIdFor, + file_key: MerkleHash, + owner: T::AccountId, + location: FileLocation, + new_root: MerkleHash, + }, + /// Notifies that a priority challenge failed to be queued for pending file deletion. + FailedToQueuePriorityChallenge { + user: T::AccountId, + file_key: MerkleHash, + }, + /// Notifies that a file will be deleted. + FileDeletionRequest { + user: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + msp_id: ProviderIdFor, + proof_of_inclusion: bool, + }, + /// Notifies that a proof has been submitted for a pending file deletion request. + ProofSubmittedForPendingFileDeletionRequest { + msp_id: ProviderIdFor, + user: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + proof_of_inclusion: bool, + }, + /// Notifies that a BSP's challenge cycle has been initialised, adding the first file + /// key(s) to the BSP's Merkle Patricia Forest. + BspChallengeCycleInitialised { + who: T::AccountId, + bsp_id: ProviderIdFor, + }, + /// Notifies that a move bucket request has expired. + MoveBucketRequestExpired { + msp_id: ProviderIdFor, + bucket_id: BucketIdFor, + }, + /// Notifies that a bucket has been moved to a new MSP. + MoveBucketAccepted { + bucket_id: BucketIdFor, + msp_id: ProviderIdFor, + }, + /// Notifies that a bucket move request has been rejected by the MSP. + MoveBucketRejected { + bucket_id: BucketIdFor, + msp_id: ProviderIdFor, + }, + /// Notifies that a data server has been registered for a move bucket request. + DataServerRegisteredForMoveBucket { + bsp_id: ProviderIdFor, + bucket_id: BucketIdFor, + }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Storage request already registered for the given file. + StorageRequestAlreadyRegistered, + /// Storage request not registered for the given file. + StorageRequestNotFound, + /// Operation not allowed while the storage request is not being revoked. + StorageRequestNotRevoked, + /// Operation not allowed while the storage request exists. + StorageRequestExists, + /// Replication target cannot be zero. + ReplicationTargetCannotBeZero, + /// BSPs required for storage request cannot exceed the maximum allowed. + BspsRequiredExceedsTarget, + /// Account is not a BSP. + NotABsp, + /// Account is not a MSP. + NotAMsp, + /// Account is not a SP. + NotASp, + /// BSP has not volunteered to store the given file. + BspNotVolunteered, + /// BSP has not confirmed storing the given file. + BspNotConfirmed, + /// BSP has already confirmed storing the given file. + BspAlreadyConfirmed, + /// Number of BSPs required for storage request has been reached. + StorageRequestBspsRequiredFulfilled, + /// BSP already volunteered to store the given file. + BspAlreadyVolunteered, + /// SP does not have enough storage capacity to store the file. + InsufficientAvailableCapacity, + /// Number of removed BSPs volunteered from storage request prefix did not match the expected number. + UnexpectedNumberOfRemovedVolunteeredBsps, + /// No slot available found in blocks to insert storage request expiration time. + StorageRequestExpiredNoSlotAvailable, + /// Not authorized to delete the storage request. + StorageRequestNotAuthorized, + /// Error created in 2024. If you see this, you are well beyond the singularity and should + /// probably stop using this pallet. + MaxBlockNumberReached, + /// Failed to encode BSP id as slice. + FailedToEncodeBsp, + /// Failed to encode fingerprint as slice. + FailedToEncodeFingerprint, + /// Failed to decode threshold. + FailedToDecodeThreshold, + /// BSP did not succeed threshold check. + AboveThreshold, + /// Arithmetic error in threshold calculation. + ThresholdArithmeticError, + /// Failed to convert to primitive type. + FailedTypeConversion, + /// Divided by 0 + DividedByZero, + /// Failed to get value when just checked it existed. + ImpossibleFailedToGetValue, + /// Bucket is not private. Call `update_bucket_privacy` to make it private. + BucketIsNotPrivate, + /// Bucket does not exist + BucketNotFound, + /// Operation failed because the account is not the owner of the bucket. + NotBucketOwner, + /// Root of the provider not found. + ProviderRootNotFound, + /// Failed to verify proof: required to provide a proof of non-inclusion. + ExpectedNonInclusionProof, + /// Failed to verify proof: required to provide a proof of inclusion. + ExpectedInclusionProof, + /// Metadata does not correspond to expected file key. + InvalidFileKeyMetadata, + /// BSPs assignment threshold cannot be below asymptote. + ThresholdBelowAsymptote, + /// Unauthorized operation, signer does not own the file. + NotFileOwner, + /// File key already pending deletion. + FileKeyAlreadyPendingDeletion, + /// Max number of user pending deletion requests reached. + MaxUserPendingDeletionRequestsReached, + /// Unauthorized operation, signer is not an MSP of the bucket id. + MspNotStoringBucket, + /// File key not found in pending deletion requests. + FileKeyNotPendingDeletion, + /// File size cannot be zero. + FileSizeCannotBeZero, + /// No global reputation weight set. + NoGlobalReputationWeightSet, + /// Maximum threshold cannot be zero. + MaximumThresholdCannotBeZero, + /// Tick range to maximum threshold cannot be zero. + TickRangeToMaximumThresholdCannotBeZero, + /// Pending stop storing request not found. + PendingStopStoringRequestNotFound, + /// Minimum amount of blocks between the request opening and being able to confirm it not reached. + MinWaitForStopStoringNotReached, + /// Pending stop storing request already exists. + PendingStopStoringRequestAlreadyExists, + /// A SP tried to stop storing files from a user that was supposedly insolvent, but the user is not insolvent. + UserNotInsolvent, + /// The MSP is trying to confirm to store a file from a storage request is not the one selected to store it. + NotSelectedMsp, + /// The MSP is trying to confirm to store a file from a storage request that it has already confirmed to store. + MspAlreadyConfirmed, + /// The MSP is trying to confirm to store a file from a storage request that does not have a MSP assigned. + RequestWithoutMsp, + /// The MSP is already storing the bucket. + MspAlreadyStoringBucket, + /// Move bucket request not found in storage. + MoveBucketRequestNotFound, + /// Action not allowed while the bucket is being moved. + BucketIsBeingMoved, + /// BSP is already a data server for the move bucket request. + BspAlreadyDataServer, + /// Too many registered data servers for the move bucket request. + BspDataServersExceeded, + /// The bounded vector that holds file metadata to process it is full but there's still more to process. + FileMetadataProcessingQueueFull, + /// Too many batch responses to process. + TooManyBatchResponses, + /// Too many storage request responses. + TooManyStorageRequestResponses, + /// Bucket id and file key pair is invalid. + InvalidBucketIdFileKeyPair, + /// Key already exists in mapping when it should not. + InconsistentStateKeyAlreadyExists, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn create_bucket( + origin: OriginFor, + msp_id: ProviderIdFor, + name: BucketNameFor, + private: bool, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (bucket_id, maybe_collection_id) = + Self::do_create_bucket(who.clone(), msp_id, name.clone(), private)?; + + Self::deposit_event(Event::NewBucket { + who, + msp_id, + bucket_id, + name, + collection_id: maybe_collection_id, + private, + }); + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn request_move_bucket( + origin: OriginFor, + bucket_id: BucketIdFor, + new_msp_id: ProviderIdFor, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_request_move_bucket(who.clone(), bucket_id, new_msp_id)?; + + Self::deposit_event(Event::MoveBucketRequested { + who, + bucket_id, + new_msp_id, + }); + + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn msp_respond_move_bucket_request( + origin: OriginFor, + bucket_id: BucketIdFor, + response: BucketMoveRequestResponse, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let msp_id = + Self::do_msp_respond_move_bucket_request(who.clone(), bucket_id, response.clone())?; + + match response { + BucketMoveRequestResponse::Accepted => { + Self::deposit_event(Event::MoveBucketAccepted { bucket_id, msp_id }); + } + BucketMoveRequestResponse::Rejected => { + Self::deposit_event(Event::MoveBucketRejected { bucket_id, msp_id }); + } + } + + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn update_bucket_privacy( + origin: OriginFor, + bucket_id: BucketIdFor, + private: bool, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let maybe_collection_id = + Self::do_update_bucket_privacy(who.clone(), bucket_id, private)?; + + Self::deposit_event(Event::BucketPrivacyUpdated { + who, + bucket_id, + private, + collection_id: maybe_collection_id, + }); + + Ok(()) + } + + /// Create and associate a collection with a bucket. + #[pallet::call_index(4)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn create_and_associate_collection_with_bucket( + origin: OriginFor, + bucket_id: BucketIdFor, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let collection_id = + Self::do_create_and_associate_collection_with_bucket(who.clone(), bucket_id)?; + + Self::deposit_event(Event::NewCollectionAndAssociation { + who, + bucket_id, + collection_id, + }); + + Ok(()) + } + + /// Issue a new storage request for a file + #[pallet::call_index(5)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn issue_storage_request( + origin: OriginFor, + bucket_id: BucketIdFor, + location: FileLocation, + fingerprint: Fingerprint, + size: StorageData, + msp_id: ProviderIdFor, + peer_ids: PeerIds, + ) -> DispatchResult { + // Check that the extrinsic was signed and get the signer + let who = ensure_signed(origin)?; + + // Perform validations and register storage request + let file_key = Self::do_request_storage( + who.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + Some(msp_id), + None, + Some(peer_ids.clone()), + Default::default(), + )?; + + // BSPs listen to this event and volunteer to store the file + Self::deposit_event(Event::NewStorageRequest { + who, + file_key, + bucket_id, + location, + fingerprint, + size, + peer_ids, + }); + + Ok(()) + } + + /// Revoke storage request + #[pallet::call_index(6)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn revoke_storage_request( + origin: OriginFor, + file_key: MerkleHash, + ) -> DispatchResult { + // Check that the extrinsic was signed and get the signer + let who = ensure_signed(origin)?; + + // Perform validations and revoke storage request + Self::do_revoke_storage_request(who, file_key)?; + + // Emit event. + Self::deposit_event(Event::StorageRequestRevoked { file_key }); + + Ok(()) + } + + /// Add yourself as a data server for providing the files of the bucket requested to be moved. + #[pallet::call_index(7)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn bsp_add_data_server_for_move_bucket_request( + origin: OriginFor, + bucket_id: BucketIdFor, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let bsp_id = + Self::do_bsp_add_data_server_for_move_bucket_request(who.clone(), bucket_id)?; + + Self::deposit_event(Event::DataServerRegisteredForMoveBucket { bsp_id, bucket_id }); + + Ok(()) + } + + /// Used by a MSP to accept or decline storage requests in batches, grouped by bucket. + /// + /// This follows a best-effort strategy, meaning that all file keys will be processed and declared to have successfully be + /// accepted, rejected or have failed to be processed in the results of the event emitted. + /// + /// The MSP has to provide a file proof for all the file keys that are being accepted and a non-inclusion proof for the file keys + /// in the bucket's Merkle Patricia Forest. The file proofs for the file keys is necessary to verify that + /// the MSP actually has the files, while the non-inclusion proof is necessary to verify that the MSP + /// wasn't storing it before. + #[pallet::call_index(8)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn msp_respond_storage_requests_multiple_buckets( + origin: OriginFor, + file_key_responses_input: FileKeyResponsesInput, + ) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + let results = + Self::do_msp_respond_storage_request(who.clone(), file_key_responses_input)?; + + Self::deposit_event(Event::MspRespondedToStorageRequests { results }); + + Ok(()) + } + + /// Used by a BSP to volunteer for storing a file. + /// + /// The transaction will fail if the XOR between the file ID and the BSP ID is not below the threshold, + /// so a BSP is strongly advised to check beforehand. Another reason for failure is + /// if the maximum number of BSPs has been reached. A successful assignment as BSP means + /// that some of the collateral tokens of that MSP are frozen. + #[pallet::call_index(9)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn bsp_volunteer(origin: OriginFor, file_key: MerkleHash) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Perform validations and register Storage Provider as BSP for file. + let (bsp_id, multiaddresses, storage_request_metadata) = + Self::do_bsp_volunteer(who.clone(), file_key)?; + + // Emit new BSP volunteer event. + Self::deposit_event(Event::AcceptedBspVolunteer { + bsp_id, + multiaddresses, + bucket_id: storage_request_metadata.bucket_id, + location: storage_request_metadata.location, + fingerprint: storage_request_metadata.fingerprint, + owner: storage_request_metadata.owner, + size: storage_request_metadata.size, + }); + + Ok(()) + } + + /// Used by a BSP to confirm they are storing data of a storage request. + #[pallet::call_index(10)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn bsp_confirm_storing( + origin: OriginFor, + non_inclusion_forest_proof: ForestProof, + file_keys_and_proofs: BoundedVec< + (MerkleHash, KeyProof), + T::MaxBatchConfirmStorageRequests, + >, + ) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Perform validations and confirm storage. + Self::do_bsp_confirm_storing( + who.clone(), + non_inclusion_forest_proof.clone(), + file_keys_and_proofs, + ) + } + + /// Executed by a BSP to request to stop storing a file. + /// + /// In the event when a storage request no longer exists for the data the BSP no longer stores, + /// it is required that the BSP still has access to the metadata of the initial storage request. + /// If they do not, they will at least need that metadata to reconstruct the File ID and from wherever + /// the BSP gets that data is up to it. One example could be from the assigned MSP. + /// This metadata is necessary since it is needed to reconstruct the leaf node key in the storage + /// provider's Merkle Forest. + #[pallet::call_index(11)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn bsp_request_stop_storing( + origin: OriginFor, + file_key: MerkleHash, + bucket_id: BucketIdFor, + location: FileLocation, + owner: T::AccountId, + fingerprint: Fingerprint, + size: StorageData, + can_serve: bool, + inclusion_forest_proof: ForestProof, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Perform validations and open the request to stop storing the file. + let bsp_id = Self::do_bsp_request_stop_storing( + who.clone(), + file_key, + bucket_id, + location.clone(), + owner.clone(), + fingerprint, + size, + can_serve, + inclusion_forest_proof, + )?; + + // Emit event. + Self::deposit_event(Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner, + location, + }); + + Ok(()) + } + + /// Executed by a BSP to confirm to stop storing a file. + /// + /// It has to have previously opened a pending stop storing request using the `bsp_request_stop_storing` extrinsic. + /// The minimum amount of blocks between the request and the confirmation is defined by the runtime, such that the + /// BSP can't immediately stop storing a file it has previously lost when receiving a challenge for it. + #[pallet::call_index(12)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn bsp_confirm_stop_storing( + origin: OriginFor, + file_key: MerkleHash, + inclusion_forest_proof: ForestProof, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Perform validations and stop storing the file. + let (bsp_id, new_root) = + Self::do_bsp_confirm_stop_storing(who.clone(), file_key, inclusion_forest_proof)?; + + // Emit event. + Self::deposit_event(Event::BspConfirmStoppedStoring { + bsp_id, + file_key, + new_root, + }); + + Ok(()) + } + + /// Executed by a SP to stop storing a file from an insolvent user. + /// + /// This is used when a user has become insolvent and the SP needs to stop storing the files of that user, since + /// it won't be getting paid for it anymore. + /// The validations are similar to the ones in the `bsp_request_stop_storing` and `bsp_confirm_stop_storing` extrinsics, but the SP doesn't need to + /// wait for a minimum amount of blocks to confirm to stop storing the file nor it has to be a BSP. + #[pallet::call_index(13)] + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())] + pub fn stop_storing_for_insolvent_user( + origin: OriginFor, + file_key: MerkleHash, + bucket_id: BucketIdFor, + location: FileLocation, + owner: T::AccountId, + fingerprint: Fingerprint, + size: StorageData, + inclusion_forest_proof: ForestProof, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Perform validations and stop storing the file. + let (sp_id, new_root) = Self::do_sp_stop_storing_for_insolvent_user( + who.clone(), + file_key, + bucket_id, + location.clone(), + owner.clone(), + fingerprint, + size, + inclusion_forest_proof, + )?; + + // Emit event. + Self::deposit_event(Event::SpStopStoringInsolventUser { + sp_id, + file_key, + owner, + location, + new_root, + }); + + Ok(()) + } + + #[pallet::call_index(14)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn delete_file( + origin: OriginFor, + bucket_id: BucketIdFor, + file_key: MerkleHash, + location: FileLocation, + size: StorageData, + fingerprint: Fingerprint, + maybe_inclusion_forest_proof: Option>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (proof_of_inclusion, msp_id) = Self::do_delete_file( + who.clone(), + bucket_id, + file_key, + location, + fingerprint, + size, + maybe_inclusion_forest_proof, + )?; + + Self::deposit_event(Event::FileDeletionRequest { + user: who, + file_key, + bucket_id, + msp_id, + proof_of_inclusion, + }); + + Ok(()) + } + + #[pallet::call_index(15)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn pending_file_deletion_request_submit_proof( + origin: OriginFor, + user: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + forest_proof: ForestProof, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (proof_of_inclusion, msp_id) = Self::do_pending_file_deletion_request_submit_proof( + who.clone(), + user.clone(), + file_key, + bucket_id, + forest_proof, + )?; + + Self::deposit_event(Event::ProofSubmittedForPendingFileDeletionRequest { + msp_id, + user, + file_key, + bucket_id, + proof_of_inclusion, + }); + + Ok(()) + } + + #[pallet::call_index(16)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn set_global_parameters( + origin: OriginFor, + replication_target: Option, + tick_range_to_maximum_threshold: Option>, + ) -> DispatchResult { + // Check that the extrinsic was sent with root origin. + ensure_root(origin)?; + + if let Some(replication_target) = replication_target { + ensure!( + replication_target > T::ReplicationTargetType::zero(), + Error::::ReplicationTargetCannotBeZero + ); + + ReplicationTarget::::put(replication_target); + } + + if let Some(tick_range_to_maximum_threshold) = tick_range_to_maximum_threshold { + ensure!( + tick_range_to_maximum_threshold > TickNumber::::zero(), + Error::::TickRangeToMaximumThresholdCannotBeZero + ); + + TickRangeToMaximumThreshold::::put(tick_range_to_maximum_threshold); + } + + Ok(().into()) + } + } + + #[pallet::hooks] + impl Hooks> for Pallet + where + u32: TryFrom>, + { + fn on_idle(current_block: BlockNumberFor, remaining_weight: Weight) -> Weight { + let mut remaining_weight = remaining_weight; + + Self::do_on_idle(current_block, &mut remaining_weight); + + remaining_weight + } + } +} diff --git a/pallets/file-system/src/mock.rs b/pallets/file-system/src/mock.rs index 931d5ec9e..b9ce3c03c 100644 --- a/pallets/file-system/src/mock.rs +++ b/pallets/file-system/src/mock.rs @@ -1,567 +1,567 @@ -use core::marker::PhantomData; -use frame_support::{ - construct_runtime, derive_impl, - dispatch::DispatchClass, - parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Hooks, Randomness}, - weights::{constants::RocksDbWeight, Weight, WeightMeter}, - BoundedBTreeSet, -}; -use frame_system::{self as system, limits::BlockWeights, BlockWeight, ConsumedWeight}; -use num_bigint::BigUint; -use pallet_nfts::PalletFeatures; -use shp_file_metadata::ChunkId; -use shp_traits::{ - CommitmentVerifier, MaybeDebug, ProofSubmittersInterface, ReadUserSolvencyInterface, - TrieMutation, TrieProofDeltaApplier, -}; -use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; -use sp_keyring::sr25519::Keyring; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, ConvertBack, IdentifyAccount, IdentityLookup, Verify, Zero}, - BuildStorage, DispatchError, MultiSignature, Perbill, SaturatedConversion, -}; -use sp_std::collections::btree_set::BTreeSet; -use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; -use system::pallet_prelude::BlockNumberFor; - -type Block = frame_system::mocking::MockBlock; -pub(crate) type BlockNumber = u64; -type Balance = u128; -type Signature = MultiSignature; -type AccountPublic = ::Signer; -type AccountId = ::AccountId; - -const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10; -const UNITS: Balance = 1_000_000_000_000; -const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; - -// We mock the Randomness trait to use a simple randomness function when testing the pallet -const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumber = 3; -pub struct MockRandomness; -impl Randomness for MockRandomness { - fn random(subject: &[u8]) -> (H256, BlockNumber) { - // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks - - // Concatenate the subject with the block number to get a unique hash for each block - let subject_concat_block = [ - subject, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - - let hashed_subject = blake2_256(&subject_concat_block); - - ( - H256::from_slice(&hashed_subject), - frame_system::Pallet::::block_number() - .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), - ) - } -} - -/// Rolls to the desired block, with non-spammed blocks. Returns the number of blocks played. -pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber { - let mut num_blocks = 0; - let mut block = System::block_number(); - while block < n { - block = roll_one_block(false); - num_blocks += 1; - } - num_blocks -} - -/// Rolls to the desired block with spammed blocks. Returns the number of blocks played. -pub(crate) fn roll_to_spammed(n: BlockNumber) -> BlockNumber { - let mut num_blocks = 0; - let mut block = System::block_number(); - while block < n { - block = roll_one_block(true); - num_blocks += 1; - } - num_blocks -} - -/// Rolls forward one block. Returns the new block number. -/// -/// It can be configured whether the block is spammed or not. -/// A spammed block is one where there is no weight left for other transactions. -fn roll_one_block(spammed: bool) -> BlockNumber { - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set block weight usage. - let normal_weight = if spammed { - let weights: BlockWeights = ::BlockWeights::get(); - weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block) - } else { - Zero::zero() - }; - let block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => normal_weight, - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(block_weight); - - FileSystem::on_idle(System::block_number(), Weight::MAX); - ProofsDealer::on_finalize(System::block_number()); - System::block_number() -} - -// Configure a mock runtime to test the pallet. -construct_runtime!( - pub enum Test - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - FileSystem: crate::{Pallet, Call, Storage, Event}, - Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, - ProofsDealer: pallet_proofs_dealer::{Pallet, Call, Storage, Event}, - PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, - BucketNfts: pallet_bucket_nfts::{Pallet, Call, Storage, Event}, - Nfts: pallet_nfts::{Pallet, Call, Storage, Event}, - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; - pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::Providers(pallet_storage_providers::HoldReason::StorageProviderDeposit); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_balances::Config for Test { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxLocks = ConstU32<10>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<10>; -} - -parameter_types! { - pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); -} - -impl pallet_nfts::Config for Test { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u128; - type ItemId = u128; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Locker = (); - type CollectionDeposit = ConstU128<2>; - type ItemDeposit = ConstU128<1>; - type MetadataDepositBase = ConstU128<1>; - type AttributeDepositBase = ConstU128<1>; - type DepositPerByte = ConstU128<1>; - type StringLimit = ConstU32<50>; - type KeyLimit = ConstU32<50>; - type ValueLimit = ConstU32<50>; - type ApprovalsLimit = ConstU32<10>; - type ItemAttributesApprovalsLimit = ConstU32<2>; - type MaxTips = ConstU32<10>; - type MaxDeadlineDuration = ConstU64<10000>; - type MaxAttributesPerCall = ConstU32<2>; - type Features = Features; - type OffchainSignature = Signature; - type OffchainPublic = AccountPublic; - type WeightInfo = (); - pallet_nfts::runtime_benchmarks_enabled! { - type Helper = (); - } -} - -// Payment streams pallet: -parameter_types! { - pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); -} - -impl pallet_payment_streams::Config for Test { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = Providers; - type RuntimeHoldReason = RuntimeHoldReason; - type Units = u64; - type NewStreamDeposit = ConstU64<10>; - type UserWithoutFundsCooldown = ConstU64<100>; - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = MockSubmittingProviders; -} -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; -impl Convert, Balance> for BlockNumberToBalance { - fn convert(block_number: BlockNumberFor) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -parameter_types! { - pub const MaxNumberOfPeerIds: u32 = 100; - pub const MaxMultiAddressSize: u32 = 100; - pub const MaxMultiAddressAmount: u32 = 5; -} - -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} -impl pallet_storage_providers::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = MockRandomness; - type PaymentStreams = PaymentStreams; - type FileMetadataManager = shp_file_metadata::FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = u64; - type SpCount = u32; - type MerklePatriciaRoot = H256; - type ValuePropId = H256; - type ReadAccessGroupId = ::CollectionId; - type ProvidersProofSubmitters = MockSubmittingProviders; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = ConstU128<{ 10 * UNITS }>; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = MaxMultiAddressSize; - type MaxMultiAddressAmount = MaxMultiAddressAmount; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = ConstU128<10>; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; - type MinBlocksBetweenCapacityChanges = ConstU64<10>; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = ConstU128<10>; - type StartingReputationWeight = ConstU32<1>; -} - -// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. -pub struct MockSubmittingProviders; -impl ProofSubmittersInterface for MockSubmittingProviders { - type ProviderId = ::Hash; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = ConstU32<1000>; - fn get_proof_submitters_for_tick( - _block_number: &Self::TickNumber, - ) -> Option> { - None - } - - fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { - None - } - - fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} -} - -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId { - AccountId::new([0; 32]) - } -} - -pub struct BlockFullnessHeadroom; -impl Get for BlockFullnessHeadroom { - fn get() -> Weight { - Weight::from_parts(10_000, 0) - + ::DbWeight::get().reads_writes(0, 1) - } -} - -pub struct MinNotFullBlocksRatio; -impl Get for MinNotFullBlocksRatio { - fn get() -> Perbill { - Perbill::from_percent(50) - } -} - -impl pallet_proofs_dealer::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersPallet = Providers; - type NativeBalance = Balances; - type MerkleTrieHash = H256; - type MerkleTrieHashing = BlakeTwo256; - type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type StakeToBlockNumber = SaturatingBalanceToBlockNumber; - type RandomChallengesPerBlock = ConstU32<10>; - type MaxCustomChallengesPerBlock = ConstU32<10>; - type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight - type TargetTicksStorageOfSubmitters = ConstU32<3>; - type ChallengeHistoryLength = ConstU64<30>; - type ChallengesQueueLength = ConstU32<25>; - type CheckpointChallengePeriod = ConstU64<20>; - type ChallengesFee = ConstU128<1_000_000>; - type Treasury = TreasuryAccount; - type RandomnessProvider = MockRandomness; - type StakeToChallengePeriod = ConstU128; - type MinChallengePeriod = ConstU64<4>; - type ChallengeTicksTolerance = ConstU64<10>; - type BlockFullnessPeriod = ConstU64<10>; - type BlockFullnessHeadroom = BlockFullnessHeadroom; - type MinNotFullBlocksRatio = MinNotFullBlocksRatio; -} - -/// Structure to mock a verifier that returns `true` when `proof` is not empty -/// and `false` otherwise. -pub struct MockVerifier { - _phantom: core::marker::PhantomData<(C, T)>, -} - -/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. -impl CommitmentVerifier for MockVerifier -where - C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, -{ - type Proof = CompactProof; - type Commitment = H256; - type Challenge = H256; - - fn verify_proof( - _root: &Self::Commitment, - _challenges: &[Self::Challenge], - proof: &CompactProof, - ) -> Result, DispatchError> { - if proof.encoded_nodes.len() > 0 { - Ok(proof - .encoded_nodes - .iter() - .map(|node| H256::from_slice(&node[..])) - .collect()) - } else { - Err("Proof is empty".into()) - } - } -} - -impl TrieProofDeltaApplier - for MockVerifier -where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - type Proof = CompactProof; - type Key = ::Out; - - fn apply_delta( - root: &Self::Key, - _mutations: &[(Self::Key, TrieMutation)], - _proof: &Self::Proof, - ) -> Result< - ( - MemoryDB, - Self::Key, - Vec<(Self::Key, Option>)>, - ), - DispatchError, - > { - // Just return the root as is with no mutations - Ok((MemoryDB::::default(), *root, Vec::new())) - } -} - -impl pallet_bucket_nfts::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Buckets = Providers; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); -} - -pub(crate) type ThresholdType = u32; - -parameter_types! { - pub const MinWaitForStopStoring: BlockNumber = 1; -} - -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Providers = Providers; - type ProofDealer = ProofsDealer; - type PaymentStreams = PaymentStreams; - type UserSolvency = MockUserSolvency; - type Fingerprint = H256; - type ReplicationTargetType = u32; - type ThresholdType = ThresholdType; - type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; - type HashToThresholdType = HashToThresholdTypeConverter; - type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; - type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; - type Currency = Balances; - type Nfts = Nfts; - type CollectionInspector = BucketNfts; - type MaxBspsPerStorageRequest = ConstU32<10>; - type MaxBatchConfirmStorageRequests = ConstU32<10>; - type MaxBatchMspRespondStorageRequests = ConstU32<10>; - type MaxFilePathSize = ConstU32<512u32>; - type MaxPeerIdSize = ConstU32<100>; - type MaxNumberOfPeerIds = MaxNumberOfPeerIds; - type MaxDataServerMultiAddresses = ConstU32<5>; - type MaxExpiredItemsInBlock = ConstU32<100u32>; - type StorageRequestTtl = ConstU32<40u32>; - type PendingFileDeletionRequestTtl = ConstU32<40u32>; - type MoveBucketRequestTtl = ConstU32<40u32>; - type MaxUserPendingDeletionRequests = ConstU32<10u32>; - type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; - type MinWaitForStopStoring = MinWaitForStopStoring; -} - -// If we ever require a better mock that doesn't just return true if it is Eve, change this. -pub struct MockUserSolvency; -impl ReadUserSolvencyInterface for MockUserSolvency { - type AccountId = AccountId; - - fn is_user_insolvent(user_account: &Self::AccountId) -> bool { - if user_account == &Keyring::Eve.to_account_id() { - true - } else { - false - } - } -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - crate::GenesisConfig:: { - replication_target: 2, - tick_range_to_maximum_threshold: 1, - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (Keyring::Alice.to_account_id(), 1_000_000_000_000_000), - (Keyring::Bob.to_account_id(), 1_000_000_000_000_000), - (Keyring::Charlie.to_account_id(), 1_000_000_000_000_000), - (Keyring::Dave.to_account_id(), 1_000_000_000_000_000), - (Keyring::Eve.to_account_id(), 1_000_000_000_000_000), - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| roll_one_block(false)); - ext -} - -// Converter from the Balance type to the BlockNumber type for math. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct SaturatingBalanceToBlockNumber; - -impl Convert> for SaturatingBalanceToBlockNumber { - fn convert(block_number: Balance) -> BlockNumberFor { - block_number.saturated_into() - } -} - -// Converter from the ThresholdType to the BlockNumber type and vice versa. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct ThresholdTypeToBlockNumberConverter; - -impl Convert> for ThresholdTypeToBlockNumberConverter { - fn convert(threshold: ThresholdType) -> BlockNumberFor { - threshold.saturated_into() - } -} - -impl ConvertBack> for ThresholdTypeToBlockNumberConverter { - fn convert_back(block_number: BlockNumberFor) -> ThresholdType { - block_number.saturated_into() - } -} - -/// Converter from the [`Hash`] type to the [`ThresholdType`]. -pub struct HashToThresholdTypeConverter; -impl Convert<::Hash, ThresholdType> for HashToThresholdTypeConverter { - fn convert(hash: ::Hash) -> ThresholdType { - // Get the hash as bytes - let hash_bytes = hash.as_ref(); - - // Get the 4 least significant bytes of the hash and interpret them as an u32 - let truncated_hash_bytes: [u8; 4] = - hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); - - ThresholdType::from_be_bytes(truncated_hash_bytes) - } -} - -// Converter from the MerkleHash (H256) type to the RandomnessOutput type. -pub struct MerkleHashToRandomnessOutputConverter; - -impl Convert for MerkleHashToRandomnessOutputConverter { - fn convert(hash: H256) -> H256 { - hash - } -} - -// Converter from the ChunkId type to the MerkleHash (H256) type. -pub struct ChunkIdToMerkleHashConverter; - -impl Convert for ChunkIdToMerkleHashConverter { - fn convert(chunk_id: ChunkId) -> H256 { - let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); - let mut bytes = chunk_id_biguint.to_bytes_be(); - - // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros - if bytes.len() < 32 { - let mut padded_bytes = vec![0u8; 32 - bytes.len()]; - padded_bytes.extend(bytes); - bytes = padded_bytes; - } - - H256::from_slice(&bytes) - } -} +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, + dispatch::DispatchClass, + parameter_types, + traits::{AsEnsureOriginWithArg, Everything, Hooks, Randomness}, + weights::{constants::RocksDbWeight, Weight, WeightMeter}, + BoundedBTreeSet, +}; +use frame_system::{self as system, limits::BlockWeights, BlockWeight, ConsumedWeight}; +use num_bigint::BigUint; +use pallet_nfts::PalletFeatures; +use shp_file_metadata::ChunkId; +use shp_traits::{ + CommitmentVerifier, MaybeDebug, ProofSubmittersInterface, ReadUserSolvencyInterface, + TrieMutation, TrieProofDeltaApplier, +}; +use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; +use sp_keyring::sr25519::Keyring; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, ConvertBack, IdentifyAccount, IdentityLookup, Verify, Zero}, + BuildStorage, DispatchError, MultiSignature, Perbill, SaturatedConversion, +}; +use sp_std::collections::btree_set::BTreeSet; +use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; +use system::pallet_prelude::BlockNumberFor; + +type Block = frame_system::mocking::MockBlock; +pub(crate) type BlockNumber = u64; +type Balance = u128; +type Signature = MultiSignature; +type AccountPublic = ::Signer; +type AccountId = ::AccountId; + +const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10; +const UNITS: Balance = 1_000_000_000_000; +const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; + +// We mock the Randomness trait to use a simple randomness function when testing the pallet +const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumber = 3; +pub struct MockRandomness; +impl Randomness for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumber) { + // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks + + // Concatenate the subject with the block number to get a unique hash for each block + let subject_concat_block = [ + subject, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + + let hashed_subject = blake2_256(&subject_concat_block); + + ( + H256::from_slice(&hashed_subject), + frame_system::Pallet::::block_number() + .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), + ) + } +} + +/// Rolls to the desired block, with non-spammed blocks. Returns the number of blocks played. +pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber { + let mut num_blocks = 0; + let mut block = System::block_number(); + while block < n { + block = roll_one_block(false); + num_blocks += 1; + } + num_blocks +} + +/// Rolls to the desired block with spammed blocks. Returns the number of blocks played. +pub(crate) fn roll_to_spammed(n: BlockNumber) -> BlockNumber { + let mut num_blocks = 0; + let mut block = System::block_number(); + while block < n { + block = roll_one_block(true); + num_blocks += 1; + } + num_blocks +} + +/// Rolls forward one block. Returns the new block number. +/// +/// It can be configured whether the block is spammed or not. +/// A spammed block is one where there is no weight left for other transactions. +fn roll_one_block(spammed: bool) -> BlockNumber { + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set block weight usage. + let normal_weight = if spammed { + let weights: BlockWeights = ::BlockWeights::get(); + weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block) + } else { + Zero::zero() + }; + let block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => normal_weight, + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(block_weight); + + FileSystem::on_idle(System::block_number(), Weight::MAX); + ProofsDealer::on_finalize(System::block_number()); + System::block_number() +} + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + FileSystem: crate::{Pallet, Call, Storage, Event}, + Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, + ProofsDealer: pallet_proofs_dealer::{Pallet, Call, Storage, Event}, + PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, + BucketNfts: pallet_bucket_nfts::{Pallet, Call, Storage, Event}, + Nfts: pallet_nfts::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; + pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::Providers(pallet_storage_providers::HoldReason::StorageProviderDeposit); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<10>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<10>; +} + +parameter_types! { + pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); +} + +impl pallet_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u128; + type ItemId = u128; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Locker = (); + type CollectionDeposit = ConstU128<2>; + type ItemDeposit = ConstU128<1>; + type MetadataDepositBase = ConstU128<1>; + type AttributeDepositBase = ConstU128<1>; + type DepositPerByte = ConstU128<1>; + type StringLimit = ConstU32<50>; + type KeyLimit = ConstU32<50>; + type ValueLimit = ConstU32<50>; + type ApprovalsLimit = ConstU32<10>; + type ItemAttributesApprovalsLimit = ConstU32<2>; + type MaxTips = ConstU32<10>; + type MaxDeadlineDuration = ConstU64<10000>; + type MaxAttributesPerCall = ConstU32<2>; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; + type WeightInfo = (); + pallet_nfts::runtime_benchmarks_enabled! { + type Helper = (); + } +} + +// Payment streams pallet: +parameter_types! { + pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); +} + +impl pallet_payment_streams::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = Providers; + type RuntimeHoldReason = RuntimeHoldReason; + type Units = u64; + type NewStreamDeposit = ConstU64<10>; + type UserWithoutFundsCooldown = ConstU64<100>; + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = MockSubmittingProviders; +} +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; +impl Convert, Balance> for BlockNumberToBalance { + fn convert(block_number: BlockNumberFor) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +parameter_types! { + pub const MaxNumberOfPeerIds: u32 = 100; + pub const MaxMultiAddressSize: u32 = 100; + pub const MaxMultiAddressAmount: u32 = 5; +} + +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} +impl pallet_storage_providers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = MockRandomness; + type PaymentStreams = PaymentStreams; + type FileMetadataManager = shp_file_metadata::FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = u64; + type SpCount = u32; + type MerklePatriciaRoot = H256; + type ValuePropId = H256; + type ReadAccessGroupId = ::CollectionId; + type ProvidersProofSubmitters = MockSubmittingProviders; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = ConstU128<{ 10 * UNITS }>; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = MaxMultiAddressSize; + type MaxMultiAddressAmount = MaxMultiAddressAmount; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = ConstU128<10>; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; + type MinBlocksBetweenCapacityChanges = ConstU64<10>; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = ConstU128<10>; + type StartingReputationWeight = ConstU32<1>; +} + +// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. +pub struct MockSubmittingProviders; +impl ProofSubmittersInterface for MockSubmittingProviders { + type ProviderId = ::Hash; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = ConstU32<1000>; + fn get_proof_submitters_for_tick( + _block_number: &Self::TickNumber, + ) -> Option> { + None + } + + fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { + None + } + + fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} +} + +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId { + AccountId::new([0; 32]) + } +} + +pub struct BlockFullnessHeadroom; +impl Get for BlockFullnessHeadroom { + fn get() -> Weight { + Weight::from_parts(10_000, 0) + + ::DbWeight::get().reads_writes(0, 1) + } +} + +pub struct MinNotFullBlocksRatio; +impl Get for MinNotFullBlocksRatio { + fn get() -> Perbill { + Perbill::from_percent(50) + } +} + +impl pallet_proofs_dealer::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersPallet = Providers; + type NativeBalance = Balances; + type MerkleTrieHash = H256; + type MerkleTrieHashing = BlakeTwo256; + type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type StakeToBlockNumber = SaturatingBalanceToBlockNumber; + type RandomChallengesPerBlock = ConstU32<10>; + type MaxCustomChallengesPerBlock = ConstU32<10>; + type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight + type TargetTicksStorageOfSubmitters = ConstU32<3>; + type ChallengeHistoryLength = ConstU64<30>; + type ChallengesQueueLength = ConstU32<25>; + type CheckpointChallengePeriod = ConstU64<20>; + type ChallengesFee = ConstU128<1_000_000>; + type Treasury = TreasuryAccount; + type RandomnessProvider = MockRandomness; + type StakeToChallengePeriod = ConstU128; + type MinChallengePeriod = ConstU64<4>; + type ChallengeTicksTolerance = ConstU64<10>; + type BlockFullnessPeriod = ConstU64<10>; + type BlockFullnessHeadroom = BlockFullnessHeadroom; + type MinNotFullBlocksRatio = MinNotFullBlocksRatio; +} + +/// Structure to mock a verifier that returns `true` when `proof` is not empty +/// and `false` otherwise. +pub struct MockVerifier { + _phantom: core::marker::PhantomData<(C, T)>, +} + +/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. +impl CommitmentVerifier for MockVerifier +where + C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, +{ + type Proof = CompactProof; + type Commitment = H256; + type Challenge = H256; + + fn verify_proof( + _root: &Self::Commitment, + _challenges: &[Self::Challenge], + proof: &CompactProof, + ) -> Result, DispatchError> { + if proof.encoded_nodes.len() > 0 { + Ok(proof + .encoded_nodes + .iter() + .map(|node| H256::from_slice(&node[..])) + .collect()) + } else { + Err("Proof is empty".into()) + } + } +} + +impl TrieProofDeltaApplier + for MockVerifier +where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + type Proof = CompactProof; + type Key = ::Out; + + fn apply_delta( + root: &Self::Key, + _mutations: &[(Self::Key, TrieMutation)], + _proof: &Self::Proof, + ) -> Result< + ( + MemoryDB, + Self::Key, + Vec<(Self::Key, Option>)>, + ), + DispatchError, + > { + // Just return the root as is with no mutations + Ok((MemoryDB::::default(), *root, Vec::new())) + } +} + +impl pallet_bucket_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Buckets = Providers; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); +} + +pub(crate) type ThresholdType = u32; + +parameter_types! { + pub const MinWaitForStopStoring: BlockNumber = 1; +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Providers = Providers; + type ProofDealer = ProofsDealer; + type PaymentStreams = PaymentStreams; + type UserSolvency = MockUserSolvency; + type Fingerprint = H256; + type ReplicationTargetType = u32; + type ThresholdType = ThresholdType; + type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; + type HashToThresholdType = HashToThresholdTypeConverter; + type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; + type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; + type Currency = Balances; + type Nfts = Nfts; + type CollectionInspector = BucketNfts; + type MaxBspsPerStorageRequest = ConstU32<10>; + type MaxBatchConfirmStorageRequests = ConstU32<10>; + type MaxBatchMspRespondStorageRequests = ConstU32<10>; + type MaxFilePathSize = ConstU32<512u32>; + type MaxPeerIdSize = ConstU32<100>; + type MaxNumberOfPeerIds = MaxNumberOfPeerIds; + type MaxDataServerMultiAddresses = ConstU32<5>; + type MaxExpiredItemsInBlock = ConstU32<100u32>; + type StorageRequestTtl = ConstU32<40u32>; + type PendingFileDeletionRequestTtl = ConstU32<40u32>; + type MoveBucketRequestTtl = ConstU32<40u32>; + type MaxUserPendingDeletionRequests = ConstU32<10u32>; + type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; + type MinWaitForStopStoring = MinWaitForStopStoring; +} + +// If we ever require a better mock that doesn't just return true if it is Eve, change this. +pub struct MockUserSolvency; +impl ReadUserSolvencyInterface for MockUserSolvency { + type AccountId = AccountId; + + fn is_user_insolvent(user_account: &Self::AccountId) -> bool { + if user_account == &Keyring::Eve.to_account_id() { + true + } else { + false + } + } +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + crate::GenesisConfig:: { + replication_target: 2, + tick_range_to_maximum_threshold: 1, + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (Keyring::Alice.to_account_id(), 1_000_000_000_000_000), + (Keyring::Bob.to_account_id(), 1_000_000_000_000_000), + (Keyring::Charlie.to_account_id(), 1_000_000_000_000_000), + (Keyring::Dave.to_account_id(), 1_000_000_000_000_000), + (Keyring::Eve.to_account_id(), 1_000_000_000_000_000), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| roll_one_block(false)); + ext +} + +// Converter from the Balance type to the BlockNumber type for math. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct SaturatingBalanceToBlockNumber; + +impl Convert> for SaturatingBalanceToBlockNumber { + fn convert(block_number: Balance) -> BlockNumberFor { + block_number.saturated_into() + } +} + +// Converter from the ThresholdType to the BlockNumber type and vice versa. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct ThresholdTypeToBlockNumberConverter; + +impl Convert> for ThresholdTypeToBlockNumberConverter { + fn convert(threshold: ThresholdType) -> BlockNumberFor { + threshold.saturated_into() + } +} + +impl ConvertBack> for ThresholdTypeToBlockNumberConverter { + fn convert_back(block_number: BlockNumberFor) -> ThresholdType { + block_number.saturated_into() + } +} + +/// Converter from the [`Hash`] type to the [`ThresholdType`]. +pub struct HashToThresholdTypeConverter; +impl Convert<::Hash, ThresholdType> for HashToThresholdTypeConverter { + fn convert(hash: ::Hash) -> ThresholdType { + // Get the hash as bytes + let hash_bytes = hash.as_ref(); + + // Get the 4 least significant bytes of the hash and interpret them as an u32 + let truncated_hash_bytes: [u8; 4] = + hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); + + ThresholdType::from_be_bytes(truncated_hash_bytes) + } +} + +// Converter from the MerkleHash (H256) type to the RandomnessOutput type. +pub struct MerkleHashToRandomnessOutputConverter; + +impl Convert for MerkleHashToRandomnessOutputConverter { + fn convert(hash: H256) -> H256 { + hash + } +} + +// Converter from the ChunkId type to the MerkleHash (H256) type. +pub struct ChunkIdToMerkleHashConverter; + +impl Convert for ChunkIdToMerkleHashConverter { + fn convert(chunk_id: ChunkId) -> H256 { + let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); + let mut bytes = chunk_id_biguint.to_bytes_be(); + + // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros + if bytes.len() < 32 { + let mut padded_bytes = vec![0u8; 32 - bytes.len()]; + padded_bytes.extend(bytes); + bytes = padded_bytes; + } + + H256::from_slice(&bytes) + } +} diff --git a/pallets/file-system/src/tests.rs b/pallets/file-system/src/tests.rs index 6f6fefe06..dff562233 100644 --- a/pallets/file-system/src/tests.rs +++ b/pallets/file-system/src/tests.rs @@ -1,8217 +1,8217 @@ -use crate::{ - self as file_system, - mock::*, - types::{ - AcceptedStorageRequestParameters, BatchResponses, BucketIdFor, BucketMoveRequestResponse, - BucketNameFor, FileKeyResponsesInput, FileLocation, MerkleHash, MoveBucketRequestMetadata, - MspFailedBatchStorageRequests, MspRejectedBatchStorageRequests, - MspRespondStorageRequestsResult, MspStorageRequestResponse, PeerIds, - PendingFileDeletionRequestTtl, ProviderIdFor, StorageData, StorageRequestBspsMetadata, - StorageRequestMetadata, StorageRequestTtl, ThresholdType, - }, - Config, DataServersForMoveBucket, Error, Event, PendingBucketsToMove, - PendingMoveBucketRequests, PendingStopStoringRequests, ReplicationTarget, - StorageRequestExpirations, StorageRequests, TickRangeToMaximumThreshold, -}; -use frame_support::{ - assert_noop, assert_ok, - dispatch::DispatchResultWithPostInfo, - traits::{nonfungibles_v2::Destroy, Hooks, OriginTrait}, - weights::Weight, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use pallet_proofs_dealer::{LastTickProviderSubmittedAProofFor, PriorityChallengesQueue}; -use pallet_storage_providers::types::Bucket; -use shp_traits::{ - MutateStorageProvidersInterface, ReadBucketsInterface, ReadChallengeableProvidersInterface, - ReadStorageProvidersInterface, TrieRemoveMutation, -}; -use sp_core::{ByteArray, Hasher, H256}; -use sp_keyring::sr25519::Keyring; -use sp_runtime::{ - bounded_vec, - traits::{BlakeTwo256, Get, Zero}, - BoundedVec, DispatchError, -}; -use sp_std::collections::btree_map::BTreeMap; -use sp_trie::CompactProof; - -mod create_bucket_tests { - use super::*; - - mod failure { - use super::*; - - #[test] - fn create_bucket_msp_not_provider_fail() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - - assert_noop!( - FileSystem::create_bucket( - origin, - H256::from_slice(&msp.as_slice()), - name, - true - ), - Error::::NotAMsp - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn create_private_bucket_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = true; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin, - msp_id, - name.clone(), - private - )); - - // Check if collection was created - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewBucket { - who: owner, - msp_id, - bucket_id, - name, - collection_id: Some(0), - private, - } - .into(), - ); - }); - } - - #[test] - fn create_public_bucket_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = false; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin, - msp_id, - name.clone(), - private - )); - - // Check that the bucket does not have a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_none() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewBucket { - who: owner, - msp_id, - bucket_id, - name, - collection_id: None, - private, - } - .into(), - ); - }); - } - } -} - -mod request_move_bucket { - use super::*; - mod failure { - use super::*; - - #[test] - fn move_bucket_while_storage_request_opened() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - origin.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_charlie_id, - peer_ids.clone(), - )); - - assert_noop!( - FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), - Error::::StorageRequestExists - ); - }); - } - - #[test] - fn move_bucket_when_already_requested() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - assert_noop!( - FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), - Error::::BucketIsBeingMoved - ); - }); - } - - #[test] - fn move_bucket_request_to_msp_already_storing_it() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - assert_noop!( - FileSystem::request_move_bucket(origin, bucket_id, msp_charlie_id), - Error::::MspAlreadyStoringBucket - ); - }); - } - - #[test] - fn move_bucket_to_non_existent_msp() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = - <::Hashing as Hasher>::hash(&msp_dave.as_slice()); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - assert_noop!( - FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), - Error::::NotAMsp - ); - }); - } - - #[test] - fn move_bucket_not_bucket_owner() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let not_owner = Keyring::Bob.to_account_id(); - let origin = RuntimeOrigin::signed(not_owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - assert_noop!( - FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), - Error::::NotBucketOwner - ); - }); - } - - #[test] - fn move_bucket_request_accepted_msp_not_enough_capacity() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Issue storage request with a big file size - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = vec![0; 1000]; - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let size = 1000; - - // Set replication target to 1 to automatically fulfill the storage request after a single bsp confirms. - crate::ReplicationTarget::::put(1); - - assert_ok!(FileSystem::issue_storage_request( - origin.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_charlie_id, - peer_ids.clone(), - )); - - // Compute the file key. - let file_key = FileSystem::compute_file_key( - owner.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Manually set enough capacity for Charlie - pallet_storage_providers::MainStorageProviders::::mutate( - msp_charlie_id, - |msp| { - if let Some(msp) = msp { - msp.capacity = 1000; - } - }, - ); - - // Dispatch the MSP accept request. - // This operation increases the bucket size. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp_charlie), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None - } - )] - )); - - // Check bucket size - let bucket_size = Providers::get_bucket_size(&bucket_id).unwrap(); - assert_eq!(bucket_size, size); - - // BSP confirm storage request - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), size * 2)); - - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner, - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - // Manually set low capacity for Dave - pallet_storage_providers::MainStorageProviders::::mutate( - msp_dave_id, - |msp| { - if let Some(msp) = msp { - msp.capacity = 0; - } - }, - ); - - // Dispatch a signed extrinsic. - assert_noop!( - FileSystem::msp_respond_move_bucket_request( - RuntimeOrigin::signed(msp_dave), - bucket_id, - BucketMoveRequestResponse::Accepted - ), - Error::::InsufficientAvailableCapacity - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn move_bucket_request_and_accepted_by_new_msp() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner, - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::msp_respond_move_bucket_request( - RuntimeOrigin::signed(msp_dave), - bucket_id, - BucketMoveRequestResponse::Accepted - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketAccepted { - msp_id: msp_dave_id, - bucket_id, - } - .into(), - ); - - // Check bucket is stored by Dave - assert!(Providers::is_bucket_stored_by_msp(&msp_dave_id, &bucket_id)); - - // Check pending bucket storages are cleared - assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); - assert!(!PendingMoveBucketRequests::::contains_key( - &msp_dave_id, - bucket_id - )); - }); - } - - #[test] - fn move_bucket_request_and_rejected_by_new_msp() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner, - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::msp_respond_move_bucket_request( - RuntimeOrigin::signed(msp_dave), - bucket_id, - BucketMoveRequestResponse::Rejected - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRejected { - msp_id: msp_dave_id, - bucket_id, - } - .into(), - ); - - // Check bucket is still stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Check pending bucket storages are cleared - assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); - assert!(!PendingMoveBucketRequests::::contains_key( - &msp_dave_id, - bucket_id - )); - }); - } - - #[test] - fn move_bucket_request_and_expires() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner, - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - // Check move bucket request expires after MoveBucketRequestTtl - let move_bucket_request_ttl: u32 = ::MoveBucketRequestTtl::get(); - let move_bucket_request_ttl: BlockNumber = move_bucket_request_ttl.into(); - let expiration = move_bucket_request_ttl + System::block_number(); - - // Move block number to expiration - roll_to(expiration); - - assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); - assert!(!PendingMoveBucketRequests::::contains_key( - &msp_dave_id, - bucket_id - )); - }); - } - } -} - -mod bsp_add_data_server_for_move_bucket_request { - use super::*; - - mod failure { - use super::*; - - #[test] - fn not_a_bsp() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let bucket_id = H256::zero(); - - assert_noop!( - FileSystem::bsp_add_data_server_for_move_bucket_request(origin, bucket_id), - Error::::NotABsp - ); - }); - } - - #[test] - fn no_move_bucket_request_found() { - new_test_ext().execute_with(|| { - let bsp_account_id = Keyring::Bob.to_account_id(); - let origin = RuntimeOrigin::signed(bsp_account_id.clone()); - let bucket_id = H256::zero(); - - assert_ok!(bsp_sign_up(origin.clone(), 1000)); - - assert_noop!( - FileSystem::bsp_add_data_server_for_move_bucket_request(origin, bucket_id), - Error::::MoveBucketRequestNotFound - ); - }); - } - - #[test] - fn bsp_already_data_server() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - assert_ok!(bsp_sign_up( - RuntimeOrigin::signed(bsp_account_id.clone()), - 1000 - )); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner.clone(), - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - assert_ok!(FileSystem::bsp_add_data_server_for_move_bucket_request( - RuntimeOrigin::signed(bsp_account_id.clone()), - bucket_id, - )); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { requester: owner }) - ); - assert_eq!( - DataServersForMoveBucket::::iter_key_prefix(&bucket_id).next(), - Some(bsp_id) - ); - - assert_noop!( - FileSystem::bsp_add_data_server_for_move_bucket_request( - RuntimeOrigin::signed(bsp_account_id.clone()), - bucket_id, - ), - Error::::BspAlreadyDataServer - ); - }); - } - } - - mod success { - use crate::DataServersForMoveBucket; - - use super::*; - - #[test] - fn add_bsp_as_data_server() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - assert_ok!(bsp_sign_up( - RuntimeOrigin::signed(bsp_account_id.clone()), - 1000 - )); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert!(PendingBucketsToMove::::contains_key(&bucket_id)); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MoveBucketRequested { - who: owner.clone(), - bucket_id, - new_msp_id: msp_dave_id, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::bsp_add_data_server_for_move_bucket_request( - RuntimeOrigin::signed(bsp_account_id.clone()), - bucket_id, - )); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { requester: owner }) - ); - assert_eq!( - DataServersForMoveBucket::::iter_key_prefix(&bucket_id).next(), - Some(bsp_id) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::DataServerRegisteredForMoveBucket { bsp_id, bucket_id }.into(), - ); - }); - } - } -} - -mod update_bucket_privacy_tests { - use super::*; - - mod failure { - use super::*; - - #[test] - fn update_bucket_privacy_bucket_not_found_fail() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - assert_noop!( - FileSystem::update_bucket_privacy(origin, bucket_id, false), - pallet_storage_providers::Error::::BucketNotFound - ); - }); - } - } - - mod success { - use super::*; - #[test] - fn update_bucket_privacy_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = true; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin.clone(), - msp_id, - name.clone(), - private - )); - - // Check if collection was created - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewBucket { - who: owner.clone(), - msp_id, - bucket_id, - name, - collection_id: Some(0), - private, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, false)); - - // Check that the bucket still has a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BucketPrivacyUpdated { - who: owner, - bucket_id, - collection_id: Some(0), - private: false, - } - .into(), - ); - }); - } - - #[test] - fn update_bucket_privacy_collection_remains_after_many_privacy_updates_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = true; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin.clone(), - msp_id, - name.clone(), - private - )); - - // Check if collection was created - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewBucket { - who: owner.clone(), - msp_id, - bucket_id, - name, - collection_id: Some(0), - private, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::update_bucket_privacy( - origin.clone(), - bucket_id, - false - )); - - // Check that the bucket still has a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BucketPrivacyUpdated { - who: owner.clone(), - bucket_id, - collection_id: Some(0), - private: false, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, true)); - - // Check that the bucket still has a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BucketPrivacyUpdated { - who: owner, - bucket_id, - collection_id: Some(0), - private: true, - } - .into(), - ); - }); - } - - #[test] - fn update_bucket_privacy_delete_collection_before_going_from_public_to_private_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = true; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin.clone(), - msp_id, - name.clone(), - private - )); - - // Check that the bucket does not have a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewBucket { - who: owner.clone(), - msp_id, - bucket_id, - name, - collection_id: Some(0), - private, - } - .into(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::update_bucket_privacy( - origin.clone(), - bucket_id, - false - )); - - // Check that the bucket still has a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - let collection_id = - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id, - ) - .unwrap() - .expect("Collection ID should exist"); - - let w = Nfts::get_destroy_witness(&collection_id).unwrap(); - - // Delete collection before going from public to private bucket - assert_ok!(Nfts::destroy(origin.clone(), collection_id, w)); - - // Update bucket privacy from public to private - assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, true)); - - // Check that the bucket still has a corresponding collection - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - // Assert that the correct event was deposited and that a new collection with index 1 has been created - System::assert_last_event( - Event::BucketPrivacyUpdated { - who: owner, - bucket_id, - collection_id: Some(1), - private: true, - } - .into(), - ); - }); - } - } -} - -mod create_and_associate_collection_with_bucket_tests { - use super::*; - - mod failure { - use super::*; - - #[test] - fn create_and_associate_collection_with_bucket_bucket_not_found_fail() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - assert_noop!( - FileSystem::create_and_associate_collection_with_bucket(origin, bucket_id), - pallet_storage_providers::Error::::BucketNotFound - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn create_and_associate_collection_with_bucket_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let private = true; - - let msp_id = add_msp_to_provider_storage(&msp); - - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - &owner, - name.clone(), - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin.clone(), - msp_id, - name.clone(), - private - )); - - // Check if collection was created - assert!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .is_some() - ); - - let collection_id = - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id, - ) - .unwrap() - .expect("Collection ID should exist"); - - assert_ok!(FileSystem::create_and_associate_collection_with_bucket( - origin, bucket_id - )); - - // Check if collection was associated with the bucket - assert_ne!( - ::Providers::get_read_access_group_id_of_bucket( - &bucket_id - ) - .unwrap() - .expect("Collection ID should exist"), - collection_id - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewCollectionAndAssociation { - who: owner, - bucket_id, - collection_id: 1, // Collection ID should be incremented from 0 - } - .into(), - ); - }); - } - } -} - -mod request_storage { - use super::*; - mod failure { - use super::*; - - #[test] - fn request_storage_bucket_does_not_exist_fail() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name: BucketNameFor = BoundedVec::try_from([0u8; 32].to_vec()).unwrap(); - let bucket_id = H256::from_slice(&name); - - assert_noop!( - FileSystem::issue_storage_request( - origin, - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - ), - pallet_storage_providers::Error::::BucketNotFound - ); - }); - } - - #[test] - fn request_storage_not_bucket_owner_fail() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let not_owner = Keyring::Bob.to_account_id(); - let origin = RuntimeOrigin::signed(not_owner.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_id); - - assert_noop!( - FileSystem::issue_storage_request( - origin, - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - ), - Error::::NotBucketOwner - ); - }); - } - - #[test] - fn request_storage_while_pending_move_bucket() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let msp_charlie = Keyring::Charlie.to_account_id(); - let msp_dave = Keyring::Dave.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); - let msp_dave_id = add_msp_to_provider_storage(&msp_dave); - - let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); - - // Check bucket is stored by Charlie - assert!(Providers::is_bucket_stored_by_msp( - &msp_charlie_id, - &bucket_id - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::request_move_bucket( - origin.clone(), - bucket_id, - msp_dave_id - )); - - let pending_move_bucket = - PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); - assert_eq!( - pending_move_bucket, - Some(MoveBucketRequestMetadata { - requester: owner.clone() - }) - ); - - assert_noop!( - FileSystem::issue_storage_request( - origin, - bucket_id, - location.clone(), - fingerprint, - 4, - msp_charlie_id, - peer_ids.clone(), - ), - Error::::BucketIsBeingMoved - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn request_storage_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewStorageRequest { - who: owner_account_id, - file_key, - bucket_id, - location: location.clone(), - fingerprint, - size: 4, - peer_ids, - } - .into(), - ); - }); - } - - #[test] - fn two_request_storage_in_same_block() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let file_1_location = FileLocation::::try_from(b"test1".to_vec()).unwrap(); - let file_2_location = FileLocation::::try_from(b"test2".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - file_1_location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - file_1_location.clone(), - size, - fingerprint, - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id: bucket_id.clone(), - location: file_1_location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - file_2_location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - file_2_location.clone(), - size, - fingerprint, - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: file_2_location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::NewStorageRequest { - who: owner_account_id, - file_key, - bucket_id, - location: file_2_location.clone(), - fingerprint, - size, - peer_ids, - } - .into(), - ); - }); - } - - #[test] - fn request_storage_failure_if_size_is_zero() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 0; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_noop!( - FileSystem::issue_storage_request( - user.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - ), - Error::::FileSizeCannotBeZero - ); - }); - } - - #[test] - fn request_storage_expiration_clear_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let size = 4; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let storage_request_ttl: u32 = StorageRequestTtl::::get(); - let storage_request_ttl: BlockNumberFor = storage_request_ttl.into(); - let expiration_block = System::block_number() + storage_request_ttl; - - // Assert that the next expiration block number is the storage request ttl since a single storage request was made - assert_eq!( - file_system::NextAvailableStorageRequestExpirationBlock::::get(), - expiration_block - ); - - // Assert that the storage request expiration was appended to the list at `StorageRequestTtl` - assert_eq!( - file_system::StorageRequestExpirations::::get(expiration_block), - vec![file_key] - ); - - roll_to(expiration_block + 1); - - // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` - assert_eq!( - file_system::StorageRequestExpirations::::get(expiration_block), - vec![] - ); - }); - } - - #[test] - fn request_storage_expiration_current_block_increment_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); - let expected_expiration_block_number: BlockNumberFor = - expected_expiration_block_number.into(); - - // Append storage request expiration to the list at `StorageRequestTtl` - let max_expired_items_in_block: u32 = - ::MaxExpiredItemsInBlock::get(); - for _ in 0..max_expired_items_in_block { - assert_ok!(StorageRequestExpirations::::try_append( - expected_expiration_block_number, - file_key - )); - } - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids, - )); - - // Assert that the storage request expirations storage is at max capacity - assert_eq!( - file_system::StorageRequestExpirations::::get( - expected_expiration_block_number - ) - .len(), - max_expired_items_in_block as usize - ); - - // Go to block number after which the storage request expirations should be removed - roll_to(expected_expiration_block_number); - - // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` - assert_eq!( - file_system::StorageRequestExpirations::::get( - expected_expiration_block_number - ), - vec![] - ); - }); - } - - #[test] - fn request_storage_clear_old_expirations_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Append storage request expiration to the list at `StorageRequestTtl` - let max_storage_request_expiry: u32 = - ::MaxExpiredItemsInBlock::get(); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); - let expected_expiration_block_number: BlockNumberFor = - expected_expiration_block_number.into(); - - for _ in 0..max_storage_request_expiry { - assert_ok!(StorageRequestExpirations::::try_append( - expected_expiration_block_number, - file_key - )); - } - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids, - )); - - let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); - let expected_expiration_block_number: BlockNumberFor = - expected_expiration_block_number.into(); - - // Assert that the storage request expirations storage is at max capacity - assert_eq!( - file_system::StorageRequestExpirations::::get( - expected_expiration_block_number - ) - .len(), - max_storage_request_expiry as usize - ); - - let used_weight = FileSystem::on_idle(System::block_number(), Weight::zero()); - - // Assert that the weight used is zero - assert_eq!(used_weight, Weight::zero()); - - // Assert that the storage request expirations storage is at max capacity - assert_eq!( - file_system::StorageRequestExpirations::::get( - expected_expiration_block_number - ) - .len(), - max_storage_request_expiry as usize - ); - - // Go to block number after which the storage request expirations should be removed - roll_to(expected_expiration_block_number + 1); - - // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` - assert_eq!( - file_system::StorageRequestExpirations::::get( - expected_expiration_block_number - ), - vec![] - ); - - // Assert that the `NextExpirationInsertionBlockNumber` storage is set to the next block number - assert_eq!( - file_system::NextStartingBlockToCleanUp::::get(), - System::block_number() + 1 - ); - }); - } - } -} - -mod revoke_storage_request { - use super::*; - mod failure { - use super::*; - - #[test] - fn revoke_non_existing_storage_request_fail() { - new_test_ext().execute_with(|| { - let owner = RuntimeOrigin::signed(Keyring::Alice.to_account_id()); - let file_key = H256::zero(); - - assert_noop!( - FileSystem::revoke_storage_request(owner.clone(), file_key), - Error::::StorageRequestNotFound - ); - }); - } - - #[test] - fn revoke_storage_request_not_owner_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let not_owner = RuntimeOrigin::signed(Keyring::Bob.to_account_id()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - Default::default() - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - assert_noop!( - FileSystem::revoke_storage_request(not_owner.clone(), file_key), - Error::::StorageRequestNotAuthorized - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn revoke_request_storage_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - Default::default() - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - let storage_request_ttl: u32 = StorageRequestTtl::::get(); - let storage_request_ttl: BlockNumberFor = storage_request_ttl.into(); - let expiration_block = System::block_number() + storage_request_ttl; - - // Assert that the storage request expiration was appended to the list at `StorageRequestTtl` - assert_eq!( - file_system::StorageRequestExpirations::::get(expiration_block), - vec![file_key] - ); - - assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); - - System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); - }); - } - - #[test] - fn revoke_storage_request_with_volunteered_bsps_success() { - new_test_ext().execute_with(|| { - let owner_account = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account.clone(), name.clone(), msp_id); - - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - )); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let file_key = FileSystem::compute_file_key( - owner_account.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Check StorageRequestBsps storage for confirmed BSPs - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: false, - _phantom: Default::default() - } - ); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); - - // Assert that the correct event was deposited - System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); - }); - } - - #[test] - fn revoke_storage_request_with_confirmed_bsps_success() { - new_test_ext().execute_with(|| { - let owner_account = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - )); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); - - // Check ProofsDealer pallet storage for queued custom challenges for remove trie mutation of file key - let priority_challenges_queue = PriorityChallengesQueue::::get(); - - assert!(priority_challenges_queue.contains(&(file_key, Some(TrieRemoveMutation)))); - - // Assert that the correct event was deposited - System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); - }); - } - } -} - -mod msp_respond_storage_request { - use super::*; - - mod success { - use super::*; - use crate::types::{ - AcceptedStorageRequestParameters, MspAcceptedBatchStorageRequests, - MspStorageRequestResponse, RejectedStorageRequestReason, - }; - use sp_core::crypto::AccountId32; - - #[test] - fn msp_respond_storage_request_works() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - // Register the MSP. - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp); - - // Create the bucket that will hold the file. - // Create the bucket that will hold the file. - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Compute the file key. - // Compute the file key. - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch the MSP accept request. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets(RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some( - AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![(file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - } - ), - reject: None, - } - )])); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - // Get the new root of the bucket. - let new_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id,) - .unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![file_key], - bucket_id, - new_bucket_root, - owner: owner_account_id - } - )], - }, - }.into(), - ); - }); - } - - #[test] - fn msp_respond_storage_request_works_multiple_times_for_same_user_same_bucket() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let second_location = FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp); - - // Create the bucket that will hold both files. - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Compute the file key for the first file. - let first_file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - first_location.clone(), - size, - fingerprint, - ); - - // Compute the file key for the second file. - let second_file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - second_location.clone(), - size, - fingerprint, - ); - - // Dispatch a storage request for the first file. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - first_location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Dispatch a storage request for the second file. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - second_location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Dispatch the MSP accept request for the first file. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![(first_file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }), (second_file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec(), H256::default().as_ref().to_vec()], - }, - }), - reject: None - } - )] - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(first_file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - assert_eq!( - file_system::StorageRequests::::get(second_file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - // Get the new root of the bucket. - let new_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id) - .unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![first_file_key, second_file_key], - bucket_id, - new_bucket_root, - owner: owner_account_id - } - )], - }, - }.into(), - ); - - // Assert that the MSP used capacity has been updated. - assert_eq!( - ::get_used_capacity(&msp_id), - size * 2 - ); - }); - } - - #[test] - fn msp_respond_storage_request_works_multiple_times_for_same_user_different_bucket() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let second_location = - FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()) - .unwrap(); - let first_size = 4; - let second_size = 8; - let first_fingerprint = H256::zero(); - let second_fingerprint = H256::random(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp); - - // Create the bucket that will hold the first file. - let first_name = BoundedVec::try_from(b"first bucket".to_vec()).unwrap(); - let first_bucket_id = create_bucket(&owner_account_id.clone(), first_name, msp_id); - - // Create the bucket that will hold the second file. - let second_name = BoundedVec::try_from(b"second bucket".to_vec()).unwrap(); - let second_bucket_id = - create_bucket(&owner_account_id.clone(), second_name, msp_id); - - // Compute the file key for the first file. - let first_file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - first_bucket_id, - first_location.clone(), - first_size, - first_fingerprint, - ); - - // Compute the file key for the second file. - let second_file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - second_bucket_id, - second_location.clone(), - second_size, - second_fingerprint, - ); - - // Dispatch a storage request for the first file. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - first_bucket_id, - first_location.clone(), - first_fingerprint, - first_size, - msp_id, - peer_ids.clone(), - )); - - // Dispatch a storage request for the second file. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - second_bucket_id, - second_location.clone(), - second_fingerprint, - second_size, - msp_id, - peer_ids.clone(), - )); - - // Dispatch the MSP accept request for the second file. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - first_bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![(first_file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - }), - reject: None, - } - ), - ( - second_bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![(second_file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - }), - reject: None, - } - ) - ], - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(first_file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(second_file_key).unwrap().msp, - Some((msp_id, true)) - ); - - let first_bucket_root = <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&first_bucket_id,) - .unwrap(); - let second_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&second_bucket_id,) - .unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![first_file_key], - bucket_id: first_bucket_id, - new_bucket_root: first_bucket_root, - owner: owner_account_id.clone() - } - ), BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![second_file_key], - bucket_id: second_bucket_id, - new_bucket_root: second_bucket_root, - owner: owner_account_id - } - )], - }, - }.into(), - ); - - // Assert that the MSP used capacity has been updated. - assert_eq!( - ::get_used_capacity(&msp_id), - first_size + second_size - ); - }); - } - - #[test] - fn msp_respond_storage_request_works_multiple_times_for_different_users() { - new_test_ext().execute_with(|| { - let first_owner_account_id = Keyring::Alice.to_account_id(); - let first_owner_signed = RuntimeOrigin::signed(first_owner_account_id.clone()); - let second_owner_account_id = Keyring::Bob.to_account_id(); - let second_owner_signed = RuntimeOrigin::signed(second_owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let second_location = - FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()) - .unwrap(); - let first_size = 4; - let second_size = 8; - let first_fingerprint = H256::zero(); - let second_fingerprint = H256::random(); - let first_peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let first_peer_ids: PeerIds = - BoundedVec::try_from(vec![first_peer_id]).unwrap(); - let second_peer_id = BoundedVec::try_from(vec![2]).unwrap(); - let second_peer_ids: PeerIds = - BoundedVec::try_from(vec![second_peer_id]).unwrap(); - - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp); - - // Create the bucket that will hold the first file. - let first_name = BoundedVec::try_from(b"first bucket".to_vec()).unwrap(); - let first_bucket_id = - create_bucket(&first_owner_account_id.clone(), first_name, msp_id); - - // Create the bucket that will hold the second file. - let second_name = BoundedVec::try_from(b"second bucket".to_vec()).unwrap(); - let second_bucket_id = - create_bucket(&second_owner_account_id.clone(), second_name, msp_id); - - // Compute the file key for the first file. - let first_file_key = FileSystem::compute_file_key( - first_owner_account_id.clone(), - first_bucket_id, - first_location.clone(), - first_size, - first_fingerprint, - ); - - // Compute the file key for the second file. - let second_file_key = FileSystem::compute_file_key( - second_owner_account_id.clone(), - second_bucket_id, - second_location.clone(), - second_size, - second_fingerprint, - ); - - // Dispatch a storage request for the first file. - assert_ok!(FileSystem::issue_storage_request( - first_owner_signed.clone(), - first_bucket_id, - first_location.clone(), - first_fingerprint, - first_size, - msp_id, - first_peer_ids.clone(), - )); - - // Dispatch a storage request for the second file. - assert_ok!(FileSystem::issue_storage_request( - second_owner_signed.clone(), - second_bucket_id, - second_location.clone(), - second_fingerprint, - second_size, - msp_id, - second_peer_ids.clone(), - )); - - // Dispatch the MSP accept request for the second file. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - first_bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - first_file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - }), ( - second_bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - second_file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None - }, - ) - ], - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(first_file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(second_file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - let first_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&first_bucket_id,) - .unwrap(); - - let second_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&second_bucket_id,) - .unwrap(); - - // Check event - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![first_file_key], - bucket_id: first_bucket_id, - new_bucket_root: first_bucket_root, - owner: first_owner_account_id.clone() - } - ), BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![second_file_key], - bucket_id: second_bucket_id, - new_bucket_root: second_bucket_root, - owner: second_owner_account_id.clone() - } - )], - }, - }.into(), - ); - - // Assert that the MSP used capacity has been updated. - assert_eq!( - ::get_used_capacity(&msp_id), - first_size + second_size - ); - }); - } - - #[test] - fn msp_respond_storage_request_fullfilled() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp); - - // Create the bucket that will hold the file. - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Set replication target to 1 - ReplicationTarget::::put(1); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Compute the file key. - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - // Dispatch the BSP volunteer - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch the BSP confirm storing - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Dispatch the MSP accept request. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some( - AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![(file_key, CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - } - ), - reject: None, - } - )], - )); - - System::assert_has_event( - Event::StorageRequestFulfilled { file_key }.into(), - ); - - let new_bucket_root = - <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id,) - .unwrap(); - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Accepted( - MspAcceptedBatchStorageRequests:: { - file_keys: bounded_vec![file_key], - bucket_id, - new_bucket_root, - owner: owner_account_id - } - )], - }, - }.into(), - ); - - // Storage request should be removed - assert!(file_system::StorageRequests::::get(file_key).is_none()); - assert!( - file_system::BucketsWithStorageRequests::::get(bucket_id, file_key) - .is_none() - ); - }); - } - - struct StorageRequestParams { - owner_account_id: AccountId32, - bucket_name: Vec, - location: Vec, - size: u64, - fingerprint: H256, - peer_ids: PeerIds, - } - - fn generate_storage_requests( - params_list: Vec, - msp_id: ProviderIdFor, - ) -> Vec<(BucketIdFor, MerkleHash, AccountId32)> { - let mut results = Vec::new(); - - for params in params_list { - // Create bucket if not already created - let bucket_id = ::Providers::derive_bucket_id( - &msp_id, - ¶ms.owner_account_id.clone().try_into().unwrap(), - params.bucket_name.clone().try_into().unwrap(), - ); - - if !::Providers::bucket_exists(&bucket_id) { - create_bucket( - ¶ms.owner_account_id.clone(), - params.bucket_name.clone().try_into().unwrap(), - msp_id, - ); - } - - // Compute file key - let file_key = FileSystem::compute_file_key( - params.owner_account_id.clone(), - bucket_id, - FileLocation::::try_from(params.location.clone()).unwrap(), - params.size, - params.fingerprint, - ); - - // Issue storage request - assert_ok!(FileSystem::issue_storage_request( - RuntimeOrigin::signed(params.owner_account_id.clone()), - bucket_id, - FileLocation::::try_from(params.location).unwrap(), - params.fingerprint, - params.size, - msp_id, - params.peer_ids.clone(), - )); - - results.push((bucket_id, file_key, params.owner_account_id.clone())); - } - - results - } - - fn generate_msp_responses_and_results( - storage_requests: Vec<(BucketIdFor, MerkleHash, AccountId32)>, - msp_id: ProviderIdFor, - ) -> ( - FileKeyResponsesInput, - MspRespondStorageRequestsResult, - ) { - let mut responses: BTreeMap, MspStorageRequestResponse> = - BTreeMap::new(); - let mut batch_responses: Vec> = Vec::new(); - - for (bucket_id, file_key, owner_account_id) in storage_requests { - let response: &mut MspStorageRequestResponse = responses - .entry(bucket_id) - .or_insert_with(|| MspStorageRequestResponse { - accept: None, - reject: None, - }); - - if file_key.as_ref()[0] % 2 == 0 { - if let Some(accept) = &mut response.accept { - accept - .file_keys_and_proofs - .try_push(( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - )) - .unwrap(); - } else { - response.accept = Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }); - } - - if let Some(BatchResponses::Accepted(ref mut accepted)) = batch_responses.iter_mut().find(|br| matches!(br, BatchResponses::Accepted(a) if a.bucket_id == bucket_id)) { - accepted.file_keys.try_push(file_key).unwrap(); - } else { - batch_responses.push(BatchResponses::Accepted(MspAcceptedBatchStorageRequests { - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - bucket_id, - new_bucket_root: H256::zero(), - owner: owner_account_id, - })); - } - } else { - // Rejected response - let reject_reason = RejectedStorageRequestReason::InternalError; - - if let Some(reject) = &mut response.reject { - reject.try_push((file_key, reject_reason.clone())).unwrap(); - } else { - response.reject = Some(bounded_vec![(file_key, reject_reason.clone())]); - } - - if let Some(BatchResponses::Rejected(ref mut rejected)) = batch_responses.iter_mut().find(|br| matches!(br, BatchResponses::Rejected(r) if r.bucket_id == bucket_id)) { - rejected.file_keys.try_push((file_key, reject_reason)).unwrap(); - } else { - batch_responses.push(BatchResponses::Rejected(MspRejectedBatchStorageRequests { - file_keys: BoundedVec::try_from(vec![(file_key, reject_reason)]).unwrap(), - bucket_id, - owner: owner_account_id, - })); - } - } - } - - let responses: FileKeyResponsesInput = responses - .into_iter() - .collect::>() - .try_into() - .expect("Should not exceed MaxBatchConfirmStorageRequests"); - - let results = MspRespondStorageRequestsResult { - msp_id, - responses: BoundedVec::try_from(batch_responses).unwrap(), - }; - - println!("Generated results: {:?}", results); - - (responses, results) - } - - #[test] - fn msp_respond_storage_request_accepts_and_rejects_failed_mixed_responses() { - new_test_ext().execute_with(|| { - // Create accounts - let msp_account_id = Keyring::Charlie.to_account_id(); - - // Register the MSP. - let msp_id = add_msp_to_provider_storage(&msp_account_id); - - let first_bucket = b"first bucket".to_vec(); - let second_bucket = b"second bucket".to_vec(); - let size = 4; - let fingerprint = H256::zero(); - - // Define storage request parameters - let storage_request_params = vec![ - StorageRequestParams { - owner_account_id: Keyring::Alice.to_account_id(), - bucket_name: first_bucket, - location: b"location".to_vec(), - size, - fingerprint, - peer_ids: BoundedVec::try_from( - vec![BoundedVec::try_from(vec![1]).unwrap()], - ) - .unwrap(), - }, - StorageRequestParams { - owner_account_id: Keyring::Bob.to_account_id(), - bucket_name: second_bucket.clone(), - location: b"location2".to_vec(), - size, - fingerprint, - peer_ids: BoundedVec::try_from( - vec![BoundedVec::try_from(vec![2]).unwrap()], - ) - .unwrap(), - }, - StorageRequestParams { - owner_account_id: Keyring::Bob.to_account_id(), - bucket_name: second_bucket, - location: b"location3".to_vec(), - size, - fingerprint, - peer_ids: BoundedVec::try_from( - vec![BoundedVec::try_from(vec![2]).unwrap()], - ) - .unwrap(), - }, - ]; - - // Generate storage requests - let storage_requests: Vec<(BucketIdFor, MerkleHash, AccountId32)> = - generate_storage_requests(storage_request_params, msp_id); - - let (responses, expected_results) = - generate_msp_responses_and_results(storage_requests, msp_id); - - // Use `responses` to call the extrinsic - FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp_account_id), - responses, - ) - .unwrap(); - - let expected_results = MspRespondStorageRequestsResult { - msp_id, - responses: { - let updated_responses: Vec<_> = expected_results - .responses - .into_iter() - .map(|batch_response| match batch_response { - BatchResponses::Accepted(mut accepted) => { - accepted.new_bucket_root = - ::Providers::get_root_bucket( - &accepted.bucket_id, - ) - .expect("Root bucket should exist"); - BatchResponses::Accepted(accepted) - } - br => br, - }) - .collect(); - - BoundedVec::try_from(updated_responses) - .expect("Number of responses should not exceed the bound") - }, - }; - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: expected_results, - } - .into(), - ); - }); - } - } - - mod failure { - use super::*; - - #[test] - fn fails_if_storage_request_not_found() { - new_test_ext().execute_with(|| { - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let msp_id = add_msp_to_provider_storage(&msp); - - // create bucket - let owner_account_id = Keyring::Alice.to_account_id(); - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = H256::zero(); - - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )] - )); - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Failed( - MspFailedBatchStorageRequests:: { - file_keys: bounded_vec![( - file_key, - Error::::StorageRequestNotFound.into() - )], - bucket_id, - owner: owner_account_id, - } - )], - }, - } - .into(), - ); - }); - } - - #[test] - fn fails_if_caller_not_a_provider() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let not_msp = Keyring::Bob.to_account_id(); - let not_msp_signed = RuntimeOrigin::signed(not_msp.clone()); - - assert_noop!( - FileSystem::msp_respond_storage_requests_multiple_buckets( - not_msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![], - }, - }), - reject: None, - } - )] - ), - Error::::NotASp - ); - }); - } - - #[test] - fn fails_if_caller_not_a_msp() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - assert_noop!( - FileSystem::msp_respond_storage_requests_multiple_buckets( - bsp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![], - }, - }), - reject: None, - } - )] - ), - Error::::NotAMsp - ); - }); - } - - #[test] - fn fails_if_request_is_not_expecting_a_msp() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Insert a storage request that is not expecting a MSP. - StorageRequests::::insert( - file_key, - StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: None, - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }, - ); - - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )] - ),); - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Failed( - MspFailedBatchStorageRequests:: { - file_keys: bounded_vec![( - file_key, - Error::::RequestWithoutMsp.into() - )], - bucket_id, - owner: owner_account_id, - } - )], - }, - } - .into(), - ); - }); - } - - #[test] - fn fails_if_caller_is_msp_but_not_assigned_one() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let expected_msp = Keyring::Charlie.to_account_id(); - let caller_msp = Keyring::Dave.to_account_id(); - let caller_msp_signed = RuntimeOrigin::signed(caller_msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let expected_msp_id = add_msp_to_provider_storage(&expected_msp); - let _caller_msp_id = add_msp_to_provider_storage(&caller_msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, expected_msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - expected_msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Try to accept storing a file with a MSP that is not the one assigned to the file. - assert_noop!( - FileSystem::msp_respond_storage_requests_multiple_buckets( - caller_msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![], - }, - }), - reject: None, - } - )] - ), - Error::::MspNotStoringBucket - ); - }); - } - - #[test] - fn fails_if_msp_already_accepted_storing() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Accept storing the file. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )], - )); - - // Try to accept storing the file again. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )], - ),); - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Failed( - MspFailedBatchStorageRequests:: { - file_keys: bounded_vec![( - file_key, - Error::::MspAlreadyConfirmed.into() - )], - bucket_id, - owner: owner_account_id, - } - )], - }, - } - .into(), - ); - }); - } - - #[test] - fn fails_if_msp_is_not_the_one_storing_the_bucket() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let expected_msp = Keyring::Charlie.to_account_id(); - let expected_msp_signed = RuntimeOrigin::signed(expected_msp.clone()); - let other_msp = Keyring::Dave.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let expected_msp_id = add_msp_to_provider_storage(&expected_msp); - let other_msp_id = add_msp_to_provider_storage(&other_msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, other_msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Insert a storage request with the expected MSP but a bucket ID from another MSP. - // Note: this should never happen since `issue_storage_request` checks that the bucket ID - // belongs to the MSP, but we are testing it just in case. - StorageRequests::::insert( - file_key, - StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((expected_msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }, - ); - - // Try to accept storing a file with a MSP that is not the owner of the bucket ID - assert_noop!( - FileSystem::msp_respond_storage_requests_multiple_buckets( - expected_msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![], - }, - }), - reject: None, - } - )] - ), - Error::::MspNotStoringBucket - ); - }); - } - - #[test] - fn fails_if_msp_does_not_have_enough_available_capacity() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 200; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Insert a storage request for a MSP with not enough available capacity. - // Note: `issue_storage_request` checks that the MSP has enough available capacity, but it could happen - // that when the storage request was initially created the MSP had enough available capacity but it - // accepted other storage requests in the meantime and now it does not have enough available capacity. - StorageRequests::::insert( - file_key, - StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }, - ); - - // Try to accept storing a file with a MSP that does not have enough available capacity - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )] - ),); - - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Failed( - MspFailedBatchStorageRequests:: { - file_keys: bounded_vec![( - file_key, - Error::::InsufficientAvailableCapacity.into() - )], - bucket_id, - owner: owner_account_id, - } - )], - }, - } - .into(), - ); - }); - } - - #[test] - fn fails_if_the_non_inclusion_proof_includes_the_file_key() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch a storage request. - assert_ok!(FileSystem::issue_storage_request( - RuntimeOrigin::signed(owner_account_id.clone()), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Try to accept storing a file with a non-inclusion proof that includes the file key - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - }), - reject: None, - } - )] - ),); - - // Check the event Event::MspRespondedToStorageRequests - System::assert_last_event( - Event::MspRespondedToStorageRequests { - results: MspRespondStorageRequestsResult:: { - msp_id, - responses: bounded_vec![BatchResponses::::Failed( - MspFailedBatchStorageRequests:: { - file_keys: bounded_vec![( - file_key, - Error::::ExpectedNonInclusionProof.into() - )], - bucket_id, - owner: owner_account_id, - } - )], - }, - } - .into(), - ); - }); - } - } -} - -mod bsp_volunteer { - use super::*; - mod failure { - use super::*; - use core::u32; - - #[test] - fn bsp_actions_not_a_bsp_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - RuntimeOrigin::signed(owner_account_id.clone()), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::NotABsp - ); - }); - } - - #[test] - fn bsp_volunteer_storage_request_not_found_fail() { - new_test_ext().execute_with(|| { - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let fingerprint = H256::zero(); - - assert_ok!(bsp_sign_up(bsp_signed.clone(), 100,)); - - let file_key = FileSystem::compute_file_key( - bsp_account_id.clone(), - H256::zero(), - location.clone(), - 4, - fingerprint, - ); - - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::StorageRequestNotFound - ); - }); - } - - #[test] - fn bsp_already_volunteered_failed() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::BspAlreadyVolunteered - ); - }); - } - - #[test] - fn bsp_volunteer_above_threshold_high_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Set very high block range to maximum threshold. - assert_ok!(FileSystem::set_global_parameters( - RuntimeOrigin::root(), - None, - Some(u32::MAX.into()) - )); - - // Dispatch BSP volunteer. - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::AboveThreshold - ); - }); - } - - #[test] - fn bsp_volunteer_above_threshold_high_fail_even_with_spamming() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); - - // Get BSP ID. - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Compute the file key to volunteer for. - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Set a somewhat high block range to maximum threshold. - assert_ok!(FileSystem::set_global_parameters( - RuntimeOrigin::root(), - None, - Some(40) - )); - - // Calculate how many ticks until this BSP can volunteer for the file. - let current_tick = ProofsDealer::get_current_tick(); - let tick_when_bsp_can_volunteer = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - let ticks_to_advance = tick_when_bsp_can_volunteer - current_tick + 1; - let current_block = System::block_number(); - - // Advance by the number of ticks until this BSP can volunteer for the file. - // In the process, this BSP will spam the chain to prevent others from volunteering and confirming. - roll_to_spammed(current_block + ticks_to_advance); - - // Dispatch BSP volunteer. - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::AboveThreshold - ); - }); - } - - #[test] - fn bsp_volunteer_with_insufficient_capacity() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner.clone(), name.clone(), msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - origin, - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - pallet_storage_providers::BackupStorageProviders::::mutate(bsp_id, |bsp| { - assert!(bsp.is_some()); - if let Some(bsp) = bsp { - bsp.capacity = 0; - } - }); - - let file_key = FileSystem::compute_file_key( - owner.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - // Dispatch BSP volunteer. - assert_noop!( - FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), - Error::::InsufficientAvailableCapacity - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn bsp_volunteer_success() { - new_test_ext().execute_with(|| { - let owner = Keyring::Alice.to_account_id(); - let origin = RuntimeOrigin::signed(owner.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = StorageData::::try_from(4).unwrap(); - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner.clone(), name.clone(), msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - origin, - bucket_id, - location.clone(), - fingerprint, - 4, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner.clone(), - bucket_id, - location.clone(), - 4, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - - // Assert that the RequestStorageBsps has the correct value - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: false, - _phantom: Default::default() - } - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::AcceptedBspVolunteer { - bsp_id, - bucket_id, - location, - fingerprint, - multiaddresses: create_sp_multiaddresses(), - owner, - size, - } - .into(), - ); - }); - } - - #[test] - fn bsp_volunteer_succeeds_after_waiting_enough_blocks_without_spam() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); - - // Get BSP ID. - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Compute the file key to volunteer for. - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Set a somewhat high block range to maximum threshold. - assert_ok!(FileSystem::set_global_parameters( - RuntimeOrigin::root(), - None, - Some(40) - )); - - // Calculate how many ticks until this BSP can volunteer for the file. - let current_tick = ProofsDealer::get_current_tick(); - let tick_when_bsp_can_volunteer = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - let ticks_to_advance = tick_when_bsp_can_volunteer - current_tick + 1; - let current_block = System::block_number(); - - // Advance by the number of ticks until this BSP can volunteer for the file. - roll_to(current_block + ticks_to_advance); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - - // Assert that the RequestStorageBsps has the correct value - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: false, - _phantom: Default::default() - } - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::AcceptedBspVolunteer { - bsp_id, - bucket_id, - location, - fingerprint, - multiaddresses: create_sp_multiaddresses(), - owner: owner_account_id, - size, - } - .into(), - ); - }); - } - } -} - -mod bsp_confirm { - use super::*; - mod failure { - use super::*; - use pallet_storage_providers::types::ReputationWeightType; - - #[test] - fn bsp_actions_not_a_bsp_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - RuntimeOrigin::signed(owner_account_id.clone()), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - assert_noop!( - FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - ), - Error::::NotABsp - ); - }); - } - - #[test] - fn bsp_confirm_storing_storage_request_not_found_fail() { - new_test_ext().execute_with(|| { - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), 100,)); - - let file_key = FileSystem::compute_file_key( - bsp_account_id.clone(), - H256::zero(), - location.clone(), - 4, - H256::zero(), - ); - - assert_noop!( - FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - ), - Error::::StorageRequestNotFound - ); - }); - } - - #[test] - fn bsp_confirm_storing_not_volunteered_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - assert_noop!( - FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - ), - Error::::BspNotVolunteered - ); - }); - } - - #[test] - fn bsp_confirming_for_non_existent_storage_request() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_bob_account_id = Keyring::Bob.to_account_id(); - let bsp_bob_signed = RuntimeOrigin::signed(bsp_bob_account_id.clone()); - let bsp_charlie_account_id = Keyring::Dave.to_account_id(); - let bsp_charlie_signed = RuntimeOrigin::signed(bsp_charlie_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let msp_signed = RuntimeOrigin::signed(msp.clone()); - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - msp_signed.clone(), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None, - } - )] - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key) - .unwrap() - .msp, - Some((msp_id, true)) - ); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_bob_signed.clone(), storage_amount,)); - assert_ok!(bsp_sign_up(bsp_charlie_signed.clone(), storage_amount,)); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_bob_signed.clone(), file_key,)); - - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_bob_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - assert_ok!(FileSystem::bsp_volunteer( - bsp_charlie_signed.clone(), - file_key, - )); - - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_charlie_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - assert_noop!( - FileSystem::bsp_confirm_storing( - bsp_bob_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - ), - Error::::StorageRequestNotFound - ); - }); - } - - #[test] - fn bsp_failing_to_confirm_all_proofs_submitted_insufficient_capacity() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let storage_amount: StorageData = 100; - let size = 4; - - let msp_id = add_msp_to_provider_storage(&msp); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Force BSP to pass all threshold checks when volunteering. - pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { - if let Some(bsp) = bsp { - bsp.reputation_weight = ReputationWeightType::::max_value(); - } - }); - - let mut file_keys = Vec::new(); - - // Issue 5 storage requests and volunteer for each - for i in 0..5 { - let location = - FileLocation::::try_from(format!("test{}", i).into_bytes()).unwrap(); - let fingerprint = H256::repeat_byte(i as u8); - - let name = BoundedVec::try_from(format!("bucket{}", i).into_bytes()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Issue storage request - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - Default::default(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - file_keys.push(file_key); - - // Volunteer for storage - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - } - - // Set BSP storage capacity to 0 - pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { - if let Some(bsp) = bsp { - bsp.capacity = size * 2; - } - }); - - // Prepare proofs for all files - let non_inclusion_forest_proof = CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }; - - let file_keys_and_proofs: BoundedVec< - _, - ::MaxBatchConfirmStorageRequests, - > = file_keys - .into_iter() - .map(|file_key| { - ( - file_key, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ) - }) - .collect::>() - .try_into() - .unwrap(); - - // Attempt to confirm storing all files at once - assert_noop!( - FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - non_inclusion_forest_proof, - file_keys_and_proofs, - ), - Error::::InsufficientAvailableCapacity - ); - }); - } - } - - mod success { - use shp_traits::PaymentStreamsInterface; - - use super::*; - - #[test] - fn bsp_confirm_storing_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let new_root = Providers::get_root(bsp_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id, - bsp_id, - } - .into(), - ); - - // Assert that the payment stream between the BSP and the user has been created - assert!(<::PaymentStreams as PaymentStreamsInterface>::has_active_payment_stream(&bsp_id, &owner_account_id)); - }); - } - - #[test] - fn bsp_confirm_storing_correctly_updates_already_existing_payment_stream() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = - Providers::get_provider_id( - bsp_account_id.clone(), - ) - .unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![(file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })]).unwrap() - , - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let new_root = - Providers::get_root( - bsp_id, - ) - .unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id.clone(), - bsp_id, - } - .into(), - ); - - // Assert that the payment stream between the BSP and the user has been created and get its amount provided - let amount_provided_payment_stream = <::PaymentStreams as PaymentStreamsInterface>::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &owner_account_id); - assert!(amount_provided_payment_stream.is_some()); - assert_eq!(amount_provided_payment_stream.unwrap(), size); - - // Dispatch another storage request. - let current_block = System::block_number(); - let new_size = 8; - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - new_size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - new_size, - fingerprint, - ); - - // Advance a few blocks and dispatch BSP volunteer. - roll_to(current_block + 10); - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![(file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - })]).unwrap() - , - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: current_block, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size: new_size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let new_root = - Providers::get_root( - bsp_id, - ) - .unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the payment stream between the BSP and the user has been correctly updated - let new_amount_provided_payment_stream = <::PaymentStreams as PaymentStreamsInterface>::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &owner_account_id).unwrap(); - assert_eq!(amount_provided_payment_stream.unwrap() + new_size, new_amount_provided_payment_stream); - }); - } - } -} - -mod bsp_stop_storing { - use super::*; - mod failure { - use super::*; - #[test] - fn bsp_actions_not_a_bsp_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - RuntimeOrigin::signed(owner_account_id.clone()), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP stop storing. - assert_noop!( - FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::NotABsp - ); - }); - } - - #[test] - fn bsp_request_stop_storing_fails_if_file_key_does_not_match_metadata() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the RequestStorageBsps now contains the BSP under the location - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size - 1, // We change the size so the file key doesn't match the file's metadata - fingerprint, - ); - - // Dispatch BSP stop storing. - assert_noop!( - FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::InvalidFileKeyMetadata - ); - }); - } - - #[test] - fn bsp_request_stop_storing_fails_if_pending_stop_storing_request_exists() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the RequestStorageBsps now contains the BSP under the location - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP request stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Check that the request now exists. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); - - // Try sending the stop storing request again. - assert_noop!( - FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::PendingStopStoringRequestAlreadyExists - ); - }); - } - - #[test] - fn bsp_confirm_stop_storing_fails_if_not_enough_time_has_passed_since_request() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the RequestStorageBsps now contains the BSP under the location - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP request stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the RequestStorageBsps has the correct value - assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the request was added to the pending stop storing requests. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - - // Dispatch BSP confirm stop storing. - assert_noop!( - FileSystem::bsp_confirm_stop_storing( - bsp_signed.clone(), - file_key, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::MinWaitForStopStoringNotReached - ); - - // Assert that the pending stop storing request is still there. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); - }); - } - } - - mod success { - use super::*; - - #[test] - fn bsp_request_stop_storing_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the RequestStorageBsps now contains the BSP under the location - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP request stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the RequestStorageBsps has the correct value - assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the request was added to the pending stop storing requests. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - }); - } - - #[test] - fn bsp_confirm_stop_storing_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the RequestStorageBsps now contains the BSP under the location - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP request stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the RequestStorageBsps has the correct value - assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the request was added to the pending stop storing requests. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - - // Advance enough blocks to allow the BSP to confirm the stop storing request. - roll_to( - frame_system::Pallet::::block_number() + MinWaitForStopStoring::get(), - ); - - // Dispatch BSP confirm stop storing. - assert_ok!(FileSystem::bsp_confirm_stop_storing( - bsp_signed.clone(), - file_key, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the pending stop storing request was removed. - assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_none()); - - // Assert that the correct event was deposited. - let new_root = Providers::get_root(bsp_id).unwrap(); - - System::assert_last_event( - Event::BspConfirmStoppedStoring { - bsp_id, - file_key, - new_root, - } - .into(), - ); - }); - } - - #[test] - fn bsp_request_stop_storing_while_storage_request_open_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - Default::default(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap() - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - H256::zero(), - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the RequestStorageBsps has the correct value - assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint: H256::zero(), - size, - msp: Some((msp_id, false)), - user_peer_ids: Default::default(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - }); - } - - #[test] - fn bsp_request_stop_storing_not_volunteered_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(vec![1]).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - Default::default(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Increase the data used by the registered bsp, to simulate that it is indeed storing the file - assert_ok!(Providers::increase_capacity_used(&bsp_id, size,)); - - // Dispatch BSP stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - let current_bsps_required: ::ReplicationTargetType = - ReplicationTarget::::get(); - - // Assert that the storage request bsps_required was incremented - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: Default::default(), - data_server_sps: BoundedVec::default(), - bsps_required: current_bsps_required.checked_add(1).unwrap(), - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - }); - } - - #[test] - fn bsp_request_stop_storing_no_storage_request_success() { - new_test_ext().execute_with(|| { - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let owner_account_id = Keyring::Alice.to_account_id(); - let size = 4; - let fingerprint = H256::zero(); - - let msp_id = add_msp_to_provider_storage(&Keyring::Charlie.to_account_id()); - - let bucket_id = create_bucket( - &owner_account_id, - BoundedVec::try_from(b"bucket".to_vec()).unwrap(), - msp_id, - ); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), 100)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Increase the data used by the registered bsp, to simulate that it is indeed storing the file - assert_ok!(Providers::increase_capacity_used(&bsp_id, size,)); - - // Dispatch BSP stop storing. - assert_ok!(FileSystem::bsp_request_stop_storing( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - false, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the storage request was created with one bsps_required - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 5, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: None, - user_peer_ids: Default::default(), - data_server_sps: BoundedVec::default(), - bsps_required: 1, - bsps_confirmed: 0, - bsps_volunteered: 0, - }) - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspRequestedToStopStoring { - bsp_id, - file_key, - owner: owner_account_id, - location, - } - .into(), - ); - }); - } - } -} - -mod set_global_parameters_tests { - use super::*; - - mod failure { - use super::*; - #[test] - fn set_global_parameters_non_root_signer_fail() { - new_test_ext().execute_with(|| { - let non_root = Keyring::Bob.to_account_id(); - let non_root_signed = RuntimeOrigin::signed(non_root.clone()); - - // Assert BadOrigin error when non-root account tries to set the threshold - assert_noop!( - FileSystem::set_global_parameters(non_root_signed, None, None), - DispatchError::BadOrigin - ); - }); - } - - #[test] - fn set_global_parameters_0_value() { - new_test_ext().execute_with(|| { - assert_noop!( - FileSystem::set_global_parameters(RuntimeOrigin::root(), Some(0), None), - Error::::ReplicationTargetCannotBeZero - ); - - assert_noop!( - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(0)), - Error::::TickRangeToMaximumThresholdCannotBeZero - ); - }); - } - } - - mod success { - use super::*; - - #[test] - fn set_global_parameters() { - new_test_ext().execute_with(|| { - let root = RuntimeOrigin::root(); - - // Set the global parameters - assert_ok!(FileSystem::set_global_parameters( - root.clone(), - Some(3), - Some(10) - )); - - // Assert that the global parameters were set correctly - assert_eq!(ReplicationTarget::::get(), 3); - assert_eq!(TickRangeToMaximumThreshold::::get(), 10); - }); - } - } -} - -mod delete_file_and_pending_deletions_tests { - use super::*; - - mod failure { - use super::*; - - #[test] - fn delete_file_bucket_not_owned_by_user_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let _ = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - let other_user = Keyring::Bob.to_account_id(); - let bucket_id = create_bucket(&other_user.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }; - - // Assert that the user does not own the bucket - assert_noop!( - FileSystem::delete_file( - owner_signed, - bucket_id, - file_key, - location, - size, - fingerprint, - Some(forest_proof), - ), - Error::::NotBucketOwner - ); - }); - } - - #[test] - fn delete_file_beyond_maximum_limit_allowed_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = u64::MAX; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // For loop to create 1 over maximum of MaxUserPendingDeletionRequests - for i in 0..::MaxUserPendingDeletionRequests::get() { - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - i as u64, - fingerprint, - ); - - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location.clone(), - i as u64, - fingerprint, - None, - )); - } - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - assert_noop!( - FileSystem::delete_file( - owner_signed, - bucket_id, - file_key, - location, - size, - fingerprint, - None - ), - Error::::MaxUserPendingDeletionRequestsReached - ); - }); - } - - #[test] - fn delete_file_pending_file_deletion_request_submit_proof_not_msp_of_bucket_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Delete file - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location, - size, - fingerprint, - None, - )); - - // Assert that the pending file deletion request was added to storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( - vec![(file_key, bucket_id)] - ) - .unwrap() - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![vec![0]], - }; - - let msp_dave = Keyring::Dave.to_account_id(); - add_msp_to_provider_storage(&msp_dave); - let msp_origin = RuntimeOrigin::signed(msp_dave.clone()); - - assert_noop!( - FileSystem::pending_file_deletion_request_submit_proof( - msp_origin, - owner_account_id.clone(), - file_key, - bucket_id, - forest_proof - ), - Error::::MspNotStoringBucket - ); - - // Assert that the pending file deletion request was not removed from storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( - vec![(file_key, bucket_id)] - ) - .unwrap() - ); - }); - } - - #[test] - fn submit_proof_pending_file_deletion_not_found_fail() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - - let msp = Keyring::Charlie.to_account_id(); - let msp_origin = RuntimeOrigin::signed(msp.clone()); - - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![vec![0]], - }; - - assert_noop!( - FileSystem::pending_file_deletion_request_submit_proof( - msp_origin, - owner_account_id.clone(), - file_key, - bucket_id, - forest_proof - ), - Error::::FileKeyNotPendingDeletion - ); - }); - } - } - - mod success { - use super::*; - #[test] - fn delete_file_with_proof_of_inclusion_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }; - - // Delete file - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location, - size, - fingerprint, - Some(forest_proof), - )); - - // Assert that there is a queued priority challenge for file key in proofs dealer pallet - assert!( - // Find file key in vec of queued priority challenges - pallet_proofs_dealer::PriorityChallengesQueue::::get() - .iter() - .any(|x| *x == (file_key, Some(TrieRemoveMutation))), - ); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::FileDeletionRequest { - user: owner_account_id.clone(), - file_key, - bucket_id, - msp_id, - proof_of_inclusion: true, - } - .into(), - ); - }); - } - - #[test] - fn delete_file_expired_pending_file_deletion_request_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Delete file - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location, - size, - fingerprint, - None, - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::FileDeletionRequest { - user: owner_account_id.clone(), - file_key, - bucket_id, - msp_id, - proof_of_inclusion: false, - } - .into(), - ); - - // Assert that the pending file deletion request was added to storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( - vec![(file_key, bucket_id)] - ) - .unwrap() - ); - - let pending_file_deletion_request_ttl: u32 = - PendingFileDeletionRequestTtl::::get(); - let pending_file_deletion_request_ttl: BlockNumberFor = - pending_file_deletion_request_ttl.into(); - let expiration_block = pending_file_deletion_request_ttl + System::block_number(); - - // Assert that the pending file deletion request was added to storage - assert_eq!( - file_system::FileDeletionRequestExpirations::::get(expiration_block), - vec![( - owner_account_id.clone(), - file_key - )] - ); - - // Roll past the expiration block - roll_to(pending_file_deletion_request_ttl + 1); - - // Item expiration should be removed - assert_eq!( - file_system::FileDeletionRequestExpirations::::get(expiration_block), - vec![] - ); - - // Asser that the pending file deletion request was removed from storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() - ); - - // Assert that there is a queued priority challenge for file key in proofs dealer pallet - assert!(pallet_proofs_dealer::PriorityChallengesQueue::::get() - .iter() - .any(|x| *x == (file_key, Some(TrieRemoveMutation))),); - }); - } - - #[test] - fn delete_file_pending_file_deletion_request_submit_proof_of_inclusion_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Delete file - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location, - size, - fingerprint, - None, - )); - - // Assert that the pending file deletion request was added to storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( - vec![(file_key, bucket_id)] - ) - .unwrap() - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }; - - let msp_origin = RuntimeOrigin::signed(msp.clone()); - - assert_ok!(FileSystem::pending_file_deletion_request_submit_proof( - msp_origin, - owner_account_id.clone(), - file_key, - bucket_id, - forest_proof - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::ProofSubmittedForPendingFileDeletionRequest { - msp_id, - user: owner_account_id.clone(), - file_key, - bucket_id, - proof_of_inclusion: true, - } - .into(), - ); - - // Assert that there is a queued priority challenge for file key in proofs dealer pallet - assert!(pallet_proofs_dealer::PriorityChallengesQueue::::get() - .iter() - .any(|x| *x == (file_key, Some(TrieRemoveMutation))),); - - // Assert that the pending file deletion request was removed from storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() - ); - }); - } - - #[test] - fn delete_file_pending_file_deletion_request_submit_proof_of_non_inclusion_success() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Delete file - assert_ok!(FileSystem::delete_file( - owner_signed.clone(), - bucket_id, - file_key, - location, - size, - fingerprint, - None, - )); - - // Assert that the pending file deletion request was added to storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( - vec![(file_key, bucket_id)] - ) - .unwrap() - ); - - let forest_proof = CompactProof { - encoded_nodes: vec![H256::zero().as_bytes().to_vec()], - }; - - let msp_origin = RuntimeOrigin::signed(msp.clone()); - - assert_ok!(FileSystem::pending_file_deletion_request_submit_proof( - msp_origin, - owner_account_id.clone(), - file_key, - bucket_id, - forest_proof - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::ProofSubmittedForPendingFileDeletionRequest { - msp_id, - user: owner_account_id.clone(), - file_key, - bucket_id, - proof_of_inclusion: false, - } - .into(), - ); - - // Assert that there is a queued priority challenge for file key in proofs dealer pallet - assert!( - !pallet_proofs_dealer::PriorityChallengesQueue::::get() - .iter() - .any(|x| *x == (file_key, Some(TrieRemoveMutation))), - ); - - // Assert that the pending file deletion request was removed from storage - assert_eq!( - file_system::PendingFileDeletionRequests::::get(owner_account_id), - BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() - ); - }); - } - } -} - -mod compute_threshold { - use super::*; - mod success { - use super::*; - #[test] - fn query_earliest_file_volunteer_tick() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let block_number = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - - assert!(frame_system::Pallet::::block_number() <= block_number); - }); - } - - #[test] - fn compute_threshold_to_succeed() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - let storage_request = file_system::StorageRequests::::get(file_key).unwrap(); - - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); - - assert_eq!(TickRangeToMaximumThreshold::::get(), 1); - - let (threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) - .unwrap(); - - assert!( - threshold_to_succeed > 0 - && threshold_to_succeed <= ThresholdType::::max_value() - ); - assert!(slope > 0); - - let block_number = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - - // BSP should be able to volunteer immediately for the storage request since the TickRangeToMaximumThreshold is 1 - assert_eq!(block_number, frame_system::Pallet::::block_number()); - - let starting_bsp_weight: pallet_storage_providers::types::ReputationWeightType< - Test, - > = ::StartingReputationWeight::get(); - - // Simulate there being many BSPs in the network with high reputation weight - pallet_storage_providers::GlobalBspsReputationWeight::::set( - 1000u32.saturating_mul(starting_bsp_weight.into()), - ); - - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1000000000)) - .unwrap(); - - assert_eq!(TickRangeToMaximumThreshold::::get(), 1000000000); - - let (threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) - .unwrap(); - - assert!( - threshold_to_succeed > 0 - && threshold_to_succeed <= ThresholdType::::max_value() - ); - assert!(slope > 0); - - let block_number = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - - // BSP can only volunteer after some number of blocks have passed. - assert!(block_number > frame_system::Pallet::::block_number()); - - // Set reputation weight of BSP to max - pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { - match bsp { - Some(bsp) => { - bsp.reputation_weight = u32::MAX; - } - None => { - panic!("BSP should exits"); - } - } - }); - - let (threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) - .unwrap(); - - assert!( - threshold_to_succeed > 0 - && threshold_to_succeed <= ThresholdType::::max_value() - ); - assert!(slope > 0); - - let block_number = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - - // BSP should be able to volunteer immediately for the storage request since the reputation weight is so high. - assert_eq!(block_number, frame_system::Pallet::::block_number()); - }); - } - - #[test] - fn compute_threshold_to_succeed_returns_max_when_bsp_weight_max() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let user = RuntimeOrigin::signed(owner_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let file_content = b"test".to_vec(); - let fingerprint = BlakeTwo256::hash(&file_content); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::issue_storage_request( - user.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - - let storage_amount: StorageData = 100; - - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set reputation weight of BSP to max - pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { - match bsp { - Some(bsp) => { - bsp.reputation_weight = u32::MAX; - } - None => { - panic!("BSP should exits"); - } - } - }); - - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); - - assert_eq!(TickRangeToMaximumThreshold::::get(), 1); - - let (threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, 0).unwrap(); - - assert_eq!(threshold_to_succeed, ThresholdType::::max_value()); - assert!(slope > 0); - - let block_number = - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); - - // BSP should be able to volunteer immediately for the storage request since the reputation weight is so high. - assert_eq!(block_number, frame_system::Pallet::::block_number()); - }); - } - #[test] - fn compute_threshold_to_succeed_fails_when_global_weight_zero() { - new_test_ext().execute_with(|| { - // Setup: create a BSP - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set global_weight to zero - pallet_storage_providers::GlobalBspsReputationWeight::::set(0); - - let requested_at = frame_system::Pallet::::block_number(); - - let result = FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at); - - assert_noop!(result, Error::::NoGlobalReputationWeightSet); - }); - } - - #[test] - fn compute_threshold_to_succeed_with_one_block_range() { - new_test_ext().execute_with(|| { - // Setup: create a BSP - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set TickRangeToMaximumThreshold to one - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); - - assert_eq!(TickRangeToMaximumThreshold::::get(), 1); - - let requested_at = frame_system::Pallet::::block_number(); - - let (threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at).unwrap(); - - // Check that base_slope is set to one due to division by zero handling - assert!(slope > ThresholdType::::zero()); - - // Ensure threshold_to_succeed is greater than zero - assert!(threshold_to_succeed > ThresholdType::::zero()); - }); - } - - #[test] - fn compute_threshold_to_succeed_with_max_slope() { - new_test_ext().execute_with(|| { - // Setup: create a BSP - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set global_weight to 1 - pallet_storage_providers::GlobalBspsReputationWeight::::set(1); - - // Set ReplicationTarget to 2 - ReplicationTarget::::set(2); - - // Set TickRangeToMaximumThreshold to a non-zero value - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); - - // Set max reputation weight - pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { - match bsp { - Some(bsp) => { - bsp.reputation_weight = u32::MAX; - } - None => { - panic!("BSP should exist"); - } - } - }); - - let requested_at = frame_system::Pallet::::block_number(); - - let (_threshold_to_succeed, slope) = - FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at).unwrap(); - - assert_eq!(slope, ThresholdType::::max_value()); - }); - } - - #[test] - fn bsp_with_higher_weight_should_have_higher_slope() { - new_test_ext().execute_with(|| { - // Setup: create a BSP - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_bob_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Create another BSP with higher weight - let bsp_account_id = Keyring::Charlie.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_charlie_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set global_weight to the sum of the two BSPs reputation weights - pallet_storage_providers::GlobalBspsReputationWeight::::set(10 + 1); - - // Set ReplicationTarget to 2 - ReplicationTarget::::set(2); - - // Set TickRangeToMaximumThreshold to a non-zero value - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); - - let requested_at = frame_system::Pallet::::block_number(); - - let (_threshold_to_succeed, slope_bsp_1) = - FileSystem::compute_threshold_to_succeed(&bsp_bob_id, requested_at).unwrap(); - - // Set BSP's reputation weight to 10 (10 times higher than the other BSP) - pallet_storage_providers::BackupStorageProviders::::mutate( - &bsp_charlie_id, - |bsp| match bsp { - Some(bsp) => { - bsp.reputation_weight = 10; - } - None => { - panic!("BSP should exist"); - } - }, - ); - - let (_threshold_to_succeed, slope_bsp_2) = - FileSystem::compute_threshold_to_succeed(&bsp_charlie_id, requested_at) - .unwrap(); - - // BSP with higher weight should have higher slope - assert!(slope_bsp_2 > slope_bsp_1); - }); - } - - #[test] - fn compute_threshold_to_succeed_slope_should_be_equal_for_all_starting_weight() { - new_test_ext().execute_with(|| { - // Setup: create a BSP - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_bob_id = Providers::get_provider_id(bsp_account_id).unwrap(); - // Create another BSP - let bsp_account_id = Keyring::Charlie.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let storage_amount: StorageData = 100; - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - let bsp_charlie_id = Providers::get_provider_id(bsp_account_id).unwrap(); - - // Set global_weight to the sum of the weights of the BSPs - pallet_storage_providers::GlobalBspsReputationWeight::::set(1 + 1); - - // Set ReplicationTarget to 2 - ReplicationTarget::::set(2); - - // Set TickRangeToMaximumThreshold to a non-zero value - FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); - - let requested_at = frame_system::Pallet::::block_number(); - - let (_threshold_to_succeed, slope_bsp_1) = - FileSystem::compute_threshold_to_succeed(&bsp_bob_id, requested_at).unwrap(); - - let (_threshold_to_succeed, slope_bsp_2) = - FileSystem::compute_threshold_to_succeed(&bsp_charlie_id, requested_at) - .unwrap(); - - // BSPs with equal weight should have equal slope - assert_eq!(slope_bsp_2, slope_bsp_1); - }); - } - } -} - -mod stop_storing_for_insolvent_user { - use super::*; - - mod success { - - use shp_traits::PaymentStreamsInterface; - - use super::*; - - #[test] - fn stop_storing_for_insolvent_user_works_for_bsps() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Eve.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let new_root = Providers::get_root(bsp_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id, - bsp_id, - } - .into(), - ); - - // Assert that the capacity used by the BSP was updated - assert_eq!( - pallet_storage_providers::BackupStorageProviders::::get(bsp_id) - .expect("BSP should exist in storage") - .capacity_used, - size - ); - - // Now that the BSP has confirmed storing, we can simulate the user being insolvent - // and the BSP stopping storing for the user. - // Try to stop storing for the insolvent user. - assert_ok!(FileSystem::stop_storing_for_insolvent_user( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::SpStopStoringInsolventUser { - sp_id: bsp_id, - file_key, - owner: owner_account_id, - location, - new_root, - } - .into(), - ); - - // Assert that the capacity used by the BSP was updated - assert_eq!( - pallet_storage_providers::BackupStorageProviders::::get(bsp_id) - .expect("BSP should exist in storage") - .capacity_used, - 0 - ); - }); - } - - #[test] - fn stop_storing_for_insolvent_user_works_for_msps() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Eve.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let msp_signed = RuntimeOrigin::signed(msp.clone()); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 50; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Dispatch MSP confirm storing. - assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( - RuntimeOrigin::signed(msp.clone()), - bounded_vec![( - bucket_id, - MspStorageRequestResponse { - accept: Some(AcceptedStorageRequestParameters { - file_keys_and_proofs: bounded_vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )], - non_inclusion_forest_proof: CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - }), - reject: None - } - )] - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, true)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Assert that the capacity used by the MSP was updated - assert_eq!( - pallet_storage_providers::MainStorageProviders::::get(msp_id) - .expect("MSP should exist in storage") - .capacity_used, - size - ); - - // Now that the MSP has accepted storing, we can simulate the user being insolvent - // and the MSP stopping storing for the user. - // Try to stop storing for the insolvent user. - assert_ok!(FileSystem::stop_storing_for_insolvent_user( - msp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Get the new bucket root after deletion - let new_bucket_root_after_deletion = - Providers::get_root_bucket(&bucket_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::SpStopStoringInsolventUser { - sp_id: msp_id, - file_key, - owner: owner_account_id, - location, - new_root: new_bucket_root_after_deletion, - } - .into(), - ); - - // Assert that the capacity used by the MSP was updated - assert_eq!( - pallet_storage_providers::MainStorageProviders::::get(msp_id) - .expect("MSP should exist in storage") - .capacity_used, - 0 - ); - }); - } - - #[test] - fn stop_storing_for_insolvent_user_works_if_user_does_not_have_payment_stream_with_sp() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let new_root = Providers::get_root(bsp_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id, - bsp_id, - } - .into(), - ); - - // Assert that the capacity used by the BSP was updated - assert_eq!( - pallet_storage_providers::BackupStorageProviders::::get(bsp_id) - .expect("BSP should exist in storage") - .capacity_used, - size - ); - - // Now that the BSP has confirmed storing, we can simulate the payment stream being deleted - // and the BSP stopping storing for the user. Note that we use Alice as a user for this test, - // which is NOT an insolvent user. This simulates the case where the user has correctly paid all - // its debt but a lagging SP has not updated its storage state yet. - - // Delete the payment stream between the user and the BSP. - assert_ok!( - ::delete_dynamic_rate_payment_stream( - &bsp_id, - &owner_account_id, - ) - ); - // Try to stop storing the user's file as the BSP. - assert_ok!(FileSystem::stop_storing_for_insolvent_user( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - )); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::SpStopStoringInsolventUser { - sp_id: bsp_id, - file_key, - owner: owner_account_id, - location, - new_root, - } - .into(), - ); - - // Assert that the capacity used by the BSP was updated - assert_eq!( - pallet_storage_providers::BackupStorageProviders::::get(bsp_id) - .expect("BSP should exist in storage") - .capacity_used, - 0 - ); - }); - } - } - - mod failure { - - use super::*; - - #[test] - fn stop_storing_for_insolvent_user_fails_if_caller_not_a_sp() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Eve.to_account_id(); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Try to stop storing for the insolvent user. - assert_noop!( - FileSystem::stop_storing_for_insolvent_user( - bsp_signed.clone(), - H256::zero(), - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![H256::zero().as_ref().to_vec()], - }, - ), - Error::::NotASp - ); - }); - } - - #[test] - fn stop_storing_for_insolvent_user_fails_if_user_not_insolvent() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Alice.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let new_root = Providers::get_root(bsp_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id, - bsp_id, - } - .into(), - ); - - // Now that the BSP has confirmed storing, we can simulate the user being not insolvent - // and the BSP trying to stop storing a file for the user using the stop_storing_for_insolvent_user function. - assert_noop!( - FileSystem::stop_storing_for_insolvent_user( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::UserNotInsolvent - ); - }); - } - - #[test] - fn stop_storing_for_insolvent_user_fails_if_caller_not_owner_of_bucket() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Eve.to_account_id(); - let msp = Keyring::Charlie.to_account_id(); - let another_msp = Keyring::Dave.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - - let msp_id = add_msp_to_provider_storage(&msp); - add_msp_to_provider_storage(&another_msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Try to stop storing for the insolvent user using another MSP account. - assert_noop!( - FileSystem::stop_storing_for_insolvent_user( - RuntimeOrigin::signed(another_msp.clone()), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![file_key.as_ref().to_vec()], - }, - ), - Error::::MspNotStoringBucket - ); - }); - } - - #[test] - fn stop_storing_for_insolvent_user_fails_if_proof_does_not_contain_file_key() { - new_test_ext().execute_with(|| { - let owner_account_id = Keyring::Eve.to_account_id(); - let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); - let bsp_account_id = Keyring::Bob.to_account_id(); - let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); - let msp = Keyring::Charlie.to_account_id(); - let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); - let size = 4; - let fingerprint = H256::zero(); - let peer_id = BoundedVec::try_from(vec![1]).unwrap(); - let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); - let storage_amount: StorageData = 100; - - let msp_id = add_msp_to_provider_storage(&msp); - - let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); - let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); - - // Dispatch storage request. - assert_ok!(FileSystem::issue_storage_request( - owner_signed.clone(), - bucket_id, - location.clone(), - fingerprint, - size, - msp_id, - peer_ids.clone(), - )); - - // Sign up account as a Backup Storage Provider - assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); - - // Dispatch BSP volunteer. - assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); - - // Get the current tick number. - let tick_when_confirming = ProofsDealer::get_current_tick(); - - // Dispatch BSP confirm storing. - assert_ok!(FileSystem::bsp_confirm_storing( - bsp_signed.clone(), - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - }, - BoundedVec::try_from(vec![( - file_key, - CompactProof { - encoded_nodes: vec![H256::default().as_ref().to_vec()], - } - )]) - .unwrap(), - )); - - // Assert that the storage was updated - assert_eq!( - file_system::StorageRequests::::get(file_key), - Some(StorageRequestMetadata { - requested_at: 1, - owner: owner_account_id.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp: Some((msp_id, false)), - user_peer_ids: peer_ids.clone(), - data_server_sps: BoundedVec::default(), - bsps_required: ReplicationTarget::::get(), - bsps_confirmed: 1, - bsps_volunteered: 1, - }) - ); - - // Assert that the RequestStorageBsps was updated - assert_eq!( - file_system::StorageRequestBsps::::get(file_key, bsp_id) - .expect("BSP should exist in storage"), - StorageRequestBspsMetadata:: { - confirmed: true, - _phantom: Default::default() - } - ); - - let file_key = FileSystem::compute_file_key( - owner_account_id.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - let new_root = Providers::get_root(bsp_id).unwrap(); - - // Assert that the correct event was deposited - System::assert_last_event( - Event::BspConfirmedStoring { - who: bsp_account_id.clone(), - bsp_id, - file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), - new_root, - } - .into(), - ); - - // Assert that the proving cycle was initialised for this BSP. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); - - // Assert that the correct event was deposited. - System::assert_has_event( - Event::BspChallengeCycleInitialised { - who: bsp_account_id, - bsp_id, - } - .into(), - ); - - // Now that the BSP has confirmed storing, we can simulate the user being insolvent - // and the BSP trying to stop storing for that user with an incorrect inclusion proof. - assert_noop!( - FileSystem::stop_storing_for_insolvent_user( - bsp_signed.clone(), - file_key, - bucket_id, - location.clone(), - owner_account_id.clone(), - fingerprint, - size, - CompactProof { - encoded_nodes: vec![H256::zero().as_ref().to_vec()], - }, - ), - Error::::ExpectedInclusionProof - ); - }); - } - } -} - -/// Helper function that registers an account as a Backup Storage Provider -fn bsp_sign_up( - bsp_signed: RuntimeOrigin, - storage_amount: StorageData, -) -> DispatchResultWithPostInfo { - let multiaddresses = create_sp_multiaddresses(); - - // Request to sign up the account as a Backup Storage Provider - assert_ok!(Providers::request_bsp_sign_up( - bsp_signed.clone(), - storage_amount, - multiaddresses, - bsp_signed.clone().into_signer().unwrap() - )); - - // Advance enough blocks for randomness to be valid - roll_to(frame_system::Pallet::::block_number() + 4); - - // Confirm the sign up of the account as a Backup Storage Provider - assert_ok!(Providers::confirm_sign_up(bsp_signed.clone(), None)); - - Ok(().into()) -} - -fn create_sp_multiaddresses( -) -> BoundedVec, MaxMultiAddressAmount> { - let mut multiaddresses: BoundedVec, MaxMultiAddressAmount> = - BoundedVec::new(); - multiaddresses.force_push( - "/ip4/127.0.0.1/udp/1234" - .as_bytes() - .to_vec() - .try_into() - .unwrap(), - ); - multiaddresses -} - -fn add_msp_to_provider_storage(msp: &sp_runtime::AccountId32) -> ProviderIdFor { - let msp_hash = <::Hashing as Hasher>::hash(msp.as_slice()); - - let msp_info = pallet_storage_providers::types::MainStorageProvider { - buckets: BoundedVec::default(), - capacity: 100, - capacity_used: 0, - multiaddresses: BoundedVec::default(), - value_prop: pallet_storage_providers::types::ValueProposition { - identifier: pallet_storage_providers::types::ValuePropId::::default(), - data_limit: 100, - protocols: BoundedVec::default(), - }, - last_capacity_change: frame_system::Pallet::::block_number(), - owner_account: msp.clone(), - payment_account: msp.clone(), - }; - - pallet_storage_providers::MainStorageProviders::::insert(msp_hash, msp_info); - pallet_storage_providers::AccountIdToMainStorageProviderId::::insert( - msp.clone(), - msp_hash, - ); - - msp_hash -} - -fn create_bucket( - owner: &sp_runtime::AccountId32, - name: BucketNameFor, - msp_id: ProviderIdFor, -) -> BucketIdFor { - let bucket_id = - ::Providers::derive_bucket_id(&msp_id, &owner, name.clone()); - - let origin = RuntimeOrigin::signed(owner.clone()); - - // Dispatch a signed extrinsic. - assert_ok!(FileSystem::create_bucket( - origin, - msp_id, - name.clone(), - false - )); - - // Assert bucket was created - assert_eq!( - pallet_storage_providers::Buckets::::get(bucket_id), - Some(Bucket { - root: ::DefaultMerkleRoot::get(), - user_id: owner.clone(), - msp_id, - private: false, - read_access_group_id: None, - size: 0, - }) - ); - - bucket_id -} +use crate::{ + self as file_system, + mock::*, + types::{ + AcceptedStorageRequestParameters, BatchResponses, BucketIdFor, BucketMoveRequestResponse, + BucketNameFor, FileKeyResponsesInput, FileLocation, MerkleHash, MoveBucketRequestMetadata, + MspFailedBatchStorageRequests, MspRejectedBatchStorageRequests, + MspRespondStorageRequestsResult, MspStorageRequestResponse, PeerIds, + PendingFileDeletionRequestTtl, ProviderIdFor, StorageData, StorageRequestBspsMetadata, + StorageRequestMetadata, StorageRequestTtl, ThresholdType, + }, + Config, DataServersForMoveBucket, Error, Event, PendingBucketsToMove, + PendingMoveBucketRequests, PendingStopStoringRequests, ReplicationTarget, + StorageRequestExpirations, StorageRequests, TickRangeToMaximumThreshold, +}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + traits::{nonfungibles_v2::Destroy, Hooks, OriginTrait}, + weights::Weight, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_proofs_dealer::{LastTickProviderSubmittedAProofFor, PriorityChallengesQueue}; +use pallet_storage_providers::types::Bucket; +use shp_traits::{ + MutateStorageProvidersInterface, ReadBucketsInterface, ReadChallengeableProvidersInterface, + ReadStorageProvidersInterface, TrieRemoveMutation, +}; +use sp_core::{ByteArray, Hasher, H256}; +use sp_keyring::sr25519::Keyring; +use sp_runtime::{ + bounded_vec, + traits::{BlakeTwo256, Get, Zero}, + BoundedVec, DispatchError, +}; +use sp_std::collections::btree_map::BTreeMap; +use sp_trie::CompactProof; + +mod create_bucket_tests { + use super::*; + + mod failure { + use super::*; + + #[test] + fn create_bucket_msp_not_provider_fail() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + + assert_noop!( + FileSystem::create_bucket( + origin, + H256::from_slice(&msp.as_slice()), + name, + true + ), + Error::::NotAMsp + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn create_private_bucket_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = true; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin, + msp_id, + name.clone(), + private + )); + + // Check if collection was created + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewBucket { + who: owner, + msp_id, + bucket_id, + name, + collection_id: Some(0), + private, + } + .into(), + ); + }); + } + + #[test] + fn create_public_bucket_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = false; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin, + msp_id, + name.clone(), + private + )); + + // Check that the bucket does not have a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_none() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewBucket { + who: owner, + msp_id, + bucket_id, + name, + collection_id: None, + private, + } + .into(), + ); + }); + } + } +} + +mod request_move_bucket { + use super::*; + mod failure { + use super::*; + + #[test] + fn move_bucket_while_storage_request_opened() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + origin.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_charlie_id, + peer_ids.clone(), + )); + + assert_noop!( + FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), + Error::::StorageRequestExists + ); + }); + } + + #[test] + fn move_bucket_when_already_requested() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + assert_noop!( + FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), + Error::::BucketIsBeingMoved + ); + }); + } + + #[test] + fn move_bucket_request_to_msp_already_storing_it() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + assert_noop!( + FileSystem::request_move_bucket(origin, bucket_id, msp_charlie_id), + Error::::MspAlreadyStoringBucket + ); + }); + } + + #[test] + fn move_bucket_to_non_existent_msp() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = + <::Hashing as Hasher>::hash(&msp_dave.as_slice()); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + assert_noop!( + FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), + Error::::NotAMsp + ); + }); + } + + #[test] + fn move_bucket_not_bucket_owner() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let not_owner = Keyring::Bob.to_account_id(); + let origin = RuntimeOrigin::signed(not_owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + assert_noop!( + FileSystem::request_move_bucket(origin, bucket_id, msp_dave_id), + Error::::NotBucketOwner + ); + }); + } + + #[test] + fn move_bucket_request_accepted_msp_not_enough_capacity() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Issue storage request with a big file size + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = vec![0; 1000]; + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let size = 1000; + + // Set replication target to 1 to automatically fulfill the storage request after a single bsp confirms. + crate::ReplicationTarget::::put(1); + + assert_ok!(FileSystem::issue_storage_request( + origin.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_charlie_id, + peer_ids.clone(), + )); + + // Compute the file key. + let file_key = FileSystem::compute_file_key( + owner.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Manually set enough capacity for Charlie + pallet_storage_providers::MainStorageProviders::::mutate( + msp_charlie_id, + |msp| { + if let Some(msp) = msp { + msp.capacity = 1000; + } + }, + ); + + // Dispatch the MSP accept request. + // This operation increases the bucket size. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp_charlie), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None + } + )] + )); + + // Check bucket size + let bucket_size = Providers::get_bucket_size(&bucket_id).unwrap(); + assert_eq!(bucket_size, size); + + // BSP confirm storage request + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), size * 2)); + + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner, + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + // Manually set low capacity for Dave + pallet_storage_providers::MainStorageProviders::::mutate( + msp_dave_id, + |msp| { + if let Some(msp) = msp { + msp.capacity = 0; + } + }, + ); + + // Dispatch a signed extrinsic. + assert_noop!( + FileSystem::msp_respond_move_bucket_request( + RuntimeOrigin::signed(msp_dave), + bucket_id, + BucketMoveRequestResponse::Accepted + ), + Error::::InsufficientAvailableCapacity + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn move_bucket_request_and_accepted_by_new_msp() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner, + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::msp_respond_move_bucket_request( + RuntimeOrigin::signed(msp_dave), + bucket_id, + BucketMoveRequestResponse::Accepted + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketAccepted { + msp_id: msp_dave_id, + bucket_id, + } + .into(), + ); + + // Check bucket is stored by Dave + assert!(Providers::is_bucket_stored_by_msp(&msp_dave_id, &bucket_id)); + + // Check pending bucket storages are cleared + assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); + assert!(!PendingMoveBucketRequests::::contains_key( + &msp_dave_id, + bucket_id + )); + }); + } + + #[test] + fn move_bucket_request_and_rejected_by_new_msp() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner, + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::msp_respond_move_bucket_request( + RuntimeOrigin::signed(msp_dave), + bucket_id, + BucketMoveRequestResponse::Rejected + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRejected { + msp_id: msp_dave_id, + bucket_id, + } + .into(), + ); + + // Check bucket is still stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Check pending bucket storages are cleared + assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); + assert!(!PendingMoveBucketRequests::::contains_key( + &msp_dave_id, + bucket_id + )); + }); + } + + #[test] + fn move_bucket_request_and_expires() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner, + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + // Check move bucket request expires after MoveBucketRequestTtl + let move_bucket_request_ttl: u32 = ::MoveBucketRequestTtl::get(); + let move_bucket_request_ttl: BlockNumber = move_bucket_request_ttl.into(); + let expiration = move_bucket_request_ttl + System::block_number(); + + // Move block number to expiration + roll_to(expiration); + + assert!(!PendingBucketsToMove::::contains_key(&bucket_id)); + assert!(!PendingMoveBucketRequests::::contains_key( + &msp_dave_id, + bucket_id + )); + }); + } + } +} + +mod bsp_add_data_server_for_move_bucket_request { + use super::*; + + mod failure { + use super::*; + + #[test] + fn not_a_bsp() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let bucket_id = H256::zero(); + + assert_noop!( + FileSystem::bsp_add_data_server_for_move_bucket_request(origin, bucket_id), + Error::::NotABsp + ); + }); + } + + #[test] + fn no_move_bucket_request_found() { + new_test_ext().execute_with(|| { + let bsp_account_id = Keyring::Bob.to_account_id(); + let origin = RuntimeOrigin::signed(bsp_account_id.clone()); + let bucket_id = H256::zero(); + + assert_ok!(bsp_sign_up(origin.clone(), 1000)); + + assert_noop!( + FileSystem::bsp_add_data_server_for_move_bucket_request(origin, bucket_id), + Error::::MoveBucketRequestNotFound + ); + }); + } + + #[test] + fn bsp_already_data_server() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + assert_ok!(bsp_sign_up( + RuntimeOrigin::signed(bsp_account_id.clone()), + 1000 + )); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner.clone(), + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + assert_ok!(FileSystem::bsp_add_data_server_for_move_bucket_request( + RuntimeOrigin::signed(bsp_account_id.clone()), + bucket_id, + )); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { requester: owner }) + ); + assert_eq!( + DataServersForMoveBucket::::iter_key_prefix(&bucket_id).next(), + Some(bsp_id) + ); + + assert_noop!( + FileSystem::bsp_add_data_server_for_move_bucket_request( + RuntimeOrigin::signed(bsp_account_id.clone()), + bucket_id, + ), + Error::::BspAlreadyDataServer + ); + }); + } + } + + mod success { + use crate::DataServersForMoveBucket; + + use super::*; + + #[test] + fn add_bsp_as_data_server() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + assert_ok!(bsp_sign_up( + RuntimeOrigin::signed(bsp_account_id.clone()), + 1000 + )); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert!(PendingBucketsToMove::::contains_key(&bucket_id)); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MoveBucketRequested { + who: owner.clone(), + bucket_id, + new_msp_id: msp_dave_id, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::bsp_add_data_server_for_move_bucket_request( + RuntimeOrigin::signed(bsp_account_id.clone()), + bucket_id, + )); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { requester: owner }) + ); + assert_eq!( + DataServersForMoveBucket::::iter_key_prefix(&bucket_id).next(), + Some(bsp_id) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::DataServerRegisteredForMoveBucket { bsp_id, bucket_id }.into(), + ); + }); + } + } +} + +mod update_bucket_privacy_tests { + use super::*; + + mod failure { + use super::*; + + #[test] + fn update_bucket_privacy_bucket_not_found_fail() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + assert_noop!( + FileSystem::update_bucket_privacy(origin, bucket_id, false), + pallet_storage_providers::Error::::BucketNotFound + ); + }); + } + } + + mod success { + use super::*; + #[test] + fn update_bucket_privacy_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = true; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin.clone(), + msp_id, + name.clone(), + private + )); + + // Check if collection was created + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewBucket { + who: owner.clone(), + msp_id, + bucket_id, + name, + collection_id: Some(0), + private, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, false)); + + // Check that the bucket still has a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BucketPrivacyUpdated { + who: owner, + bucket_id, + collection_id: Some(0), + private: false, + } + .into(), + ); + }); + } + + #[test] + fn update_bucket_privacy_collection_remains_after_many_privacy_updates_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = true; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin.clone(), + msp_id, + name.clone(), + private + )); + + // Check if collection was created + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewBucket { + who: owner.clone(), + msp_id, + bucket_id, + name, + collection_id: Some(0), + private, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::update_bucket_privacy( + origin.clone(), + bucket_id, + false + )); + + // Check that the bucket still has a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BucketPrivacyUpdated { + who: owner.clone(), + bucket_id, + collection_id: Some(0), + private: false, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, true)); + + // Check that the bucket still has a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BucketPrivacyUpdated { + who: owner, + bucket_id, + collection_id: Some(0), + private: true, + } + .into(), + ); + }); + } + + #[test] + fn update_bucket_privacy_delete_collection_before_going_from_public_to_private_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = true; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin.clone(), + msp_id, + name.clone(), + private + )); + + // Check that the bucket does not have a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewBucket { + who: owner.clone(), + msp_id, + bucket_id, + name, + collection_id: Some(0), + private, + } + .into(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::update_bucket_privacy( + origin.clone(), + bucket_id, + false + )); + + // Check that the bucket still has a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + let collection_id = + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id, + ) + .unwrap() + .expect("Collection ID should exist"); + + let w = Nfts::get_destroy_witness(&collection_id).unwrap(); + + // Delete collection before going from public to private bucket + assert_ok!(Nfts::destroy(origin.clone(), collection_id, w)); + + // Update bucket privacy from public to private + assert_ok!(FileSystem::update_bucket_privacy(origin, bucket_id, true)); + + // Check that the bucket still has a corresponding collection + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + // Assert that the correct event was deposited and that a new collection with index 1 has been created + System::assert_last_event( + Event::BucketPrivacyUpdated { + who: owner, + bucket_id, + collection_id: Some(1), + private: true, + } + .into(), + ); + }); + } + } +} + +mod create_and_associate_collection_with_bucket_tests { + use super::*; + + mod failure { + use super::*; + + #[test] + fn create_and_associate_collection_with_bucket_bucket_not_found_fail() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + assert_noop!( + FileSystem::create_and_associate_collection_with_bucket(origin, bucket_id), + pallet_storage_providers::Error::::BucketNotFound + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn create_and_associate_collection_with_bucket_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let private = true; + + let msp_id = add_msp_to_provider_storage(&msp); + + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + &owner, + name.clone(), + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin.clone(), + msp_id, + name.clone(), + private + )); + + // Check if collection was created + assert!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .is_some() + ); + + let collection_id = + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id, + ) + .unwrap() + .expect("Collection ID should exist"); + + assert_ok!(FileSystem::create_and_associate_collection_with_bucket( + origin, bucket_id + )); + + // Check if collection was associated with the bucket + assert_ne!( + ::Providers::get_read_access_group_id_of_bucket( + &bucket_id + ) + .unwrap() + .expect("Collection ID should exist"), + collection_id + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewCollectionAndAssociation { + who: owner, + bucket_id, + collection_id: 1, // Collection ID should be incremented from 0 + } + .into(), + ); + }); + } + } +} + +mod request_storage { + use super::*; + mod failure { + use super::*; + + #[test] + fn request_storage_bucket_does_not_exist_fail() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name: BucketNameFor = BoundedVec::try_from([0u8; 32].to_vec()).unwrap(); + let bucket_id = H256::from_slice(&name); + + assert_noop!( + FileSystem::issue_storage_request( + origin, + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + ), + pallet_storage_providers::Error::::BucketNotFound + ); + }); + } + + #[test] + fn request_storage_not_bucket_owner_fail() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let not_owner = Keyring::Bob.to_account_id(); + let origin = RuntimeOrigin::signed(not_owner.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_id); + + assert_noop!( + FileSystem::issue_storage_request( + origin, + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + ), + Error::::NotBucketOwner + ); + }); + } + + #[test] + fn request_storage_while_pending_move_bucket() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let msp_charlie = Keyring::Charlie.to_account_id(); + let msp_dave = Keyring::Dave.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_charlie_id = add_msp_to_provider_storage(&msp_charlie); + let msp_dave_id = add_msp_to_provider_storage(&msp_dave); + + let name: BucketNameFor = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner, name.clone(), msp_charlie_id); + + // Check bucket is stored by Charlie + assert!(Providers::is_bucket_stored_by_msp( + &msp_charlie_id, + &bucket_id + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::request_move_bucket( + origin.clone(), + bucket_id, + msp_dave_id + )); + + let pending_move_bucket = + PendingMoveBucketRequests::::get(&msp_dave_id, bucket_id); + assert_eq!( + pending_move_bucket, + Some(MoveBucketRequestMetadata { + requester: owner.clone() + }) + ); + + assert_noop!( + FileSystem::issue_storage_request( + origin, + bucket_id, + location.clone(), + fingerprint, + 4, + msp_charlie_id, + peer_ids.clone(), + ), + Error::::BucketIsBeingMoved + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn request_storage_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewStorageRequest { + who: owner_account_id, + file_key, + bucket_id, + location: location.clone(), + fingerprint, + size: 4, + peer_ids, + } + .into(), + ); + }); + } + + #[test] + fn two_request_storage_in_same_block() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let file_1_location = FileLocation::::try_from(b"test1".to_vec()).unwrap(); + let file_2_location = FileLocation::::try_from(b"test2".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + file_1_location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + file_1_location.clone(), + size, + fingerprint, + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id: bucket_id.clone(), + location: file_1_location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + file_2_location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + file_2_location.clone(), + size, + fingerprint, + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: file_2_location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::NewStorageRequest { + who: owner_account_id, + file_key, + bucket_id, + location: file_2_location.clone(), + fingerprint, + size, + peer_ids, + } + .into(), + ); + }); + } + + #[test] + fn request_storage_failure_if_size_is_zero() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 0; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_noop!( + FileSystem::issue_storage_request( + user.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + ), + Error::::FileSizeCannotBeZero + ); + }); + } + + #[test] + fn request_storage_expiration_clear_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let size = 4; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let storage_request_ttl: u32 = StorageRequestTtl::::get(); + let storage_request_ttl: BlockNumberFor = storage_request_ttl.into(); + let expiration_block = System::block_number() + storage_request_ttl; + + // Assert that the next expiration block number is the storage request ttl since a single storage request was made + assert_eq!( + file_system::NextAvailableStorageRequestExpirationBlock::::get(), + expiration_block + ); + + // Assert that the storage request expiration was appended to the list at `StorageRequestTtl` + assert_eq!( + file_system::StorageRequestExpirations::::get(expiration_block), + vec![file_key] + ); + + roll_to(expiration_block + 1); + + // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` + assert_eq!( + file_system::StorageRequestExpirations::::get(expiration_block), + vec![] + ); + }); + } + + #[test] + fn request_storage_expiration_current_block_increment_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); + let expected_expiration_block_number: BlockNumberFor = + expected_expiration_block_number.into(); + + // Append storage request expiration to the list at `StorageRequestTtl` + let max_expired_items_in_block: u32 = + ::MaxExpiredItemsInBlock::get(); + for _ in 0..max_expired_items_in_block { + assert_ok!(StorageRequestExpirations::::try_append( + expected_expiration_block_number, + file_key + )); + } + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids, + )); + + // Assert that the storage request expirations storage is at max capacity + assert_eq!( + file_system::StorageRequestExpirations::::get( + expected_expiration_block_number + ) + .len(), + max_expired_items_in_block as usize + ); + + // Go to block number after which the storage request expirations should be removed + roll_to(expected_expiration_block_number); + + // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` + assert_eq!( + file_system::StorageRequestExpirations::::get( + expected_expiration_block_number + ), + vec![] + ); + }); + } + + #[test] + fn request_storage_clear_old_expirations_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Append storage request expiration to the list at `StorageRequestTtl` + let max_storage_request_expiry: u32 = + ::MaxExpiredItemsInBlock::get(); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); + let expected_expiration_block_number: BlockNumberFor = + expected_expiration_block_number.into(); + + for _ in 0..max_storage_request_expiry { + assert_ok!(StorageRequestExpirations::::try_append( + expected_expiration_block_number, + file_key + )); + } + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids, + )); + + let expected_expiration_block_number: u32 = StorageRequestTtl::::get(); + let expected_expiration_block_number: BlockNumberFor = + expected_expiration_block_number.into(); + + // Assert that the storage request expirations storage is at max capacity + assert_eq!( + file_system::StorageRequestExpirations::::get( + expected_expiration_block_number + ) + .len(), + max_storage_request_expiry as usize + ); + + let used_weight = FileSystem::on_idle(System::block_number(), Weight::zero()); + + // Assert that the weight used is zero + assert_eq!(used_weight, Weight::zero()); + + // Assert that the storage request expirations storage is at max capacity + assert_eq!( + file_system::StorageRequestExpirations::::get( + expected_expiration_block_number + ) + .len(), + max_storage_request_expiry as usize + ); + + // Go to block number after which the storage request expirations should be removed + roll_to(expected_expiration_block_number + 1); + + // Assert that the storage request expiration was removed from the list at `StorageRequestTtl` + assert_eq!( + file_system::StorageRequestExpirations::::get( + expected_expiration_block_number + ), + vec![] + ); + + // Assert that the `NextExpirationInsertionBlockNumber` storage is set to the next block number + assert_eq!( + file_system::NextStartingBlockToCleanUp::::get(), + System::block_number() + 1 + ); + }); + } + } +} + +mod revoke_storage_request { + use super::*; + mod failure { + use super::*; + + #[test] + fn revoke_non_existing_storage_request_fail() { + new_test_ext().execute_with(|| { + let owner = RuntimeOrigin::signed(Keyring::Alice.to_account_id()); + let file_key = H256::zero(); + + assert_noop!( + FileSystem::revoke_storage_request(owner.clone(), file_key), + Error::::StorageRequestNotFound + ); + }); + } + + #[test] + fn revoke_storage_request_not_owner_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let not_owner = RuntimeOrigin::signed(Keyring::Bob.to_account_id()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + Default::default() + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + assert_noop!( + FileSystem::revoke_storage_request(not_owner.clone(), file_key), + Error::::StorageRequestNotAuthorized + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn revoke_request_storage_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + Default::default() + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + let storage_request_ttl: u32 = StorageRequestTtl::::get(); + let storage_request_ttl: BlockNumberFor = storage_request_ttl.into(); + let expiration_block = System::block_number() + storage_request_ttl; + + // Assert that the storage request expiration was appended to the list at `StorageRequestTtl` + assert_eq!( + file_system::StorageRequestExpirations::::get(expiration_block), + vec![file_key] + ); + + assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); + + System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); + }); + } + + #[test] + fn revoke_storage_request_with_volunteered_bsps_success() { + new_test_ext().execute_with(|| { + let owner_account = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account.clone(), name.clone(), msp_id); + + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + )); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let file_key = FileSystem::compute_file_key( + owner_account.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Check StorageRequestBsps storage for confirmed BSPs + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: false, + _phantom: Default::default() + } + ); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); + + // Assert that the correct event was deposited + System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); + }); + } + + #[test] + fn revoke_storage_request_with_confirmed_bsps_success() { + new_test_ext().execute_with(|| { + let owner_account = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + )); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::revoke_storage_request(owner.clone(), file_key)); + + // Check ProofsDealer pallet storage for queued custom challenges for remove trie mutation of file key + let priority_challenges_queue = PriorityChallengesQueue::::get(); + + assert!(priority_challenges_queue.contains(&(file_key, Some(TrieRemoveMutation)))); + + // Assert that the correct event was deposited + System::assert_last_event(Event::StorageRequestRevoked { file_key }.into()); + }); + } + } +} + +mod msp_respond_storage_request { + use super::*; + + mod success { + use super::*; + use crate::types::{ + AcceptedStorageRequestParameters, MspAcceptedBatchStorageRequests, + MspStorageRequestResponse, RejectedStorageRequestReason, + }; + use sp_core::crypto::AccountId32; + + #[test] + fn msp_respond_storage_request_works() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + // Register the MSP. + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp); + + // Create the bucket that will hold the file. + // Create the bucket that will hold the file. + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Compute the file key. + // Compute the file key. + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch the MSP accept request. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets(RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some( + AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![(file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + } + ), + reject: None, + } + )])); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + // Get the new root of the bucket. + let new_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id,) + .unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![file_key], + bucket_id, + new_bucket_root, + owner: owner_account_id + } + )], + }, + }.into(), + ); + }); + } + + #[test] + fn msp_respond_storage_request_works_multiple_times_for_same_user_same_bucket() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let second_location = FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp); + + // Create the bucket that will hold both files. + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Compute the file key for the first file. + let first_file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + first_location.clone(), + size, + fingerprint, + ); + + // Compute the file key for the second file. + let second_file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + second_location.clone(), + size, + fingerprint, + ); + + // Dispatch a storage request for the first file. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + first_location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Dispatch a storage request for the second file. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + second_location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Dispatch the MSP accept request for the first file. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![(first_file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }), (second_file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec(), H256::default().as_ref().to_vec()], + }, + }), + reject: None + } + )] + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(first_file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + assert_eq!( + file_system::StorageRequests::::get(second_file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + // Get the new root of the bucket. + let new_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id) + .unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![first_file_key, second_file_key], + bucket_id, + new_bucket_root, + owner: owner_account_id + } + )], + }, + }.into(), + ); + + // Assert that the MSP used capacity has been updated. + assert_eq!( + ::get_used_capacity(&msp_id), + size * 2 + ); + }); + } + + #[test] + fn msp_respond_storage_request_works_multiple_times_for_same_user_different_bucket() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let second_location = + FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()) + .unwrap(); + let first_size = 4; + let second_size = 8; + let first_fingerprint = H256::zero(); + let second_fingerprint = H256::random(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp); + + // Create the bucket that will hold the first file. + let first_name = BoundedVec::try_from(b"first bucket".to_vec()).unwrap(); + let first_bucket_id = create_bucket(&owner_account_id.clone(), first_name, msp_id); + + // Create the bucket that will hold the second file. + let second_name = BoundedVec::try_from(b"second bucket".to_vec()).unwrap(); + let second_bucket_id = + create_bucket(&owner_account_id.clone(), second_name, msp_id); + + // Compute the file key for the first file. + let first_file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + first_bucket_id, + first_location.clone(), + first_size, + first_fingerprint, + ); + + // Compute the file key for the second file. + let second_file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + second_bucket_id, + second_location.clone(), + second_size, + second_fingerprint, + ); + + // Dispatch a storage request for the first file. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + first_bucket_id, + first_location.clone(), + first_fingerprint, + first_size, + msp_id, + peer_ids.clone(), + )); + + // Dispatch a storage request for the second file. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + second_bucket_id, + second_location.clone(), + second_fingerprint, + second_size, + msp_id, + peer_ids.clone(), + )); + + // Dispatch the MSP accept request for the second file. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + first_bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![(first_file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + }), + reject: None, + } + ), + ( + second_bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![(second_file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + }), + reject: None, + } + ) + ], + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(first_file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(second_file_key).unwrap().msp, + Some((msp_id, true)) + ); + + let first_bucket_root = <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&first_bucket_id,) + .unwrap(); + let second_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&second_bucket_id,) + .unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![first_file_key], + bucket_id: first_bucket_id, + new_bucket_root: first_bucket_root, + owner: owner_account_id.clone() + } + ), BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![second_file_key], + bucket_id: second_bucket_id, + new_bucket_root: second_bucket_root, + owner: owner_account_id + } + )], + }, + }.into(), + ); + + // Assert that the MSP used capacity has been updated. + assert_eq!( + ::get_used_capacity(&msp_id), + first_size + second_size + ); + }); + } + + #[test] + fn msp_respond_storage_request_works_multiple_times_for_different_users() { + new_test_ext().execute_with(|| { + let first_owner_account_id = Keyring::Alice.to_account_id(); + let first_owner_signed = RuntimeOrigin::signed(first_owner_account_id.clone()); + let second_owner_account_id = Keyring::Bob.to_account_id(); + let second_owner_signed = RuntimeOrigin::signed(second_owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let first_location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let second_location = + FileLocation::::try_from(b"never/go/to/a/second/location".to_vec()) + .unwrap(); + let first_size = 4; + let second_size = 8; + let first_fingerprint = H256::zero(); + let second_fingerprint = H256::random(); + let first_peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let first_peer_ids: PeerIds = + BoundedVec::try_from(vec![first_peer_id]).unwrap(); + let second_peer_id = BoundedVec::try_from(vec![2]).unwrap(); + let second_peer_ids: PeerIds = + BoundedVec::try_from(vec![second_peer_id]).unwrap(); + + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp); + + // Create the bucket that will hold the first file. + let first_name = BoundedVec::try_from(b"first bucket".to_vec()).unwrap(); + let first_bucket_id = + create_bucket(&first_owner_account_id.clone(), first_name, msp_id); + + // Create the bucket that will hold the second file. + let second_name = BoundedVec::try_from(b"second bucket".to_vec()).unwrap(); + let second_bucket_id = + create_bucket(&second_owner_account_id.clone(), second_name, msp_id); + + // Compute the file key for the first file. + let first_file_key = FileSystem::compute_file_key( + first_owner_account_id.clone(), + first_bucket_id, + first_location.clone(), + first_size, + first_fingerprint, + ); + + // Compute the file key for the second file. + let second_file_key = FileSystem::compute_file_key( + second_owner_account_id.clone(), + second_bucket_id, + second_location.clone(), + second_size, + second_fingerprint, + ); + + // Dispatch a storage request for the first file. + assert_ok!(FileSystem::issue_storage_request( + first_owner_signed.clone(), + first_bucket_id, + first_location.clone(), + first_fingerprint, + first_size, + msp_id, + first_peer_ids.clone(), + )); + + // Dispatch a storage request for the second file. + assert_ok!(FileSystem::issue_storage_request( + second_owner_signed.clone(), + second_bucket_id, + second_location.clone(), + second_fingerprint, + second_size, + msp_id, + second_peer_ids.clone(), + )); + + // Dispatch the MSP accept request for the second file. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + first_bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + first_file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + }), ( + second_bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + second_file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None + }, + ) + ], + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(first_file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(second_file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + let first_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&first_bucket_id,) + .unwrap(); + + let second_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&second_bucket_id,) + .unwrap(); + + // Check event + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![first_file_key], + bucket_id: first_bucket_id, + new_bucket_root: first_bucket_root, + owner: first_owner_account_id.clone() + } + ), BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![second_file_key], + bucket_id: second_bucket_id, + new_bucket_root: second_bucket_root, + owner: second_owner_account_id.clone() + } + )], + }, + }.into(), + ); + + // Assert that the MSP used capacity has been updated. + assert_eq!( + ::get_used_capacity(&msp_id), + first_size + second_size + ); + }); + } + + #[test] + fn msp_respond_storage_request_fullfilled() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp); + + // Create the bucket that will hold the file. + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Set replication target to 1 + ReplicationTarget::::put(1); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Compute the file key. + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + // Dispatch the BSP volunteer + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch the BSP confirm storing + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Dispatch the MSP accept request. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some( + AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![(file_key, CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + } + ), + reject: None, + } + )], + )); + + System::assert_has_event( + Event::StorageRequestFulfilled { file_key }.into(), + ); + + let new_bucket_root = + <::Providers as shp_traits::ReadBucketsInterface>::get_root_bucket(&bucket_id,) + .unwrap(); + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Accepted( + MspAcceptedBatchStorageRequests:: { + file_keys: bounded_vec![file_key], + bucket_id, + new_bucket_root, + owner: owner_account_id + } + )], + }, + }.into(), + ); + + // Storage request should be removed + assert!(file_system::StorageRequests::::get(file_key).is_none()); + assert!( + file_system::BucketsWithStorageRequests::::get(bucket_id, file_key) + .is_none() + ); + }); + } + + struct StorageRequestParams { + owner_account_id: AccountId32, + bucket_name: Vec, + location: Vec, + size: u64, + fingerprint: H256, + peer_ids: PeerIds, + } + + fn generate_storage_requests( + params_list: Vec, + msp_id: ProviderIdFor, + ) -> Vec<(BucketIdFor, MerkleHash, AccountId32)> { + let mut results = Vec::new(); + + for params in params_list { + // Create bucket if not already created + let bucket_id = ::Providers::derive_bucket_id( + &msp_id, + ¶ms.owner_account_id.clone().try_into().unwrap(), + params.bucket_name.clone().try_into().unwrap(), + ); + + if !::Providers::bucket_exists(&bucket_id) { + create_bucket( + ¶ms.owner_account_id.clone(), + params.bucket_name.clone().try_into().unwrap(), + msp_id, + ); + } + + // Compute file key + let file_key = FileSystem::compute_file_key( + params.owner_account_id.clone(), + bucket_id, + FileLocation::::try_from(params.location.clone()).unwrap(), + params.size, + params.fingerprint, + ); + + // Issue storage request + assert_ok!(FileSystem::issue_storage_request( + RuntimeOrigin::signed(params.owner_account_id.clone()), + bucket_id, + FileLocation::::try_from(params.location).unwrap(), + params.fingerprint, + params.size, + msp_id, + params.peer_ids.clone(), + )); + + results.push((bucket_id, file_key, params.owner_account_id.clone())); + } + + results + } + + fn generate_msp_responses_and_results( + storage_requests: Vec<(BucketIdFor, MerkleHash, AccountId32)>, + msp_id: ProviderIdFor, + ) -> ( + FileKeyResponsesInput, + MspRespondStorageRequestsResult, + ) { + let mut responses: BTreeMap, MspStorageRequestResponse> = + BTreeMap::new(); + let mut batch_responses: Vec> = Vec::new(); + + for (bucket_id, file_key, owner_account_id) in storage_requests { + let response: &mut MspStorageRequestResponse = responses + .entry(bucket_id) + .or_insert_with(|| MspStorageRequestResponse { + accept: None, + reject: None, + }); + + if file_key.as_ref()[0] % 2 == 0 { + if let Some(accept) = &mut response.accept { + accept + .file_keys_and_proofs + .try_push(( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + )) + .unwrap(); + } else { + response.accept = Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }); + } + + if let Some(BatchResponses::Accepted(ref mut accepted)) = batch_responses.iter_mut().find(|br| matches!(br, BatchResponses::Accepted(a) if a.bucket_id == bucket_id)) { + accepted.file_keys.try_push(file_key).unwrap(); + } else { + batch_responses.push(BatchResponses::Accepted(MspAcceptedBatchStorageRequests { + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + bucket_id, + new_bucket_root: H256::zero(), + owner: owner_account_id, + })); + } + } else { + // Rejected response + let reject_reason = RejectedStorageRequestReason::InternalError; + + if let Some(reject) = &mut response.reject { + reject.try_push((file_key, reject_reason.clone())).unwrap(); + } else { + response.reject = Some(bounded_vec![(file_key, reject_reason.clone())]); + } + + if let Some(BatchResponses::Rejected(ref mut rejected)) = batch_responses.iter_mut().find(|br| matches!(br, BatchResponses::Rejected(r) if r.bucket_id == bucket_id)) { + rejected.file_keys.try_push((file_key, reject_reason)).unwrap(); + } else { + batch_responses.push(BatchResponses::Rejected(MspRejectedBatchStorageRequests { + file_keys: BoundedVec::try_from(vec![(file_key, reject_reason)]).unwrap(), + bucket_id, + owner: owner_account_id, + })); + } + } + } + + let responses: FileKeyResponsesInput = responses + .into_iter() + .collect::>() + .try_into() + .expect("Should not exceed MaxBatchConfirmStorageRequests"); + + let results = MspRespondStorageRequestsResult { + msp_id, + responses: BoundedVec::try_from(batch_responses).unwrap(), + }; + + println!("Generated results: {:?}", results); + + (responses, results) + } + + #[test] + fn msp_respond_storage_request_accepts_and_rejects_failed_mixed_responses() { + new_test_ext().execute_with(|| { + // Create accounts + let msp_account_id = Keyring::Charlie.to_account_id(); + + // Register the MSP. + let msp_id = add_msp_to_provider_storage(&msp_account_id); + + let first_bucket = b"first bucket".to_vec(); + let second_bucket = b"second bucket".to_vec(); + let size = 4; + let fingerprint = H256::zero(); + + // Define storage request parameters + let storage_request_params = vec![ + StorageRequestParams { + owner_account_id: Keyring::Alice.to_account_id(), + bucket_name: first_bucket, + location: b"location".to_vec(), + size, + fingerprint, + peer_ids: BoundedVec::try_from( + vec![BoundedVec::try_from(vec![1]).unwrap()], + ) + .unwrap(), + }, + StorageRequestParams { + owner_account_id: Keyring::Bob.to_account_id(), + bucket_name: second_bucket.clone(), + location: b"location2".to_vec(), + size, + fingerprint, + peer_ids: BoundedVec::try_from( + vec![BoundedVec::try_from(vec![2]).unwrap()], + ) + .unwrap(), + }, + StorageRequestParams { + owner_account_id: Keyring::Bob.to_account_id(), + bucket_name: second_bucket, + location: b"location3".to_vec(), + size, + fingerprint, + peer_ids: BoundedVec::try_from( + vec![BoundedVec::try_from(vec![2]).unwrap()], + ) + .unwrap(), + }, + ]; + + // Generate storage requests + let storage_requests: Vec<(BucketIdFor, MerkleHash, AccountId32)> = + generate_storage_requests(storage_request_params, msp_id); + + let (responses, expected_results) = + generate_msp_responses_and_results(storage_requests, msp_id); + + // Use `responses` to call the extrinsic + FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp_account_id), + responses, + ) + .unwrap(); + + let expected_results = MspRespondStorageRequestsResult { + msp_id, + responses: { + let updated_responses: Vec<_> = expected_results + .responses + .into_iter() + .map(|batch_response| match batch_response { + BatchResponses::Accepted(mut accepted) => { + accepted.new_bucket_root = + ::Providers::get_root_bucket( + &accepted.bucket_id, + ) + .expect("Root bucket should exist"); + BatchResponses::Accepted(accepted) + } + br => br, + }) + .collect(); + + BoundedVec::try_from(updated_responses) + .expect("Number of responses should not exceed the bound") + }, + }; + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: expected_results, + } + .into(), + ); + }); + } + } + + mod failure { + use super::*; + + #[test] + fn fails_if_storage_request_not_found() { + new_test_ext().execute_with(|| { + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let msp_id = add_msp_to_provider_storage(&msp); + + // create bucket + let owner_account_id = Keyring::Alice.to_account_id(); + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = H256::zero(); + + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )] + )); + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Failed( + MspFailedBatchStorageRequests:: { + file_keys: bounded_vec![( + file_key, + Error::::StorageRequestNotFound.into() + )], + bucket_id, + owner: owner_account_id, + } + )], + }, + } + .into(), + ); + }); + } + + #[test] + fn fails_if_caller_not_a_provider() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let not_msp = Keyring::Bob.to_account_id(); + let not_msp_signed = RuntimeOrigin::signed(not_msp.clone()); + + assert_noop!( + FileSystem::msp_respond_storage_requests_multiple_buckets( + not_msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![], + }, + }), + reject: None, + } + )] + ), + Error::::NotASp + ); + }); + } + + #[test] + fn fails_if_caller_not_a_msp() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + assert_noop!( + FileSystem::msp_respond_storage_requests_multiple_buckets( + bsp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![], + }, + }), + reject: None, + } + )] + ), + Error::::NotAMsp + ); + }); + } + + #[test] + fn fails_if_request_is_not_expecting_a_msp() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Insert a storage request that is not expecting a MSP. + StorageRequests::::insert( + file_key, + StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: None, + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }, + ); + + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )] + ),); + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Failed( + MspFailedBatchStorageRequests:: { + file_keys: bounded_vec![( + file_key, + Error::::RequestWithoutMsp.into() + )], + bucket_id, + owner: owner_account_id, + } + )], + }, + } + .into(), + ); + }); + } + + #[test] + fn fails_if_caller_is_msp_but_not_assigned_one() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let expected_msp = Keyring::Charlie.to_account_id(); + let caller_msp = Keyring::Dave.to_account_id(); + let caller_msp_signed = RuntimeOrigin::signed(caller_msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let expected_msp_id = add_msp_to_provider_storage(&expected_msp); + let _caller_msp_id = add_msp_to_provider_storage(&caller_msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, expected_msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + expected_msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Try to accept storing a file with a MSP that is not the one assigned to the file. + assert_noop!( + FileSystem::msp_respond_storage_requests_multiple_buckets( + caller_msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![], + }, + }), + reject: None, + } + )] + ), + Error::::MspNotStoringBucket + ); + }); + } + + #[test] + fn fails_if_msp_already_accepted_storing() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Accept storing the file. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )], + )); + + // Try to accept storing the file again. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )], + ),); + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Failed( + MspFailedBatchStorageRequests:: { + file_keys: bounded_vec![( + file_key, + Error::::MspAlreadyConfirmed.into() + )], + bucket_id, + owner: owner_account_id, + } + )], + }, + } + .into(), + ); + }); + } + + #[test] + fn fails_if_msp_is_not_the_one_storing_the_bucket() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let expected_msp = Keyring::Charlie.to_account_id(); + let expected_msp_signed = RuntimeOrigin::signed(expected_msp.clone()); + let other_msp = Keyring::Dave.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let expected_msp_id = add_msp_to_provider_storage(&expected_msp); + let other_msp_id = add_msp_to_provider_storage(&other_msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, other_msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Insert a storage request with the expected MSP but a bucket ID from another MSP. + // Note: this should never happen since `issue_storage_request` checks that the bucket ID + // belongs to the MSP, but we are testing it just in case. + StorageRequests::::insert( + file_key, + StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((expected_msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }, + ); + + // Try to accept storing a file with a MSP that is not the owner of the bucket ID + assert_noop!( + FileSystem::msp_respond_storage_requests_multiple_buckets( + expected_msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![], + }, + }), + reject: None, + } + )] + ), + Error::::MspNotStoringBucket + ); + }); + } + + #[test] + fn fails_if_msp_does_not_have_enough_available_capacity() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 200; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Insert a storage request for a MSP with not enough available capacity. + // Note: `issue_storage_request` checks that the MSP has enough available capacity, but it could happen + // that when the storage request was initially created the MSP had enough available capacity but it + // accepted other storage requests in the meantime and now it does not have enough available capacity. + StorageRequests::::insert( + file_key, + StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }, + ); + + // Try to accept storing a file with a MSP that does not have enough available capacity + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )] + ),); + + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Failed( + MspFailedBatchStorageRequests:: { + file_keys: bounded_vec![( + file_key, + Error::::InsufficientAvailableCapacity.into() + )], + bucket_id, + owner: owner_account_id, + } + )], + }, + } + .into(), + ); + }); + } + + #[test] + fn fails_if_the_non_inclusion_proof_includes_the_file_key() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch a storage request. + assert_ok!(FileSystem::issue_storage_request( + RuntimeOrigin::signed(owner_account_id.clone()), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Try to accept storing a file with a non-inclusion proof that includes the file key + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + }), + reject: None, + } + )] + ),); + + // Check the event Event::MspRespondedToStorageRequests + System::assert_last_event( + Event::MspRespondedToStorageRequests { + results: MspRespondStorageRequestsResult:: { + msp_id, + responses: bounded_vec![BatchResponses::::Failed( + MspFailedBatchStorageRequests:: { + file_keys: bounded_vec![( + file_key, + Error::::ExpectedNonInclusionProof.into() + )], + bucket_id, + owner: owner_account_id, + } + )], + }, + } + .into(), + ); + }); + } + } +} + +mod bsp_volunteer { + use super::*; + mod failure { + use super::*; + use core::u32; + + #[test] + fn bsp_actions_not_a_bsp_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + RuntimeOrigin::signed(owner_account_id.clone()), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::NotABsp + ); + }); + } + + #[test] + fn bsp_volunteer_storage_request_not_found_fail() { + new_test_ext().execute_with(|| { + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let fingerprint = H256::zero(); + + assert_ok!(bsp_sign_up(bsp_signed.clone(), 100,)); + + let file_key = FileSystem::compute_file_key( + bsp_account_id.clone(), + H256::zero(), + location.clone(), + 4, + fingerprint, + ); + + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::StorageRequestNotFound + ); + }); + } + + #[test] + fn bsp_already_volunteered_failed() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::BspAlreadyVolunteered + ); + }); + } + + #[test] + fn bsp_volunteer_above_threshold_high_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Set very high block range to maximum threshold. + assert_ok!(FileSystem::set_global_parameters( + RuntimeOrigin::root(), + None, + Some(u32::MAX.into()) + )); + + // Dispatch BSP volunteer. + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::AboveThreshold + ); + }); + } + + #[test] + fn bsp_volunteer_above_threshold_high_fail_even_with_spamming() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); + + // Get BSP ID. + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Compute the file key to volunteer for. + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Set a somewhat high block range to maximum threshold. + assert_ok!(FileSystem::set_global_parameters( + RuntimeOrigin::root(), + None, + Some(40) + )); + + // Calculate how many ticks until this BSP can volunteer for the file. + let current_tick = ProofsDealer::get_current_tick(); + let tick_when_bsp_can_volunteer = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + let ticks_to_advance = tick_when_bsp_can_volunteer - current_tick + 1; + let current_block = System::block_number(); + + // Advance by the number of ticks until this BSP can volunteer for the file. + // In the process, this BSP will spam the chain to prevent others from volunteering and confirming. + roll_to_spammed(current_block + ticks_to_advance); + + // Dispatch BSP volunteer. + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::AboveThreshold + ); + }); + } + + #[test] + fn bsp_volunteer_with_insufficient_capacity() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner.clone(), name.clone(), msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + origin, + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + pallet_storage_providers::BackupStorageProviders::::mutate(bsp_id, |bsp| { + assert!(bsp.is_some()); + if let Some(bsp) = bsp { + bsp.capacity = 0; + } + }); + + let file_key = FileSystem::compute_file_key( + owner.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + // Dispatch BSP volunteer. + assert_noop!( + FileSystem::bsp_volunteer(bsp_signed.clone(), file_key), + Error::::InsufficientAvailableCapacity + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn bsp_volunteer_success() { + new_test_ext().execute_with(|| { + let owner = Keyring::Alice.to_account_id(); + let origin = RuntimeOrigin::signed(owner.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = StorageData::::try_from(4).unwrap(); + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner.clone(), name.clone(), msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + origin, + bucket_id, + location.clone(), + fingerprint, + 4, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner.clone(), + bucket_id, + location.clone(), + 4, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + + // Assert that the RequestStorageBsps has the correct value + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: false, + _phantom: Default::default() + } + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::AcceptedBspVolunteer { + bsp_id, + bucket_id, + location, + fingerprint, + multiaddresses: create_sp_multiaddresses(), + owner, + size, + } + .into(), + ); + }); + } + + #[test] + fn bsp_volunteer_succeeds_after_waiting_enough_blocks_without_spam() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); + + // Get BSP ID. + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Compute the file key to volunteer for. + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Set a somewhat high block range to maximum threshold. + assert_ok!(FileSystem::set_global_parameters( + RuntimeOrigin::root(), + None, + Some(40) + )); + + // Calculate how many ticks until this BSP can volunteer for the file. + let current_tick = ProofsDealer::get_current_tick(); + let tick_when_bsp_can_volunteer = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + let ticks_to_advance = tick_when_bsp_can_volunteer - current_tick + 1; + let current_block = System::block_number(); + + // Advance by the number of ticks until this BSP can volunteer for the file. + roll_to(current_block + ticks_to_advance); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + + // Assert that the RequestStorageBsps has the correct value + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: false, + _phantom: Default::default() + } + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::AcceptedBspVolunteer { + bsp_id, + bucket_id, + location, + fingerprint, + multiaddresses: create_sp_multiaddresses(), + owner: owner_account_id, + size, + } + .into(), + ); + }); + } + } +} + +mod bsp_confirm { + use super::*; + mod failure { + use super::*; + use pallet_storage_providers::types::ReputationWeightType; + + #[test] + fn bsp_actions_not_a_bsp_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + RuntimeOrigin::signed(owner_account_id.clone()), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + assert_noop!( + FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + ), + Error::::NotABsp + ); + }); + } + + #[test] + fn bsp_confirm_storing_storage_request_not_found_fail() { + new_test_ext().execute_with(|| { + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), 100,)); + + let file_key = FileSystem::compute_file_key( + bsp_account_id.clone(), + H256::zero(), + location.clone(), + 4, + H256::zero(), + ); + + assert_noop!( + FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + ), + Error::::StorageRequestNotFound + ); + }); + } + + #[test] + fn bsp_confirm_storing_not_volunteered_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount,)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + assert_noop!( + FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + ), + Error::::BspNotVolunteered + ); + }); + } + + #[test] + fn bsp_confirming_for_non_existent_storage_request() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_bob_account_id = Keyring::Bob.to_account_id(); + let bsp_bob_signed = RuntimeOrigin::signed(bsp_bob_account_id.clone()); + let bsp_charlie_account_id = Keyring::Dave.to_account_id(); + let bsp_charlie_signed = RuntimeOrigin::signed(bsp_charlie_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let msp_signed = RuntimeOrigin::signed(msp.clone()); + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + msp_signed.clone(), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None, + } + )] + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key) + .unwrap() + .msp, + Some((msp_id, true)) + ); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_bob_signed.clone(), storage_amount,)); + assert_ok!(bsp_sign_up(bsp_charlie_signed.clone(), storage_amount,)); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_bob_signed.clone(), file_key,)); + + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_bob_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + assert_ok!(FileSystem::bsp_volunteer( + bsp_charlie_signed.clone(), + file_key, + )); + + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_charlie_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + assert_noop!( + FileSystem::bsp_confirm_storing( + bsp_bob_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + ), + Error::::StorageRequestNotFound + ); + }); + } + + #[test] + fn bsp_failing_to_confirm_all_proofs_submitted_insufficient_capacity() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let storage_amount: StorageData = 100; + let size = 4; + + let msp_id = add_msp_to_provider_storage(&msp); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Force BSP to pass all threshold checks when volunteering. + pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { + if let Some(bsp) = bsp { + bsp.reputation_weight = ReputationWeightType::::max_value(); + } + }); + + let mut file_keys = Vec::new(); + + // Issue 5 storage requests and volunteer for each + for i in 0..5 { + let location = + FileLocation::::try_from(format!("test{}", i).into_bytes()).unwrap(); + let fingerprint = H256::repeat_byte(i as u8); + + let name = BoundedVec::try_from(format!("bucket{}", i).into_bytes()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Issue storage request + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + Default::default(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + file_keys.push(file_key); + + // Volunteer for storage + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + } + + // Set BSP storage capacity to 0 + pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { + if let Some(bsp) = bsp { + bsp.capacity = size * 2; + } + }); + + // Prepare proofs for all files + let non_inclusion_forest_proof = CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }; + + let file_keys_and_proofs: BoundedVec< + _, + ::MaxBatchConfirmStorageRequests, + > = file_keys + .into_iter() + .map(|file_key| { + ( + file_key, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ) + }) + .collect::>() + .try_into() + .unwrap(); + + // Attempt to confirm storing all files at once + assert_noop!( + FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + non_inclusion_forest_proof, + file_keys_and_proofs, + ), + Error::::InsufficientAvailableCapacity + ); + }); + } + } + + mod success { + use shp_traits::PaymentStreamsInterface; + + use super::*; + + #[test] + fn bsp_confirm_storing_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let new_root = Providers::get_root(bsp_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id, + bsp_id, + } + .into(), + ); + + // Assert that the payment stream between the BSP and the user has been created + assert!(<::PaymentStreams as PaymentStreamsInterface>::has_active_payment_stream(&bsp_id, &owner_account_id)); + }); + } + + #[test] + fn bsp_confirm_storing_correctly_updates_already_existing_payment_stream() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = + Providers::get_provider_id( + bsp_account_id.clone(), + ) + .unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![(file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })]).unwrap() + , + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let new_root = + Providers::get_root( + bsp_id, + ) + .unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id.clone(), + bsp_id, + } + .into(), + ); + + // Assert that the payment stream between the BSP and the user has been created and get its amount provided + let amount_provided_payment_stream = <::PaymentStreams as PaymentStreamsInterface>::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &owner_account_id); + assert!(amount_provided_payment_stream.is_some()); + assert_eq!(amount_provided_payment_stream.unwrap(), size); + + // Dispatch another storage request. + let current_block = System::block_number(); + let new_size = 8; + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + new_size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + new_size, + fingerprint, + ); + + // Advance a few blocks and dispatch BSP volunteer. + roll_to(current_block + 10); + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![(file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + })]).unwrap() + , + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: current_block, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size: new_size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let new_root = + Providers::get_root( + bsp_id, + ) + .unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the payment stream between the BSP and the user has been correctly updated + let new_amount_provided_payment_stream = <::PaymentStreams as PaymentStreamsInterface>::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &owner_account_id).unwrap(); + assert_eq!(amount_provided_payment_stream.unwrap() + new_size, new_amount_provided_payment_stream); + }); + } + } +} + +mod bsp_stop_storing { + use super::*; + mod failure { + use super::*; + #[test] + fn bsp_actions_not_a_bsp_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + RuntimeOrigin::signed(owner_account_id.clone()), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP stop storing. + assert_noop!( + FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::NotABsp + ); + }); + } + + #[test] + fn bsp_request_stop_storing_fails_if_file_key_does_not_match_metadata() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the RequestStorageBsps now contains the BSP under the location + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size - 1, // We change the size so the file key doesn't match the file's metadata + fingerprint, + ); + + // Dispatch BSP stop storing. + assert_noop!( + FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::InvalidFileKeyMetadata + ); + }); + } + + #[test] + fn bsp_request_stop_storing_fails_if_pending_stop_storing_request_exists() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the RequestStorageBsps now contains the BSP under the location + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP request stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Check that the request now exists. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); + + // Try sending the stop storing request again. + assert_noop!( + FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::PendingStopStoringRequestAlreadyExists + ); + }); + } + + #[test] + fn bsp_confirm_stop_storing_fails_if_not_enough_time_has_passed_since_request() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the RequestStorageBsps now contains the BSP under the location + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP request stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the RequestStorageBsps has the correct value + assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the request was added to the pending stop storing requests. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + + // Dispatch BSP confirm stop storing. + assert_noop!( + FileSystem::bsp_confirm_stop_storing( + bsp_signed.clone(), + file_key, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::MinWaitForStopStoringNotReached + ); + + // Assert that the pending stop storing request is still there. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); + }); + } + } + + mod success { + use super::*; + + #[test] + fn bsp_request_stop_storing_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the RequestStorageBsps now contains the BSP under the location + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP request stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the RequestStorageBsps has the correct value + assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the request was added to the pending stop storing requests. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + }); + } + + #[test] + fn bsp_confirm_stop_storing_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the RequestStorageBsps now contains the BSP under the location + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP request stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the RequestStorageBsps has the correct value + assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the request was added to the pending stop storing requests. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_some()); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + + // Advance enough blocks to allow the BSP to confirm the stop storing request. + roll_to( + frame_system::Pallet::::block_number() + MinWaitForStopStoring::get(), + ); + + // Dispatch BSP confirm stop storing. + assert_ok!(FileSystem::bsp_confirm_stop_storing( + bsp_signed.clone(), + file_key, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the pending stop storing request was removed. + assert!(PendingStopStoringRequests::::get(&bsp_id, &file_key).is_none()); + + // Assert that the correct event was deposited. + let new_root = Providers::get_root(bsp_id).unwrap(); + + System::assert_last_event( + Event::BspConfirmStoppedStoring { + bsp_id, + file_key, + new_root, + } + .into(), + ); + }); + } + + #[test] + fn bsp_request_stop_storing_while_storage_request_open_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + Default::default(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap() + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + H256::zero(), + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the RequestStorageBsps has the correct value + assert!(file_system::StorageRequestBsps::::get(file_key, bsp_id).is_none()); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint: H256::zero(), + size, + msp: Some((msp_id, false)), + user_peer_ids: Default::default(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + }); + } + + #[test] + fn bsp_request_stop_storing_not_volunteered_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(vec![1]).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + Default::default(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Increase the data used by the registered bsp, to simulate that it is indeed storing the file + assert_ok!(Providers::increase_capacity_used(&bsp_id, size,)); + + // Dispatch BSP stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + let current_bsps_required: ::ReplicationTargetType = + ReplicationTarget::::get(); + + // Assert that the storage request bsps_required was incremented + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: Default::default(), + data_server_sps: BoundedVec::default(), + bsps_required: current_bsps_required.checked_add(1).unwrap(), + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + }); + } + + #[test] + fn bsp_request_stop_storing_no_storage_request_success() { + new_test_ext().execute_with(|| { + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let owner_account_id = Keyring::Alice.to_account_id(); + let size = 4; + let fingerprint = H256::zero(); + + let msp_id = add_msp_to_provider_storage(&Keyring::Charlie.to_account_id()); + + let bucket_id = create_bucket( + &owner_account_id, + BoundedVec::try_from(b"bucket".to_vec()).unwrap(), + msp_id, + ); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), 100)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Increase the data used by the registered bsp, to simulate that it is indeed storing the file + assert_ok!(Providers::increase_capacity_used(&bsp_id, size,)); + + // Dispatch BSP stop storing. + assert_ok!(FileSystem::bsp_request_stop_storing( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + false, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the storage request was created with one bsps_required + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 5, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: None, + user_peer_ids: Default::default(), + data_server_sps: BoundedVec::default(), + bsps_required: 1, + bsps_confirmed: 0, + bsps_volunteered: 0, + }) + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspRequestedToStopStoring { + bsp_id, + file_key, + owner: owner_account_id, + location, + } + .into(), + ); + }); + } + } +} + +mod set_global_parameters_tests { + use super::*; + + mod failure { + use super::*; + #[test] + fn set_global_parameters_non_root_signer_fail() { + new_test_ext().execute_with(|| { + let non_root = Keyring::Bob.to_account_id(); + let non_root_signed = RuntimeOrigin::signed(non_root.clone()); + + // Assert BadOrigin error when non-root account tries to set the threshold + assert_noop!( + FileSystem::set_global_parameters(non_root_signed, None, None), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn set_global_parameters_0_value() { + new_test_ext().execute_with(|| { + assert_noop!( + FileSystem::set_global_parameters(RuntimeOrigin::root(), Some(0), None), + Error::::ReplicationTargetCannotBeZero + ); + + assert_noop!( + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(0)), + Error::::TickRangeToMaximumThresholdCannotBeZero + ); + }); + } + } + + mod success { + use super::*; + + #[test] + fn set_global_parameters() { + new_test_ext().execute_with(|| { + let root = RuntimeOrigin::root(); + + // Set the global parameters + assert_ok!(FileSystem::set_global_parameters( + root.clone(), + Some(3), + Some(10) + )); + + // Assert that the global parameters were set correctly + assert_eq!(ReplicationTarget::::get(), 3); + assert_eq!(TickRangeToMaximumThreshold::::get(), 10); + }); + } + } +} + +mod delete_file_and_pending_deletions_tests { + use super::*; + + mod failure { + use super::*; + + #[test] + fn delete_file_bucket_not_owned_by_user_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let _ = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + let other_user = Keyring::Bob.to_account_id(); + let bucket_id = create_bucket(&other_user.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }; + + // Assert that the user does not own the bucket + assert_noop!( + FileSystem::delete_file( + owner_signed, + bucket_id, + file_key, + location, + size, + fingerprint, + Some(forest_proof), + ), + Error::::NotBucketOwner + ); + }); + } + + #[test] + fn delete_file_beyond_maximum_limit_allowed_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = u64::MAX; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // For loop to create 1 over maximum of MaxUserPendingDeletionRequests + for i in 0..::MaxUserPendingDeletionRequests::get() { + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + i as u64, + fingerprint, + ); + + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location.clone(), + i as u64, + fingerprint, + None, + )); + } + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + assert_noop!( + FileSystem::delete_file( + owner_signed, + bucket_id, + file_key, + location, + size, + fingerprint, + None + ), + Error::::MaxUserPendingDeletionRequestsReached + ); + }); + } + + #[test] + fn delete_file_pending_file_deletion_request_submit_proof_not_msp_of_bucket_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Delete file + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location, + size, + fingerprint, + None, + )); + + // Assert that the pending file deletion request was added to storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( + vec![(file_key, bucket_id)] + ) + .unwrap() + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![vec![0]], + }; + + let msp_dave = Keyring::Dave.to_account_id(); + add_msp_to_provider_storage(&msp_dave); + let msp_origin = RuntimeOrigin::signed(msp_dave.clone()); + + assert_noop!( + FileSystem::pending_file_deletion_request_submit_proof( + msp_origin, + owner_account_id.clone(), + file_key, + bucket_id, + forest_proof + ), + Error::::MspNotStoringBucket + ); + + // Assert that the pending file deletion request was not removed from storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( + vec![(file_key, bucket_id)] + ) + .unwrap() + ); + }); + } + + #[test] + fn submit_proof_pending_file_deletion_not_found_fail() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + + let msp = Keyring::Charlie.to_account_id(); + let msp_origin = RuntimeOrigin::signed(msp.clone()); + + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![vec![0]], + }; + + assert_noop!( + FileSystem::pending_file_deletion_request_submit_proof( + msp_origin, + owner_account_id.clone(), + file_key, + bucket_id, + forest_proof + ), + Error::::FileKeyNotPendingDeletion + ); + }); + } + } + + mod success { + use super::*; + #[test] + fn delete_file_with_proof_of_inclusion_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }; + + // Delete file + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location, + size, + fingerprint, + Some(forest_proof), + )); + + // Assert that there is a queued priority challenge for file key in proofs dealer pallet + assert!( + // Find file key in vec of queued priority challenges + pallet_proofs_dealer::PriorityChallengesQueue::::get() + .iter() + .any(|x| *x == (file_key, Some(TrieRemoveMutation))), + ); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::FileDeletionRequest { + user: owner_account_id.clone(), + file_key, + bucket_id, + msp_id, + proof_of_inclusion: true, + } + .into(), + ); + }); + } + + #[test] + fn delete_file_expired_pending_file_deletion_request_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Delete file + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location, + size, + fingerprint, + None, + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::FileDeletionRequest { + user: owner_account_id.clone(), + file_key, + bucket_id, + msp_id, + proof_of_inclusion: false, + } + .into(), + ); + + // Assert that the pending file deletion request was added to storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( + vec![(file_key, bucket_id)] + ) + .unwrap() + ); + + let pending_file_deletion_request_ttl: u32 = + PendingFileDeletionRequestTtl::::get(); + let pending_file_deletion_request_ttl: BlockNumberFor = + pending_file_deletion_request_ttl.into(); + let expiration_block = pending_file_deletion_request_ttl + System::block_number(); + + // Assert that the pending file deletion request was added to storage + assert_eq!( + file_system::FileDeletionRequestExpirations::::get(expiration_block), + vec![( + owner_account_id.clone(), + file_key + )] + ); + + // Roll past the expiration block + roll_to(pending_file_deletion_request_ttl + 1); + + // Item expiration should be removed + assert_eq!( + file_system::FileDeletionRequestExpirations::::get(expiration_block), + vec![] + ); + + // Asser that the pending file deletion request was removed from storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() + ); + + // Assert that there is a queued priority challenge for file key in proofs dealer pallet + assert!(pallet_proofs_dealer::PriorityChallengesQueue::::get() + .iter() + .any(|x| *x == (file_key, Some(TrieRemoveMutation))),); + }); + } + + #[test] + fn delete_file_pending_file_deletion_request_submit_proof_of_inclusion_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Delete file + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location, + size, + fingerprint, + None, + )); + + // Assert that the pending file deletion request was added to storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( + vec![(file_key, bucket_id)] + ) + .unwrap() + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }; + + let msp_origin = RuntimeOrigin::signed(msp.clone()); + + assert_ok!(FileSystem::pending_file_deletion_request_submit_proof( + msp_origin, + owner_account_id.clone(), + file_key, + bucket_id, + forest_proof + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::ProofSubmittedForPendingFileDeletionRequest { + msp_id, + user: owner_account_id.clone(), + file_key, + bucket_id, + proof_of_inclusion: true, + } + .into(), + ); + + // Assert that there is a queued priority challenge for file key in proofs dealer pallet + assert!(pallet_proofs_dealer::PriorityChallengesQueue::::get() + .iter() + .any(|x| *x == (file_key, Some(TrieRemoveMutation))),); + + // Assert that the pending file deletion request was removed from storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() + ); + }); + } + + #[test] + fn delete_file_pending_file_deletion_request_submit_proof_of_non_inclusion_success() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Delete file + assert_ok!(FileSystem::delete_file( + owner_signed.clone(), + bucket_id, + file_key, + location, + size, + fingerprint, + None, + )); + + // Assert that the pending file deletion request was added to storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id.clone()), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::try_from( + vec![(file_key, bucket_id)] + ) + .unwrap() + ); + + let forest_proof = CompactProof { + encoded_nodes: vec![H256::zero().as_bytes().to_vec()], + }; + + let msp_origin = RuntimeOrigin::signed(msp.clone()); + + assert_ok!(FileSystem::pending_file_deletion_request_submit_proof( + msp_origin, + owner_account_id.clone(), + file_key, + bucket_id, + forest_proof + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::ProofSubmittedForPendingFileDeletionRequest { + msp_id, + user: owner_account_id.clone(), + file_key, + bucket_id, + proof_of_inclusion: false, + } + .into(), + ); + + // Assert that there is a queued priority challenge for file key in proofs dealer pallet + assert!( + !pallet_proofs_dealer::PriorityChallengesQueue::::get() + .iter() + .any(|x| *x == (file_key, Some(TrieRemoveMutation))), + ); + + // Assert that the pending file deletion request was removed from storage + assert_eq!( + file_system::PendingFileDeletionRequests::::get(owner_account_id), + BoundedVec::<_, ::MaxUserPendingDeletionRequests>::default() + ); + }); + } + } +} + +mod compute_threshold { + use super::*; + mod success { + use super::*; + #[test] + fn query_earliest_file_volunteer_tick() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let block_number = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + + assert!(frame_system::Pallet::::block_number() <= block_number); + }); + } + + #[test] + fn compute_threshold_to_succeed() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + let storage_request = file_system::StorageRequests::::get(file_key).unwrap(); + + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); + + assert_eq!(TickRangeToMaximumThreshold::::get(), 1); + + let (threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) + .unwrap(); + + assert!( + threshold_to_succeed > 0 + && threshold_to_succeed <= ThresholdType::::max_value() + ); + assert!(slope > 0); + + let block_number = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + + // BSP should be able to volunteer immediately for the storage request since the TickRangeToMaximumThreshold is 1 + assert_eq!(block_number, frame_system::Pallet::::block_number()); + + let starting_bsp_weight: pallet_storage_providers::types::ReputationWeightType< + Test, + > = ::StartingReputationWeight::get(); + + // Simulate there being many BSPs in the network with high reputation weight + pallet_storage_providers::GlobalBspsReputationWeight::::set( + 1000u32.saturating_mul(starting_bsp_weight.into()), + ); + + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1000000000)) + .unwrap(); + + assert_eq!(TickRangeToMaximumThreshold::::get(), 1000000000); + + let (threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) + .unwrap(); + + assert!( + threshold_to_succeed > 0 + && threshold_to_succeed <= ThresholdType::::max_value() + ); + assert!(slope > 0); + + let block_number = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + + // BSP can only volunteer after some number of blocks have passed. + assert!(block_number > frame_system::Pallet::::block_number()); + + // Set reputation weight of BSP to max + pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { + match bsp { + Some(bsp) => { + bsp.reputation_weight = u32::MAX; + } + None => { + panic!("BSP should exits"); + } + } + }); + + let (threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, storage_request.requested_at) + .unwrap(); + + assert!( + threshold_to_succeed > 0 + && threshold_to_succeed <= ThresholdType::::max_value() + ); + assert!(slope > 0); + + let block_number = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + + // BSP should be able to volunteer immediately for the storage request since the reputation weight is so high. + assert_eq!(block_number, frame_system::Pallet::::block_number()); + }); + } + + #[test] + fn compute_threshold_to_succeed_returns_max_when_bsp_weight_max() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let user = RuntimeOrigin::signed(owner_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let file_content = b"test".to_vec(); + let fingerprint = BlakeTwo256::hash(&file_content); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name.clone(), msp_id); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::issue_storage_request( + user.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + + let storage_amount: StorageData = 100; + + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set reputation weight of BSP to max + pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { + match bsp { + Some(bsp) => { + bsp.reputation_weight = u32::MAX; + } + None => { + panic!("BSP should exits"); + } + } + }); + + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); + + assert_eq!(TickRangeToMaximumThreshold::::get(), 1); + + let (threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, 0).unwrap(); + + assert_eq!(threshold_to_succeed, ThresholdType::::max_value()); + assert!(slope > 0); + + let block_number = + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key).unwrap(); + + // BSP should be able to volunteer immediately for the storage request since the reputation weight is so high. + assert_eq!(block_number, frame_system::Pallet::::block_number()); + }); + } + #[test] + fn compute_threshold_to_succeed_fails_when_global_weight_zero() { + new_test_ext().execute_with(|| { + // Setup: create a BSP + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set global_weight to zero + pallet_storage_providers::GlobalBspsReputationWeight::::set(0); + + let requested_at = frame_system::Pallet::::block_number(); + + let result = FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at); + + assert_noop!(result, Error::::NoGlobalReputationWeightSet); + }); + } + + #[test] + fn compute_threshold_to_succeed_with_one_block_range() { + new_test_ext().execute_with(|| { + // Setup: create a BSP + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set TickRangeToMaximumThreshold to one + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(1)).unwrap(); + + assert_eq!(TickRangeToMaximumThreshold::::get(), 1); + + let requested_at = frame_system::Pallet::::block_number(); + + let (threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at).unwrap(); + + // Check that base_slope is set to one due to division by zero handling + assert!(slope > ThresholdType::::zero()); + + // Ensure threshold_to_succeed is greater than zero + assert!(threshold_to_succeed > ThresholdType::::zero()); + }); + } + + #[test] + fn compute_threshold_to_succeed_with_max_slope() { + new_test_ext().execute_with(|| { + // Setup: create a BSP + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set global_weight to 1 + pallet_storage_providers::GlobalBspsReputationWeight::::set(1); + + // Set ReplicationTarget to 2 + ReplicationTarget::::set(2); + + // Set TickRangeToMaximumThreshold to a non-zero value + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); + + // Set max reputation weight + pallet_storage_providers::BackupStorageProviders::::mutate(&bsp_id, |bsp| { + match bsp { + Some(bsp) => { + bsp.reputation_weight = u32::MAX; + } + None => { + panic!("BSP should exist"); + } + } + }); + + let requested_at = frame_system::Pallet::::block_number(); + + let (_threshold_to_succeed, slope) = + FileSystem::compute_threshold_to_succeed(&bsp_id, requested_at).unwrap(); + + assert_eq!(slope, ThresholdType::::max_value()); + }); + } + + #[test] + fn bsp_with_higher_weight_should_have_higher_slope() { + new_test_ext().execute_with(|| { + // Setup: create a BSP + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_bob_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Create another BSP with higher weight + let bsp_account_id = Keyring::Charlie.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_charlie_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set global_weight to the sum of the two BSPs reputation weights + pallet_storage_providers::GlobalBspsReputationWeight::::set(10 + 1); + + // Set ReplicationTarget to 2 + ReplicationTarget::::set(2); + + // Set TickRangeToMaximumThreshold to a non-zero value + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); + + let requested_at = frame_system::Pallet::::block_number(); + + let (_threshold_to_succeed, slope_bsp_1) = + FileSystem::compute_threshold_to_succeed(&bsp_bob_id, requested_at).unwrap(); + + // Set BSP's reputation weight to 10 (10 times higher than the other BSP) + pallet_storage_providers::BackupStorageProviders::::mutate( + &bsp_charlie_id, + |bsp| match bsp { + Some(bsp) => { + bsp.reputation_weight = 10; + } + None => { + panic!("BSP should exist"); + } + }, + ); + + let (_threshold_to_succeed, slope_bsp_2) = + FileSystem::compute_threshold_to_succeed(&bsp_charlie_id, requested_at) + .unwrap(); + + // BSP with higher weight should have higher slope + assert!(slope_bsp_2 > slope_bsp_1); + }); + } + + #[test] + fn compute_threshold_to_succeed_slope_should_be_equal_for_all_starting_weight() { + new_test_ext().execute_with(|| { + // Setup: create a BSP + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_bob_id = Providers::get_provider_id(bsp_account_id).unwrap(); + // Create another BSP + let bsp_account_id = Keyring::Charlie.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let storage_amount: StorageData = 100; + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + let bsp_charlie_id = Providers::get_provider_id(bsp_account_id).unwrap(); + + // Set global_weight to the sum of the weights of the BSPs + pallet_storage_providers::GlobalBspsReputationWeight::::set(1 + 1); + + // Set ReplicationTarget to 2 + ReplicationTarget::::set(2); + + // Set TickRangeToMaximumThreshold to a non-zero value + FileSystem::set_global_parameters(RuntimeOrigin::root(), None, Some(100)).unwrap(); + + let requested_at = frame_system::Pallet::::block_number(); + + let (_threshold_to_succeed, slope_bsp_1) = + FileSystem::compute_threshold_to_succeed(&bsp_bob_id, requested_at).unwrap(); + + let (_threshold_to_succeed, slope_bsp_2) = + FileSystem::compute_threshold_to_succeed(&bsp_charlie_id, requested_at) + .unwrap(); + + // BSPs with equal weight should have equal slope + assert_eq!(slope_bsp_2, slope_bsp_1); + }); + } + } +} + +mod stop_storing_for_insolvent_user { + use super::*; + + mod success { + + use shp_traits::PaymentStreamsInterface; + + use super::*; + + #[test] + fn stop_storing_for_insolvent_user_works_for_bsps() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Eve.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let new_root = Providers::get_root(bsp_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id, + bsp_id, + } + .into(), + ); + + // Assert that the capacity used by the BSP was updated + assert_eq!( + pallet_storage_providers::BackupStorageProviders::::get(bsp_id) + .expect("BSP should exist in storage") + .capacity_used, + size + ); + + // Now that the BSP has confirmed storing, we can simulate the user being insolvent + // and the BSP stopping storing for the user. + // Try to stop storing for the insolvent user. + assert_ok!(FileSystem::stop_storing_for_insolvent_user( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::SpStopStoringInsolventUser { + sp_id: bsp_id, + file_key, + owner: owner_account_id, + location, + new_root, + } + .into(), + ); + + // Assert that the capacity used by the BSP was updated + assert_eq!( + pallet_storage_providers::BackupStorageProviders::::get(bsp_id) + .expect("BSP should exist in storage") + .capacity_used, + 0 + ); + }); + } + + #[test] + fn stop_storing_for_insolvent_user_works_for_msps() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Eve.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let msp_signed = RuntimeOrigin::signed(msp.clone()); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 50; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Dispatch MSP confirm storing. + assert_ok!(FileSystem::msp_respond_storage_requests_multiple_buckets( + RuntimeOrigin::signed(msp.clone()), + bounded_vec![( + bucket_id, + MspStorageRequestResponse { + accept: Some(AcceptedStorageRequestParameters { + file_keys_and_proofs: bounded_vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )], + non_inclusion_forest_proof: CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + }), + reject: None + } + )] + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, true)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Assert that the capacity used by the MSP was updated + assert_eq!( + pallet_storage_providers::MainStorageProviders::::get(msp_id) + .expect("MSP should exist in storage") + .capacity_used, + size + ); + + // Now that the MSP has accepted storing, we can simulate the user being insolvent + // and the MSP stopping storing for the user. + // Try to stop storing for the insolvent user. + assert_ok!(FileSystem::stop_storing_for_insolvent_user( + msp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Get the new bucket root after deletion + let new_bucket_root_after_deletion = + Providers::get_root_bucket(&bucket_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::SpStopStoringInsolventUser { + sp_id: msp_id, + file_key, + owner: owner_account_id, + location, + new_root: new_bucket_root_after_deletion, + } + .into(), + ); + + // Assert that the capacity used by the MSP was updated + assert_eq!( + pallet_storage_providers::MainStorageProviders::::get(msp_id) + .expect("MSP should exist in storage") + .capacity_used, + 0 + ); + }); + } + + #[test] + fn stop_storing_for_insolvent_user_works_if_user_does_not_have_payment_stream_with_sp() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let new_root = Providers::get_root(bsp_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id, + bsp_id, + } + .into(), + ); + + // Assert that the capacity used by the BSP was updated + assert_eq!( + pallet_storage_providers::BackupStorageProviders::::get(bsp_id) + .expect("BSP should exist in storage") + .capacity_used, + size + ); + + // Now that the BSP has confirmed storing, we can simulate the payment stream being deleted + // and the BSP stopping storing for the user. Note that we use Alice as a user for this test, + // which is NOT an insolvent user. This simulates the case where the user has correctly paid all + // its debt but a lagging SP has not updated its storage state yet. + + // Delete the payment stream between the user and the BSP. + assert_ok!( + ::delete_dynamic_rate_payment_stream( + &bsp_id, + &owner_account_id, + ) + ); + // Try to stop storing the user's file as the BSP. + assert_ok!(FileSystem::stop_storing_for_insolvent_user( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + )); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::SpStopStoringInsolventUser { + sp_id: bsp_id, + file_key, + owner: owner_account_id, + location, + new_root, + } + .into(), + ); + + // Assert that the capacity used by the BSP was updated + assert_eq!( + pallet_storage_providers::BackupStorageProviders::::get(bsp_id) + .expect("BSP should exist in storage") + .capacity_used, + 0 + ); + }); + } + } + + mod failure { + + use super::*; + + #[test] + fn stop_storing_for_insolvent_user_fails_if_caller_not_a_sp() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Eve.to_account_id(); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Try to stop storing for the insolvent user. + assert_noop!( + FileSystem::stop_storing_for_insolvent_user( + bsp_signed.clone(), + H256::zero(), + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![H256::zero().as_ref().to_vec()], + }, + ), + Error::::NotASp + ); + }); + } + + #[test] + fn stop_storing_for_insolvent_user_fails_if_user_not_insolvent() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Alice.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let new_root = Providers::get_root(bsp_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id, + bsp_id, + } + .into(), + ); + + // Now that the BSP has confirmed storing, we can simulate the user being not insolvent + // and the BSP trying to stop storing a file for the user using the stop_storing_for_insolvent_user function. + assert_noop!( + FileSystem::stop_storing_for_insolvent_user( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::UserNotInsolvent + ); + }); + } + + #[test] + fn stop_storing_for_insolvent_user_fails_if_caller_not_owner_of_bucket() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Eve.to_account_id(); + let msp = Keyring::Charlie.to_account_id(); + let another_msp = Keyring::Dave.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + + let msp_id = add_msp_to_provider_storage(&msp); + add_msp_to_provider_storage(&another_msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Try to stop storing for the insolvent user using another MSP account. + assert_noop!( + FileSystem::stop_storing_for_insolvent_user( + RuntimeOrigin::signed(another_msp.clone()), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![file_key.as_ref().to_vec()], + }, + ), + Error::::MspNotStoringBucket + ); + }); + } + + #[test] + fn stop_storing_for_insolvent_user_fails_if_proof_does_not_contain_file_key() { + new_test_ext().execute_with(|| { + let owner_account_id = Keyring::Eve.to_account_id(); + let owner_signed = RuntimeOrigin::signed(owner_account_id.clone()); + let bsp_account_id = Keyring::Bob.to_account_id(); + let bsp_signed = RuntimeOrigin::signed(bsp_account_id.clone()); + let msp = Keyring::Charlie.to_account_id(); + let location = FileLocation::::try_from(b"test".to_vec()).unwrap(); + let size = 4; + let fingerprint = H256::zero(); + let peer_id = BoundedVec::try_from(vec![1]).unwrap(); + let peer_ids: PeerIds = BoundedVec::try_from(vec![peer_id]).unwrap(); + let storage_amount: StorageData = 100; + + let msp_id = add_msp_to_provider_storage(&msp); + + let name = BoundedVec::try_from(b"bucket".to_vec()).unwrap(); + let bucket_id = create_bucket(&owner_account_id.clone(), name, msp_id); + + // Dispatch storage request. + assert_ok!(FileSystem::issue_storage_request( + owner_signed.clone(), + bucket_id, + location.clone(), + fingerprint, + size, + msp_id, + peer_ids.clone(), + )); + + // Sign up account as a Backup Storage Provider + assert_ok!(bsp_sign_up(bsp_signed.clone(), storage_amount)); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let bsp_id = Providers::get_provider_id(bsp_account_id.clone()).unwrap(); + + // Dispatch BSP volunteer. + assert_ok!(FileSystem::bsp_volunteer(bsp_signed.clone(), file_key,)); + + // Get the current tick number. + let tick_when_confirming = ProofsDealer::get_current_tick(); + + // Dispatch BSP confirm storing. + assert_ok!(FileSystem::bsp_confirm_storing( + bsp_signed.clone(), + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + }, + BoundedVec::try_from(vec![( + file_key, + CompactProof { + encoded_nodes: vec![H256::default().as_ref().to_vec()], + } + )]) + .unwrap(), + )); + + // Assert that the storage was updated + assert_eq!( + file_system::StorageRequests::::get(file_key), + Some(StorageRequestMetadata { + requested_at: 1, + owner: owner_account_id.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp: Some((msp_id, false)), + user_peer_ids: peer_ids.clone(), + data_server_sps: BoundedVec::default(), + bsps_required: ReplicationTarget::::get(), + bsps_confirmed: 1, + bsps_volunteered: 1, + }) + ); + + // Assert that the RequestStorageBsps was updated + assert_eq!( + file_system::StorageRequestBsps::::get(file_key, bsp_id) + .expect("BSP should exist in storage"), + StorageRequestBspsMetadata:: { + confirmed: true, + _phantom: Default::default() + } + ); + + let file_key = FileSystem::compute_file_key( + owner_account_id.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + let new_root = Providers::get_root(bsp_id).unwrap(); + + // Assert that the correct event was deposited + System::assert_last_event( + Event::BspConfirmedStoring { + who: bsp_account_id.clone(), + bsp_id, + file_keys: BoundedVec::try_from(vec![file_key]).unwrap(), + new_root, + } + .into(), + ); + + // Assert that the proving cycle was initialised for this BSP. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&bsp_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, tick_when_confirming); + + // Assert that the correct event was deposited. + System::assert_has_event( + Event::BspChallengeCycleInitialised { + who: bsp_account_id, + bsp_id, + } + .into(), + ); + + // Now that the BSP has confirmed storing, we can simulate the user being insolvent + // and the BSP trying to stop storing for that user with an incorrect inclusion proof. + assert_noop!( + FileSystem::stop_storing_for_insolvent_user( + bsp_signed.clone(), + file_key, + bucket_id, + location.clone(), + owner_account_id.clone(), + fingerprint, + size, + CompactProof { + encoded_nodes: vec![H256::zero().as_ref().to_vec()], + }, + ), + Error::::ExpectedInclusionProof + ); + }); + } + } +} + +/// Helper function that registers an account as a Backup Storage Provider +fn bsp_sign_up( + bsp_signed: RuntimeOrigin, + storage_amount: StorageData, +) -> DispatchResultWithPostInfo { + let multiaddresses = create_sp_multiaddresses(); + + // Request to sign up the account as a Backup Storage Provider + assert_ok!(Providers::request_bsp_sign_up( + bsp_signed.clone(), + storage_amount, + multiaddresses, + bsp_signed.clone().into_signer().unwrap() + )); + + // Advance enough blocks for randomness to be valid + roll_to(frame_system::Pallet::::block_number() + 4); + + // Confirm the sign up of the account as a Backup Storage Provider + assert_ok!(Providers::confirm_sign_up(bsp_signed.clone(), None)); + + Ok(().into()) +} + +fn create_sp_multiaddresses( +) -> BoundedVec, MaxMultiAddressAmount> { + let mut multiaddresses: BoundedVec, MaxMultiAddressAmount> = + BoundedVec::new(); + multiaddresses.force_push( + "/ip4/127.0.0.1/udp/1234" + .as_bytes() + .to_vec() + .try_into() + .unwrap(), + ); + multiaddresses +} + +fn add_msp_to_provider_storage(msp: &sp_runtime::AccountId32) -> ProviderIdFor { + let msp_hash = <::Hashing as Hasher>::hash(msp.as_slice()); + + let msp_info = pallet_storage_providers::types::MainStorageProvider { + buckets: BoundedVec::default(), + capacity: 100, + capacity_used: 0, + multiaddresses: BoundedVec::default(), + value_prop: pallet_storage_providers::types::ValueProposition { + identifier: pallet_storage_providers::types::ValuePropId::::default(), + data_limit: 100, + protocols: BoundedVec::default(), + }, + last_capacity_change: frame_system::Pallet::::block_number(), + owner_account: msp.clone(), + payment_account: msp.clone(), + }; + + pallet_storage_providers::MainStorageProviders::::insert(msp_hash, msp_info); + pallet_storage_providers::AccountIdToMainStorageProviderId::::insert( + msp.clone(), + msp_hash, + ); + + msp_hash +} + +fn create_bucket( + owner: &sp_runtime::AccountId32, + name: BucketNameFor, + msp_id: ProviderIdFor, +) -> BucketIdFor { + let bucket_id = + ::Providers::derive_bucket_id(&msp_id, &owner, name.clone()); + + let origin = RuntimeOrigin::signed(owner.clone()); + + // Dispatch a signed extrinsic. + assert_ok!(FileSystem::create_bucket( + origin, + msp_id, + name.clone(), + false + )); + + // Assert bucket was created + assert_eq!( + pallet_storage_providers::Buckets::::get(bucket_id), + Some(Bucket { + root: ::DefaultMerkleRoot::get(), + user_id: owner.clone(), + msp_id, + private: false, + read_access_group_id: None, + size: 0, + }) + ); + + bucket_id +} diff --git a/pallets/file-system/src/types.rs b/pallets/file-system/src/types.rs index f9bf1643e..b524139c7 100644 --- a/pallets/file-system/src/types.rs +++ b/pallets/file-system/src/types.rs @@ -1,469 +1,469 @@ -use core::cmp::max; - -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{ - traits::{fungible::Inspect, nonfungibles_v2::Inspect as NonFungiblesInspect, Get}, - BoundedVec, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use pallet_nfts::CollectionConfig; -use scale_info::TypeInfo; -use shp_file_metadata::FileMetadata; -use shp_traits::ReadProvidersInterface; -use sp_runtime::{traits::CheckedAdd, DispatchError}; -use sp_std::fmt::Debug; - -use crate::{ - Config, Error, FileDeletionRequestExpirations, MoveBucketRequestExpirations, - NextAvailableFileDeletionRequestExpirationBlock, NextAvailableMoveBucketRequestExpirationBlock, - NextAvailableStorageRequestExpirationBlock, StorageRequestExpirations, -}; - -/// Ephemeral metadata of a storage request. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct StorageRequestMetadata { - /// Tick number at which the storage request was made. - /// - /// Used primarily for tracking the age of the request which is useful for - /// cleaning up old requests. - pub requested_at: TickNumber, - - /// AccountId of the user who owns the data being stored. - pub owner: T::AccountId, - - /// Bucket id where this file is stored. - pub bucket_id: BucketIdFor, - - /// User defined name of the file being stored. - pub location: FileLocation, - - /// Identifier of the data being stored. - pub fingerprint: Fingerprint, - - /// Size of the data being stored. - /// - /// SPs will use this to determine if they have enough space to store the data. - /// This is also used to verify that the data sent by the user matches the size specified here. - pub size: StorageData, - - /// MSP who is requested to store the data, and if it has already confirmed that it is storing it. - /// - /// This is optional in the event when a storage request is created solely to replicate data to other BSPs and an MSP is already storing the data. - pub msp: Option<(ProviderIdFor, bool)>, - - /// Peer Ids of the user who requested the storage. - /// - /// SPs will expect a connection request to be initiated by the user with this Peer Id. - pub user_peer_ids: PeerIds, - - /// List of storage providers that can serve the data that is requested to be stored. - /// - /// This is useful when a BSP stops serving data and automatically creates a new storage request with no user multiaddresses, since - /// SPs can prove and serve the data to be replicated to other BSPs without the user having this stored on their local machine. - pub data_server_sps: BoundedVec, MaxBspsPerStorageRequest>, // TODO: Change the Maximum data servers to be the maximum SPs allowed - - /// Number of BSPs requested to store the data. - /// - /// The storage request will be dropped/complete once all the minimum required BSPs have - /// submitted a proof of storage after volunteering to store the data. - pub bsps_required: ReplicationTargetType, - - /// Number of BSPs that have successfully volunteered AND confirmed that they are storing the data. - /// - /// This starts at 0 and increases up to `bsps_required`. Once this reaches `bsps_required`, the - /// storage request is considered complete and will be deleted.. - pub bsps_confirmed: ReplicationTargetType, - - /// Number of BSPs that have volunteered to store the data. - /// - /// There can be more than `bsps_required` volunteers, but it is essentially a race for BSPs to confirm that they are storing the data. - pub bsps_volunteered: ReplicationTargetType, -} - -impl StorageRequestMetadata { - pub fn to_file_metadata( - self, - ) -> FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - > { - FileMetadata { - owner: self.owner.encode(), - bucket_id: self.bucket_id.as_ref().to_vec(), - location: self.location.to_vec(), - file_size: self.size.into() as u64, - fingerprint: self.fingerprint.as_ref().into(), - } - } -} - -/// Possible MSP responses to a storage request. -/// -/// Contains two lists: one for accepted storage requests and one for rejected -/// storage requests, and either of them can be `None` if there are no accepted/rejected -/// storage requests. -/// -/// Accepted storage requests come bundled into a [`AcceptedStorageRequestParameters`]. -/// Rejected storage requests are represented by a list of tuples, where the first element -/// is the rejected file key and the second element is the reason for rejection as a -/// [`RejectedStorageRequestReason`]. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MspStorageRequestResponse { - pub accept: Option>, - /// Reject the storage request. (file_key, reason) - pub reject: Option< - BoundedVec< - (MerkleHash, RejectedStorageRequestReason), - MaxBatchMspRespondStorageRequests, - >, - >, -} - -impl Debug for MspStorageRequestResponse { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "MspStorageRequestResponse(accept: {:?}, reject: {:?})", - self.accept.encode(), - self.reject.encode() - ) - } -} - -/// A bundle of file keys that have been accepted by an MSP, alongside the proofs required to -/// add these file keys into the corresponding bucket. -/// -/// This struct includes a list of file keys and their corresponding key proofs (i.e. the -/// proofs for the file chunks) and a non-inclusion forest proof. The latter is required to -/// verify that the file keys were not part of the bucket's Merkle Patricia Forest before, -/// and add them now. One single non-inclusion forest proof for all the file keys is sufficient. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct AcceptedStorageRequestParameters { - pub file_keys_and_proofs: - BoundedVec<(MerkleHash, KeyProof), MaxBatchMspRespondStorageRequests>, - pub non_inclusion_forest_proof: ForestProof, -} - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -pub enum RejectedStorageRequestReason { - ReachedMaximumCapacity, - ReceivedInvalidProof, - InternalError, -} - -/// Input for MSPs to respond to storage request(s). -/// -/// The input is a list of ([BucketIdFor], [MspStorageRequestResponse]) elements, -/// where the [MspStorageRequestResponse] contains the file keys that are accepted -/// or rejected by the MSP. -pub type FileKeyResponsesInput = BoundedVec< - (BucketIdFor, MspStorageRequestResponse), - MaxBatchMspRespondStorageRequests, ->; - -/// Result from an MSP responding to storage request(s). -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MspRespondStorageRequestsResult { - pub msp_id: ProviderIdFor, - pub responses: BoundedVec, MaxBatchMspRespondStorageRequests>, -} - -impl Debug for MspRespondStorageRequestsResult { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "MspRespondStorageRequestsResult(msp_id: {:?}, responses: {:?})", - self.msp_id, - self.responses.encode() - ) - } -} - -/// Possible response batches for an MSP accepting, rejecting, or failing to respond to storage requests. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub enum BatchResponses { - Accepted(MspAcceptedBatchStorageRequests), - Rejected(MspRejectedBatchStorageRequests), - Failed(MspFailedBatchStorageRequests), -} - -/// Batch of accepted storage requests (i.e. file keys) all belonging to the same bucket. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MspAcceptedBatchStorageRequests { - pub file_keys: BoundedVec, MaxBatchMspRespondStorageRequests>, - pub bucket_id: BucketIdFor, - pub new_bucket_root: MerkleHash, - pub owner: T::AccountId, -} - -/// Batch of rejected storage requests (i.e. file keys) all belonging to the same bucket. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MspRejectedBatchStorageRequests { - pub file_keys: BoundedVec< - (MerkleHash, RejectedStorageRequestReason), - MaxBatchMspRespondStorageRequests, - >, - pub bucket_id: BucketIdFor, - pub owner: T::AccountId, -} - -/// Batch of failed storage requests (i.e. file keys) all belonging to the same bucket. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MspFailedBatchStorageRequests { - pub file_keys: BoundedVec<(MerkleHash, DispatchError), MaxBatchMspRespondStorageRequests>, - pub bucket_id: BucketIdFor, - pub owner: T::AccountId, -} - -/// Ephemeral BSP storage request tracking metadata. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct StorageRequestBspsMetadata { - /// Confirmed that the data is being stored. - /// - /// This is normally when the BSP submits a proof of storage to the `pallet-proofs-dealer-trie`. - pub confirmed: bool, - pub _phantom: core::marker::PhantomData, -} - -/// Bucket privacy settings. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -pub enum BucketPrivacy { - Public, - Private, -} - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub enum ExpirationItem { - StorageRequest(MerkleHash), - PendingFileDeletionRequests((T::AccountId, MerkleHash)), - MoveBucketRequest((ProviderIdFor, BucketIdFor)), -} - -impl ExpirationItem { - pub(crate) fn get_ttl(&self) -> BlockNumberFor { - match self { - ExpirationItem::StorageRequest(_) => T::StorageRequestTtl::get().into(), - ExpirationItem::PendingFileDeletionRequests(_) => { - T::PendingFileDeletionRequestTtl::get().into() - } - ExpirationItem::MoveBucketRequest(_) => T::MoveBucketRequestTtl::get().into(), - } - } - - pub(crate) fn get_next_expiration_block(&self) -> BlockNumberFor { - // The expiration block is the maximum between the next available block and the current block number plus the TTL. - let current_block_plus_ttl = frame_system::Pallet::::block_number() + self.get_ttl(); - let next_available_block = match self { - ExpirationItem::StorageRequest(_) => { - NextAvailableStorageRequestExpirationBlock::::get() - } - ExpirationItem::PendingFileDeletionRequests(_) => { - NextAvailableFileDeletionRequestExpirationBlock::::get() - } - ExpirationItem::MoveBucketRequest(_) => { - NextAvailableMoveBucketRequestExpirationBlock::::get() - } - }; - - max(next_available_block, current_block_plus_ttl) - } - - pub(crate) fn try_append( - &self, - expiration_block: BlockNumberFor, - ) -> Result, DispatchError> { - let mut next_expiration_block = expiration_block; - while let Err(_) = match self { - ExpirationItem::StorageRequest(storage_request) => { - >::try_append(next_expiration_block, *storage_request) - } - ExpirationItem::PendingFileDeletionRequests(pending_file_deletion_requests) => { - >::try_append( - next_expiration_block, - pending_file_deletion_requests.clone(), - ) - } - ExpirationItem::MoveBucketRequest(msp_bucket_id) => { - >::try_append(next_expiration_block, *msp_bucket_id) - } - } { - next_expiration_block = next_expiration_block - .checked_add(&1u8.into()) - .ok_or(Error::::MaxBlockNumberReached)?; - } - - Ok(next_expiration_block) - } - - pub(crate) fn set_next_expiration_block(&self, next_expiration_block: BlockNumberFor) { - match self { - ExpirationItem::StorageRequest(_) => { - NextAvailableStorageRequestExpirationBlock::::set(next_expiration_block); - } - ExpirationItem::PendingFileDeletionRequests(_) => { - NextAvailableFileDeletionRequestExpirationBlock::::set(next_expiration_block); - } - ExpirationItem::MoveBucketRequest(_) => { - NextAvailableMoveBucketRequestExpirationBlock::::set(next_expiration_block); - } - } - } -} - -/// Possible responses to a move bucket request. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -pub enum BucketMoveRequestResponse { - Accepted, - Rejected, -} - -/// Move bucket request metadata -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub struct MoveBucketRequestMetadata { - /// The user who requested to move the bucket. - pub requester: T::AccountId, -} - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] -#[scale_info(skip_type_params(T))] -pub enum EitherAccountIdOrMspId { - AccountId(T::AccountId), - MspId(ProviderIdFor), -} - -impl Debug for EitherAccountIdOrMspId { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - EitherAccountIdOrMspId::AccountId(account_id) => { - write!(f, "AccountId({:?})", account_id) - } - EitherAccountIdOrMspId::MspId(provider_id) => { - write!(f, "MspId({:?})", provider_id) - } - } - } -} - -/// Alias for the `MerkleHash` type used in the ProofsDealerInterface representing file keys. -pub type MerkleHash = - <::ProofDealer as shp_traits::ProofsDealerInterface>::MerkleHash; - -/// Alias for the `ForestProof` type used in the ProofsDealerInterface. -pub type ForestProof = - <::ProofDealer as shp_traits::ProofsDealerInterface>::ForestProof; - -/// Alias for the `KeyProof` type used in the ProofsDealerInterface. -pub type KeyProof = - <::ProofDealer as shp_traits::ProofsDealerInterface>::KeyProof; - -/// Alias for the `MerkleHashing` type used in the ProofsDealerInterface. -pub type FileKeyHasher = - <::ProofDealer as shp_traits::ProofsDealerInterface>::MerkleHashing; - -/// Alias for the `MaxBspsPerStorageRequest` type used in the FileSystem pallet. -pub type MaxBspsPerStorageRequest = ::MaxBspsPerStorageRequest; - -/// Alias for the `MaxBatchConfirmStorageRequests` type used in the FileSystem pallet. -pub type MaxBatchConfirmStorageRequests = ::MaxBatchConfirmStorageRequests; - -/// Alias for the `MaxBatchMspRespondStorageRequests` type used in the FileSystem pallet. -pub type MaxBatchMspRespondStorageRequests = - ::MaxBatchMspRespondStorageRequests; - -/// Alias for the `MaxFilePathSize` type used in the FileSystem pallet. -pub type MaxFilePathSize = ::MaxFilePathSize; - -/// Alias for the `Fingerprint` type used in the FileSystem pallet. -pub type Fingerprint = ::Fingerprint; - -/// Alias for the `StorageData` type used in the MutateProvidersInterface. -pub type StorageData = - <::Providers as shp_traits::MutateStorageProvidersInterface>::StorageDataUnit; - -/// Alias for the `ReplicationTargetType` type used in the FileSystem pallet. -pub type ReplicationTargetType = ::ReplicationTargetType; - -/// Alias for the `StorageRequestTtl` type used in the FileSystem pallet. -pub type StorageRequestTtl = ::StorageRequestTtl; - -/// Alias for the `PendingFileDeletionRequestTtl` type used in the FileSystem pallet. -pub type PendingFileDeletionRequestTtl = ::PendingFileDeletionRequestTtl; - -/// Byte array representing the file path. -pub type FileLocation = BoundedVec>; - -/// Alias for the `MaxPeerIdSize` type used in the FileSystem pallet. -pub type MaxPeerIdSize = ::MaxPeerIdSize; - -/// Byte array representing the libp2p peer Id. -pub type PeerId = BoundedVec>; - -/// Alias for the `MaxNumberOfPeerIds` type used in the FileSystem pallet. -pub type MaxNumberOfPeerIds = ::MaxNumberOfPeerIds; - -/// Alias for a bounded vector of [`PeerId`]. -pub type PeerIds = BoundedVec, MaxNumberOfPeerIds>; - -/// Alias for the `MultiAddress` type used in the ReadProvidersInterface. -pub type MultiAddress = - <::Providers as shp_traits::ReadStorageProvidersInterface>::MultiAddress; - -/// Alias for the `MaxMultiAddresses` type used in the ReadProvidersInterface. -pub type MaxMultiAddresses = - <::Providers as shp_traits::ReadStorageProvidersInterface>::MaxNumberOfMultiAddresses; - -/// Alias for a bounded vector of [`MultiAddress`]. -pub type MultiAddresses = BoundedVec, MaxMultiAddresses>; - -/// Alias for the `Balance` type used in the FileSystem pallet. -pub type BalanceOf = - <::Currency as Inspect<::AccountId>>::Balance; - -/// Alias for the `CollectionId` type used in the Nfts pallet. -pub(super) type CollectionIdFor = <::Nfts as NonFungiblesInspect< - ::AccountId, ->>::CollectionId; - -/// Alias for the `CollectionConfig` type used in the FileSystem pallet. -pub(super) type CollectionConfigFor = - CollectionConfig, BlockNumberFor, CollectionIdFor>; - -/// Alias for the `BucketNameLimit` type used in the ReadProvidersInterface. -pub(super) type BucketNameLimitFor = - <::Providers as shp_traits::ReadBucketsInterface>::BucketNameLimit; - -/// Type alias representing the type of `BucketId` used in `ProvidersInterface`. -pub(crate) type BucketIdFor = - <::Providers as shp_traits::ReadBucketsInterface>::BucketId; - -/// Alias for the `ProviderId` type used in the ProvidersInterface. -pub type ProviderIdFor = <::Providers as ReadProvidersInterface>::ProviderId; - -/// Alias for the bucket name. -pub type BucketNameFor = BoundedVec>; - -/// Alias for the type of the storage request expiration item. -pub type StorageRequestExpirationItem = MerkleHash; - -/// Alias for the type of the file deletion request expiration item. -pub type FileDeletionRequestExpirationItem = - (::AccountId, MerkleHash); - -/// Alias for the `ThresholdType` used in the FileSystem pallet. -pub type ThresholdType = ::ThresholdType; - -/// Alias for the `TickNumber` used in the ProofsDealer pallet. -pub type TickNumber = - <::ProofDealer as shp_traits::ProofsDealerInterface>::TickNumber; +use core::cmp::max; + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + traits::{fungible::Inspect, nonfungibles_v2::Inspect as NonFungiblesInspect, Get}, + BoundedVec, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_nfts::CollectionConfig; +use scale_info::TypeInfo; +use shp_file_metadata::FileMetadata; +use shp_traits::ReadProvidersInterface; +use sp_runtime::{traits::CheckedAdd, DispatchError}; +use sp_std::fmt::Debug; + +use crate::{ + Config, Error, FileDeletionRequestExpirations, MoveBucketRequestExpirations, + NextAvailableFileDeletionRequestExpirationBlock, NextAvailableMoveBucketRequestExpirationBlock, + NextAvailableStorageRequestExpirationBlock, StorageRequestExpirations, +}; + +/// Ephemeral metadata of a storage request. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct StorageRequestMetadata { + /// Tick number at which the storage request was made. + /// + /// Used primarily for tracking the age of the request which is useful for + /// cleaning up old requests. + pub requested_at: TickNumber, + + /// AccountId of the user who owns the data being stored. + pub owner: T::AccountId, + + /// Bucket id where this file is stored. + pub bucket_id: BucketIdFor, + + /// User defined name of the file being stored. + pub location: FileLocation, + + /// Identifier of the data being stored. + pub fingerprint: Fingerprint, + + /// Size of the data being stored. + /// + /// SPs will use this to determine if they have enough space to store the data. + /// This is also used to verify that the data sent by the user matches the size specified here. + pub size: StorageData, + + /// MSP who is requested to store the data, and if it has already confirmed that it is storing it. + /// + /// This is optional in the event when a storage request is created solely to replicate data to other BSPs and an MSP is already storing the data. + pub msp: Option<(ProviderIdFor, bool)>, + + /// Peer Ids of the user who requested the storage. + /// + /// SPs will expect a connection request to be initiated by the user with this Peer Id. + pub user_peer_ids: PeerIds, + + /// List of storage providers that can serve the data that is requested to be stored. + /// + /// This is useful when a BSP stops serving data and automatically creates a new storage request with no user multiaddresses, since + /// SPs can prove and serve the data to be replicated to other BSPs without the user having this stored on their local machine. + pub data_server_sps: BoundedVec, MaxBspsPerStorageRequest>, // TODO: Change the Maximum data servers to be the maximum SPs allowed + + /// Number of BSPs requested to store the data. + /// + /// The storage request will be dropped/complete once all the minimum required BSPs have + /// submitted a proof of storage after volunteering to store the data. + pub bsps_required: ReplicationTargetType, + + /// Number of BSPs that have successfully volunteered AND confirmed that they are storing the data. + /// + /// This starts at 0 and increases up to `bsps_required`. Once this reaches `bsps_required`, the + /// storage request is considered complete and will be deleted.. + pub bsps_confirmed: ReplicationTargetType, + + /// Number of BSPs that have volunteered to store the data. + /// + /// There can be more than `bsps_required` volunteers, but it is essentially a race for BSPs to confirm that they are storing the data. + pub bsps_volunteered: ReplicationTargetType, +} + +impl StorageRequestMetadata { + pub fn to_file_metadata( + self, + ) -> FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + > { + FileMetadata { + owner: self.owner.encode(), + bucket_id: self.bucket_id.as_ref().to_vec(), + location: self.location.to_vec(), + file_size: self.size.into() as u64, + fingerprint: self.fingerprint.as_ref().into(), + } + } +} + +/// Possible MSP responses to a storage request. +/// +/// Contains two lists: one for accepted storage requests and one for rejected +/// storage requests, and either of them can be `None` if there are no accepted/rejected +/// storage requests. +/// +/// Accepted storage requests come bundled into a [`AcceptedStorageRequestParameters`]. +/// Rejected storage requests are represented by a list of tuples, where the first element +/// is the rejected file key and the second element is the reason for rejection as a +/// [`RejectedStorageRequestReason`]. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MspStorageRequestResponse { + pub accept: Option>, + /// Reject the storage request. (file_key, reason) + pub reject: Option< + BoundedVec< + (MerkleHash, RejectedStorageRequestReason), + MaxBatchMspRespondStorageRequests, + >, + >, +} + +impl Debug for MspStorageRequestResponse { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "MspStorageRequestResponse(accept: {:?}, reject: {:?})", + self.accept.encode(), + self.reject.encode() + ) + } +} + +/// A bundle of file keys that have been accepted by an MSP, alongside the proofs required to +/// add these file keys into the corresponding bucket. +/// +/// This struct includes a list of file keys and their corresponding key proofs (i.e. the +/// proofs for the file chunks) and a non-inclusion forest proof. The latter is required to +/// verify that the file keys were not part of the bucket's Merkle Patricia Forest before, +/// and add them now. One single non-inclusion forest proof for all the file keys is sufficient. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct AcceptedStorageRequestParameters { + pub file_keys_and_proofs: + BoundedVec<(MerkleHash, KeyProof), MaxBatchMspRespondStorageRequests>, + pub non_inclusion_forest_proof: ForestProof, +} + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +pub enum RejectedStorageRequestReason { + ReachedMaximumCapacity, + ReceivedInvalidProof, + InternalError, +} + +/// Input for MSPs to respond to storage request(s). +/// +/// The input is a list of ([BucketIdFor], [MspStorageRequestResponse]) elements, +/// where the [MspStorageRequestResponse] contains the file keys that are accepted +/// or rejected by the MSP. +pub type FileKeyResponsesInput = BoundedVec< + (BucketIdFor, MspStorageRequestResponse), + MaxBatchMspRespondStorageRequests, +>; + +/// Result from an MSP responding to storage request(s). +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MspRespondStorageRequestsResult { + pub msp_id: ProviderIdFor, + pub responses: BoundedVec, MaxBatchMspRespondStorageRequests>, +} + +impl Debug for MspRespondStorageRequestsResult { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "MspRespondStorageRequestsResult(msp_id: {:?}, responses: {:?})", + self.msp_id, + self.responses.encode() + ) + } +} + +/// Possible response batches for an MSP accepting, rejecting, or failing to respond to storage requests. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub enum BatchResponses { + Accepted(MspAcceptedBatchStorageRequests), + Rejected(MspRejectedBatchStorageRequests), + Failed(MspFailedBatchStorageRequests), +} + +/// Batch of accepted storage requests (i.e. file keys) all belonging to the same bucket. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MspAcceptedBatchStorageRequests { + pub file_keys: BoundedVec, MaxBatchMspRespondStorageRequests>, + pub bucket_id: BucketIdFor, + pub new_bucket_root: MerkleHash, + pub owner: T::AccountId, +} + +/// Batch of rejected storage requests (i.e. file keys) all belonging to the same bucket. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MspRejectedBatchStorageRequests { + pub file_keys: BoundedVec< + (MerkleHash, RejectedStorageRequestReason), + MaxBatchMspRespondStorageRequests, + >, + pub bucket_id: BucketIdFor, + pub owner: T::AccountId, +} + +/// Batch of failed storage requests (i.e. file keys) all belonging to the same bucket. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MspFailedBatchStorageRequests { + pub file_keys: BoundedVec<(MerkleHash, DispatchError), MaxBatchMspRespondStorageRequests>, + pub bucket_id: BucketIdFor, + pub owner: T::AccountId, +} + +/// Ephemeral BSP storage request tracking metadata. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct StorageRequestBspsMetadata { + /// Confirmed that the data is being stored. + /// + /// This is normally when the BSP submits a proof of storage to the `pallet-proofs-dealer-trie`. + pub confirmed: bool, + pub _phantom: core::marker::PhantomData, +} + +/// Bucket privacy settings. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +pub enum BucketPrivacy { + Public, + Private, +} + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub enum ExpirationItem { + StorageRequest(MerkleHash), + PendingFileDeletionRequests((T::AccountId, MerkleHash)), + MoveBucketRequest((ProviderIdFor, BucketIdFor)), +} + +impl ExpirationItem { + pub(crate) fn get_ttl(&self) -> BlockNumberFor { + match self { + ExpirationItem::StorageRequest(_) => T::StorageRequestTtl::get().into(), + ExpirationItem::PendingFileDeletionRequests(_) => { + T::PendingFileDeletionRequestTtl::get().into() + } + ExpirationItem::MoveBucketRequest(_) => T::MoveBucketRequestTtl::get().into(), + } + } + + pub(crate) fn get_next_expiration_block(&self) -> BlockNumberFor { + // The expiration block is the maximum between the next available block and the current block number plus the TTL. + let current_block_plus_ttl = frame_system::Pallet::::block_number() + self.get_ttl(); + let next_available_block = match self { + ExpirationItem::StorageRequest(_) => { + NextAvailableStorageRequestExpirationBlock::::get() + } + ExpirationItem::PendingFileDeletionRequests(_) => { + NextAvailableFileDeletionRequestExpirationBlock::::get() + } + ExpirationItem::MoveBucketRequest(_) => { + NextAvailableMoveBucketRequestExpirationBlock::::get() + } + }; + + max(next_available_block, current_block_plus_ttl) + } + + pub(crate) fn try_append( + &self, + expiration_block: BlockNumberFor, + ) -> Result, DispatchError> { + let mut next_expiration_block = expiration_block; + while let Err(_) = match self { + ExpirationItem::StorageRequest(storage_request) => { + >::try_append(next_expiration_block, *storage_request) + } + ExpirationItem::PendingFileDeletionRequests(pending_file_deletion_requests) => { + >::try_append( + next_expiration_block, + pending_file_deletion_requests.clone(), + ) + } + ExpirationItem::MoveBucketRequest(msp_bucket_id) => { + >::try_append(next_expiration_block, *msp_bucket_id) + } + } { + next_expiration_block = next_expiration_block + .checked_add(&1u8.into()) + .ok_or(Error::::MaxBlockNumberReached)?; + } + + Ok(next_expiration_block) + } + + pub(crate) fn set_next_expiration_block(&self, next_expiration_block: BlockNumberFor) { + match self { + ExpirationItem::StorageRequest(_) => { + NextAvailableStorageRequestExpirationBlock::::set(next_expiration_block); + } + ExpirationItem::PendingFileDeletionRequests(_) => { + NextAvailableFileDeletionRequestExpirationBlock::::set(next_expiration_block); + } + ExpirationItem::MoveBucketRequest(_) => { + NextAvailableMoveBucketRequestExpirationBlock::::set(next_expiration_block); + } + } + } +} + +/// Possible responses to a move bucket request. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +pub enum BucketMoveRequestResponse { + Accepted, + Rejected, +} + +/// Move bucket request metadata +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub struct MoveBucketRequestMetadata { + /// The user who requested to move the bucket. + pub requester: T::AccountId, +} + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone)] +#[scale_info(skip_type_params(T))] +pub enum EitherAccountIdOrMspId { + AccountId(T::AccountId), + MspId(ProviderIdFor), +} + +impl Debug for EitherAccountIdOrMspId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + EitherAccountIdOrMspId::AccountId(account_id) => { + write!(f, "AccountId({:?})", account_id) + } + EitherAccountIdOrMspId::MspId(provider_id) => { + write!(f, "MspId({:?})", provider_id) + } + } + } +} + +/// Alias for the `MerkleHash` type used in the ProofsDealerInterface representing file keys. +pub type MerkleHash = + <::ProofDealer as shp_traits::ProofsDealerInterface>::MerkleHash; + +/// Alias for the `ForestProof` type used in the ProofsDealerInterface. +pub type ForestProof = + <::ProofDealer as shp_traits::ProofsDealerInterface>::ForestProof; + +/// Alias for the `KeyProof` type used in the ProofsDealerInterface. +pub type KeyProof = + <::ProofDealer as shp_traits::ProofsDealerInterface>::KeyProof; + +/// Alias for the `MerkleHashing` type used in the ProofsDealerInterface. +pub type FileKeyHasher = + <::ProofDealer as shp_traits::ProofsDealerInterface>::MerkleHashing; + +/// Alias for the `MaxBspsPerStorageRequest` type used in the FileSystem pallet. +pub type MaxBspsPerStorageRequest = ::MaxBspsPerStorageRequest; + +/// Alias for the `MaxBatchConfirmStorageRequests` type used in the FileSystem pallet. +pub type MaxBatchConfirmStorageRequests = ::MaxBatchConfirmStorageRequests; + +/// Alias for the `MaxBatchMspRespondStorageRequests` type used in the FileSystem pallet. +pub type MaxBatchMspRespondStorageRequests = + ::MaxBatchMspRespondStorageRequests; + +/// Alias for the `MaxFilePathSize` type used in the FileSystem pallet. +pub type MaxFilePathSize = ::MaxFilePathSize; + +/// Alias for the `Fingerprint` type used in the FileSystem pallet. +pub type Fingerprint = ::Fingerprint; + +/// Alias for the `StorageData` type used in the MutateProvidersInterface. +pub type StorageData = + <::Providers as shp_traits::MutateStorageProvidersInterface>::StorageDataUnit; + +/// Alias for the `ReplicationTargetType` type used in the FileSystem pallet. +pub type ReplicationTargetType = ::ReplicationTargetType; + +/// Alias for the `StorageRequestTtl` type used in the FileSystem pallet. +pub type StorageRequestTtl = ::StorageRequestTtl; + +/// Alias for the `PendingFileDeletionRequestTtl` type used in the FileSystem pallet. +pub type PendingFileDeletionRequestTtl = ::PendingFileDeletionRequestTtl; + +/// Byte array representing the file path. +pub type FileLocation = BoundedVec>; + +/// Alias for the `MaxPeerIdSize` type used in the FileSystem pallet. +pub type MaxPeerIdSize = ::MaxPeerIdSize; + +/// Byte array representing the libp2p peer Id. +pub type PeerId = BoundedVec>; + +/// Alias for the `MaxNumberOfPeerIds` type used in the FileSystem pallet. +pub type MaxNumberOfPeerIds = ::MaxNumberOfPeerIds; + +/// Alias for a bounded vector of [`PeerId`]. +pub type PeerIds = BoundedVec, MaxNumberOfPeerIds>; + +/// Alias for the `MultiAddress` type used in the ReadProvidersInterface. +pub type MultiAddress = + <::Providers as shp_traits::ReadStorageProvidersInterface>::MultiAddress; + +/// Alias for the `MaxMultiAddresses` type used in the ReadProvidersInterface. +pub type MaxMultiAddresses = + <::Providers as shp_traits::ReadStorageProvidersInterface>::MaxNumberOfMultiAddresses; + +/// Alias for a bounded vector of [`MultiAddress`]. +pub type MultiAddresses = BoundedVec, MaxMultiAddresses>; + +/// Alias for the `Balance` type used in the FileSystem pallet. +pub type BalanceOf = + <::Currency as Inspect<::AccountId>>::Balance; + +/// Alias for the `CollectionId` type used in the Nfts pallet. +pub(super) type CollectionIdFor = <::Nfts as NonFungiblesInspect< + ::AccountId, +>>::CollectionId; + +/// Alias for the `CollectionConfig` type used in the FileSystem pallet. +pub(super) type CollectionConfigFor = + CollectionConfig, BlockNumberFor, CollectionIdFor>; + +/// Alias for the `BucketNameLimit` type used in the ReadProvidersInterface. +pub(super) type BucketNameLimitFor = + <::Providers as shp_traits::ReadBucketsInterface>::BucketNameLimit; + +/// Type alias representing the type of `BucketId` used in `ProvidersInterface`. +pub(crate) type BucketIdFor = + <::Providers as shp_traits::ReadBucketsInterface>::BucketId; + +/// Alias for the `ProviderId` type used in the ProvidersInterface. +pub type ProviderIdFor = <::Providers as ReadProvidersInterface>::ProviderId; + +/// Alias for the bucket name. +pub type BucketNameFor = BoundedVec>; + +/// Alias for the type of the storage request expiration item. +pub type StorageRequestExpirationItem = MerkleHash; + +/// Alias for the type of the file deletion request expiration item. +pub type FileDeletionRequestExpirationItem = + (::AccountId, MerkleHash); + +/// Alias for the `ThresholdType` used in the FileSystem pallet. +pub type ThresholdType = ::ThresholdType; + +/// Alias for the `TickNumber` used in the ProofsDealer pallet. +pub type TickNumber = + <::ProofDealer as shp_traits::ProofsDealerInterface>::TickNumber; diff --git a/pallets/file-system/src/utils.rs b/pallets/file-system/src/utils.rs index 8371e6046..7ca902ac8 100644 --- a/pallets/file-system/src/utils.rs +++ b/pallets/file-system/src/utils.rs @@ -1,2310 +1,2310 @@ -use codec::Encode; -use frame_support::{ - ensure, - pallet_prelude::DispatchResult, - traits::{nonfungibles_v2::Create, Get}, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use num_bigint::BigUint; -use sp_runtime::{ - traits::{ - Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Convert, ConvertBack, Hash, One, - Saturating, Zero, - }, - ArithmeticError, BoundedVec, DispatchError, -}; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - vec, - vec::Vec, -}; - -use pallet_file_system_runtime_api::{ - QueryBspConfirmChunksToProveForFileError, QueryConfirmChunksToProveForFileError, - QueryFileEarliestVolunteerTickError, QueryMspConfirmChunksToProveForFileError, -}; -use pallet_nfts::{CollectionConfig, CollectionSettings, ItemSettings, MintSettings, MintType}; -use shp_file_metadata::ChunkId; -use shp_traits::{ - MutateBucketsInterface, MutateStorageProvidersInterface, PaymentStreamsInterface, - ReadBucketsInterface, ReadProvidersInterface, ReadStorageProvidersInterface, - ReadUserSolvencyInterface, TrieAddMutation, TrieRemoveMutation, -}; - -use crate::types::AcceptedStorageRequestParameters; -use crate::{ - pallet, - types::{ - BatchResponses, BucketIdFor, BucketMoveRequestResponse, BucketNameFor, CollectionConfigFor, - CollectionIdFor, EitherAccountIdOrMspId, ExpirationItem, FileKeyHasher, - FileKeyResponsesInput, FileLocation, Fingerprint, ForestProof, KeyProof, - MaxBatchMspRespondStorageRequests, MaxBspsPerStorageRequest, MerkleHash, - MoveBucketRequestMetadata, MspAcceptedBatchStorageRequests, MspFailedBatchStorageRequests, - MspRejectedBatchStorageRequests, MspRespondStorageRequestsResult, MultiAddresses, PeerIds, - ProviderIdFor, RejectedStorageRequestReason, ReplicationTargetType, StorageData, - StorageRequestBspsMetadata, StorageRequestMetadata, TickNumber, - }, - BucketsWithStorageRequests, DataServersForMoveBucket, Error, Event, Pallet, - PendingBucketsToMove, PendingFileDeletionRequests, PendingMoveBucketRequests, - PendingStopStoringRequests, ReplicationTarget, StorageRequestBsps, StorageRequests, - TickRangeToMaximumThreshold, -}; - -macro_rules! expect_or_err { - // Handle Option type - ($optional:expr, $error_msg:expr, $error_type:path) => {{ - match $optional { - Some(value) => value, - None => { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - } - }}; - // Handle boolean type - ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ - if !$condition { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - }}; - // Handle Result type - ($result:expr, $error_msg:expr, $error_type:path, result) => {{ - match $result { - Ok(value) => value, - Err(_) => { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - } - }}; -} - -impl Pallet -where - T: pallet::Config, -{ - /// Compute the tick number at which the BSP is eligible to volunteer for a storage request. - pub fn query_earliest_file_volunteer_tick( - bsp_id: ProviderIdFor, - file_key: MerkleHash, - ) -> Result, QueryFileEarliestVolunteerTickError> - where - T: frame_system::Config, - { - // Get the tick number at which the storage request was created. - let (storage_request_tick, fingerprint) = match >::get(&file_key) { - Some(storage_request) => (storage_request.requested_at, storage_request.fingerprint), - None => { - return Err(QueryFileEarliestVolunteerTickError::StorageRequestNotFound); - } - }; - - // Get the threshold needed for the BSP to be able to volunteer for the storage request. - let bsp_threshold = Self::get_threshold_for_bsp_request(&bsp_id, &fingerprint); - - // Compute the tick number at which the BSP should send the volunteer request. - Self::compute_volunteer_tick_number(bsp_id, bsp_threshold, storage_request_tick) - .map_err(|_| QueryFileEarliestVolunteerTickError::ThresholdArithmeticError) - } - - fn compute_volunteer_tick_number( - bsp_id: ProviderIdFor, - bsp_threshold: T::ThresholdType, - storage_request_tick: TickNumber, - ) -> Result, DispatchError> - where - T: frame_system::Config, - { - // Compute the threshold to succeed and the slope of the bsp. - let (to_succeed, slope) = - Self::compute_threshold_to_succeed(&bsp_id, storage_request_tick)?; - - let threshold_diff = match bsp_threshold.checked_sub(&to_succeed) { - Some(diff) => diff, - None => { - // The BSP's threshold is less than the current threshold. - let current_tick = - ::get_current_tick(); - return Ok(current_tick); - } - }; - - // Calculate the number of ticks required to be below the threshold. - let ticks_to_wait = match threshold_diff.checked_div(&slope) { - Some(ticks) => ticks, - None => { - return Err(Error::::ThresholdArithmeticError.into()); - } - }; - - // Compute the tick number at which the BSP should send the volunteer request. - let volunteer_tick_number = storage_request_tick - .saturating_add(T::ThresholdTypeToTickNumber::convert(ticks_to_wait)); - - Ok(volunteer_tick_number) - } - - pub fn query_bsp_confirm_chunks_to_prove_for_file( - bsp_id: ProviderIdFor, - file_key: MerkleHash, - ) -> Result, QueryBspConfirmChunksToProveForFileError> { - // Get the storage request metadata. - let storage_request_metadata = match >::get(&file_key) { - Some(storage_request) => storage_request, - None => { - return Err(QueryBspConfirmChunksToProveForFileError::StorageRequestNotFound); - } - }; - - Self::query_confirm_chunks_to_prove_for_file(bsp_id, storage_request_metadata, file_key) - .map_err(|e| QueryBspConfirmChunksToProveForFileError::ConfirmChunks(e)) - } - - pub fn query_msp_confirm_chunks_to_prove_for_file( - msp_id: ProviderIdFor, - file_key: MerkleHash, - ) -> Result, QueryMspConfirmChunksToProveForFileError> { - // Get the storage request metadata. - let storage_request_metadata = match >::get(&file_key) { - Some(storage_request) => storage_request, - None => { - return Err(QueryMspConfirmChunksToProveForFileError::StorageRequestNotFound); - } - }; - - Self::query_confirm_chunks_to_prove_for_file(msp_id, storage_request_metadata, file_key) - .map_err(|e| QueryMspConfirmChunksToProveForFileError::ConfirmChunks(e)) - } - - fn query_confirm_chunks_to_prove_for_file( - provider_id: ProviderIdFor, - storage_request_metadata: StorageRequestMetadata, - file_key: MerkleHash, - ) -> Result, QueryConfirmChunksToProveForFileError> { - // Generate the list of chunks to prove. - let challenges = Self::generate_chunk_challenges_on_sp_confirm( - provider_id, - file_key, - &storage_request_metadata, - ); - - let chunks = storage_request_metadata.to_file_metadata().chunks_count(); - - let chunks_to_prove = challenges - .iter() - .map(|challenge| { - let challenged_chunk = BigUint::from_bytes_be(challenge.as_ref()) % chunks; - let challenged_chunk: ChunkId = - ChunkId::new(challenged_chunk.try_into().map_err(|_| { - QueryConfirmChunksToProveForFileError::ChallengedChunkToChunkIdError - })?); - - Ok(challenged_chunk) - }) - .collect::, _>>()?; - - Ok(chunks_to_prove) - } - - fn generate_chunk_challenges_on_sp_confirm( - sp_id: ProviderIdFor, - file_key: MerkleHash, - storage_request_metadata: &StorageRequestMetadata, - ) -> Vec<<::Providers as ReadProvidersInterface>::MerkleHash> { - let file_metadata = storage_request_metadata.clone().to_file_metadata(); - let chunks_to_check = file_metadata.chunks_to_check(); - - let mut challenges = - ::generate_challenges_from_seed( - T::MerkleHashToRandomnessOutput::convert(file_key), - &sp_id, - chunks_to_check - 1, - ); - - let last_chunk_id = file_metadata.last_chunk_id(); - - challenges.push(T::ChunkIdToMerkleHash::convert(last_chunk_id)); - - challenges - } - - /// Create a bucket for an owner (user) under a given MSP account. - pub(crate) fn do_create_bucket( - sender: T::AccountId, - msp_id: ProviderIdFor, - name: BucketNameFor, - private: bool, - ) -> Result<(BucketIdFor, Option>), DispatchError> { - // TODO: Hold user funds for the bucket creation. - - // Check if the MSP is indeed an MSP. - ensure!( - ::is_msp(&msp_id), - Error::::NotAMsp - ); - - // Create collection only if bucket is private - let maybe_collection_id = if private { - // The `owner` of the collection is also the admin of the collection since most operations require the sender to be the admin. - Some(Self::create_collection(sender.clone())?) - } else { - None - }; - - let bucket_id = ::Providers::derive_bucket_id(&msp_id, &sender, name); - - ::add_bucket( - msp_id, - sender, - bucket_id, - private, - maybe_collection_id.clone(), - )?; - - Ok((bucket_id, maybe_collection_id)) - } - - /// This does not guarantee that the MSP will have enough storage capacity to store the entire bucket. Therefore, - /// between the creation of the request and its expiration, the MSP can increase its capacity before accepting the request. - /// - /// Forcing the MSP to have enough capacity before the request is created would not enable MSPs to automatically scale based on demand. - pub(crate) fn do_request_move_bucket( - sender: T::AccountId, - bucket_id: BucketIdFor, - new_msp_id: ProviderIdFor, - ) -> Result<(), DispatchError> { - // Check if the sender is the owner of the bucket. - ensure!( - ::is_bucket_owner(&sender, &bucket_id)?, - Error::::NotBucketOwner - ); - - // Check if the new MSP is indeed an MSP. - ensure!( - ::is_msp(&new_msp_id), - Error::::NotAMsp - ); - - // Check if the bucket is already stored by the new MSP. - ensure!( - !::is_bucket_stored_by_msp( - &new_msp_id, - &bucket_id - ), - Error::::MspAlreadyStoringBucket - ); - - if >::contains_key(&bucket_id) { - return Err(Error::::BucketIsBeingMoved.into()); - } - - // Check if there are any open storage requests for the bucket. - // Do not allow any storage requests and move bucket requests to coexist for the same bucket. - ensure!( - !>::iter_prefix(bucket_id) - .next() - .is_some(), - Error::::StorageRequestExists - ); - - // Register the move bucket request. - >::insert( - &new_msp_id, - bucket_id, - MoveBucketRequestMetadata { - requester: sender.clone(), - }, - ); - >::insert(&bucket_id, ()); - - let expiration_item = ExpirationItem::MoveBucketRequest((new_msp_id, bucket_id)); - Self::enqueue_expiration_item(expiration_item)?; - - Ok(()) - } - - pub(crate) fn do_bsp_add_data_server_for_move_bucket_request( - sender: T::AccountId, - bucket_id: BucketIdFor, - ) -> Result, DispatchError> { - let bsp_id = ::get_provider_id(sender) - .ok_or(Error::::NotABsp)?; - - // Check if the sender is a Storage Provider. - ensure!( - ::is_bsp(&bsp_id), - Error::::NotABsp - ); - - // Check if the move bucket request exists. - ensure!( - >::contains_key(&bucket_id), - Error::::MoveBucketRequestNotFound, - ); - - // Check if the BSP is already a data server for the move bucket request. - ensure!( - !DataServersForMoveBucket::::contains_key(&bucket_id, &bsp_id), - Error::::BspAlreadyDataServer - ); - - // Add the data server to the move bucket request. - DataServersForMoveBucket::::insert(&bucket_id, &bsp_id, ()); - - Ok(bsp_id) - } - - pub(crate) fn do_msp_respond_move_bucket_request( - sender: T::AccountId, - bucket_id: BucketIdFor, - response: BucketMoveRequestResponse, - ) -> Result, DispatchError> { - let msp_id = ::get_provider_id(sender) - .ok_or(Error::::NotAMsp)?; - - // Check if the sender is the MSP. - ensure!( - ::is_msp(&msp_id), - Error::::NotAMsp - ); - - // Check if the move bucket request exists for MSP and bucket. - let move_bucket_requester = >::take(&msp_id, bucket_id); - ensure!( - move_bucket_requester.is_some(), - Error::::MoveBucketRequestNotFound - ); - - if response == BucketMoveRequestResponse::Rejected { - >::remove(&bucket_id); - >::remove(&msp_id, bucket_id); - - return Ok(msp_id); - } - - let previous_msp_id = ::get_msp_bucket(&bucket_id)?; - - // Decrease the used capacity of the current MSP. - let bucket_size = ::get_bucket_size(&bucket_id)?; - - // Check if MSP has enough available capacity to store the bucket. - ensure!( - ::available_capacity(&msp_id) - >= bucket_size, - Error::::InsufficientAvailableCapacity - ); - - // Change the MSP that stores the bucket. - ::change_msp_bucket(&bucket_id, &msp_id)?; - - // Decrease the used capacity of the previous MSP. - ::decrease_capacity_used( - &previous_msp_id, - bucket_size, - )?; - - // Increase the used capacity of the new MSP. - ::increase_capacity_used( - &msp_id, - bucket_size, - )?; - - >::remove(&bucket_id); - - Self::deposit_event(Event::MoveBucketAccepted { bucket_id, msp_id }); - - Ok(msp_id) - } - - /// Update the privacy of a bucket. - /// - /// This function allows the owner of a bucket to update its privacy setting. - /// If the bucket is set to private and no collection exists, - /// a new collection will be created. If the bucket is set to public and - /// an associated collection exists, the collection remains but the privacy setting is updated to public. - /// If the bucket has an associated collection, and it does not exist in storage, a new collection will be created. - pub(crate) fn do_update_bucket_privacy( - sender: T::AccountId, - bucket_id: BucketIdFor, - private: bool, - ) -> Result>, DispatchError> { - // Ensure the sender is the owner of the bucket. - ensure!( - T::Providers::is_bucket_owner(&sender, &bucket_id)?, - Error::::NotBucketOwner - ); - - // Retrieve the collection ID associated with the bucket, if any. - let maybe_collection_id = T::Providers::get_read_access_group_id_of_bucket(&bucket_id)?; - - // Determine the appropriate collection ID based on the new privacy setting. - let collection_id = match (private, maybe_collection_id) { - // Create a new collection if the bucket will be private and no collection exists. - (true, None) => { - Some(Self::do_create_and_associate_collection_with_bucket(sender.clone(), bucket_id)?) - } - // Handle case where the bucket has an existing collection. - (_, Some(current_collection_id)) - if !::collection_exists(¤t_collection_id) => - { - Some(Self::do_create_and_associate_collection_with_bucket(sender.clone(), bucket_id)?) - } - // Use the existing collection ID if it exists. - (_, Some(current_collection_id)) => Some(current_collection_id), - // No collection needed if the bucket is public and no collection exists. - (false, None) => None, - }; - - // Update the privacy setting of the bucket. - T::Providers::update_bucket_privacy(bucket_id, private)?; - - Ok(collection_id) - } - - /// Create and associate collection with a bucket. - /// - /// *Callable only by the owner of the bucket. The bucket must be private.* - /// - /// It is possible to have a bucket that is private but does not have a collection associated with it. This can happen if - /// a user destroys the collection associated with the bucket by calling the NFTs pallet directly. - /// - /// In any case, we will set a new collection the bucket even if there is an existing one associated with it. - pub(crate) fn do_create_and_associate_collection_with_bucket( - sender: T::AccountId, - bucket_id: BucketIdFor, - ) -> Result, DispatchError> { - // Check if sender is the owner of the bucket. - ensure!( - ::is_bucket_owner(&sender, &bucket_id)?, - Error::::NotBucketOwner - ); - - let collection_id = Self::create_collection(sender)?; - - ::update_bucket_read_access_group_id( - bucket_id, - Some(collection_id.clone()), - )?; - - Ok(collection_id) - } - - /// Request storage for a file. - /// - /// In the event that a storage request is created without any user multiaddresses (checkout `do_bsp_stop_storing`), - /// it is expected that storage providers that do have this file in storage already, will be able to send a - /// transaction to the chain to add themselves as a data server for the storage request. - pub(crate) fn do_request_storage( - sender: T::AccountId, - bucket_id: BucketIdFor, - location: FileLocation, - fingerprint: Fingerprint, - size: StorageData, - msp_id: Option>, - bsps_required: Option>, - user_peer_ids: Option>, - data_server_sps: BoundedVec, MaxBspsPerStorageRequest>, - ) -> Result, DispatchError> { - // TODO: Check user funds and lock them for the storage request. - - // Check that the file size is greater than zero. - ensure!(size > Zero::zero(), Error::::FileSizeCannotBeZero); - - // Check that a bucket under the received ID exists and that the sender is the owner of the bucket. - ensure!( - ::is_bucket_owner(&sender, &bucket_id)?, - Error::::NotBucketOwner - ); - - // Check that the bucket is not being moved. - // Do not allow any storage requests and move bucket requests to coexist for the same bucket. - ensure!( - !>::contains_key(&bucket_id), - Error::::BucketIsBeingMoved - ); - - // If a specific MSP ID is provided, check that it is a valid MSP and that it has enough available capacity to store the file. - let msp = if let Some(ref msp_id) = msp_id { - // Check that the received Provider ID corresponds to a valid MSP. - ensure!( - ::is_msp(msp_id), - Error::::NotAMsp - ); - - // Check that the MSP received is the one storing the bucket. - ensure!( - ::is_bucket_stored_by_msp(msp_id, &bucket_id), - Error::::MspNotStoringBucket - ); - - Some((*msp_id, false)) - } else { - None - }; - - let bsps_required = bsps_required.unwrap_or(ReplicationTarget::::get()); - - if bsps_required.is_zero() { - return Err(Error::::ReplicationTargetCannotBeZero)?; - } - - if bsps_required > ReplicationTarget::::get().into() { - return Err(Error::::BspsRequiredExceedsTarget)?; - } - - let current_tick = - ::get_current_tick(); - let storage_request_metadata = StorageRequestMetadata:: { - requested_at: current_tick, - owner: sender.clone(), - bucket_id, - location: location.clone(), - fingerprint, - size, - msp, - user_peer_ids: user_peer_ids.unwrap_or_default(), - data_server_sps, - bsps_required, - bsps_confirmed: ReplicationTargetType::::zero(), - bsps_volunteered: ReplicationTargetType::::zero(), - }; - - // Compute the file key used throughout this file's lifespan. - let file_key = Self::compute_file_key( - sender.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Check a storage request does not already exist for this file key. - ensure!( - !>::contains_key(&file_key), - Error::::StorageRequestAlreadyRegistered - ); - - // Register storage request. - >::insert(&file_key, storage_request_metadata); - - >::insert(&bucket_id, &file_key, ()); - - let expiration_item = ExpirationItem::StorageRequest(file_key); - Self::enqueue_expiration_item(expiration_item)?; - - Ok(file_key) - } - - /// Accepts or rejects batches of storage requests assumed to be grouped by bucket. - /// - /// This is using a best-effort strategy to process as many file keys as possible, returning - /// the ones that were accepted, rejected, or failed to be processed. - /// - /// File keys that are not part of the bucket they belong to will be skipped (failed). - /// - /// All file keys will be processed (unless there are duplicates, they are simply skipped) and any errors - /// while processing them will be marked as a failed key and continue processing the rest. It is up to the - /// caller to verify the final result and apply only the file keys that have been successfully accepted. - pub(crate) fn do_msp_respond_storage_request( - sender: T::AccountId, - file_key_responses_input: FileKeyResponsesInput, - ) -> Result, DispatchError> { - // Check that the sender is a Storage Provider and get its MSP ID - let msp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotASp)?; - - // Check that the sender is an MSP - ensure!( - ::is_msp(&msp_id), - Error::::NotAMsp - ); - - // Initialize batch responses - let mut batch_responses: BoundedVec< - BatchResponses, - MaxBatchMspRespondStorageRequests, - > = BoundedVec::default(); - - // Preliminary check to ensure that the MSP is the one storing each bucket in the responses - for (bucket_id, _) in file_key_responses_input.iter() { - ensure!( - ::is_bucket_stored_by_msp( - &msp_id, &bucket_id - ), - Error::::MspNotStoringBucket - ); - } - - // Process each bucket's responses - for (bucket_id, file_key_responses) in file_key_responses_input { - let mut failed: BoundedVec< - (MerkleHash, DispatchError), - MaxBatchMspRespondStorageRequests, - > = BoundedVec::default(); - - let owner = ::get_bucket_owner(&bucket_id) - .map_err(|_| Error::::BucketNotFound)?; - - if let Some(accepted_file_keys) = file_key_responses.accept { - // Call do_msp_accept_storage_request, which returns the new_bucket_root - let (new_bucket_root, accepted_file_keys, failed_file_keys) = - Self::do_msp_accept_storage_request(msp_id, bucket_id, accepted_file_keys)?; - - // Create batch responses - if !accepted_file_keys.is_empty() { - let accepted_batch = MspAcceptedBatchStorageRequests { - file_keys: accepted_file_keys, - bucket_id, - new_bucket_root, - owner: owner.clone(), - }; - - batch_responses - .try_push(BatchResponses::Accepted(accepted_batch)) - .map_err(|_| Error::::TooManyBatchResponses)?; - } - - if !failed_file_keys.is_empty() { - for rejected_file_key in failed_file_keys { - failed - .try_push(rejected_file_key) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - } - } - } - - if let Some(rejected_file_keys) = file_key_responses.reject { - let mut rejected: BoundedVec< - (MerkleHash, RejectedStorageRequestReason), - MaxBatchMspRespondStorageRequests, - > = BoundedVec::default(); - - for file_key in rejected_file_keys.iter() { - let storage_request_metadata = match >::get(&file_key.0) { - Some(metadata) => metadata, - None => { - failed - .try_push((file_key.0, Error::::StorageRequestNotFound.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - }; - - if let Err(e) = Self::cleanup_storage_request( - EitherAccountIdOrMspId::MspId(msp_id), - file_key.0, - &storage_request_metadata, - ) { - failed - .try_push((file_key.0, e)) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - rejected - .try_push((file_key.0, file_key.1.clone())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - } - - if !rejected.is_empty() { - let rejected_batch = MspRejectedBatchStorageRequests { - file_keys: rejected, - bucket_id, - owner: owner.clone(), - }; - - batch_responses - .try_push(BatchResponses::Rejected(rejected_batch)) - .map_err(|_| Error::::TooManyBatchResponses)?; - } - } - - if !failed.is_empty() { - let failed_batch = MspFailedBatchStorageRequests { - file_keys: failed, - bucket_id, - owner: owner.clone(), - }; - - batch_responses - .try_push(BatchResponses::Failed(failed_batch)) - .map_err(|_| Error::::TooManyBatchResponses)?; - } - } - - // Construct the result - let result = MspRespondStorageRequestsResult { - msp_id, - responses: batch_responses, - }; - - Ok(result) - } - - /// Accept as many storage requests as possible (best-effort) belonging to the same bucket. - /// - /// There should be a single non-inclusion forest proof for all file keys, and finally there should - /// be a list of file key(s) with a key proof for each of them. - /// - /// The implementation follows this sequence: - /// 1. Verify the non-inclusion proof. - /// 2. For each file key: Record a successful acceptance or a failure. Any failed operation while processing a file key - /// will not result in the function failing, but the file key will be marked as failed and the function will continue processing the rest. - /// 3. Apply the delta with all the keys that were successfully accepted to the root of the bucket. - fn do_msp_accept_storage_request( - msp_id: ProviderIdFor, - bucket_id: BucketIdFor, - accepted_file_keys: AcceptedStorageRequestParameters, - ) -> Result< - ( - MerkleHash, - BoundedVec, T::MaxBatchMspRespondStorageRequests>, - BoundedVec< - ( - ::MerkleHash, - DispatchError, - ), - T::MaxBatchMspRespondStorageRequests, - >, - ), - DispatchError, - > { - let file_keys = accepted_file_keys - .file_keys_and_proofs - .iter() - .map(|(fk, _)| *fk) - .collect::>(); - - // Get the Bucket's root - let bucket_root = - ::get_root_bucket(&bucket_id) - .ok_or(Error::::BucketNotFound)?; - - // Verify the proof of non-inclusion. - let proven_keys: BTreeSet> = - ::verify_generic_forest_proof( - &bucket_root, - file_keys.as_slice(), - &accepted_file_keys.non_inclusion_forest_proof, - )?; - - // Initialize accepted, rejected, and failed file keys - let mut accepted_file_keys_and_metadata = BTreeMap::new(); - let mut failed_file_keys: BoundedVec< - (MerkleHash, DispatchError), - T::MaxBatchMspRespondStorageRequests, - > = BoundedVec::default(); - - for (file_key, key_proof) in accepted_file_keys.file_keys_and_proofs { - // Skip any duplicates. - if accepted_file_keys_and_metadata.contains_key(&file_key) { - continue; - } - - let mut storage_request_metadata = match >::get(&file_key) { - Some(metadata) => metadata, - None => { - failed_file_keys - .try_push((file_key, Error::::StorageRequestNotFound.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - }; - - // Ensure that the file key IS NOT part of the bucket's forest. - if proven_keys.contains(&file_key) { - failed_file_keys - .try_push((file_key, Error::::ExpectedNonInclusionProof.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - if storage_request_metadata.bucket_id != bucket_id { - failed_file_keys - .try_push((file_key, Error::::InvalidBucketIdFileKeyPair.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - // Check that the MSP is the one storing the bucket. - if !::is_bucket_stored_by_msp( - &msp_id, - &storage_request_metadata.bucket_id, - ) { - failed_file_keys - .try_push((file_key, Error::::MspNotStoringBucket.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - // Check that the sender corresponds to the MSP in the storage request and that it hasn't yet confirmed storing the file. - match storage_request_metadata.msp { - Some((request_msp_id, confirm_status)) => { - if request_msp_id != msp_id { - failed_file_keys - .try_push((file_key, Error::::NotSelectedMsp.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - if confirm_status { - failed_file_keys - .try_push((file_key, Error::::MspAlreadyConfirmed.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - } - None => { - failed_file_keys - .try_push((file_key, Error::::RequestWithoutMsp.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - } - - // Check that the MSP still has enough available capacity to store the file. - if ::available_capacity(&msp_id) - < storage_request_metadata.size - { - failed_file_keys - .try_push((file_key, Error::::InsufficientAvailableCapacity.into())) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - // Get the file metadata to insert into the bucket under the file key. - let file_metadata = storage_request_metadata.clone().to_file_metadata(); - let encoded_trie_value = file_metadata.encode(); - - let chunk_challenges = Self::generate_chunk_challenges_on_sp_confirm( - msp_id, - file_key, - &storage_request_metadata, - ); - - // Check that the key proof is valid. - if let Err(e) = ::verify_key_proof( - &file_key, - &chunk_challenges, - &key_proof, - ) { - failed_file_keys - .try_push((file_key, e)) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - // Increase size of the bucket. - if let Err(e) = ::increase_bucket_size( - &storage_request_metadata.bucket_id, - storage_request_metadata.size, - ) { - failed_file_keys - .try_push((file_key, e)) - .map_err(|_| Error::::TooManyStorageRequestResponses)?; - continue; - } - - // Increase the used capacity of the MSP - // This should not fail since we checked that the MSP has enough available capacity to store the file. - expect_or_err!( - ::increase_capacity_used( - &msp_id, - storage_request_metadata.size, - ), - "Failed to increase capacity used for MSP", - Error::::TooManyStorageRequestResponses, - result - ); - - // Check if all BSPs have confirmed storing the file. - if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required { - // Remove storage request metadata. - >::remove(&file_key); - >::remove( - &storage_request_metadata.bucket_id, - &file_key, - ); - - // Remove storage request bsps - let removed = - >::drain_prefix(&file_key).fold(0, |acc, _| acc + 1); - - // Make sure that the expected number of bsps were removed. - expect_or_err!( - storage_request_metadata.bsps_volunteered == removed.into(), - "Number of volunteered bsps for storage request should have been removed", - Error::::UnexpectedNumberOfRemovedVolunteeredBsps, - bool - ); - - // Notify that the storage request has been fulfilled. - Self::deposit_event(Event::StorageRequestFulfilled { file_key }); - } else { - // Set as confirmed the MSP in the storage request metadata. - storage_request_metadata.msp = Some((msp_id, true)); - - // Update storage request metadata. - >::set(&file_key, Some(storage_request_metadata.clone())); - } - - // This should not fail since we checked that the key is not already in the map. - expect_or_err!( - accepted_file_keys_and_metadata - .insert(file_key, encoded_trie_value) - .is_none(), - "Failed to insert file key and metadata into accepted_file_keys_and_metadata", - Error::::InconsistentStateKeyAlreadyExists, - bool - ); - } - - // Get the current root of the bucket where the file will be stored. - let bucket_root = expect_or_err!( - ::get_root_bucket(&bucket_id), - "Failed to get root for bucket, when it was already checked to exist", - Error::::BucketNotFound - ); - - // Compute the new bucket root after inserting new file key in its forest partial trie. - let new_bucket_root = - ::generic_apply_delta( - &bucket_root, - accepted_file_keys_and_metadata - .iter() - .map(|(fk, metadata)| (*fk, TrieAddMutation::new(metadata.clone()).into())) - .collect::>() - .as_slice(), - &accepted_file_keys.non_inclusion_forest_proof, - )?; - - // Update root of the bucket. - ::change_root_bucket( - bucket_id, - new_bucket_root, - )?; - - let accepted_file_keys: Vec> = - accepted_file_keys_and_metadata.keys().cloned().collect(); - - Ok(( - new_bucket_root, - accepted_file_keys - .try_into() - .map_err(|_| Error::::TooManyStorageRequestResponses)?, - failed_file_keys, - )) - } - - /// Volunteer to store a file. - /// - /// *Callable only by BSP accounts* - /// - /// A BSP can only volunteer for a storage request if it is eligible based on the XOR of the `fingerprint` and the BSP's account ID and if it evaluates to a value - /// less than the [globally computed threshold](BspsAssignmentThreshold). As the number of BSPs signed up increases, the threshold decreases, meaning there is a - /// lower chance of a BSP being eligible to volunteer for a storage request. - /// - /// Though, as the storage request remains open, the threshold increases over time based on the number of ticks since the storage request was issued. This is to - /// ensure that the storage request is fulfilled by opening up the opportunity for more BSPs to volunteer. - /// - /// For more information on what "ticks" are, see the [Proofs Dealer pallet](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/proofs-dealer/README.md). - pub(crate) fn do_bsp_volunteer( - sender: T::AccountId, - file_key: MerkleHash, - ) -> Result< - ( - ProviderIdFor, - MultiAddresses, - StorageRequestMetadata, - ), - DispatchError, - > { - let bsp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotABsp)?; - - // Check that the provider is indeed a BSP. - ensure!( - ::is_bsp(&bsp_id), - Error::::NotABsp - ); - - // Check that the storage request exists. - let mut storage_request_metadata = - >::get(&file_key).ok_or(Error::::StorageRequestNotFound)?; - - expect_or_err!( - storage_request_metadata.bsps_confirmed < storage_request_metadata.bsps_required, - "Storage request should never have confirmed bsps equal to or greater than required bsps, since they are deleted when it is reached.", - Error::::StorageRequestBspsRequiredFulfilled, - bool - ); - - let available_capacity = - ::available_capacity(&bsp_id); - - // Check if the BSP has enough capacity to store the file. - ensure!( - available_capacity > storage_request_metadata.size, - Error::::InsufficientAvailableCapacity - ); - - // Check if the BSP is already volunteered for this storage request. - ensure!( - !>::contains_key(&file_key, &bsp_id), - Error::::BspAlreadyVolunteered - ); - - // Get the threshold needed for the BSP to be able to volunteer for the storage request. - let bsp_threshold = - Self::get_threshold_for_bsp_request(&bsp_id, &storage_request_metadata.fingerprint); - - // Compute threshold for BSP to succeed. - let (to_succeed, _slope) = - Self::compute_threshold_to_succeed(&bsp_id, storage_request_metadata.requested_at)?; - - // Check that the BSP's threshold is under the threshold required to volunteer for the storage request. - ensure!(bsp_threshold <= to_succeed, Error::::AboveThreshold); - - // Add BSP to storage request metadata. - >::insert( - &file_key, - &bsp_id, - StorageRequestBspsMetadata:: { - confirmed: false, - _phantom: Default::default(), - }, - ); - - // Increment the number of bsps volunteered. - match storage_request_metadata - .bsps_volunteered - .checked_add(&ReplicationTargetType::::one()) - { - Some(inc_bsps_volunteered) => { - storage_request_metadata.bsps_volunteered = inc_bsps_volunteered; - } - None => { - return Err(ArithmeticError::Overflow.into()); - } - } - - // Update storage request metadata. - >::set(&file_key, Some(storage_request_metadata.clone())); - - let multiaddresses = T::Providers::get_bsp_multiaddresses(&bsp_id)?; - - Ok((bsp_id, multiaddresses, storage_request_metadata)) - } - - /// Confirm storing a file. - /// - /// *Callable only by BSP accounts* - /// - /// This function can only be called after a BSP has volunteered for the storage request. The BSP must provide a merkle proof of the file - /// and a proof of inclusion of the `file_key` in their merkle patricia trie. - /// - /// If the proof is valid, the root of the BSP is updated to reflect the new root of the merkle patricia trie and the number of `bsps_confirmed` is - /// incremented. If the number of `bsps_confirmed` reaches the number of `bsps_required`, the storage request is deleted. Finally, the BSP's data - /// used is incremented by the size of the file. - pub(crate) fn do_bsp_confirm_storing( - sender: T::AccountId, - non_inclusion_forest_proof: ForestProof, - file_keys_and_proofs: BoundedVec< - (MerkleHash, KeyProof), - T::MaxBatchConfirmStorageRequests, - >, - ) -> DispatchResult { - let bsp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotABsp)?; - - // Check that the provider is indeed a BSP. - ensure!( - ::is_bsp(&bsp_id), - Error::::NotABsp - ); - - let file_keys = file_keys_and_proofs - .iter() - .map(|(fk, _)| *fk) - .collect::>(); - - // Verify the proof of non-inclusion. - let proven_keys: BTreeSet> = - ::verify_forest_proof( - &bsp_id, - file_keys.as_slice(), - &non_inclusion_forest_proof, - )?; - - // Create a queue to store the file keys and metadata to be processed. - let mut file_keys_and_metadatas: BoundedVec< - (MerkleHash, Vec), - T::MaxBatchConfirmStorageRequests, - > = BoundedVec::new(); - - let mut seen_keys = BTreeSet::new(); - for file_key in file_keys_and_proofs.iter() { - // Skip any duplicates. - if !seen_keys.insert(file_key.0) { - continue; - } - - // Check that the storage request exists. - let mut storage_request_metadata = - >::get(&file_key.0).ok_or(Error::::StorageRequestNotFound)?; - - if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required { - // Since BSPs need to race one another to confirm storage requests, it is entirely possible that a BSP confirms a storage request - // after the storage request has been fulfilled within the same block. - // TODO: Accumulate a list of storage requests that have been skipped for the BSP to confirm what it can. - continue; - } - - expect_or_err!( - storage_request_metadata.bsps_confirmed < storage_request_metadata.bsps_required, - "Storage request should never have confirmed bsps equal to or greater than required bsps, since they are deleted when it is reached.", - Error::::StorageRequestBspsRequiredFulfilled, - bool - ); - - // Check that the BSP has volunteered for the storage request. - ensure!( - >::contains_key(&file_key.0, &bsp_id), - Error::::BspNotVolunteered - ); - - let requests = expect_or_err!( - >::get(&file_key.0, &bsp_id), - "BSP should exist since we checked it above", - Error::::ImpossibleFailedToGetValue - ); - - // Check that the storage provider has not already confirmed storing the file. - ensure!(!requests.confirmed, Error::::BspAlreadyConfirmed); - - let available_capacity = - ::available_capacity(&bsp_id); - - // Check if the BSP has enough capacity to store the file. - ensure!( - available_capacity > storage_request_metadata.size, - Error::::InsufficientAvailableCapacity - ); - - // Increment the number of bsps confirmed. - match storage_request_metadata - .bsps_confirmed - .checked_add(&ReplicationTargetType::::one()) - { - Some(inc_bsps_confirmed) => { - storage_request_metadata.bsps_confirmed = inc_bsps_confirmed; - } - None => { - return Err(ArithmeticError::Overflow.into()); - } - } - - // Ensure that the file key IS NOT part of the BSP's forest. - // Note: The runtime is responsible for adding and removing keys, computing the new root and updating the BSP's root. - ensure!( - !proven_keys.contains(&file_key.0), - Error::::ExpectedNonInclusionProof - ); - - let chunk_challenges = Self::generate_chunk_challenges_on_sp_confirm( - bsp_id, - file_key.0, - &storage_request_metadata, - ); - - // Check that the key proof is valid. - ::verify_key_proof( - &file_key.0, - &chunk_challenges, - &file_key.1, - )?; - - // Add data to storage provider. - ::increase_capacity_used( - &bsp_id, - storage_request_metadata.size, - )?; - - // Check if a payment stream between the user and provider already exists. - // If it does not, create it. If it does, update it. - match ::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &storage_request_metadata.owner) { - Some(previous_amount_provided) => { - // Update the payment stream. - ::update_dynamic_rate_payment_stream( - &bsp_id, - &storage_request_metadata.owner, - &(previous_amount_provided + storage_request_metadata.size), - )?; - }, - None => { - // Create the payment stream. - ::create_dynamic_rate_payment_stream( - &bsp_id, - &storage_request_metadata.owner, - &storage_request_metadata.size, - )?; - } - } - - // Get the file metadata to insert into the Provider's trie under the file key. - let file_metadata = storage_request_metadata.clone().to_file_metadata(); - let encoded_trie_value = file_metadata.encode(); - expect_or_err!( - file_keys_and_metadatas.try_push((file_key.0, encoded_trie_value)), - "Failed to push file key and metadata", - Error::::FileMetadataProcessingQueueFull, - result - ); - - // Remove storage request if we reached the required number of bsps and the MSP has confirmed storing the file. - if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required - && storage_request_metadata - .msp - .map(|(_, confirmed)| confirmed) - .unwrap_or(true) - { - // Remove storage request metadata. - >::remove(&file_key.0); - >::remove( - &storage_request_metadata.bucket_id, - &file_key.0, - ); - - // Remove storage request bsps - let removed = - >::drain_prefix(&file_key.0).fold(0, |acc, _| acc + 1); - - // Make sure that the expected number of bsps were removed. - expect_or_err!( - storage_request_metadata.bsps_volunteered == removed.into(), - "Number of volunteered bsps for storage request should have been removed", - Error::::UnexpectedNumberOfRemovedVolunteeredBsps, - bool - ); - - // Notify that the storage request has been fulfilled. - Self::deposit_event(Event::StorageRequestFulfilled { - file_key: file_key.0, - }); - } else { - // Update storage request metadata. - >::set(&file_key.0, Some(storage_request_metadata.clone())); - - // Update bsp for storage request. - >::mutate(&file_key.0, &bsp_id, |bsp| { - if let Some(bsp) = bsp { - bsp.confirmed = true; - } - }); - } - } - - // Check if this is the first file added to the BSP's Forest. If so, initialise last tick proven by this BSP. - let old_root = expect_or_err!( - ::get_root(bsp_id), - "Failed to get root for BSP, when it was already checked to be a BSP", - Error::::NotABsp - ); - - if old_root == ::get_default_root() { - // This means that this is the first file added to the BSP's Forest. - ::initialise_challenge_cycle( - &bsp_id, - )?; - - // Emit the corresponding event. - Self::deposit_event(Event::::BspChallengeCycleInitialised { - who: sender.clone(), - bsp_id, - }); - } - - // Compute new root after inserting new file keys in forest partial trie. - let new_root = ::apply_delta( - &bsp_id, - file_keys_and_metadatas - .iter() - .map(|(fk, metadata)| (*fk, TrieAddMutation::new(metadata.clone()).into())) - .collect::>() - .as_slice(), - &non_inclusion_forest_proof, - )?; - - // Update root of BSP. - ::update_root(bsp_id, new_root)?; - - // Emit event. - Self::deposit_event(Event::BspConfirmedStoring { - who: sender, - bsp_id, - file_keys: file_keys.to_vec().try_into().unwrap(), - new_root, - }); - - Ok(()) - } - - /// Revoke a storage request. - /// - /// *Callable by the owner of the storage request* - pub(crate) fn do_revoke_storage_request( - sender: T::AccountId, - file_key: MerkleHash, - ) -> DispatchResult { - // Check that the storage request exists. - ensure!( - >::contains_key(&file_key), - Error::::StorageRequestNotFound - ); - - // Get storage request metadata. - let storage_request_metadata = expect_or_err!( - >::get(&file_key), - "Storage request should exist", - Error::::StorageRequestNotFound - ); - - // Check that the sender is the owner of the storage request. - ensure!( - storage_request_metadata.owner == sender, - Error::::StorageRequestNotAuthorized - ); - - Self::cleanup_storage_request( - EitherAccountIdOrMspId::AccountId(sender), - file_key, - &storage_request_metadata, - )?; - - Ok(()) - } - - /// When a storage request is revoked and has already been confirmed by some BSPs, a challenge (with priority) is - /// issued to force the BSPs to update their storage root to uninclude the file from their storage. - /// - /// All BSPs that have volunteered to store the file are removed from the storage request and the storage request is deleted. - fn cleanup_storage_request( - revoker: EitherAccountIdOrMspId, - file_key: MerkleHash, - storage_request_metadata: &StorageRequestMetadata, - ) -> DispatchResult { - // Check if there are already BSPs who have confirmed to store the file. - if storage_request_metadata.bsps_confirmed >= ReplicationTargetType::::one() { - // Apply Remove mutation of the file key to the BSPs that have confirmed storing the file (proofs of inclusion). - ::challenge_with_priority( - &file_key, - Some(TrieRemoveMutation), - )?; - - // Emit event. - Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { - issuer: revoker, - file_key, - }); - } - - // Remove storage request bsps - let removed = >::drain_prefix(&file_key).fold(0, |acc, _| acc + 1); - - // Make sure that the expected number of bsps were removed. - expect_or_err!( - storage_request_metadata.bsps_volunteered == removed.into(), - "Number of volunteered bsps for storage request should have been removed", - Error::::UnexpectedNumberOfRemovedVolunteeredBsps, - bool - ); - - // Remove storage request. - >::remove(&file_key); - - // A revoked storage request is not considered active anymore. - >::remove(&storage_request_metadata.bucket_id, &file_key); - - Ok(()) - } - - /// BSP stops storing a file. - /// - /// *Callable only by BSP accounts* - /// - /// This function covers a few scenarios in which a BSP invokes this function to stop storing a file: - /// - /// 1. The BSP has volunteered and confirmed storing the file and wants to stop storing it while the storage request is still open. - /// - /// > In this case, the BSP has volunteered and confirmed storing the file for an existing storage request. - /// Therefore, we decrement the `bsps_confirmed` by 1. - /// - /// 2. The BSP stops storing a file that has an opened storage request but is not a volunteer. - /// - /// > In this case, the storage request was probably created by another BSP for some reason (e.g. that BSP lost the file) - /// and the current BSP is not a volunteer for this since it is already storing it. But since they to have stopped storing it, - /// we increment the `bsps_required` by 1. - /// - /// 3. The BSP stops storing a file that no longer has an opened storage request. - /// - /// > In this case, there is no storage request opened for the file they no longer are storing. Therefore, we - /// create a storage request with `bsps_required` set to 1. - /// - /// *This function does not give BSPs the possibility to remove themselves from being a __volunteer__ of a storage request.* - /// - /// A proof of inclusion is required to record the new root of the BSPs merkle patricia trie. First we validate the proof - /// and ensure the `file_key` is indeed part of the merkle patricia trie. Then finally compute the new merkle patricia trie root - /// by removing the `file_key` and update the root of the BSP. - /// - /// `can_serve`: A flag that indicates if the BSP can serve the file to other BSPs. If the BSP can serve the file, then - /// they are added to the storage request as a data server. - pub(crate) fn do_bsp_request_stop_storing( - sender: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - location: FileLocation, - owner: T::AccountId, - fingerprint: Fingerprint, - size: StorageData, - can_serve: bool, - inclusion_forest_proof: ForestProof, - ) -> Result, DispatchError> { - let bsp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotABsp)?; - - // Check that the provider is indeed a BSP. - ensure!( - ::is_bsp(&bsp_id), - Error::::NotABsp - ); - - // TODO: charge SP for this action. - - // Compute the file key hash. - let computed_file_key = Self::compute_file_key( - owner.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Check that the metadata corresponds to the expected file key. - ensure!( - file_key == computed_file_key, - Error::::InvalidFileKeyMetadata - ); - - // Check that a pending stop storing request for that BSP and file does not exist yet. - ensure!( - !>::contains_key(&bsp_id, &file_key), - Error::::PendingStopStoringRequestAlreadyExists - ); - - // Verify the proof of inclusion. - let proven_keys = - ::verify_forest_proof( - &bsp_id, - &[file_key], - &inclusion_forest_proof, - )?; - - // Ensure that the file key IS part of the BSP's forest. - ensure!( - proven_keys.contains(&file_key), - Error::::ExpectedInclusionProof - ); - - match >::get(&file_key) { - Some(mut storage_request_metadata) => { - match >::get(&file_key, &bsp_id) { - // We hit scenario 1. The BSP is a volunteer and has confirmed storing the file. - // We need to decrement the number of bsps confirmed and volunteered and remove the BSP from the storage request. - Some(bsp) => { - expect_or_err!( - bsp.confirmed, - "BSP should have confirmed storing the file since we verify the proof and their root matches the one in storage", - Error::::BspNotConfirmed, - bool - ); - - storage_request_metadata.bsps_confirmed = storage_request_metadata - .bsps_confirmed - .saturating_sub(ReplicationTargetType::::one()); - - storage_request_metadata.bsps_volunteered = storage_request_metadata - .bsps_volunteered - .saturating_sub(ReplicationTargetType::::one()); - - >::remove(&file_key, &bsp_id); - } - // We hit scenario 2. There is an open storage request but the BSP is not a volunteer. - // We need to increment the number of bsps required. - None => { - storage_request_metadata.bsps_required = storage_request_metadata - .bsps_required - .saturating_add(ReplicationTargetType::::one()); - } - } - - // Update storage request metadata. - >::set(&file_key, Some(storage_request_metadata)); - } - // We hit scenario 3. There is no storage request opened for the file. - // We need to create a new storage request with a single bsp required. - None => { - Self::do_request_storage( - owner, - bucket_id, - location.clone(), - fingerprint, - size, - None, - Some(ReplicationTargetType::::one()), - None, - if can_serve { - BoundedVec::try_from(vec![bsp_id]).unwrap() - } else { - BoundedVec::default() - }, - )?; - } - }; - - // Add the pending stop storing request to storage. - >::insert( - &bsp_id, - &file_key, - (frame_system::Pallet::::block_number(), size), - ); - - Ok(bsp_id) - } - - pub(crate) fn do_bsp_confirm_stop_storing( - sender: T::AccountId, - file_key: MerkleHash, - inclusion_forest_proof: ForestProof, - ) -> Result<(ProviderIdFor, MerkleHash), DispatchError> { - // Get the BSP ID of the sender - let bsp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotABsp)?; - - // Get the block when the pending stop storing request of the BSP for the file key was opened. - let (block_when_opened, file_size) = - >::get(&bsp_id, &file_key) - .ok_or(Error::::PendingStopStoringRequestNotFound)?; - - // Check that enough time has passed since the pending stop storing request was opened. - ensure!( - frame_system::Pallet::::block_number() - >= block_when_opened + T::MinWaitForStopStoring::get(), - Error::::MinWaitForStopStoringNotReached - ); - - // Verify the proof of inclusion. - let proven_keys = - ::verify_forest_proof( - &bsp_id, - &[file_key], - &inclusion_forest_proof, - )?; - - // Ensure that the file key IS part of the BSP's forest. - // The runtime is responsible for adding and removing keys, computing the new root and updating the BSP's root. - ensure!( - proven_keys.contains(&file_key), - Error::::ExpectedInclusionProof - ); - - // Compute new root after removing file key from forest partial trie. - let new_root = ::apply_delta( - &bsp_id, - &[(file_key, TrieRemoveMutation::default().into())], - &inclusion_forest_proof, - )?; - - // Update root of BSP. - ::update_root(bsp_id, new_root)?; - - // Decrease data used by the BSP. - ::decrease_capacity_used( - &bsp_id, file_size, - )?; - - // Remove the pending stop storing request from storage. - >::remove(&bsp_id, &file_key); - - Ok((bsp_id, new_root)) - } - - /// SP stops storing a file from a User that has been flagged as insolvent. - /// - /// *Callable only by SP accounts* - /// - /// A proof of inclusion is required to record the new root of the SPs merkle patricia trie. First we validate the proof - /// and ensure the `file_key` is indeed part of the merkle patricia trie. Then finally compute the new merkle patricia trie root - /// by removing the `file_key` and update the root of the SP. - pub(crate) fn do_sp_stop_storing_for_insolvent_user( - sender: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - location: FileLocation, - owner: T::AccountId, - fingerprint: Fingerprint, - size: StorageData, - inclusion_forest_proof: ForestProof, - ) -> Result<(ProviderIdFor, MerkleHash), DispatchError> { - // Get the SP ID - let sp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotASp)?; - - // Check that the owner of the file has been flagged as insolvent OR that the Provider does not - // have any active payment streams with the user. The rationale here is that if there is a - // user who cannot pay, or is just not paying anymore, the SP has the right to stop storing files for them - // without having to pay any penalty. - ensure!( - ::is_user_insolvent(&owner) - || !::has_active_payment_stream( - &sp_id, &owner - ), - Error::::UserNotInsolvent - ); - - // Compute the file key hash. - let computed_file_key = Self::compute_file_key( - owner.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Check that the metadata corresponds to the expected file key. - ensure!( - file_key == computed_file_key, - Error::::InvalidFileKeyMetadata - ); - - // Verify the proof of inclusion. - // If the Provider is a BSP, the proof is verified against the BSP's forest. - let new_root = if ::is_bsp(&sp_id) { - let proven_keys = - ::verify_forest_proof( - &sp_id, - &[file_key], - &inclusion_forest_proof, - )?; - - // Ensure that the file key IS part of the BSP's forest. - ensure!( - proven_keys.contains(&file_key), - Error::::ExpectedInclusionProof - ); - - // Compute new root after removing file key from forest partial trie. - let new_root = ::apply_delta( - &sp_id, - &[(file_key, TrieRemoveMutation::default().into())], - &inclusion_forest_proof, - )?; - - // Update root of the BSP. - ::update_root(sp_id, new_root)?; - - new_root - } else { - // If the Provider is a MSP, the proof is verified against the Bucket's root. - - // Check that the Bucket is stored by the MSP - ensure!( - ::is_bucket_stored_by_msp( - &sp_id, &bucket_id - ), - Error::::MspNotStoringBucket - ); - - // Decrease size of the bucket. - ::decrease_bucket_size(&bucket_id, size)?; - - // Get the Bucket's root - let bucket_root = - ::get_root_bucket(&bucket_id) - .ok_or(Error::::BucketNotFound)?; - - let proven_keys = - ::verify_generic_forest_proof( - &bucket_root, - &[file_key], - &inclusion_forest_proof, - )?; - - // Ensure that the file key IS part of the Bucket's trie. - ensure!( - proven_keys.contains(&file_key), - Error::::ExpectedInclusionProof - ); - - // Compute new root after removing file key from forest partial trie. - let new_root = - ::generic_apply_delta( - &bucket_root, - &[(file_key, TrieRemoveMutation::default().into())], - &inclusion_forest_proof, - )?; - - // Update root of the Bucket. - ::change_root_bucket( - bucket_id, new_root, - )?; - - new_root - }; - - // Decrease data used by the SP. - ::decrease_capacity_used(&sp_id, size)?; - - Ok((sp_id, new_root)) - } - - pub(crate) fn do_delete_file( - sender: T::AccountId, - bucket_id: BucketIdFor, - file_key: MerkleHash, - location: FileLocation, - fingerprint: Fingerprint, - size: StorageData, - maybe_inclusion_forest_proof: Option>, - ) -> Result<(bool, ProviderIdFor), DispatchError> { - // Compute the file key hash. - let computed_file_key = Self::compute_file_key( - sender.clone(), - bucket_id, - location.clone(), - size, - fingerprint, - ); - - // Check that the metadata corresponds to the expected file key. - ensure!( - file_key == computed_file_key, - Error::::InvalidFileKeyMetadata - ); - - // Check if sender is the owner of the bucket. - ensure!( - ::is_bucket_owner(&sender, &bucket_id)?, - Error::::NotBucketOwner - ); - - let msp_id = ::get_msp_of_bucket(&bucket_id) - .ok_or(Error::::BucketNotFound)?; - - let file_key_included = match maybe_inclusion_forest_proof { - // If the user did not supply a proof of inclusion, queue a pending deletion file request. - // This will leave a window of time for the MSP to provide the proof of (non-)inclusion. - // If the proof is not provided within the TTL, the hook will queue a priority challenge to remove the file key from all the providers. - None => { - let pending_file_deletion_requests = >::get(&sender); - - // Check if the file key is already in the pending deletion requests. - ensure!( - !pending_file_deletion_requests.contains(&(file_key, bucket_id)), - Error::::FileKeyAlreadyPendingDeletion - ); - - // Add the file key to the pending deletion requests. - PendingFileDeletionRequests::::try_append(&sender, (file_key, bucket_id)) - .map_err(|_| Error::::MaxUserPendingDeletionRequestsReached)?; - - // Queue the expiration item. - let expiration_item = - ExpirationItem::PendingFileDeletionRequests((sender, file_key)); - Self::enqueue_expiration_item(expiration_item)?; - - false - } - // If the user supplied a proof of inclusion, verify the proof and queue a priority challenge to remove the file key from all the providers. - Some(inclusion_forest_proof) => { - // Get the root of the bucket. - let bucket_root = - ::get_root_bucket(&bucket_id) - .ok_or(Error::::BucketNotFound)?; - - // Verify the proof of inclusion. - let proven_keys = - ::verify_generic_forest_proof( - &bucket_root, - &[file_key], - &inclusion_forest_proof, - )?; - - // Ensure that the file key IS part of the owner's forest. - ensure!( - proven_keys.contains(&file_key), - Error::::ExpectedInclusionProof - ); - - // Initiate the priority challenge to remove the file key from all the providers. - ::challenge_with_priority( - &file_key, - Some(TrieRemoveMutation), - )?; - - // Decrease size of the bucket. - ::decrease_bucket_size(&bucket_id, size)?; - - // Emit event. - Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { - issuer: EitherAccountIdOrMspId::::AccountId(sender.clone()), - file_key, - }); - - true - } - }; - - Ok((file_key_included, msp_id)) - } - - pub(crate) fn do_pending_file_deletion_request_submit_proof( - sender: T::AccountId, - user: T::AccountId, - file_key: MerkleHash, - bucket_id: BucketIdFor, - forest_proof: ForestProof, - ) -> Result<(bool, ProviderIdFor), DispatchError> { - let msp_id = - ::get_provider_id(sender.clone()) - .ok_or(Error::::NotAMsp)?; - - // Check that the provider is indeed an MSP. - ensure!( - ::is_msp(&msp_id), - Error::::NotAMsp - ); - - ensure!( - ::is_bucket_stored_by_msp(&msp_id, &bucket_id), - Error::::MspNotStoringBucket - ); - - let pending_file_deletion_requests = >::get(&user); - - // Check if the file key is in the pending deletion requests. - ensure!( - pending_file_deletion_requests.contains(&(file_key, bucket_id)), - Error::::FileKeyNotPendingDeletion - ); - - // Get the root of the bucket. - let bucket_root = - ::get_root_bucket(&bucket_id) - .ok_or(Error::::BucketNotFound)?; - - // Verify the proof of inclusion. - let proven_keys = - ::verify_generic_forest_proof( - &bucket_root, - &[file_key], - &forest_proof, - )?; - - let file_key_included = proven_keys.contains(&file_key); - - if file_key_included { - // Initiate the priority challenge to remove the file key from all the providers. - ::challenge_with_priority( - &file_key, - Some(TrieRemoveMutation), - )?; - - // Emit event. - Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { - issuer: EitherAccountIdOrMspId::::MspId(msp_id), - file_key, - }); - } - - // Delete the pending deletion request. - >::mutate(&user, |requests| { - requests.retain(|(key, _)| key != &file_key); - }); - - Ok((file_key_included, msp_id)) - } - - /// Create a collection. - fn create_collection(owner: T::AccountId) -> Result, DispatchError> { - // TODO: Parametrize the collection settings. - let config: CollectionConfigFor = CollectionConfig { - settings: CollectionSettings::all_enabled(), - max_supply: None, - mint_settings: MintSettings { - mint_type: MintType::Issuer, - price: None, - start_block: None, - end_block: None, - default_item_settings: ItemSettings::all_enabled(), - }, - }; - - T::Nfts::create_collection(&owner, &owner, &config) - } - - /// Compute the next block number to insert an expiring item, and insert it in the corresponding expiration queue. - /// - /// This function attempts to insert a the expiration item at the next available block starting from - /// the current next available block. - pub(crate) fn enqueue_expiration_item( - expiration_item: ExpirationItem, - ) -> Result, DispatchError> { - let expiration_block = expiration_item.get_next_expiration_block(); - let new_expiration_block = expiration_item.try_append(expiration_block)?; - expiration_item.set_next_expiration_block(new_expiration_block); - - Ok(new_expiration_block) - } - - pub fn compute_file_key( - owner: T::AccountId, - bucket_id: BucketIdFor, - location: FileLocation, - size: StorageData, - fingerprint: Fingerprint, - ) -> MerkleHash { - shp_file_metadata::FileMetadata::< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - > { - owner: owner.encode(), - bucket_id: bucket_id.as_ref().to_vec(), - location: location.clone().to_vec(), - file_size: size.into(), - fingerprint: fingerprint.as_ref().into(), - } - .file_key::>() - } - - pub fn get_threshold_for_bsp_request( - bsp_id: &ProviderIdFor, - fingerprint: &Fingerprint, - ) -> T::ThresholdType { - // Concatenate the BSP ID and the fingerprint and hash them to get the volunteering hash. - let concatenated = sp_std::vec![bsp_id.encode(), fingerprint.encode()].concat(); - let volunteering_hash = - <::Hashing as Hash>::hash(concatenated.as_ref()); - - // Return the threshold needed for the BSP to be able to volunteer for the storage request. - T::HashToThresholdType::convert(volunteering_hash) - } - - /// Compute the threshold for a BSP to succeed. - /// - /// Succeeding this threshold is required for the BSP to be eligible to volunteer for a storage request. - /// The threshold is computed based on the global reputation weight and the BSP's reputation weight, giving - /// an advantage to BSPs with higher reputation weights. - /// - /// The formalized formulas are documented in the [README](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/file-system/README.md#volunteering-succeeding-threshold-checks). - pub fn compute_threshold_to_succeed( - bsp_id: &ProviderIdFor, - requested_at: TickNumber, - ) -> Result<(T::ThresholdType, T::ThresholdType), DispatchError> { - let maximum_threshold = T::ThresholdType::max_value(); - - let global_weight = - T::ThresholdType::from(T::Providers::get_global_bsps_reputation_weight()); - - if global_weight == T::ThresholdType::zero() { - return Err(Error::::NoGlobalReputationWeightSet.into()); - } - - // Global threshold starting point from which all BSPs begin their threshold slope. All BSPs start at this point - // with the starting reputation weight. - // - // The calculation is designed to achieve the following: - // - // 1. In a regular scenario, `maximum_threshold` would be very large, and you'd start bringing it down with - // `global_weight`, also a large number. That way, when you multiply it by the replication target, - // you should still be within the numerical domain. - // - // 2. If `global_weight` is low still (in the early days of the network), when multiplying with - // replication target, you'll get at most `u32::MAX` and then the threshold would be - // u32::MAX / 2 (still pretty high). - // - // 3. If maximum_threshold is very low (like sometimes set in tests), the division would saturate to 1, - // and then the threshold would be replication target / 2 (still very low). - let threshold_global_starting_point = maximum_threshold - .checked_div(&global_weight) - .unwrap_or(T::ThresholdType::one()) - .checked_mul(&ReplicationTarget::::get().into()).unwrap_or({ - log::warn!("Global starting point is beyond MaximumThreshold. Setting it to half of the MaximumThreshold."); - maximum_threshold - }) - .checked_div(&T::ThresholdType::from(2u32)) - .unwrap_or(T::ThresholdType::one()); - - // Get the BSP's reputation weight. - let bsp_weight = T::ThresholdType::from(T::Providers::get_bsp_reputation_weight(&bsp_id)?); - - // Actual BSP's threshold starting point, taking into account their reputation weight. - let threshold_weighted_starting_point = - bsp_weight.saturating_mul(threshold_global_starting_point); - - // Rate of increase from the weighted threshold starting point up to the maximum threshold within a tick range. - let base_slope = maximum_threshold - .saturating_sub(threshold_global_starting_point) - .checked_div(&T::ThresholdTypeToTickNumber::convert_back( - TickRangeToMaximumThreshold::::get(), - )) - .unwrap_or(T::ThresholdType::one()); - - let threshold_slope = base_slope - .checked_mul(&bsp_weight) - .unwrap_or(maximum_threshold); - - // Since checked_div only returns None on a result of zero, there is the case when the result is between 0 and 1 and rounds down to 0. - let threshold_slope = if threshold_slope.is_zero() { - T::ThresholdType::one() - } else { - threshold_slope - }; - - let current_tick_number = - ::get_current_tick(); - - // Get number of ticks since the storage request was issued. - let ticks_since_requested = current_tick_number.saturating_sub(requested_at); - let ticks_since_requested = - T::ThresholdTypeToTickNumber::convert_back(ticks_since_requested); - - let to_succeed = threshold_weighted_starting_point - .saturating_add(threshold_slope.saturating_mul(ticks_since_requested)); - - Ok((to_succeed, threshold_slope)) - } -} - -mod hooks { - use crate::{ - pallet, - types::MerkleHash, - utils::{BucketIdFor, EitherAccountIdOrMspId, ProviderIdFor}, - DataServersForMoveBucket, Event, FileDeletionRequestExpirations, - NextStartingBlockToCleanUp, Pallet, PendingFileDeletionRequests, PendingMoveBucketRequests, - ReplicationTarget, StorageRequestBsps, StorageRequestExpirations, StorageRequests, - }; - use crate::{MoveBucketRequestExpirations, PendingBucketsToMove}; - use frame_support::weights::Weight; - use frame_system::pallet_prelude::BlockNumberFor; - use shp_traits::TrieRemoveMutation; - use sp_runtime::traits::{Get, One, Zero}; - use sp_runtime::Saturating; - - impl Pallet { - pub(crate) fn do_on_idle( - current_block: BlockNumberFor, - mut remaining_weight: &mut Weight, - ) -> &mut Weight { - let db_weight = T::DbWeight::get(); - let mut block_to_clean = NextStartingBlockToCleanUp::::get(); - - while block_to_clean <= current_block && !remaining_weight.is_zero() { - Self::process_block_expired_items(block_to_clean, &mut remaining_weight); - - if remaining_weight.is_zero() { - break; - } - - block_to_clean.saturating_accrue(BlockNumberFor::::one()); - } - - // Update the next starting block for cleanup - if block_to_clean > NextStartingBlockToCleanUp::::get() { - NextStartingBlockToCleanUp::::put(block_to_clean); - remaining_weight.saturating_reduce(db_weight.writes(1)); - } - - remaining_weight - } - - fn process_block_expired_items(block: BlockNumberFor, remaining_weight: &mut Weight) { - let db_weight = T::DbWeight::get(); - let minimum_required_weight_processing_expired_items = db_weight.reads_writes(1, 1); - - if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { - return; - } - - // Remove expired storage requests if any existed and process them. - let mut expired_storage_requests = StorageRequestExpirations::::take(&block); - remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); - - // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to - // TODO: process all the expired storage requests. If not, we should return early. - while let Some(file_key) = expired_storage_requests.pop() { - Self::process_expired_storage_request(file_key, remaining_weight) - } - - // If there are remaining items which were not processed, put them back in storage - if !expired_storage_requests.is_empty() { - StorageRequestExpirations::::insert(&block, expired_storage_requests); - remaining_weight.saturating_reduce(db_weight.writes(1)); - } - - // Check if there is enough remaining weight to process the expired file deletion requests. - if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { - return; - } - - // Remove expired file deletion requests if any existed and process them. - let mut expired_file_deletion_requests = - FileDeletionRequestExpirations::::take(&block); - remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); - - // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to - // TODO: process all the expired file deletion requests. If not, we should return early. - while let Some((user, file_key)) = expired_file_deletion_requests.pop() { - Self::process_expired_pending_file_deletion(user, file_key, remaining_weight) - } - - // If there are remaining items which were not processed, put them back in storage - if !expired_file_deletion_requests.is_empty() { - FileDeletionRequestExpirations::::insert(&block, expired_file_deletion_requests); - remaining_weight.saturating_reduce(db_weight.writes(1)); - } - - // Check if there is enough remaining weight to process expired move bucket requests - if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { - return; - } - - // Remove expired move bucket requests if any existed and process them. - let mut expired_move_bucket_requests = MoveBucketRequestExpirations::::take(&block); - remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); - - // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to - // TODO: process all the expired move bucket requests. If not, we should return early. - while let Some((msp_id, bucket_id)) = expired_move_bucket_requests.pop() { - Self::process_expired_move_bucket_request(msp_id, bucket_id, remaining_weight); - } - - // If there are remaining items which were not processed, put them back in storage - if !expired_move_bucket_requests.is_empty() { - MoveBucketRequestExpirations::::insert(&block, expired_move_bucket_requests); - remaining_weight.saturating_reduce(db_weight.writes(1)); - } - } - - fn process_expired_storage_request(file_key: MerkleHash, remaining_weight: &mut Weight) { - let db_weight = T::DbWeight::get(); - - // As of right now, the upper bound limit to the number of BSPs required to fulfill a storage request is set by `ReplicationTarget`. - // We could increase this potential weight to account for potentially more volunteers. - let potential_weight = - db_weight.writes(ReplicationTarget::::get().saturating_plus_one().into()); - - if !remaining_weight.all_gte(potential_weight) { - return; - } - - // Remove storage request and all bsps that volunteered for it. - StorageRequests::::remove(&file_key); - let removed = - StorageRequestBsps::::drain_prefix(&file_key).fold(0, |acc, _| acc + 1u32); - - remaining_weight.saturating_reduce(db_weight.writes(1.saturating_add(removed.into()))); - - Self::deposit_event(Event::StorageRequestExpired { file_key }); - } - - fn process_expired_pending_file_deletion( - user: T::AccountId, - file_key: MerkleHash, - remaining_weight: &mut Weight, - ) { - let db_weight = T::DbWeight::get(); - let potential_weight = db_weight.reads_writes(2, 3); - - if !remaining_weight.all_gte(potential_weight) { - return; - } - - let requests = PendingFileDeletionRequests::::get(&user); - - // Check if the file key is still a pending deletion requests. - let expired_item_index = match requests.iter().position(|(key, _)| key == &file_key) { - Some(i) => i, - None => return, - }; - - // Remove the file key from the pending deletion requests. - PendingFileDeletionRequests::::mutate(&user, |requests| { - requests.remove(expired_item_index); - }); - - // Queue a priority challenge to remove the file key from all the providers. - let _ = ::challenge_with_priority( - &file_key, - Some(TrieRemoveMutation), - ) - .map_err(|_| { - Self::deposit_event(Event::FailedToQueuePriorityChallenge { - user: user.clone(), - file_key, - }); - }); - // Emit event. - Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { - issuer: EitherAccountIdOrMspId::::AccountId(user), - file_key, - }); - - remaining_weight.saturating_reduce(potential_weight); - } - - fn process_expired_move_bucket_request( - msp_id: ProviderIdFor, - bucket_id: BucketIdFor, - remaining_weight: &mut Weight, - ) { - let db_weight = T::DbWeight::get(); - let potential_weight = db_weight.reads_writes(0, 2); - - if !remaining_weight.all_gte(potential_weight) { - return; - } - - PendingMoveBucketRequests::::remove(&msp_id, &bucket_id); - PendingBucketsToMove::::remove(&bucket_id); - DataServersForMoveBucket::::drain_prefix(&bucket_id); - - remaining_weight.saturating_reduce(potential_weight); - - Self::deposit_event(Event::MoveBucketRequestExpired { msp_id, bucket_id }); - } - } -} +use codec::Encode; +use frame_support::{ + ensure, + pallet_prelude::DispatchResult, + traits::{nonfungibles_v2::Create, Get}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use num_bigint::BigUint; +use sp_runtime::{ + traits::{ + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Convert, ConvertBack, Hash, One, + Saturating, Zero, + }, + ArithmeticError, BoundedVec, DispatchError, +}; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + vec, + vec::Vec, +}; + +use pallet_file_system_runtime_api::{ + QueryBspConfirmChunksToProveForFileError, QueryConfirmChunksToProveForFileError, + QueryFileEarliestVolunteerTickError, QueryMspConfirmChunksToProveForFileError, +}; +use pallet_nfts::{CollectionConfig, CollectionSettings, ItemSettings, MintSettings, MintType}; +use shp_file_metadata::ChunkId; +use shp_traits::{ + MutateBucketsInterface, MutateStorageProvidersInterface, PaymentStreamsInterface, + ReadBucketsInterface, ReadProvidersInterface, ReadStorageProvidersInterface, + ReadUserSolvencyInterface, TrieAddMutation, TrieRemoveMutation, +}; + +use crate::types::AcceptedStorageRequestParameters; +use crate::{ + pallet, + types::{ + BatchResponses, BucketIdFor, BucketMoveRequestResponse, BucketNameFor, CollectionConfigFor, + CollectionIdFor, EitherAccountIdOrMspId, ExpirationItem, FileKeyHasher, + FileKeyResponsesInput, FileLocation, Fingerprint, ForestProof, KeyProof, + MaxBatchMspRespondStorageRequests, MaxBspsPerStorageRequest, MerkleHash, + MoveBucketRequestMetadata, MspAcceptedBatchStorageRequests, MspFailedBatchStorageRequests, + MspRejectedBatchStorageRequests, MspRespondStorageRequestsResult, MultiAddresses, PeerIds, + ProviderIdFor, RejectedStorageRequestReason, ReplicationTargetType, StorageData, + StorageRequestBspsMetadata, StorageRequestMetadata, TickNumber, + }, + BucketsWithStorageRequests, DataServersForMoveBucket, Error, Event, Pallet, + PendingBucketsToMove, PendingFileDeletionRequests, PendingMoveBucketRequests, + PendingStopStoringRequests, ReplicationTarget, StorageRequestBsps, StorageRequests, + TickRangeToMaximumThreshold, +}; + +macro_rules! expect_or_err { + // Handle Option type + ($optional:expr, $error_msg:expr, $error_type:path) => {{ + match $optional { + Some(value) => value, + None => { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + } + }}; + // Handle boolean type + ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ + if !$condition { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + }}; + // Handle Result type + ($result:expr, $error_msg:expr, $error_type:path, result) => {{ + match $result { + Ok(value) => value, + Err(_) => { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + } + }}; +} + +impl Pallet +where + T: pallet::Config, +{ + /// Compute the tick number at which the BSP is eligible to volunteer for a storage request. + pub fn query_earliest_file_volunteer_tick( + bsp_id: ProviderIdFor, + file_key: MerkleHash, + ) -> Result, QueryFileEarliestVolunteerTickError> + where + T: frame_system::Config, + { + // Get the tick number at which the storage request was created. + let (storage_request_tick, fingerprint) = match >::get(&file_key) { + Some(storage_request) => (storage_request.requested_at, storage_request.fingerprint), + None => { + return Err(QueryFileEarliestVolunteerTickError::StorageRequestNotFound); + } + }; + + // Get the threshold needed for the BSP to be able to volunteer for the storage request. + let bsp_threshold = Self::get_threshold_for_bsp_request(&bsp_id, &fingerprint); + + // Compute the tick number at which the BSP should send the volunteer request. + Self::compute_volunteer_tick_number(bsp_id, bsp_threshold, storage_request_tick) + .map_err(|_| QueryFileEarliestVolunteerTickError::ThresholdArithmeticError) + } + + fn compute_volunteer_tick_number( + bsp_id: ProviderIdFor, + bsp_threshold: T::ThresholdType, + storage_request_tick: TickNumber, + ) -> Result, DispatchError> + where + T: frame_system::Config, + { + // Compute the threshold to succeed and the slope of the bsp. + let (to_succeed, slope) = + Self::compute_threshold_to_succeed(&bsp_id, storage_request_tick)?; + + let threshold_diff = match bsp_threshold.checked_sub(&to_succeed) { + Some(diff) => diff, + None => { + // The BSP's threshold is less than the current threshold. + let current_tick = + ::get_current_tick(); + return Ok(current_tick); + } + }; + + // Calculate the number of ticks required to be below the threshold. + let ticks_to_wait = match threshold_diff.checked_div(&slope) { + Some(ticks) => ticks, + None => { + return Err(Error::::ThresholdArithmeticError.into()); + } + }; + + // Compute the tick number at which the BSP should send the volunteer request. + let volunteer_tick_number = storage_request_tick + .saturating_add(T::ThresholdTypeToTickNumber::convert(ticks_to_wait)); + + Ok(volunteer_tick_number) + } + + pub fn query_bsp_confirm_chunks_to_prove_for_file( + bsp_id: ProviderIdFor, + file_key: MerkleHash, + ) -> Result, QueryBspConfirmChunksToProveForFileError> { + // Get the storage request metadata. + let storage_request_metadata = match >::get(&file_key) { + Some(storage_request) => storage_request, + None => { + return Err(QueryBspConfirmChunksToProveForFileError::StorageRequestNotFound); + } + }; + + Self::query_confirm_chunks_to_prove_for_file(bsp_id, storage_request_metadata, file_key) + .map_err(|e| QueryBspConfirmChunksToProveForFileError::ConfirmChunks(e)) + } + + pub fn query_msp_confirm_chunks_to_prove_for_file( + msp_id: ProviderIdFor, + file_key: MerkleHash, + ) -> Result, QueryMspConfirmChunksToProveForFileError> { + // Get the storage request metadata. + let storage_request_metadata = match >::get(&file_key) { + Some(storage_request) => storage_request, + None => { + return Err(QueryMspConfirmChunksToProveForFileError::StorageRequestNotFound); + } + }; + + Self::query_confirm_chunks_to_prove_for_file(msp_id, storage_request_metadata, file_key) + .map_err(|e| QueryMspConfirmChunksToProveForFileError::ConfirmChunks(e)) + } + + fn query_confirm_chunks_to_prove_for_file( + provider_id: ProviderIdFor, + storage_request_metadata: StorageRequestMetadata, + file_key: MerkleHash, + ) -> Result, QueryConfirmChunksToProveForFileError> { + // Generate the list of chunks to prove. + let challenges = Self::generate_chunk_challenges_on_sp_confirm( + provider_id, + file_key, + &storage_request_metadata, + ); + + let chunks = storage_request_metadata.to_file_metadata().chunks_count(); + + let chunks_to_prove = challenges + .iter() + .map(|challenge| { + let challenged_chunk = BigUint::from_bytes_be(challenge.as_ref()) % chunks; + let challenged_chunk: ChunkId = + ChunkId::new(challenged_chunk.try_into().map_err(|_| { + QueryConfirmChunksToProveForFileError::ChallengedChunkToChunkIdError + })?); + + Ok(challenged_chunk) + }) + .collect::, _>>()?; + + Ok(chunks_to_prove) + } + + fn generate_chunk_challenges_on_sp_confirm( + sp_id: ProviderIdFor, + file_key: MerkleHash, + storage_request_metadata: &StorageRequestMetadata, + ) -> Vec<<::Providers as ReadProvidersInterface>::MerkleHash> { + let file_metadata = storage_request_metadata.clone().to_file_metadata(); + let chunks_to_check = file_metadata.chunks_to_check(); + + let mut challenges = + ::generate_challenges_from_seed( + T::MerkleHashToRandomnessOutput::convert(file_key), + &sp_id, + chunks_to_check - 1, + ); + + let last_chunk_id = file_metadata.last_chunk_id(); + + challenges.push(T::ChunkIdToMerkleHash::convert(last_chunk_id)); + + challenges + } + + /// Create a bucket for an owner (user) under a given MSP account. + pub(crate) fn do_create_bucket( + sender: T::AccountId, + msp_id: ProviderIdFor, + name: BucketNameFor, + private: bool, + ) -> Result<(BucketIdFor, Option>), DispatchError> { + // TODO: Hold user funds for the bucket creation. + + // Check if the MSP is indeed an MSP. + ensure!( + ::is_msp(&msp_id), + Error::::NotAMsp + ); + + // Create collection only if bucket is private + let maybe_collection_id = if private { + // The `owner` of the collection is also the admin of the collection since most operations require the sender to be the admin. + Some(Self::create_collection(sender.clone())?) + } else { + None + }; + + let bucket_id = ::Providers::derive_bucket_id(&msp_id, &sender, name); + + ::add_bucket( + msp_id, + sender, + bucket_id, + private, + maybe_collection_id.clone(), + )?; + + Ok((bucket_id, maybe_collection_id)) + } + + /// This does not guarantee that the MSP will have enough storage capacity to store the entire bucket. Therefore, + /// between the creation of the request and its expiration, the MSP can increase its capacity before accepting the request. + /// + /// Forcing the MSP to have enough capacity before the request is created would not enable MSPs to automatically scale based on demand. + pub(crate) fn do_request_move_bucket( + sender: T::AccountId, + bucket_id: BucketIdFor, + new_msp_id: ProviderIdFor, + ) -> Result<(), DispatchError> { + // Check if the sender is the owner of the bucket. + ensure!( + ::is_bucket_owner(&sender, &bucket_id)?, + Error::::NotBucketOwner + ); + + // Check if the new MSP is indeed an MSP. + ensure!( + ::is_msp(&new_msp_id), + Error::::NotAMsp + ); + + // Check if the bucket is already stored by the new MSP. + ensure!( + !::is_bucket_stored_by_msp( + &new_msp_id, + &bucket_id + ), + Error::::MspAlreadyStoringBucket + ); + + if >::contains_key(&bucket_id) { + return Err(Error::::BucketIsBeingMoved.into()); + } + + // Check if there are any open storage requests for the bucket. + // Do not allow any storage requests and move bucket requests to coexist for the same bucket. + ensure!( + !>::iter_prefix(bucket_id) + .next() + .is_some(), + Error::::StorageRequestExists + ); + + // Register the move bucket request. + >::insert( + &new_msp_id, + bucket_id, + MoveBucketRequestMetadata { + requester: sender.clone(), + }, + ); + >::insert(&bucket_id, ()); + + let expiration_item = ExpirationItem::MoveBucketRequest((new_msp_id, bucket_id)); + Self::enqueue_expiration_item(expiration_item)?; + + Ok(()) + } + + pub(crate) fn do_bsp_add_data_server_for_move_bucket_request( + sender: T::AccountId, + bucket_id: BucketIdFor, + ) -> Result, DispatchError> { + let bsp_id = ::get_provider_id(sender) + .ok_or(Error::::NotABsp)?; + + // Check if the sender is a Storage Provider. + ensure!( + ::is_bsp(&bsp_id), + Error::::NotABsp + ); + + // Check if the move bucket request exists. + ensure!( + >::contains_key(&bucket_id), + Error::::MoveBucketRequestNotFound, + ); + + // Check if the BSP is already a data server for the move bucket request. + ensure!( + !DataServersForMoveBucket::::contains_key(&bucket_id, &bsp_id), + Error::::BspAlreadyDataServer + ); + + // Add the data server to the move bucket request. + DataServersForMoveBucket::::insert(&bucket_id, &bsp_id, ()); + + Ok(bsp_id) + } + + pub(crate) fn do_msp_respond_move_bucket_request( + sender: T::AccountId, + bucket_id: BucketIdFor, + response: BucketMoveRequestResponse, + ) -> Result, DispatchError> { + let msp_id = ::get_provider_id(sender) + .ok_or(Error::::NotAMsp)?; + + // Check if the sender is the MSP. + ensure!( + ::is_msp(&msp_id), + Error::::NotAMsp + ); + + // Check if the move bucket request exists for MSP and bucket. + let move_bucket_requester = >::take(&msp_id, bucket_id); + ensure!( + move_bucket_requester.is_some(), + Error::::MoveBucketRequestNotFound + ); + + if response == BucketMoveRequestResponse::Rejected { + >::remove(&bucket_id); + >::remove(&msp_id, bucket_id); + + return Ok(msp_id); + } + + let previous_msp_id = ::get_msp_bucket(&bucket_id)?; + + // Decrease the used capacity of the current MSP. + let bucket_size = ::get_bucket_size(&bucket_id)?; + + // Check if MSP has enough available capacity to store the bucket. + ensure!( + ::available_capacity(&msp_id) + >= bucket_size, + Error::::InsufficientAvailableCapacity + ); + + // Change the MSP that stores the bucket. + ::change_msp_bucket(&bucket_id, &msp_id)?; + + // Decrease the used capacity of the previous MSP. + ::decrease_capacity_used( + &previous_msp_id, + bucket_size, + )?; + + // Increase the used capacity of the new MSP. + ::increase_capacity_used( + &msp_id, + bucket_size, + )?; + + >::remove(&bucket_id); + + Self::deposit_event(Event::MoveBucketAccepted { bucket_id, msp_id }); + + Ok(msp_id) + } + + /// Update the privacy of a bucket. + /// + /// This function allows the owner of a bucket to update its privacy setting. + /// If the bucket is set to private and no collection exists, + /// a new collection will be created. If the bucket is set to public and + /// an associated collection exists, the collection remains but the privacy setting is updated to public. + /// If the bucket has an associated collection, and it does not exist in storage, a new collection will be created. + pub(crate) fn do_update_bucket_privacy( + sender: T::AccountId, + bucket_id: BucketIdFor, + private: bool, + ) -> Result>, DispatchError> { + // Ensure the sender is the owner of the bucket. + ensure!( + T::Providers::is_bucket_owner(&sender, &bucket_id)?, + Error::::NotBucketOwner + ); + + // Retrieve the collection ID associated with the bucket, if any. + let maybe_collection_id = T::Providers::get_read_access_group_id_of_bucket(&bucket_id)?; + + // Determine the appropriate collection ID based on the new privacy setting. + let collection_id = match (private, maybe_collection_id) { + // Create a new collection if the bucket will be private and no collection exists. + (true, None) => { + Some(Self::do_create_and_associate_collection_with_bucket(sender.clone(), bucket_id)?) + } + // Handle case where the bucket has an existing collection. + (_, Some(current_collection_id)) + if !::collection_exists(¤t_collection_id) => + { + Some(Self::do_create_and_associate_collection_with_bucket(sender.clone(), bucket_id)?) + } + // Use the existing collection ID if it exists. + (_, Some(current_collection_id)) => Some(current_collection_id), + // No collection needed if the bucket is public and no collection exists. + (false, None) => None, + }; + + // Update the privacy setting of the bucket. + T::Providers::update_bucket_privacy(bucket_id, private)?; + + Ok(collection_id) + } + + /// Create and associate collection with a bucket. + /// + /// *Callable only by the owner of the bucket. The bucket must be private.* + /// + /// It is possible to have a bucket that is private but does not have a collection associated with it. This can happen if + /// a user destroys the collection associated with the bucket by calling the NFTs pallet directly. + /// + /// In any case, we will set a new collection the bucket even if there is an existing one associated with it. + pub(crate) fn do_create_and_associate_collection_with_bucket( + sender: T::AccountId, + bucket_id: BucketIdFor, + ) -> Result, DispatchError> { + // Check if sender is the owner of the bucket. + ensure!( + ::is_bucket_owner(&sender, &bucket_id)?, + Error::::NotBucketOwner + ); + + let collection_id = Self::create_collection(sender)?; + + ::update_bucket_read_access_group_id( + bucket_id, + Some(collection_id.clone()), + )?; + + Ok(collection_id) + } + + /// Request storage for a file. + /// + /// In the event that a storage request is created without any user multiaddresses (checkout `do_bsp_stop_storing`), + /// it is expected that storage providers that do have this file in storage already, will be able to send a + /// transaction to the chain to add themselves as a data server for the storage request. + pub(crate) fn do_request_storage( + sender: T::AccountId, + bucket_id: BucketIdFor, + location: FileLocation, + fingerprint: Fingerprint, + size: StorageData, + msp_id: Option>, + bsps_required: Option>, + user_peer_ids: Option>, + data_server_sps: BoundedVec, MaxBspsPerStorageRequest>, + ) -> Result, DispatchError> { + // TODO: Check user funds and lock them for the storage request. + + // Check that the file size is greater than zero. + ensure!(size > Zero::zero(), Error::::FileSizeCannotBeZero); + + // Check that a bucket under the received ID exists and that the sender is the owner of the bucket. + ensure!( + ::is_bucket_owner(&sender, &bucket_id)?, + Error::::NotBucketOwner + ); + + // Check that the bucket is not being moved. + // Do not allow any storage requests and move bucket requests to coexist for the same bucket. + ensure!( + !>::contains_key(&bucket_id), + Error::::BucketIsBeingMoved + ); + + // If a specific MSP ID is provided, check that it is a valid MSP and that it has enough available capacity to store the file. + let msp = if let Some(ref msp_id) = msp_id { + // Check that the received Provider ID corresponds to a valid MSP. + ensure!( + ::is_msp(msp_id), + Error::::NotAMsp + ); + + // Check that the MSP received is the one storing the bucket. + ensure!( + ::is_bucket_stored_by_msp(msp_id, &bucket_id), + Error::::MspNotStoringBucket + ); + + Some((*msp_id, false)) + } else { + None + }; + + let bsps_required = bsps_required.unwrap_or(ReplicationTarget::::get()); + + if bsps_required.is_zero() { + return Err(Error::::ReplicationTargetCannotBeZero)?; + } + + if bsps_required > ReplicationTarget::::get().into() { + return Err(Error::::BspsRequiredExceedsTarget)?; + } + + let current_tick = + ::get_current_tick(); + let storage_request_metadata = StorageRequestMetadata:: { + requested_at: current_tick, + owner: sender.clone(), + bucket_id, + location: location.clone(), + fingerprint, + size, + msp, + user_peer_ids: user_peer_ids.unwrap_or_default(), + data_server_sps, + bsps_required, + bsps_confirmed: ReplicationTargetType::::zero(), + bsps_volunteered: ReplicationTargetType::::zero(), + }; + + // Compute the file key used throughout this file's lifespan. + let file_key = Self::compute_file_key( + sender.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Check a storage request does not already exist for this file key. + ensure!( + !>::contains_key(&file_key), + Error::::StorageRequestAlreadyRegistered + ); + + // Register storage request. + >::insert(&file_key, storage_request_metadata); + + >::insert(&bucket_id, &file_key, ()); + + let expiration_item = ExpirationItem::StorageRequest(file_key); + Self::enqueue_expiration_item(expiration_item)?; + + Ok(file_key) + } + + /// Accepts or rejects batches of storage requests assumed to be grouped by bucket. + /// + /// This is using a best-effort strategy to process as many file keys as possible, returning + /// the ones that were accepted, rejected, or failed to be processed. + /// + /// File keys that are not part of the bucket they belong to will be skipped (failed). + /// + /// All file keys will be processed (unless there are duplicates, they are simply skipped) and any errors + /// while processing them will be marked as a failed key and continue processing the rest. It is up to the + /// caller to verify the final result and apply only the file keys that have been successfully accepted. + pub(crate) fn do_msp_respond_storage_request( + sender: T::AccountId, + file_key_responses_input: FileKeyResponsesInput, + ) -> Result, DispatchError> { + // Check that the sender is a Storage Provider and get its MSP ID + let msp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotASp)?; + + // Check that the sender is an MSP + ensure!( + ::is_msp(&msp_id), + Error::::NotAMsp + ); + + // Initialize batch responses + let mut batch_responses: BoundedVec< + BatchResponses, + MaxBatchMspRespondStorageRequests, + > = BoundedVec::default(); + + // Preliminary check to ensure that the MSP is the one storing each bucket in the responses + for (bucket_id, _) in file_key_responses_input.iter() { + ensure!( + ::is_bucket_stored_by_msp( + &msp_id, &bucket_id + ), + Error::::MspNotStoringBucket + ); + } + + // Process each bucket's responses + for (bucket_id, file_key_responses) in file_key_responses_input { + let mut failed: BoundedVec< + (MerkleHash, DispatchError), + MaxBatchMspRespondStorageRequests, + > = BoundedVec::default(); + + let owner = ::get_bucket_owner(&bucket_id) + .map_err(|_| Error::::BucketNotFound)?; + + if let Some(accepted_file_keys) = file_key_responses.accept { + // Call do_msp_accept_storage_request, which returns the new_bucket_root + let (new_bucket_root, accepted_file_keys, failed_file_keys) = + Self::do_msp_accept_storage_request(msp_id, bucket_id, accepted_file_keys)?; + + // Create batch responses + if !accepted_file_keys.is_empty() { + let accepted_batch = MspAcceptedBatchStorageRequests { + file_keys: accepted_file_keys, + bucket_id, + new_bucket_root, + owner: owner.clone(), + }; + + batch_responses + .try_push(BatchResponses::Accepted(accepted_batch)) + .map_err(|_| Error::::TooManyBatchResponses)?; + } + + if !failed_file_keys.is_empty() { + for rejected_file_key in failed_file_keys { + failed + .try_push(rejected_file_key) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + } + } + } + + if let Some(rejected_file_keys) = file_key_responses.reject { + let mut rejected: BoundedVec< + (MerkleHash, RejectedStorageRequestReason), + MaxBatchMspRespondStorageRequests, + > = BoundedVec::default(); + + for file_key in rejected_file_keys.iter() { + let storage_request_metadata = match >::get(&file_key.0) { + Some(metadata) => metadata, + None => { + failed + .try_push((file_key.0, Error::::StorageRequestNotFound.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + }; + + if let Err(e) = Self::cleanup_storage_request( + EitherAccountIdOrMspId::MspId(msp_id), + file_key.0, + &storage_request_metadata, + ) { + failed + .try_push((file_key.0, e)) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + rejected + .try_push((file_key.0, file_key.1.clone())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + } + + if !rejected.is_empty() { + let rejected_batch = MspRejectedBatchStorageRequests { + file_keys: rejected, + bucket_id, + owner: owner.clone(), + }; + + batch_responses + .try_push(BatchResponses::Rejected(rejected_batch)) + .map_err(|_| Error::::TooManyBatchResponses)?; + } + } + + if !failed.is_empty() { + let failed_batch = MspFailedBatchStorageRequests { + file_keys: failed, + bucket_id, + owner: owner.clone(), + }; + + batch_responses + .try_push(BatchResponses::Failed(failed_batch)) + .map_err(|_| Error::::TooManyBatchResponses)?; + } + } + + // Construct the result + let result = MspRespondStorageRequestsResult { + msp_id, + responses: batch_responses, + }; + + Ok(result) + } + + /// Accept as many storage requests as possible (best-effort) belonging to the same bucket. + /// + /// There should be a single non-inclusion forest proof for all file keys, and finally there should + /// be a list of file key(s) with a key proof for each of them. + /// + /// The implementation follows this sequence: + /// 1. Verify the non-inclusion proof. + /// 2. For each file key: Record a successful acceptance or a failure. Any failed operation while processing a file key + /// will not result in the function failing, but the file key will be marked as failed and the function will continue processing the rest. + /// 3. Apply the delta with all the keys that were successfully accepted to the root of the bucket. + fn do_msp_accept_storage_request( + msp_id: ProviderIdFor, + bucket_id: BucketIdFor, + accepted_file_keys: AcceptedStorageRequestParameters, + ) -> Result< + ( + MerkleHash, + BoundedVec, T::MaxBatchMspRespondStorageRequests>, + BoundedVec< + ( + ::MerkleHash, + DispatchError, + ), + T::MaxBatchMspRespondStorageRequests, + >, + ), + DispatchError, + > { + let file_keys = accepted_file_keys + .file_keys_and_proofs + .iter() + .map(|(fk, _)| *fk) + .collect::>(); + + // Get the Bucket's root + let bucket_root = + ::get_root_bucket(&bucket_id) + .ok_or(Error::::BucketNotFound)?; + + // Verify the proof of non-inclusion. + let proven_keys: BTreeSet> = + ::verify_generic_forest_proof( + &bucket_root, + file_keys.as_slice(), + &accepted_file_keys.non_inclusion_forest_proof, + )?; + + // Initialize accepted, rejected, and failed file keys + let mut accepted_file_keys_and_metadata = BTreeMap::new(); + let mut failed_file_keys: BoundedVec< + (MerkleHash, DispatchError), + T::MaxBatchMspRespondStorageRequests, + > = BoundedVec::default(); + + for (file_key, key_proof) in accepted_file_keys.file_keys_and_proofs { + // Skip any duplicates. + if accepted_file_keys_and_metadata.contains_key(&file_key) { + continue; + } + + let mut storage_request_metadata = match >::get(&file_key) { + Some(metadata) => metadata, + None => { + failed_file_keys + .try_push((file_key, Error::::StorageRequestNotFound.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + }; + + // Ensure that the file key IS NOT part of the bucket's forest. + if proven_keys.contains(&file_key) { + failed_file_keys + .try_push((file_key, Error::::ExpectedNonInclusionProof.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + if storage_request_metadata.bucket_id != bucket_id { + failed_file_keys + .try_push((file_key, Error::::InvalidBucketIdFileKeyPair.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + // Check that the MSP is the one storing the bucket. + if !::is_bucket_stored_by_msp( + &msp_id, + &storage_request_metadata.bucket_id, + ) { + failed_file_keys + .try_push((file_key, Error::::MspNotStoringBucket.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + // Check that the sender corresponds to the MSP in the storage request and that it hasn't yet confirmed storing the file. + match storage_request_metadata.msp { + Some((request_msp_id, confirm_status)) => { + if request_msp_id != msp_id { + failed_file_keys + .try_push((file_key, Error::::NotSelectedMsp.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + if confirm_status { + failed_file_keys + .try_push((file_key, Error::::MspAlreadyConfirmed.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + } + None => { + failed_file_keys + .try_push((file_key, Error::::RequestWithoutMsp.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + } + + // Check that the MSP still has enough available capacity to store the file. + if ::available_capacity(&msp_id) + < storage_request_metadata.size + { + failed_file_keys + .try_push((file_key, Error::::InsufficientAvailableCapacity.into())) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + // Get the file metadata to insert into the bucket under the file key. + let file_metadata = storage_request_metadata.clone().to_file_metadata(); + let encoded_trie_value = file_metadata.encode(); + + let chunk_challenges = Self::generate_chunk_challenges_on_sp_confirm( + msp_id, + file_key, + &storage_request_metadata, + ); + + // Check that the key proof is valid. + if let Err(e) = ::verify_key_proof( + &file_key, + &chunk_challenges, + &key_proof, + ) { + failed_file_keys + .try_push((file_key, e)) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + // Increase size of the bucket. + if let Err(e) = ::increase_bucket_size( + &storage_request_metadata.bucket_id, + storage_request_metadata.size, + ) { + failed_file_keys + .try_push((file_key, e)) + .map_err(|_| Error::::TooManyStorageRequestResponses)?; + continue; + } + + // Increase the used capacity of the MSP + // This should not fail since we checked that the MSP has enough available capacity to store the file. + expect_or_err!( + ::increase_capacity_used( + &msp_id, + storage_request_metadata.size, + ), + "Failed to increase capacity used for MSP", + Error::::TooManyStorageRequestResponses, + result + ); + + // Check if all BSPs have confirmed storing the file. + if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required { + // Remove storage request metadata. + >::remove(&file_key); + >::remove( + &storage_request_metadata.bucket_id, + &file_key, + ); + + // Remove storage request bsps + let removed = + >::drain_prefix(&file_key).fold(0, |acc, _| acc + 1); + + // Make sure that the expected number of bsps were removed. + expect_or_err!( + storage_request_metadata.bsps_volunteered == removed.into(), + "Number of volunteered bsps for storage request should have been removed", + Error::::UnexpectedNumberOfRemovedVolunteeredBsps, + bool + ); + + // Notify that the storage request has been fulfilled. + Self::deposit_event(Event::StorageRequestFulfilled { file_key }); + } else { + // Set as confirmed the MSP in the storage request metadata. + storage_request_metadata.msp = Some((msp_id, true)); + + // Update storage request metadata. + >::set(&file_key, Some(storage_request_metadata.clone())); + } + + // This should not fail since we checked that the key is not already in the map. + expect_or_err!( + accepted_file_keys_and_metadata + .insert(file_key, encoded_trie_value) + .is_none(), + "Failed to insert file key and metadata into accepted_file_keys_and_metadata", + Error::::InconsistentStateKeyAlreadyExists, + bool + ); + } + + // Get the current root of the bucket where the file will be stored. + let bucket_root = expect_or_err!( + ::get_root_bucket(&bucket_id), + "Failed to get root for bucket, when it was already checked to exist", + Error::::BucketNotFound + ); + + // Compute the new bucket root after inserting new file key in its forest partial trie. + let new_bucket_root = + ::generic_apply_delta( + &bucket_root, + accepted_file_keys_and_metadata + .iter() + .map(|(fk, metadata)| (*fk, TrieAddMutation::new(metadata.clone()).into())) + .collect::>() + .as_slice(), + &accepted_file_keys.non_inclusion_forest_proof, + )?; + + // Update root of the bucket. + ::change_root_bucket( + bucket_id, + new_bucket_root, + )?; + + let accepted_file_keys: Vec> = + accepted_file_keys_and_metadata.keys().cloned().collect(); + + Ok(( + new_bucket_root, + accepted_file_keys + .try_into() + .map_err(|_| Error::::TooManyStorageRequestResponses)?, + failed_file_keys, + )) + } + + /// Volunteer to store a file. + /// + /// *Callable only by BSP accounts* + /// + /// A BSP can only volunteer for a storage request if it is eligible based on the XOR of the `fingerprint` and the BSP's account ID and if it evaluates to a value + /// less than the [globally computed threshold](BspsAssignmentThreshold). As the number of BSPs signed up increases, the threshold decreases, meaning there is a + /// lower chance of a BSP being eligible to volunteer for a storage request. + /// + /// Though, as the storage request remains open, the threshold increases over time based on the number of ticks since the storage request was issued. This is to + /// ensure that the storage request is fulfilled by opening up the opportunity for more BSPs to volunteer. + /// + /// For more information on what "ticks" are, see the [Proofs Dealer pallet](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/proofs-dealer/README.md). + pub(crate) fn do_bsp_volunteer( + sender: T::AccountId, + file_key: MerkleHash, + ) -> Result< + ( + ProviderIdFor, + MultiAddresses, + StorageRequestMetadata, + ), + DispatchError, + > { + let bsp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotABsp)?; + + // Check that the provider is indeed a BSP. + ensure!( + ::is_bsp(&bsp_id), + Error::::NotABsp + ); + + // Check that the storage request exists. + let mut storage_request_metadata = + >::get(&file_key).ok_or(Error::::StorageRequestNotFound)?; + + expect_or_err!( + storage_request_metadata.bsps_confirmed < storage_request_metadata.bsps_required, + "Storage request should never have confirmed bsps equal to or greater than required bsps, since they are deleted when it is reached.", + Error::::StorageRequestBspsRequiredFulfilled, + bool + ); + + let available_capacity = + ::available_capacity(&bsp_id); + + // Check if the BSP has enough capacity to store the file. + ensure!( + available_capacity > storage_request_metadata.size, + Error::::InsufficientAvailableCapacity + ); + + // Check if the BSP is already volunteered for this storage request. + ensure!( + !>::contains_key(&file_key, &bsp_id), + Error::::BspAlreadyVolunteered + ); + + // Get the threshold needed for the BSP to be able to volunteer for the storage request. + let bsp_threshold = + Self::get_threshold_for_bsp_request(&bsp_id, &storage_request_metadata.fingerprint); + + // Compute threshold for BSP to succeed. + let (to_succeed, _slope) = + Self::compute_threshold_to_succeed(&bsp_id, storage_request_metadata.requested_at)?; + + // Check that the BSP's threshold is under the threshold required to volunteer for the storage request. + ensure!(bsp_threshold <= to_succeed, Error::::AboveThreshold); + + // Add BSP to storage request metadata. + >::insert( + &file_key, + &bsp_id, + StorageRequestBspsMetadata:: { + confirmed: false, + _phantom: Default::default(), + }, + ); + + // Increment the number of bsps volunteered. + match storage_request_metadata + .bsps_volunteered + .checked_add(&ReplicationTargetType::::one()) + { + Some(inc_bsps_volunteered) => { + storage_request_metadata.bsps_volunteered = inc_bsps_volunteered; + } + None => { + return Err(ArithmeticError::Overflow.into()); + } + } + + // Update storage request metadata. + >::set(&file_key, Some(storage_request_metadata.clone())); + + let multiaddresses = T::Providers::get_bsp_multiaddresses(&bsp_id)?; + + Ok((bsp_id, multiaddresses, storage_request_metadata)) + } + + /// Confirm storing a file. + /// + /// *Callable only by BSP accounts* + /// + /// This function can only be called after a BSP has volunteered for the storage request. The BSP must provide a merkle proof of the file + /// and a proof of inclusion of the `file_key` in their merkle patricia trie. + /// + /// If the proof is valid, the root of the BSP is updated to reflect the new root of the merkle patricia trie and the number of `bsps_confirmed` is + /// incremented. If the number of `bsps_confirmed` reaches the number of `bsps_required`, the storage request is deleted. Finally, the BSP's data + /// used is incremented by the size of the file. + pub(crate) fn do_bsp_confirm_storing( + sender: T::AccountId, + non_inclusion_forest_proof: ForestProof, + file_keys_and_proofs: BoundedVec< + (MerkleHash, KeyProof), + T::MaxBatchConfirmStorageRequests, + >, + ) -> DispatchResult { + let bsp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotABsp)?; + + // Check that the provider is indeed a BSP. + ensure!( + ::is_bsp(&bsp_id), + Error::::NotABsp + ); + + let file_keys = file_keys_and_proofs + .iter() + .map(|(fk, _)| *fk) + .collect::>(); + + // Verify the proof of non-inclusion. + let proven_keys: BTreeSet> = + ::verify_forest_proof( + &bsp_id, + file_keys.as_slice(), + &non_inclusion_forest_proof, + )?; + + // Create a queue to store the file keys and metadata to be processed. + let mut file_keys_and_metadatas: BoundedVec< + (MerkleHash, Vec), + T::MaxBatchConfirmStorageRequests, + > = BoundedVec::new(); + + let mut seen_keys = BTreeSet::new(); + for file_key in file_keys_and_proofs.iter() { + // Skip any duplicates. + if !seen_keys.insert(file_key.0) { + continue; + } + + // Check that the storage request exists. + let mut storage_request_metadata = + >::get(&file_key.0).ok_or(Error::::StorageRequestNotFound)?; + + if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required { + // Since BSPs need to race one another to confirm storage requests, it is entirely possible that a BSP confirms a storage request + // after the storage request has been fulfilled within the same block. + // TODO: Accumulate a list of storage requests that have been skipped for the BSP to confirm what it can. + continue; + } + + expect_or_err!( + storage_request_metadata.bsps_confirmed < storage_request_metadata.bsps_required, + "Storage request should never have confirmed bsps equal to or greater than required bsps, since they are deleted when it is reached.", + Error::::StorageRequestBspsRequiredFulfilled, + bool + ); + + // Check that the BSP has volunteered for the storage request. + ensure!( + >::contains_key(&file_key.0, &bsp_id), + Error::::BspNotVolunteered + ); + + let requests = expect_or_err!( + >::get(&file_key.0, &bsp_id), + "BSP should exist since we checked it above", + Error::::ImpossibleFailedToGetValue + ); + + // Check that the storage provider has not already confirmed storing the file. + ensure!(!requests.confirmed, Error::::BspAlreadyConfirmed); + + let available_capacity = + ::available_capacity(&bsp_id); + + // Check if the BSP has enough capacity to store the file. + ensure!( + available_capacity > storage_request_metadata.size, + Error::::InsufficientAvailableCapacity + ); + + // Increment the number of bsps confirmed. + match storage_request_metadata + .bsps_confirmed + .checked_add(&ReplicationTargetType::::one()) + { + Some(inc_bsps_confirmed) => { + storage_request_metadata.bsps_confirmed = inc_bsps_confirmed; + } + None => { + return Err(ArithmeticError::Overflow.into()); + } + } + + // Ensure that the file key IS NOT part of the BSP's forest. + // Note: The runtime is responsible for adding and removing keys, computing the new root and updating the BSP's root. + ensure!( + !proven_keys.contains(&file_key.0), + Error::::ExpectedNonInclusionProof + ); + + let chunk_challenges = Self::generate_chunk_challenges_on_sp_confirm( + bsp_id, + file_key.0, + &storage_request_metadata, + ); + + // Check that the key proof is valid. + ::verify_key_proof( + &file_key.0, + &chunk_challenges, + &file_key.1, + )?; + + // Add data to storage provider. + ::increase_capacity_used( + &bsp_id, + storage_request_metadata.size, + )?; + + // Check if a payment stream between the user and provider already exists. + // If it does not, create it. If it does, update it. + match ::get_dynamic_rate_payment_stream_amount_provided(&bsp_id, &storage_request_metadata.owner) { + Some(previous_amount_provided) => { + // Update the payment stream. + ::update_dynamic_rate_payment_stream( + &bsp_id, + &storage_request_metadata.owner, + &(previous_amount_provided + storage_request_metadata.size), + )?; + }, + None => { + // Create the payment stream. + ::create_dynamic_rate_payment_stream( + &bsp_id, + &storage_request_metadata.owner, + &storage_request_metadata.size, + )?; + } + } + + // Get the file metadata to insert into the Provider's trie under the file key. + let file_metadata = storage_request_metadata.clone().to_file_metadata(); + let encoded_trie_value = file_metadata.encode(); + expect_or_err!( + file_keys_and_metadatas.try_push((file_key.0, encoded_trie_value)), + "Failed to push file key and metadata", + Error::::FileMetadataProcessingQueueFull, + result + ); + + // Remove storage request if we reached the required number of bsps and the MSP has confirmed storing the file. + if storage_request_metadata.bsps_confirmed == storage_request_metadata.bsps_required + && storage_request_metadata + .msp + .map(|(_, confirmed)| confirmed) + .unwrap_or(true) + { + // Remove storage request metadata. + >::remove(&file_key.0); + >::remove( + &storage_request_metadata.bucket_id, + &file_key.0, + ); + + // Remove storage request bsps + let removed = + >::drain_prefix(&file_key.0).fold(0, |acc, _| acc + 1); + + // Make sure that the expected number of bsps were removed. + expect_or_err!( + storage_request_metadata.bsps_volunteered == removed.into(), + "Number of volunteered bsps for storage request should have been removed", + Error::::UnexpectedNumberOfRemovedVolunteeredBsps, + bool + ); + + // Notify that the storage request has been fulfilled. + Self::deposit_event(Event::StorageRequestFulfilled { + file_key: file_key.0, + }); + } else { + // Update storage request metadata. + >::set(&file_key.0, Some(storage_request_metadata.clone())); + + // Update bsp for storage request. + >::mutate(&file_key.0, &bsp_id, |bsp| { + if let Some(bsp) = bsp { + bsp.confirmed = true; + } + }); + } + } + + // Check if this is the first file added to the BSP's Forest. If so, initialise last tick proven by this BSP. + let old_root = expect_or_err!( + ::get_root(bsp_id), + "Failed to get root for BSP, when it was already checked to be a BSP", + Error::::NotABsp + ); + + if old_root == ::get_default_root() { + // This means that this is the first file added to the BSP's Forest. + ::initialise_challenge_cycle( + &bsp_id, + )?; + + // Emit the corresponding event. + Self::deposit_event(Event::::BspChallengeCycleInitialised { + who: sender.clone(), + bsp_id, + }); + } + + // Compute new root after inserting new file keys in forest partial trie. + let new_root = ::apply_delta( + &bsp_id, + file_keys_and_metadatas + .iter() + .map(|(fk, metadata)| (*fk, TrieAddMutation::new(metadata.clone()).into())) + .collect::>() + .as_slice(), + &non_inclusion_forest_proof, + )?; + + // Update root of BSP. + ::update_root(bsp_id, new_root)?; + + // Emit event. + Self::deposit_event(Event::BspConfirmedStoring { + who: sender, + bsp_id, + file_keys: file_keys.to_vec().try_into().unwrap(), + new_root, + }); + + Ok(()) + } + + /// Revoke a storage request. + /// + /// *Callable by the owner of the storage request* + pub(crate) fn do_revoke_storage_request( + sender: T::AccountId, + file_key: MerkleHash, + ) -> DispatchResult { + // Check that the storage request exists. + ensure!( + >::contains_key(&file_key), + Error::::StorageRequestNotFound + ); + + // Get storage request metadata. + let storage_request_metadata = expect_or_err!( + >::get(&file_key), + "Storage request should exist", + Error::::StorageRequestNotFound + ); + + // Check that the sender is the owner of the storage request. + ensure!( + storage_request_metadata.owner == sender, + Error::::StorageRequestNotAuthorized + ); + + Self::cleanup_storage_request( + EitherAccountIdOrMspId::AccountId(sender), + file_key, + &storage_request_metadata, + )?; + + Ok(()) + } + + /// When a storage request is revoked and has already been confirmed by some BSPs, a challenge (with priority) is + /// issued to force the BSPs to update their storage root to uninclude the file from their storage. + /// + /// All BSPs that have volunteered to store the file are removed from the storage request and the storage request is deleted. + fn cleanup_storage_request( + revoker: EitherAccountIdOrMspId, + file_key: MerkleHash, + storage_request_metadata: &StorageRequestMetadata, + ) -> DispatchResult { + // Check if there are already BSPs who have confirmed to store the file. + if storage_request_metadata.bsps_confirmed >= ReplicationTargetType::::one() { + // Apply Remove mutation of the file key to the BSPs that have confirmed storing the file (proofs of inclusion). + ::challenge_with_priority( + &file_key, + Some(TrieRemoveMutation), + )?; + + // Emit event. + Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { + issuer: revoker, + file_key, + }); + } + + // Remove storage request bsps + let removed = >::drain_prefix(&file_key).fold(0, |acc, _| acc + 1); + + // Make sure that the expected number of bsps were removed. + expect_or_err!( + storage_request_metadata.bsps_volunteered == removed.into(), + "Number of volunteered bsps for storage request should have been removed", + Error::::UnexpectedNumberOfRemovedVolunteeredBsps, + bool + ); + + // Remove storage request. + >::remove(&file_key); + + // A revoked storage request is not considered active anymore. + >::remove(&storage_request_metadata.bucket_id, &file_key); + + Ok(()) + } + + /// BSP stops storing a file. + /// + /// *Callable only by BSP accounts* + /// + /// This function covers a few scenarios in which a BSP invokes this function to stop storing a file: + /// + /// 1. The BSP has volunteered and confirmed storing the file and wants to stop storing it while the storage request is still open. + /// + /// > In this case, the BSP has volunteered and confirmed storing the file for an existing storage request. + /// Therefore, we decrement the `bsps_confirmed` by 1. + /// + /// 2. The BSP stops storing a file that has an opened storage request but is not a volunteer. + /// + /// > In this case, the storage request was probably created by another BSP for some reason (e.g. that BSP lost the file) + /// and the current BSP is not a volunteer for this since it is already storing it. But since they to have stopped storing it, + /// we increment the `bsps_required` by 1. + /// + /// 3. The BSP stops storing a file that no longer has an opened storage request. + /// + /// > In this case, there is no storage request opened for the file they no longer are storing. Therefore, we + /// create a storage request with `bsps_required` set to 1. + /// + /// *This function does not give BSPs the possibility to remove themselves from being a __volunteer__ of a storage request.* + /// + /// A proof of inclusion is required to record the new root of the BSPs merkle patricia trie. First we validate the proof + /// and ensure the `file_key` is indeed part of the merkle patricia trie. Then finally compute the new merkle patricia trie root + /// by removing the `file_key` and update the root of the BSP. + /// + /// `can_serve`: A flag that indicates if the BSP can serve the file to other BSPs. If the BSP can serve the file, then + /// they are added to the storage request as a data server. + pub(crate) fn do_bsp_request_stop_storing( + sender: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + location: FileLocation, + owner: T::AccountId, + fingerprint: Fingerprint, + size: StorageData, + can_serve: bool, + inclusion_forest_proof: ForestProof, + ) -> Result, DispatchError> { + let bsp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotABsp)?; + + // Check that the provider is indeed a BSP. + ensure!( + ::is_bsp(&bsp_id), + Error::::NotABsp + ); + + // TODO: charge SP for this action. + + // Compute the file key hash. + let computed_file_key = Self::compute_file_key( + owner.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Check that the metadata corresponds to the expected file key. + ensure!( + file_key == computed_file_key, + Error::::InvalidFileKeyMetadata + ); + + // Check that a pending stop storing request for that BSP and file does not exist yet. + ensure!( + !>::contains_key(&bsp_id, &file_key), + Error::::PendingStopStoringRequestAlreadyExists + ); + + // Verify the proof of inclusion. + let proven_keys = + ::verify_forest_proof( + &bsp_id, + &[file_key], + &inclusion_forest_proof, + )?; + + // Ensure that the file key IS part of the BSP's forest. + ensure!( + proven_keys.contains(&file_key), + Error::::ExpectedInclusionProof + ); + + match >::get(&file_key) { + Some(mut storage_request_metadata) => { + match >::get(&file_key, &bsp_id) { + // We hit scenario 1. The BSP is a volunteer and has confirmed storing the file. + // We need to decrement the number of bsps confirmed and volunteered and remove the BSP from the storage request. + Some(bsp) => { + expect_or_err!( + bsp.confirmed, + "BSP should have confirmed storing the file since we verify the proof and their root matches the one in storage", + Error::::BspNotConfirmed, + bool + ); + + storage_request_metadata.bsps_confirmed = storage_request_metadata + .bsps_confirmed + .saturating_sub(ReplicationTargetType::::one()); + + storage_request_metadata.bsps_volunteered = storage_request_metadata + .bsps_volunteered + .saturating_sub(ReplicationTargetType::::one()); + + >::remove(&file_key, &bsp_id); + } + // We hit scenario 2. There is an open storage request but the BSP is not a volunteer. + // We need to increment the number of bsps required. + None => { + storage_request_metadata.bsps_required = storage_request_metadata + .bsps_required + .saturating_add(ReplicationTargetType::::one()); + } + } + + // Update storage request metadata. + >::set(&file_key, Some(storage_request_metadata)); + } + // We hit scenario 3. There is no storage request opened for the file. + // We need to create a new storage request with a single bsp required. + None => { + Self::do_request_storage( + owner, + bucket_id, + location.clone(), + fingerprint, + size, + None, + Some(ReplicationTargetType::::one()), + None, + if can_serve { + BoundedVec::try_from(vec![bsp_id]).unwrap() + } else { + BoundedVec::default() + }, + )?; + } + }; + + // Add the pending stop storing request to storage. + >::insert( + &bsp_id, + &file_key, + (frame_system::Pallet::::block_number(), size), + ); + + Ok(bsp_id) + } + + pub(crate) fn do_bsp_confirm_stop_storing( + sender: T::AccountId, + file_key: MerkleHash, + inclusion_forest_proof: ForestProof, + ) -> Result<(ProviderIdFor, MerkleHash), DispatchError> { + // Get the BSP ID of the sender + let bsp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotABsp)?; + + // Get the block when the pending stop storing request of the BSP for the file key was opened. + let (block_when_opened, file_size) = + >::get(&bsp_id, &file_key) + .ok_or(Error::::PendingStopStoringRequestNotFound)?; + + // Check that enough time has passed since the pending stop storing request was opened. + ensure!( + frame_system::Pallet::::block_number() + >= block_when_opened + T::MinWaitForStopStoring::get(), + Error::::MinWaitForStopStoringNotReached + ); + + // Verify the proof of inclusion. + let proven_keys = + ::verify_forest_proof( + &bsp_id, + &[file_key], + &inclusion_forest_proof, + )?; + + // Ensure that the file key IS part of the BSP's forest. + // The runtime is responsible for adding and removing keys, computing the new root and updating the BSP's root. + ensure!( + proven_keys.contains(&file_key), + Error::::ExpectedInclusionProof + ); + + // Compute new root after removing file key from forest partial trie. + let new_root = ::apply_delta( + &bsp_id, + &[(file_key, TrieRemoveMutation::default().into())], + &inclusion_forest_proof, + )?; + + // Update root of BSP. + ::update_root(bsp_id, new_root)?; + + // Decrease data used by the BSP. + ::decrease_capacity_used( + &bsp_id, file_size, + )?; + + // Remove the pending stop storing request from storage. + >::remove(&bsp_id, &file_key); + + Ok((bsp_id, new_root)) + } + + /// SP stops storing a file from a User that has been flagged as insolvent. + /// + /// *Callable only by SP accounts* + /// + /// A proof of inclusion is required to record the new root of the SPs merkle patricia trie. First we validate the proof + /// and ensure the `file_key` is indeed part of the merkle patricia trie. Then finally compute the new merkle patricia trie root + /// by removing the `file_key` and update the root of the SP. + pub(crate) fn do_sp_stop_storing_for_insolvent_user( + sender: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + location: FileLocation, + owner: T::AccountId, + fingerprint: Fingerprint, + size: StorageData, + inclusion_forest_proof: ForestProof, + ) -> Result<(ProviderIdFor, MerkleHash), DispatchError> { + // Get the SP ID + let sp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotASp)?; + + // Check that the owner of the file has been flagged as insolvent OR that the Provider does not + // have any active payment streams with the user. The rationale here is that if there is a + // user who cannot pay, or is just not paying anymore, the SP has the right to stop storing files for them + // without having to pay any penalty. + ensure!( + ::is_user_insolvent(&owner) + || !::has_active_payment_stream( + &sp_id, &owner + ), + Error::::UserNotInsolvent + ); + + // Compute the file key hash. + let computed_file_key = Self::compute_file_key( + owner.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Check that the metadata corresponds to the expected file key. + ensure!( + file_key == computed_file_key, + Error::::InvalidFileKeyMetadata + ); + + // Verify the proof of inclusion. + // If the Provider is a BSP, the proof is verified against the BSP's forest. + let new_root = if ::is_bsp(&sp_id) { + let proven_keys = + ::verify_forest_proof( + &sp_id, + &[file_key], + &inclusion_forest_proof, + )?; + + // Ensure that the file key IS part of the BSP's forest. + ensure!( + proven_keys.contains(&file_key), + Error::::ExpectedInclusionProof + ); + + // Compute new root after removing file key from forest partial trie. + let new_root = ::apply_delta( + &sp_id, + &[(file_key, TrieRemoveMutation::default().into())], + &inclusion_forest_proof, + )?; + + // Update root of the BSP. + ::update_root(sp_id, new_root)?; + + new_root + } else { + // If the Provider is a MSP, the proof is verified against the Bucket's root. + + // Check that the Bucket is stored by the MSP + ensure!( + ::is_bucket_stored_by_msp( + &sp_id, &bucket_id + ), + Error::::MspNotStoringBucket + ); + + // Decrease size of the bucket. + ::decrease_bucket_size(&bucket_id, size)?; + + // Get the Bucket's root + let bucket_root = + ::get_root_bucket(&bucket_id) + .ok_or(Error::::BucketNotFound)?; + + let proven_keys = + ::verify_generic_forest_proof( + &bucket_root, + &[file_key], + &inclusion_forest_proof, + )?; + + // Ensure that the file key IS part of the Bucket's trie. + ensure!( + proven_keys.contains(&file_key), + Error::::ExpectedInclusionProof + ); + + // Compute new root after removing file key from forest partial trie. + let new_root = + ::generic_apply_delta( + &bucket_root, + &[(file_key, TrieRemoveMutation::default().into())], + &inclusion_forest_proof, + )?; + + // Update root of the Bucket. + ::change_root_bucket( + bucket_id, new_root, + )?; + + new_root + }; + + // Decrease data used by the SP. + ::decrease_capacity_used(&sp_id, size)?; + + Ok((sp_id, new_root)) + } + + pub(crate) fn do_delete_file( + sender: T::AccountId, + bucket_id: BucketIdFor, + file_key: MerkleHash, + location: FileLocation, + fingerprint: Fingerprint, + size: StorageData, + maybe_inclusion_forest_proof: Option>, + ) -> Result<(bool, ProviderIdFor), DispatchError> { + // Compute the file key hash. + let computed_file_key = Self::compute_file_key( + sender.clone(), + bucket_id, + location.clone(), + size, + fingerprint, + ); + + // Check that the metadata corresponds to the expected file key. + ensure!( + file_key == computed_file_key, + Error::::InvalidFileKeyMetadata + ); + + // Check if sender is the owner of the bucket. + ensure!( + ::is_bucket_owner(&sender, &bucket_id)?, + Error::::NotBucketOwner + ); + + let msp_id = ::get_msp_of_bucket(&bucket_id) + .ok_or(Error::::BucketNotFound)?; + + let file_key_included = match maybe_inclusion_forest_proof { + // If the user did not supply a proof of inclusion, queue a pending deletion file request. + // This will leave a window of time for the MSP to provide the proof of (non-)inclusion. + // If the proof is not provided within the TTL, the hook will queue a priority challenge to remove the file key from all the providers. + None => { + let pending_file_deletion_requests = >::get(&sender); + + // Check if the file key is already in the pending deletion requests. + ensure!( + !pending_file_deletion_requests.contains(&(file_key, bucket_id)), + Error::::FileKeyAlreadyPendingDeletion + ); + + // Add the file key to the pending deletion requests. + PendingFileDeletionRequests::::try_append(&sender, (file_key, bucket_id)) + .map_err(|_| Error::::MaxUserPendingDeletionRequestsReached)?; + + // Queue the expiration item. + let expiration_item = + ExpirationItem::PendingFileDeletionRequests((sender, file_key)); + Self::enqueue_expiration_item(expiration_item)?; + + false + } + // If the user supplied a proof of inclusion, verify the proof and queue a priority challenge to remove the file key from all the providers. + Some(inclusion_forest_proof) => { + // Get the root of the bucket. + let bucket_root = + ::get_root_bucket(&bucket_id) + .ok_or(Error::::BucketNotFound)?; + + // Verify the proof of inclusion. + let proven_keys = + ::verify_generic_forest_proof( + &bucket_root, + &[file_key], + &inclusion_forest_proof, + )?; + + // Ensure that the file key IS part of the owner's forest. + ensure!( + proven_keys.contains(&file_key), + Error::::ExpectedInclusionProof + ); + + // Initiate the priority challenge to remove the file key from all the providers. + ::challenge_with_priority( + &file_key, + Some(TrieRemoveMutation), + )?; + + // Decrease size of the bucket. + ::decrease_bucket_size(&bucket_id, size)?; + + // Emit event. + Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { + issuer: EitherAccountIdOrMspId::::AccountId(sender.clone()), + file_key, + }); + + true + } + }; + + Ok((file_key_included, msp_id)) + } + + pub(crate) fn do_pending_file_deletion_request_submit_proof( + sender: T::AccountId, + user: T::AccountId, + file_key: MerkleHash, + bucket_id: BucketIdFor, + forest_proof: ForestProof, + ) -> Result<(bool, ProviderIdFor), DispatchError> { + let msp_id = + ::get_provider_id(sender.clone()) + .ok_or(Error::::NotAMsp)?; + + // Check that the provider is indeed an MSP. + ensure!( + ::is_msp(&msp_id), + Error::::NotAMsp + ); + + ensure!( + ::is_bucket_stored_by_msp(&msp_id, &bucket_id), + Error::::MspNotStoringBucket + ); + + let pending_file_deletion_requests = >::get(&user); + + // Check if the file key is in the pending deletion requests. + ensure!( + pending_file_deletion_requests.contains(&(file_key, bucket_id)), + Error::::FileKeyNotPendingDeletion + ); + + // Get the root of the bucket. + let bucket_root = + ::get_root_bucket(&bucket_id) + .ok_or(Error::::BucketNotFound)?; + + // Verify the proof of inclusion. + let proven_keys = + ::verify_generic_forest_proof( + &bucket_root, + &[file_key], + &forest_proof, + )?; + + let file_key_included = proven_keys.contains(&file_key); + + if file_key_included { + // Initiate the priority challenge to remove the file key from all the providers. + ::challenge_with_priority( + &file_key, + Some(TrieRemoveMutation), + )?; + + // Emit event. + Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { + issuer: EitherAccountIdOrMspId::::MspId(msp_id), + file_key, + }); + } + + // Delete the pending deletion request. + >::mutate(&user, |requests| { + requests.retain(|(key, _)| key != &file_key); + }); + + Ok((file_key_included, msp_id)) + } + + /// Create a collection. + fn create_collection(owner: T::AccountId) -> Result, DispatchError> { + // TODO: Parametrize the collection settings. + let config: CollectionConfigFor = CollectionConfig { + settings: CollectionSettings::all_enabled(), + max_supply: None, + mint_settings: MintSettings { + mint_type: MintType::Issuer, + price: None, + start_block: None, + end_block: None, + default_item_settings: ItemSettings::all_enabled(), + }, + }; + + T::Nfts::create_collection(&owner, &owner, &config) + } + + /// Compute the next block number to insert an expiring item, and insert it in the corresponding expiration queue. + /// + /// This function attempts to insert a the expiration item at the next available block starting from + /// the current next available block. + pub(crate) fn enqueue_expiration_item( + expiration_item: ExpirationItem, + ) -> Result, DispatchError> { + let expiration_block = expiration_item.get_next_expiration_block(); + let new_expiration_block = expiration_item.try_append(expiration_block)?; + expiration_item.set_next_expiration_block(new_expiration_block); + + Ok(new_expiration_block) + } + + pub fn compute_file_key( + owner: T::AccountId, + bucket_id: BucketIdFor, + location: FileLocation, + size: StorageData, + fingerprint: Fingerprint, + ) -> MerkleHash { + shp_file_metadata::FileMetadata::< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + > { + owner: owner.encode(), + bucket_id: bucket_id.as_ref().to_vec(), + location: location.clone().to_vec(), + file_size: size.into(), + fingerprint: fingerprint.as_ref().into(), + } + .file_key::>() + } + + pub fn get_threshold_for_bsp_request( + bsp_id: &ProviderIdFor, + fingerprint: &Fingerprint, + ) -> T::ThresholdType { + // Concatenate the BSP ID and the fingerprint and hash them to get the volunteering hash. + let concatenated = sp_std::vec![bsp_id.encode(), fingerprint.encode()].concat(); + let volunteering_hash = + <::Hashing as Hash>::hash(concatenated.as_ref()); + + // Return the threshold needed for the BSP to be able to volunteer for the storage request. + T::HashToThresholdType::convert(volunteering_hash) + } + + /// Compute the threshold for a BSP to succeed. + /// + /// Succeeding this threshold is required for the BSP to be eligible to volunteer for a storage request. + /// The threshold is computed based on the global reputation weight and the BSP's reputation weight, giving + /// an advantage to BSPs with higher reputation weights. + /// + /// The formalized formulas are documented in the [README](https://github.com/Moonsong-Labs/storage-hub/blob/main/pallets/file-system/README.md#volunteering-succeeding-threshold-checks). + pub fn compute_threshold_to_succeed( + bsp_id: &ProviderIdFor, + requested_at: TickNumber, + ) -> Result<(T::ThresholdType, T::ThresholdType), DispatchError> { + let maximum_threshold = T::ThresholdType::max_value(); + + let global_weight = + T::ThresholdType::from(T::Providers::get_global_bsps_reputation_weight()); + + if global_weight == T::ThresholdType::zero() { + return Err(Error::::NoGlobalReputationWeightSet.into()); + } + + // Global threshold starting point from which all BSPs begin their threshold slope. All BSPs start at this point + // with the starting reputation weight. + // + // The calculation is designed to achieve the following: + // + // 1. In a regular scenario, `maximum_threshold` would be very large, and you'd start bringing it down with + // `global_weight`, also a large number. That way, when you multiply it by the replication target, + // you should still be within the numerical domain. + // + // 2. If `global_weight` is low still (in the early days of the network), when multiplying with + // replication target, you'll get at most `u32::MAX` and then the threshold would be + // u32::MAX / 2 (still pretty high). + // + // 3. If maximum_threshold is very low (like sometimes set in tests), the division would saturate to 1, + // and then the threshold would be replication target / 2 (still very low). + let threshold_global_starting_point = maximum_threshold + .checked_div(&global_weight) + .unwrap_or(T::ThresholdType::one()) + .checked_mul(&ReplicationTarget::::get().into()).unwrap_or({ + log::warn!("Global starting point is beyond MaximumThreshold. Setting it to half of the MaximumThreshold."); + maximum_threshold + }) + .checked_div(&T::ThresholdType::from(2u32)) + .unwrap_or(T::ThresholdType::one()); + + // Get the BSP's reputation weight. + let bsp_weight = T::ThresholdType::from(T::Providers::get_bsp_reputation_weight(&bsp_id)?); + + // Actual BSP's threshold starting point, taking into account their reputation weight. + let threshold_weighted_starting_point = + bsp_weight.saturating_mul(threshold_global_starting_point); + + // Rate of increase from the weighted threshold starting point up to the maximum threshold within a tick range. + let base_slope = maximum_threshold + .saturating_sub(threshold_global_starting_point) + .checked_div(&T::ThresholdTypeToTickNumber::convert_back( + TickRangeToMaximumThreshold::::get(), + )) + .unwrap_or(T::ThresholdType::one()); + + let threshold_slope = base_slope + .checked_mul(&bsp_weight) + .unwrap_or(maximum_threshold); + + // Since checked_div only returns None on a result of zero, there is the case when the result is between 0 and 1 and rounds down to 0. + let threshold_slope = if threshold_slope.is_zero() { + T::ThresholdType::one() + } else { + threshold_slope + }; + + let current_tick_number = + ::get_current_tick(); + + // Get number of ticks since the storage request was issued. + let ticks_since_requested = current_tick_number.saturating_sub(requested_at); + let ticks_since_requested = + T::ThresholdTypeToTickNumber::convert_back(ticks_since_requested); + + let to_succeed = threshold_weighted_starting_point + .saturating_add(threshold_slope.saturating_mul(ticks_since_requested)); + + Ok((to_succeed, threshold_slope)) + } +} + +mod hooks { + use crate::{ + pallet, + types::MerkleHash, + utils::{BucketIdFor, EitherAccountIdOrMspId, ProviderIdFor}, + DataServersForMoveBucket, Event, FileDeletionRequestExpirations, + NextStartingBlockToCleanUp, Pallet, PendingFileDeletionRequests, PendingMoveBucketRequests, + ReplicationTarget, StorageRequestBsps, StorageRequestExpirations, StorageRequests, + }; + use crate::{MoveBucketRequestExpirations, PendingBucketsToMove}; + use frame_support::weights::Weight; + use frame_system::pallet_prelude::BlockNumberFor; + use shp_traits::TrieRemoveMutation; + use sp_runtime::traits::{Get, One, Zero}; + use sp_runtime::Saturating; + + impl Pallet { + pub(crate) fn do_on_idle( + current_block: BlockNumberFor, + mut remaining_weight: &mut Weight, + ) -> &mut Weight { + let db_weight = T::DbWeight::get(); + let mut block_to_clean = NextStartingBlockToCleanUp::::get(); + + while block_to_clean <= current_block && !remaining_weight.is_zero() { + Self::process_block_expired_items(block_to_clean, &mut remaining_weight); + + if remaining_weight.is_zero() { + break; + } + + block_to_clean.saturating_accrue(BlockNumberFor::::one()); + } + + // Update the next starting block for cleanup + if block_to_clean > NextStartingBlockToCleanUp::::get() { + NextStartingBlockToCleanUp::::put(block_to_clean); + remaining_weight.saturating_reduce(db_weight.writes(1)); + } + + remaining_weight + } + + fn process_block_expired_items(block: BlockNumberFor, remaining_weight: &mut Weight) { + let db_weight = T::DbWeight::get(); + let minimum_required_weight_processing_expired_items = db_weight.reads_writes(1, 1); + + if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { + return; + } + + // Remove expired storage requests if any existed and process them. + let mut expired_storage_requests = StorageRequestExpirations::::take(&block); + remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); + + // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to + // TODO: process all the expired storage requests. If not, we should return early. + while let Some(file_key) = expired_storage_requests.pop() { + Self::process_expired_storage_request(file_key, remaining_weight) + } + + // If there are remaining items which were not processed, put them back in storage + if !expired_storage_requests.is_empty() { + StorageRequestExpirations::::insert(&block, expired_storage_requests); + remaining_weight.saturating_reduce(db_weight.writes(1)); + } + + // Check if there is enough remaining weight to process the expired file deletion requests. + if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { + return; + } + + // Remove expired file deletion requests if any existed and process them. + let mut expired_file_deletion_requests = + FileDeletionRequestExpirations::::take(&block); + remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); + + // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to + // TODO: process all the expired file deletion requests. If not, we should return early. + while let Some((user, file_key)) = expired_file_deletion_requests.pop() { + Self::process_expired_pending_file_deletion(user, file_key, remaining_weight) + } + + // If there are remaining items which were not processed, put them back in storage + if !expired_file_deletion_requests.is_empty() { + FileDeletionRequestExpirations::::insert(&block, expired_file_deletion_requests); + remaining_weight.saturating_reduce(db_weight.writes(1)); + } + + // Check if there is enough remaining weight to process expired move bucket requests + if !remaining_weight.all_gte(minimum_required_weight_processing_expired_items) { + return; + } + + // Remove expired move bucket requests if any existed and process them. + let mut expired_move_bucket_requests = MoveBucketRequestExpirations::::take(&block); + remaining_weight.saturating_reduce(minimum_required_weight_processing_expired_items); + + // TODO: After benchmarking, we should check before this loop that there is enough remaining weight to + // TODO: process all the expired move bucket requests. If not, we should return early. + while let Some((msp_id, bucket_id)) = expired_move_bucket_requests.pop() { + Self::process_expired_move_bucket_request(msp_id, bucket_id, remaining_weight); + } + + // If there are remaining items which were not processed, put them back in storage + if !expired_move_bucket_requests.is_empty() { + MoveBucketRequestExpirations::::insert(&block, expired_move_bucket_requests); + remaining_weight.saturating_reduce(db_weight.writes(1)); + } + } + + fn process_expired_storage_request(file_key: MerkleHash, remaining_weight: &mut Weight) { + let db_weight = T::DbWeight::get(); + + // As of right now, the upper bound limit to the number of BSPs required to fulfill a storage request is set by `ReplicationTarget`. + // We could increase this potential weight to account for potentially more volunteers. + let potential_weight = + db_weight.writes(ReplicationTarget::::get().saturating_plus_one().into()); + + if !remaining_weight.all_gte(potential_weight) { + return; + } + + // Remove storage request and all bsps that volunteered for it. + StorageRequests::::remove(&file_key); + let removed = + StorageRequestBsps::::drain_prefix(&file_key).fold(0, |acc, _| acc + 1u32); + + remaining_weight.saturating_reduce(db_weight.writes(1.saturating_add(removed.into()))); + + Self::deposit_event(Event::StorageRequestExpired { file_key }); + } + + fn process_expired_pending_file_deletion( + user: T::AccountId, + file_key: MerkleHash, + remaining_weight: &mut Weight, + ) { + let db_weight = T::DbWeight::get(); + let potential_weight = db_weight.reads_writes(2, 3); + + if !remaining_weight.all_gte(potential_weight) { + return; + } + + let requests = PendingFileDeletionRequests::::get(&user); + + // Check if the file key is still a pending deletion requests. + let expired_item_index = match requests.iter().position(|(key, _)| key == &file_key) { + Some(i) => i, + None => return, + }; + + // Remove the file key from the pending deletion requests. + PendingFileDeletionRequests::::mutate(&user, |requests| { + requests.remove(expired_item_index); + }); + + // Queue a priority challenge to remove the file key from all the providers. + let _ = ::challenge_with_priority( + &file_key, + Some(TrieRemoveMutation), + ) + .map_err(|_| { + Self::deposit_event(Event::FailedToQueuePriorityChallenge { + user: user.clone(), + file_key, + }); + }); + // Emit event. + Self::deposit_event(Event::PriorityChallengeForFileDeletionQueued { + issuer: EitherAccountIdOrMspId::::AccountId(user), + file_key, + }); + + remaining_weight.saturating_reduce(potential_weight); + } + + fn process_expired_move_bucket_request( + msp_id: ProviderIdFor, + bucket_id: BucketIdFor, + remaining_weight: &mut Weight, + ) { + let db_weight = T::DbWeight::get(); + let potential_weight = db_weight.reads_writes(0, 2); + + if !remaining_weight.all_gte(potential_weight) { + return; + } + + PendingMoveBucketRequests::::remove(&msp_id, &bucket_id); + PendingBucketsToMove::::remove(&bucket_id); + DataServersForMoveBucket::::drain_prefix(&bucket_id); + + remaining_weight.saturating_reduce(potential_weight); + + Self::deposit_event(Event::MoveBucketRequestExpired { msp_id, bucket_id }); + } + } +} diff --git a/pallets/payment-streams/Cargo.toml b/pallets/payment-streams/Cargo.toml index 34ce3b60e..a8463a4e3 100644 --- a/pallets/payment-streams/Cargo.toml +++ b/pallets/payment-streams/Cargo.toml @@ -1,76 +1,76 @@ -[package] -name = "pallet-payment-streams" -description = "FRAME pallet that allows for the creation and management of payment streams." -version = "0.1.0" -homepage = { workspace = true } -license = { workspace = true } -authors = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { workspace = true } -scale-info = { workspace = true } - -# Local -pallet-payment-streams-runtime-api = { workspace = true } -shp-traits = { workspace = true } - -# Substrate -frame-benchmarking = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -sp-runtime = { workspace = true } - -[dev-dependencies] -serde = { workspace = true } - -# Local -pallet-storage-providers = { workspace = true } -shp-constants = { workspace = true } -shp-file-metadata = { workspace = true } - -# Substrate -sp-core = { workspace = true } -sp-io = { workspace = true } -sp-trie = { workspace = true } - -# Frame -pallet-balances = { workspace = true, features = ["std"] } -pallet-nfts = { workspace = true } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-nfts/std", - "pallet-payment-streams-runtime-api/std", - "pallet-storage-providers/std", - "scale-info/std", - "shp-traits/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] +[package] +name = "pallet-payment-streams" +description = "FRAME pallet that allows for the creation and management of payment streams." +version = "0.1.0" +homepage = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } + +# Local +pallet-payment-streams-runtime-api = { workspace = true } +shp-traits = { workspace = true } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +sp-runtime = { workspace = true } + +[dev-dependencies] +serde = { workspace = true } + +# Local +pallet-storage-providers = { workspace = true } +shp-constants = { workspace = true } +shp-file-metadata = { workspace = true } + +# Substrate +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-trie = { workspace = true } + +# Frame +pallet-balances = { workspace = true, features = ["std"] } +pallet-nfts = { workspace = true } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-nfts/std", + "pallet-payment-streams-runtime-api/std", + "pallet-storage-providers/std", + "scale-info/std", + "shp-traits/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/payment-streams/src/mock.rs b/pallets/payment-streams/src/mock.rs index b255a04fc..b4f1aafa9 100644 --- a/pallets/payment-streams/src/mock.rs +++ b/pallets/payment-streams/src/mock.rs @@ -1,318 +1,318 @@ -use crate as pallet_payment_streams; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use frame_support::{ - construct_runtime, derive_impl, - pallet_prelude::Get, - parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Randomness}, - weights::constants::RocksDbWeight, -}; -use frame_system as system; -use pallet_nfts::PalletFeatures; -use shp_traits::{ProofSubmittersInterface, ReadProvidersInterface}; -use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Hasher, H256}; -use sp_runtime::{ - testing::TestSignature, - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; -use sp_runtime::{traits::Convert, BoundedBTreeSet}; -use sp_trie::{LayoutV1, TrieConfiguration, TrieLayout}; -use system::pallet_prelude::BlockNumberFor; - -type Block = frame_system::mocking::MockBlock; -type Balance = u128; -type StorageUnit = u64; -type AccountId = u64; - -const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; -// We mock the Randomness trait to use a simple randomness function when testing the pallet -const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; -pub struct MockRandomness; -impl Randomness> for MockRandomness { - fn random(subject: &[u8]) -> (H256, BlockNumberFor) { - // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks - - // Concatenate the subject with the block number to get a unique hash for each block - let subject_concat_block = [ - subject, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - - let hashed_subject = blake2_256(&subject_concat_block); - - ( - H256::from_slice(&hashed_subject), - frame_system::Pallet::::block_number() - .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), - ) - } -} - -// Configure a mock runtime to test the pallet. -construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - StorageProviders: pallet_storage_providers, - PaymentStreams: pallet_payment_streams, - Nfts: pallet_nfts - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_balances::Config for Test { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxLocks = ConstU32<10>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = (); - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<10>; -} - -parameter_types! { - pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::StorageProviderDeposit); -} -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} - -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId { - 0 - } -} - -pub struct MockFileMetadataManager; -impl shp_traits::FileMetadataInterface for MockFileMetadataManager { - type AccountId = AccountId; - type Metadata = shp_file_metadata::FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type StorageDataUnit = u64; - - fn encode(metadata: &Self::Metadata) -> Vec { - metadata.encode() - } - - fn decode(data: &[u8]) -> Result { - as Decode>::decode(&mut &data[..]) - } - - fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { - metadata.file_size - } - - fn get_file_owner(metadata: &Self::Metadata) -> Result { - Self::AccountId::decode(&mut metadata.owner.as_slice()) - } -} - -impl pallet_storage_providers::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = MockRandomness; - type FileMetadataManager = MockFileMetadataManager; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = StorageUnit; - type PaymentStreams = PaymentStreams; - type SpCount = u32; - type MerklePatriciaRoot = H256; - type ValuePropId = H256; - type ReadAccessGroupId = ::CollectionId; - type ProvidersProofSubmitters = MockSubmittingProviders; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = ConstU128<10>; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = ConstU32<100>; - type MaxMultiAddressAmount = ConstU32<5>; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = ConstU128<10>; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; - type MinBlocksBetweenCapacityChanges = ConstU64<10>; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = ConstU128<10>; - type StartingReputationWeight = ConstU32<1>; -} - -parameter_types! { - pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); -} - -impl pallet_nfts::Config for Test { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u128; - type ItemId = u128; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Locker = (); - type CollectionDeposit = ConstU128<2>; - type ItemDeposit = ConstU128<1>; - type MetadataDepositBase = ConstU128<1>; - type AttributeDepositBase = ConstU128<1>; - type DepositPerByte = ConstU128<1>; - type StringLimit = ConstU32<50>; - type KeyLimit = ConstU32<50>; - type ValueLimit = ConstU32<50>; - type ApprovalsLimit = ConstU32<10>; - type ItemAttributesApprovalsLimit = ConstU32<2>; - type MaxTips = ConstU32<10>; - type MaxDeadlineDuration = ConstU64<10000>; - type MaxAttributesPerCall = ConstU32<2>; - type Features = Features; - type OffchainSignature = TestSignature; - type OffchainPublic = ::Signer; - type WeightInfo = (); - pallet_nfts::runtime_benchmarks_enabled! { - type Helper = (); - } -} - -parameter_types! { - pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); -} - -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; - -impl Convert, Balance> for BlockNumberToBalance { - fn convert(block_number: BlockNumberFor) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. -pub struct MockSubmittingProviders; -impl ProofSubmittersInterface for MockSubmittingProviders { - type ProviderId = ::Hash; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = ConstU32<1000>; - fn get_proof_submitters_for_tick( - block_number: &Self::TickNumber, - ) -> Option> { - let mut set = BoundedBTreeSet::::new(); - // We convert the block number + 1 to the corresponding Provider ID, to simulate that the Provider submitted a proof - ::get_provider_id(*block_number + 1) - .map(|id| set.try_insert(id)); - Some(set) - } - - fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { - None - } - - fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} -} - -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = StorageProviders; - type RuntimeHoldReason = RuntimeHoldReason; - type Units = StorageUnit; - type NewStreamDeposit = ConstU64<10>; - type UserWithoutFundsCooldown = ConstU64<100>; - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = MockSubmittingProviders; -} - -// Build genesis storage according to the mock runtime. -pub fn _new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::::default() - .build_storage() - .unwrap() - .into() -} - -// Externalities builder with predefined balances for accounts and starting at block number 1 -pub struct ExtBuilder; -impl ExtBuilder { - pub fn build() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (0, 5_000_000), // Alice = 0 - (1, 10_000_000), // Bob = 1 - (2, 20_000_000), // Charlie = 2 - (3, 30_000_000), // David = 3 - (4, 400_000_000), // Eve = 4 - (5, 5_000_000_000), // Ferdie = 5 - (6, 600_000_000_000), // George = 6 - (123, 5_000_000), // Alice for `on_poll` testing = 123 - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - crate::GenesisConfig:: { current_price: 1 } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| { - System::set_block_number(1); - pallet_payment_streams::OnPollTicker::::set(1); - }); - ext - } -} +use crate as pallet_payment_streams; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, + pallet_prelude::Get, + parameter_types, + traits::{AsEnsureOriginWithArg, Everything, Randomness}, + weights::constants::RocksDbWeight, +}; +use frame_system as system; +use pallet_nfts::PalletFeatures; +use shp_traits::{ProofSubmittersInterface, ReadProvidersInterface}; +use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Hasher, H256}; +use sp_runtime::{ + testing::TestSignature, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; +use sp_runtime::{traits::Convert, BoundedBTreeSet}; +use sp_trie::{LayoutV1, TrieConfiguration, TrieLayout}; +use system::pallet_prelude::BlockNumberFor; + +type Block = frame_system::mocking::MockBlock; +type Balance = u128; +type StorageUnit = u64; +type AccountId = u64; + +const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; +// We mock the Randomness trait to use a simple randomness function when testing the pallet +const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; +pub struct MockRandomness; +impl Randomness> for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumberFor) { + // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks + + // Concatenate the subject with the block number to get a unique hash for each block + let subject_concat_block = [ + subject, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + + let hashed_subject = blake2_256(&subject_concat_block); + + ( + H256::from_slice(&hashed_subject), + frame_system::Pallet::::block_number() + .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), + ) + } +} + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + StorageProviders: pallet_storage_providers, + PaymentStreams: pallet_payment_streams, + Nfts: pallet_nfts + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<10>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = (); + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<10>; +} + +parameter_types! { + pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::StorageProviderDeposit); +} +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} + +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId { + 0 + } +} + +pub struct MockFileMetadataManager; +impl shp_traits::FileMetadataInterface for MockFileMetadataManager { + type AccountId = AccountId; + type Metadata = shp_file_metadata::FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type StorageDataUnit = u64; + + fn encode(metadata: &Self::Metadata) -> Vec { + metadata.encode() + } + + fn decode(data: &[u8]) -> Result { + as Decode>::decode(&mut &data[..]) + } + + fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { + metadata.file_size + } + + fn get_file_owner(metadata: &Self::Metadata) -> Result { + Self::AccountId::decode(&mut metadata.owner.as_slice()) + } +} + +impl pallet_storage_providers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = MockRandomness; + type FileMetadataManager = MockFileMetadataManager; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = StorageUnit; + type PaymentStreams = PaymentStreams; + type SpCount = u32; + type MerklePatriciaRoot = H256; + type ValuePropId = H256; + type ReadAccessGroupId = ::CollectionId; + type ProvidersProofSubmitters = MockSubmittingProviders; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = ConstU128<10>; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = ConstU32<100>; + type MaxMultiAddressAmount = ConstU32<5>; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = ConstU128<10>; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; + type MinBlocksBetweenCapacityChanges = ConstU64<10>; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = ConstU128<10>; + type StartingReputationWeight = ConstU32<1>; +} + +parameter_types! { + pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); +} + +impl pallet_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u128; + type ItemId = u128; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Locker = (); + type CollectionDeposit = ConstU128<2>; + type ItemDeposit = ConstU128<1>; + type MetadataDepositBase = ConstU128<1>; + type AttributeDepositBase = ConstU128<1>; + type DepositPerByte = ConstU128<1>; + type StringLimit = ConstU32<50>; + type KeyLimit = ConstU32<50>; + type ValueLimit = ConstU32<50>; + type ApprovalsLimit = ConstU32<10>; + type ItemAttributesApprovalsLimit = ConstU32<2>; + type MaxTips = ConstU32<10>; + type MaxDeadlineDuration = ConstU64<10000>; + type MaxAttributesPerCall = ConstU32<2>; + type Features = Features; + type OffchainSignature = TestSignature; + type OffchainPublic = ::Signer; + type WeightInfo = (); + pallet_nfts::runtime_benchmarks_enabled! { + type Helper = (); + } +} + +parameter_types! { + pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); +} + +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; + +impl Convert, Balance> for BlockNumberToBalance { + fn convert(block_number: BlockNumberFor) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. +pub struct MockSubmittingProviders; +impl ProofSubmittersInterface for MockSubmittingProviders { + type ProviderId = ::Hash; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = ConstU32<1000>; + fn get_proof_submitters_for_tick( + block_number: &Self::TickNumber, + ) -> Option> { + let mut set = BoundedBTreeSet::::new(); + // We convert the block number + 1 to the corresponding Provider ID, to simulate that the Provider submitted a proof + ::get_provider_id(*block_number + 1) + .map(|id| set.try_insert(id)); + Some(set) + } + + fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { + None + } + + fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = StorageProviders; + type RuntimeHoldReason = RuntimeHoldReason; + type Units = StorageUnit; + type NewStreamDeposit = ConstU64<10>; + type UserWithoutFundsCooldown = ConstU64<100>; + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = MockSubmittingProviders; +} + +// Build genesis storage according to the mock runtime. +pub fn _new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +// Externalities builder with predefined balances for accounts and starting at block number 1 +pub struct ExtBuilder; +impl ExtBuilder { + pub fn build() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (0, 5_000_000), // Alice = 0 + (1, 10_000_000), // Bob = 1 + (2, 20_000_000), // Charlie = 2 + (3, 30_000_000), // David = 3 + (4, 400_000_000), // Eve = 4 + (5, 5_000_000_000), // Ferdie = 5 + (6, 600_000_000_000), // George = 6 + (123, 5_000_000), // Alice for `on_poll` testing = 123 + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + crate::GenesisConfig:: { current_price: 1 } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + pallet_payment_streams::OnPollTicker::::set(1); + }); + ext + } +} diff --git a/pallets/proofs-dealer/src/lib.rs b/pallets/proofs-dealer/src/lib.rs index 36c9e9156..4fea595d5 100644 --- a/pallets/proofs-dealer/src/lib.rs +++ b/pallets/proofs-dealer/src/lib.rs @@ -1,744 +1,744 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -// TODO #[cfg(feature = "runtime-benchmarks")] -// TODO mod benchmarking; -pub mod types; -pub mod utils; - -#[frame_support::pallet] -pub mod pallet { - use codec::FullCodec; - use frame_support::{ - dispatch::DispatchResultWithPostInfo, - pallet_prelude::{ValueQuery, *}, - sp_runtime::traits::{CheckEqual, Hash, MaybeDisplay, SimpleBitOps}, - traits::{fungible, Randomness}, - }; - use frame_system::pallet_prelude::*; - use scale_info::prelude::fmt::Debug; - use shp_traits::{ - CommitmentVerifier, MutateChallengeableProvidersInterface, ProofsDealerInterface, - ReadChallengeableProvidersInterface, TrieProofDeltaApplier, TrieRemoveMutation, - }; - use sp_runtime::{ - traits::{CheckedSub, Convert, Saturating}, - Perbill, - }; - use sp_std::vec::Vec; - use types::{KeyFor, ProviderIdFor}; - - use crate::types::*; - use crate::*; - - #[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>; - - /// The Providers pallet. - /// To check if whoever submits a proof is a registered Provider. - type ProvidersPallet: ReadChallengeableProvidersInterface< - AccountId = Self::AccountId, - MerkleHash = Self::MerkleTrieHash, - Balance = Self::NativeBalance, - > + MutateChallengeableProvidersInterface::ProviderId, MerkleHash = Self::MerkleTrieHash>; - - /// The type used to verify Merkle Patricia Forest proofs. - /// This verifies proofs of keys belonging to the Merkle Patricia Forest. - /// Something that implements the [`CommitmentVerifier`] trait. - /// The type of the challenge is a hash, and it is expected that a proof will provide the - /// exact hash if it exists in the forest, or the previous and next hashes if it does not. - type ForestVerifier: CommitmentVerifier, Challenge = KeyFor> - + TrieProofDeltaApplier< - Self::MerkleTrieHashing, - Key = KeyFor, - Proof = ForestVerifierProofFor, - >; - - /// The type used to verify the proof of a specific key within the Merkle Patricia Forest. - /// While [`Config::ForestVerifier`] verifies that some keys are in the Merkle Patricia Forest, this - /// verifies specifically a proof for that key. For example, if the keys in the forest - /// represent files, this would verify the proof for a specific file, and [`Config::ForestVerifier`] - /// would verify that the file is in the forest. - /// The type of the challenge is a `[u8; 8]` that actually represents a u64 number, which is - /// the index of the chunk being challenged. - type KeyVerifier: CommitmentVerifier, Challenge = KeyFor>; - - /// Type to access the Balances Pallet. - type NativeBalance: fungible::Inspect - + fungible::Mutate - + fungible::hold::Inspect - + fungible::hold::Mutate; - - /// Type to access source of randomness. - type RandomnessProvider: Randomness>; - - /// The type for the hashes of Merkle Patricia Forest nodes. - /// Applies to keys (leaf nodes) and root hashes (root nodes). - /// Generally a hash (the output of a Hasher). - type MerkleTrieHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// The hashing system (algorithm) being used for the Merkle Patricia Forests (e.g. Blake2). - type MerkleTrieHashing: Hash + TypeInfo; - - /// The type to convert a balance to a block number. - type StakeToBlockNumber: Convert, BlockNumberFor>; - - /// The number of random challenges that are generated per block, using the random seed - /// generated for that block. - #[pallet::constant] - type RandomChallengesPerBlock: Get; - - /// The maximum number of custom challenges that can be made in a single checkpoint block. - #[pallet::constant] - type MaxCustomChallengesPerBlock: Get; - - /// The number of ticks that challenges history is kept for. - /// After this many ticks, challenges are removed from [`TickToChallengesSeed`] StorageMap. - /// A "tick" is usually one block, but some blocks may be skipped due to migrations. - #[pallet::constant] - type ChallengeHistoryLength: Get>; - - /// The length of the `ChallengesQueue` StorageValue. - /// This is to limit the size of the queue, and therefore the number of - /// manual challenges that can be made. - #[pallet::constant] - type ChallengesQueueLength: Get; - - /// The number of blocks in between a checkpoint challenges round (i.e. with custom challenges). - /// This is used to determine when to include the challenges from the `ChallengesQueue` and - /// `PriorityChallengesQueue` in the `BlockToChallenges` StorageMap. These checkpoint challenge - /// rounds have to be answered by ALL Providers, and this is enforced by the `submit_proof` - /// extrinsic. - /// - /// WARNING: This period needs to be equal or larger than the challenge period of the smallest - /// Provider in the network. If the smallest Provider has a challenge period of 10 ticks (blocks), - /// then the checkpoint challenge period needs to be at least 10 ticks. - #[pallet::constant] - type CheckpointChallengePeriod: Get>; - - /// The ratio to convert staked balance to block period. - /// This is used to determine the period in which a Provider should submit a proof, based on - /// their stake. The period is calculated as `StakeToChallengePeriod / stake`, saturating at [`Config::MinChallengePeriod`]. - #[pallet::constant] - type StakeToChallengePeriod: Get>; - - /// The minimum period in which a Provider can be challenged, regardless of their stake. - #[pallet::constant] - type MinChallengePeriod: Get>; - - /// The tolerance in number of ticks (almost equivalent to blocks, but skipping MBM) that - /// a Provider has to submit a proof, counting from the tick the challenge is emitted for - /// that Provider. - /// - /// For example, if a Provider is supposed to submit a proof for tick `n`, and the tolerance - /// is set to `t`, then the Provider has to submit a proof for challenges in tick `n`, before - /// `n + t`. - #[pallet::constant] - type ChallengeTicksTolerance: Get>; - - /// The fee charged for submitting a challenge. - /// This fee goes to the Treasury, and is used to prevent spam. Registered Providers are - /// exempt from this fee. - #[pallet::constant] - type ChallengesFee: Get>; - - /// The target number of ticks for which to store the submitters that submitted valid proofs in them, - /// stored in the `ValidProofSubmittersLastTicks` StorageMap. That storage will be trimmed down to this number - /// of ticks in the `on_idle` hook of this pallet, to avoid bloating the state. - #[pallet::constant] - type TargetTicksStorageOfSubmitters: Get; - - /// The maximum amount of Providers that can submit a proof in a single block. - /// Although this can be seen as an arbitrary limit, if set to the already existing - /// implicit limit that is "how many `submit_proof` extrinsics fit in the weight of - /// a block, this wouldn't add any additional artificial limit. - #[pallet::constant] - type MaxSubmittersPerTick: Get; - - /// The Treasury AccountId. - /// The account to which: - /// - The fees for submitting a challenge are transferred. - /// - The slashed funds are transferred. - #[pallet::constant] - type Treasury: Get; - - /// The period of blocks for which the block fullness is checked. - /// - /// This is the amount of blocks from the past, for which the block fullness has been checked - /// and is stored. Blocks older than `current_block` - [`Config::BlockFullnessPeriod`] are - /// cleared from storage. - /// - /// This constant should be equal or smaller than the [`Config::ChallengeTicksTolerance`] constant, - /// if the goal is to prevent spamming attacks that would prevent honest Providers from submitting - /// their proofs in time. - #[pallet::constant] - type BlockFullnessPeriod: Get>; - - /// The minimum unused weight that a block must have to be considered _not_ full. - /// - /// This is used as part of the criteria for checking if the network is presumably under a spam attack. - /// For example, this can be set to the benchmarked weight of a `submit_proof` extrinsic, which would - /// mean that a block is not considered full if a `submit_proof` extrinsic could have still fit in it. - #[pallet::constant] - type BlockFullnessHeadroom: Get; - - /// The minimum ratio (or percentage if you will) of blocks that must be considered _not_ full, - /// from the total number of [`Config::BlockFullnessPeriod`] blocks taken into account. - /// - /// If less than this percentage of blocks are not full, the networks is considered to be presumably - /// under a spam attack. - /// This can also be thought of as the maximum ratio of misbehaving collators tolerated. For example, - /// if this is set to `Perbill::from_percent(50)`, then if more than half of the last `BlockFullnessPeriod` - /// blocks are not full, then one of those blocks surely was produced by an honest collator, meaning - /// that there was at least one truly _not_ full block in the last `BlockFullnessPeriod` blocks. - #[pallet::constant] - type MinNotFullBlocksRatio: Get; - } - - #[pallet::pallet] - pub struct Pallet(_); - - /// A mapping from challenges tick to a random seed used for generating the challenges in that tick. - /// - /// This is used to keep track of the challenges' seed in the past. - /// This mapping goes back only [`ChallengeHistoryLengthFor`] blocks. Previous challenges are removed. - #[pallet::storage] - pub type TickToChallengesSeed = - StorageMap<_, Blake2_128Concat, BlockNumberFor, RandomnessOutputFor>; - - /// A mapping from challenges tick to a vector of custom challenged keys for that tick. - /// - /// This is used to keep track of the challenges that have been made in the past, specifically - /// in the checkpoint challenge rounds. - /// The vector is bounded by [`MaxCustomChallengesPerBlockFor`]. - /// This mapping goes back only [`ChallengeHistoryLengthFor`] ticks. Previous challenges are removed. - #[pallet::storage] - pub type TickToCheckpointChallenges = StorageMap< - _, - Blake2_128Concat, - BlockNumberFor, - BoundedVec<(KeyFor, Option), MaxCustomChallengesPerBlockFor>, - >; - - /// The challenge tick of the last checkpoint challenge round. - /// - /// This is used to determine when to include the challenges from the [`ChallengesQueue`] and - /// [`PriorityChallengesQueue`] in the [`TickToCheckpointChallenges`] StorageMap. These checkpoint - /// challenge rounds have to be answered by ALL Providers, and this is enforced by the - /// `submit_proof` extrinsic. - #[pallet::storage] - pub type LastCheckpointTick = StorageValue<_, BlockNumberFor, ValueQuery>; - - /// A mapping from challenge tick to a vector of challenged Providers for that tick. - /// - /// This is used to keep track of the Providers that have been challenged, and should - /// submit a proof by the time of the [`ChallengesTicker`] reaches the number used as - /// key in the mapping. Providers who do submit a proof are removed from their respective - /// entry and pushed forward to the next tick in which they should submit a proof. - /// Those who are still in the entry by the time the tick is reached are considered to - /// have failed to submit a proof and subject to slashing. - #[pallet::storage] - pub type TickToProvidersDeadlines = StorageDoubleMap< - _, - Blake2_128Concat, - BlockNumberFor, - Blake2_128Concat, - ProviderIdFor, - (), - >; - - /// A mapping from a Provider to the last tick for which they SHOULD have submitted a proof. - /// If for a Provider `p`, `LastTickProviderSubmittedAProofFor[p]` is `n`, then the - /// Provider should submit a proof for tick `n + stake_to_challenge_period(p)`. - /// - /// This gets updated when a Provider submits a proof successfully and is used to determine the - /// next tick for which the Provider should submit a proof, and it's deadline. - /// - /// If the Provider fails to submit a proof in time and is slashed, this will still get updated - /// to the tick it should have submitted a proof for. - #[pallet::storage] - pub type LastTickProviderSubmittedAProofFor = - StorageMap<_, Blake2_128Concat, ProviderIdFor, BlockNumberFor>; - - /// A queue of keys that have been challenged manually. - /// - /// The elements in this queue will be challenged in the coming blocks, - /// always ensuring that the maximum number of challenges per block is not exceeded. - /// A `BoundedVec` is used because the `parity_scale_codec::MaxEncodedLen` trait - /// is required, but using a `VecDeque` would be more efficient as this is a FIFO queue. - #[pallet::storage] - pub type ChallengesQueue = - StorageValue<_, BoundedVec, ChallengesQueueLengthFor>, ValueQuery>; - - /// A priority queue of keys that have been challenged manually. - /// - /// The difference between this and `ChallengesQueue` is that the challenges - /// in this queue are given priority over the others. So this queue should be - /// emptied before any of the challenges in the `ChallengesQueue` are dispatched. - /// This queue should not be accessible to the public. - /// The elements in this queue will be challenged in the coming blocks, - /// always ensuring that the maximum number of challenges per block is not exceeded. - /// A `BoundedVec` is used because the `parity_scale_codec::MaxEncodedLen` trait - /// is required, but using a `VecDeque` would be more efficient as this is a FIFO queue. - #[pallet::storage] - pub type PriorityChallengesQueue = StorageValue< - _, - BoundedVec<(KeyFor, Option), ChallengesQueueLengthFor>, - ValueQuery, - >; - - /// A counter of blocks in which challenges were distributed. - /// - /// This counter is not necessarily the same as the block number, as challenges are - /// distributed in the `on_poll` hook, which happens at the beginning of every block, - /// so long as the block is not part of a [Multi-Block-Migration](https://github.com/paritytech/polkadot-sdk/pull/1781) (MBM). - /// During MBMsm, the block number increases, but [`ChallengesTicker`] does not. - #[pallet::storage] - pub type ChallengesTicker = StorageValue<_, BlockNumberFor, ValueQuery>; - - #[pallet::storage] - pub type SlashableProviders = StorageMap<_, Blake2_128Concat, ProviderIdFor, u32>; - - /// A mapping from tick to Providers, which is set if the Provider submitted a valid proof in that tick. - /// - /// This is used to keep track of the Providers that have submitted proofs in the last few - /// ticks, where availability only up to the last [`Config::TargetTicksStorageOfSubmitters`] ticks is guaranteed. - /// This storage is then made available for other pallets to use through the `ProofSubmittersInterface`. - #[pallet::storage] - #[pallet::getter(fn valid_proof_submitters_last_ticks)] - pub type ValidProofSubmittersLastTicks = StorageMap< - _, - Blake2_128Concat, - BlockNumberFor, - BoundedBTreeSet, T::MaxSubmittersPerTick>, - >; - - /// A value that represents the last tick that was deleted from the [`ValidProofSubmittersLastTicks`] StorageMap. - /// - /// This is used to know which tick to delete from the [`ValidProofSubmittersLastTicks`] StorageMap when the - /// `on_idle` hook is called. - #[pallet::storage] - #[pallet::getter(fn last_deleted_tick)] - pub type LastDeletedTick = StorageValue<_, BlockNumberFor, ValueQuery>; - - /// A boolean that represents whether the [`ChallengesTicker`] is paused. - /// - /// By default, this is `false`, meaning that the [`ChallengesTicker`] is incremented every time `on_poll` is called. - /// This can be set to `true` which would pause the [`ChallengesTicker`], preventing `do_new_challenges_round` from - /// being executed. Therefore: - /// - No new random challenges would be emitted and added to [`TickToChallengesSeed`]. - /// - No new checkpoint challenges would be emitted and added to [`TickToCheckpointChallenges`]. - /// - Deadlines for proof submissions are indefinitely postponed. - #[pallet::storage] - #[pallet::getter(fn challenges_ticker_paused)] - pub type ChallengesTickerPaused = StorageValue<_, ()>; - - /// A mapping from block number to the weight used in that block. - /// - /// This is used to check if the network is presumably under a spam attack. - /// It is cleared for blocks older than `current_block` - ([`Config::BlockFullnessPeriod`] + 1). - #[pallet::storage] - #[pallet::getter(fn past_blocks_fullness)] - pub type PastBlocksWeight = - StorageMap<_, Blake2_128Concat, BlockNumberFor, Weight>; - - /// The number of blocks that have been considered _not_ full in the last [`Config::BlockFullnessPeriod`]. - /// - /// This is used to check if the network is presumably under a spam attack. - #[pallet::storage] - #[pallet::getter(fn not_full_blocks_count)] - pub type NotFullBlocksCount = StorageValue<_, BlockNumberFor, ValueQuery>; - - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/v3/runtime/events-and-errors - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// A manual challenge was submitted. - NewChallenge { - who: AccountIdFor, - key_challenged: KeyFor, - }, - - /// A proof was accepted. - ProofAccepted { - provider: ProviderIdFor, - proof: Proof, - }, - - /// A new challenge seed was generated. - NewChallengeSeed { - challenges_ticker: BlockNumberFor, - seed: RandomnessOutputFor, - }, - - /// A new checkpoint challenge was generated. - NewCheckpointChallenge { - challenges_ticker: BlockNumberFor, - challenges: BoundedVec< - (KeyFor, Option), - MaxCustomChallengesPerBlockFor, - >, - }, - - /// A provider was marked as slashable and their challenge deadline was forcefully pushed. - SlashableProvider { - provider: ProviderIdFor, - next_challenge_deadline: BlockNumberFor, - }, - - /// No record of the last tick the Provider submitted a proof for. - NoRecordOfLastSubmittedProof { provider: ProviderIdFor }, - - /// A provider's challenge cycle was initialised. - NewChallengeCycleInitialised { - current_tick: BlockNumberFor, - next_challenge_deadline: BlockNumberFor, - provider: ProviderIdFor, - maybe_provider_account: Option, - }, - - /// A set of mutations has been applied to the Forest. - MutationsApplied { - provider: ProviderIdFor, - mutations: Vec<(KeyFor, TrieRemoveMutation)>, - new_root: KeyFor, - }, - - /// The [`ChallengesTicker`] has been paused or unpaused. - ChallengesTickerSet { paused: bool }, - } - - // Errors inform users that something went wrong. - #[pallet::error] - pub enum Error { - /// General errors - - /// The proof submitter is not a registered Provider. - NotProvider, - - /// `challenge` extrinsic errors - - /// The ChallengesQueue is full. No more manual challenges can be made - /// until some of the challenges in the queue are dispatched. - ChallengesQueueOverflow, - - /// The PriorityChallengesQueue is full. No more priority challenges can be made - /// until some of the challenges in the queue are dispatched. - PriorityChallengesQueueOverflow, - - /// The fee for submitting a challenge could not be charged. - FeeChargeFailed, - - /// `submit_proof` extrinsic errors - - /// There are no key proofs submitted. - EmptyKeyProofs, - - /// The root for the Provider could not be found. - ProviderRootNotFound, - - /// Provider is submitting a proof when they have a zero root. - /// Providers with zero roots are not providing any service, so they should not be - /// submitting proofs. - ZeroRoot, - - /// Provider is submitting a proof but there is no record of the last tick they - /// submitted a proof for. - /// Providers who are required to submit proofs should always have a record of the - /// last tick they submitted a proof for, otherwise it means they haven't started - /// providing service for any user yet. - NoRecordOfLastSubmittedProof, - - /// The provider stake could not be found. - ProviderStakeNotFound, - - /// Provider is submitting a proof but their stake is zero. - ZeroStake, - - /// The staked balance of the Provider could not be converted to `u128`. - /// This should not be possible, as the `Balance` type should be an unsigned integer type. - StakeCouldNotBeConverted, - - /// Provider is submitting a proof for a tick in the future. - ChallengesTickNotReached, - - /// Provider is submitting a proof for a tick before the last tick this pallet registers - /// challenges for. - ChallengesTickTooOld, - - /// Provider is submitting a proof for a tick too late, i.e. that the challenges tick - /// is greater or equal than `challenges_tick` + `T::ChallengeTicksTolerance::get()`. - ChallengesTickTooLate, - - /// The seed for the tick could not be found. - /// This should not be possible for a tick within the `ChallengeHistoryLength` range, as - /// seeds are generated for all ticks, and stored within this range. - SeedNotFound, - - /// Checkpoint challenges not found in block. - /// This should only be possible if `TickToCheckpointChallenges` is dereferenced for a tick - /// that is not a checkpoint tick. - CheckpointChallengesNotFound, - - /// The forest proof submitted by the Provider is invalid. - /// This could be because the proof is not valid for the root, or because the proof is - /// not sufficient for the challenges made. - ForestProofVerificationFailed, - - /// There is at least one key proven in the forest proof, that does not have a corresponding - /// key proof. - KeyProofNotFound, - - /// A key proof submitted by the Provider is invalid. - /// This could be because the proof is not valid for the root of that key, or because the proof - /// is not sufficient for the challenges made. - KeyProofVerificationFailed, - - /// Failed to apply delta to the forest proof partial trie. - FailedToApplyDelta, - - /// Failed to update the provider after a key removal mutation. - FailedToUpdateProviderAfterKeyRemoval, - - /// The limit of Providers that can submit a proof in a single tick has been reached. - TooManyValidProofSubmitters, - } - - #[pallet::call] - impl Pallet { - /// Introduce a new challenge. - /// - /// This function allows anyone to add a new challenge to the `ChallengesQueue`. - /// The challenge will be dispatched in the coming blocks. - /// Users are charged a small fee for submitting a challenge, which - /// goes to the Treasury. - #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn challenge(origin: OriginFor, key: KeyFor) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - Self::do_challenge(&who, &key)?; - - // Emit event. - Self::deposit_event(Event::NewChallenge { - who, - key_challenged: key, - }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// For a Provider to submit a proof. - /// - /// Checks that `provider` is a registered Provider. If none - /// is provided, the proof submitter is considered to be the Provider. - /// Relies on a Providers pallet to get the root for the Provider. - /// Validates that the proof corresponds to a challenge that was made in the past, - /// by checking the `TickToChallengesSeed` StorageMap. The challenge tick that the - /// Provider should have submitted a proof is calculated based on the last tick they - /// submitted a proof for ([`LastTickProviderSubmittedAProofFor`]), and the proving period for - /// that Provider, which is a function of their stake. - /// This extrinsic also checks that there hasn't been a checkpoint challenge round - /// in between the last time the Provider submitted a proof for and the tick - /// for which the proof is being submitted. If there has been, the Provider is - /// subject to slashing. - /// - /// If valid: - /// - Pushes forward the Provider in the [`TickToProvidersDeadlines`] StorageMap a number - /// of ticks corresponding to the stake of the Provider. - /// - Registers this tick as the last tick in which the Provider submitted a proof. - /// - /// Execution of this extrinsic should be refunded if the proof is valid. - #[pallet::call_index(1)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn submit_proof( - origin: OriginFor, - proof: Proof, - provider: Option>, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Getting provider from the origin if none is provided. - let provider = match provider { - Some(provider) => provider, - None => { - let sp = T::ProvidersPallet::get_provider_id(who.clone()) - .ok_or(Error::::NotProvider)?; - sp - } - }; - - Self::do_submit_proof(&provider, &proof)?; - - // Emit event. - Self::deposit_event(Event::ProofAccepted { provider, proof }); - - // Return a successful DispatchResultWithPostInfo. - // If the proof is valid, the execution of this extrinsic should be refunded. - Ok(Pays::No.into()) - } - - /// Initialise a Provider's challenge cycle. - /// - /// Only callable by sudo. - /// - /// Sets the last tick the Provider submitted a proof for to the current tick, and sets the - /// deadline for submitting a proof to the current tick + the Provider's period + the tolerance. - #[pallet::call_index(2)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn force_initialise_challenge_cycle( - origin: OriginFor, - provider: ProviderIdFor, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was executed by the root origin. - ensure_root(origin)?; - - // Execute checks and logic, update storage. - ::initialise_challenge_cycle(&provider)?; - - // Return a successful DispatchResultWithPostInfo. - Ok(Pays::No.into()) - } - - /// Set the [`ChallengesTickerPaused`] to `true` or `false`. - /// - /// Only callable by sudo. - #[pallet::call_index(3)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn set_paused(origin: OriginFor, paused: bool) -> DispatchResultWithPostInfo { - // Check that the extrinsic was executed by the root origin. - ensure_root(origin)?; - - if paused { - ChallengesTickerPaused::::set(Some(())); - } else { - ChallengesTickerPaused::::set(None); - } - - // Emit the corresponding event. - Self::deposit_event(Event::::ChallengesTickerSet { paused }); - - // Return a successful DispatchResultWithPostInfo. - Ok(Pays::No.into()) - } - } - - #[pallet::hooks] - impl Hooks> for Pallet { - /// This hook is used to generate new challenges. - /// - /// It will be called at the beginning of every block, if the block is not being part of a - /// [Multi-Block-Migration](https://github.com/paritytech/polkadot-sdk/pull/1781) (MBM). - /// For more information on the lifecycle of the block and its hooks, see the [Substrate - /// documentation](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.on_poll). - fn on_poll(_n: BlockNumberFor, weight: &mut frame_support::weights::WeightMeter) { - // TODO: Benchmark computational weight cost of this hook. - - // Only execute the `do_new_challenges_round` if the `ChallengesTicker` is not paused. - if ChallengesTickerPaused::::get().is_none() { - Self::do_new_challenges_round(weight); - } - - // Check if the network is presumably under a spam attack. - // If so, `ChallengesTicker` will be paused. - // This check is done "a posteriori", meaning that we first increment the `ChallengesTicker`, send out challenges - // and slash Providers if in the last block we didn't consider the network to be under spam. - // Then if at this block we consider the network to be under spam, we pause the `ChallengesTicker`, which will not - // be incremented in the next block. - Self::do_check_spamming_condition(weight); - } - - /// This hook is called on block initialization and returns the Weight of the `on_finalize` hook to - /// let block builders know how much weight to reserve for it - /// TODO: Benchmark on_finalize to get its weight and replace the placeholder weight for that - fn on_initialize(_n: BlockNumberFor) -> Weight { - Weight::from_parts(10_000, 0) + T::DbWeight::get().reads_writes(0, 2) - } - - fn on_finalize(block_number: BlockNumberFor) { - // Get weight usage in this block so far, for the dispatch class of `submit_proof` extrinsics. - let weight_used = frame_system::Pallet::::block_weight(); - let weight_used_for_class = weight_used.get(DispatchClass::Normal); - - // Store the weight usage in this block. - PastBlocksWeight::::insert(block_number, weight_used_for_class); - - // Get the oldest block weight registered. - let block_fullness_period = T::BlockFullnessPeriod::get(); - - // Clear the storage for block at `current_block` - (`BlockFullnessPeriod` + 1). - if let Some(oldest_block_fullness_number) = - block_number.checked_sub(&block_fullness_period.saturating_add(1u32.into())) - { - // If it is older than `BlockFullnessPeriod` + 1, we clear the storage. - PastBlocksWeight::::remove(oldest_block_fullness_number); - } - } - - /// This integrity test checks that: - /// 1. `CheckpointChallengePeriod` is greater or equal to the longest period a Provider can have. - /// 2. `BlockFullnessPeriod` is smaller or equal than `ChallengeTicksTolerance`. - /// - /// Any code located in this hook is placed in an auto-generated test, and generated as a part - /// of crate::construct_runtime's expansion. - /// Look for a test case with a name along the lines of: __construct_runtime_integrity_test. - fn integrity_test() { - // Calculate longest period a Provider can have. - // That would be the period of the Provider with the minimum stake. - let min_stake = T::ProvidersPallet::get_min_stake(); - let max_period = Self::stake_to_challenge_period(min_stake); - - // Check that `CheckpointChallengePeriod` is greater or equal to the longest period a Provider can have. - assert!( - T::CheckpointChallengePeriod::get() >= max_period, - "CheckpointChallengePeriod ({:?}) const in ProofsDealer pallet should be greater or equal than the longest period a Provider can have ({:?}).", - T::CheckpointChallengePeriod::get(), - max_period - ); - - // Check that `BlockFullnessPeriod` is smaller or equal than `ChallengeTicksTolerance`. - assert!( - T::BlockFullnessPeriod::get() <= T::ChallengeTicksTolerance::get(), - "BlockFullnessPeriod const ({:?}) in ProofsDealer pallet should be smaller or equal than ChallengeTicksTolerance ({:?}).", - T::BlockFullnessPeriod::get(), - T::ChallengeTicksTolerance::get() - ); - } - - /// This hook is used to trim down the `ValidProofSubmittersLastTicks` StorageMap up to the `TargetTicksOfProofsStorage`. - /// - /// It runs when the block is being finalized (but before the `on_finalize` hook) and can consume all remaining weight. - /// It returns the used weight, so it can be used to calculate the remaining weight for the block for any other - /// pallets that have `on_idle` hooks. - fn on_idle(n: BlockNumberFor, weight: Weight) -> Weight { - // TODO: Benchmark computational and proof size weight cost of this hook. - Self::do_trim_valid_proof_submitters_last_ticks(n, weight) - } - } -} +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +// TODO #[cfg(feature = "runtime-benchmarks")] +// TODO mod benchmarking; +pub mod types; +pub mod utils; + +#[frame_support::pallet] +pub mod pallet { + use codec::FullCodec; + use frame_support::{ + dispatch::DispatchResultWithPostInfo, + pallet_prelude::{ValueQuery, *}, + sp_runtime::traits::{CheckEqual, Hash, MaybeDisplay, SimpleBitOps}, + traits::{fungible, Randomness}, + }; + use frame_system::pallet_prelude::*; + use scale_info::prelude::fmt::Debug; + use shp_traits::{ + CommitmentVerifier, MutateChallengeableProvidersInterface, ProofsDealerInterface, + ReadChallengeableProvidersInterface, TrieProofDeltaApplier, TrieRemoveMutation, + }; + use sp_runtime::{ + traits::{CheckedSub, Convert, Saturating}, + Perbill, + }; + use sp_std::vec::Vec; + use types::{KeyFor, ProviderIdFor}; + + use crate::types::*; + use crate::*; + + #[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>; + + /// The Providers pallet. + /// To check if whoever submits a proof is a registered Provider. + type ProvidersPallet: ReadChallengeableProvidersInterface< + AccountId = Self::AccountId, + MerkleHash = Self::MerkleTrieHash, + Balance = Self::NativeBalance, + > + MutateChallengeableProvidersInterface::ProviderId, MerkleHash = Self::MerkleTrieHash>; + + /// The type used to verify Merkle Patricia Forest proofs. + /// This verifies proofs of keys belonging to the Merkle Patricia Forest. + /// Something that implements the [`CommitmentVerifier`] trait. + /// The type of the challenge is a hash, and it is expected that a proof will provide the + /// exact hash if it exists in the forest, or the previous and next hashes if it does not. + type ForestVerifier: CommitmentVerifier, Challenge = KeyFor> + + TrieProofDeltaApplier< + Self::MerkleTrieHashing, + Key = KeyFor, + Proof = ForestVerifierProofFor, + >; + + /// The type used to verify the proof of a specific key within the Merkle Patricia Forest. + /// While [`Config::ForestVerifier`] verifies that some keys are in the Merkle Patricia Forest, this + /// verifies specifically a proof for that key. For example, if the keys in the forest + /// represent files, this would verify the proof for a specific file, and [`Config::ForestVerifier`] + /// would verify that the file is in the forest. + /// The type of the challenge is a `[u8; 8]` that actually represents a u64 number, which is + /// the index of the chunk being challenged. + type KeyVerifier: CommitmentVerifier, Challenge = KeyFor>; + + /// Type to access the Balances Pallet. + type NativeBalance: fungible::Inspect + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate; + + /// Type to access source of randomness. + type RandomnessProvider: Randomness>; + + /// The type for the hashes of Merkle Patricia Forest nodes. + /// Applies to keys (leaf nodes) and root hashes (root nodes). + /// Generally a hash (the output of a Hasher). + type MerkleTrieHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// The hashing system (algorithm) being used for the Merkle Patricia Forests (e.g. Blake2). + type MerkleTrieHashing: Hash + TypeInfo; + + /// The type to convert a balance to a block number. + type StakeToBlockNumber: Convert, BlockNumberFor>; + + /// The number of random challenges that are generated per block, using the random seed + /// generated for that block. + #[pallet::constant] + type RandomChallengesPerBlock: Get; + + /// The maximum number of custom challenges that can be made in a single checkpoint block. + #[pallet::constant] + type MaxCustomChallengesPerBlock: Get; + + /// The number of ticks that challenges history is kept for. + /// After this many ticks, challenges are removed from [`TickToChallengesSeed`] StorageMap. + /// A "tick" is usually one block, but some blocks may be skipped due to migrations. + #[pallet::constant] + type ChallengeHistoryLength: Get>; + + /// The length of the `ChallengesQueue` StorageValue. + /// This is to limit the size of the queue, and therefore the number of + /// manual challenges that can be made. + #[pallet::constant] + type ChallengesQueueLength: Get; + + /// The number of blocks in between a checkpoint challenges round (i.e. with custom challenges). + /// This is used to determine when to include the challenges from the `ChallengesQueue` and + /// `PriorityChallengesQueue` in the `BlockToChallenges` StorageMap. These checkpoint challenge + /// rounds have to be answered by ALL Providers, and this is enforced by the `submit_proof` + /// extrinsic. + /// + /// WARNING: This period needs to be equal or larger than the challenge period of the smallest + /// Provider in the network. If the smallest Provider has a challenge period of 10 ticks (blocks), + /// then the checkpoint challenge period needs to be at least 10 ticks. + #[pallet::constant] + type CheckpointChallengePeriod: Get>; + + /// The ratio to convert staked balance to block period. + /// This is used to determine the period in which a Provider should submit a proof, based on + /// their stake. The period is calculated as `StakeToChallengePeriod / stake`, saturating at [`Config::MinChallengePeriod`]. + #[pallet::constant] + type StakeToChallengePeriod: Get>; + + /// The minimum period in which a Provider can be challenged, regardless of their stake. + #[pallet::constant] + type MinChallengePeriod: Get>; + + /// The tolerance in number of ticks (almost equivalent to blocks, but skipping MBM) that + /// a Provider has to submit a proof, counting from the tick the challenge is emitted for + /// that Provider. + /// + /// For example, if a Provider is supposed to submit a proof for tick `n`, and the tolerance + /// is set to `t`, then the Provider has to submit a proof for challenges in tick `n`, before + /// `n + t`. + #[pallet::constant] + type ChallengeTicksTolerance: Get>; + + /// The fee charged for submitting a challenge. + /// This fee goes to the Treasury, and is used to prevent spam. Registered Providers are + /// exempt from this fee. + #[pallet::constant] + type ChallengesFee: Get>; + + /// The target number of ticks for which to store the submitters that submitted valid proofs in them, + /// stored in the `ValidProofSubmittersLastTicks` StorageMap. That storage will be trimmed down to this number + /// of ticks in the `on_idle` hook of this pallet, to avoid bloating the state. + #[pallet::constant] + type TargetTicksStorageOfSubmitters: Get; + + /// The maximum amount of Providers that can submit a proof in a single block. + /// Although this can be seen as an arbitrary limit, if set to the already existing + /// implicit limit that is "how many `submit_proof` extrinsics fit in the weight of + /// a block, this wouldn't add any additional artificial limit. + #[pallet::constant] + type MaxSubmittersPerTick: Get; + + /// The Treasury AccountId. + /// The account to which: + /// - The fees for submitting a challenge are transferred. + /// - The slashed funds are transferred. + #[pallet::constant] + type Treasury: Get; + + /// The period of blocks for which the block fullness is checked. + /// + /// This is the amount of blocks from the past, for which the block fullness has been checked + /// and is stored. Blocks older than `current_block` - [`Config::BlockFullnessPeriod`] are + /// cleared from storage. + /// + /// This constant should be equal or smaller than the [`Config::ChallengeTicksTolerance`] constant, + /// if the goal is to prevent spamming attacks that would prevent honest Providers from submitting + /// their proofs in time. + #[pallet::constant] + type BlockFullnessPeriod: Get>; + + /// The minimum unused weight that a block must have to be considered _not_ full. + /// + /// This is used as part of the criteria for checking if the network is presumably under a spam attack. + /// For example, this can be set to the benchmarked weight of a `submit_proof` extrinsic, which would + /// mean that a block is not considered full if a `submit_proof` extrinsic could have still fit in it. + #[pallet::constant] + type BlockFullnessHeadroom: Get; + + /// The minimum ratio (or percentage if you will) of blocks that must be considered _not_ full, + /// from the total number of [`Config::BlockFullnessPeriod`] blocks taken into account. + /// + /// If less than this percentage of blocks are not full, the networks is considered to be presumably + /// under a spam attack. + /// This can also be thought of as the maximum ratio of misbehaving collators tolerated. For example, + /// if this is set to `Perbill::from_percent(50)`, then if more than half of the last `BlockFullnessPeriod` + /// blocks are not full, then one of those blocks surely was produced by an honest collator, meaning + /// that there was at least one truly _not_ full block in the last `BlockFullnessPeriod` blocks. + #[pallet::constant] + type MinNotFullBlocksRatio: Get; + } + + #[pallet::pallet] + pub struct Pallet(_); + + /// A mapping from challenges tick to a random seed used for generating the challenges in that tick. + /// + /// This is used to keep track of the challenges' seed in the past. + /// This mapping goes back only [`ChallengeHistoryLengthFor`] blocks. Previous challenges are removed. + #[pallet::storage] + pub type TickToChallengesSeed = + StorageMap<_, Blake2_128Concat, BlockNumberFor, RandomnessOutputFor>; + + /// A mapping from challenges tick to a vector of custom challenged keys for that tick. + /// + /// This is used to keep track of the challenges that have been made in the past, specifically + /// in the checkpoint challenge rounds. + /// The vector is bounded by [`MaxCustomChallengesPerBlockFor`]. + /// This mapping goes back only [`ChallengeHistoryLengthFor`] ticks. Previous challenges are removed. + #[pallet::storage] + pub type TickToCheckpointChallenges = StorageMap< + _, + Blake2_128Concat, + BlockNumberFor, + BoundedVec<(KeyFor, Option), MaxCustomChallengesPerBlockFor>, + >; + + /// The challenge tick of the last checkpoint challenge round. + /// + /// This is used to determine when to include the challenges from the [`ChallengesQueue`] and + /// [`PriorityChallengesQueue`] in the [`TickToCheckpointChallenges`] StorageMap. These checkpoint + /// challenge rounds have to be answered by ALL Providers, and this is enforced by the + /// `submit_proof` extrinsic. + #[pallet::storage] + pub type LastCheckpointTick = StorageValue<_, BlockNumberFor, ValueQuery>; + + /// A mapping from challenge tick to a vector of challenged Providers for that tick. + /// + /// This is used to keep track of the Providers that have been challenged, and should + /// submit a proof by the time of the [`ChallengesTicker`] reaches the number used as + /// key in the mapping. Providers who do submit a proof are removed from their respective + /// entry and pushed forward to the next tick in which they should submit a proof. + /// Those who are still in the entry by the time the tick is reached are considered to + /// have failed to submit a proof and subject to slashing. + #[pallet::storage] + pub type TickToProvidersDeadlines = StorageDoubleMap< + _, + Blake2_128Concat, + BlockNumberFor, + Blake2_128Concat, + ProviderIdFor, + (), + >; + + /// A mapping from a Provider to the last tick for which they SHOULD have submitted a proof. + /// If for a Provider `p`, `LastTickProviderSubmittedAProofFor[p]` is `n`, then the + /// Provider should submit a proof for tick `n + stake_to_challenge_period(p)`. + /// + /// This gets updated when a Provider submits a proof successfully and is used to determine the + /// next tick for which the Provider should submit a proof, and it's deadline. + /// + /// If the Provider fails to submit a proof in time and is slashed, this will still get updated + /// to the tick it should have submitted a proof for. + #[pallet::storage] + pub type LastTickProviderSubmittedAProofFor = + StorageMap<_, Blake2_128Concat, ProviderIdFor, BlockNumberFor>; + + /// A queue of keys that have been challenged manually. + /// + /// The elements in this queue will be challenged in the coming blocks, + /// always ensuring that the maximum number of challenges per block is not exceeded. + /// A `BoundedVec` is used because the `parity_scale_codec::MaxEncodedLen` trait + /// is required, but using a `VecDeque` would be more efficient as this is a FIFO queue. + #[pallet::storage] + pub type ChallengesQueue = + StorageValue<_, BoundedVec, ChallengesQueueLengthFor>, ValueQuery>; + + /// A priority queue of keys that have been challenged manually. + /// + /// The difference between this and `ChallengesQueue` is that the challenges + /// in this queue are given priority over the others. So this queue should be + /// emptied before any of the challenges in the `ChallengesQueue` are dispatched. + /// This queue should not be accessible to the public. + /// The elements in this queue will be challenged in the coming blocks, + /// always ensuring that the maximum number of challenges per block is not exceeded. + /// A `BoundedVec` is used because the `parity_scale_codec::MaxEncodedLen` trait + /// is required, but using a `VecDeque` would be more efficient as this is a FIFO queue. + #[pallet::storage] + pub type PriorityChallengesQueue = StorageValue< + _, + BoundedVec<(KeyFor, Option), ChallengesQueueLengthFor>, + ValueQuery, + >; + + /// A counter of blocks in which challenges were distributed. + /// + /// This counter is not necessarily the same as the block number, as challenges are + /// distributed in the `on_poll` hook, which happens at the beginning of every block, + /// so long as the block is not part of a [Multi-Block-Migration](https://github.com/paritytech/polkadot-sdk/pull/1781) (MBM). + /// During MBMsm, the block number increases, but [`ChallengesTicker`] does not. + #[pallet::storage] + pub type ChallengesTicker = StorageValue<_, BlockNumberFor, ValueQuery>; + + #[pallet::storage] + pub type SlashableProviders = StorageMap<_, Blake2_128Concat, ProviderIdFor, u32>; + + /// A mapping from tick to Providers, which is set if the Provider submitted a valid proof in that tick. + /// + /// This is used to keep track of the Providers that have submitted proofs in the last few + /// ticks, where availability only up to the last [`Config::TargetTicksStorageOfSubmitters`] ticks is guaranteed. + /// This storage is then made available for other pallets to use through the `ProofSubmittersInterface`. + #[pallet::storage] + #[pallet::getter(fn valid_proof_submitters_last_ticks)] + pub type ValidProofSubmittersLastTicks = StorageMap< + _, + Blake2_128Concat, + BlockNumberFor, + BoundedBTreeSet, T::MaxSubmittersPerTick>, + >; + + /// A value that represents the last tick that was deleted from the [`ValidProofSubmittersLastTicks`] StorageMap. + /// + /// This is used to know which tick to delete from the [`ValidProofSubmittersLastTicks`] StorageMap when the + /// `on_idle` hook is called. + #[pallet::storage] + #[pallet::getter(fn last_deleted_tick)] + pub type LastDeletedTick = StorageValue<_, BlockNumberFor, ValueQuery>; + + /// A boolean that represents whether the [`ChallengesTicker`] is paused. + /// + /// By default, this is `false`, meaning that the [`ChallengesTicker`] is incremented every time `on_poll` is called. + /// This can be set to `true` which would pause the [`ChallengesTicker`], preventing `do_new_challenges_round` from + /// being executed. Therefore: + /// - No new random challenges would be emitted and added to [`TickToChallengesSeed`]. + /// - No new checkpoint challenges would be emitted and added to [`TickToCheckpointChallenges`]. + /// - Deadlines for proof submissions are indefinitely postponed. + #[pallet::storage] + #[pallet::getter(fn challenges_ticker_paused)] + pub type ChallengesTickerPaused = StorageValue<_, ()>; + + /// A mapping from block number to the weight used in that block. + /// + /// This is used to check if the network is presumably under a spam attack. + /// It is cleared for blocks older than `current_block` - ([`Config::BlockFullnessPeriod`] + 1). + #[pallet::storage] + #[pallet::getter(fn past_blocks_fullness)] + pub type PastBlocksWeight = + StorageMap<_, Blake2_128Concat, BlockNumberFor, Weight>; + + /// The number of blocks that have been considered _not_ full in the last [`Config::BlockFullnessPeriod`]. + /// + /// This is used to check if the network is presumably under a spam attack. + #[pallet::storage] + #[pallet::getter(fn not_full_blocks_count)] + pub type NotFullBlocksCount = StorageValue<_, BlockNumberFor, ValueQuery>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/v3/runtime/events-and-errors + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A manual challenge was submitted. + NewChallenge { + who: AccountIdFor, + key_challenged: KeyFor, + }, + + /// A proof was accepted. + ProofAccepted { + provider: ProviderIdFor, + proof: Proof, + }, + + /// A new challenge seed was generated. + NewChallengeSeed { + challenges_ticker: BlockNumberFor, + seed: RandomnessOutputFor, + }, + + /// A new checkpoint challenge was generated. + NewCheckpointChallenge { + challenges_ticker: BlockNumberFor, + challenges: BoundedVec< + (KeyFor, Option), + MaxCustomChallengesPerBlockFor, + >, + }, + + /// A provider was marked as slashable and their challenge deadline was forcefully pushed. + SlashableProvider { + provider: ProviderIdFor, + next_challenge_deadline: BlockNumberFor, + }, + + /// No record of the last tick the Provider submitted a proof for. + NoRecordOfLastSubmittedProof { provider: ProviderIdFor }, + + /// A provider's challenge cycle was initialised. + NewChallengeCycleInitialised { + current_tick: BlockNumberFor, + next_challenge_deadline: BlockNumberFor, + provider: ProviderIdFor, + maybe_provider_account: Option, + }, + + /// A set of mutations has been applied to the Forest. + MutationsApplied { + provider: ProviderIdFor, + mutations: Vec<(KeyFor, TrieRemoveMutation)>, + new_root: KeyFor, + }, + + /// The [`ChallengesTicker`] has been paused or unpaused. + ChallengesTickerSet { paused: bool }, + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// General errors + + /// The proof submitter is not a registered Provider. + NotProvider, + + /// `challenge` extrinsic errors + + /// The ChallengesQueue is full. No more manual challenges can be made + /// until some of the challenges in the queue are dispatched. + ChallengesQueueOverflow, + + /// The PriorityChallengesQueue is full. No more priority challenges can be made + /// until some of the challenges in the queue are dispatched. + PriorityChallengesQueueOverflow, + + /// The fee for submitting a challenge could not be charged. + FeeChargeFailed, + + /// `submit_proof` extrinsic errors + + /// There are no key proofs submitted. + EmptyKeyProofs, + + /// The root for the Provider could not be found. + ProviderRootNotFound, + + /// Provider is submitting a proof when they have a zero root. + /// Providers with zero roots are not providing any service, so they should not be + /// submitting proofs. + ZeroRoot, + + /// Provider is submitting a proof but there is no record of the last tick they + /// submitted a proof for. + /// Providers who are required to submit proofs should always have a record of the + /// last tick they submitted a proof for, otherwise it means they haven't started + /// providing service for any user yet. + NoRecordOfLastSubmittedProof, + + /// The provider stake could not be found. + ProviderStakeNotFound, + + /// Provider is submitting a proof but their stake is zero. + ZeroStake, + + /// The staked balance of the Provider could not be converted to `u128`. + /// This should not be possible, as the `Balance` type should be an unsigned integer type. + StakeCouldNotBeConverted, + + /// Provider is submitting a proof for a tick in the future. + ChallengesTickNotReached, + + /// Provider is submitting a proof for a tick before the last tick this pallet registers + /// challenges for. + ChallengesTickTooOld, + + /// Provider is submitting a proof for a tick too late, i.e. that the challenges tick + /// is greater or equal than `challenges_tick` + `T::ChallengeTicksTolerance::get()`. + ChallengesTickTooLate, + + /// The seed for the tick could not be found. + /// This should not be possible for a tick within the `ChallengeHistoryLength` range, as + /// seeds are generated for all ticks, and stored within this range. + SeedNotFound, + + /// Checkpoint challenges not found in block. + /// This should only be possible if `TickToCheckpointChallenges` is dereferenced for a tick + /// that is not a checkpoint tick. + CheckpointChallengesNotFound, + + /// The forest proof submitted by the Provider is invalid. + /// This could be because the proof is not valid for the root, or because the proof is + /// not sufficient for the challenges made. + ForestProofVerificationFailed, + + /// There is at least one key proven in the forest proof, that does not have a corresponding + /// key proof. + KeyProofNotFound, + + /// A key proof submitted by the Provider is invalid. + /// This could be because the proof is not valid for the root of that key, or because the proof + /// is not sufficient for the challenges made. + KeyProofVerificationFailed, + + /// Failed to apply delta to the forest proof partial trie. + FailedToApplyDelta, + + /// Failed to update the provider after a key removal mutation. + FailedToUpdateProviderAfterKeyRemoval, + + /// The limit of Providers that can submit a proof in a single tick has been reached. + TooManyValidProofSubmitters, + } + + #[pallet::call] + impl Pallet { + /// Introduce a new challenge. + /// + /// This function allows anyone to add a new challenge to the `ChallengesQueue`. + /// The challenge will be dispatched in the coming blocks. + /// Users are charged a small fee for submitting a challenge, which + /// goes to the Treasury. + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn challenge(origin: OriginFor, key: KeyFor) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + Self::do_challenge(&who, &key)?; + + // Emit event. + Self::deposit_event(Event::NewChallenge { + who, + key_challenged: key, + }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// For a Provider to submit a proof. + /// + /// Checks that `provider` is a registered Provider. If none + /// is provided, the proof submitter is considered to be the Provider. + /// Relies on a Providers pallet to get the root for the Provider. + /// Validates that the proof corresponds to a challenge that was made in the past, + /// by checking the `TickToChallengesSeed` StorageMap. The challenge tick that the + /// Provider should have submitted a proof is calculated based on the last tick they + /// submitted a proof for ([`LastTickProviderSubmittedAProofFor`]), and the proving period for + /// that Provider, which is a function of their stake. + /// This extrinsic also checks that there hasn't been a checkpoint challenge round + /// in between the last time the Provider submitted a proof for and the tick + /// for which the proof is being submitted. If there has been, the Provider is + /// subject to slashing. + /// + /// If valid: + /// - Pushes forward the Provider in the [`TickToProvidersDeadlines`] StorageMap a number + /// of ticks corresponding to the stake of the Provider. + /// - Registers this tick as the last tick in which the Provider submitted a proof. + /// + /// Execution of this extrinsic should be refunded if the proof is valid. + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn submit_proof( + origin: OriginFor, + proof: Proof, + provider: Option>, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Getting provider from the origin if none is provided. + let provider = match provider { + Some(provider) => provider, + None => { + let sp = T::ProvidersPallet::get_provider_id(who.clone()) + .ok_or(Error::::NotProvider)?; + sp + } + }; + + Self::do_submit_proof(&provider, &proof)?; + + // Emit event. + Self::deposit_event(Event::ProofAccepted { provider, proof }); + + // Return a successful DispatchResultWithPostInfo. + // If the proof is valid, the execution of this extrinsic should be refunded. + Ok(Pays::No.into()) + } + + /// Initialise a Provider's challenge cycle. + /// + /// Only callable by sudo. + /// + /// Sets the last tick the Provider submitted a proof for to the current tick, and sets the + /// deadline for submitting a proof to the current tick + the Provider's period + the tolerance. + #[pallet::call_index(2)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn force_initialise_challenge_cycle( + origin: OriginFor, + provider: ProviderIdFor, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was executed by the root origin. + ensure_root(origin)?; + + // Execute checks and logic, update storage. + ::initialise_challenge_cycle(&provider)?; + + // Return a successful DispatchResultWithPostInfo. + Ok(Pays::No.into()) + } + + /// Set the [`ChallengesTickerPaused`] to `true` or `false`. + /// + /// Only callable by sudo. + #[pallet::call_index(3)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn set_paused(origin: OriginFor, paused: bool) -> DispatchResultWithPostInfo { + // Check that the extrinsic was executed by the root origin. + ensure_root(origin)?; + + if paused { + ChallengesTickerPaused::::set(Some(())); + } else { + ChallengesTickerPaused::::set(None); + } + + // Emit the corresponding event. + Self::deposit_event(Event::::ChallengesTickerSet { paused }); + + // Return a successful DispatchResultWithPostInfo. + Ok(Pays::No.into()) + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + /// This hook is used to generate new challenges. + /// + /// It will be called at the beginning of every block, if the block is not being part of a + /// [Multi-Block-Migration](https://github.com/paritytech/polkadot-sdk/pull/1781) (MBM). + /// For more information on the lifecycle of the block and its hooks, see the [Substrate + /// documentation](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.on_poll). + fn on_poll(_n: BlockNumberFor, weight: &mut frame_support::weights::WeightMeter) { + // TODO: Benchmark computational weight cost of this hook. + + // Only execute the `do_new_challenges_round` if the `ChallengesTicker` is not paused. + if ChallengesTickerPaused::::get().is_none() { + Self::do_new_challenges_round(weight); + } + + // Check if the network is presumably under a spam attack. + // If so, `ChallengesTicker` will be paused. + // This check is done "a posteriori", meaning that we first increment the `ChallengesTicker`, send out challenges + // and slash Providers if in the last block we didn't consider the network to be under spam. + // Then if at this block we consider the network to be under spam, we pause the `ChallengesTicker`, which will not + // be incremented in the next block. + Self::do_check_spamming_condition(weight); + } + + /// This hook is called on block initialization and returns the Weight of the `on_finalize` hook to + /// let block builders know how much weight to reserve for it + /// TODO: Benchmark on_finalize to get its weight and replace the placeholder weight for that + fn on_initialize(_n: BlockNumberFor) -> Weight { + Weight::from_parts(10_000, 0) + T::DbWeight::get().reads_writes(0, 2) + } + + fn on_finalize(block_number: BlockNumberFor) { + // Get weight usage in this block so far, for the dispatch class of `submit_proof` extrinsics. + let weight_used = frame_system::Pallet::::block_weight(); + let weight_used_for_class = weight_used.get(DispatchClass::Normal); + + // Store the weight usage in this block. + PastBlocksWeight::::insert(block_number, weight_used_for_class); + + // Get the oldest block weight registered. + let block_fullness_period = T::BlockFullnessPeriod::get(); + + // Clear the storage for block at `current_block` - (`BlockFullnessPeriod` + 1). + if let Some(oldest_block_fullness_number) = + block_number.checked_sub(&block_fullness_period.saturating_add(1u32.into())) + { + // If it is older than `BlockFullnessPeriod` + 1, we clear the storage. + PastBlocksWeight::::remove(oldest_block_fullness_number); + } + } + + /// This integrity test checks that: + /// 1. `CheckpointChallengePeriod` is greater or equal to the longest period a Provider can have. + /// 2. `BlockFullnessPeriod` is smaller or equal than `ChallengeTicksTolerance`. + /// + /// Any code located in this hook is placed in an auto-generated test, and generated as a part + /// of crate::construct_runtime's expansion. + /// Look for a test case with a name along the lines of: __construct_runtime_integrity_test. + fn integrity_test() { + // Calculate longest period a Provider can have. + // That would be the period of the Provider with the minimum stake. + let min_stake = T::ProvidersPallet::get_min_stake(); + let max_period = Self::stake_to_challenge_period(min_stake); + + // Check that `CheckpointChallengePeriod` is greater or equal to the longest period a Provider can have. + assert!( + T::CheckpointChallengePeriod::get() >= max_period, + "CheckpointChallengePeriod ({:?}) const in ProofsDealer pallet should be greater or equal than the longest period a Provider can have ({:?}).", + T::CheckpointChallengePeriod::get(), + max_period + ); + + // Check that `BlockFullnessPeriod` is smaller or equal than `ChallengeTicksTolerance`. + assert!( + T::BlockFullnessPeriod::get() <= T::ChallengeTicksTolerance::get(), + "BlockFullnessPeriod const ({:?}) in ProofsDealer pallet should be smaller or equal than ChallengeTicksTolerance ({:?}).", + T::BlockFullnessPeriod::get(), + T::ChallengeTicksTolerance::get() + ); + } + + /// This hook is used to trim down the `ValidProofSubmittersLastTicks` StorageMap up to the `TargetTicksOfProofsStorage`. + /// + /// It runs when the block is being finalized (but before the `on_finalize` hook) and can consume all remaining weight. + /// It returns the used weight, so it can be used to calculate the remaining weight for the block for any other + /// pallets that have `on_idle` hooks. + fn on_idle(n: BlockNumberFor, weight: Weight) -> Weight { + // TODO: Benchmark computational and proof size weight cost of this hook. + Self::do_trim_valid_proof_submitters_last_ticks(n, weight) + } + } +} diff --git a/pallets/proofs-dealer/src/mock.rs b/pallets/proofs-dealer/src/mock.rs index 801428b44..bc6fef048 100644 --- a/pallets/proofs-dealer/src/mock.rs +++ b/pallets/proofs-dealer/src/mock.rs @@ -1,382 +1,382 @@ -#![allow(non_camel_case_types)] - -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use frame_support::{ - derive_impl, - pallet_prelude::Get, - parameter_types, - traits::{Everything, Randomness}, - weights::{constants::RocksDbWeight, Weight}, - BoundedBTreeSet, -}; -use frame_system as system; -use shp_file_metadata::{FileMetadata, Fingerprint}; -use shp_traits::{ - CommitmentVerifier, MaybeDebug, ProofSubmittersInterface, TrieMutation, TrieProofDeltaApplier, -}; -use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Hasher, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, IdentityLookup}, - BuildStorage, DispatchError, Perbill, SaturatedConversion, -}; -use sp_std::collections::btree_set::BTreeSet; -use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; -use system::pallet_prelude::BlockNumberFor; - -type Block = frame_system::mocking::MockBlock; -type Balance = u128; -type AccountId = u64; - -const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; -const UNITS: Balance = 1_000_000_000_000; -pub(crate) const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; - -// We mock the Randomness trait to use a simple randomness function when testing the pallet -const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; -pub struct MockRandomness; -impl Randomness> for MockRandomness { - fn random(subject: &[u8]) -> (H256, BlockNumberFor) { - // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks - - // Concatenate the subject with the block number to get a unique hash for each block - let subject_concat_block = [ - subject, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - - let hashed_subject = blake2_256(&subject_concat_block); - - ( - H256::from_slice(&hashed_subject), - frame_system::Pallet::::block_number() - .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), - ) - } -} - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, - ProofsDealer: crate::{Pallet, Call, Storage, Event}, - PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_balances::Config for Test { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxLocks = ConstU32<10>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<10>; -} - -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} - -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId { - 0 - } -} - -// Payment streams pallet: -impl pallet_payment_streams::Config for Test { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = Providers; - type RuntimeHoldReason = RuntimeHoldReason; - type Units = u64; - type NewStreamDeposit = ConstU64<10>; - type UserWithoutFundsCooldown = ConstU64<100>; - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = MockSubmittingProviders; -} -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; -impl Convert, Balance> for BlockNumberToBalance { - fn convert(block_number: BlockNumberFor) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -// Storage Providers pallet: -impl pallet_storage_providers::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = MockRandomness; - type PaymentStreams = PaymentStreams; - type FileMetadataManager = MockFileMetadataManager; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = u64; - type SpCount = u32; - type MerklePatriciaRoot = H256; - type ValuePropId = H256; - type ReadAccessGroupId = u32; - type ProvidersProofSubmitters = MockSubmittingProviders; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = ConstU128<{ 10 * UNITS }>; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = ConstU32<100>; - type MaxMultiAddressAmount = ConstU32<5>; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = ConstU128<10>; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; - type MinBlocksBetweenCapacityChanges = ConstU64<10>; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = ConstU128<10>; - type StartingReputationWeight = ConstU32<1>; -} - -// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. -pub struct MockSubmittingProviders; -impl ProofSubmittersInterface for MockSubmittingProviders { - type ProviderId = ::Hash; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = ConstU32<1000>; - fn get_proof_submitters_for_tick( - _block_number: &Self::TickNumber, - ) -> Option> { - None - } - - fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { - None - } - - fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} -} - -pub struct MockFileMetadataManager; -impl shp_traits::FileMetadataInterface for MockFileMetadataManager { - type AccountId = AccountId; - type Metadata = FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type StorageDataUnit = u64; - - fn encode(metadata: &Self::Metadata) -> Vec { - metadata.encode() - } - - fn decode(data: &[u8]) -> Result { - as Decode>::decode(&mut &data[..]) - } - - fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { - metadata.file_size - } - - fn get_file_owner(metadata: &Self::Metadata) -> Result { - Self::AccountId::decode(&mut metadata.owner.as_slice()) - } -} - -pub struct BlockFullnessHeadroom; -impl Get for BlockFullnessHeadroom { - fn get() -> Weight { - Weight::from_parts(10_000, 0) - + ::DbWeight::get().reads_writes(0, 1) - } -} - -pub struct MinNotFullBlocksRatio; -impl Get for MinNotFullBlocksRatio { - fn get() -> Perbill { - Perbill::from_percent(50) - } -} - -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersPallet = Providers; - type NativeBalance = Balances; - type MerkleTrieHash = H256; - type MerkleTrieHashing = BlakeTwo256; - type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type StakeToBlockNumber = SaturatingBalanceToBlockNumber; - type RandomChallengesPerBlock = ConstU32<10>; - type MaxCustomChallengesPerBlock = ConstU32<10>; - type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight - type TargetTicksStorageOfSubmitters = ConstU32<3>; - type ChallengeHistoryLength = ConstU64<30>; - type ChallengesQueueLength = ConstU32<25>; - type CheckpointChallengePeriod = ConstU64<20>; - type ChallengesFee = ConstU128<1_000_000>; - type Treasury = ConstU64<181222>; - type RandomnessProvider = MockRandomness; - type StakeToChallengePeriod = ConstU128; - type MinChallengePeriod = ConstU64<4>; - type ChallengeTicksTolerance = ConstU64<10>; - type BlockFullnessPeriod = ConstU64<10>; - type BlockFullnessHeadroom = BlockFullnessHeadroom; - type MinNotFullBlocksRatio = MinNotFullBlocksRatio; -} - -/// Structure to mock a verifier that returns `true` when `proof` is not empty -/// and `false` otherwise. -pub struct MockVerifier { - _phantom: core::marker::PhantomData<(C, T)>, -} - -/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. -impl CommitmentVerifier for MockVerifier -where - C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, -{ - type Proof = CompactProof; - type Commitment = H256; - type Challenge = C; - - fn verify_proof( - _root: &Self::Commitment, - challenges: &[Self::Challenge], - proof: &CompactProof, - ) -> Result, DispatchError> { - if proof.encoded_nodes.len() > 0 { - let challenges: BTreeSet = challenges.iter().cloned().collect(); - Ok(challenges) - } else { - Err("Proof is empty".into()) - } - } -} - -impl TrieProofDeltaApplier - for MockVerifier -where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - type Proof = CompactProof; - type Key = ::Out; - - fn apply_delta( - _root: &Self::Key, - mutations: &[(Self::Key, TrieMutation)], - _proof: &Self::Proof, - ) -> Result< - ( - MemoryDB, - Self::Key, - Vec<(Self::Key, Option>)>, - ), - DispatchError, - > { - let last_key = mutations.last().unwrap().0; - - let db = MemoryDB::::default(); - - let mutated_keys_and_values = mutations - .iter() - .map(|(key, mutation)| { - let value = match mutation { - TrieMutation::Add(add_mutation) => Some(add_mutation.value.clone()), - TrieMutation::Remove(_) => { - let file_metadata: FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - > = FileMetadata::new( - 1_u64.encode(), - blake2_256(b"bucket").as_ref().to_vec(), - b"path/to/file".to_vec(), - 1, - Fingerprint::default().into(), - ); - if key.as_ref() != [0; H_LENGTH] { - Some(file_metadata.encode()) - } else { - Some(vec![1, 2, 3, 4, 5, 6]) // We make it so the metadata is invalid for the empty key - } - } - }; - (*key, value) - }) - .collect(); - - // Return default db, the last key in mutations as the new root, and a - // vector holding the supposedly mutated keys and values, so it is deterministic for testing. - Ok((db, last_key, mutated_keys_and_values)) - } -} - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::::default() - .build_storage() - .unwrap() - .into() -} - -// Converter from the Balance type to the BlockNumber type for math. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct SaturatingBalanceToBlockNumber; - -impl Convert> for SaturatingBalanceToBlockNumber { - fn convert(block_number: Balance) -> BlockNumberFor { - block_number.saturated_into() - } -} +#![allow(non_camel_case_types)] + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use frame_support::{ + derive_impl, + pallet_prelude::Get, + parameter_types, + traits::{Everything, Randomness}, + weights::{constants::RocksDbWeight, Weight}, + BoundedBTreeSet, +}; +use frame_system as system; +use shp_file_metadata::{FileMetadata, Fingerprint}; +use shp_traits::{ + CommitmentVerifier, MaybeDebug, ProofSubmittersInterface, TrieMutation, TrieProofDeltaApplier, +}; +use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Hasher, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, IdentityLookup}, + BuildStorage, DispatchError, Perbill, SaturatedConversion, +}; +use sp_std::collections::btree_set::BTreeSet; +use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; +use system::pallet_prelude::BlockNumberFor; + +type Block = frame_system::mocking::MockBlock; +type Balance = u128; +type AccountId = u64; + +const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; +const UNITS: Balance = 1_000_000_000_000; +pub(crate) const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; + +// We mock the Randomness trait to use a simple randomness function when testing the pallet +const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; +pub struct MockRandomness; +impl Randomness> for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumberFor) { + // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks + + // Concatenate the subject with the block number to get a unique hash for each block + let subject_concat_block = [ + subject, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + + let hashed_subject = blake2_256(&subject_concat_block); + + ( + H256::from_slice(&hashed_subject), + frame_system::Pallet::::block_number() + .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), + ) + } +} + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Providers: pallet_storage_providers::{Pallet, Call, Storage, Event, HoldReason}, + ProofsDealer: crate::{Pallet, Call, Storage, Event}, + PaymentStreams: pallet_payment_streams::{Pallet, Call, Storage, Event, HoldReason}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<10>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<10>; +} + +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} + +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId { + 0 + } +} + +// Payment streams pallet: +impl pallet_payment_streams::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = Providers; + type RuntimeHoldReason = RuntimeHoldReason; + type Units = u64; + type NewStreamDeposit = ConstU64<10>; + type UserWithoutFundsCooldown = ConstU64<100>; + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = MockSubmittingProviders; +} +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; +impl Convert, Balance> for BlockNumberToBalance { + fn convert(block_number: BlockNumberFor) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +// Storage Providers pallet: +impl pallet_storage_providers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = MockRandomness; + type PaymentStreams = PaymentStreams; + type FileMetadataManager = MockFileMetadataManager; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = u64; + type SpCount = u32; + type MerklePatriciaRoot = H256; + type ValuePropId = H256; + type ReadAccessGroupId = u32; + type ProvidersProofSubmitters = MockSubmittingProviders; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = ConstU128<{ 10 * UNITS }>; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = ConstU32<100>; + type MaxMultiAddressAmount = ConstU32<5>; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = ConstU128<10>; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; + type MinBlocksBetweenCapacityChanges = ConstU64<10>; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = ConstU128<10>; + type StartingReputationWeight = ConstU32<1>; +} + +// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. +pub struct MockSubmittingProviders; +impl ProofSubmittersInterface for MockSubmittingProviders { + type ProviderId = ::Hash; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = ConstU32<1000>; + fn get_proof_submitters_for_tick( + _block_number: &Self::TickNumber, + ) -> Option> { + None + } + + fn get_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) -> Option { + None + } + + fn clear_accrued_failed_proof_submissions(_provider_id: &Self::ProviderId) {} +} + +pub struct MockFileMetadataManager; +impl shp_traits::FileMetadataInterface for MockFileMetadataManager { + type AccountId = AccountId; + type Metadata = FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type StorageDataUnit = u64; + + fn encode(metadata: &Self::Metadata) -> Vec { + metadata.encode() + } + + fn decode(data: &[u8]) -> Result { + as Decode>::decode(&mut &data[..]) + } + + fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { + metadata.file_size + } + + fn get_file_owner(metadata: &Self::Metadata) -> Result { + Self::AccountId::decode(&mut metadata.owner.as_slice()) + } +} + +pub struct BlockFullnessHeadroom; +impl Get for BlockFullnessHeadroom { + fn get() -> Weight { + Weight::from_parts(10_000, 0) + + ::DbWeight::get().reads_writes(0, 1) + } +} + +pub struct MinNotFullBlocksRatio; +impl Get for MinNotFullBlocksRatio { + fn get() -> Perbill { + Perbill::from_percent(50) + } +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersPallet = Providers; + type NativeBalance = Balances; + type MerkleTrieHash = H256; + type MerkleTrieHashing = BlakeTwo256; + type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type StakeToBlockNumber = SaturatingBalanceToBlockNumber; + type RandomChallengesPerBlock = ConstU32<10>; + type MaxCustomChallengesPerBlock = ConstU32<10>; + type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight + type TargetTicksStorageOfSubmitters = ConstU32<3>; + type ChallengeHistoryLength = ConstU64<30>; + type ChallengesQueueLength = ConstU32<25>; + type CheckpointChallengePeriod = ConstU64<20>; + type ChallengesFee = ConstU128<1_000_000>; + type Treasury = ConstU64<181222>; + type RandomnessProvider = MockRandomness; + type StakeToChallengePeriod = ConstU128; + type MinChallengePeriod = ConstU64<4>; + type ChallengeTicksTolerance = ConstU64<10>; + type BlockFullnessPeriod = ConstU64<10>; + type BlockFullnessHeadroom = BlockFullnessHeadroom; + type MinNotFullBlocksRatio = MinNotFullBlocksRatio; +} + +/// Structure to mock a verifier that returns `true` when `proof` is not empty +/// and `false` otherwise. +pub struct MockVerifier { + _phantom: core::marker::PhantomData<(C, T)>, +} + +/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. +impl CommitmentVerifier for MockVerifier +where + C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, +{ + type Proof = CompactProof; + type Commitment = H256; + type Challenge = C; + + fn verify_proof( + _root: &Self::Commitment, + challenges: &[Self::Challenge], + proof: &CompactProof, + ) -> Result, DispatchError> { + if proof.encoded_nodes.len() > 0 { + let challenges: BTreeSet = challenges.iter().cloned().collect(); + Ok(challenges) + } else { + Err("Proof is empty".into()) + } + } +} + +impl TrieProofDeltaApplier + for MockVerifier +where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + type Proof = CompactProof; + type Key = ::Out; + + fn apply_delta( + _root: &Self::Key, + mutations: &[(Self::Key, TrieMutation)], + _proof: &Self::Proof, + ) -> Result< + ( + MemoryDB, + Self::Key, + Vec<(Self::Key, Option>)>, + ), + DispatchError, + > { + let last_key = mutations.last().unwrap().0; + + let db = MemoryDB::::default(); + + let mutated_keys_and_values = mutations + .iter() + .map(|(key, mutation)| { + let value = match mutation { + TrieMutation::Add(add_mutation) => Some(add_mutation.value.clone()), + TrieMutation::Remove(_) => { + let file_metadata: FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + > = FileMetadata::new( + 1_u64.encode(), + blake2_256(b"bucket").as_ref().to_vec(), + b"path/to/file".to_vec(), + 1, + Fingerprint::default().into(), + ); + if key.as_ref() != [0; H_LENGTH] { + Some(file_metadata.encode()) + } else { + Some(vec![1, 2, 3, 4, 5, 6]) // We make it so the metadata is invalid for the empty key + } + } + }; + (*key, value) + }) + .collect(); + + // Return default db, the last key in mutations as the new root, and a + // vector holding the supposedly mutated keys and values, so it is deterministic for testing. + Ok((db, last_key, mutated_keys_and_values)) + } +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +// Converter from the Balance type to the BlockNumber type for math. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct SaturatingBalanceToBlockNumber; + +impl Convert> for SaturatingBalanceToBlockNumber { + fn convert(block_number: Balance) -> BlockNumberFor { + block_number.saturated_into() + } +} diff --git a/pallets/proofs-dealer/src/tests.rs b/pallets/proofs-dealer/src/tests.rs index 8f990bf45..70106aed3 100644 --- a/pallets/proofs-dealer/src/tests.rs +++ b/pallets/proofs-dealer/src/tests.rs @@ -1,4395 +1,4395 @@ -use std::{collections::BTreeMap, vec}; - -use codec::Encode; -use frame_support::{ - assert_err, assert_noop, assert_ok, - dispatch::DispatchClass, - pallet_prelude::Weight, - traits::{ - fungible::{Mutate, MutateHold}, - OnFinalize, OnIdle, OnPoll, - }, - weights::WeightMeter, - BoundedBTreeSet, -}; -use frame_system::{ - limits::BlockWeights, pallet_prelude::BlockNumberFor, BlockWeight, ConsumedWeight, -}; -use pallet_storage_providers::HoldReason; -use shp_traits::{ProofsDealerInterface, ReadChallengeableProvidersInterface, TrieRemoveMutation}; -use sp_core::{blake2_256, Get, Hasher, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Zero}, - BoundedVec, DispatchError, -}; -use sp_trie::CompactProof; - -use crate::{ - mock::*, - pallet::Event, - types::{ - BlockFullnessHeadroomFor, BlockFullnessPeriodFor, ChallengeHistoryLengthFor, - ChallengeTicksToleranceFor, ChallengesQueueLengthFor, CheckpointChallengePeriodFor, - KeyProof, MaxCustomChallengesPerBlockFor, MaxSubmittersPerTickFor, MinChallengePeriodFor, - MinNotFullBlocksRatioFor, Proof, ProviderIdFor, ProvidersPalletFor, - RandomChallengesPerBlockFor, StakeToChallengePeriodFor, TargetTicksStorageOfSubmittersFor, - }, - ChallengesTicker, ChallengesTickerPaused, LastCheckpointTick, LastDeletedTick, - LastTickProviderSubmittedAProofFor, NotFullBlocksCount, SlashableProviders, - TickToChallengesSeed, TickToCheckpointChallenges, TickToProvidersDeadlines, - ValidProofSubmittersLastTicks, -}; - -fn run_to_block(n: u64) { - while System::block_number() < n { - System::set_block_number(System::block_number() + 1); - - // Trigger on_poll hook execution. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set weight used to be zero. - let zero_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => Zero::zero(), - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(zero_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - } -} - -fn run_to_block_spammed(n: u64) { - while System::block_number() < n { - System::set_block_number(System::block_number() + 1); - - // Trigger on_poll hook execution. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Fill up block. - let weights: BlockWeights = ::BlockWeights::get(); - let max_weight_normal = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - let block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => max_weight_normal, - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - } -} - -#[test] -fn challenge_submit_succeed() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::challenge(user, file_key)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 1, - key_challenged: file_key, - } - .into(), - ); - - // Check user's balance after challenge. - let challenge_fee: u128 = ::ChallengesFee::get(); - assert_eq!( - ::NativeBalance::usable_balance(&1), - user_balance - challenge_fee - ); - - // Check that the challenge is in the queue. - let challenges_queue = crate::ChallengesQueue::::get(); - assert_eq!(challenges_queue.len(), 1); - assert_eq!(challenges_queue[0], file_key); - }); -} - -#[test] -fn challenge_submit_twice_succeed() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create two users and add funds to the accounts. - let user_1 = RuntimeOrigin::signed(1); - let user_2 = RuntimeOrigin::signed(2); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - assert_ok!(::NativeBalance::mint_into( - &2, - user_balance - )); - - // Mock two FileKeys. - let file_key_1 = BlakeTwo256::hash(b"file_key_1"); - let file_key_2 = BlakeTwo256::hash(b"file_key_2"); - - // Dispatch challenge extrinsic twice. - assert_ok!(ProofsDealer::challenge(user_1, file_key_1)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 1, - key_challenged: file_key_1, - } - .into(), - ); - - assert_ok!(ProofsDealer::challenge(user_2, file_key_2)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 2, - key_challenged: file_key_2, - } - .into(), - ); - - // Check users' balance after challenge. - let challenge_fee: u128 = ::ChallengesFee::get(); - assert_eq!( - ::NativeBalance::usable_balance(&1), - user_balance - challenge_fee - ); - assert_eq!( - ::NativeBalance::usable_balance(&2), - user_balance - challenge_fee - ); - - // Check that the challenge is in the queue. - let challenges_queue = crate::ChallengesQueue::::get(); - assert_eq!(challenges_queue.len(), 2); - assert_eq!(challenges_queue[0], file_key_1); - assert_eq!(challenges_queue[1], file_key_2); - }); -} - -#[test] -fn challenge_submit_existing_challenge_succeed() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Dispatch challenge extrinsic twice. - assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); - assert_ok!(ProofsDealer::challenge(user, file_key)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 1, - key_challenged: file_key, - } - .into(), - ); - - // Check user's balance after challenge. - let challenge_fee: u128 = ::ChallengesFee::get(); - assert_eq!( - ::NativeBalance::usable_balance(&1), - user_balance - challenge_fee * 2 - ); - - // Check that the challenge is in the queue. - let challenges_queue = crate::ChallengesQueue::::get(); - assert_eq!(challenges_queue.len(), 1); - assert_eq!(challenges_queue[0], file_key); - }); -} - -#[test] -fn challenge_submit_in_two_rounds_succeed() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Dispatch challenge extrinsic twice. - assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 1, - key_challenged: file_key, - } - .into(), - ); - - // Check user's balance after challenge. - let challenge_fee: u128 = ::ChallengesFee::get(); - assert_eq!( - ::NativeBalance::usable_balance(&1), - user_balance - challenge_fee - ); - - // Check that the challenge is in the queue. - let challenges_queue = crate::ChallengesQueue::::get(); - assert_eq!(challenges_queue.len(), 1); - assert_eq!(challenges_queue[0], file_key); - - // Advance `CheckpointChallengePeriod` blocks. - let challenge_period: u64 = ::CheckpointChallengePeriod::get(); - run_to_block(challenge_period as u64 + 1); - - // Dispatch challenge extrinsic twice. - let file_key = BlakeTwo256::hash(b"file_key_2"); - assert_ok!(ProofsDealer::challenge(user, file_key)); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewChallenge { - who: 1, - key_challenged: file_key, - } - .into(), - ); - - // Check user's balance after challenge. - assert_eq!( - ::NativeBalance::usable_balance(&1), - user_balance - challenge_fee * 2 - ); - }); -} - -#[test] -fn challenge_wrong_origin_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Dispatch challenge extrinsic with wrong origin. - assert_noop!( - ProofsDealer::challenge(RuntimeOrigin::none(), file_key), - DispatchError::BadOrigin - ); - }); -} - -#[test] -fn challenge_submit_by_regular_user_with_no_funds_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user with no funds. - let user = RuntimeOrigin::signed(1); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::challenge(user, file_key), - crate::Error::::FeeChargeFailed - ); - }); -} - -#[test] -fn challenge_overflow_challenges_queue_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Fill the challenges queue. - let queue_size: u32 = ::ChallengesQueueLength::get(); - for i in 0..queue_size { - let file_key = BlakeTwo256::hash(&i.to_le_bytes()); - assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); - } - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::challenge(user, file_key), - crate::Error::::ChallengesQueueOverflow - ); - }); -} - -#[test] -fn proofs_dealer_trait_challenge_succeed() { - new_test_ext().execute_with(|| { - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Challenge using trait. - ::challenge(&file_key).unwrap(); - - // Check that the challenge is in the queue. - let challenges_queue = crate::ChallengesQueue::::get(); - assert_eq!(challenges_queue.len(), 1); - assert_eq!(challenges_queue[0], file_key); - }); -} - -#[test] -fn proofs_dealer_trait_challenge_overflow_challenges_queue_fail() { - new_test_ext().execute_with(|| { - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Fill the challenges queue. - let queue_size: u32 = ::ChallengesQueueLength::get(); - for i in 0..queue_size { - let file_key = BlakeTwo256::hash(&i.to_le_bytes()); - assert_ok!(::challenge(&file_key)); - } - - // Dispatch challenge extrinsic. - assert_noop!( - ::challenge(&file_key), - crate::Error::::ChallengesQueueOverflow - ); - }); -} - -#[test] -fn proofs_dealer_trait_challenge_with_priority_succeed() { - new_test_ext().execute_with(|| { - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Challenge using trait. - ::challenge_with_priority( - &file_key, None, - ) - .unwrap(); - - // Check that the challenge is in the queue. - let priority_challenges_queue = crate::PriorityChallengesQueue::::get(); - assert_eq!(priority_challenges_queue.len(), 1); - assert_eq!(priority_challenges_queue[0], (file_key, None)); - }); -} - -#[test] -fn proofs_dealer_trait_challenge_with_priority_overflow_challenges_queue_fail() { - new_test_ext().execute_with(|| { - // Mock a FileKey. - let file_key = BlakeTwo256::hash(b"file_key"); - - // Fill the challenges queue. - let queue_size: u32 = ::ChallengesQueueLength::get(); - for i in 0..queue_size { - let file_key = BlakeTwo256::hash(&i.to_le_bytes()); - assert_ok!( - ::challenge_with_priority( - &file_key, None - ) - ); - } - - // Dispatch challenge extrinsic. - assert_noop!( - ::challenge_with_priority( - &file_key, None - ), - crate::Error::::PriorityChallengesQueueOverflow - ); - }); -} - -#[test] -fn proofs_dealer_trait_initialise_challenge_cycle_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Mock a Provider ID. - let provider_id = BlakeTwo256::hash(b"provider_id"); - - // Register user as a Provider in Providers pallet. - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Dispatch initialise provider extrinsic. - assert_ok!(ProofsDealer::force_initialise_challenge_cycle( - RuntimeOrigin::root(), - provider_id - )); - - // Check that the Provider's last tick was set to 1. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, 1); - - // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` - // after the initialisation. - let stake = as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let expected_deadline = - last_tick_provider_submitted_proof + challenge_period_plus_tolerance; - let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id); - assert_eq!(deadline, Some(())); - - // Check that the last event emitted is the correct one. - System::assert_last_event( - Event::NewChallengeCycleInitialised { - current_tick: 1, - next_challenge_deadline: expected_deadline, - provider: provider_id, - maybe_provider_account: Some(1u64), - } - .into(), - ); - }); -} - -#[test] -fn proofs_dealer_trait_initialise_challenge_cycle_already_initialised_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Mock a Provider ID. - let provider_id = BlakeTwo256::hash(b"provider_id"); - - // Register user as a Provider in Providers pallet. - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Dispatch initialise provider extrinsic. - assert_ok!(ProofsDealer::force_initialise_challenge_cycle( - RuntimeOrigin::root(), - provider_id - )); - - // Check that the Provider's last tick was set to 1. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, 1); - - // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` - // after the initialisation. - let stake = as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = last_tick_provider_submitted_proof + challenge_period_plus_tolerance; - let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id); - assert_eq!(deadline, Some(())); - - // Let some blocks pass (less than `ChallengeTicksTolerance` blocks). - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Re-initialise the provider. - assert_ok!(ProofsDealer::force_initialise_challenge_cycle( - RuntimeOrigin::root(), - provider_id - )); - - // Check that the Provider's last tick is the current now. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); - let current_tick = ChallengesTicker::::get(); - assert_eq!(last_tick_provider_submitted_proof, current_tick); - - // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` - // after the initialisation. - let stake = as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let expected_deadline = - last_tick_provider_submitted_proof + challenge_period_plus_tolerance; - let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id); - assert_eq!(deadline, Some(())); - - // Check that the Provider no longer has the previous deadline. - let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id); - assert_eq!(deadline, None); - - // Advance beyond the previous deadline block and check that the Provider is not marked as slashable. - run_to_block(current_block + challenge_ticks_tolerance + 1); - - assert!(!SlashableProviders::::contains_key(&provider_id)); - }); -} - -#[test] -fn proofs_dealer_trait_initialise_challenge_cycle_already_initialised_and_new_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Mock two Provider IDs. - let provider_id_1 = BlakeTwo256::hash(b"provider_id_1"); - let provider_id_2 = BlakeTwo256::hash(b"provider_id_2"); - - // Register users as a Provider in Providers pallet. - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id_1, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id_1, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &2, - provider_id_2, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id_2, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 2u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to those Providers and hold some so they have a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::mint_into( - &2, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &2, - provider_balance / 100 - )); - - // Initialise providers - assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_1)); - assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_2)); - - // Check that the Providers' last tick was set to 1. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id_1).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, 1); - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id_2).unwrap(); - assert_eq!(last_tick_provider_submitted_proof, 1); - - // Check that Provider 1's deadline was set to `challenge_period + challenge_ticks_tolerance` - // after the initialisation. - let stake = as ReadChallengeableProvidersInterface>::get_stake( - provider_id_1, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = last_tick_provider_submitted_proof + challenge_period_plus_tolerance; - let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id_1); - assert_eq!(deadline, Some(())); - - // Let some blocks pass (less than `ChallengeTicksTolerance` blocks). - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Re-initialise the provider. - assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_1)); - - // Check that the Provider's last tick is the current now. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(&provider_id_1).unwrap(); - let current_tick = ChallengesTicker::::get(); - assert_eq!(last_tick_provider_submitted_proof, current_tick); - - // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` - // after the initialisation. - let stake = as ReadChallengeableProvidersInterface>::get_stake( - provider_id_1, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let expected_deadline = - last_tick_provider_submitted_proof + challenge_period_plus_tolerance; - let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id_1); - assert_eq!(deadline, Some(())); - - // Check that the Provider no longer has the previous deadline. - let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id_1); - assert_eq!(deadline, None); - - // Advance beyond the previous deadline block and check that the Provider is not marked as slashable. - run_to_block(current_block + challenge_ticks_tolerance + 1); - assert!(!SlashableProviders::::contains_key(&provider_id_1)); - }); -} - -#[test] -fn proofs_dealer_trait_initialise_challenge_cycle_not_provider_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Mock a Provider ID. - let provider_id = BlakeTwo256::hash(b"provider_id"); - - // Expect failure since the user is not a provider. - assert_noop!( - ProofsDealer::initialise_challenge_cycle(&provider_id), - crate::Error::::NotProvider - ); - }); -} - -#[test] -fn submit_proof_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - let current_tick = ChallengesTicker::::get(); - let last_tick_provider_submitted_proof = current_tick; - LastTickProviderSubmittedAProofFor::::insert( - &provider_id, - last_tick_provider_submitted_proof, - ); - - // Set Provider's deadline for submitting a proof. - // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::submit_proof(user, proof.clone(), None)); - - // Check for event submitted. - System::assert_last_event( - Event::ProofAccepted { - provider: provider_id, - proof, - } - .into(), - ); - - // Check the new last time this provider submitted a proof. - let expected_new_tick = last_tick_provider_submitted_proof + challenge_period; - let new_last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); - assert_eq!(expected_new_tick, new_last_tick_provider_submitted_proof); - - // Check that the Provider's deadline was pushed forward. - assert_eq!( - TickToProvidersDeadlines::::get(prev_deadline, provider_id), - None - ); - let new_deadline = expected_new_tick + challenge_period + challenge_ticks_tolerance; - assert_eq!( - TickToProvidersDeadlines::::get(new_deadline, provider_id), - Some(()), - ); - }); -} - -#[test] -fn submit_proof_adds_provider_to_valid_submitters_set() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - let current_tick = ChallengesTicker::::get(); - let last_tick_provider_submitted_proof = current_tick; - LastTickProviderSubmittedAProofFor::::insert( - &provider_id, - last_tick_provider_submitted_proof, - ); - - // Set Provider's deadline for submitting a proof. - // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Advance to the next challenge the Provider should listen to. - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::submit_proof(user, proof.clone(), None)); - - // Check for event submitted. - System::assert_last_event( - Event::ProofAccepted { - provider: provider_id, - proof, - } - .into(), - ); - - // Check that the Provider is in the valid submitters set. - assert!( - ValidProofSubmittersLastTicks::::get(ChallengesTicker::::get()) - .unwrap() - .contains(&provider_id) - ); - }); -} - -#[test] -fn submit_proof_submitted_by_not_a_provider_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register user as a Provider in Providers pallet. - // The registered Provider ID will be different from the one that will be used in the proof. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::submit_proof( - RuntimeOrigin::signed(2), - proof, - Some(provider_id) - )); - }); -} - -#[test] -fn submit_proof_with_checkpoint_challenges_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof tick. - let last_tick_provider_submitted_proof = System::block_number(); - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let mut challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Set last checkpoint challenge block to be equal to the last tick this provider has submitted - // a proof for, so that custom challenges will be taken into account in proof verification. - let checkpoint_challenge_block = last_tick_provider_submitted_proof; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Make up custom challenges. - let custom_challenges = BoundedVec::try_from(vec![ - (BlakeTwo256::hash(b"custom_challenge_1"), None), - (BlakeTwo256::hash(b"custom_challenge_2"), None), - ]) - .unwrap(); - - // Set custom challenges in checkpoint block. - TickToCheckpointChallenges::::insert( - checkpoint_challenge_block, - custom_challenges.clone(), - ); - - // Add custom challenges to the challenges vector. - challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in &challenges { - key_proofs.insert( - *challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::submit_proof(user, proof, None)); - }); -} - -#[test] -fn submit_proof_with_checkpoint_challenges_mutations_success() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: 1000, - capacity_used: 100, - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Increment the used capacity of BSPs in the Providers pallet. - pallet_storage_providers::UsedBspsCapacity::::set(100); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Create a dynamic-rate payment stream between the user and the Provider. - pallet_payment_streams::DynamicRatePaymentStreams::::insert( - &provider_id, - &1, - pallet_payment_streams::types::DynamicRatePaymentStream { - amount_provided: 10, - price_index_when_last_charged: pallet_payment_streams::AccumulatedPriceIndex::::get(), - user_deposit: 10 * <::NewStreamDeposit as Get>::get() as u128 * pallet_payment_streams::CurrentPricePerUnitPerTick::::get(), - out_of_funds_tick: None, - }, - ); - - // Set Provider's last submitted proof tick. - let last_tick_provider_submitted_proof = System::block_number(); - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let mut challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Set last checkpoint challenge block to be equal to the last tick this provider has submitted - // a proof for, so that custom challenges will be taken into account in proof verification. - let checkpoint_challenge_block = last_tick_provider_submitted_proof; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Make up custom challenges. - let custom_challenges = BoundedVec::try_from(vec![ - ( - BlakeTwo256::hash(b"custom_challenge_1"), - Some(TrieRemoveMutation::default()), - ), - ( - BlakeTwo256::hash(b"custom_challenge_2"), - Some(TrieRemoveMutation::default()), - ), - ]) - .unwrap(); - - // Set custom challenges in checkpoint block. - TickToCheckpointChallenges::::insert( - checkpoint_challenge_block, - custom_challenges.clone(), - ); - - // Add custom challenges to the challenges vector. - challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in &challenges { - key_proofs.insert( - *challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - assert_ok!(ProofsDealer::submit_proof(user, proof, None)); - - // Check that the event for mutations applied is emitted. - System::assert_has_event( - Event::MutationsApplied { - provider: provider_id, - mutations: custom_challenges - .iter() - .map(|(key, mutation)| (*key, mutation.clone().unwrap())) - .collect(), - new_root: challenges.last().unwrap().clone(), - } - .into(), - ); - - // Check if root of the provider was updated the last challenge key - // Note: The apply_delta method is applying the mutation the root of the provider for every challenge key. - // This is to avoid having to construct valid tries and proofs. - let root = - <::ProvidersPallet as ReadChallengeableProvidersInterface>::get_root(provider_id) - .unwrap(); - assert_eq!(root.as_ref(), challenges.last().unwrap().as_ref()); - }); -} - -#[test] -fn submit_proof_with_checkpoint_challenges_mutations_fails_if_decoded_metadata_is_invalid() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: 1000, - capacity_used: 100, - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Increment the used capacity of BSPs in the Providers pallet. - pallet_storage_providers::UsedBspsCapacity::::set(100); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Create a dynamic-rate payment stream between the user and the Provider. - pallet_payment_streams::DynamicRatePaymentStreams::::insert( - &provider_id, - &1, - pallet_payment_streams::types::DynamicRatePaymentStream { - amount_provided: 10, - price_index_when_last_charged: - pallet_payment_streams::AccumulatedPriceIndex::::get(), - user_deposit: 10 - * <::NewStreamDeposit as Get>::get( - ) as u128 - * pallet_payment_streams::CurrentPricePerUnitPerTick::::get(), - out_of_funds_tick: None, - }, - ); - - // Set Provider's last submitted proof tick. - let last_tick_provider_submitted_proof = System::block_number(); - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let mut challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Set last checkpoint challenge block to be equal to the last tick this provider has submitted - // a proof for, so that custom challenges will be taken into account in proof verification. - let checkpoint_challenge_block = last_tick_provider_submitted_proof; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Make up custom challenges. - let custom_challenges = BoundedVec::try_from(vec![ - ( - [0; BlakeTwo256::LENGTH].into(), // Challenge that will return invalid metadata - Some(TrieRemoveMutation::default()), - ), - ( - BlakeTwo256::hash(b"custom_challenge_2"), - Some(TrieRemoveMutation::default()), - ), - ]) - .unwrap(); - - // Set custom challenges in checkpoint block. - TickToCheckpointChallenges::::insert( - checkpoint_challenge_block, - custom_challenges.clone(), - ); - - // Add custom challenges to the challenges vector. - challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in &challenges { - key_proofs.insert( - *challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic and it fails because of the invalid metadata. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::FailedToApplyDelta - ); - }); -} - -#[test] -fn submit_proof_caller_not_a_provider_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs: Default::default(), - }; - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::NotProvider - ); - }); -} - -#[test] -fn submit_proof_provider_passed_not_registered_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs: Default::default(), - }; - - // Creating a Provider ID but not registering it. - let provider_id = BlakeTwo256::hash(b"provider_id"); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, Some(provider_id)), - crate::Error::::NotProvider - ); - }); -} - -#[test] -fn submit_proof_empty_key_proofs_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs: Default::default(), - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::EmptyKeyProofs - ); - }); -} - -#[test] -fn submit_proof_no_record_of_last_proof_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::NoRecordOfLastSubmittedProof - ); - }); -} - -#[test] -fn submit_proof_challenges_block_not_reached_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::ChallengesTickNotReached - ); - }); -} - -#[test] -#[should_panic( - expected = "internal error: entered unreachable code: Challenges tick is too old, beyond the history this pallet keeps track of. This should not be possible." -)] -fn submit_proof_challenges_block_too_old_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); - - // Advance more than `ChallengeHistoryLength` blocks. - let challenge_history_length: u64 = ChallengeHistoryLengthFor::::get(); - run_to_block(challenge_history_length * 2); - - // Dispatch challenge extrinsic. - let _ = ProofsDealer::submit_proof(user, proof, None); - }); -} - -#[test] -#[should_panic( - expected = "internal error: entered unreachable code: Seed for challenges tick not found, when checked it should be within history." -)] -fn submit_proof_seed_not_found_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Remove challenge seed for challenge block. - TickToChallengesSeed::::remove(challenge_block); - - // Dispatch challenge extrinsic. - let _ = ProofsDealer::submit_proof(user, proof, None); - }); -} - -#[test] -#[should_panic( - expected = "internal error: entered unreachable code: Checkpoint challenges not found, when dereferencing in last registered checkpoint challenge block." -)] -fn submit_proof_checkpoint_challenge_not_found_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: (2 * 100) as u64, - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Set random seed for this block challenges. - let seed = BlakeTwo256::hash(b"seed"); - TickToChallengesSeed::::insert(System::block_number(), seed); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Set last checkpoint challenge block to something before the challenge tick - // that is being submitted. - let checkpoint_challenge_block = 1; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Dispatch challenge extrinsic. - let _ = ProofsDealer::submit_proof(user, proof, None); - }); -} - -#[test] -fn submit_proof_forest_proof_verification_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Mock key proofs. - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - - // Create an empty forest proof to fail verification. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Dispatch challenge extrinsic. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::ForestProofVerificationFailed - ); - }); -} - -#[test] -fn submit_proof_no_key_proofs_for_keys_verified_in_forest_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Creating empty key proof to fail verification. - let mut key_proofs = BTreeMap::new(); - key_proofs.insert( - BlakeTwo256::hash(b"key"), - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![], - }, - challenge_count: Default::default(), - }, - ); - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Dispatch challenge extrinsic. - // The forest proof will pass because it's not empty, so the MockVerifier will accept it, - // and it will return the generated challenges as keys proven. The key proofs are an empty - // vector, so it will fail saying that there are no key proofs for the keys proven. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::KeyProofNotFound - ); - }); -} - -#[test] -fn submit_proof_out_checkpoint_challenges_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Set random seed for this block challenges. - let seed = BlakeTwo256::hash(b"seed"); - TickToChallengesSeed::::insert(System::block_number(), seed); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Set last checkpoint challenge block. - let checkpoint_challenge_block = System::block_number() + 1; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Make up custom challenges. - let custom_challenges = BoundedVec::try_from(vec![ - (BlakeTwo256::hash(b"custom_challenge_1"), None), - (BlakeTwo256::hash(b"custom_challenge_2"), None), - ]) - .unwrap(); - - // Set custom challenges in checkpoint block. - TickToCheckpointChallenges::::insert( - checkpoint_challenge_block, - custom_challenges.clone(), - ); - - // Creating a vec of empty key proofs for each challenge, to fail verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Dispatch challenge extrinsic. - // The forest proof will pass because it's not empty, so the MockVerifier will accept it, - // and it will return the generated challenges as keys proven. The key proofs only contain - // proofs for the regular challenges, not the checkpoint challenges, so it will fail saying - // that there are no key proofs for the keys proven. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::KeyProofNotFound - ); - }); -} - -#[test] -fn submit_proof_key_proof_verification_fail() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Create user and add funds to the account. - let user = RuntimeOrigin::signed(1); - let user_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - user_balance - )); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Hold some of the Provider's balance so it simulates it having a stake. - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - user_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); - - // Advance to the next challenge the Provider should listen to. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for challenge block. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Creating a vec of empty key proofs for each challenge, to fail verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Dispatch challenge extrinsic. - // The forest proof will pass because it's not empty, so the MockVerifier will accept it, - // and it will return the generated challenges as keys proven. There will be key proofs - // for each key proven, but they are empty, so it will fail saying that the verification - // failed. - assert_noop!( - ProofsDealer::submit_proof(user, proof, None), - crate::Error::::KeyProofVerificationFailed - ); - }); -} - -#[test] -fn new_challenges_round_random_and_checkpoint_challenges() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Run a block and check that the random challenge was emitted. - run_to_block(2); - - // Build the expected random seed. - let challenges_ticker = ChallengesTicker::::get().encode(); - let challenges_ticker: &[u8] = challenges_ticker.as_ref(); - let subject = [challenges_ticker, &System::block_number().to_le_bytes()].concat(); - let hashed_subject = blake2_256(&subject); - let expected_seed = H256::from_slice(&hashed_subject); - - // Check that the event is emitted. - // This would be the first time the random seed is emitted. - System::assert_last_event( - Event::NewChallengeSeed { - challenges_ticker: 2, - seed: expected_seed, - } - .into(), - ); - - // Run another block and check that the random challenge was emitted. - run_to_block(3); - - // Build the expected random seed. - let challenges_ticker = ChallengesTicker::::get().encode(); - let challenges_ticker: &[u8] = challenges_ticker.as_ref(); - let subject: Vec = [ - challenges_ticker, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - let hashed_subject = blake2_256(&subject); - let expected_seed = H256::from_slice(&hashed_subject); - - // Check that the event is emitted. - // This would be the second time the random seed is emitted. - System::assert_last_event( - Event::NewChallengeSeed { - challenges_ticker: 3, - seed: expected_seed, - } - .into(), - ); - - // Run until the next checkpoint challenge block. - let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); - run_to_block(checkpoint_challenge_period); - - // Expect an empty set of checkpoint challenges. - let challenges_ticker = ChallengesTicker::::get(); - let checkpoint_challenges = - TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); - assert_eq!(checkpoint_challenges.len(), 0); - - // Check that the event is emitted. - System::assert_last_event( - Event::NewCheckpointChallenge { - challenges_ticker, - challenges: Default::default(), - } - .into(), - ); - }); -} - -#[test] -fn new_challenges_round_random_challenges_cleanup() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Run until block number `ChallengesHistoryLength` + 1. - let challenges_history_length: u64 = ChallengeHistoryLengthFor::::get(); - run_to_block(challenges_history_length + 1u64); - - // Check that the challenge seed for block 1 is not found. - assert_eq!( - TickToChallengesSeed::::get(1), - None, - "Challenge seed for block 1 should not be found." - ); - - // Check that the challenge seed exists for block 2. - let challenges_ticker = 2u64.encode(); - let challenges_ticker: &[u8] = challenges_ticker.as_ref(); - let subject: Vec = [challenges_ticker, &2u64.to_le_bytes()].concat(); - let hashed_subject = blake2_256(&subject); - let expected_seed = H256::from_slice(&hashed_subject); - assert_eq!( - TickToChallengesSeed::::get(2), - Some(expected_seed), - "Challenge seed for block 2 should be found." - ); - }); -} - -#[test] -fn new_challenges_round_checkpoint_challenges_cleanup() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Run until block number 2 * `CheckpointChallengePeriod`. - let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); - run_to_block(checkpoint_challenge_period * 2); - - // Check that the checkpoint challenge for block `checkpoint_challenge_period` is not found. - assert_eq!( - TickToCheckpointChallenges::::get(checkpoint_challenge_period), - None, - "Checkpoint challenge for block `CheckpointChallengePeriod` should not be found." - ); - - // Check that the checkpoint challenge exists for block `checkpoint_challenge_period * 2`. - assert_eq!( - TickToCheckpointChallenges::::get(checkpoint_challenge_period * 2), - Some(Default::default()), - "Checkpoint challenge for block `CheckpointChallengePeriod * 2` should be found." - ) - }); -} - -#[test] -fn new_challenges_round_checkpoint_challenges_with_custom_challenges() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Add custom challenges to the challenges vector. - let key_challenged = BlakeTwo256::hash(b"key_challenged"); - assert_ok!( as ProofsDealerInterface>::challenge( - &key_challenged - )); - - // Add priority challenge to the challenges vector. - let priority_key_challenged = BlakeTwo256::hash(b"priority_key_challenged"); - assert_ok!( - as ProofsDealerInterface>::challenge_with_priority( - &priority_key_challenged, - Some(TrieRemoveMutation::default()) - ) - ); - - // Run until the next checkpoint challenge block. - let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); - run_to_block(checkpoint_challenge_period); - - // Expect checkpoint challenges to be emitted, with the priority first. - let challenges_ticker = ChallengesTicker::::get(); - let checkpoint_challenges = - TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); - assert_eq!(checkpoint_challenges.len(), 2); - assert_eq!( - checkpoint_challenges[0], - (priority_key_challenged, Some(TrieRemoveMutation::default())) - ); - assert_eq!(checkpoint_challenges[1], (key_challenged, None)); - }); -} - -#[test] -fn new_challenges_round_max_custom_challenges() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Add max amount of custom challenges to the challenges vector. - let max_custom_challenges = ChallengesQueueLengthFor::::get(); - for i in 0..max_custom_challenges { - let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); - assert_ok!( as ProofsDealerInterface>::challenge( - &key_challenged - )); - } - - // Add another custom challenge. It should fail. - assert_err!( - as ProofsDealerInterface>::challenge(&BlakeTwo256::hash( - b"key_challenged" - )), - crate::Error::::ChallengesQueueOverflow - ); - - // Add max amount of priority challenges to the challenges vector. - let max_priority_challenges = ChallengesQueueLengthFor::::get(); - for i in 0..max_priority_challenges { - let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); - assert_ok!( - as ProofsDealerInterface>::challenge_with_priority( - &key_challenged, - Some(TrieRemoveMutation::default()) - ) - ); - } - - // Add another priority challenge. It should fail. - assert_err!( - as ProofsDealerInterface>::challenge_with_priority( - &BlakeTwo256::hash(b"key_challenged"), - Some(TrieRemoveMutation::default()) - ), - crate::Error::::PriorityChallengesQueueOverflow - ); - - // Check how many checkpoint challenges round are needed to evacuate all the queue. - let queue_length: u32 = ChallengesQueueLengthFor::::get(); - let custom_challenges_per_round: u32 = MaxCustomChallengesPerBlockFor::::get(); - let mut checkpoint_challenge_rounds_needed = queue_length / custom_challenges_per_round; - if queue_length % custom_challenges_per_round != 0 { - checkpoint_challenge_rounds_needed += 1; - } - - // Run until the next checkpoint challenge round. - let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); - run_to_block(checkpoint_challenge_period); - - // Expect checkpoint challenges to be emitted, with the priority first. - let challenges_ticker = ChallengesTicker::::get(); - let checkpoint_challenges = - TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); - assert_eq!( - checkpoint_challenges.len(), - custom_challenges_per_round as usize - ); - for i in 0..checkpoint_challenges.len() { - let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); - assert_eq!( - checkpoint_challenges[i], - (key_challenged, Some(TrieRemoveMutation::default())) - ); - } - - // Run until the needed checkpoint challenge block. - let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); - run_to_block(checkpoint_challenge_period * checkpoint_challenge_rounds_needed as u64); - - // The length of the checkpoint challenges should be max, because even if the priority - // challenges don't fill the queue, the custom challenges will. - let challenges_ticker = ChallengesTicker::::get(); - let checkpoint_challenges = - TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); - assert_eq!( - checkpoint_challenges.len(), - custom_challenges_per_round as usize - ); - - // Expect the last priority challenges in the priority queue to be emitted first. - let last_priority_challenges_amount = if queue_length % custom_challenges_per_round == 0 { - custom_challenges_per_round - } else { - queue_length % custom_challenges_per_round - }; - let last_priority_challenges_start_index = - (checkpoint_challenge_rounds_needed - 1) * custom_challenges_per_round; - for i in 0..last_priority_challenges_amount { - let key_challenged = BlakeTwo256::hash( - &((last_priority_challenges_start_index + i) as usize).to_le_bytes(), - ); - assert_eq!( - checkpoint_challenges[i as usize], - (key_challenged, Some(TrieRemoveMutation::default())) - ); - } - - // Check that the last checkpoint challenges contain the custom challenges, if there was - // enough space in this challenge round. - let checkpoint_challenges_start_index = if queue_length % custom_challenges_per_round == 0 { - custom_challenges_per_round - } else { - queue_length % custom_challenges_per_round - }; - let checkpoint_challenges_amount = - custom_challenges_per_round - checkpoint_challenges_start_index; - for i in 0..checkpoint_challenges_amount { - let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); - assert_eq!( - checkpoint_challenges[(checkpoint_challenges_start_index + i) as usize], - (key_challenged, None) - ); - } - - // Run until the custom challenges are all evacuated. - let mut checkpoint_challenge_rounds_needed = queue_length / custom_challenges_per_round * 2; - if queue_length % custom_challenges_per_round != 0 { - checkpoint_challenge_rounds_needed += 1; - } - run_to_block(checkpoint_challenge_period * checkpoint_challenge_rounds_needed as u64); - - // The last checkpoint challenge should be the last custom challenge. - let challenges_ticker = ChallengesTicker::::get(); - let checkpoint_challenges = - TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); - let last_checkpoint_challenge = &checkpoint_challenges[checkpoint_challenges.len() - 1]; - assert_eq!( - last_checkpoint_challenge, - &( - BlakeTwo256::hash(&((queue_length - 1) as usize).to_le_bytes()), - None - ) - ) - }); -} - -#[test] -fn new_challenges_round_provider_marked_as_slashable() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - let current_tick = ChallengesTicker::::get(); - let prev_tick_provider_submitted_proof = current_tick; - LastTickProviderSubmittedAProofFor::::insert( - &provider_id, - prev_tick_provider_submitted_proof, - ); - - // Set Provider's deadline for submitting a proof. - // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Check that Provider is not in the SlashableProviders storage map. - assert!(!SlashableProviders::::contains_key(&provider_id)); - - // Advance to the deadline block for this Provider. - run_to_block(prev_deadline); - - // Check event of provider being marked as slashable. - System::assert_has_event( - Event::SlashableProvider { - provider: provider_id, - next_challenge_deadline: prev_deadline + challenge_period, - } - .into(), - ); - - // Check that Provider is in the SlashableProviders storage map. - assert!(SlashableProviders::::contains_key(&provider_id)); - assert_eq!( - SlashableProviders::::get(&provider_id), - Some(::RandomChallengesPerBlock::get()) - ); - - // Check the new last time this provider submitted a proof. - let current_tick_provider_submitted_proof = - prev_tick_provider_submitted_proof + challenge_period; - let new_last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); - assert_eq!( - current_tick_provider_submitted_proof, - new_last_tick_provider_submitted_proof - ); - - // Check that the Provider's deadline was pushed forward. - assert_eq!( - TickToProvidersDeadlines::::get(prev_deadline, provider_id), - None - ); - let new_deadline = - new_last_tick_provider_submitted_proof + challenge_period + challenge_ticks_tolerance; - assert_eq!( - TickToProvidersDeadlines::::get(new_deadline, provider_id), - Some(()), - ); - }); -} - -#[test] -fn multiple_new_challenges_round_provider_accrued_many_failed_proof_submissions() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different from the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - let prev_tick_provider_submitted_proof = ChallengesTicker::::get(); - LastTickProviderSubmittedAProofFor::::insert( - &provider_id, - prev_tick_provider_submitted_proof, - ); - - // New challenges round - let current_tick = ChallengesTicker::::get(); - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Check that Provider is not in the SlashableProviders storage map. - assert!(!SlashableProviders::::contains_key(&provider_id)); - - // Set last checkpoint challenge block. - let checkpoint_challenge_block = 1; - LastCheckpointTick::::set(checkpoint_challenge_block); - - // Make up custom challenges. - let custom_challenges = BoundedVec::try_from(vec![ - (BlakeTwo256::hash(b"custom_challenge_1"), None), - (BlakeTwo256::hash(b"custom_challenge_2"), None), - ]) - .unwrap(); - - // Set custom challenges in checkpoint block. - TickToCheckpointChallenges::::insert( - checkpoint_challenge_block, - custom_challenges.clone(), - ); - - // Advance to the deadline block for this Provider. - run_to_block(prev_deadline); - - let next_challenge_deadline = prev_deadline + challenge_period; - // Check event of provider being marked as slashable. - System::assert_has_event( - Event::SlashableProvider { - provider: provider_id, - next_challenge_deadline, - } - .into(), - ); - - // Check that Provider is in the SlashableProviders storage map. - assert!(SlashableProviders::::contains_key(&provider_id)); - - let random_challenges_per_block: u32 = - ::RandomChallengesPerBlock::get(); - - let missed_proof_submissions = random_challenges_per_block.saturating_add(2); - - assert_eq!( - SlashableProviders::::get(&provider_id), - Some(missed_proof_submissions) - ); - - // New challenges round - let current_tick = ChallengesTicker::::get(); - let prev_deadline = current_tick + challenge_period; - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Advance to the deadline block for this Provider. - run_to_block(next_challenge_deadline); - - // Check event of provider being marked as slashable. - System::assert_has_event( - Event::SlashableProvider { - provider: provider_id, - next_challenge_deadline: prev_deadline + challenge_period, - } - .into(), - ); - - // Check that Provider is in the SlashableProviders storage map. - assert!(SlashableProviders::::contains_key(&provider_id)); - assert_eq!( - SlashableProviders::::get(&provider_id), - Some(random_challenges_per_block.saturating_add(missed_proof_submissions)) - ); - }); -} - -#[test] -fn new_challenges_round_bad_provider_marked_as_slashable_but_good_no() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register Alice as a Provider in Providers pallet. - let alice_provider_id = BlakeTwo256::hash(b"alice_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - alice_provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &alice_provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to Alice and hold some so it has a stake. - let alice_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - alice_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - alice_balance / 100 - )); - - // Register Bob as a Provider in Providers pallet. - let bob_provider_id = BlakeTwo256::hash(b"bob_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &2, - bob_provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &bob_provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 2u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to Bob and hold some so it has a stake. - let bob_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &2, - bob_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &2, - bob_balance / 100 - )); - - // Set Alice and Bob's root to be an arbitrary value, different than the default root, - // to simulate that they are actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &alice_provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - pallet_storage_providers::BackupStorageProviders::::mutate( - &bob_provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Alice and Bob's last submitted proof block. - let current_tick = ChallengesTicker::::get(); - let last_interval_tick = current_tick; - LastTickProviderSubmittedAProofFor::::insert(&alice_provider_id, last_interval_tick); - LastTickProviderSubmittedAProofFor::::insert(&bob_provider_id, last_interval_tick); - - // Set Alice and Bob's deadline for submitting a proof. - // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - alice_provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - TickToProvidersDeadlines::::insert(prev_deadline, alice_provider_id, ()); - TickToProvidersDeadlines::::insert(prev_deadline, bob_provider_id, ()); - - // Check that Alice and Bob are not in the SlashableProviders storage map. - assert!(!SlashableProviders::::contains_key( - &alice_provider_id - )); - assert!(!SlashableProviders::::contains_key(&bob_provider_id)); - - // Advance to the next challenge Alice and Bob should listen to. - let current_block = System::block_number(); - let challenge_block = current_block + challenge_period; - run_to_block(challenge_block); - // Advance less than `ChallengeTicksTolerance` blocks. - let current_block = System::block_number(); - run_to_block(current_block + challenge_ticks_tolerance - 1); - - // Get the seed for block 2. - let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); - - // Calculate challenges from seed, so that we can mock a key proof for each. - let challenges = crate::Pallet::::generate_challenges_from_seed( - seed, - &alice_provider_id, - RandomChallengesPerBlockFor::::get(), - ); - - // Creating a vec of proofs with some content to pass verification. - let mut key_proofs = BTreeMap::new(); - for challenge in challenges { - key_proofs.insert( - challenge, - KeyProof:: { - proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - challenge_count: Default::default(), - }, - ); - } - - // Mock a proof. - let proof = Proof:: { - forest_proof: CompactProof { - encoded_nodes: vec![vec![0]], - }, - key_proofs, - }; - - // Have Alice submit a proof. - assert_ok!(ProofsDealer::submit_proof( - RuntimeOrigin::signed(1), - proof.clone(), - None - )); - - // Check for event submitted. - System::assert_last_event( - Event::ProofAccepted { - provider: alice_provider_id, - proof, - } - .into(), - ); - - // Advance to the deadline block for this Provider. - run_to_block(prev_deadline); - - System::assert_has_event( - Event::SlashableProvider { - provider: bob_provider_id, - next_challenge_deadline: prev_deadline + challenge_period, - } - .into(), - ); - - // Check that Bob is in the SlashableProviders storage map and that Alice is not. - assert!(!SlashableProviders::::contains_key( - &alice_provider_id - )); - assert!(SlashableProviders::::contains_key(&bob_provider_id)); - assert_eq!( - SlashableProviders::::get(&bob_provider_id), - Some(::RandomChallengesPerBlock::get()) - ); - - // Check the new last tick interval for Alice and Bob. - let expected_new_tick = last_interval_tick + challenge_period; - let new_last_interval_tick_alice = - LastTickProviderSubmittedAProofFor::::get(alice_provider_id).unwrap(); - assert_eq!(expected_new_tick, new_last_interval_tick_alice); - let new_last_interval_tick_bob = - LastTickProviderSubmittedAProofFor::::get(bob_provider_id).unwrap(); - assert_eq!(expected_new_tick, new_last_interval_tick_bob); - - assert_eq!( - TickToProvidersDeadlines::::get(prev_deadline, alice_provider_id), - None - ); - assert_eq!( - TickToProvidersDeadlines::::get(prev_deadline, bob_provider_id), - None - ); - - // Check that the both Alice and Bob's deadlines were pushed forward. - let new_deadline = expected_new_tick + challenge_period_plus_tolerance; - assert_eq!( - TickToProvidersDeadlines::::get(new_deadline, alice_provider_id), - Some(()), - ); - assert_eq!( - TickToProvidersDeadlines::::get(new_deadline, bob_provider_id), - Some(()), - ); - }); -} - -#[test] -fn challenges_ticker_paused_works() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Get the current tick. - let current_tick = ChallengesTicker::::get(); - - // Set the challenges ticker to paused. - assert_ok!(ProofsDealer::set_paused(RuntimeOrigin::root(), true)); - - // Assert event emitted. - System::assert_last_event(Event::::ChallengesTickerSet { paused: true }.into()); - - // Advance a number of blocks. - let current_block = System::block_number(); - run_to_block(current_block + 10); - - // Check that the challenges ticker is still the same. - assert_eq!(ChallengesTicker::::get(), current_tick); - - // Unpause the challenges ticker. - assert_ok!(ProofsDealer::set_paused(RuntimeOrigin::root(), false)); - - // Assert event emitted. - System::assert_last_event(Event::::ChallengesTickerSet { paused: false }.into()); - - // Advance a number of blocks. - let current_block = System::block_number(); - run_to_block(current_block + 10); - - // Check that the challenges ticker is now incremented. - assert_eq!(ChallengesTicker::::get(), current_tick + 10); - }); -} - -#[test] -fn challenges_ticker_block_considered_full_with_max_normal_weight() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Simulate a full block. - System::set_block_number(System::block_number() + 1); - - // Starting with `on_poll` hook. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set normal weight used to be the maximum. - let weights: BlockWeights = ::BlockWeights::get(); - let max_weight_normal = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => max_weight_normal, - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(max_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - - // Get the current count of non-full blocks. - let blocks_not_full = NotFullBlocksCount::::get(); - - // In the next block, after executing `on_poll`, `NonFullBlocksCount` should NOT be incremented. - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - assert_eq!(NotFullBlocksCount::::get(), blocks_not_full); - }); -} - -#[test] -fn challenges_ticker_block_considered_full_with_weight_left_smaller_than_headroom() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Simulate an almost full block. - System::set_block_number(System::block_number() + 1); - - // Starting with `on_poll` hook. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set weight used to NOT leave headroom. - let headroom_weight = BlockFullnessHeadroomFor::::get(); - let weights: BlockWeights = ::BlockWeights::get(); - let max_weight_normal = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => max_weight_normal - headroom_weight + Weight::from_parts(1, 0), - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(max_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - - // Get the current count of non-full blocks. - let blocks_not_full = NotFullBlocksCount::::get(); - - // In the next block, after executing `on_poll`, `NonFullBlocksCount` should NOT be incremented. - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - assert_eq!(NotFullBlocksCount::::get(), blocks_not_full); - }); -} - -#[test] -fn challenges_ticker_block_considered_not_full_with_weight_left_equal_to_headroom() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Simulate a block with exactly headroom weight left. - System::set_block_number(System::block_number() + 1); - - // Starting with `on_poll` hook. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set weight used to leave headroom. - let headroom_weight = BlockFullnessHeadroomFor::::get(); - let weights: BlockWeights = ::BlockWeights::get(); - let max_weight_normal = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => max_weight_normal.saturating_sub(headroom_weight), - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(max_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - - // Get the current count of non-full blocks. - let blocks_not_full = NotFullBlocksCount::::get(); - - // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); - }); -} - -#[test] -fn challenges_ticker_block_considered_not_full_with_weight_left_greater_than_headroom() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Simulate a not-full block. - System::set_block_number(System::block_number() + 1); - - // Starting with `on_poll` hook. - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - - // Set weight used to leave headroom. - let headroom_weight = BlockFullnessHeadroomFor::::get(); - let weights: BlockWeights = ::BlockWeights::get(); - let max_weight_normal = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => max_weight_normal.saturating_sub(headroom_weight.mul(2)), - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(max_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - - // Get the current count of non-full blocks. - let blocks_not_full = NotFullBlocksCount::::get(); - - // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); - }); -} - -#[test] -fn challenges_ticker_paused_only_after_tolerance_blocks() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Tick counter should be 1 while in block 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. - let block_fullness_period = BlockFullnessPeriodFor::::get(); - run_to_block_spammed(block_fullness_period); - - // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriodFor`. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period); - - // Go one more block beyond `BlockFullnessPeriodFor`. - // Ticker should stop at this tick. - run_to_block_spammed(block_fullness_period + 1); - - // Assert that now the challenges ticker is paused, and the tick counter stopped at `BlockFullnessPeriodFor` + 1. - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - - // Going one block beyond, shouldn't increment the ticker. - run_to_block(block_fullness_period + 2); - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - }); -} - -#[test] -fn challenges_ticker_paused_when_less_than_min_not_full_blocks_ratio_are_not_full() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Tick counter should be 1 while in block 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // Make sure there are `BlockFullnessPeriod * MinNotFullBlocksRatio` (floor) - // not-spammed blocks. Consider that the first block was not spammed. - let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); - let min_not_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let blocks_to_spam = min_not_full_blocks_ratio - .mul_floor(block_fullness_period) - .saturating_sub(1); - let current_block = System::block_number(); - run_to_block(current_block + blocks_to_spam); - - // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. - run_to_block_spammed(block_fullness_period); - - // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriod`. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period); - - // Go one more block beyond `BlockFullnessPeriod`. - // Ticker should stop at this tick. - run_to_block(block_fullness_period + 1); - - // Assert that now the challenges ticker is paused, and the tick counter stopped at `BlockFullnessPeriod` + 1. - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - - // Going one block beyond, shouldn't increment the ticker. - run_to_block(block_fullness_period + 2); - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - }); -} - -#[test] -fn challenges_ticker_not_paused_when_more_than_min_not_full_blocks_ratio_are_not_full() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Tick counter should be 1 while in block 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // Make sure there are more than `BlockFullnessPeriod * MinNotFullBlocksRatio` (floor) - // not-spammed blocks. Consider that the first block was not spammed. - let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); - let min_not_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let blocks_to_spam = min_not_full_blocks_ratio - .mul_floor(block_fullness_period) - .saturating_sub(1); - let current_block = System::block_number(); - run_to_block(current_block + blocks_to_spam + 1); - - // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. - run_to_block_spammed(block_fullness_period); - - // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriod`. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period); - - // Go one more block beyond `BlockFullnessPeriod`. - // Ticker should NOT stop at this tick. - run_to_block(block_fullness_period + 1); - - // Assert that the challenges ticker is still NOT paused, and the tick counter continues. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - - // Going one block beyond, should increment the ticker. - run_to_block(block_fullness_period + 2); - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 2); - }); -} - -#[test] -fn challenges_ticker_not_paused_when_blocks_dont_run_on_poll() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Simulate multiple non-spammed blocks that don't run on `on_poll`. - // Like multi block migrations. - let block_fullness_period = BlockFullnessPeriodFor::::get(); - for _ in 1..block_fullness_period { - System::set_block_number(System::block_number() + 1); - - // Set weight used to zero (not-spammed). - let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { - DispatchClass::Normal => Zero::zero(), - DispatchClass::Operational => Zero::zero(), - DispatchClass::Mandatory => Zero::zero(), - }); - BlockWeight::::set(max_block_weight); - - // Trigger on_finalize hook execution. - ProofsDealer::on_finalize(System::block_number()); - } - - // Get the current count of non-full blocks. Should be zero as `on_poll` was only run - // once in `run_to_block(1)`, taking into account the genesis block. In other words, not - // adding anything. - let blocks_not_full = NotFullBlocksCount::::get(); - assert_eq!(blocks_not_full, 0); - - // Current ticker should be 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. - System::set_block_number(System::block_number() + 1); - ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); - assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); - }); -} - -#[test] -fn challenges_ticker_unpaused_after_spam_finishes() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Tick counter should be 1 while in block 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // Go until `BlockFullnessPeriod` blocks, with spammed blocks. - let block_fullness_period = BlockFullnessPeriodFor::::get(); - run_to_block_spammed(block_fullness_period); - - // Go one more block beyond `BlockFullnessPeriod`. - // Ticker should stop at this tick. - run_to_block_spammed(block_fullness_period + 1); - - // Going one block beyond, shouldn't increment the ticker. - run_to_block(block_fullness_period + 2); - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - - // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. - // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. - let blocks_not_full = NotFullBlocksCount::::get(); - let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let min_non_full_blocks: u64 = - min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); - let empty_blocks_to_advance = min_non_full_blocks + 1 - blocks_not_full; - - // Advance `empty_blocks_to_advance` blocks. - let current_ticker = ChallengesTicker::::get(); - let current_block = System::block_number(); - run_to_block(current_block + empty_blocks_to_advance); - - // Assert that the challenges ticker is NOT paused, but that the `ChallengesTicker` is still the same. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker); - - // Advance one more block and assert that the challenges ticker increments. - run_to_block(current_block + empty_blocks_to_advance + 1); - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker + 1); - }); -} - -#[test] -fn challenges_ticker_paused_twice() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Tick counter should be 1 while in block 1. - assert_eq!(ChallengesTicker::::get(), 1); - - // Go until `BlockFullnessPeriod` blocks, with spammed blocks. - let block_fullness_period = BlockFullnessPeriodFor::::get(); - run_to_block_spammed(block_fullness_period); - - // Go one more block beyond `BlockFullnessPeriod`. - // Ticker should stop at this tick. - run_to_block_spammed(block_fullness_period + 1); - - // Going one block beyond, shouldn't increment the ticker. - run_to_block(block_fullness_period + 2); - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); - - // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. - // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. - let blocks_not_full = NotFullBlocksCount::::get(); - let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let min_non_full_blocks: u64 = - min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); - let empty_blocks_to_advance = min_non_full_blocks + 1 - blocks_not_full; - - // Advance `empty_blocks_to_advance` blocks. - let current_ticker = ChallengesTicker::::get(); - let current_block = System::block_number(); - run_to_block(current_block + empty_blocks_to_advance); - - // Assert that the challenges ticker is NOT paused, but that the `ChallengesTicker` is still the same. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker); - - // Advance one more block and assert that the challenges ticker increments. - run_to_block(current_block + empty_blocks_to_advance + 1); - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker + 1); - - // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. - // We need to decrease that number so that it is smaller or equal to`BlockFullnessPeriod * MinNotFullBlocksRatio`. - let mut blocks_not_full = NotFullBlocksCount::::get(); - let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let min_non_full_blocks: u64 = - min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); - - // We cannot just spam however many blocks of difference are in `blocks_not_full` - `min_non_full_blocks` - // because the oldest blocks being considered were also spammed. We would be adding new spammed blocks - // in the newest blocks, and removing them from the oldest ones. So we need to spam blocks until non-spammed - // blocks are old enough and start getting discarded. - let mut blocks_advanced = 0; - let current_ticker = ChallengesTicker::::get(); - while blocks_not_full > min_non_full_blocks { - let current_block = System::block_number(); - run_to_block_spammed(current_block + 1); - blocks_not_full = NotFullBlocksCount::::get(); - blocks_advanced += 1; - } - - // Assert that the challenges ticker IS paused, but that the `ChallengesTicker` has advanced `not_empty_blocks_to_advance`. - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!( - ChallengesTicker::::get(), - current_ticker + blocks_advanced - ); - - // Advance one more block and assert that the challenges ticker doesn't increment. - let current_block = System::block_number(); - run_to_block(current_block + 1); - assert!(ChallengesTickerPaused::::get().is_some()); - assert_eq!( - ChallengesTicker::::get(), - current_ticker + blocks_advanced - ); - }); -} - -#[test] -fn challenges_ticker_provider_not_slashed_if_network_spammed() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Go beyond `BlockFullnessPeriod` blocks, with not spammed blocks, to simulate - // an operational scenario already. - let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); - run_to_block(block_fullness_period + 1); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add balance to that Provider and hold some so it has a stake. - let provider_balance = 1_000_000_000_000_000; - assert_ok!(::NativeBalance::mint_into( - &1, - provider_balance - )); - assert_ok!(::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - &1, - provider_balance / 100 - )); - - // Set Provider's root to be an arbitrary value, different than the default root, - // to simulate that it is actually providing a service. - let root = BlakeTwo256::hash(b"1234"); - pallet_storage_providers::BackupStorageProviders::::mutate( - &provider_id, - |provider| { - provider.as_mut().expect("Provider should exist").root = root; - }, - ); - - // Set Provider's last submitted proof block. - let current_tick = ChallengesTicker::::get(); - let prev_tick_provider_submitted_proof = current_tick; - LastTickProviderSubmittedAProofFor::::insert( - &provider_id, - prev_tick_provider_submitted_proof, - ); - - // Set Provider's deadline for submitting a proof. - // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. - let providers_stake = - as ReadChallengeableProvidersInterface>::get_stake( - provider_id, - ) - .unwrap(); - let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); - let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); - let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; - let prev_deadline = current_tick + challenge_period_plus_tolerance; - TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); - - // Check that Provider is not in the SlashableProviders storage map. - assert!(!SlashableProviders::::contains_key(&provider_id)); - - // Up until this point, all blocks have been not-spammed, so the `NotFullBlocksCount` - // should be equal to `BlockFullnessPeriod`. - let current_not_full_blocks_count = NotFullBlocksCount::::get(); - assert_eq!(current_not_full_blocks_count, block_fullness_period); - - // Advance until the next challenge period block without spammed blocks. - let current_block = System::block_number(); - run_to_block(current_block + challenge_period); - - // Advance to the deadline block for this Provider, but with spammed blocks. - run_to_block_spammed(prev_deadline); - - // Check that Provider is NOT in the SlashableProviders storage map. - assert!(!SlashableProviders::::contains_key(&provider_id)); - - // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. - // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. - let mut blocks_not_full = NotFullBlocksCount::::get(); - let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); - let min_non_full_blocks: u64 = - min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); - - let current_ticker = ChallengesTicker::::get(); - while blocks_not_full <= min_non_full_blocks { - let current_block = System::block_number(); - run_to_block(current_block + 1); - blocks_not_full = NotFullBlocksCount::::get(); - } - - // Now the `ChallengesTicker` shouldn't be paused. But current ticker should be the same. - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker); - - // Advancing one more block should increase the `ChallengesTicker` by one. - let current_block = System::block_number(); - run_to_block(current_block + 1); - assert!(ChallengesTickerPaused::::get().is_none()); - assert_eq!(ChallengesTicker::::get(), current_ticker + 1); - - // Get how many blocks until the deadline tick. - let current_ticker = ChallengesTicker::::get(); - let blocks_to_advance = prev_deadline - current_ticker; - let current_block = System::block_number(); - run_to_block(current_block + blocks_to_advance); - - // Check event of provider being marked as slashable. - System::assert_has_event( - Event::SlashableProvider { - provider: provider_id, - next_challenge_deadline: prev_deadline + challenge_period, - } - .into(), - ); - - // Check that Provider is in the SlashableProviders storage map. - assert!(SlashableProviders::::contains_key(&provider_id)); - assert_eq!( - SlashableProviders::::get(&provider_id), - Some(::RandomChallengesPerBlock::get()) - ); - - // Check the new last time this provider submitted a proof. - let current_tick_provider_submitted_proof = - prev_tick_provider_submitted_proof + challenge_period; - let new_last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); - assert_eq!( - current_tick_provider_submitted_proof, - new_last_tick_provider_submitted_proof - ); - - // Check that the Provider's deadline was pushed forward. - assert_eq!( - TickToProvidersDeadlines::::get(prev_deadline, provider_id), - None - ); - let new_deadline = - new_last_tick_provider_submitted_proof + challenge_period + challenge_ticks_tolerance; - assert_eq!( - TickToProvidersDeadlines::::get(new_deadline, provider_id), - Some(()), - ); - }); -} - -#[test] -fn stake_to_challenge_period_saturates_properly() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - let stake_to_challenge_period = StakeToChallengePeriodFor::::get(); - let min_challenge_period: BlockNumberFor = MinChallengePeriodFor::::get(); - - // A provider with stake equal to `StakeToChallengePeriod` should have a challenge period equal to `MinChallengePeriod`. - assert_eq!( - crate::Pallet::::stake_to_challenge_period(stake_to_challenge_period), - min_challenge_period - ); - - // A provider with stake greater than `StakeToChallengePeriod` should have a challenge period equal to `MinChallengePeriod`. - assert_eq!( - crate::Pallet::::stake_to_challenge_period(stake_to_challenge_period * 2), - min_challenge_period - ); - - // A provider with stake 1 should have a challenge period equal to `StakeToChallengePeriod`. - assert_eq!( - crate::Pallet::::stake_to_challenge_period(1) as u128, - stake_to_challenge_period - ); - - // A provider with a stake somewhere in between should have a challenge period given by the formula. - let half_stake_to_challenge_period = - stake_to_challenge_period / (2 * min_challenge_period as u128); - assert_eq!( - crate::Pallet::::stake_to_challenge_period(half_stake_to_challenge_period), - 2 * min_challenge_period - ); - }); -} - -mod on_idle_hook_tests { - use super::*; - - #[test] - fn on_idle_hook_works() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick. - let tick_when_proof_provided = ChallengesTicker::::get(); - let mut new_valid_submitters = - BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); - new_valid_submitters.try_insert(provider_id).unwrap(); - ValidProofSubmittersLastTicks::::insert( - tick_when_proof_provided, - new_valid_submitters, - ); - - // Check that the Provider was successfully added to the set. - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Advance a tick, executing `on_idle`, to check if the Provider is removed from the set. - run_to_block(System::block_number() + 1); - ProofsDealer::on_idle(System::block_number(), Weight::MAX); - - // Check that the set which had the Provider that submitted a valid proof has not been deleted. - assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Check that the last deleted tick is still 0 - assert_eq!(LastDeletedTick::::get(), 0); - - // Advance enough ticks so that the Provider list which has the `provider_id` is set to be deleted. - run_to_block( - System::block_number() - + as Get>::get() as u64, - ); - - // Call the `on_idle` hook. - ProofsDealer::on_idle(System::block_number(), Weight::MAX); - - // Check that the set which had the Provider that submitted a valid proof has been correctly deleted. - assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_none()); - - // Check that the last deleted tick is the one that was just deleted. - assert_eq!( - LastDeletedTick::::get(), - System::block_number() - - as Get>::get() as u64 - ); - }); - } - - #[test] - fn on_idle_hook_does_not_delete_with_not_enough_weight() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick. - let tick_when_proof_provided = ChallengesTicker::::get(); - let mut new_valid_submitters = - BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); - new_valid_submitters.try_insert(provider_id).unwrap(); - ValidProofSubmittersLastTicks::::insert( - tick_when_proof_provided, - new_valid_submitters, - ); - - // Check that the Provider was successfully added to the set. - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Advance a tick, executing `on_idle`, to check if the Provider is removed from the set. - run_to_block(System::block_number() + 1); - ProofsDealer::on_idle(System::block_number(), Weight::MAX); - - // Check that the set which had the Provider that submitted a valid proof has not been deleted. - assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Check that the last deleted tick is still 0 - assert_eq!(LastDeletedTick::::get(), 0); - - // Advance enough ticks so that the Provider list which has the `provider_id` is set to be deleted. - run_to_block( - System::block_number() - + as Get>::get() as u64, - ); - - // Call the `on_idle` hook, but without enough weight to delete the set. - ProofsDealer::on_idle(System::block_number(), Weight::zero()); - - // Check that the set which had the Provider that submitted a valid proof still exists and has the Provider - assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Check that the last deleted tick is still zero. - assert_eq!(LastDeletedTick::::get(), 0); - }); - } - - #[test] - fn on_idle_hook_deletes_multiple_old_ticks_if_enough_weight_is_remaining() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited. - run_to_block(1); // Block number = 1 - - // Register user as a Provider in Providers pallet. - let provider_id = BlakeTwo256::hash(b"provider_id"); - pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( - &1, - provider_id, - ); - pallet_storage_providers::BackupStorageProviders::::insert( - &provider_id, - pallet_storage_providers::types::BackupStorageProvider { - capacity: Default::default(), - capacity_used: Default::default(), - multiaddresses: Default::default(), - root: Default::default(), - last_capacity_change: Default::default(), - owner_account: 1u64, - payment_account: Default::default(), - reputation_weight: - ::StartingReputationWeight::get(), - }, - ); - - // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick and two ticks after that. - let tick_when_first_proof_provided = ChallengesTicker::::get(); // Block number = 1 - let mut new_valid_submitters = - BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); - new_valid_submitters.try_insert(provider_id).unwrap(); - ValidProofSubmittersLastTicks::::insert( - tick_when_first_proof_provided, - new_valid_submitters.clone(), - ); - let tick_when_second_proof_provided = tick_when_first_proof_provided + 2; // Block number = 1 + 2 = 3 - ValidProofSubmittersLastTicks::::insert( - tick_when_second_proof_provided, - new_valid_submitters, - ); - - // Check that the Provider was successfully added to the set in both ticks - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) - .unwrap() - .contains(&provider_id) - ); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Advance a tick, executing `on_idle`, to check if the Provider is removed from any of the two sets. - run_to_block(System::block_number() + 1); // Block number = 2 - ProofsDealer::on_idle(System::block_number(), Weight::MAX); - - // Check that the sets which had the Provider that submitted a valid proof have not been deleted. - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) - .is_some() - ); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) - .unwrap() - .contains(&provider_id) - ); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) - .is_some() - ); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) - .unwrap() - .contains(&provider_id) - ); - - // Check that the last deleted tick is still 0 - assert_eq!(LastDeletedTick::::get(), 0); - - // Advance enough ticks so that both the Provider lists which have the `provider_id` are set to be deleted. - run_to_block( - System::block_number() - + as Get>::get() as u64 - + 3, - ); // Block number = 2 + 3 + 3 = 8, 8 - 3 - 0 > 3 so both sets should be deleted. - - // Call the `on_idle` hook with enough weight to delete both sets. - ProofsDealer::on_idle(System::block_number(), Weight::MAX); - - // Check that the sets which had the Provider that submitted a valid proof have been correctly deleted. - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) - .is_none() - ); - assert!( - ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) - .is_none() - ); - - // Check that the last deleted tick is the one that was just deleted. - assert_eq!( - LastDeletedTick::::get(), - System::block_number() - - as Get>::get() as u64 - ); - }); - } -} +use std::{collections::BTreeMap, vec}; + +use codec::Encode; +use frame_support::{ + assert_err, assert_noop, assert_ok, + dispatch::DispatchClass, + pallet_prelude::Weight, + traits::{ + fungible::{Mutate, MutateHold}, + OnFinalize, OnIdle, OnPoll, + }, + weights::WeightMeter, + BoundedBTreeSet, +}; +use frame_system::{ + limits::BlockWeights, pallet_prelude::BlockNumberFor, BlockWeight, ConsumedWeight, +}; +use pallet_storage_providers::HoldReason; +use shp_traits::{ProofsDealerInterface, ReadChallengeableProvidersInterface, TrieRemoveMutation}; +use sp_core::{blake2_256, Get, Hasher, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, Zero}, + BoundedVec, DispatchError, +}; +use sp_trie::CompactProof; + +use crate::{ + mock::*, + pallet::Event, + types::{ + BlockFullnessHeadroomFor, BlockFullnessPeriodFor, ChallengeHistoryLengthFor, + ChallengeTicksToleranceFor, ChallengesQueueLengthFor, CheckpointChallengePeriodFor, + KeyProof, MaxCustomChallengesPerBlockFor, MaxSubmittersPerTickFor, MinChallengePeriodFor, + MinNotFullBlocksRatioFor, Proof, ProviderIdFor, ProvidersPalletFor, + RandomChallengesPerBlockFor, StakeToChallengePeriodFor, TargetTicksStorageOfSubmittersFor, + }, + ChallengesTicker, ChallengesTickerPaused, LastCheckpointTick, LastDeletedTick, + LastTickProviderSubmittedAProofFor, NotFullBlocksCount, SlashableProviders, + TickToChallengesSeed, TickToCheckpointChallenges, TickToProvidersDeadlines, + ValidProofSubmittersLastTicks, +}; + +fn run_to_block(n: u64) { + while System::block_number() < n { + System::set_block_number(System::block_number() + 1); + + // Trigger on_poll hook execution. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set weight used to be zero. + let zero_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => Zero::zero(), + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(zero_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + } +} + +fn run_to_block_spammed(n: u64) { + while System::block_number() < n { + System::set_block_number(System::block_number() + 1); + + // Trigger on_poll hook execution. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Fill up block. + let weights: BlockWeights = ::BlockWeights::get(); + let max_weight_normal = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + let block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => max_weight_normal, + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + } +} + +#[test] +fn challenge_submit_succeed() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::challenge(user, file_key)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 1, + key_challenged: file_key, + } + .into(), + ); + + // Check user's balance after challenge. + let challenge_fee: u128 = ::ChallengesFee::get(); + assert_eq!( + ::NativeBalance::usable_balance(&1), + user_balance - challenge_fee + ); + + // Check that the challenge is in the queue. + let challenges_queue = crate::ChallengesQueue::::get(); + assert_eq!(challenges_queue.len(), 1); + assert_eq!(challenges_queue[0], file_key); + }); +} + +#[test] +fn challenge_submit_twice_succeed() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create two users and add funds to the accounts. + let user_1 = RuntimeOrigin::signed(1); + let user_2 = RuntimeOrigin::signed(2); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + assert_ok!(::NativeBalance::mint_into( + &2, + user_balance + )); + + // Mock two FileKeys. + let file_key_1 = BlakeTwo256::hash(b"file_key_1"); + let file_key_2 = BlakeTwo256::hash(b"file_key_2"); + + // Dispatch challenge extrinsic twice. + assert_ok!(ProofsDealer::challenge(user_1, file_key_1)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 1, + key_challenged: file_key_1, + } + .into(), + ); + + assert_ok!(ProofsDealer::challenge(user_2, file_key_2)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 2, + key_challenged: file_key_2, + } + .into(), + ); + + // Check users' balance after challenge. + let challenge_fee: u128 = ::ChallengesFee::get(); + assert_eq!( + ::NativeBalance::usable_balance(&1), + user_balance - challenge_fee + ); + assert_eq!( + ::NativeBalance::usable_balance(&2), + user_balance - challenge_fee + ); + + // Check that the challenge is in the queue. + let challenges_queue = crate::ChallengesQueue::::get(); + assert_eq!(challenges_queue.len(), 2); + assert_eq!(challenges_queue[0], file_key_1); + assert_eq!(challenges_queue[1], file_key_2); + }); +} + +#[test] +fn challenge_submit_existing_challenge_succeed() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Dispatch challenge extrinsic twice. + assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); + assert_ok!(ProofsDealer::challenge(user, file_key)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 1, + key_challenged: file_key, + } + .into(), + ); + + // Check user's balance after challenge. + let challenge_fee: u128 = ::ChallengesFee::get(); + assert_eq!( + ::NativeBalance::usable_balance(&1), + user_balance - challenge_fee * 2 + ); + + // Check that the challenge is in the queue. + let challenges_queue = crate::ChallengesQueue::::get(); + assert_eq!(challenges_queue.len(), 1); + assert_eq!(challenges_queue[0], file_key); + }); +} + +#[test] +fn challenge_submit_in_two_rounds_succeed() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Dispatch challenge extrinsic twice. + assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 1, + key_challenged: file_key, + } + .into(), + ); + + // Check user's balance after challenge. + let challenge_fee: u128 = ::ChallengesFee::get(); + assert_eq!( + ::NativeBalance::usable_balance(&1), + user_balance - challenge_fee + ); + + // Check that the challenge is in the queue. + let challenges_queue = crate::ChallengesQueue::::get(); + assert_eq!(challenges_queue.len(), 1); + assert_eq!(challenges_queue[0], file_key); + + // Advance `CheckpointChallengePeriod` blocks. + let challenge_period: u64 = ::CheckpointChallengePeriod::get(); + run_to_block(challenge_period as u64 + 1); + + // Dispatch challenge extrinsic twice. + let file_key = BlakeTwo256::hash(b"file_key_2"); + assert_ok!(ProofsDealer::challenge(user, file_key)); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewChallenge { + who: 1, + key_challenged: file_key, + } + .into(), + ); + + // Check user's balance after challenge. + assert_eq!( + ::NativeBalance::usable_balance(&1), + user_balance - challenge_fee * 2 + ); + }); +} + +#[test] +fn challenge_wrong_origin_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Dispatch challenge extrinsic with wrong origin. + assert_noop!( + ProofsDealer::challenge(RuntimeOrigin::none(), file_key), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn challenge_submit_by_regular_user_with_no_funds_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user with no funds. + let user = RuntimeOrigin::signed(1); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::challenge(user, file_key), + crate::Error::::FeeChargeFailed + ); + }); +} + +#[test] +fn challenge_overflow_challenges_queue_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Fill the challenges queue. + let queue_size: u32 = ::ChallengesQueueLength::get(); + for i in 0..queue_size { + let file_key = BlakeTwo256::hash(&i.to_le_bytes()); + assert_ok!(ProofsDealer::challenge(user.clone(), file_key)); + } + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::challenge(user, file_key), + crate::Error::::ChallengesQueueOverflow + ); + }); +} + +#[test] +fn proofs_dealer_trait_challenge_succeed() { + new_test_ext().execute_with(|| { + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Challenge using trait. + ::challenge(&file_key).unwrap(); + + // Check that the challenge is in the queue. + let challenges_queue = crate::ChallengesQueue::::get(); + assert_eq!(challenges_queue.len(), 1); + assert_eq!(challenges_queue[0], file_key); + }); +} + +#[test] +fn proofs_dealer_trait_challenge_overflow_challenges_queue_fail() { + new_test_ext().execute_with(|| { + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Fill the challenges queue. + let queue_size: u32 = ::ChallengesQueueLength::get(); + for i in 0..queue_size { + let file_key = BlakeTwo256::hash(&i.to_le_bytes()); + assert_ok!(::challenge(&file_key)); + } + + // Dispatch challenge extrinsic. + assert_noop!( + ::challenge(&file_key), + crate::Error::::ChallengesQueueOverflow + ); + }); +} + +#[test] +fn proofs_dealer_trait_challenge_with_priority_succeed() { + new_test_ext().execute_with(|| { + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Challenge using trait. + ::challenge_with_priority( + &file_key, None, + ) + .unwrap(); + + // Check that the challenge is in the queue. + let priority_challenges_queue = crate::PriorityChallengesQueue::::get(); + assert_eq!(priority_challenges_queue.len(), 1); + assert_eq!(priority_challenges_queue[0], (file_key, None)); + }); +} + +#[test] +fn proofs_dealer_trait_challenge_with_priority_overflow_challenges_queue_fail() { + new_test_ext().execute_with(|| { + // Mock a FileKey. + let file_key = BlakeTwo256::hash(b"file_key"); + + // Fill the challenges queue. + let queue_size: u32 = ::ChallengesQueueLength::get(); + for i in 0..queue_size { + let file_key = BlakeTwo256::hash(&i.to_le_bytes()); + assert_ok!( + ::challenge_with_priority( + &file_key, None + ) + ); + } + + // Dispatch challenge extrinsic. + assert_noop!( + ::challenge_with_priority( + &file_key, None + ), + crate::Error::::PriorityChallengesQueueOverflow + ); + }); +} + +#[test] +fn proofs_dealer_trait_initialise_challenge_cycle_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Mock a Provider ID. + let provider_id = BlakeTwo256::hash(b"provider_id"); + + // Register user as a Provider in Providers pallet. + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Dispatch initialise provider extrinsic. + assert_ok!(ProofsDealer::force_initialise_challenge_cycle( + RuntimeOrigin::root(), + provider_id + )); + + // Check that the Provider's last tick was set to 1. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, 1); + + // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` + // after the initialisation. + let stake = as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let expected_deadline = + last_tick_provider_submitted_proof + challenge_period_plus_tolerance; + let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id); + assert_eq!(deadline, Some(())); + + // Check that the last event emitted is the correct one. + System::assert_last_event( + Event::NewChallengeCycleInitialised { + current_tick: 1, + next_challenge_deadline: expected_deadline, + provider: provider_id, + maybe_provider_account: Some(1u64), + } + .into(), + ); + }); +} + +#[test] +fn proofs_dealer_trait_initialise_challenge_cycle_already_initialised_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Mock a Provider ID. + let provider_id = BlakeTwo256::hash(b"provider_id"); + + // Register user as a Provider in Providers pallet. + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Dispatch initialise provider extrinsic. + assert_ok!(ProofsDealer::force_initialise_challenge_cycle( + RuntimeOrigin::root(), + provider_id + )); + + // Check that the Provider's last tick was set to 1. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, 1); + + // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` + // after the initialisation. + let stake = as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = last_tick_provider_submitted_proof + challenge_period_plus_tolerance; + let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id); + assert_eq!(deadline, Some(())); + + // Let some blocks pass (less than `ChallengeTicksTolerance` blocks). + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Re-initialise the provider. + assert_ok!(ProofsDealer::force_initialise_challenge_cycle( + RuntimeOrigin::root(), + provider_id + )); + + // Check that the Provider's last tick is the current now. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id).unwrap(); + let current_tick = ChallengesTicker::::get(); + assert_eq!(last_tick_provider_submitted_proof, current_tick); + + // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` + // after the initialisation. + let stake = as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let expected_deadline = + last_tick_provider_submitted_proof + challenge_period_plus_tolerance; + let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id); + assert_eq!(deadline, Some(())); + + // Check that the Provider no longer has the previous deadline. + let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id); + assert_eq!(deadline, None); + + // Advance beyond the previous deadline block and check that the Provider is not marked as slashable. + run_to_block(current_block + challenge_ticks_tolerance + 1); + + assert!(!SlashableProviders::::contains_key(&provider_id)); + }); +} + +#[test] +fn proofs_dealer_trait_initialise_challenge_cycle_already_initialised_and_new_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Mock two Provider IDs. + let provider_id_1 = BlakeTwo256::hash(b"provider_id_1"); + let provider_id_2 = BlakeTwo256::hash(b"provider_id_2"); + + // Register users as a Provider in Providers pallet. + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id_1, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id_1, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &2, + provider_id_2, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id_2, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 2u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to those Providers and hold some so they have a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::mint_into( + &2, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &2, + provider_balance / 100 + )); + + // Initialise providers + assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_1)); + assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_2)); + + // Check that the Providers' last tick was set to 1. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id_1).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, 1); + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id_2).unwrap(); + assert_eq!(last_tick_provider_submitted_proof, 1); + + // Check that Provider 1's deadline was set to `challenge_period + challenge_ticks_tolerance` + // after the initialisation. + let stake = as ReadChallengeableProvidersInterface>::get_stake( + provider_id_1, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = last_tick_provider_submitted_proof + challenge_period_plus_tolerance; + let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id_1); + assert_eq!(deadline, Some(())); + + // Let some blocks pass (less than `ChallengeTicksTolerance` blocks). + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Re-initialise the provider. + assert_ok!(ProofsDealer::initialise_challenge_cycle(&provider_id_1)); + + // Check that the Provider's last tick is the current now. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(&provider_id_1).unwrap(); + let current_tick = ChallengesTicker::::get(); + assert_eq!(last_tick_provider_submitted_proof, current_tick); + + // Check that the Provider's deadline was set to `challenge_period + challenge_ticks_tolerance` + // after the initialisation. + let stake = as ReadChallengeableProvidersInterface>::get_stake( + provider_id_1, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let expected_deadline = + last_tick_provider_submitted_proof + challenge_period_plus_tolerance; + let deadline = TickToProvidersDeadlines::::get(expected_deadline, provider_id_1); + assert_eq!(deadline, Some(())); + + // Check that the Provider no longer has the previous deadline. + let deadline = TickToProvidersDeadlines::::get(prev_deadline, provider_id_1); + assert_eq!(deadline, None); + + // Advance beyond the previous deadline block and check that the Provider is not marked as slashable. + run_to_block(current_block + challenge_ticks_tolerance + 1); + assert!(!SlashableProviders::::contains_key(&provider_id_1)); + }); +} + +#[test] +fn proofs_dealer_trait_initialise_challenge_cycle_not_provider_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Mock a Provider ID. + let provider_id = BlakeTwo256::hash(b"provider_id"); + + // Expect failure since the user is not a provider. + assert_noop!( + ProofsDealer::initialise_challenge_cycle(&provider_id), + crate::Error::::NotProvider + ); + }); +} + +#[test] +fn submit_proof_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + let current_tick = ChallengesTicker::::get(); + let last_tick_provider_submitted_proof = current_tick; + LastTickProviderSubmittedAProofFor::::insert( + &provider_id, + last_tick_provider_submitted_proof, + ); + + // Set Provider's deadline for submitting a proof. + // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::submit_proof(user, proof.clone(), None)); + + // Check for event submitted. + System::assert_last_event( + Event::ProofAccepted { + provider: provider_id, + proof, + } + .into(), + ); + + // Check the new last time this provider submitted a proof. + let expected_new_tick = last_tick_provider_submitted_proof + challenge_period; + let new_last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); + assert_eq!(expected_new_tick, new_last_tick_provider_submitted_proof); + + // Check that the Provider's deadline was pushed forward. + assert_eq!( + TickToProvidersDeadlines::::get(prev_deadline, provider_id), + None + ); + let new_deadline = expected_new_tick + challenge_period + challenge_ticks_tolerance; + assert_eq!( + TickToProvidersDeadlines::::get(new_deadline, provider_id), + Some(()), + ); + }); +} + +#[test] +fn submit_proof_adds_provider_to_valid_submitters_set() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + let current_tick = ChallengesTicker::::get(); + let last_tick_provider_submitted_proof = current_tick; + LastTickProviderSubmittedAProofFor::::insert( + &provider_id, + last_tick_provider_submitted_proof, + ); + + // Set Provider's deadline for submitting a proof. + // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Advance to the next challenge the Provider should listen to. + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::submit_proof(user, proof.clone(), None)); + + // Check for event submitted. + System::assert_last_event( + Event::ProofAccepted { + provider: provider_id, + proof, + } + .into(), + ); + + // Check that the Provider is in the valid submitters set. + assert!( + ValidProofSubmittersLastTicks::::get(ChallengesTicker::::get()) + .unwrap() + .contains(&provider_id) + ); + }); +} + +#[test] +fn submit_proof_submitted_by_not_a_provider_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register user as a Provider in Providers pallet. + // The registered Provider ID will be different from the one that will be used in the proof. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::submit_proof( + RuntimeOrigin::signed(2), + proof, + Some(provider_id) + )); + }); +} + +#[test] +fn submit_proof_with_checkpoint_challenges_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof tick. + let last_tick_provider_submitted_proof = System::block_number(); + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let mut challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Set last checkpoint challenge block to be equal to the last tick this provider has submitted + // a proof for, so that custom challenges will be taken into account in proof verification. + let checkpoint_challenge_block = last_tick_provider_submitted_proof; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Make up custom challenges. + let custom_challenges = BoundedVec::try_from(vec![ + (BlakeTwo256::hash(b"custom_challenge_1"), None), + (BlakeTwo256::hash(b"custom_challenge_2"), None), + ]) + .unwrap(); + + // Set custom challenges in checkpoint block. + TickToCheckpointChallenges::::insert( + checkpoint_challenge_block, + custom_challenges.clone(), + ); + + // Add custom challenges to the challenges vector. + challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in &challenges { + key_proofs.insert( + *challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::submit_proof(user, proof, None)); + }); +} + +#[test] +fn submit_proof_with_checkpoint_challenges_mutations_success() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: 1000, + capacity_used: 100, + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Increment the used capacity of BSPs in the Providers pallet. + pallet_storage_providers::UsedBspsCapacity::::set(100); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Create a dynamic-rate payment stream between the user and the Provider. + pallet_payment_streams::DynamicRatePaymentStreams::::insert( + &provider_id, + &1, + pallet_payment_streams::types::DynamicRatePaymentStream { + amount_provided: 10, + price_index_when_last_charged: pallet_payment_streams::AccumulatedPriceIndex::::get(), + user_deposit: 10 * <::NewStreamDeposit as Get>::get() as u128 * pallet_payment_streams::CurrentPricePerUnitPerTick::::get(), + out_of_funds_tick: None, + }, + ); + + // Set Provider's last submitted proof tick. + let last_tick_provider_submitted_proof = System::block_number(); + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let mut challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Set last checkpoint challenge block to be equal to the last tick this provider has submitted + // a proof for, so that custom challenges will be taken into account in proof verification. + let checkpoint_challenge_block = last_tick_provider_submitted_proof; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Make up custom challenges. + let custom_challenges = BoundedVec::try_from(vec![ + ( + BlakeTwo256::hash(b"custom_challenge_1"), + Some(TrieRemoveMutation::default()), + ), + ( + BlakeTwo256::hash(b"custom_challenge_2"), + Some(TrieRemoveMutation::default()), + ), + ]) + .unwrap(); + + // Set custom challenges in checkpoint block. + TickToCheckpointChallenges::::insert( + checkpoint_challenge_block, + custom_challenges.clone(), + ); + + // Add custom challenges to the challenges vector. + challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in &challenges { + key_proofs.insert( + *challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + assert_ok!(ProofsDealer::submit_proof(user, proof, None)); + + // Check that the event for mutations applied is emitted. + System::assert_has_event( + Event::MutationsApplied { + provider: provider_id, + mutations: custom_challenges + .iter() + .map(|(key, mutation)| (*key, mutation.clone().unwrap())) + .collect(), + new_root: challenges.last().unwrap().clone(), + } + .into(), + ); + + // Check if root of the provider was updated the last challenge key + // Note: The apply_delta method is applying the mutation the root of the provider for every challenge key. + // This is to avoid having to construct valid tries and proofs. + let root = + <::ProvidersPallet as ReadChallengeableProvidersInterface>::get_root(provider_id) + .unwrap(); + assert_eq!(root.as_ref(), challenges.last().unwrap().as_ref()); + }); +} + +#[test] +fn submit_proof_with_checkpoint_challenges_mutations_fails_if_decoded_metadata_is_invalid() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: 1000, + capacity_used: 100, + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Increment the used capacity of BSPs in the Providers pallet. + pallet_storage_providers::UsedBspsCapacity::::set(100); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Create a dynamic-rate payment stream between the user and the Provider. + pallet_payment_streams::DynamicRatePaymentStreams::::insert( + &provider_id, + &1, + pallet_payment_streams::types::DynamicRatePaymentStream { + amount_provided: 10, + price_index_when_last_charged: + pallet_payment_streams::AccumulatedPriceIndex::::get(), + user_deposit: 10 + * <::NewStreamDeposit as Get>::get( + ) as u128 + * pallet_payment_streams::CurrentPricePerUnitPerTick::::get(), + out_of_funds_tick: None, + }, + ); + + // Set Provider's last submitted proof tick. + let last_tick_provider_submitted_proof = System::block_number(); + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let mut challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Set last checkpoint challenge block to be equal to the last tick this provider has submitted + // a proof for, so that custom challenges will be taken into account in proof verification. + let checkpoint_challenge_block = last_tick_provider_submitted_proof; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Make up custom challenges. + let custom_challenges = BoundedVec::try_from(vec![ + ( + [0; BlakeTwo256::LENGTH].into(), // Challenge that will return invalid metadata + Some(TrieRemoveMutation::default()), + ), + ( + BlakeTwo256::hash(b"custom_challenge_2"), + Some(TrieRemoveMutation::default()), + ), + ]) + .unwrap(); + + // Set custom challenges in checkpoint block. + TickToCheckpointChallenges::::insert( + checkpoint_challenge_block, + custom_challenges.clone(), + ); + + // Add custom challenges to the challenges vector. + challenges.extend(custom_challenges.iter().map(|(challenge, _)| *challenge)); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in &challenges { + key_proofs.insert( + *challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic and it fails because of the invalid metadata. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::FailedToApplyDelta + ); + }); +} + +#[test] +fn submit_proof_caller_not_a_provider_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs: Default::default(), + }; + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::NotProvider + ); + }); +} + +#[test] +fn submit_proof_provider_passed_not_registered_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs: Default::default(), + }; + + // Creating a Provider ID but not registering it. + let provider_id = BlakeTwo256::hash(b"provider_id"); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, Some(provider_id)), + crate::Error::::NotProvider + ); + }); +} + +#[test] +fn submit_proof_empty_key_proofs_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs: Default::default(), + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::EmptyKeyProofs + ); + }); +} + +#[test] +fn submit_proof_no_record_of_last_proof_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::NoRecordOfLastSubmittedProof + ); + }); +} + +#[test] +fn submit_proof_challenges_block_not_reached_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::ChallengesTickNotReached + ); + }); +} + +#[test] +#[should_panic( + expected = "internal error: entered unreachable code: Challenges tick is too old, beyond the history this pallet keeps track of. This should not be possible." +)] +fn submit_proof_challenges_block_too_old_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); + + // Advance more than `ChallengeHistoryLength` blocks. + let challenge_history_length: u64 = ChallengeHistoryLengthFor::::get(); + run_to_block(challenge_history_length * 2); + + // Dispatch challenge extrinsic. + let _ = ProofsDealer::submit_proof(user, proof, None); + }); +} + +#[test] +#[should_panic( + expected = "internal error: entered unreachable code: Seed for challenges tick not found, when checked it should be within history." +)] +fn submit_proof_seed_not_found_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, 1); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Remove challenge seed for challenge block. + TickToChallengesSeed::::remove(challenge_block); + + // Dispatch challenge extrinsic. + let _ = ProofsDealer::submit_proof(user, proof, None); + }); +} + +#[test] +#[should_panic( + expected = "internal error: entered unreachable code: Checkpoint challenges not found, when dereferencing in last registered checkpoint challenge block." +)] +fn submit_proof_checkpoint_challenge_not_found_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: (2 * 100) as u64, + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Set random seed for this block challenges. + let seed = BlakeTwo256::hash(b"seed"); + TickToChallengesSeed::::insert(System::block_number(), seed); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Set last checkpoint challenge block to something before the challenge tick + // that is being submitted. + let checkpoint_challenge_block = 1; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Dispatch challenge extrinsic. + let _ = ProofsDealer::submit_proof(user, proof, None); + }); +} + +#[test] +fn submit_proof_forest_proof_verification_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Mock key proofs. + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + + // Create an empty forest proof to fail verification. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Dispatch challenge extrinsic. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::ForestProofVerificationFailed + ); + }); +} + +#[test] +fn submit_proof_no_key_proofs_for_keys_verified_in_forest_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Creating empty key proof to fail verification. + let mut key_proofs = BTreeMap::new(); + key_proofs.insert( + BlakeTwo256::hash(b"key"), + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![], + }, + challenge_count: Default::default(), + }, + ); + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Dispatch challenge extrinsic. + // The forest proof will pass because it's not empty, so the MockVerifier will accept it, + // and it will return the generated challenges as keys proven. The key proofs are an empty + // vector, so it will fail saying that there are no key proofs for the keys proven. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::KeyProofNotFound + ); + }); +} + +#[test] +fn submit_proof_out_checkpoint_challenges_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Set random seed for this block challenges. + let seed = BlakeTwo256::hash(b"seed"); + TickToChallengesSeed::::insert(System::block_number(), seed); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Set last checkpoint challenge block. + let checkpoint_challenge_block = System::block_number() + 1; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Make up custom challenges. + let custom_challenges = BoundedVec::try_from(vec![ + (BlakeTwo256::hash(b"custom_challenge_1"), None), + (BlakeTwo256::hash(b"custom_challenge_2"), None), + ]) + .unwrap(); + + // Set custom challenges in checkpoint block. + TickToCheckpointChallenges::::insert( + checkpoint_challenge_block, + custom_challenges.clone(), + ); + + // Creating a vec of empty key proofs for each challenge, to fail verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Dispatch challenge extrinsic. + // The forest proof will pass because it's not empty, so the MockVerifier will accept it, + // and it will return the generated challenges as keys proven. The key proofs only contain + // proofs for the regular challenges, not the checkpoint challenges, so it will fail saying + // that there are no key proofs for the keys proven. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::KeyProofNotFound + ); + }); +} + +#[test] +fn submit_proof_key_proof_verification_fail() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Create user and add funds to the account. + let user = RuntimeOrigin::signed(1); + let user_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + user_balance + )); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Hold some of the Provider's balance so it simulates it having a stake. + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + user_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + LastTickProviderSubmittedAProofFor::::insert(&provider_id, System::block_number()); + + // Advance to the next challenge the Provider should listen to. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for challenge block. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Creating a vec of empty key proofs for each challenge, to fail verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Dispatch challenge extrinsic. + // The forest proof will pass because it's not empty, so the MockVerifier will accept it, + // and it will return the generated challenges as keys proven. There will be key proofs + // for each key proven, but they are empty, so it will fail saying that the verification + // failed. + assert_noop!( + ProofsDealer::submit_proof(user, proof, None), + crate::Error::::KeyProofVerificationFailed + ); + }); +} + +#[test] +fn new_challenges_round_random_and_checkpoint_challenges() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Run a block and check that the random challenge was emitted. + run_to_block(2); + + // Build the expected random seed. + let challenges_ticker = ChallengesTicker::::get().encode(); + let challenges_ticker: &[u8] = challenges_ticker.as_ref(); + let subject = [challenges_ticker, &System::block_number().to_le_bytes()].concat(); + let hashed_subject = blake2_256(&subject); + let expected_seed = H256::from_slice(&hashed_subject); + + // Check that the event is emitted. + // This would be the first time the random seed is emitted. + System::assert_last_event( + Event::NewChallengeSeed { + challenges_ticker: 2, + seed: expected_seed, + } + .into(), + ); + + // Run another block and check that the random challenge was emitted. + run_to_block(3); + + // Build the expected random seed. + let challenges_ticker = ChallengesTicker::::get().encode(); + let challenges_ticker: &[u8] = challenges_ticker.as_ref(); + let subject: Vec = [ + challenges_ticker, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + let hashed_subject = blake2_256(&subject); + let expected_seed = H256::from_slice(&hashed_subject); + + // Check that the event is emitted. + // This would be the second time the random seed is emitted. + System::assert_last_event( + Event::NewChallengeSeed { + challenges_ticker: 3, + seed: expected_seed, + } + .into(), + ); + + // Run until the next checkpoint challenge block. + let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); + run_to_block(checkpoint_challenge_period); + + // Expect an empty set of checkpoint challenges. + let challenges_ticker = ChallengesTicker::::get(); + let checkpoint_challenges = + TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); + assert_eq!(checkpoint_challenges.len(), 0); + + // Check that the event is emitted. + System::assert_last_event( + Event::NewCheckpointChallenge { + challenges_ticker, + challenges: Default::default(), + } + .into(), + ); + }); +} + +#[test] +fn new_challenges_round_random_challenges_cleanup() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Run until block number `ChallengesHistoryLength` + 1. + let challenges_history_length: u64 = ChallengeHistoryLengthFor::::get(); + run_to_block(challenges_history_length + 1u64); + + // Check that the challenge seed for block 1 is not found. + assert_eq!( + TickToChallengesSeed::::get(1), + None, + "Challenge seed for block 1 should not be found." + ); + + // Check that the challenge seed exists for block 2. + let challenges_ticker = 2u64.encode(); + let challenges_ticker: &[u8] = challenges_ticker.as_ref(); + let subject: Vec = [challenges_ticker, &2u64.to_le_bytes()].concat(); + let hashed_subject = blake2_256(&subject); + let expected_seed = H256::from_slice(&hashed_subject); + assert_eq!( + TickToChallengesSeed::::get(2), + Some(expected_seed), + "Challenge seed for block 2 should be found." + ); + }); +} + +#[test] +fn new_challenges_round_checkpoint_challenges_cleanup() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Run until block number 2 * `CheckpointChallengePeriod`. + let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); + run_to_block(checkpoint_challenge_period * 2); + + // Check that the checkpoint challenge for block `checkpoint_challenge_period` is not found. + assert_eq!( + TickToCheckpointChallenges::::get(checkpoint_challenge_period), + None, + "Checkpoint challenge for block `CheckpointChallengePeriod` should not be found." + ); + + // Check that the checkpoint challenge exists for block `checkpoint_challenge_period * 2`. + assert_eq!( + TickToCheckpointChallenges::::get(checkpoint_challenge_period * 2), + Some(Default::default()), + "Checkpoint challenge for block `CheckpointChallengePeriod * 2` should be found." + ) + }); +} + +#[test] +fn new_challenges_round_checkpoint_challenges_with_custom_challenges() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Add custom challenges to the challenges vector. + let key_challenged = BlakeTwo256::hash(b"key_challenged"); + assert_ok!( as ProofsDealerInterface>::challenge( + &key_challenged + )); + + // Add priority challenge to the challenges vector. + let priority_key_challenged = BlakeTwo256::hash(b"priority_key_challenged"); + assert_ok!( + as ProofsDealerInterface>::challenge_with_priority( + &priority_key_challenged, + Some(TrieRemoveMutation::default()) + ) + ); + + // Run until the next checkpoint challenge block. + let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); + run_to_block(checkpoint_challenge_period); + + // Expect checkpoint challenges to be emitted, with the priority first. + let challenges_ticker = ChallengesTicker::::get(); + let checkpoint_challenges = + TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); + assert_eq!(checkpoint_challenges.len(), 2); + assert_eq!( + checkpoint_challenges[0], + (priority_key_challenged, Some(TrieRemoveMutation::default())) + ); + assert_eq!(checkpoint_challenges[1], (key_challenged, None)); + }); +} + +#[test] +fn new_challenges_round_max_custom_challenges() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Add max amount of custom challenges to the challenges vector. + let max_custom_challenges = ChallengesQueueLengthFor::::get(); + for i in 0..max_custom_challenges { + let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); + assert_ok!( as ProofsDealerInterface>::challenge( + &key_challenged + )); + } + + // Add another custom challenge. It should fail. + assert_err!( + as ProofsDealerInterface>::challenge(&BlakeTwo256::hash( + b"key_challenged" + )), + crate::Error::::ChallengesQueueOverflow + ); + + // Add max amount of priority challenges to the challenges vector. + let max_priority_challenges = ChallengesQueueLengthFor::::get(); + for i in 0..max_priority_challenges { + let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); + assert_ok!( + as ProofsDealerInterface>::challenge_with_priority( + &key_challenged, + Some(TrieRemoveMutation::default()) + ) + ); + } + + // Add another priority challenge. It should fail. + assert_err!( + as ProofsDealerInterface>::challenge_with_priority( + &BlakeTwo256::hash(b"key_challenged"), + Some(TrieRemoveMutation::default()) + ), + crate::Error::::PriorityChallengesQueueOverflow + ); + + // Check how many checkpoint challenges round are needed to evacuate all the queue. + let queue_length: u32 = ChallengesQueueLengthFor::::get(); + let custom_challenges_per_round: u32 = MaxCustomChallengesPerBlockFor::::get(); + let mut checkpoint_challenge_rounds_needed = queue_length / custom_challenges_per_round; + if queue_length % custom_challenges_per_round != 0 { + checkpoint_challenge_rounds_needed += 1; + } + + // Run until the next checkpoint challenge round. + let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); + run_to_block(checkpoint_challenge_period); + + // Expect checkpoint challenges to be emitted, with the priority first. + let challenges_ticker = ChallengesTicker::::get(); + let checkpoint_challenges = + TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); + assert_eq!( + checkpoint_challenges.len(), + custom_challenges_per_round as usize + ); + for i in 0..checkpoint_challenges.len() { + let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); + assert_eq!( + checkpoint_challenges[i], + (key_challenged, Some(TrieRemoveMutation::default())) + ); + } + + // Run until the needed checkpoint challenge block. + let checkpoint_challenge_period: u64 = CheckpointChallengePeriodFor::::get(); + run_to_block(checkpoint_challenge_period * checkpoint_challenge_rounds_needed as u64); + + // The length of the checkpoint challenges should be max, because even if the priority + // challenges don't fill the queue, the custom challenges will. + let challenges_ticker = ChallengesTicker::::get(); + let checkpoint_challenges = + TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); + assert_eq!( + checkpoint_challenges.len(), + custom_challenges_per_round as usize + ); + + // Expect the last priority challenges in the priority queue to be emitted first. + let last_priority_challenges_amount = if queue_length % custom_challenges_per_round == 0 { + custom_challenges_per_round + } else { + queue_length % custom_challenges_per_round + }; + let last_priority_challenges_start_index = + (checkpoint_challenge_rounds_needed - 1) * custom_challenges_per_round; + for i in 0..last_priority_challenges_amount { + let key_challenged = BlakeTwo256::hash( + &((last_priority_challenges_start_index + i) as usize).to_le_bytes(), + ); + assert_eq!( + checkpoint_challenges[i as usize], + (key_challenged, Some(TrieRemoveMutation::default())) + ); + } + + // Check that the last checkpoint challenges contain the custom challenges, if there was + // enough space in this challenge round. + let checkpoint_challenges_start_index = if queue_length % custom_challenges_per_round == 0 { + custom_challenges_per_round + } else { + queue_length % custom_challenges_per_round + }; + let checkpoint_challenges_amount = + custom_challenges_per_round - checkpoint_challenges_start_index; + for i in 0..checkpoint_challenges_amount { + let key_challenged = BlakeTwo256::hash(&(i as usize).to_le_bytes()); + assert_eq!( + checkpoint_challenges[(checkpoint_challenges_start_index + i) as usize], + (key_challenged, None) + ); + } + + // Run until the custom challenges are all evacuated. + let mut checkpoint_challenge_rounds_needed = queue_length / custom_challenges_per_round * 2; + if queue_length % custom_challenges_per_round != 0 { + checkpoint_challenge_rounds_needed += 1; + } + run_to_block(checkpoint_challenge_period * checkpoint_challenge_rounds_needed as u64); + + // The last checkpoint challenge should be the last custom challenge. + let challenges_ticker = ChallengesTicker::::get(); + let checkpoint_challenges = + TickToCheckpointChallenges::::get(challenges_ticker).unwrap(); + let last_checkpoint_challenge = &checkpoint_challenges[checkpoint_challenges.len() - 1]; + assert_eq!( + last_checkpoint_challenge, + &( + BlakeTwo256::hash(&((queue_length - 1) as usize).to_le_bytes()), + None + ) + ) + }); +} + +#[test] +fn new_challenges_round_provider_marked_as_slashable() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + let current_tick = ChallengesTicker::::get(); + let prev_tick_provider_submitted_proof = current_tick; + LastTickProviderSubmittedAProofFor::::insert( + &provider_id, + prev_tick_provider_submitted_proof, + ); + + // Set Provider's deadline for submitting a proof. + // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Check that Provider is not in the SlashableProviders storage map. + assert!(!SlashableProviders::::contains_key(&provider_id)); + + // Advance to the deadline block for this Provider. + run_to_block(prev_deadline); + + // Check event of provider being marked as slashable. + System::assert_has_event( + Event::SlashableProvider { + provider: provider_id, + next_challenge_deadline: prev_deadline + challenge_period, + } + .into(), + ); + + // Check that Provider is in the SlashableProviders storage map. + assert!(SlashableProviders::::contains_key(&provider_id)); + assert_eq!( + SlashableProviders::::get(&provider_id), + Some(::RandomChallengesPerBlock::get()) + ); + + // Check the new last time this provider submitted a proof. + let current_tick_provider_submitted_proof = + prev_tick_provider_submitted_proof + challenge_period; + let new_last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); + assert_eq!( + current_tick_provider_submitted_proof, + new_last_tick_provider_submitted_proof + ); + + // Check that the Provider's deadline was pushed forward. + assert_eq!( + TickToProvidersDeadlines::::get(prev_deadline, provider_id), + None + ); + let new_deadline = + new_last_tick_provider_submitted_proof + challenge_period + challenge_ticks_tolerance; + assert_eq!( + TickToProvidersDeadlines::::get(new_deadline, provider_id), + Some(()), + ); + }); +} + +#[test] +fn multiple_new_challenges_round_provider_accrued_many_failed_proof_submissions() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different from the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + let prev_tick_provider_submitted_proof = ChallengesTicker::::get(); + LastTickProviderSubmittedAProofFor::::insert( + &provider_id, + prev_tick_provider_submitted_proof, + ); + + // New challenges round + let current_tick = ChallengesTicker::::get(); + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Check that Provider is not in the SlashableProviders storage map. + assert!(!SlashableProviders::::contains_key(&provider_id)); + + // Set last checkpoint challenge block. + let checkpoint_challenge_block = 1; + LastCheckpointTick::::set(checkpoint_challenge_block); + + // Make up custom challenges. + let custom_challenges = BoundedVec::try_from(vec![ + (BlakeTwo256::hash(b"custom_challenge_1"), None), + (BlakeTwo256::hash(b"custom_challenge_2"), None), + ]) + .unwrap(); + + // Set custom challenges in checkpoint block. + TickToCheckpointChallenges::::insert( + checkpoint_challenge_block, + custom_challenges.clone(), + ); + + // Advance to the deadline block for this Provider. + run_to_block(prev_deadline); + + let next_challenge_deadline = prev_deadline + challenge_period; + // Check event of provider being marked as slashable. + System::assert_has_event( + Event::SlashableProvider { + provider: provider_id, + next_challenge_deadline, + } + .into(), + ); + + // Check that Provider is in the SlashableProviders storage map. + assert!(SlashableProviders::::contains_key(&provider_id)); + + let random_challenges_per_block: u32 = + ::RandomChallengesPerBlock::get(); + + let missed_proof_submissions = random_challenges_per_block.saturating_add(2); + + assert_eq!( + SlashableProviders::::get(&provider_id), + Some(missed_proof_submissions) + ); + + // New challenges round + let current_tick = ChallengesTicker::::get(); + let prev_deadline = current_tick + challenge_period; + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Advance to the deadline block for this Provider. + run_to_block(next_challenge_deadline); + + // Check event of provider being marked as slashable. + System::assert_has_event( + Event::SlashableProvider { + provider: provider_id, + next_challenge_deadline: prev_deadline + challenge_period, + } + .into(), + ); + + // Check that Provider is in the SlashableProviders storage map. + assert!(SlashableProviders::::contains_key(&provider_id)); + assert_eq!( + SlashableProviders::::get(&provider_id), + Some(random_challenges_per_block.saturating_add(missed_proof_submissions)) + ); + }); +} + +#[test] +fn new_challenges_round_bad_provider_marked_as_slashable_but_good_no() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register Alice as a Provider in Providers pallet. + let alice_provider_id = BlakeTwo256::hash(b"alice_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + alice_provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &alice_provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to Alice and hold some so it has a stake. + let alice_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + alice_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + alice_balance / 100 + )); + + // Register Bob as a Provider in Providers pallet. + let bob_provider_id = BlakeTwo256::hash(b"bob_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &2, + bob_provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &bob_provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 2u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to Bob and hold some so it has a stake. + let bob_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &2, + bob_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &2, + bob_balance / 100 + )); + + // Set Alice and Bob's root to be an arbitrary value, different than the default root, + // to simulate that they are actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &alice_provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + pallet_storage_providers::BackupStorageProviders::::mutate( + &bob_provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Alice and Bob's last submitted proof block. + let current_tick = ChallengesTicker::::get(); + let last_interval_tick = current_tick; + LastTickProviderSubmittedAProofFor::::insert(&alice_provider_id, last_interval_tick); + LastTickProviderSubmittedAProofFor::::insert(&bob_provider_id, last_interval_tick); + + // Set Alice and Bob's deadline for submitting a proof. + // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + alice_provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + TickToProvidersDeadlines::::insert(prev_deadline, alice_provider_id, ()); + TickToProvidersDeadlines::::insert(prev_deadline, bob_provider_id, ()); + + // Check that Alice and Bob are not in the SlashableProviders storage map. + assert!(!SlashableProviders::::contains_key( + &alice_provider_id + )); + assert!(!SlashableProviders::::contains_key(&bob_provider_id)); + + // Advance to the next challenge Alice and Bob should listen to. + let current_block = System::block_number(); + let challenge_block = current_block + challenge_period; + run_to_block(challenge_block); + // Advance less than `ChallengeTicksTolerance` blocks. + let current_block = System::block_number(); + run_to_block(current_block + challenge_ticks_tolerance - 1); + + // Get the seed for block 2. + let seed = TickToChallengesSeed::::get(challenge_block).unwrap(); + + // Calculate challenges from seed, so that we can mock a key proof for each. + let challenges = crate::Pallet::::generate_challenges_from_seed( + seed, + &alice_provider_id, + RandomChallengesPerBlockFor::::get(), + ); + + // Creating a vec of proofs with some content to pass verification. + let mut key_proofs = BTreeMap::new(); + for challenge in challenges { + key_proofs.insert( + challenge, + KeyProof:: { + proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + challenge_count: Default::default(), + }, + ); + } + + // Mock a proof. + let proof = Proof:: { + forest_proof: CompactProof { + encoded_nodes: vec![vec![0]], + }, + key_proofs, + }; + + // Have Alice submit a proof. + assert_ok!(ProofsDealer::submit_proof( + RuntimeOrigin::signed(1), + proof.clone(), + None + )); + + // Check for event submitted. + System::assert_last_event( + Event::ProofAccepted { + provider: alice_provider_id, + proof, + } + .into(), + ); + + // Advance to the deadline block for this Provider. + run_to_block(prev_deadline); + + System::assert_has_event( + Event::SlashableProvider { + provider: bob_provider_id, + next_challenge_deadline: prev_deadline + challenge_period, + } + .into(), + ); + + // Check that Bob is in the SlashableProviders storage map and that Alice is not. + assert!(!SlashableProviders::::contains_key( + &alice_provider_id + )); + assert!(SlashableProviders::::contains_key(&bob_provider_id)); + assert_eq!( + SlashableProviders::::get(&bob_provider_id), + Some(::RandomChallengesPerBlock::get()) + ); + + // Check the new last tick interval for Alice and Bob. + let expected_new_tick = last_interval_tick + challenge_period; + let new_last_interval_tick_alice = + LastTickProviderSubmittedAProofFor::::get(alice_provider_id).unwrap(); + assert_eq!(expected_new_tick, new_last_interval_tick_alice); + let new_last_interval_tick_bob = + LastTickProviderSubmittedAProofFor::::get(bob_provider_id).unwrap(); + assert_eq!(expected_new_tick, new_last_interval_tick_bob); + + assert_eq!( + TickToProvidersDeadlines::::get(prev_deadline, alice_provider_id), + None + ); + assert_eq!( + TickToProvidersDeadlines::::get(prev_deadline, bob_provider_id), + None + ); + + // Check that the both Alice and Bob's deadlines were pushed forward. + let new_deadline = expected_new_tick + challenge_period_plus_tolerance; + assert_eq!( + TickToProvidersDeadlines::::get(new_deadline, alice_provider_id), + Some(()), + ); + assert_eq!( + TickToProvidersDeadlines::::get(new_deadline, bob_provider_id), + Some(()), + ); + }); +} + +#[test] +fn challenges_ticker_paused_works() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Get the current tick. + let current_tick = ChallengesTicker::::get(); + + // Set the challenges ticker to paused. + assert_ok!(ProofsDealer::set_paused(RuntimeOrigin::root(), true)); + + // Assert event emitted. + System::assert_last_event(Event::::ChallengesTickerSet { paused: true }.into()); + + // Advance a number of blocks. + let current_block = System::block_number(); + run_to_block(current_block + 10); + + // Check that the challenges ticker is still the same. + assert_eq!(ChallengesTicker::::get(), current_tick); + + // Unpause the challenges ticker. + assert_ok!(ProofsDealer::set_paused(RuntimeOrigin::root(), false)); + + // Assert event emitted. + System::assert_last_event(Event::::ChallengesTickerSet { paused: false }.into()); + + // Advance a number of blocks. + let current_block = System::block_number(); + run_to_block(current_block + 10); + + // Check that the challenges ticker is now incremented. + assert_eq!(ChallengesTicker::::get(), current_tick + 10); + }); +} + +#[test] +fn challenges_ticker_block_considered_full_with_max_normal_weight() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Simulate a full block. + System::set_block_number(System::block_number() + 1); + + // Starting with `on_poll` hook. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set normal weight used to be the maximum. + let weights: BlockWeights = ::BlockWeights::get(); + let max_weight_normal = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => max_weight_normal, + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(max_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + + // Get the current count of non-full blocks. + let blocks_not_full = NotFullBlocksCount::::get(); + + // In the next block, after executing `on_poll`, `NonFullBlocksCount` should NOT be incremented. + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + assert_eq!(NotFullBlocksCount::::get(), blocks_not_full); + }); +} + +#[test] +fn challenges_ticker_block_considered_full_with_weight_left_smaller_than_headroom() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Simulate an almost full block. + System::set_block_number(System::block_number() + 1); + + // Starting with `on_poll` hook. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set weight used to NOT leave headroom. + let headroom_weight = BlockFullnessHeadroomFor::::get(); + let weights: BlockWeights = ::BlockWeights::get(); + let max_weight_normal = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => max_weight_normal - headroom_weight + Weight::from_parts(1, 0), + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(max_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + + // Get the current count of non-full blocks. + let blocks_not_full = NotFullBlocksCount::::get(); + + // In the next block, after executing `on_poll`, `NonFullBlocksCount` should NOT be incremented. + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + assert_eq!(NotFullBlocksCount::::get(), blocks_not_full); + }); +} + +#[test] +fn challenges_ticker_block_considered_not_full_with_weight_left_equal_to_headroom() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Simulate a block with exactly headroom weight left. + System::set_block_number(System::block_number() + 1); + + // Starting with `on_poll` hook. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set weight used to leave headroom. + let headroom_weight = BlockFullnessHeadroomFor::::get(); + let weights: BlockWeights = ::BlockWeights::get(); + let max_weight_normal = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => max_weight_normal.saturating_sub(headroom_weight), + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(max_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + + // Get the current count of non-full blocks. + let blocks_not_full = NotFullBlocksCount::::get(); + + // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); + }); +} + +#[test] +fn challenges_ticker_block_considered_not_full_with_weight_left_greater_than_headroom() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Simulate a not-full block. + System::set_block_number(System::block_number() + 1); + + // Starting with `on_poll` hook. + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + + // Set weight used to leave headroom. + let headroom_weight = BlockFullnessHeadroomFor::::get(); + let weights: BlockWeights = ::BlockWeights::get(); + let max_weight_normal = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => max_weight_normal.saturating_sub(headroom_weight.mul(2)), + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(max_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + + // Get the current count of non-full blocks. + let blocks_not_full = NotFullBlocksCount::::get(); + + // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); + }); +} + +#[test] +fn challenges_ticker_paused_only_after_tolerance_blocks() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Tick counter should be 1 while in block 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. + let block_fullness_period = BlockFullnessPeriodFor::::get(); + run_to_block_spammed(block_fullness_period); + + // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriodFor`. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period); + + // Go one more block beyond `BlockFullnessPeriodFor`. + // Ticker should stop at this tick. + run_to_block_spammed(block_fullness_period + 1); + + // Assert that now the challenges ticker is paused, and the tick counter stopped at `BlockFullnessPeriodFor` + 1. + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + + // Going one block beyond, shouldn't increment the ticker. + run_to_block(block_fullness_period + 2); + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + }); +} + +#[test] +fn challenges_ticker_paused_when_less_than_min_not_full_blocks_ratio_are_not_full() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Tick counter should be 1 while in block 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // Make sure there are `BlockFullnessPeriod * MinNotFullBlocksRatio` (floor) + // not-spammed blocks. Consider that the first block was not spammed. + let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); + let min_not_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let blocks_to_spam = min_not_full_blocks_ratio + .mul_floor(block_fullness_period) + .saturating_sub(1); + let current_block = System::block_number(); + run_to_block(current_block + blocks_to_spam); + + // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. + run_to_block_spammed(block_fullness_period); + + // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriod`. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period); + + // Go one more block beyond `BlockFullnessPeriod`. + // Ticker should stop at this tick. + run_to_block(block_fullness_period + 1); + + // Assert that now the challenges ticker is paused, and the tick counter stopped at `BlockFullnessPeriod` + 1. + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + + // Going one block beyond, shouldn't increment the ticker. + run_to_block(block_fullness_period + 2); + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + }); +} + +#[test] +fn challenges_ticker_not_paused_when_more_than_min_not_full_blocks_ratio_are_not_full() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Tick counter should be 1 while in block 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // Make sure there are more than `BlockFullnessPeriod * MinNotFullBlocksRatio` (floor) + // not-spammed blocks. Consider that the first block was not spammed. + let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); + let min_not_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let blocks_to_spam = min_not_full_blocks_ratio + .mul_floor(block_fullness_period) + .saturating_sub(1); + let current_block = System::block_number(); + run_to_block(current_block + blocks_to_spam + 1); + + // Go until `BlockFullnessPeriodFor` blocks, with spammed blocks. + run_to_block_spammed(block_fullness_period); + + // Assert that the challenges ticker is NOT paused, and the tick counter advanced `BlockFullnessPeriod`. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period); + + // Go one more block beyond `BlockFullnessPeriod`. + // Ticker should NOT stop at this tick. + run_to_block(block_fullness_period + 1); + + // Assert that the challenges ticker is still NOT paused, and the tick counter continues. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + + // Going one block beyond, should increment the ticker. + run_to_block(block_fullness_period + 2); + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 2); + }); +} + +#[test] +fn challenges_ticker_not_paused_when_blocks_dont_run_on_poll() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Simulate multiple non-spammed blocks that don't run on `on_poll`. + // Like multi block migrations. + let block_fullness_period = BlockFullnessPeriodFor::::get(); + for _ in 1..block_fullness_period { + System::set_block_number(System::block_number() + 1); + + // Set weight used to zero (not-spammed). + let max_block_weight = ConsumedWeight::new(|class: DispatchClass| match class { + DispatchClass::Normal => Zero::zero(), + DispatchClass::Operational => Zero::zero(), + DispatchClass::Mandatory => Zero::zero(), + }); + BlockWeight::::set(max_block_weight); + + // Trigger on_finalize hook execution. + ProofsDealer::on_finalize(System::block_number()); + } + + // Get the current count of non-full blocks. Should be zero as `on_poll` was only run + // once in `run_to_block(1)`, taking into account the genesis block. In other words, not + // adding anything. + let blocks_not_full = NotFullBlocksCount::::get(); + assert_eq!(blocks_not_full, 0); + + // Current ticker should be 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // In the next block, after executing `on_poll`, `NonFullBlocksCount` should be incremented. + System::set_block_number(System::block_number() + 1); + ProofsDealer::on_poll(System::block_number(), &mut WeightMeter::new()); + assert_eq!(NotFullBlocksCount::::get(), blocks_not_full + 1); + }); +} + +#[test] +fn challenges_ticker_unpaused_after_spam_finishes() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Tick counter should be 1 while in block 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // Go until `BlockFullnessPeriod` blocks, with spammed blocks. + let block_fullness_period = BlockFullnessPeriodFor::::get(); + run_to_block_spammed(block_fullness_period); + + // Go one more block beyond `BlockFullnessPeriod`. + // Ticker should stop at this tick. + run_to_block_spammed(block_fullness_period + 1); + + // Going one block beyond, shouldn't increment the ticker. + run_to_block(block_fullness_period + 2); + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + + // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. + // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. + let blocks_not_full = NotFullBlocksCount::::get(); + let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let min_non_full_blocks: u64 = + min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); + let empty_blocks_to_advance = min_non_full_blocks + 1 - blocks_not_full; + + // Advance `empty_blocks_to_advance` blocks. + let current_ticker = ChallengesTicker::::get(); + let current_block = System::block_number(); + run_to_block(current_block + empty_blocks_to_advance); + + // Assert that the challenges ticker is NOT paused, but that the `ChallengesTicker` is still the same. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker); + + // Advance one more block and assert that the challenges ticker increments. + run_to_block(current_block + empty_blocks_to_advance + 1); + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker + 1); + }); +} + +#[test] +fn challenges_ticker_paused_twice() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Tick counter should be 1 while in block 1. + assert_eq!(ChallengesTicker::::get(), 1); + + // Go until `BlockFullnessPeriod` blocks, with spammed blocks. + let block_fullness_period = BlockFullnessPeriodFor::::get(); + run_to_block_spammed(block_fullness_period); + + // Go one more block beyond `BlockFullnessPeriod`. + // Ticker should stop at this tick. + run_to_block_spammed(block_fullness_period + 1); + + // Going one block beyond, shouldn't increment the ticker. + run_to_block(block_fullness_period + 2); + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!(ChallengesTicker::::get(), block_fullness_period + 1); + + // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. + // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. + let blocks_not_full = NotFullBlocksCount::::get(); + let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let min_non_full_blocks: u64 = + min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); + let empty_blocks_to_advance = min_non_full_blocks + 1 - blocks_not_full; + + // Advance `empty_blocks_to_advance` blocks. + let current_ticker = ChallengesTicker::::get(); + let current_block = System::block_number(); + run_to_block(current_block + empty_blocks_to_advance); + + // Assert that the challenges ticker is NOT paused, but that the `ChallengesTicker` is still the same. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker); + + // Advance one more block and assert that the challenges ticker increments. + run_to_block(current_block + empty_blocks_to_advance + 1); + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker + 1); + + // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. + // We need to decrease that number so that it is smaller or equal to`BlockFullnessPeriod * MinNotFullBlocksRatio`. + let mut blocks_not_full = NotFullBlocksCount::::get(); + let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let min_non_full_blocks: u64 = + min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); + + // We cannot just spam however many blocks of difference are in `blocks_not_full` - `min_non_full_blocks` + // because the oldest blocks being considered were also spammed. We would be adding new spammed blocks + // in the newest blocks, and removing them from the oldest ones. So we need to spam blocks until non-spammed + // blocks are old enough and start getting discarded. + let mut blocks_advanced = 0; + let current_ticker = ChallengesTicker::::get(); + while blocks_not_full > min_non_full_blocks { + let current_block = System::block_number(); + run_to_block_spammed(current_block + 1); + blocks_not_full = NotFullBlocksCount::::get(); + blocks_advanced += 1; + } + + // Assert that the challenges ticker IS paused, but that the `ChallengesTicker` has advanced `not_empty_blocks_to_advance`. + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!( + ChallengesTicker::::get(), + current_ticker + blocks_advanced + ); + + // Advance one more block and assert that the challenges ticker doesn't increment. + let current_block = System::block_number(); + run_to_block(current_block + 1); + assert!(ChallengesTickerPaused::::get().is_some()); + assert_eq!( + ChallengesTicker::::get(), + current_ticker + blocks_advanced + ); + }); +} + +#[test] +fn challenges_ticker_provider_not_slashed_if_network_spammed() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Go beyond `BlockFullnessPeriod` blocks, with not spammed blocks, to simulate + // an operational scenario already. + let block_fullness_period: u64 = BlockFullnessPeriodFor::::get(); + run_to_block(block_fullness_period + 1); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add balance to that Provider and hold some so it has a stake. + let provider_balance = 1_000_000_000_000_000; + assert_ok!(::NativeBalance::mint_into( + &1, + provider_balance + )); + assert_ok!(::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + &1, + provider_balance / 100 + )); + + // Set Provider's root to be an arbitrary value, different than the default root, + // to simulate that it is actually providing a service. + let root = BlakeTwo256::hash(b"1234"); + pallet_storage_providers::BackupStorageProviders::::mutate( + &provider_id, + |provider| { + provider.as_mut().expect("Provider should exist").root = root; + }, + ); + + // Set Provider's last submitted proof block. + let current_tick = ChallengesTicker::::get(); + let prev_tick_provider_submitted_proof = current_tick; + LastTickProviderSubmittedAProofFor::::insert( + &provider_id, + prev_tick_provider_submitted_proof, + ); + + // Set Provider's deadline for submitting a proof. + // It is the sum of this Provider's challenge period and the `ChallengesTicksTolerance`. + let providers_stake = + as ReadChallengeableProvidersInterface>::get_stake( + provider_id, + ) + .unwrap(); + let challenge_period = crate::Pallet::::stake_to_challenge_period(providers_stake); + let challenge_ticks_tolerance: u64 = ChallengeTicksToleranceFor::::get(); + let challenge_period_plus_tolerance = challenge_period + challenge_ticks_tolerance; + let prev_deadline = current_tick + challenge_period_plus_tolerance; + TickToProvidersDeadlines::::insert(prev_deadline, provider_id, ()); + + // Check that Provider is not in the SlashableProviders storage map. + assert!(!SlashableProviders::::contains_key(&provider_id)); + + // Up until this point, all blocks have been not-spammed, so the `NotFullBlocksCount` + // should be equal to `BlockFullnessPeriod`. + let current_not_full_blocks_count = NotFullBlocksCount::::get(); + assert_eq!(current_not_full_blocks_count, block_fullness_period); + + // Advance until the next challenge period block without spammed blocks. + let current_block = System::block_number(); + run_to_block(current_block + challenge_period); + + // Advance to the deadline block for this Provider, but with spammed blocks. + run_to_block_spammed(prev_deadline); + + // Check that Provider is NOT in the SlashableProviders storage map. + assert!(!SlashableProviders::::contains_key(&provider_id)); + + // Getting how many blocks have been considered NOT full from the last `BlockFullnessPeriod`. + // We need to increase that number so that it is greater than `BlockFullnessPeriod * MinNotFullBlocksRatio`. + let mut blocks_not_full = NotFullBlocksCount::::get(); + let min_non_full_blocks_ratio = MinNotFullBlocksRatioFor::::get(); + let min_non_full_blocks: u64 = + min_non_full_blocks_ratio.mul_floor(BlockFullnessPeriodFor::::get()); + + let current_ticker = ChallengesTicker::::get(); + while blocks_not_full <= min_non_full_blocks { + let current_block = System::block_number(); + run_to_block(current_block + 1); + blocks_not_full = NotFullBlocksCount::::get(); + } + + // Now the `ChallengesTicker` shouldn't be paused. But current ticker should be the same. + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker); + + // Advancing one more block should increase the `ChallengesTicker` by one. + let current_block = System::block_number(); + run_to_block(current_block + 1); + assert!(ChallengesTickerPaused::::get().is_none()); + assert_eq!(ChallengesTicker::::get(), current_ticker + 1); + + // Get how many blocks until the deadline tick. + let current_ticker = ChallengesTicker::::get(); + let blocks_to_advance = prev_deadline - current_ticker; + let current_block = System::block_number(); + run_to_block(current_block + blocks_to_advance); + + // Check event of provider being marked as slashable. + System::assert_has_event( + Event::SlashableProvider { + provider: provider_id, + next_challenge_deadline: prev_deadline + challenge_period, + } + .into(), + ); + + // Check that Provider is in the SlashableProviders storage map. + assert!(SlashableProviders::::contains_key(&provider_id)); + assert_eq!( + SlashableProviders::::get(&provider_id), + Some(::RandomChallengesPerBlock::get()) + ); + + // Check the new last time this provider submitted a proof. + let current_tick_provider_submitted_proof = + prev_tick_provider_submitted_proof + challenge_period; + let new_last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(provider_id).unwrap(); + assert_eq!( + current_tick_provider_submitted_proof, + new_last_tick_provider_submitted_proof + ); + + // Check that the Provider's deadline was pushed forward. + assert_eq!( + TickToProvidersDeadlines::::get(prev_deadline, provider_id), + None + ); + let new_deadline = + new_last_tick_provider_submitted_proof + challenge_period + challenge_ticks_tolerance; + assert_eq!( + TickToProvidersDeadlines::::get(new_deadline, provider_id), + Some(()), + ); + }); +} + +#[test] +fn stake_to_challenge_period_saturates_properly() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + let stake_to_challenge_period = StakeToChallengePeriodFor::::get(); + let min_challenge_period: BlockNumberFor = MinChallengePeriodFor::::get(); + + // A provider with stake equal to `StakeToChallengePeriod` should have a challenge period equal to `MinChallengePeriod`. + assert_eq!( + crate::Pallet::::stake_to_challenge_period(stake_to_challenge_period), + min_challenge_period + ); + + // A provider with stake greater than `StakeToChallengePeriod` should have a challenge period equal to `MinChallengePeriod`. + assert_eq!( + crate::Pallet::::stake_to_challenge_period(stake_to_challenge_period * 2), + min_challenge_period + ); + + // A provider with stake 1 should have a challenge period equal to `StakeToChallengePeriod`. + assert_eq!( + crate::Pallet::::stake_to_challenge_period(1) as u128, + stake_to_challenge_period + ); + + // A provider with a stake somewhere in between should have a challenge period given by the formula. + let half_stake_to_challenge_period = + stake_to_challenge_period / (2 * min_challenge_period as u128); + assert_eq!( + crate::Pallet::::stake_to_challenge_period(half_stake_to_challenge_period), + 2 * min_challenge_period + ); + }); +} + +mod on_idle_hook_tests { + use super::*; + + #[test] + fn on_idle_hook_works() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick. + let tick_when_proof_provided = ChallengesTicker::::get(); + let mut new_valid_submitters = + BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); + new_valid_submitters.try_insert(provider_id).unwrap(); + ValidProofSubmittersLastTicks::::insert( + tick_when_proof_provided, + new_valid_submitters, + ); + + // Check that the Provider was successfully added to the set. + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Advance a tick, executing `on_idle`, to check if the Provider is removed from the set. + run_to_block(System::block_number() + 1); + ProofsDealer::on_idle(System::block_number(), Weight::MAX); + + // Check that the set which had the Provider that submitted a valid proof has not been deleted. + assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Check that the last deleted tick is still 0 + assert_eq!(LastDeletedTick::::get(), 0); + + // Advance enough ticks so that the Provider list which has the `provider_id` is set to be deleted. + run_to_block( + System::block_number() + + as Get>::get() as u64, + ); + + // Call the `on_idle` hook. + ProofsDealer::on_idle(System::block_number(), Weight::MAX); + + // Check that the set which had the Provider that submitted a valid proof has been correctly deleted. + assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_none()); + + // Check that the last deleted tick is the one that was just deleted. + assert_eq!( + LastDeletedTick::::get(), + System::block_number() + - as Get>::get() as u64 + ); + }); + } + + #[test] + fn on_idle_hook_does_not_delete_with_not_enough_weight() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick. + let tick_when_proof_provided = ChallengesTicker::::get(); + let mut new_valid_submitters = + BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); + new_valid_submitters.try_insert(provider_id).unwrap(); + ValidProofSubmittersLastTicks::::insert( + tick_when_proof_provided, + new_valid_submitters, + ); + + // Check that the Provider was successfully added to the set. + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Advance a tick, executing `on_idle`, to check if the Provider is removed from the set. + run_to_block(System::block_number() + 1); + ProofsDealer::on_idle(System::block_number(), Weight::MAX); + + // Check that the set which had the Provider that submitted a valid proof has not been deleted. + assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Check that the last deleted tick is still 0 + assert_eq!(LastDeletedTick::::get(), 0); + + // Advance enough ticks so that the Provider list which has the `provider_id` is set to be deleted. + run_to_block( + System::block_number() + + as Get>::get() as u64, + ); + + // Call the `on_idle` hook, but without enough weight to delete the set. + ProofsDealer::on_idle(System::block_number(), Weight::zero()); + + // Check that the set which had the Provider that submitted a valid proof still exists and has the Provider + assert!(ValidProofSubmittersLastTicks::::get(tick_when_proof_provided).is_some()); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Check that the last deleted tick is still zero. + assert_eq!(LastDeletedTick::::get(), 0); + }); + } + + #[test] + fn on_idle_hook_deletes_multiple_old_ticks_if_enough_weight_is_remaining() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited. + run_to_block(1); // Block number = 1 + + // Register user as a Provider in Providers pallet. + let provider_id = BlakeTwo256::hash(b"provider_id"); + pallet_storage_providers::AccountIdToBackupStorageProviderId::::insert( + &1, + provider_id, + ); + pallet_storage_providers::BackupStorageProviders::::insert( + &provider_id, + pallet_storage_providers::types::BackupStorageProvider { + capacity: Default::default(), + capacity_used: Default::default(), + multiaddresses: Default::default(), + root: Default::default(), + last_capacity_change: Default::default(), + owner_account: 1u64, + payment_account: Default::default(), + reputation_weight: + ::StartingReputationWeight::get(), + }, + ); + + // Add the Provider to the `ValidProofSubmittersLastTicks` storage map for the current tick and two ticks after that. + let tick_when_first_proof_provided = ChallengesTicker::::get(); // Block number = 1 + let mut new_valid_submitters = + BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); + new_valid_submitters.try_insert(provider_id).unwrap(); + ValidProofSubmittersLastTicks::::insert( + tick_when_first_proof_provided, + new_valid_submitters.clone(), + ); + let tick_when_second_proof_provided = tick_when_first_proof_provided + 2; // Block number = 1 + 2 = 3 + ValidProofSubmittersLastTicks::::insert( + tick_when_second_proof_provided, + new_valid_submitters, + ); + + // Check that the Provider was successfully added to the set in both ticks + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) + .unwrap() + .contains(&provider_id) + ); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Advance a tick, executing `on_idle`, to check if the Provider is removed from any of the two sets. + run_to_block(System::block_number() + 1); // Block number = 2 + ProofsDealer::on_idle(System::block_number(), Weight::MAX); + + // Check that the sets which had the Provider that submitted a valid proof have not been deleted. + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) + .is_some() + ); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) + .unwrap() + .contains(&provider_id) + ); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) + .is_some() + ); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) + .unwrap() + .contains(&provider_id) + ); + + // Check that the last deleted tick is still 0 + assert_eq!(LastDeletedTick::::get(), 0); + + // Advance enough ticks so that both the Provider lists which have the `provider_id` are set to be deleted. + run_to_block( + System::block_number() + + as Get>::get() as u64 + + 3, + ); // Block number = 2 + 3 + 3 = 8, 8 - 3 - 0 > 3 so both sets should be deleted. + + // Call the `on_idle` hook with enough weight to delete both sets. + ProofsDealer::on_idle(System::block_number(), Weight::MAX); + + // Check that the sets which had the Provider that submitted a valid proof have been correctly deleted. + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_first_proof_provided) + .is_none() + ); + assert!( + ValidProofSubmittersLastTicks::::get(tick_when_second_proof_provided) + .is_none() + ); + + // Check that the last deleted tick is the one that was just deleted. + assert_eq!( + LastDeletedTick::::get(), + System::block_number() + - as Get>::get() as u64 + ); + }); + } +} diff --git a/pallets/proofs-dealer/src/utils.rs b/pallets/proofs-dealer/src/utils.rs index 9eca6b145..d99cc8a51 100644 --- a/pallets/proofs-dealer/src/utils.rs +++ b/pallets/proofs-dealer/src/utils.rs @@ -1,1217 +1,1217 @@ -use codec::Encode; -use frame_support::{ - ensure, - pallet_prelude::{DispatchClass, DispatchResult}, - traits::{fungible::Mutate, tokens::Preservation, Get, Randomness}, - weights::{Weight, WeightMeter}, - BoundedBTreeSet, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use pallet_proofs_dealer_runtime_api::{ - GetChallengePeriodError, GetChallengeSeedError, GetCheckpointChallengesError, - GetLastTickProviderSubmittedProofError, GetNextDeadlineTickError, -}; -use shp_traits::{ - CommitmentVerifier, MutateChallengeableProvidersInterface, ProofSubmittersInterface, - ProofsDealerInterface, ReadChallengeableProvidersInterface, TrieMutation, - TrieProofDeltaApplier, TrieRemoveMutation, -}; -use sp_runtime::{ - traits::{CheckedAdd, CheckedDiv, CheckedSub, Convert, Hash, One, Zero}, - ArithmeticError, BoundedVec, DispatchError, SaturatedConversion, Saturating, -}; -use sp_std::{ - collections::{btree_set::BTreeSet, vec_deque::VecDeque}, - vec::Vec, -}; - -use crate::{ - pallet, - types::{ - AccountIdFor, BalanceFor, BalancePalletFor, ChallengeHistoryLengthFor, - ChallengeTicksToleranceFor, ChallengesFeeFor, ChallengesQueueLengthFor, - CheckpointChallengePeriodFor, ForestVerifierFor, ForestVerifierProofFor, KeyFor, - KeyVerifierFor, KeyVerifierProofFor, MaxCustomChallengesPerBlockFor, - MaxSubmittersPerTickFor, MinChallengePeriodFor, Proof, ProviderIdFor, ProvidersPalletFor, - RandomChallengesPerBlockFor, RandomnessOutputFor, RandomnessProviderFor, - StakeToChallengePeriodFor, TargetTicksStorageOfSubmittersFor, TreasuryAccountFor, - }, - ChallengesQueue, ChallengesTicker, ChallengesTickerPaused, Error, Event, LastCheckpointTick, - LastDeletedTick, LastTickProviderSubmittedAProofFor, NotFullBlocksCount, Pallet, - PastBlocksWeight, PriorityChallengesQueue, SlashableProviders, TickToChallengesSeed, - TickToCheckpointChallenges, TickToProvidersDeadlines, ValidProofSubmittersLastTicks, -}; - -macro_rules! expect_or_err { - // Handle Option type - ($optional:expr, $error_msg:expr, $error_type:path) => {{ - match $optional { - Some(value) => value, - None => { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - } - }}; - // Handle boolean type - ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ - if !$condition { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - }}; - // Handle Result type - ($result:expr, $error_msg:expr, $error_type:path, result) => {{ - match $result { - Ok(value) => value, - Err(_) => { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - } - }}; -} - -impl Pallet -where - T: pallet::Config, -{ - /// Add custom challenge to ChallengesQueue. - /// - /// Charges a fee for the challenge. - /// This is to prevent spamming the network with challenges. If the challenge is already queued, - /// just return. Otherwise, add the challenge to the queue. - /// - /// Failures: - /// - `FeeChargeFailed`: If the fee transfer to the treasury account fails. - /// - `ChallengesQueueOverflow`: If the challenges queue is full. - pub fn do_challenge(who: &AccountIdFor, key: &KeyFor) -> DispatchResult { - // Charge a fee for the challenge. - BalancePalletFor::::transfer( - &who, - &TreasuryAccountFor::::get(), - ChallengesFeeFor::::get(), - Preservation::Expendable, - ) - .map_err(|_| Error::::FeeChargeFailed)?; - - // Enqueue challenge. - Self::enqueue_challenge(key) - } - - /// Submit proof. - /// - /// For a given `submitter`, verify the `proof` submitted. The proof is verified by checking - /// the forest proof and each key proof. - /// Relies on the `ProvidersPallet` to get the root for the submitter, the last tick for which - /// the submitter submitted a proof and the stake for the submitter. With that information, it - /// computes the next tick for which the submitter should be submitting a proof. It then gets - /// the seed for that tick and generates the challenges from the seed. It also checks if there - /// has been a Checkpoint Challenge block in between the last tick proven and the current tick. - /// If there has been, the Provider should have included proofs for the challenges in that block. - /// It then verifies the forest proof and each key proof, using the `ForestVerifier` and `KeyVerifier`. - pub fn do_submit_proof(submitter: &ProviderIdFor, proof: &Proof) -> DispatchResult { - let forest_proof = &proof.forest_proof; - let key_proofs = &proof.key_proofs; - - // Check if submitter is a registered Provider. - ensure!( - ProvidersPalletFor::::is_provider(*submitter), - Error::::NotProvider - ); - - // Check that key_proofs is not empty. - ensure!(!key_proofs.is_empty(), Error::::EmptyKeyProofs); - - // The check for whether forest_proof and each key_proof is not empty is handled by the corresponding - // verifiers for each. We do not preemptively check for this here, since the `CommitmentVerifier::Proof` - // type is not required to have an `is_empty` method. - - // Get root for submitter. - // If a submitter is a registered Provider, it must have a root, so this shouldn't happen. - // However, since the implementation of that is not up to this pallet, we need to check. - let root = ProvidersPalletFor::::get_root(*submitter) - .ok_or(Error::::ProviderRootNotFound)?; - - // Check that the root is not the default root. - // A default root means that the Provider is not providing any service yet, so he shouldn't be - // submitting any proofs. - ensure!( - root != ProvidersPalletFor::::get_default_root(), - Error::::ZeroRoot - ); - - // Get last tick for which the submitter submitted a proof. - let last_tick_proven = match LastTickProviderSubmittedAProofFor::::get(*submitter) { - Some(tick) => tick, - None => return Err(Error::::NoRecordOfLastSubmittedProof.into()), - }; - - // Get stake for submitter. - // If a submitter is a registered Provider, it must have a stake, so this shouldn't happen. - // However, since the implementation of that is not up to this pallet, we need to check. - let stake = ProvidersPalletFor::::get_stake(*submitter) - .ok_or(Error::::ProviderStakeNotFound)?; - - // Check that the stake is non-zero. - ensure!(stake > BalanceFor::::zero(), Error::::ZeroStake); - - // Compute the next tick for which the submitter should be submitting a proof. - let challenges_tick = last_tick_proven - .checked_add(&Self::stake_to_challenge_period(stake)) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Check that the challenges tick is lower than the current tick. - ensure!( - challenges_tick < ChallengesTicker::::get(), - Error::::ChallengesTickNotReached - ); - - // Check that the challenges tick is greater than current tick minus `ChallengeHistoryLength`, - // i.e. that the challenges tick is within the ticks this pallet keeps track of. - expect_or_err!( - challenges_tick - > ChallengesTicker::::get() - .saturating_sub(ChallengeHistoryLengthFor::::get()), - "Challenges tick is too old, beyond the history this pallet keeps track of. This should not be possible.", - Error::::ChallengesTickTooOld, - bool - ); - - // Check that the submitter is not submitting the proof to late, i.e. that the challenges tick - // is not greater or equal than `challenges_tick` + `T::ChallengeTicksTolerance::get()`. - // This should never happen, as the `TickToProvidersDeadlines` StorageMap is - // cleaned up every block. Therefore, if a Provider reached this deadline, it should have been - // slashed, and its next challenge tick pushed forwards. - let challenges_tick_deadline = challenges_tick - .checked_add(&T::ChallengeTicksTolerance::get()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - expect_or_err!( - challenges_tick_deadline > >::block_number(), - "Challenges tick is too late, the proof should be submitted at most `T::ChallengeTicksTolerance::get()` ticks after the challenges tick.", - Error::::ChallengesTickTooLate, - bool - ); - - // Get seed for challenges tick. - let seed = expect_or_err!( - TickToChallengesSeed::::get(challenges_tick), - "Seed for challenges tick not found, when checked it should be within history.", - Error::::SeedNotFound - ); - - // Generate forest challenges from seed. - let mut challenges = Self::generate_challenges_from_seed( - seed, - submitter, - RandomChallengesPerBlockFor::::get(), - ); - - // Check if there's been a Checkpoint Challenge tick in between the last tick proven and - // the tick for which the proof is being submitted. If there has been, the Provider should - // have included proofs for those checkpoint challenges. - let last_checkpoint_tick = LastCheckpointTick::::get(); - let mut checkpoint_challenges = None; - if last_tick_proven <= last_checkpoint_tick && last_checkpoint_tick < challenges_tick { - // Add challenges from the Checkpoint Challenge block. - checkpoint_challenges = - Some(expect_or_err!( - TickToCheckpointChallenges::::get(last_checkpoint_tick), - "Checkpoint challenges not found, when dereferencing in last registered checkpoint challenge block.", - Error::::CheckpointChallengesNotFound - )); - - if let Some(ref checkpoint_challenges) = checkpoint_challenges { - challenges.extend(checkpoint_challenges.iter().map(|(key, _)| key)); - } - } - - // Verify forest proof. - let mut forest_keys_proven = - ForestVerifierFor::::verify_proof(&root, &challenges, forest_proof) - .map_err(|_| Error::::ForestProofVerificationFailed)?; - - // Apply the delta to the Forest root for all mutations that are in checkpoint challenges. - if let Some(challenges) = checkpoint_challenges { - // Aggregate all mutations to apply to the Forest root. - let mutations: Vec<_> = challenges - .iter() - .filter_map(|(key, mutation)| match mutation { - Some(mutation) if forest_keys_proven.contains(key) => Some((*key, mutation)), - Some(_) | None => None, - }) - .collect(); - - if !mutations.is_empty() { - let mut mutations_applied = Vec::new(); - let new_root = mutations.iter().try_fold(root, |acc_root, mutation| { - // Remove the key from the list of `forest_keys_proven` to avoid having to verify the key proof. - forest_keys_proven.remove(&mutation.0); - - // Add mutation to list of mutations applied. - mutations_applied.push((mutation.0, mutation.1.clone())); - - // Apply the mutation to the Forest. - let apply_delta_result = >::apply_delta( - &acc_root, - &[(mutation.0, mutation.1.clone().into())], - forest_proof, - ) - .map_err(|_| Error::::FailedToApplyDelta); - - // If the mutation was correctly applied, update the Provider's info and return the new root. - match apply_delta_result { - Ok((_, new_root, mutated_keys_and_values)) => { - // Check that the mutated key is the same as the mutation (and is the only one). - ensure!( - mutated_keys_and_values.len() == 1, - Error::::FailedToApplyDelta - ); - ensure!( - mutated_keys_and_values[0].0 == mutation.0, - Error::::FailedToApplyDelta - ); - - // Use the interface exposed by the Providers pallet to update the submitting Provider - // after the key removal if the key had a value. - let removed_trie_value = &mutated_keys_and_values[0].1; - if let Some(trie_value) = removed_trie_value { - ProvidersPalletFor::::update_provider_after_key_removal( - submitter, trie_value, - ) - .map_err(|_| Error::::FailedToApplyDelta)?; - } - - Ok(new_root) - } - Err(err) => Err(err), - } - })?; - - // Emit event of mutation applied. - Self::deposit_event(Event::::MutationsApplied { - provider: *submitter, - mutations: mutations_applied, - new_root, - }); - - // Update root of Provider after all mutations have been applied to the Forest. - ::update_root( - *submitter, new_root, - )?; - } - }; - - // Verify each key proof. - for key_proven in forest_keys_proven { - // Check that there is a key proof for each key proven. - let key_proof = key_proofs - .get(&key_proven) - .ok_or(Error::::KeyProofNotFound)?; - - // Generate the challenges for the key. - let challenges = - Self::generate_challenges_from_seed(seed, submitter, key_proof.challenge_count); - - // Verify key proof. - KeyVerifierFor::::verify_proof(&key_proven, &challenges, &key_proof.proof) - .map_err(|_| Error::::KeyProofVerificationFailed)?; - } - - // Update `LastTickProviderSubmittedProofFor` to the challenge tick the provider has just - // submitted a proof for. - LastTickProviderSubmittedAProofFor::::set(*submitter, Some(challenges_tick)); - - // Remove the submitter from its current deadline registered in `TickToProvidersDeadlines`. - TickToProvidersDeadlines::::remove(challenges_tick_deadline, submitter); - - // Calculate the next tick for which the submitter should be submitting a proof. - let next_challenges_tick = challenges_tick - .checked_add(&Self::stake_to_challenge_period(stake)) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Add tolerance to `next_challenges_tick` to know when is the next deadline for submitting a - // proof, for this provider. - let next_challenges_tick_deadline = next_challenges_tick - .checked_add(&T::ChallengeTicksTolerance::get()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Add this Provider to the `TickToProvidersDeadlines` StorageMap, with its new deadline. - TickToProvidersDeadlines::::set(next_challenges_tick_deadline, submitter, Some(())); - - // Add this Provider to the `ValidProofSubmittersLastTicks` StorageMap, with the current tick number. - let current_tick_valid_submitters = - ValidProofSubmittersLastTicks::::take(ChallengesTicker::::get()); - match current_tick_valid_submitters { - // If the set already exists and has valid submitters, we just insert the new submitter. - Some(mut valid_submitters) => { - let did_not_already_exist = expect_or_err!(valid_submitters.try_insert(*submitter), "The set should never be full as the limit we set should be greater than the implicit limit given by max block weight.", Error::::TooManyValidProofSubmitters, result); - // We only update storage if the Provider ID wasn't yet in the set to avoid unnecessary writes. - if did_not_already_exist { - ValidProofSubmittersLastTicks::::insert( - ChallengesTicker::::get(), - valid_submitters, - ); - } - } - // If the set doesn't exist, we create it and insert the submitter. - None => { - let mut new_valid_submitters = - BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); - expect_or_err!( - new_valid_submitters.try_insert(*submitter), - "The set has just been created, it's empty and as such won't be full. qed", - Error::::TooManyValidProofSubmitters, - result - ); - ValidProofSubmittersLastTicks::::insert( - ChallengesTicker::::get(), - new_valid_submitters, - ); - } - } - - Ok(()) - } - - /// Generate a new round of challenges, be it random or checkpoint. - /// - /// Random challenges are automatically generated based on some external source of - /// randomness, and are added to [`TickToChallengesSeed`], for this tick's number. - /// - /// It also takes care of including the challenges from the `ChallengesQueue` and - /// `PriorityChallengesQueue`. These custom challenges are only included in "checkpoint" - /// ticks - /// - /// Additionally, it takes care of checking if there are Providers that have - /// failed to submit a proof, and should have submitted one by this tick. It does so - /// by checking the [`TickToProvidersDeadlines`] StorageMap. If a Provider is found - /// to have failed to submit a proof, it is subject to slashing. - /// - /// Finally, it cleans up: - /// - The [`TickToChallengesSeed`] StorageMap, removing entries older than `ChallengeHistoryLength`. - /// - The [`TickToCheckpointChallenges`] StorageMap, removing the previous checkpoint challenge block. - /// - The [`TickToProvidersDeadlines`] StorageMap, removing entries for the current challenges tick. - pub fn do_new_challenges_round(weight: &mut WeightMeter) { - // Increment the challenges' ticker. - let mut challenges_ticker = ChallengesTicker::::get(); - challenges_ticker.saturating_inc(); - ChallengesTicker::::set(challenges_ticker); - weight.consume(T::DbWeight::get().reads_writes(1, 1)); - - // Store random seed for this tick. - let (seed, _) = RandomnessProviderFor::::random(challenges_ticker.encode().as_ref()); - TickToChallengesSeed::::set(challenges_ticker, Some(seed)); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Remove the oldest challenge seed stored, to clean up the storage. - let tick_to_remove = challenges_ticker.checked_sub(&ChallengeHistoryLengthFor::::get()); - if let Some(tick_to_remove) = tick_to_remove { - TickToChallengesSeed::::remove(tick_to_remove); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - } - - // Emit new challenge seed event. - Self::deposit_event(Event::NewChallengeSeed { - challenges_ticker, - seed, - }); - - let last_checkpoint_tick = LastCheckpointTick::::get(); - - // Count last checkpoint challenges tick challenges - let checkpoint_challenges_count = - TickToCheckpointChallenges::::get(last_checkpoint_tick) - .unwrap_or_else(|| - // Returning an empty list so slashable providers will not accrue any failed proof submissions for checkpoint challenges. - BoundedVec::new()) - .len(); - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - // This hook does not return an error, and it cannot fail, that's why we use `saturating_add`. - let next_checkpoint_tick = - last_checkpoint_tick.saturating_add(T::CheckpointChallengePeriod::get()); - if challenges_ticker == next_checkpoint_tick { - // This is a checkpoint challenge round, so we also generate new checkpoint challenges. - Self::do_new_checkpoint_challenge_round(challenges_ticker, weight); - } - weight.consume(T::DbWeight::get().reads_writes(2, 0)); - - // If there are providers left in `TickToProvidersDeadlines` for this tick, - // they are marked as slashable. - let mut slashable_providers = - TickToProvidersDeadlines::::drain_prefix(challenges_ticker); - while let Some((provider, _)) = slashable_providers.next() { - // One read for every provider in the prefix, and one write as we're consuming and deleting the entry. - weight.consume(T::DbWeight::get().reads_writes(1, 1)); - - // Accrue number of failed proof submission for this slashable provider. - // Add custom checkpoint challenges if the provider needed to respond to them. - SlashableProviders::::mutate(provider, |slashable| { - let mut accrued = slashable.unwrap_or(0); - - let last_tick_provider_submitted_proof = - match LastTickProviderSubmittedAProofFor::::get(provider) { - Some(tick) => tick, - None => { - Self::deposit_event(Event::NoRecordOfLastSubmittedProof { provider }); - - #[cfg(test)] - unreachable!( - "Provider should have a last tick it submitted a proof for." - ); - - #[allow(unreachable_code)] - { - // If the Provider has no record of the last tick it submitted a proof for, - // we set it to the current challenges ticker, so they will not be slashed. - challenges_ticker - } - } - }; - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - let challenge_ticker_provider_should_have_responded_to = - challenges_ticker.saturating_sub(T::ChallengeTicksTolerance::get()); - - if checkpoint_challenges_count != 0 - && last_tick_provider_submitted_proof <= last_checkpoint_tick - && last_checkpoint_tick < challenge_ticker_provider_should_have_responded_to - { - accrued = accrued.saturating_add(checkpoint_challenges_count as u32); - } - - accrued = accrued.saturating_add(RandomChallengesPerBlockFor::::get()); - - *slashable = Some(accrued); - }); - - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Get the stake for this Provider, to know its challenge period. - // If a submitter is a registered Provider, it must have a stake, so there shouldn't be an error. - let stake = match ProvidersPalletFor::::get_stake(provider) { - Some(stake) => stake, - // But to avoid panics, in the odd case of a Provider not being registered, we - // arbitrarily set the stake to be that which would result in `CheckpointChallengePeriod` ticks of challenge period. - None => { - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - let checkpoint_challenge_period = - CheckpointChallengePeriodFor::::get().saturated_into::(); - StakeToChallengePeriodFor::::get() * checkpoint_challenge_period.into() - } - }; - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - // Calculate the next challenge deadline for this Provider. - // At this point, we are processing all providers who have reached their deadline (i.e. tolerance ticks after the tick they should provide a proof for): - // challenge_ticker = last_tick_provider_should_have_submitted_a_proof_for + ChallengeTicksTolerance - // - // By definition, the next deadline should be tolerance ticks after the next tick they should submit proof for (i.e. one period after the last tick they should have submitted a proof for): - // next_challenge_deadline = last_tick_provider_should_have_submitted_a_proof_for + provider_period + ChallengeTicksTolerance - // - // Therefore, the next deadline is one period from now: - // next_challenge_deadline = challenge_ticker + provider_period - let next_challenge_deadline = - challenges_ticker.saturating_add(Self::stake_to_challenge_period(stake)); - - // Update this Provider's next challenge deadline. - TickToProvidersDeadlines::::set(next_challenge_deadline, provider, Some(())); - - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Calculate the tick for which the Provider should have submitted a proof. - let last_interval_tick = - challenges_ticker.saturating_sub(T::ChallengeTicksTolerance::get()); - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - // Update this Provider's last interval tick for the next challenge. - LastTickProviderSubmittedAProofFor::::set(provider, Some(last_interval_tick)); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Emit slashable provider event. - Self::deposit_event(Event::SlashableProvider { - provider, - next_challenge_deadline, - }); - } - } - - /// Check if the network is presumably under a spam attack. - /// - /// The function looks at the weight used in the past `BlockFullnessPeriod` blocks, comparing it - /// with the maximum allowed weight (`max_weight_for_class`) for the dispatch class of `submit_proof` extrinsics. - /// The idea is to track blocks that have not been filled to capacity within a - /// specific period (`BlockFullnessPeriod`) and determine if there is enough "headroom" - /// (unused block capacity) to consider the network not under spam. - pub fn do_check_spamming_condition(weight: &mut WeightMeter) { - // Get the maximum weight for the dispatch class of `submit_proof` extrinsics. - let weights = T::BlockWeights::get(); - let max_weight_for_class = weights - .get(DispatchClass::Normal) - .max_total - .unwrap_or(weights.max_block); - - let current_block = frame_system::Pallet::::block_number(); - - // Get the number of blocks that have been considered _not_ full in the past `BlockFullnessPeriod`. - let not_full_blocks_count = NotFullBlocksCount::::get(); - let mut new_not_full_blocks_count = not_full_blocks_count.clone(); - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - // This would only be `None` if the block number is 0, so this should be safe. - if let Some(prev_block) = current_block.checked_sub(&1u32.into()) { - // Get the weight usage in the previous block. - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - if let Some(weight_used_in_prev_block) = PastBlocksWeight::::get(prev_block) { - // Check how much weight was left in the previous block, compared to the maximum weight. - // This is computed both for proof size and ref time. - let weight_left_in_prev_block = - max_weight_for_class.saturating_sub(weight_used_in_prev_block); - - // If the weight left in the previous block is greater or equal than the headroom, for both proof size or ref time, - // we consider the previous block to be NOT full and count it as such. - if weight_left_in_prev_block.ref_time() - >= T::BlockFullnessHeadroom::get().ref_time() - && weight_left_in_prev_block.proof_size() - >= T::BlockFullnessHeadroom::get().proof_size() - { - // Increment the counter of blocks that are not full. - new_not_full_blocks_count = - new_not_full_blocks_count.saturating_add(1u32.into()); - } - } - } - - // This would be `None` during the first `BlockFullnessPeriod` + 1 blocks. - if let Some(oldest_block_fullness_number) = - current_block.checked_sub(&T::BlockFullnessPeriod::get().saturating_add(1u32.into())) - { - // Get the weight usage in the oldest registered block. - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - if let Some(weight_used_in_oldest_block) = - PastBlocksWeight::::get(oldest_block_fullness_number) - { - // Check how much weight was left in the oldest block, compared to the maximum weight. - // This is computed both for proof size and ref time. - let weight_left_in_oldest_block = - max_weight_for_class.saturating_sub(weight_used_in_oldest_block); - - // If the weight left in the oldest block is greater or equal than the headroom, for both proof size or ref time, - // we consider the oldest block to be NOT full. If that is the case, we have to remove it from the - // count as it is now out of the `BlockFullnessPeriod` of blocks taken into account. - if weight_left_in_oldest_block.ref_time() - >= T::BlockFullnessHeadroom::get().ref_time() - && weight_left_in_oldest_block.proof_size() - >= T::BlockFullnessHeadroom::get().proof_size() - { - // Decrement the counter of blocks that are not full. - new_not_full_blocks_count = - new_not_full_blocks_count.saturating_sub(1u32.into()); - } - } - } - - // If there was a change in the number of blocks that were not full, we need to update the storage. - if new_not_full_blocks_count != not_full_blocks_count { - NotFullBlocksCount::::set(new_not_full_blocks_count); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - } - - // At this point, we have an updated count of blocks that were not full in the past `BlockFullnessPeriod`. - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - if ChallengesTicker::::get() > T::BlockFullnessPeriod::get() { - // Running this check only makes sense after `ChallengesTicker` has advanced past `BlockFullnessPeriod`. - // To consider the network NOT to be under spam, we need more than `min_non_full_blocks` blocks to be not full. - let min_non_full_blocks_ratio = T::MinNotFullBlocksRatio::get(); - let min_non_full_blocks = - min_non_full_blocks_ratio.mul_floor(T::BlockFullnessPeriod::get()); - - // If `not_full_blocks_count` is greater than `min_non_full_blocks`, we consider the network NOT to be under spam. - if new_not_full_blocks_count > min_non_full_blocks { - // The network is NOT considered to be under a spam attack, so we resume the `ChallengesTicker`. - ChallengesTickerPaused::::set(None); - } else { - // At this point, the network is presumably under a spam attack, so we pause the `ChallengesTicker`. - ChallengesTickerPaused::::set(Some(())); - } - weight.consume(T::DbWeight::get().reads_writes(1, 1)); - } - } - - /// Generate new checkpoint challenges for a given block. - /// - /// Fills up a new vector of checkpoint challenges with challenges in the `PriorityChallengesQueue`, - /// and the `ChallengesQueue` if there is space left. - /// - /// Cleans up the `TickToCheckpointChallenges` StorageMap, removing the previous checkpoint challenge block. - fn do_new_checkpoint_challenge_round( - current_tick: BlockNumberFor, - weight: &mut WeightMeter, - ) { - let mut new_checkpoint_challenges: BoundedVec< - (KeyFor, Option), - MaxCustomChallengesPerBlockFor, - > = BoundedVec::new(); - - // Fill up this round's checkpoint challenges with challenges in the `PriorityChallengesQueue`. - // It gets filled up until the max number of custom challenges for a block is reached, or until - // there are no more challenges in the `PriorityChallengesQueue`. - let original_priority_challenges_queue = PriorityChallengesQueue::::get(); - let mut priority_challenges_queue = - VecDeque::from(original_priority_challenges_queue.to_vec()); - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - while !new_checkpoint_challenges.is_full() && !priority_challenges_queue.is_empty() { - let challenge = match priority_challenges_queue.pop_front() { - Some(challenge) => challenge, - // This should not happen, as we check that priority_challenges_queue is not empty - // in the while condition above, but we add this to be safe. - None => break, - }; - - if new_checkpoint_challenges.try_push(challenge).is_err() { - // This should not happen, as we check that new_checkpoint_challenges is not full - // in the while condition above, but we add this to be safe. - break; - } - } - - // Convert priority_challenges_queue back to a bounded vector. - let new_priority_challenges_queue: BoundedVec< - (KeyFor, Option), - ChallengesQueueLengthFor, - > = Vec::from(priority_challenges_queue) - .try_into() - .unwrap_or_else(|_| original_priority_challenges_queue); - - // Reset the priority challenges queue with the leftovers. - PriorityChallengesQueue::::set(new_priority_challenges_queue); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Fill up this round's checkpoint challenges with challenges in the `ChallengesQueue`. - // It gets filled up until the max number of custom challenges for a block is reached, or until - // there are no more challenges in the `ChallengesQueue`. - let mut challenges_queue = VecDeque::from(ChallengesQueue::::get().to_vec()); - weight.consume(T::DbWeight::get().reads_writes(1, 0)); - - while !new_checkpoint_challenges.is_full() && !challenges_queue.is_empty() { - let challenge = match challenges_queue.pop_front() { - Some(challenge) => challenge, - // This should not happen, as we check that challenges_queue is not empty - // in the while condition above, but we add this to be safe. - None => break, - }; - - if new_checkpoint_challenges - .try_push((challenge, None)) - .is_err() - { - // This should not happen, as we check that new_checkpoint_challenges is not full - // in the while condition above, but we add this to be safe. - break; - } - } - - // Convert challenges_queue back to a bounded vector. - let new_challenges_queue: BoundedVec, ChallengesQueueLengthFor> = - Vec::from(challenges_queue) - .try_into() - .unwrap_or_else(|_| BoundedVec::new()); - - // Reset the challenges queue with the leftovers. - ChallengesQueue::::set(new_challenges_queue); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Store the new checkpoint challenges. - TickToCheckpointChallenges::::set(current_tick, Some(new_checkpoint_challenges.clone())); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Remove the last checkpoint challenge from storage to clean up. - let last_checkpoint_tick = LastCheckpointTick::::get(); - TickToCheckpointChallenges::::remove(last_checkpoint_tick); - weight.consume(T::DbWeight::get().reads_writes(1, 1)); - - // Set this tick as the last checkpoint tick. - LastCheckpointTick::::set(current_tick); - weight.consume(T::DbWeight::get().reads_writes(0, 1)); - - // Emit new checkpoint challenge event. - Self::deposit_event(Event::NewCheckpointChallenge { - challenges_ticker: current_tick, - challenges: new_checkpoint_challenges, - }); - } - - /// Convert stake to challenge period. - /// - /// [`StakeToChallengePeriodFor`] is divided by `stake` to get the number of blocks in between challenges - /// for a Provider. The result is then converted to `BlockNumber` type. The division saturates at [`MinChallengePeriodFor`]. - pub(crate) fn stake_to_challenge_period(stake: BalanceFor) -> BlockNumberFor { - let min_challenge_period = MinChallengePeriodFor::::get(); - let challenge_period = match StakeToChallengePeriodFor::::get().checked_div(&stake) { - Some(block_period) => T::StakeToBlockNumber::convert(block_period), - None => min_challenge_period, - }; - - // Return the maximum between the calculated challenge period and the minimum challenge period. - min_challenge_period.max(challenge_period) - } - - /// Add challenge to ChallengesQueue. - /// - /// Check if challenge is already queued. If it is, just return. Otherwise, add the challenge - /// to the queue. - fn enqueue_challenge(key: &KeyFor) -> DispatchResult { - // Get challenges queue from storage. - let mut challenges_queue = ChallengesQueue::::get(); - - // Check if challenge is already queued. If it is, just return. - if challenges_queue.contains(&key) { - return Ok(()); - } - - // Add challenge to queue. - challenges_queue - .try_push(*key) - .map_err(|_| Error::::ChallengesQueueOverflow)?; - - // Set challenges queue in storage. - ChallengesQueue::::put(challenges_queue); - - Ok(()) - } - - /// Add challenge to `PriorityChallengesQueue`. - /// - /// Check if challenge is already queued. If it is, just return. Otherwise, add the challenge - /// to the queue. - fn enqueue_challenge_with_priority( - key: &KeyFor, - mutation: Option, - ) -> DispatchResult { - // Get priority challenges queue from storage. - let mut priority_challenges_queue = PriorityChallengesQueue::::get(); - - // Check if challenge is already queued. If it is, just return. - if priority_challenges_queue.contains(&(*key, mutation.clone())) { - return Ok(()); - } - - // Add challenge to queue. - priority_challenges_queue - .try_push((*key, mutation)) - .map_err(|_| Error::::PriorityChallengesQueueOverflow)?; - - // Set priority challenges queue in storage. - PriorityChallengesQueue::::put(priority_challenges_queue); - - Ok(()) - } - - /// Generate challenges from seed. - /// - /// Generate a number of challenges from a seed and a Provider's ID. - /// Challenges are generated by hashing the seed, the Provider's ID and an index. - pub(crate) fn generate_challenges_from_seed( - seed: RandomnessOutputFor, - provider_id: &ProviderIdFor, - count: u32, - ) -> Vec { - let mut challenges = Vec::new(); - - for i in 0..count { - // Each challenge is generated by hashing the seed, the provider's ID and the index. - let challenge = T::MerkleTrieHashing::hash( - &[ - seed.as_ref(), - provider_id.encode().as_ref(), - i.encode().as_ref(), - ] - .concat(), - ); - - challenges.push(challenge.into()); - } - - challenges - } - - /// Trim the storage that holds the Providers that submitted valid proofs in the last ticks until there's - /// `TargetTicksOfProofsStorage` ticks left (or until the remaining weight allows it). - /// - /// This function is called in the `on_idle` hook, which means it's only called when the block has - /// unused weight. - /// - /// It removes the oldest tick from the storage that holds the providers that submitted valid proofs - /// in the last ticks as many times as the remaining weight allows it, but at most until the storage - /// has `TargetTicksOfProofsStorage` ticks left. - pub fn do_trim_valid_proof_submitters_last_ticks( - _n: BlockNumberFor, - usable_weight: Weight, - ) -> Weight { - // Initialize the weight used by this function. - let mut used_weight = Weight::zero(); - - // Check how many ticks should be removed to keep the storage at the target amount. - let mut last_deleted_tick = LastDeletedTick::::get(); - used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); - let target_ticks_to_keep = TargetTicksStorageOfSubmittersFor::::get(); - used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); - let current_tick = ChallengesTicker::::get(); - used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); - let ticks_to_remove = current_tick - .saturating_sub(last_deleted_tick) - .saturating_sub(target_ticks_to_keep.into()); - - // Check how much ticks can be removed considering weight limitations - let weight_to_remove_tick = T::DbWeight::get().reads_writes(0, 2); - let removable_ticks = usable_weight - .saturating_sub(used_weight) - .checked_div_per_component(&weight_to_remove_tick); - - // If there is enough weight to remove ticks, try to remove as many ticks as possible until the target is reached. - if let Some(removable_ticks) = removable_ticks { - let removable_ticks = - removable_ticks.min(ticks_to_remove.try_into().unwrap_or(u64::MAX)); - // Remove all the ticks that we can, until we reach the target amount. - for _ in 0..removable_ticks { - // Get the next tick to delete. - let next_tick_to_delete = last_deleted_tick.saturating_add(One::one()); - - // Remove it from storage - ValidProofSubmittersLastTicks::::remove(next_tick_to_delete); - - // Update the last removed tick - LastDeletedTick::::set(next_tick_to_delete); - last_deleted_tick = next_tick_to_delete; // We do this to avoid having to read from storage again. - - // Increment the used weight. - used_weight = used_weight.saturating_add(weight_to_remove_tick); - } - } - - // Return the weight used by this function. - used_weight - } -} - -impl ProofsDealerInterface for Pallet { - type ProviderId = ProviderIdFor; - type ForestProof = ForestVerifierProofFor; - type KeyProof = KeyVerifierProofFor; - type MerkleHash = T::MerkleTrieHash; - type MerkleHashing = T::MerkleTrieHashing; - type RandomnessOutput = RandomnessOutputFor; - type TickNumber = BlockNumberFor; - - fn verify_forest_proof( - provider_id: &Self::ProviderId, - challenges: &[Self::MerkleHash], - proof: &Self::ForestProof, - ) -> Result, DispatchError> { - // Check if submitter is a registered Provider. - ensure!( - ProvidersPalletFor::::is_provider(*provider_id), - Error::::NotProvider - ); - - // Get root for submitter. - // If a submitter is a registered Provider, it must have a root. - let root = ProvidersPalletFor::::get_root(*provider_id) - .ok_or(Error::::ProviderRootNotFound)?; - - // Verify forest proof. - ForestVerifierFor::::verify_proof(&root, challenges, proof) - .map_err(|_| Error::::ForestProofVerificationFailed.into()) - } - - fn verify_generic_forest_proof( - root: &Self::MerkleHash, - challenges: &[Self::MerkleHash], - proof: &Self::ForestProof, - ) -> Result, DispatchError> { - // Verify forest proof. - ForestVerifierFor::::verify_proof(&root, challenges, proof) - .map_err(|_| Error::::ForestProofVerificationFailed.into()) - } - - fn verify_key_proof( - key: &Self::MerkleHash, - challenges: &[Self::MerkleHash], - proof: &Self::KeyProof, - ) -> Result, DispatchError> { - // Verify key proof. - KeyVerifierFor::::verify_proof(key, &challenges, proof) - .map_err(|_| Error::::KeyProofVerificationFailed.into()) - } - - fn challenge(key_challenged: &Self::MerkleHash) -> DispatchResult { - Self::enqueue_challenge(key_challenged) - } - - fn challenge_with_priority( - key_challenged: &Self::MerkleHash, - mutation: Option, - ) -> DispatchResult { - Self::enqueue_challenge_with_priority(key_challenged, mutation) - } - - fn generate_challenges_from_seed( - seed: Self::RandomnessOutput, - provider_id: &Self::ProviderId, - count: u32, - ) -> Vec { - Self::generate_challenges_from_seed(seed, provider_id, count) - } - - fn apply_delta( - provider_id: &Self::ProviderId, - mutations: &[(Self::MerkleHash, TrieMutation)], - proof: &Self::ForestProof, - ) -> Result { - // Check if submitter is a registered Provider. - ensure!( - ProvidersPalletFor::::is_provider(*provider_id), - Error::::NotProvider - ); - - // Get root for submitter. - // If a submitter is a registered Provider, it must have a root. - let root = ProvidersPalletFor::::get_root(*provider_id) - .ok_or(Error::::ProviderRootNotFound)?; - - Ok( - >::apply_delta( - &root, mutations, proof, - ) - .map_err(|_| Error::::FailedToApplyDelta)? - .1, - ) - } - - fn generic_apply_delta( - root: &Self::MerkleHash, - mutations: &[(Self::MerkleHash, TrieMutation)], - proof: &Self::ForestProof, - ) -> Result { - Ok( - >::apply_delta( - &root, mutations, proof, - ) - .map_err(|_| Error::::FailedToApplyDelta)? - .1, - ) - } - - fn initialise_challenge_cycle(provider_id: &Self::ProviderId) -> DispatchResult { - // Check that `who` is a registered Provider. - if !ProvidersPalletFor::::is_provider(*provider_id) { - return Err(Error::::NotProvider.into()); - } - - // Get stake for submitter. - // If a submitter is a registered Provider, it must have a stake, so this shouldn't happen. - // However, since the implementation of that is not up to this pallet, we need to check. - let stake = ProvidersPalletFor::::get_stake(*provider_id) - .ok_or(Error::::ProviderStakeNotFound)?; - - // Check that the stake is non-zero. - ensure!(stake > BalanceFor::::zero(), Error::::ZeroStake); - - // Check if this Provider previously had a challenge cycle initialised. - if let Some(last_tick_proven) = LastTickProviderSubmittedAProofFor::::get(*provider_id) { - // Compute the next tick for which the Provider should have been submitting a proof. - let old_next_challenge_tick = last_tick_proven - .checked_add(&Self::stake_to_challenge_period(stake)) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Calculate the deadline for submitting a proof. Should be the next challenge tick + the challenges tick tolerance. - let old_next_challenge_deadline = old_next_challenge_tick - .checked_add(&ChallengeTicksToleranceFor::::get()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Remove the old deadline. - TickToProvidersDeadlines::::remove(old_next_challenge_deadline, *provider_id); - } - - // Set `LastTickProviderSubmittedAProofFor` to the current tick. - let current_tick = ChallengesTicker::::get(); - LastTickProviderSubmittedAProofFor::::set(*provider_id, Some(current_tick)); - - // Compute the next tick for which the Provider should be submitting a proof. - let next_challenge_tick = current_tick - .checked_add(&Self::stake_to_challenge_period(stake)) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Calculate the deadline for submitting a proof. Should be the next challenge tick + the challenges tick tolerance. - let next_challenge_deadline = next_challenge_tick - .checked_add(&ChallengeTicksToleranceFor::::get()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Set the deadline for submitting a proof. - TickToProvidersDeadlines::::set(next_challenge_deadline, *provider_id, Some(())); - - // Emit event. - Self::deposit_event(Event::::NewChallengeCycleInitialised { - current_tick, - next_challenge_deadline, - provider: *provider_id, - maybe_provider_account: ProvidersPalletFor::::get_owner_account(*provider_id), - }); - - Ok(()) - } - - fn get_current_tick() -> Self::TickNumber { - ChallengesTicker::::get() - } -} - -impl ProofSubmittersInterface for Pallet { - type ProviderId = ProviderIdFor; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = MaxSubmittersPerTickFor; - - fn get_proof_submitters_for_tick( - tick_number: &Self::TickNumber, - ) -> Option> { - ValidProofSubmittersLastTicks::::get(tick_number) - } - - fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option { - SlashableProviders::::get(provider_id) - } - - fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) { - SlashableProviders::::remove(provider_id); - } -} - -/// Runtime API implementation for the ProofsDealer pallet. -impl Pallet -where - T: pallet::Config, -{ - pub fn get_last_tick_provider_submitted_proof( - provider_id: &ProviderIdFor, - ) -> Result, GetLastTickProviderSubmittedProofError> { - // Check if submitter is a registered Provider. - if !ProvidersPalletFor::::is_provider(*provider_id) { - return Err(GetLastTickProviderSubmittedProofError::ProviderNotRegistered); - } - - LastTickProviderSubmittedAProofFor::::get(provider_id) - .ok_or(GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof) - } - - pub fn get_last_checkpoint_challenge_tick() -> BlockNumberFor { - LastCheckpointTick::::get() - } - - pub fn get_checkpoint_challenges( - tick: BlockNumberFor, - ) -> Result, Option)>, GetCheckpointChallengesError> { - // Check that the tick is smaller than the last checkpoint tick. - if LastCheckpointTick::::get() < tick { - return Err(GetCheckpointChallengesError::TickGreaterThanLastCheckpointTick); - } - - let checkpoint_challenges = TickToCheckpointChallenges::::get(tick) - .ok_or(GetCheckpointChallengesError::NoCheckpointChallengesInTick)?; - - Ok(checkpoint_challenges.into()) - } - - pub fn get_challenge_seed( - tick: BlockNumberFor, - ) -> Result, GetChallengeSeedError> { - let current_tick = ChallengesTicker::::get(); - if tick > current_tick { - return Err(GetChallengeSeedError::TickIsInTheFuture); - } - - let seed = TickToChallengesSeed::::get(tick) - .ok_or(GetChallengeSeedError::TickBeyondLastSeedStored)?; - - Ok(seed) - } - - pub fn get_challenge_period( - provider_id: &ProviderIdFor, - ) -> Result, GetChallengePeriodError> { - let stake = ProvidersPalletFor::::get_stake(*provider_id) - .ok_or(GetChallengePeriodError::ProviderNotRegistered)?; - - Ok(Self::stake_to_challenge_period(stake)) - } - - pub fn get_checkpoint_challenge_period() -> BlockNumberFor { - CheckpointChallengePeriodFor::::get() - } - - pub fn get_challenges_from_seed( - seed: &RandomnessOutputFor, - provider_id: &ProviderIdFor, - count: u32, - ) -> Vec> { - Self::generate_challenges_from_seed(*seed, provider_id, count) - } - - pub fn get_forest_challenges_from_seed( - seed: &RandomnessOutputFor, - provider_id: &ProviderIdFor, - ) -> Vec> { - Self::generate_challenges_from_seed( - *seed, - provider_id, - RandomChallengesPerBlockFor::::get(), - ) - } - - pub fn get_current_tick() -> BlockNumberFor { - ChallengesTicker::::get() - } - - pub fn get_next_deadline_tick( - provider_id: &ProviderIdFor, - ) -> Result, GetNextDeadlineTickError> { - // Check if the provider is indeed a registered Provider. - if !ProvidersPalletFor::::is_provider(*provider_id) { - return Err(GetNextDeadlineTickError::ProviderNotRegistered); - } - - // Get the last tick for which the submitter submitted a proof. - let last_tick_provider_submitted_proof = - LastTickProviderSubmittedAProofFor::::get(provider_id) - .ok_or(GetNextDeadlineTickError::ProviderNotInitialised)?; - - // The next deadline tick is the last tick + the challenge period + the challenge tolerance. - let challenge_period = Self::get_challenge_period(provider_id) - .map_err(|_| GetNextDeadlineTickError::ProviderNotRegistered)?; - let next_deadline_tick = last_tick_provider_submitted_proof - .checked_add(&challenge_period) - .ok_or(GetNextDeadlineTickError::ArithmeticOverflow)? - .checked_add(&ChallengeTicksToleranceFor::::get()) - .ok_or(GetNextDeadlineTickError::ArithmeticOverflow)?; - - Ok(next_deadline_tick) - } -} +use codec::Encode; +use frame_support::{ + ensure, + pallet_prelude::{DispatchClass, DispatchResult}, + traits::{fungible::Mutate, tokens::Preservation, Get, Randomness}, + weights::{Weight, WeightMeter}, + BoundedBTreeSet, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_proofs_dealer_runtime_api::{ + GetChallengePeriodError, GetChallengeSeedError, GetCheckpointChallengesError, + GetLastTickProviderSubmittedProofError, GetNextDeadlineTickError, +}; +use shp_traits::{ + CommitmentVerifier, MutateChallengeableProvidersInterface, ProofSubmittersInterface, + ProofsDealerInterface, ReadChallengeableProvidersInterface, TrieMutation, + TrieProofDeltaApplier, TrieRemoveMutation, +}; +use sp_runtime::{ + traits::{CheckedAdd, CheckedDiv, CheckedSub, Convert, Hash, One, Zero}, + ArithmeticError, BoundedVec, DispatchError, SaturatedConversion, Saturating, +}; +use sp_std::{ + collections::{btree_set::BTreeSet, vec_deque::VecDeque}, + vec::Vec, +}; + +use crate::{ + pallet, + types::{ + AccountIdFor, BalanceFor, BalancePalletFor, ChallengeHistoryLengthFor, + ChallengeTicksToleranceFor, ChallengesFeeFor, ChallengesQueueLengthFor, + CheckpointChallengePeriodFor, ForestVerifierFor, ForestVerifierProofFor, KeyFor, + KeyVerifierFor, KeyVerifierProofFor, MaxCustomChallengesPerBlockFor, + MaxSubmittersPerTickFor, MinChallengePeriodFor, Proof, ProviderIdFor, ProvidersPalletFor, + RandomChallengesPerBlockFor, RandomnessOutputFor, RandomnessProviderFor, + StakeToChallengePeriodFor, TargetTicksStorageOfSubmittersFor, TreasuryAccountFor, + }, + ChallengesQueue, ChallengesTicker, ChallengesTickerPaused, Error, Event, LastCheckpointTick, + LastDeletedTick, LastTickProviderSubmittedAProofFor, NotFullBlocksCount, Pallet, + PastBlocksWeight, PriorityChallengesQueue, SlashableProviders, TickToChallengesSeed, + TickToCheckpointChallenges, TickToProvidersDeadlines, ValidProofSubmittersLastTicks, +}; + +macro_rules! expect_or_err { + // Handle Option type + ($optional:expr, $error_msg:expr, $error_type:path) => {{ + match $optional { + Some(value) => value, + None => { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + } + }}; + // Handle boolean type + ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ + if !$condition { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + }}; + // Handle Result type + ($result:expr, $error_msg:expr, $error_type:path, result) => {{ + match $result { + Ok(value) => value, + Err(_) => { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + } + }}; +} + +impl Pallet +where + T: pallet::Config, +{ + /// Add custom challenge to ChallengesQueue. + /// + /// Charges a fee for the challenge. + /// This is to prevent spamming the network with challenges. If the challenge is already queued, + /// just return. Otherwise, add the challenge to the queue. + /// + /// Failures: + /// - `FeeChargeFailed`: If the fee transfer to the treasury account fails. + /// - `ChallengesQueueOverflow`: If the challenges queue is full. + pub fn do_challenge(who: &AccountIdFor, key: &KeyFor) -> DispatchResult { + // Charge a fee for the challenge. + BalancePalletFor::::transfer( + &who, + &TreasuryAccountFor::::get(), + ChallengesFeeFor::::get(), + Preservation::Expendable, + ) + .map_err(|_| Error::::FeeChargeFailed)?; + + // Enqueue challenge. + Self::enqueue_challenge(key) + } + + /// Submit proof. + /// + /// For a given `submitter`, verify the `proof` submitted. The proof is verified by checking + /// the forest proof and each key proof. + /// Relies on the `ProvidersPallet` to get the root for the submitter, the last tick for which + /// the submitter submitted a proof and the stake for the submitter. With that information, it + /// computes the next tick for which the submitter should be submitting a proof. It then gets + /// the seed for that tick and generates the challenges from the seed. It also checks if there + /// has been a Checkpoint Challenge block in between the last tick proven and the current tick. + /// If there has been, the Provider should have included proofs for the challenges in that block. + /// It then verifies the forest proof and each key proof, using the `ForestVerifier` and `KeyVerifier`. + pub fn do_submit_proof(submitter: &ProviderIdFor, proof: &Proof) -> DispatchResult { + let forest_proof = &proof.forest_proof; + let key_proofs = &proof.key_proofs; + + // Check if submitter is a registered Provider. + ensure!( + ProvidersPalletFor::::is_provider(*submitter), + Error::::NotProvider + ); + + // Check that key_proofs is not empty. + ensure!(!key_proofs.is_empty(), Error::::EmptyKeyProofs); + + // The check for whether forest_proof and each key_proof is not empty is handled by the corresponding + // verifiers for each. We do not preemptively check for this here, since the `CommitmentVerifier::Proof` + // type is not required to have an `is_empty` method. + + // Get root for submitter. + // If a submitter is a registered Provider, it must have a root, so this shouldn't happen. + // However, since the implementation of that is not up to this pallet, we need to check. + let root = ProvidersPalletFor::::get_root(*submitter) + .ok_or(Error::::ProviderRootNotFound)?; + + // Check that the root is not the default root. + // A default root means that the Provider is not providing any service yet, so he shouldn't be + // submitting any proofs. + ensure!( + root != ProvidersPalletFor::::get_default_root(), + Error::::ZeroRoot + ); + + // Get last tick for which the submitter submitted a proof. + let last_tick_proven = match LastTickProviderSubmittedAProofFor::::get(*submitter) { + Some(tick) => tick, + None => return Err(Error::::NoRecordOfLastSubmittedProof.into()), + }; + + // Get stake for submitter. + // If a submitter is a registered Provider, it must have a stake, so this shouldn't happen. + // However, since the implementation of that is not up to this pallet, we need to check. + let stake = ProvidersPalletFor::::get_stake(*submitter) + .ok_or(Error::::ProviderStakeNotFound)?; + + // Check that the stake is non-zero. + ensure!(stake > BalanceFor::::zero(), Error::::ZeroStake); + + // Compute the next tick for which the submitter should be submitting a proof. + let challenges_tick = last_tick_proven + .checked_add(&Self::stake_to_challenge_period(stake)) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Check that the challenges tick is lower than the current tick. + ensure!( + challenges_tick < ChallengesTicker::::get(), + Error::::ChallengesTickNotReached + ); + + // Check that the challenges tick is greater than current tick minus `ChallengeHistoryLength`, + // i.e. that the challenges tick is within the ticks this pallet keeps track of. + expect_or_err!( + challenges_tick + > ChallengesTicker::::get() + .saturating_sub(ChallengeHistoryLengthFor::::get()), + "Challenges tick is too old, beyond the history this pallet keeps track of. This should not be possible.", + Error::::ChallengesTickTooOld, + bool + ); + + // Check that the submitter is not submitting the proof to late, i.e. that the challenges tick + // is not greater or equal than `challenges_tick` + `T::ChallengeTicksTolerance::get()`. + // This should never happen, as the `TickToProvidersDeadlines` StorageMap is + // cleaned up every block. Therefore, if a Provider reached this deadline, it should have been + // slashed, and its next challenge tick pushed forwards. + let challenges_tick_deadline = challenges_tick + .checked_add(&T::ChallengeTicksTolerance::get()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + expect_or_err!( + challenges_tick_deadline > >::block_number(), + "Challenges tick is too late, the proof should be submitted at most `T::ChallengeTicksTolerance::get()` ticks after the challenges tick.", + Error::::ChallengesTickTooLate, + bool + ); + + // Get seed for challenges tick. + let seed = expect_or_err!( + TickToChallengesSeed::::get(challenges_tick), + "Seed for challenges tick not found, when checked it should be within history.", + Error::::SeedNotFound + ); + + // Generate forest challenges from seed. + let mut challenges = Self::generate_challenges_from_seed( + seed, + submitter, + RandomChallengesPerBlockFor::::get(), + ); + + // Check if there's been a Checkpoint Challenge tick in between the last tick proven and + // the tick for which the proof is being submitted. If there has been, the Provider should + // have included proofs for those checkpoint challenges. + let last_checkpoint_tick = LastCheckpointTick::::get(); + let mut checkpoint_challenges = None; + if last_tick_proven <= last_checkpoint_tick && last_checkpoint_tick < challenges_tick { + // Add challenges from the Checkpoint Challenge block. + checkpoint_challenges = + Some(expect_or_err!( + TickToCheckpointChallenges::::get(last_checkpoint_tick), + "Checkpoint challenges not found, when dereferencing in last registered checkpoint challenge block.", + Error::::CheckpointChallengesNotFound + )); + + if let Some(ref checkpoint_challenges) = checkpoint_challenges { + challenges.extend(checkpoint_challenges.iter().map(|(key, _)| key)); + } + } + + // Verify forest proof. + let mut forest_keys_proven = + ForestVerifierFor::::verify_proof(&root, &challenges, forest_proof) + .map_err(|_| Error::::ForestProofVerificationFailed)?; + + // Apply the delta to the Forest root for all mutations that are in checkpoint challenges. + if let Some(challenges) = checkpoint_challenges { + // Aggregate all mutations to apply to the Forest root. + let mutations: Vec<_> = challenges + .iter() + .filter_map(|(key, mutation)| match mutation { + Some(mutation) if forest_keys_proven.contains(key) => Some((*key, mutation)), + Some(_) | None => None, + }) + .collect(); + + if !mutations.is_empty() { + let mut mutations_applied = Vec::new(); + let new_root = mutations.iter().try_fold(root, |acc_root, mutation| { + // Remove the key from the list of `forest_keys_proven` to avoid having to verify the key proof. + forest_keys_proven.remove(&mutation.0); + + // Add mutation to list of mutations applied. + mutations_applied.push((mutation.0, mutation.1.clone())); + + // Apply the mutation to the Forest. + let apply_delta_result = >::apply_delta( + &acc_root, + &[(mutation.0, mutation.1.clone().into())], + forest_proof, + ) + .map_err(|_| Error::::FailedToApplyDelta); + + // If the mutation was correctly applied, update the Provider's info and return the new root. + match apply_delta_result { + Ok((_, new_root, mutated_keys_and_values)) => { + // Check that the mutated key is the same as the mutation (and is the only one). + ensure!( + mutated_keys_and_values.len() == 1, + Error::::FailedToApplyDelta + ); + ensure!( + mutated_keys_and_values[0].0 == mutation.0, + Error::::FailedToApplyDelta + ); + + // Use the interface exposed by the Providers pallet to update the submitting Provider + // after the key removal if the key had a value. + let removed_trie_value = &mutated_keys_and_values[0].1; + if let Some(trie_value) = removed_trie_value { + ProvidersPalletFor::::update_provider_after_key_removal( + submitter, trie_value, + ) + .map_err(|_| Error::::FailedToApplyDelta)?; + } + + Ok(new_root) + } + Err(err) => Err(err), + } + })?; + + // Emit event of mutation applied. + Self::deposit_event(Event::::MutationsApplied { + provider: *submitter, + mutations: mutations_applied, + new_root, + }); + + // Update root of Provider after all mutations have been applied to the Forest. + ::update_root( + *submitter, new_root, + )?; + } + }; + + // Verify each key proof. + for key_proven in forest_keys_proven { + // Check that there is a key proof for each key proven. + let key_proof = key_proofs + .get(&key_proven) + .ok_or(Error::::KeyProofNotFound)?; + + // Generate the challenges for the key. + let challenges = + Self::generate_challenges_from_seed(seed, submitter, key_proof.challenge_count); + + // Verify key proof. + KeyVerifierFor::::verify_proof(&key_proven, &challenges, &key_proof.proof) + .map_err(|_| Error::::KeyProofVerificationFailed)?; + } + + // Update `LastTickProviderSubmittedProofFor` to the challenge tick the provider has just + // submitted a proof for. + LastTickProviderSubmittedAProofFor::::set(*submitter, Some(challenges_tick)); + + // Remove the submitter from its current deadline registered in `TickToProvidersDeadlines`. + TickToProvidersDeadlines::::remove(challenges_tick_deadline, submitter); + + // Calculate the next tick for which the submitter should be submitting a proof. + let next_challenges_tick = challenges_tick + .checked_add(&Self::stake_to_challenge_period(stake)) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Add tolerance to `next_challenges_tick` to know when is the next deadline for submitting a + // proof, for this provider. + let next_challenges_tick_deadline = next_challenges_tick + .checked_add(&T::ChallengeTicksTolerance::get()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Add this Provider to the `TickToProvidersDeadlines` StorageMap, with its new deadline. + TickToProvidersDeadlines::::set(next_challenges_tick_deadline, submitter, Some(())); + + // Add this Provider to the `ValidProofSubmittersLastTicks` StorageMap, with the current tick number. + let current_tick_valid_submitters = + ValidProofSubmittersLastTicks::::take(ChallengesTicker::::get()); + match current_tick_valid_submitters { + // If the set already exists and has valid submitters, we just insert the new submitter. + Some(mut valid_submitters) => { + let did_not_already_exist = expect_or_err!(valid_submitters.try_insert(*submitter), "The set should never be full as the limit we set should be greater than the implicit limit given by max block weight.", Error::::TooManyValidProofSubmitters, result); + // We only update storage if the Provider ID wasn't yet in the set to avoid unnecessary writes. + if did_not_already_exist { + ValidProofSubmittersLastTicks::::insert( + ChallengesTicker::::get(), + valid_submitters, + ); + } + } + // If the set doesn't exist, we create it and insert the submitter. + None => { + let mut new_valid_submitters = + BoundedBTreeSet::, MaxSubmittersPerTickFor>::new(); + expect_or_err!( + new_valid_submitters.try_insert(*submitter), + "The set has just been created, it's empty and as such won't be full. qed", + Error::::TooManyValidProofSubmitters, + result + ); + ValidProofSubmittersLastTicks::::insert( + ChallengesTicker::::get(), + new_valid_submitters, + ); + } + } + + Ok(()) + } + + /// Generate a new round of challenges, be it random or checkpoint. + /// + /// Random challenges are automatically generated based on some external source of + /// randomness, and are added to [`TickToChallengesSeed`], for this tick's number. + /// + /// It also takes care of including the challenges from the `ChallengesQueue` and + /// `PriorityChallengesQueue`. These custom challenges are only included in "checkpoint" + /// ticks + /// + /// Additionally, it takes care of checking if there are Providers that have + /// failed to submit a proof, and should have submitted one by this tick. It does so + /// by checking the [`TickToProvidersDeadlines`] StorageMap. If a Provider is found + /// to have failed to submit a proof, it is subject to slashing. + /// + /// Finally, it cleans up: + /// - The [`TickToChallengesSeed`] StorageMap, removing entries older than `ChallengeHistoryLength`. + /// - The [`TickToCheckpointChallenges`] StorageMap, removing the previous checkpoint challenge block. + /// - The [`TickToProvidersDeadlines`] StorageMap, removing entries for the current challenges tick. + pub fn do_new_challenges_round(weight: &mut WeightMeter) { + // Increment the challenges' ticker. + let mut challenges_ticker = ChallengesTicker::::get(); + challenges_ticker.saturating_inc(); + ChallengesTicker::::set(challenges_ticker); + weight.consume(T::DbWeight::get().reads_writes(1, 1)); + + // Store random seed for this tick. + let (seed, _) = RandomnessProviderFor::::random(challenges_ticker.encode().as_ref()); + TickToChallengesSeed::::set(challenges_ticker, Some(seed)); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Remove the oldest challenge seed stored, to clean up the storage. + let tick_to_remove = challenges_ticker.checked_sub(&ChallengeHistoryLengthFor::::get()); + if let Some(tick_to_remove) = tick_to_remove { + TickToChallengesSeed::::remove(tick_to_remove); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + } + + // Emit new challenge seed event. + Self::deposit_event(Event::NewChallengeSeed { + challenges_ticker, + seed, + }); + + let last_checkpoint_tick = LastCheckpointTick::::get(); + + // Count last checkpoint challenges tick challenges + let checkpoint_challenges_count = + TickToCheckpointChallenges::::get(last_checkpoint_tick) + .unwrap_or_else(|| + // Returning an empty list so slashable providers will not accrue any failed proof submissions for checkpoint challenges. + BoundedVec::new()) + .len(); + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + // This hook does not return an error, and it cannot fail, that's why we use `saturating_add`. + let next_checkpoint_tick = + last_checkpoint_tick.saturating_add(T::CheckpointChallengePeriod::get()); + if challenges_ticker == next_checkpoint_tick { + // This is a checkpoint challenge round, so we also generate new checkpoint challenges. + Self::do_new_checkpoint_challenge_round(challenges_ticker, weight); + } + weight.consume(T::DbWeight::get().reads_writes(2, 0)); + + // If there are providers left in `TickToProvidersDeadlines` for this tick, + // they are marked as slashable. + let mut slashable_providers = + TickToProvidersDeadlines::::drain_prefix(challenges_ticker); + while let Some((provider, _)) = slashable_providers.next() { + // One read for every provider in the prefix, and one write as we're consuming and deleting the entry. + weight.consume(T::DbWeight::get().reads_writes(1, 1)); + + // Accrue number of failed proof submission for this slashable provider. + // Add custom checkpoint challenges if the provider needed to respond to them. + SlashableProviders::::mutate(provider, |slashable| { + let mut accrued = slashable.unwrap_or(0); + + let last_tick_provider_submitted_proof = + match LastTickProviderSubmittedAProofFor::::get(provider) { + Some(tick) => tick, + None => { + Self::deposit_event(Event::NoRecordOfLastSubmittedProof { provider }); + + #[cfg(test)] + unreachable!( + "Provider should have a last tick it submitted a proof for." + ); + + #[allow(unreachable_code)] + { + // If the Provider has no record of the last tick it submitted a proof for, + // we set it to the current challenges ticker, so they will not be slashed. + challenges_ticker + } + } + }; + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + let challenge_ticker_provider_should_have_responded_to = + challenges_ticker.saturating_sub(T::ChallengeTicksTolerance::get()); + + if checkpoint_challenges_count != 0 + && last_tick_provider_submitted_proof <= last_checkpoint_tick + && last_checkpoint_tick < challenge_ticker_provider_should_have_responded_to + { + accrued = accrued.saturating_add(checkpoint_challenges_count as u32); + } + + accrued = accrued.saturating_add(RandomChallengesPerBlockFor::::get()); + + *slashable = Some(accrued); + }); + + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Get the stake for this Provider, to know its challenge period. + // If a submitter is a registered Provider, it must have a stake, so there shouldn't be an error. + let stake = match ProvidersPalletFor::::get_stake(provider) { + Some(stake) => stake, + // But to avoid panics, in the odd case of a Provider not being registered, we + // arbitrarily set the stake to be that which would result in `CheckpointChallengePeriod` ticks of challenge period. + None => { + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + let checkpoint_challenge_period = + CheckpointChallengePeriodFor::::get().saturated_into::(); + StakeToChallengePeriodFor::::get() * checkpoint_challenge_period.into() + } + }; + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + // Calculate the next challenge deadline for this Provider. + // At this point, we are processing all providers who have reached their deadline (i.e. tolerance ticks after the tick they should provide a proof for): + // challenge_ticker = last_tick_provider_should_have_submitted_a_proof_for + ChallengeTicksTolerance + // + // By definition, the next deadline should be tolerance ticks after the next tick they should submit proof for (i.e. one period after the last tick they should have submitted a proof for): + // next_challenge_deadline = last_tick_provider_should_have_submitted_a_proof_for + provider_period + ChallengeTicksTolerance + // + // Therefore, the next deadline is one period from now: + // next_challenge_deadline = challenge_ticker + provider_period + let next_challenge_deadline = + challenges_ticker.saturating_add(Self::stake_to_challenge_period(stake)); + + // Update this Provider's next challenge deadline. + TickToProvidersDeadlines::::set(next_challenge_deadline, provider, Some(())); + + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Calculate the tick for which the Provider should have submitted a proof. + let last_interval_tick = + challenges_ticker.saturating_sub(T::ChallengeTicksTolerance::get()); + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + // Update this Provider's last interval tick for the next challenge. + LastTickProviderSubmittedAProofFor::::set(provider, Some(last_interval_tick)); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Emit slashable provider event. + Self::deposit_event(Event::SlashableProvider { + provider, + next_challenge_deadline, + }); + } + } + + /// Check if the network is presumably under a spam attack. + /// + /// The function looks at the weight used in the past `BlockFullnessPeriod` blocks, comparing it + /// with the maximum allowed weight (`max_weight_for_class`) for the dispatch class of `submit_proof` extrinsics. + /// The idea is to track blocks that have not been filled to capacity within a + /// specific period (`BlockFullnessPeriod`) and determine if there is enough "headroom" + /// (unused block capacity) to consider the network not under spam. + pub fn do_check_spamming_condition(weight: &mut WeightMeter) { + // Get the maximum weight for the dispatch class of `submit_proof` extrinsics. + let weights = T::BlockWeights::get(); + let max_weight_for_class = weights + .get(DispatchClass::Normal) + .max_total + .unwrap_or(weights.max_block); + + let current_block = frame_system::Pallet::::block_number(); + + // Get the number of blocks that have been considered _not_ full in the past `BlockFullnessPeriod`. + let not_full_blocks_count = NotFullBlocksCount::::get(); + let mut new_not_full_blocks_count = not_full_blocks_count.clone(); + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + // This would only be `None` if the block number is 0, so this should be safe. + if let Some(prev_block) = current_block.checked_sub(&1u32.into()) { + // Get the weight usage in the previous block. + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + if let Some(weight_used_in_prev_block) = PastBlocksWeight::::get(prev_block) { + // Check how much weight was left in the previous block, compared to the maximum weight. + // This is computed both for proof size and ref time. + let weight_left_in_prev_block = + max_weight_for_class.saturating_sub(weight_used_in_prev_block); + + // If the weight left in the previous block is greater or equal than the headroom, for both proof size or ref time, + // we consider the previous block to be NOT full and count it as such. + if weight_left_in_prev_block.ref_time() + >= T::BlockFullnessHeadroom::get().ref_time() + && weight_left_in_prev_block.proof_size() + >= T::BlockFullnessHeadroom::get().proof_size() + { + // Increment the counter of blocks that are not full. + new_not_full_blocks_count = + new_not_full_blocks_count.saturating_add(1u32.into()); + } + } + } + + // This would be `None` during the first `BlockFullnessPeriod` + 1 blocks. + if let Some(oldest_block_fullness_number) = + current_block.checked_sub(&T::BlockFullnessPeriod::get().saturating_add(1u32.into())) + { + // Get the weight usage in the oldest registered block. + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + if let Some(weight_used_in_oldest_block) = + PastBlocksWeight::::get(oldest_block_fullness_number) + { + // Check how much weight was left in the oldest block, compared to the maximum weight. + // This is computed both for proof size and ref time. + let weight_left_in_oldest_block = + max_weight_for_class.saturating_sub(weight_used_in_oldest_block); + + // If the weight left in the oldest block is greater or equal than the headroom, for both proof size or ref time, + // we consider the oldest block to be NOT full. If that is the case, we have to remove it from the + // count as it is now out of the `BlockFullnessPeriod` of blocks taken into account. + if weight_left_in_oldest_block.ref_time() + >= T::BlockFullnessHeadroom::get().ref_time() + && weight_left_in_oldest_block.proof_size() + >= T::BlockFullnessHeadroom::get().proof_size() + { + // Decrement the counter of blocks that are not full. + new_not_full_blocks_count = + new_not_full_blocks_count.saturating_sub(1u32.into()); + } + } + } + + // If there was a change in the number of blocks that were not full, we need to update the storage. + if new_not_full_blocks_count != not_full_blocks_count { + NotFullBlocksCount::::set(new_not_full_blocks_count); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + } + + // At this point, we have an updated count of blocks that were not full in the past `BlockFullnessPeriod`. + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + if ChallengesTicker::::get() > T::BlockFullnessPeriod::get() { + // Running this check only makes sense after `ChallengesTicker` has advanced past `BlockFullnessPeriod`. + // To consider the network NOT to be under spam, we need more than `min_non_full_blocks` blocks to be not full. + let min_non_full_blocks_ratio = T::MinNotFullBlocksRatio::get(); + let min_non_full_blocks = + min_non_full_blocks_ratio.mul_floor(T::BlockFullnessPeriod::get()); + + // If `not_full_blocks_count` is greater than `min_non_full_blocks`, we consider the network NOT to be under spam. + if new_not_full_blocks_count > min_non_full_blocks { + // The network is NOT considered to be under a spam attack, so we resume the `ChallengesTicker`. + ChallengesTickerPaused::::set(None); + } else { + // At this point, the network is presumably under a spam attack, so we pause the `ChallengesTicker`. + ChallengesTickerPaused::::set(Some(())); + } + weight.consume(T::DbWeight::get().reads_writes(1, 1)); + } + } + + /// Generate new checkpoint challenges for a given block. + /// + /// Fills up a new vector of checkpoint challenges with challenges in the `PriorityChallengesQueue`, + /// and the `ChallengesQueue` if there is space left. + /// + /// Cleans up the `TickToCheckpointChallenges` StorageMap, removing the previous checkpoint challenge block. + fn do_new_checkpoint_challenge_round( + current_tick: BlockNumberFor, + weight: &mut WeightMeter, + ) { + let mut new_checkpoint_challenges: BoundedVec< + (KeyFor, Option), + MaxCustomChallengesPerBlockFor, + > = BoundedVec::new(); + + // Fill up this round's checkpoint challenges with challenges in the `PriorityChallengesQueue`. + // It gets filled up until the max number of custom challenges for a block is reached, or until + // there are no more challenges in the `PriorityChallengesQueue`. + let original_priority_challenges_queue = PriorityChallengesQueue::::get(); + let mut priority_challenges_queue = + VecDeque::from(original_priority_challenges_queue.to_vec()); + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + while !new_checkpoint_challenges.is_full() && !priority_challenges_queue.is_empty() { + let challenge = match priority_challenges_queue.pop_front() { + Some(challenge) => challenge, + // This should not happen, as we check that priority_challenges_queue is not empty + // in the while condition above, but we add this to be safe. + None => break, + }; + + if new_checkpoint_challenges.try_push(challenge).is_err() { + // This should not happen, as we check that new_checkpoint_challenges is not full + // in the while condition above, but we add this to be safe. + break; + } + } + + // Convert priority_challenges_queue back to a bounded vector. + let new_priority_challenges_queue: BoundedVec< + (KeyFor, Option), + ChallengesQueueLengthFor, + > = Vec::from(priority_challenges_queue) + .try_into() + .unwrap_or_else(|_| original_priority_challenges_queue); + + // Reset the priority challenges queue with the leftovers. + PriorityChallengesQueue::::set(new_priority_challenges_queue); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Fill up this round's checkpoint challenges with challenges in the `ChallengesQueue`. + // It gets filled up until the max number of custom challenges for a block is reached, or until + // there are no more challenges in the `ChallengesQueue`. + let mut challenges_queue = VecDeque::from(ChallengesQueue::::get().to_vec()); + weight.consume(T::DbWeight::get().reads_writes(1, 0)); + + while !new_checkpoint_challenges.is_full() && !challenges_queue.is_empty() { + let challenge = match challenges_queue.pop_front() { + Some(challenge) => challenge, + // This should not happen, as we check that challenges_queue is not empty + // in the while condition above, but we add this to be safe. + None => break, + }; + + if new_checkpoint_challenges + .try_push((challenge, None)) + .is_err() + { + // This should not happen, as we check that new_checkpoint_challenges is not full + // in the while condition above, but we add this to be safe. + break; + } + } + + // Convert challenges_queue back to a bounded vector. + let new_challenges_queue: BoundedVec, ChallengesQueueLengthFor> = + Vec::from(challenges_queue) + .try_into() + .unwrap_or_else(|_| BoundedVec::new()); + + // Reset the challenges queue with the leftovers. + ChallengesQueue::::set(new_challenges_queue); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Store the new checkpoint challenges. + TickToCheckpointChallenges::::set(current_tick, Some(new_checkpoint_challenges.clone())); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Remove the last checkpoint challenge from storage to clean up. + let last_checkpoint_tick = LastCheckpointTick::::get(); + TickToCheckpointChallenges::::remove(last_checkpoint_tick); + weight.consume(T::DbWeight::get().reads_writes(1, 1)); + + // Set this tick as the last checkpoint tick. + LastCheckpointTick::::set(current_tick); + weight.consume(T::DbWeight::get().reads_writes(0, 1)); + + // Emit new checkpoint challenge event. + Self::deposit_event(Event::NewCheckpointChallenge { + challenges_ticker: current_tick, + challenges: new_checkpoint_challenges, + }); + } + + /// Convert stake to challenge period. + /// + /// [`StakeToChallengePeriodFor`] is divided by `stake` to get the number of blocks in between challenges + /// for a Provider. The result is then converted to `BlockNumber` type. The division saturates at [`MinChallengePeriodFor`]. + pub(crate) fn stake_to_challenge_period(stake: BalanceFor) -> BlockNumberFor { + let min_challenge_period = MinChallengePeriodFor::::get(); + let challenge_period = match StakeToChallengePeriodFor::::get().checked_div(&stake) { + Some(block_period) => T::StakeToBlockNumber::convert(block_period), + None => min_challenge_period, + }; + + // Return the maximum between the calculated challenge period and the minimum challenge period. + min_challenge_period.max(challenge_period) + } + + /// Add challenge to ChallengesQueue. + /// + /// Check if challenge is already queued. If it is, just return. Otherwise, add the challenge + /// to the queue. + fn enqueue_challenge(key: &KeyFor) -> DispatchResult { + // Get challenges queue from storage. + let mut challenges_queue = ChallengesQueue::::get(); + + // Check if challenge is already queued. If it is, just return. + if challenges_queue.contains(&key) { + return Ok(()); + } + + // Add challenge to queue. + challenges_queue + .try_push(*key) + .map_err(|_| Error::::ChallengesQueueOverflow)?; + + // Set challenges queue in storage. + ChallengesQueue::::put(challenges_queue); + + Ok(()) + } + + /// Add challenge to `PriorityChallengesQueue`. + /// + /// Check if challenge is already queued. If it is, just return. Otherwise, add the challenge + /// to the queue. + fn enqueue_challenge_with_priority( + key: &KeyFor, + mutation: Option, + ) -> DispatchResult { + // Get priority challenges queue from storage. + let mut priority_challenges_queue = PriorityChallengesQueue::::get(); + + // Check if challenge is already queued. If it is, just return. + if priority_challenges_queue.contains(&(*key, mutation.clone())) { + return Ok(()); + } + + // Add challenge to queue. + priority_challenges_queue + .try_push((*key, mutation)) + .map_err(|_| Error::::PriorityChallengesQueueOverflow)?; + + // Set priority challenges queue in storage. + PriorityChallengesQueue::::put(priority_challenges_queue); + + Ok(()) + } + + /// Generate challenges from seed. + /// + /// Generate a number of challenges from a seed and a Provider's ID. + /// Challenges are generated by hashing the seed, the Provider's ID and an index. + pub(crate) fn generate_challenges_from_seed( + seed: RandomnessOutputFor, + provider_id: &ProviderIdFor, + count: u32, + ) -> Vec { + let mut challenges = Vec::new(); + + for i in 0..count { + // Each challenge is generated by hashing the seed, the provider's ID and the index. + let challenge = T::MerkleTrieHashing::hash( + &[ + seed.as_ref(), + provider_id.encode().as_ref(), + i.encode().as_ref(), + ] + .concat(), + ); + + challenges.push(challenge.into()); + } + + challenges + } + + /// Trim the storage that holds the Providers that submitted valid proofs in the last ticks until there's + /// `TargetTicksOfProofsStorage` ticks left (or until the remaining weight allows it). + /// + /// This function is called in the `on_idle` hook, which means it's only called when the block has + /// unused weight. + /// + /// It removes the oldest tick from the storage that holds the providers that submitted valid proofs + /// in the last ticks as many times as the remaining weight allows it, but at most until the storage + /// has `TargetTicksOfProofsStorage` ticks left. + pub fn do_trim_valid_proof_submitters_last_ticks( + _n: BlockNumberFor, + usable_weight: Weight, + ) -> Weight { + // Initialize the weight used by this function. + let mut used_weight = Weight::zero(); + + // Check how many ticks should be removed to keep the storage at the target amount. + let mut last_deleted_tick = LastDeletedTick::::get(); + used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); + let target_ticks_to_keep = TargetTicksStorageOfSubmittersFor::::get(); + used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); + let current_tick = ChallengesTicker::::get(); + used_weight = used_weight.saturating_add(T::DbWeight::get().reads(1)); + let ticks_to_remove = current_tick + .saturating_sub(last_deleted_tick) + .saturating_sub(target_ticks_to_keep.into()); + + // Check how much ticks can be removed considering weight limitations + let weight_to_remove_tick = T::DbWeight::get().reads_writes(0, 2); + let removable_ticks = usable_weight + .saturating_sub(used_weight) + .checked_div_per_component(&weight_to_remove_tick); + + // If there is enough weight to remove ticks, try to remove as many ticks as possible until the target is reached. + if let Some(removable_ticks) = removable_ticks { + let removable_ticks = + removable_ticks.min(ticks_to_remove.try_into().unwrap_or(u64::MAX)); + // Remove all the ticks that we can, until we reach the target amount. + for _ in 0..removable_ticks { + // Get the next tick to delete. + let next_tick_to_delete = last_deleted_tick.saturating_add(One::one()); + + // Remove it from storage + ValidProofSubmittersLastTicks::::remove(next_tick_to_delete); + + // Update the last removed tick + LastDeletedTick::::set(next_tick_to_delete); + last_deleted_tick = next_tick_to_delete; // We do this to avoid having to read from storage again. + + // Increment the used weight. + used_weight = used_weight.saturating_add(weight_to_remove_tick); + } + } + + // Return the weight used by this function. + used_weight + } +} + +impl ProofsDealerInterface for Pallet { + type ProviderId = ProviderIdFor; + type ForestProof = ForestVerifierProofFor; + type KeyProof = KeyVerifierProofFor; + type MerkleHash = T::MerkleTrieHash; + type MerkleHashing = T::MerkleTrieHashing; + type RandomnessOutput = RandomnessOutputFor; + type TickNumber = BlockNumberFor; + + fn verify_forest_proof( + provider_id: &Self::ProviderId, + challenges: &[Self::MerkleHash], + proof: &Self::ForestProof, + ) -> Result, DispatchError> { + // Check if submitter is a registered Provider. + ensure!( + ProvidersPalletFor::::is_provider(*provider_id), + Error::::NotProvider + ); + + // Get root for submitter. + // If a submitter is a registered Provider, it must have a root. + let root = ProvidersPalletFor::::get_root(*provider_id) + .ok_or(Error::::ProviderRootNotFound)?; + + // Verify forest proof. + ForestVerifierFor::::verify_proof(&root, challenges, proof) + .map_err(|_| Error::::ForestProofVerificationFailed.into()) + } + + fn verify_generic_forest_proof( + root: &Self::MerkleHash, + challenges: &[Self::MerkleHash], + proof: &Self::ForestProof, + ) -> Result, DispatchError> { + // Verify forest proof. + ForestVerifierFor::::verify_proof(&root, challenges, proof) + .map_err(|_| Error::::ForestProofVerificationFailed.into()) + } + + fn verify_key_proof( + key: &Self::MerkleHash, + challenges: &[Self::MerkleHash], + proof: &Self::KeyProof, + ) -> Result, DispatchError> { + // Verify key proof. + KeyVerifierFor::::verify_proof(key, &challenges, proof) + .map_err(|_| Error::::KeyProofVerificationFailed.into()) + } + + fn challenge(key_challenged: &Self::MerkleHash) -> DispatchResult { + Self::enqueue_challenge(key_challenged) + } + + fn challenge_with_priority( + key_challenged: &Self::MerkleHash, + mutation: Option, + ) -> DispatchResult { + Self::enqueue_challenge_with_priority(key_challenged, mutation) + } + + fn generate_challenges_from_seed( + seed: Self::RandomnessOutput, + provider_id: &Self::ProviderId, + count: u32, + ) -> Vec { + Self::generate_challenges_from_seed(seed, provider_id, count) + } + + fn apply_delta( + provider_id: &Self::ProviderId, + mutations: &[(Self::MerkleHash, TrieMutation)], + proof: &Self::ForestProof, + ) -> Result { + // Check if submitter is a registered Provider. + ensure!( + ProvidersPalletFor::::is_provider(*provider_id), + Error::::NotProvider + ); + + // Get root for submitter. + // If a submitter is a registered Provider, it must have a root. + let root = ProvidersPalletFor::::get_root(*provider_id) + .ok_or(Error::::ProviderRootNotFound)?; + + Ok( + >::apply_delta( + &root, mutations, proof, + ) + .map_err(|_| Error::::FailedToApplyDelta)? + .1, + ) + } + + fn generic_apply_delta( + root: &Self::MerkleHash, + mutations: &[(Self::MerkleHash, TrieMutation)], + proof: &Self::ForestProof, + ) -> Result { + Ok( + >::apply_delta( + &root, mutations, proof, + ) + .map_err(|_| Error::::FailedToApplyDelta)? + .1, + ) + } + + fn initialise_challenge_cycle(provider_id: &Self::ProviderId) -> DispatchResult { + // Check that `who` is a registered Provider. + if !ProvidersPalletFor::::is_provider(*provider_id) { + return Err(Error::::NotProvider.into()); + } + + // Get stake for submitter. + // If a submitter is a registered Provider, it must have a stake, so this shouldn't happen. + // However, since the implementation of that is not up to this pallet, we need to check. + let stake = ProvidersPalletFor::::get_stake(*provider_id) + .ok_or(Error::::ProviderStakeNotFound)?; + + // Check that the stake is non-zero. + ensure!(stake > BalanceFor::::zero(), Error::::ZeroStake); + + // Check if this Provider previously had a challenge cycle initialised. + if let Some(last_tick_proven) = LastTickProviderSubmittedAProofFor::::get(*provider_id) { + // Compute the next tick for which the Provider should have been submitting a proof. + let old_next_challenge_tick = last_tick_proven + .checked_add(&Self::stake_to_challenge_period(stake)) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Calculate the deadline for submitting a proof. Should be the next challenge tick + the challenges tick tolerance. + let old_next_challenge_deadline = old_next_challenge_tick + .checked_add(&ChallengeTicksToleranceFor::::get()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Remove the old deadline. + TickToProvidersDeadlines::::remove(old_next_challenge_deadline, *provider_id); + } + + // Set `LastTickProviderSubmittedAProofFor` to the current tick. + let current_tick = ChallengesTicker::::get(); + LastTickProviderSubmittedAProofFor::::set(*provider_id, Some(current_tick)); + + // Compute the next tick for which the Provider should be submitting a proof. + let next_challenge_tick = current_tick + .checked_add(&Self::stake_to_challenge_period(stake)) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Calculate the deadline for submitting a proof. Should be the next challenge tick + the challenges tick tolerance. + let next_challenge_deadline = next_challenge_tick + .checked_add(&ChallengeTicksToleranceFor::::get()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Set the deadline for submitting a proof. + TickToProvidersDeadlines::::set(next_challenge_deadline, *provider_id, Some(())); + + // Emit event. + Self::deposit_event(Event::::NewChallengeCycleInitialised { + current_tick, + next_challenge_deadline, + provider: *provider_id, + maybe_provider_account: ProvidersPalletFor::::get_owner_account(*provider_id), + }); + + Ok(()) + } + + fn get_current_tick() -> Self::TickNumber { + ChallengesTicker::::get() + } +} + +impl ProofSubmittersInterface for Pallet { + type ProviderId = ProviderIdFor; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = MaxSubmittersPerTickFor; + + fn get_proof_submitters_for_tick( + tick_number: &Self::TickNumber, + ) -> Option> { + ValidProofSubmittersLastTicks::::get(tick_number) + } + + fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option { + SlashableProviders::::get(provider_id) + } + + fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) { + SlashableProviders::::remove(provider_id); + } +} + +/// Runtime API implementation for the ProofsDealer pallet. +impl Pallet +where + T: pallet::Config, +{ + pub fn get_last_tick_provider_submitted_proof( + provider_id: &ProviderIdFor, + ) -> Result, GetLastTickProviderSubmittedProofError> { + // Check if submitter is a registered Provider. + if !ProvidersPalletFor::::is_provider(*provider_id) { + return Err(GetLastTickProviderSubmittedProofError::ProviderNotRegistered); + } + + LastTickProviderSubmittedAProofFor::::get(provider_id) + .ok_or(GetLastTickProviderSubmittedProofError::ProviderNeverSubmittedProof) + } + + pub fn get_last_checkpoint_challenge_tick() -> BlockNumberFor { + LastCheckpointTick::::get() + } + + pub fn get_checkpoint_challenges( + tick: BlockNumberFor, + ) -> Result, Option)>, GetCheckpointChallengesError> { + // Check that the tick is smaller than the last checkpoint tick. + if LastCheckpointTick::::get() < tick { + return Err(GetCheckpointChallengesError::TickGreaterThanLastCheckpointTick); + } + + let checkpoint_challenges = TickToCheckpointChallenges::::get(tick) + .ok_or(GetCheckpointChallengesError::NoCheckpointChallengesInTick)?; + + Ok(checkpoint_challenges.into()) + } + + pub fn get_challenge_seed( + tick: BlockNumberFor, + ) -> Result, GetChallengeSeedError> { + let current_tick = ChallengesTicker::::get(); + if tick > current_tick { + return Err(GetChallengeSeedError::TickIsInTheFuture); + } + + let seed = TickToChallengesSeed::::get(tick) + .ok_or(GetChallengeSeedError::TickBeyondLastSeedStored)?; + + Ok(seed) + } + + pub fn get_challenge_period( + provider_id: &ProviderIdFor, + ) -> Result, GetChallengePeriodError> { + let stake = ProvidersPalletFor::::get_stake(*provider_id) + .ok_or(GetChallengePeriodError::ProviderNotRegistered)?; + + Ok(Self::stake_to_challenge_period(stake)) + } + + pub fn get_checkpoint_challenge_period() -> BlockNumberFor { + CheckpointChallengePeriodFor::::get() + } + + pub fn get_challenges_from_seed( + seed: &RandomnessOutputFor, + provider_id: &ProviderIdFor, + count: u32, + ) -> Vec> { + Self::generate_challenges_from_seed(*seed, provider_id, count) + } + + pub fn get_forest_challenges_from_seed( + seed: &RandomnessOutputFor, + provider_id: &ProviderIdFor, + ) -> Vec> { + Self::generate_challenges_from_seed( + *seed, + provider_id, + RandomChallengesPerBlockFor::::get(), + ) + } + + pub fn get_current_tick() -> BlockNumberFor { + ChallengesTicker::::get() + } + + pub fn get_next_deadline_tick( + provider_id: &ProviderIdFor, + ) -> Result, GetNextDeadlineTickError> { + // Check if the provider is indeed a registered Provider. + if !ProvidersPalletFor::::is_provider(*provider_id) { + return Err(GetNextDeadlineTickError::ProviderNotRegistered); + } + + // Get the last tick for which the submitter submitted a proof. + let last_tick_provider_submitted_proof = + LastTickProviderSubmittedAProofFor::::get(provider_id) + .ok_or(GetNextDeadlineTickError::ProviderNotInitialised)?; + + // The next deadline tick is the last tick + the challenge period + the challenge tolerance. + let challenge_period = Self::get_challenge_period(provider_id) + .map_err(|_| GetNextDeadlineTickError::ProviderNotRegistered)?; + let next_deadline_tick = last_tick_provider_submitted_proof + .checked_add(&challenge_period) + .ok_or(GetNextDeadlineTickError::ArithmeticOverflow)? + .checked_add(&ChallengeTicksToleranceFor::::get()) + .ok_or(GetNextDeadlineTickError::ArithmeticOverflow)?; + + Ok(next_deadline_tick) + } +} diff --git a/pallets/providers/Cargo.toml b/pallets/providers/Cargo.toml index a03fb5746..6967b91db 100644 --- a/pallets/providers/Cargo.toml +++ b/pallets/providers/Cargo.toml @@ -1,80 +1,80 @@ -[package] -name = "pallet-storage-providers" -description = "FRAME pallet that works as a registry for storage providers (both main and backup ones)" -version = "0.1.0" -homepage = { workspace = true } -license = { workspace = true } -authors = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { workspace = true } -scale-info = { workspace = true } - -# Local -pallet-storage-providers-runtime-api = { workspace = true } -shp-constants = { workspace = true } -shp-traits = { workspace = true, default-features = false } - -# Substrate -frame-benchmarking = { workspace = true, optional = true } -frame-support = { workspace = true } -frame-system = { workspace = true } - -sp-runtime = { workspace = true } -sp-std = { workspace = true } -sp-trie = { workspace = true } - -[dev-dependencies] -serde = { workspace = true } - -#Local -pallet-proofs-dealer = { workspace = true } -pallet-payment-streams = { workspace = true } -shp-file-metadata = { workspace = true } - -# Substrate -sp-core = { workspace = true } -sp-io = { workspace = true } - -# Frame -pallet-balances = { workspace = true, features = ["std"] } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-storage-providers-runtime-api/std", - "pallet-proofs-dealer/std", - "pallet-payment-streams/std", - "scale-info/std", - "shp-constants/std", - "shp-traits/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] +[package] +name = "pallet-storage-providers" +description = "FRAME pallet that works as a registry for storage providers (both main and backup ones)" +version = "0.1.0" +homepage = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } + +# Local +pallet-storage-providers-runtime-api = { workspace = true } +shp-constants = { workspace = true } +shp-traits = { workspace = true, default-features = false } + +# Substrate +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } + +sp-runtime = { workspace = true } +sp-std = { workspace = true } +sp-trie = { workspace = true } + +[dev-dependencies] +serde = { workspace = true } + +#Local +pallet-proofs-dealer = { workspace = true } +pallet-payment-streams = { workspace = true } +shp-file-metadata = { workspace = true } + +# Substrate +sp-core = { workspace = true } +sp-io = { workspace = true } + +# Frame +pallet-balances = { workspace = true, features = ["std"] } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-storage-providers-runtime-api/std", + "pallet-proofs-dealer/std", + "pallet-payment-streams/std", + "scale-info/std", + "shp-constants/std", + "shp-traits/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/providers/README.md b/pallets/providers/README.md index a3b621acb..c93907401 100644 --- a/pallets/providers/README.md +++ b/pallets/providers/README.md @@ -1,416 +1,416 @@ -# Storage Providers Pallet - -## Overview - -The Storage Providers pallet is designed for Substrate-based blockchains, providing a robust framework for managing storage providers within a decentralized network. This pallet allows for the registration and management of Main Storage Providers (MSPs) and Backup Storage Providers (BSPs). -It is designed to be flexible and extensible, allowing developers to integrate it with decentralized storage solutions in their blockchain networks with ease. It provides essential services for managing the roles and capabilities of storage providers within the ecosystem, ensuring transparency, security, and operational integrity. - -### Features - -- Provider Management: Enables the sign-up of MSPs and BSPs, allowing them to register and become part of the network's storage solution. -- Capacity and Value Proposition Management: Providers can modify their storage capacities and update their value propositions to reflect their current offerings and capabilities. -- Lifecycle Events: Tracks significant lifecycle events like sign-up requests, confirmations, and sign-offs through a series of emitted events. -- Robust Access Controls: Ensures that only authorized operations are performed by authorized storage providers. -- Error Handling: Provides detailed error messages to help developers understand and resolve issues during storage provider management operations. - -### Target Audience - -This pallet is intended for blockchain developers interested in integrating decentralized storage solutions into their Substrate-based blockchain. It provides essential services for managing the roles and capabilities of storage providers within the ecosystem. It was developed with StorageHub's specific providers framework but can be used by any other network that fits the Main Storage Provider/Backup Storage Provider structure, with some modifications. - -## Design - -### Main Storage Providers (MSPs) - -A Main Storage Provider (MSP) is responsible for storing the complete file provided by the user and making it readily available for off-chain retrieval. MSPs are expected to compete for user adoption, and a certain degree of centralization is anticipated. They are considered trusted parties for data retrieval. - -MSPs are compensated for their services by the user. The amount is agreed upon when the user selects the MSP, and it's typically higher than what a Backup Storage Provider receives due to the infrastructure required for convenient data retrieval. - -MSPs are required to regularly prove that they continue to store the data they've committed to. This ensures the integrity and availability of the data in the system. - -### Backup Storage Providers (BSPs) - -Backup Storage Providers (BSPs) store a portion of the user's file and must have it readily available to be served to another Storage Provider if the user designates a new MSP. Unlike MSPs, BSPs do not need to make the data available for public retrieval. - -BSPs play a crucial role in the system as a fallback option if the trust with the MSP is broken. They ensure that the user can freely choose another MSP, with the assurance that their data is still available in the Storage Hub. - -BSPs do not compete for user adoption as they do not offer distinctive services to one another and are not chosen by the user. Instead, they are assigned by the system, with considerations to ensure an even distribution of data. - -The remaining portion of the user's payment for storing a file, after the MSP's share, is evenly distributed among the assigned BSPs. This incentivizes BSPs to maintain the data they've committed to storing. - -### Sign Up Process - -The sign up process for Storage Providers is a two-step process to avoid malicious users from predicting the randomness used to generate the unique ID of the Storage Provider. - -1. The first step is the request to sign up, where the user provides the necessary information to become a Storage Provider, commiting to that information, and the necessary deposit is held. -2. The second step is the confirmation of the sign-up, which must be done by the user (or a third-party) after enough time has passed to ensure that the randomness used to generate the unique ID was not predictable when the sign-up request (commitment) was made. - -This process exists because the unique ID of a Storage Provider is what determines if they can volunteer to store a new file of the system after a store request was made by a user, and if the randomness used to generate the unique ID was predictable, malicious users could generate multiple Storage Provider accounts with similar IDs and collude to store the same file, which would be detrimental to the system as it would allow file storage centralization, allowing censorship and data loss. - -### Sign Off Process - -The sign off process is simpler that the sign up process: the Storage Provider requests to sign off, and if it does not have any data currently in use (that means, no user file is currently being stored by this Storage Provider), the deposit is returned and the Storage Provider information is completely deleted from the system. This deletion is permanent, as the unique ID of the Storage Provider is generated using the runtime's randomness, and it is not possible to recreate it. - -### Capacity Management - -Storage Providers can change their capacity, increasing or decreasing it as they see fit. The new capacity has to be more than the minimum allowed by the runtime, more than the Storage Provider's used capacity, and the change is subject to a timelock to avoid spam attacks. The new deposit needed for the new capacity is calculated, and the user has to pay the difference if the new deposit is greater than the current deposit. If the new deposit is less than the current deposit, the held difference is returned to the user. This allows Storage Providers to adapt to the network's needs and their own infrastructure capabilities. - -## Extrinsics - -The Storage Providers pallet provides the following extrinsics, which are explained at a high level in this section. For detailed information on the parameters and usage of each extrinsic, refer to the documentation found in the code. - -### request_msp_sign_up - -The purpose of this extrinsic is to handle the sign-up process for a new Main Storage Provider. It performs several checks and updates the blockchain's storage accordingly. We have a two-step process for signing up as a Storage Provider to avoid malicious users from predicting the randomness used to generate the unique ID of the Storage Provider, and that's why we have a request and a confirmation extrinsic, as a commitment scheme. - -### request_bsp_sign_up - -The purpose of this extrinsic is to handle the sign-up process for a new Backup Storage Provider. It performs several checks and updates the blockchain's storage accordingly. This extrinsic is similar to the `request_msp_sign_up` extrinsic, but for Backup Storage Providers. - -### confirm_sign_up - -The purpose of this extrinsic is to allow users to confirm their sign up as a Storage Provider (be it a Main Storage Provider or a Backup Storage Provider) after the required time has passed to allow the runtime's randomness used for this process to not have been predictable when the sign up request was made. This extrinsic is only available for users that have a pending sign up request. - -Notes: - -- This extrinsic could be called by the user that requested the registration itself or by a third party in behalf of the user. -- Requests have an expiration because if that wasn't the case, malicious users could wait indefinitely for a random seed from the relay chain that suits their malicious purpose. -- The deposit that the user has to pay to register as a Storage Provider is held when the user requests to register as a Storage Provider, not in this extrinsic. -- If this extrinsic is successful, it will be free for the caller, to incentive state debloating of pending requests. - -### cancel_sign_up - -The purpose of this extrinsic is to allow users to cancel their sign up request that they previously initiated. This allows users to recover the deposit that was held when they requested to sign up as a Storage Provider, and it is a way to incentivize storage debloat as users will want to delete the sign up requests that are not going to be confirmed. This extrinsic is only available for users that have a pending sign up request. - -### msp_sign_off - -The purpose of this extrinsic is to allow Main Storage Providers that are not currently being used by any user to sign off (deregister) as a Storage Provider and recover their deposit. This extrinsic is only available for Main Storage Providers that have no user storage assigned to them (no data in use). We have this restriction to avoid data loss, as if a Main Storage Provider has data in use and signs off, the data would be lost. - -### bsp_sign_off - -The purpose of this extrinsic is to allow Backup Storage Providers that are not currently being used by any user to sign off (deregister) as a Storage Provider and recover their deposit. This extrinsic is only available for Backup Storage Providers that have no user storage assigned to them (no data in use). The logic is the same as the `msp_sign_off` extrinsic, but for Backup Storage Providers. - -### change_capacity - -The purpose of this extrinsic is to allow Storage Providers (Main or Backup) to change their "contracted" capacity, increasing or decreasing it as they see fit. The new capacity has to be more than the minimum allowed by the runtime, more than the Storage Provider's used capacity and the change is subject to a timelock to avoid spam attacks. This extrinsic is available for all registered Storage Providers. - -### add_value_prop - -The purpose of this extrinsic is to allow Main Storage Providers to add new value propositions to their offerings. This allows them to offer service tiers to their users, with different fee structures and features. - -## Interfaces - -This pallet implements the following interfaces: - -- `MutateProvidersInterface` -- `ReadProvidersInterface` -- `ProvidersInterface` - -These are further explained in their own documentation. - -## Storage - -The Storage Providers pallet uses the following storage items for managing the state of the network: - -### `SignUpRequests` - -This storage holds the sign up requests initiated by users of StorageHub that want to offer their services as Storage Providers, both Main and Backup. - -It's a map from an account ID to a tuple consisting of the Storage Provider metadata that the account used when requesting to sign up and the block number in which the request was initiated. - -```rust -AccountId -> (StorageProviderMetadata, BlockWhenSignUpWasRequested) -``` - -### `AccountIdToMainStorageProviderId` - -This storage is used to keep track of the one-to-one relationship between an account ID and a Main Storage Provider ID, which is used to choose which challenges are requested from that Storage Provider. - -It's a map from an account ID to a Main Storage Provider ID, which is of the Hash type from the runtime. - -```rust -AccountId -> MainStorageProviderId -``` - -### `MainStorageProviders` - -This storage holds the metadata of each registered Main Storage Provider, including its corresponding buckets, its capacity, used data, the valid multiaddresses to connect to it, its list of value propositions and the block in which this Storage Provider last changed its capacity. - -It's a map from a Main Storage Provider ID to its metadata. - -```rust -MainStorageProviderId -> MainStorageProviderMetadata -``` - -### `Buckets` - -This storage holds the metadata of each bucket that exists in each Main Storage Provider. It holds the bucket's root, the user ID that owns that bucket and the Main Storage Provider ID that holds that bucket. It is updated using the `MutateProvidersInterface`. - -It's a map from a bucket ID to that bucket's metadata - -```rust -BucketId -> Bucket -``` - -### `AccountIdToBackupStorageProviderId` - -This storage is used to keep track of the one-to-one relationship between an account ID and a Backup Storage Provider ID, which is used to both choose which challenges are requested from that Storage Provider and to compare with the threshold used to allow Backup Storage Providers to offer themselves to store a new file of the system. - -It's a map from an account ID to a Backup Storage Provider ID, which is of the Hash type from the runtime. - -```rust -AccountId -> BackupStorageProviderId -``` - -### `BackupStorageProviders` - -This storage holds the metadata of each registered Backup Storage Provider, which has its capacity, its used data, the valid multiaddresses to connect to it, its forest root and the block in which this Storage Provider last changed its capacity. - -It's a map from a Backup Storage Provider ID to its metadata. - -```rust -BackupStorageProviderId -> BackupStorageProviderMetadata -``` - -### `MspCount` - -This storage holds the amount of Main Storage Providers that are currently registered in the system. - -### `BspCount` - -This storage holds the amount of Backup Storage Providers that are currently registered in the system. - -### `TotalBspsCapacity` - -This storage holds the sum of all the capacity that has been registered by Backup Storage Providers, which corresponds to the capacity of the whole network. - -## Events - -The Storage Providers pallet emits the following events: - -### `MspRequestSignUpSuccess` - -This event is emitted when a Main Storage Provider has requested to sign up successfully. It provides information about that Main Storage Provider's account ID, the list of valid multiaddresses that it wants to register, the total capacity that it wants to register, and its list of value propositions. - -The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Main Storage Provider was successful and that the corresponding deposit was held. - -```rust -MspRequestSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageData, - value_prop: ValueProposition, -} -``` - -### `MspSignUpSuccess` - -This event is emitted when a Main Storage Provider has confirmed its requested sign up successfully. It provides information about that Main Storage Provider's account ID, the list of valid multiaddresses that it has registered, the total capacity that it has registered, and its list of value propositions. - -The nature of this event is to allow the newly registered Main Storage Provider to know that the confirmation of its request to sign up as a Main Storage Provider was successful and that from now on, the user is a Main Storage Provider and can start storing user data. It also allows users of the network to know that a new Main Storage Provider has joined it, which can be useful for them to choose which Main Storage Provider to use. - -```rust -MspSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageData, - value_prop: ValueProposition, -} -``` - -### `BspRequestSignUpSuccess` - -This event is emitted when a Backup Storage Provider has requested to sign up successfully. It provides information about that Backup Storage Provider's account ID, the list of valid multiaddresses that it wants to register and the total capacity that it wants to register. - -The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Backup Storage Provider was successful and that the corresponding deposit was held. - -```rust -BspRequestSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageData, -} -``` - -### `BspSignUpSuccess` - -This event is emitted when a Backup Storage Provider has confirmed its requested sign up successfully. It provides information about that Backup Storage Provider's account ID, the list of valid multiaddresses that it has registered and the total capacity that it has registered. - -The nature of this event is to allow the newly registered Backup Storage Provider to know that the confirmation of its request to sign up as a Backup Storage Provider was successful and that from now on, the user is a Backup Storage Provider and can start volunteering to store user data. It also allows Main Storage Providers to know that a new Backup Storage Provider has joined the network, which can be useful for them when they need to retrieve files from the network. - -```rust -BspSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageData, -} -``` - -### `SignUpRequestCanceled` - -This event is emitted when a Storage Provider has cancelled its request to sign up successfully and the previously held deposit has been returned to it. - -The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Storage Provider was cancelled successfully and that the corresponding deposit was returned. - -```rust -SignUpRequestCanceled { who: T::AccountId } -``` - -### `MspSignOffSuccess` - -This event is emitted when a Main Storage Provider has signed off of the system successfully and the previously held deposit has been returned to it. - -The nature of this event is to allow the caller of the extrinsic to know that the sign off as a Main Storage Provider was successful and that the corresponding deposit was returned. It also allows users of the network to know that this Main Storage Provider is no longer available as an option for storing their data. - -```rust -MspSignOffSuccess { who: T::AccountId } -``` - -### `BspSignOffSuccess` - -This event is emitted when a Backup Storage Provider has signed off of the system successfully and the previously held deposit has been returned to it. - -The nature of this event is to allow the caller of the extrinsic to know that the sign off as a Backup Storage Provider was successful and that the corresponding deposit was returned. It also allows Main Storage Providers to know that this Backup Storage Provider is no longer available as an option for retrieving data. - -```rust -BspSignOffSuccess { who: T::AccountId } -``` - -### `CapacityChanged` - -This event is emitted when a Storage Provider has successfully changed its registered capacity on the network. It holds the information about the account ID of that Storage Provider, its previous capacity, the new registered capacity and the next block after which the timelock expires and it is able to change its capacity again. - -The nature of this event is to allow the caller of the extrinsic to know that the change of capacity was successful and that the difference in deposit was held or returned. It also allows users of the network to know that this Storage Provider has changed its capacity, which can be useful for them to choose which Storage Provider to use. - -```rust -CapacityChanged { - who: T::AccountId, - old_capacity: StorageData, - new_capacity: StorageData, - next_block_when_change_allowed: BlockNumberFor, -} -``` - -## Errors - -The Storage Providers pallet uses the following error types: - -### `AlreadyRegistered` - -Error thrown when a user tries to sign up as a Storage Provider but is already registered as either a Main Storage Provider or Backup Storage Provider. - -### `MaxBspsReached` - -Error thrown when a user tries to sign up as a Backup Storage Provider but the maximum amount of Backup Storage Providers has been reached. - -### `MaxMspsReached` - -Error thrown when a user tries to sign up as a Main Storage Provider but the maximum amount of Main Storage Providers has been reached. - -### `SignUpNotRequested` - -Error thrown when a user tries to confirm a sign up that was not requested previously. - -### `SignUpRequestPending` - -Error thrown when a user tries to request to sign up when it already has a sign up request pending. - -### `NoMultiAddress` - -Error thrown when a user tries to sign up providing an empty list of multiaddresses. - -### `InvalidMultiAddress` - -Error thrown when a user tries to sign up as a Storage Provider but any of its provided multiaddresses is invalid. - -### `StorageTooLow` - -Error thrown when a user tries to sign up or change its capacity to a value smaller than the minimum required by the runtime. - -### `NotEnoughBalance` - -Error thrown when a user does not have enough balance to pay the deposit that it would incur by signing up as a Storage Provider or changing its capacity to one that entails a bigger deposit. - -### `CannotHoldDeposit` - -Error thrown when the runtime cannot hold the required deposit from the account to register it as a Storage Provider or change its capacity. - -### `StorageStillInUse` - -Error thrown when a user tries to sign off as a Storage Provider but still has storage that's not free. - -### `RandomnessNotValidYet` - -Error thrown when a user tries to confirm a sign up but the available randomness from the runtime could have still been predicted by the user that requested the sign up. - -### `SignUpRequestExpired` - -Error thrown when a user tries to confirm a sign up but too much time has passed since it initially requested to sign up. - -### `NewCapacityLessThanUsedStorage` - -Error thrown when a user tries to change its capacity to less than the capacity that is has used. - -### `NewCapacityEqualsCurrentCapacity` - -Error thrown when a user tries to change its capacity to the same value it already has. - -### `NewCapacityCantBeZero` - -Error thrown when a user tries to change its capacity to zero (there are specific extrinsics to sign off as a Storage Provider). - -### `NotEnoughTimePassed` - -Error thrown when a Storage Provider tries to change its capacity but it has not been enough time since the last time it changed it, so the timelock is still active. - -### `NotRegistered` - -Error thrown when a user tries to interact as a Storage Provider with this pallet but it is not registered as either a Main Storage Provider or a Backup Storage Provider. - -### `NoUserId` - -Error thrown when trying to get the root of a bucket that belongs to a Main Storage Provider without passing a user ID needed to identify that bucket. - -### `NoBucketId` - -Error thrown when trying to get a root from a Main Storage Provider without passing the bucket ID of the bucket that the root should belong to. - -### `SpRegisteredButDataNotFound` - -Error thrown when a user has a Storage Provider ID assigned to it but its metadata data does not exist in storage (storage inconsistency error, should never happen). - -## Slashing Protocol - -Storage Providers who fail to submit a proof by their challenge deadline _will_ be slashed. In the case of the StorageHub protocol, this is predefined in the proofs-dealer pallet. - -Slashing is an asynchronous process, therefore it is possible for a Storage Provider to have failed more than one challenge before being slashed. This pallet requires an external data source to fetch the number of failed proof submissions. In the case of the StorageHub protocol, the proofs-dealer pallet would keep track of this data and expose an interface for this pallet to access it. This avoids the possibility of a Storage Provider not being slashed for all their failed challenges. Slashing a Storage Provider takes into account the aforementioned total number of failed challenges since the Provider's last slash and multiply it by a configurable slash factor. This factor is a value that is associated to the punishment for a single lost file. - -### Manual and Automatic Slashing - -The `slash` extrinsic can be called by any account to manually slash any Storage Provider that has been marked as slashable, and only requires the Storage Provider ID of a Storage Provider to be slashed. - -An automated slashing mechanism is implemented in an off-chain worker process to be executed by collators which efficiently slashes many Storage Providers. - -### Grace Period and Insolvency - -Since the Storage Provider's stake determines their total storage capacity, it is entirely possible that the amount of data currently stored would be above their total storage capacity after slashing. StorageHub grants a predetermined configurable grace period for Storage Providers to top-up their stake to have their total capacity equal to or greater than the amount of data they currently store. If the grace period has been reached, they are considered to be insolvent and cease to be a Storage Provider. - -The grace period is based on the total stake/capacity of the Storage Provider. In essence, the more stake a Storage Provider has, the longer the grace period. -This is to avoid a high stake Storage Provider from being removed from the network prematurely. - -The runtime automatically processes any expired grace periods within the `on_poll` hook to ensure that the redundancy process is initiated as soon as possible. For every insolvent Storage Provider, an event is emitted to notify the network and also mark the Storage Provider as insolvent, rendering them unable to operate as a Storage Provider. Finally all the of the Storage Provider's stake is slashed and transfered to the treasury. - -### Ensuring Data Redundancy - -> [!IMPORTANT] -> The runtime cannot ensure that all the data stored from an insolvent Storage Provider would be recovered. Given that the runtime has no knowledge of file keys stored by whom, it is up to external services to expose information of the files that are lost so that new storage requests can be initiated. Such external services can be indexer services or MSPs which are expected to run an indexer as well. - -In the event where a BSP loses all of their held file data, to avoid being slashed at every challenge interval, the BSP should request to stop storing every file key they currently have in their forest which they can no -longer respond to proofs for. - -#### Incentives and Storage Cleanup - -For every file key submitted for redundancy which was stored by an insolvent Storage Provider, the caller is rewarded with a configurable amount of tokens which must be less than the slash factor to prevent abuse. - -The runtime will automatically remove the file keys from the insolvent provider's forest. Once the forest root becomes an empty root, the Storage Provider is removed from storage. - -This process ensures that total redundancy is regained before the insolvent Storage Provider is removed from the network. +# Storage Providers Pallet + +## Overview + +The Storage Providers pallet is designed for Substrate-based blockchains, providing a robust framework for managing storage providers within a decentralized network. This pallet allows for the registration and management of Main Storage Providers (MSPs) and Backup Storage Providers (BSPs). +It is designed to be flexible and extensible, allowing developers to integrate it with decentralized storage solutions in their blockchain networks with ease. It provides essential services for managing the roles and capabilities of storage providers within the ecosystem, ensuring transparency, security, and operational integrity. + +### Features + +- Provider Management: Enables the sign-up of MSPs and BSPs, allowing them to register and become part of the network's storage solution. +- Capacity and Value Proposition Management: Providers can modify their storage capacities and update their value propositions to reflect their current offerings and capabilities. +- Lifecycle Events: Tracks significant lifecycle events like sign-up requests, confirmations, and sign-offs through a series of emitted events. +- Robust Access Controls: Ensures that only authorized operations are performed by authorized storage providers. +- Error Handling: Provides detailed error messages to help developers understand and resolve issues during storage provider management operations. + +### Target Audience + +This pallet is intended for blockchain developers interested in integrating decentralized storage solutions into their Substrate-based blockchain. It provides essential services for managing the roles and capabilities of storage providers within the ecosystem. It was developed with StorageHub's specific providers framework but can be used by any other network that fits the Main Storage Provider/Backup Storage Provider structure, with some modifications. + +## Design + +### Main Storage Providers (MSPs) + +A Main Storage Provider (MSP) is responsible for storing the complete file provided by the user and making it readily available for off-chain retrieval. MSPs are expected to compete for user adoption, and a certain degree of centralization is anticipated. They are considered trusted parties for data retrieval. + +MSPs are compensated for their services by the user. The amount is agreed upon when the user selects the MSP, and it's typically higher than what a Backup Storage Provider receives due to the infrastructure required for convenient data retrieval. + +MSPs are required to regularly prove that they continue to store the data they've committed to. This ensures the integrity and availability of the data in the system. + +### Backup Storage Providers (BSPs) + +Backup Storage Providers (BSPs) store a portion of the user's file and must have it readily available to be served to another Storage Provider if the user designates a new MSP. Unlike MSPs, BSPs do not need to make the data available for public retrieval. + +BSPs play a crucial role in the system as a fallback option if the trust with the MSP is broken. They ensure that the user can freely choose another MSP, with the assurance that their data is still available in the Storage Hub. + +BSPs do not compete for user adoption as they do not offer distinctive services to one another and are not chosen by the user. Instead, they are assigned by the system, with considerations to ensure an even distribution of data. + +The remaining portion of the user's payment for storing a file, after the MSP's share, is evenly distributed among the assigned BSPs. This incentivizes BSPs to maintain the data they've committed to storing. + +### Sign Up Process + +The sign up process for Storage Providers is a two-step process to avoid malicious users from predicting the randomness used to generate the unique ID of the Storage Provider. + +1. The first step is the request to sign up, where the user provides the necessary information to become a Storage Provider, commiting to that information, and the necessary deposit is held. +2. The second step is the confirmation of the sign-up, which must be done by the user (or a third-party) after enough time has passed to ensure that the randomness used to generate the unique ID was not predictable when the sign-up request (commitment) was made. + +This process exists because the unique ID of a Storage Provider is what determines if they can volunteer to store a new file of the system after a store request was made by a user, and if the randomness used to generate the unique ID was predictable, malicious users could generate multiple Storage Provider accounts with similar IDs and collude to store the same file, which would be detrimental to the system as it would allow file storage centralization, allowing censorship and data loss. + +### Sign Off Process + +The sign off process is simpler that the sign up process: the Storage Provider requests to sign off, and if it does not have any data currently in use (that means, no user file is currently being stored by this Storage Provider), the deposit is returned and the Storage Provider information is completely deleted from the system. This deletion is permanent, as the unique ID of the Storage Provider is generated using the runtime's randomness, and it is not possible to recreate it. + +### Capacity Management + +Storage Providers can change their capacity, increasing or decreasing it as they see fit. The new capacity has to be more than the minimum allowed by the runtime, more than the Storage Provider's used capacity, and the change is subject to a timelock to avoid spam attacks. The new deposit needed for the new capacity is calculated, and the user has to pay the difference if the new deposit is greater than the current deposit. If the new deposit is less than the current deposit, the held difference is returned to the user. This allows Storage Providers to adapt to the network's needs and their own infrastructure capabilities. + +## Extrinsics + +The Storage Providers pallet provides the following extrinsics, which are explained at a high level in this section. For detailed information on the parameters and usage of each extrinsic, refer to the documentation found in the code. + +### request_msp_sign_up + +The purpose of this extrinsic is to handle the sign-up process for a new Main Storage Provider. It performs several checks and updates the blockchain's storage accordingly. We have a two-step process for signing up as a Storage Provider to avoid malicious users from predicting the randomness used to generate the unique ID of the Storage Provider, and that's why we have a request and a confirmation extrinsic, as a commitment scheme. + +### request_bsp_sign_up + +The purpose of this extrinsic is to handle the sign-up process for a new Backup Storage Provider. It performs several checks and updates the blockchain's storage accordingly. This extrinsic is similar to the `request_msp_sign_up` extrinsic, but for Backup Storage Providers. + +### confirm_sign_up + +The purpose of this extrinsic is to allow users to confirm their sign up as a Storage Provider (be it a Main Storage Provider or a Backup Storage Provider) after the required time has passed to allow the runtime's randomness used for this process to not have been predictable when the sign up request was made. This extrinsic is only available for users that have a pending sign up request. + +Notes: + +- This extrinsic could be called by the user that requested the registration itself or by a third party in behalf of the user. +- Requests have an expiration because if that wasn't the case, malicious users could wait indefinitely for a random seed from the relay chain that suits their malicious purpose. +- The deposit that the user has to pay to register as a Storage Provider is held when the user requests to register as a Storage Provider, not in this extrinsic. +- If this extrinsic is successful, it will be free for the caller, to incentive state debloating of pending requests. + +### cancel_sign_up + +The purpose of this extrinsic is to allow users to cancel their sign up request that they previously initiated. This allows users to recover the deposit that was held when they requested to sign up as a Storage Provider, and it is a way to incentivize storage debloat as users will want to delete the sign up requests that are not going to be confirmed. This extrinsic is only available for users that have a pending sign up request. + +### msp_sign_off + +The purpose of this extrinsic is to allow Main Storage Providers that are not currently being used by any user to sign off (deregister) as a Storage Provider and recover their deposit. This extrinsic is only available for Main Storage Providers that have no user storage assigned to them (no data in use). We have this restriction to avoid data loss, as if a Main Storage Provider has data in use and signs off, the data would be lost. + +### bsp_sign_off + +The purpose of this extrinsic is to allow Backup Storage Providers that are not currently being used by any user to sign off (deregister) as a Storage Provider and recover their deposit. This extrinsic is only available for Backup Storage Providers that have no user storage assigned to them (no data in use). The logic is the same as the `msp_sign_off` extrinsic, but for Backup Storage Providers. + +### change_capacity + +The purpose of this extrinsic is to allow Storage Providers (Main or Backup) to change their "contracted" capacity, increasing or decreasing it as they see fit. The new capacity has to be more than the minimum allowed by the runtime, more than the Storage Provider's used capacity and the change is subject to a timelock to avoid spam attacks. This extrinsic is available for all registered Storage Providers. + +### add_value_prop + +The purpose of this extrinsic is to allow Main Storage Providers to add new value propositions to their offerings. This allows them to offer service tiers to their users, with different fee structures and features. + +## Interfaces + +This pallet implements the following interfaces: + +- `MutateProvidersInterface` +- `ReadProvidersInterface` +- `ProvidersInterface` + +These are further explained in their own documentation. + +## Storage + +The Storage Providers pallet uses the following storage items for managing the state of the network: + +### `SignUpRequests` + +This storage holds the sign up requests initiated by users of StorageHub that want to offer their services as Storage Providers, both Main and Backup. + +It's a map from an account ID to a tuple consisting of the Storage Provider metadata that the account used when requesting to sign up and the block number in which the request was initiated. + +```rust +AccountId -> (StorageProviderMetadata, BlockWhenSignUpWasRequested) +``` + +### `AccountIdToMainStorageProviderId` + +This storage is used to keep track of the one-to-one relationship between an account ID and a Main Storage Provider ID, which is used to choose which challenges are requested from that Storage Provider. + +It's a map from an account ID to a Main Storage Provider ID, which is of the Hash type from the runtime. + +```rust +AccountId -> MainStorageProviderId +``` + +### `MainStorageProviders` + +This storage holds the metadata of each registered Main Storage Provider, including its corresponding buckets, its capacity, used data, the valid multiaddresses to connect to it, its list of value propositions and the block in which this Storage Provider last changed its capacity. + +It's a map from a Main Storage Provider ID to its metadata. + +```rust +MainStorageProviderId -> MainStorageProviderMetadata +``` + +### `Buckets` + +This storage holds the metadata of each bucket that exists in each Main Storage Provider. It holds the bucket's root, the user ID that owns that bucket and the Main Storage Provider ID that holds that bucket. It is updated using the `MutateProvidersInterface`. + +It's a map from a bucket ID to that bucket's metadata + +```rust +BucketId -> Bucket +``` + +### `AccountIdToBackupStorageProviderId` + +This storage is used to keep track of the one-to-one relationship between an account ID and a Backup Storage Provider ID, which is used to both choose which challenges are requested from that Storage Provider and to compare with the threshold used to allow Backup Storage Providers to offer themselves to store a new file of the system. + +It's a map from an account ID to a Backup Storage Provider ID, which is of the Hash type from the runtime. + +```rust +AccountId -> BackupStorageProviderId +``` + +### `BackupStorageProviders` + +This storage holds the metadata of each registered Backup Storage Provider, which has its capacity, its used data, the valid multiaddresses to connect to it, its forest root and the block in which this Storage Provider last changed its capacity. + +It's a map from a Backup Storage Provider ID to its metadata. + +```rust +BackupStorageProviderId -> BackupStorageProviderMetadata +``` + +### `MspCount` + +This storage holds the amount of Main Storage Providers that are currently registered in the system. + +### `BspCount` + +This storage holds the amount of Backup Storage Providers that are currently registered in the system. + +### `TotalBspsCapacity` + +This storage holds the sum of all the capacity that has been registered by Backup Storage Providers, which corresponds to the capacity of the whole network. + +## Events + +The Storage Providers pallet emits the following events: + +### `MspRequestSignUpSuccess` + +This event is emitted when a Main Storage Provider has requested to sign up successfully. It provides information about that Main Storage Provider's account ID, the list of valid multiaddresses that it wants to register, the total capacity that it wants to register, and its list of value propositions. + +The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Main Storage Provider was successful and that the corresponding deposit was held. + +```rust +MspRequestSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageData, + value_prop: ValueProposition, +} +``` + +### `MspSignUpSuccess` + +This event is emitted when a Main Storage Provider has confirmed its requested sign up successfully. It provides information about that Main Storage Provider's account ID, the list of valid multiaddresses that it has registered, the total capacity that it has registered, and its list of value propositions. + +The nature of this event is to allow the newly registered Main Storage Provider to know that the confirmation of its request to sign up as a Main Storage Provider was successful and that from now on, the user is a Main Storage Provider and can start storing user data. It also allows users of the network to know that a new Main Storage Provider has joined it, which can be useful for them to choose which Main Storage Provider to use. + +```rust +MspSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageData, + value_prop: ValueProposition, +} +``` + +### `BspRequestSignUpSuccess` + +This event is emitted when a Backup Storage Provider has requested to sign up successfully. It provides information about that Backup Storage Provider's account ID, the list of valid multiaddresses that it wants to register and the total capacity that it wants to register. + +The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Backup Storage Provider was successful and that the corresponding deposit was held. + +```rust +BspRequestSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageData, +} +``` + +### `BspSignUpSuccess` + +This event is emitted when a Backup Storage Provider has confirmed its requested sign up successfully. It provides information about that Backup Storage Provider's account ID, the list of valid multiaddresses that it has registered and the total capacity that it has registered. + +The nature of this event is to allow the newly registered Backup Storage Provider to know that the confirmation of its request to sign up as a Backup Storage Provider was successful and that from now on, the user is a Backup Storage Provider and can start volunteering to store user data. It also allows Main Storage Providers to know that a new Backup Storage Provider has joined the network, which can be useful for them when they need to retrieve files from the network. + +```rust +BspSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageData, +} +``` + +### `SignUpRequestCanceled` + +This event is emitted when a Storage Provider has cancelled its request to sign up successfully and the previously held deposit has been returned to it. + +The nature of this event is to allow the caller of the extrinsic to know that the request to sign up as a Storage Provider was cancelled successfully and that the corresponding deposit was returned. + +```rust +SignUpRequestCanceled { who: T::AccountId } +``` + +### `MspSignOffSuccess` + +This event is emitted when a Main Storage Provider has signed off of the system successfully and the previously held deposit has been returned to it. + +The nature of this event is to allow the caller of the extrinsic to know that the sign off as a Main Storage Provider was successful and that the corresponding deposit was returned. It also allows users of the network to know that this Main Storage Provider is no longer available as an option for storing their data. + +```rust +MspSignOffSuccess { who: T::AccountId } +``` + +### `BspSignOffSuccess` + +This event is emitted when a Backup Storage Provider has signed off of the system successfully and the previously held deposit has been returned to it. + +The nature of this event is to allow the caller of the extrinsic to know that the sign off as a Backup Storage Provider was successful and that the corresponding deposit was returned. It also allows Main Storage Providers to know that this Backup Storage Provider is no longer available as an option for retrieving data. + +```rust +BspSignOffSuccess { who: T::AccountId } +``` + +### `CapacityChanged` + +This event is emitted when a Storage Provider has successfully changed its registered capacity on the network. It holds the information about the account ID of that Storage Provider, its previous capacity, the new registered capacity and the next block after which the timelock expires and it is able to change its capacity again. + +The nature of this event is to allow the caller of the extrinsic to know that the change of capacity was successful and that the difference in deposit was held or returned. It also allows users of the network to know that this Storage Provider has changed its capacity, which can be useful for them to choose which Storage Provider to use. + +```rust +CapacityChanged { + who: T::AccountId, + old_capacity: StorageData, + new_capacity: StorageData, + next_block_when_change_allowed: BlockNumberFor, +} +``` + +## Errors + +The Storage Providers pallet uses the following error types: + +### `AlreadyRegistered` + +Error thrown when a user tries to sign up as a Storage Provider but is already registered as either a Main Storage Provider or Backup Storage Provider. + +### `MaxBspsReached` + +Error thrown when a user tries to sign up as a Backup Storage Provider but the maximum amount of Backup Storage Providers has been reached. + +### `MaxMspsReached` + +Error thrown when a user tries to sign up as a Main Storage Provider but the maximum amount of Main Storage Providers has been reached. + +### `SignUpNotRequested` + +Error thrown when a user tries to confirm a sign up that was not requested previously. + +### `SignUpRequestPending` + +Error thrown when a user tries to request to sign up when it already has a sign up request pending. + +### `NoMultiAddress` + +Error thrown when a user tries to sign up providing an empty list of multiaddresses. + +### `InvalidMultiAddress` + +Error thrown when a user tries to sign up as a Storage Provider but any of its provided multiaddresses is invalid. + +### `StorageTooLow` + +Error thrown when a user tries to sign up or change its capacity to a value smaller than the minimum required by the runtime. + +### `NotEnoughBalance` + +Error thrown when a user does not have enough balance to pay the deposit that it would incur by signing up as a Storage Provider or changing its capacity to one that entails a bigger deposit. + +### `CannotHoldDeposit` + +Error thrown when the runtime cannot hold the required deposit from the account to register it as a Storage Provider or change its capacity. + +### `StorageStillInUse` + +Error thrown when a user tries to sign off as a Storage Provider but still has storage that's not free. + +### `RandomnessNotValidYet` + +Error thrown when a user tries to confirm a sign up but the available randomness from the runtime could have still been predicted by the user that requested the sign up. + +### `SignUpRequestExpired` + +Error thrown when a user tries to confirm a sign up but too much time has passed since it initially requested to sign up. + +### `NewCapacityLessThanUsedStorage` + +Error thrown when a user tries to change its capacity to less than the capacity that is has used. + +### `NewCapacityEqualsCurrentCapacity` + +Error thrown when a user tries to change its capacity to the same value it already has. + +### `NewCapacityCantBeZero` + +Error thrown when a user tries to change its capacity to zero (there are specific extrinsics to sign off as a Storage Provider). + +### `NotEnoughTimePassed` + +Error thrown when a Storage Provider tries to change its capacity but it has not been enough time since the last time it changed it, so the timelock is still active. + +### `NotRegistered` + +Error thrown when a user tries to interact as a Storage Provider with this pallet but it is not registered as either a Main Storage Provider or a Backup Storage Provider. + +### `NoUserId` + +Error thrown when trying to get the root of a bucket that belongs to a Main Storage Provider without passing a user ID needed to identify that bucket. + +### `NoBucketId` + +Error thrown when trying to get a root from a Main Storage Provider without passing the bucket ID of the bucket that the root should belong to. + +### `SpRegisteredButDataNotFound` + +Error thrown when a user has a Storage Provider ID assigned to it but its metadata data does not exist in storage (storage inconsistency error, should never happen). + +## Slashing Protocol + +Storage Providers who fail to submit a proof by their challenge deadline _will_ be slashed. In the case of the StorageHub protocol, this is predefined in the proofs-dealer pallet. + +Slashing is an asynchronous process, therefore it is possible for a Storage Provider to have failed more than one challenge before being slashed. This pallet requires an external data source to fetch the number of failed proof submissions. In the case of the StorageHub protocol, the proofs-dealer pallet would keep track of this data and expose an interface for this pallet to access it. This avoids the possibility of a Storage Provider not being slashed for all their failed challenges. Slashing a Storage Provider takes into account the aforementioned total number of failed challenges since the Provider's last slash and multiply it by a configurable slash factor. This factor is a value that is associated to the punishment for a single lost file. + +### Manual and Automatic Slashing + +The `slash` extrinsic can be called by any account to manually slash any Storage Provider that has been marked as slashable, and only requires the Storage Provider ID of a Storage Provider to be slashed. + +An automated slashing mechanism is implemented in an off-chain worker process to be executed by collators which efficiently slashes many Storage Providers. + +### Grace Period and Insolvency + +Since the Storage Provider's stake determines their total storage capacity, it is entirely possible that the amount of data currently stored would be above their total storage capacity after slashing. StorageHub grants a predetermined configurable grace period for Storage Providers to top-up their stake to have their total capacity equal to or greater than the amount of data they currently store. If the grace period has been reached, they are considered to be insolvent and cease to be a Storage Provider. + +The grace period is based on the total stake/capacity of the Storage Provider. In essence, the more stake a Storage Provider has, the longer the grace period. +This is to avoid a high stake Storage Provider from being removed from the network prematurely. + +The runtime automatically processes any expired grace periods within the `on_poll` hook to ensure that the redundancy process is initiated as soon as possible. For every insolvent Storage Provider, an event is emitted to notify the network and also mark the Storage Provider as insolvent, rendering them unable to operate as a Storage Provider. Finally all the of the Storage Provider's stake is slashed and transfered to the treasury. + +### Ensuring Data Redundancy + +> [!IMPORTANT] +> The runtime cannot ensure that all the data stored from an insolvent Storage Provider would be recovered. Given that the runtime has no knowledge of file keys stored by whom, it is up to external services to expose information of the files that are lost so that new storage requests can be initiated. Such external services can be indexer services or MSPs which are expected to run an indexer as well. + +In the event where a BSP loses all of their held file data, to avoid being slashed at every challenge interval, the BSP should request to stop storing every file key they currently have in their forest which they can no +longer respond to proofs for. + +#### Incentives and Storage Cleanup + +For every file key submitted for redundancy which was stored by an insolvent Storage Provider, the caller is rewarded with a configurable amount of tokens which must be less than the slash factor to prevent abuse. + +The runtime will automatically remove the file keys from the insolvent provider's forest. Once the forest root becomes an empty root, the Storage Provider is removed from storage. + +This process ensures that total redundancy is regained before the insolvent Storage Provider is removed from the network. diff --git a/pallets/providers/runtime-api/src/lib.rs b/pallets/providers/runtime-api/src/lib.rs index 88ed0e68e..a96dc6fac 100644 --- a/pallets/providers/runtime-api/src/lib.rs +++ b/pallets/providers/runtime-api/src/lib.rs @@ -1,65 +1,65 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Codec, Decode, Encode}; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; - -sp_api::decl_runtime_apis! { - #[api_version(1)] - pub trait StorageProvidersApi - where - BlockNumber: Codec, - BspId: Codec, - BspInfo: Codec, - AccountId: Codec, - ProviderId: Codec, - StorageProviderId: Codec, - StorageDataUnit: Codec, - Balance: Codec, - BucketId: Codec, - { - fn get_bsp_info(bsp_id: &BspId) -> Result; - fn get_storage_provider_id(who: &AccountId) -> Option; - fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result; - fn query_storage_provider_capacity(who: &ProviderId) -> Result; - fn query_available_storage_capacity(who: &ProviderId) -> Result; - fn query_earliest_change_capacity_block(who: &BspId) -> Result; - fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option; - fn get_slash_amount_per_max_file_size() -> Balance; - } -} - -/// Error type for the `get_bsp_info` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum GetBspInfoError { - BspNotRegistered, - InternalApiError, -} - -/// Error type for the `query_storage_provider_capacity` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryStorageProviderCapacityError { - ProviderNotRegistered, - InternalError, -} - -/// Error type for the `query_available_storage_capacity` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryAvailableStorageCapacityError { - ProviderNotRegistered, - InternalError, -} - -/// Error type for the `query_available_storage_capacity` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryEarliestChangeCapacityBlockError { - ProviderNotRegistered, - InternalError, -} - -/// Error type for the `query_msp_id_of_bucket_id` runtime API call. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum QueryMspIdOfBucketIdError { - BucketNotFound, - InternalError, -} +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Codec, Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; + +sp_api::decl_runtime_apis! { + #[api_version(1)] + pub trait StorageProvidersApi + where + BlockNumber: Codec, + BspId: Codec, + BspInfo: Codec, + AccountId: Codec, + ProviderId: Codec, + StorageProviderId: Codec, + StorageDataUnit: Codec, + Balance: Codec, + BucketId: Codec, + { + fn get_bsp_info(bsp_id: &BspId) -> Result; + fn get_storage_provider_id(who: &AccountId) -> Option; + fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result; + fn query_storage_provider_capacity(who: &ProviderId) -> Result; + fn query_available_storage_capacity(who: &ProviderId) -> Result; + fn query_earliest_change_capacity_block(who: &BspId) -> Result; + fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option; + fn get_slash_amount_per_max_file_size() -> Balance; + } +} + +/// Error type for the `get_bsp_info` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum GetBspInfoError { + BspNotRegistered, + InternalApiError, +} + +/// Error type for the `query_storage_provider_capacity` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryStorageProviderCapacityError { + ProviderNotRegistered, + InternalError, +} + +/// Error type for the `query_available_storage_capacity` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryAvailableStorageCapacityError { + ProviderNotRegistered, + InternalError, +} + +/// Error type for the `query_available_storage_capacity` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryEarliestChangeCapacityBlockError { + ProviderNotRegistered, + InternalError, +} + +/// Error type for the `query_msp_id_of_bucket_id` runtime API call. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum QueryMspIdOfBucketIdError { + BucketNotFound, + InternalError, +} diff --git a/pallets/providers/src/lib.rs b/pallets/providers/src/lib.rs index 9fd7038f7..4e7f86d7e 100644 --- a/pallets/providers/src/lib.rs +++ b/pallets/providers/src/lib.rs @@ -1,1091 +1,1091 @@ -//! # Storage Providers Pallet -//! -//! This pallet provides the functionality to manage Main Storage Providers (MSPs) -//! and Backup Storage Providers (BSPs) in a decentralized storage network. -//! -//! The functionality allows users to sign up and sign off as MSPs or BSPs and change -//! their parameters. This is the way that users can offer their storage capacity to -//! the network and get rewarded for it. -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod types; -mod utils; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -use frame_system::pallet_prelude::BlockNumberFor; -pub use pallet::*; -pub use scale_info::Type; -use types::{ - BackupStorageProvider, BackupStorageProviderId, BalanceOf, BucketId, HashId, - MainStorageProviderId, MerklePatriciaRoot, StorageDataUnit, StorageProvider, -}; - -#[frame_support::pallet] -pub mod pallet { - use super::types::*; - use codec::{FullCodec, HasCompact}; - use frame_support::traits::Randomness; - use frame_support::{ - dispatch::DispatchResultWithPostInfo, - pallet_prelude::*, - sp_runtime::traits::{ - AtLeast32BitUnsigned, CheckEqual, CheckedAdd, MaybeDisplay, One, Saturating, - SimpleBitOps, Zero, - }, - traits::{fungible::*, Incrementable}, - Blake2_128Concat, - }; - use frame_system::pallet_prelude::{BlockNumberFor, *}; - use scale_info::prelude::fmt::Debug; - use shp_traits::{FileMetadataInterface, PaymentStreamsInterface, ProofSubmittersInterface}; - use sp_runtime::traits::{Bounded, CheckedDiv}; - - /// 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 to access randomness to salt AccountIds and get the corresponding HashId - type ProvidersRandomness: Randomness, BlockNumberFor>; - - /// Trait that allows the pallet to update payment streams of its Providers and Users - type PaymentStreams: PaymentStreamsInterface< - Balance = Self::NativeBalance, - AccountId = Self::AccountId, - ProviderId = HashId, - Units = Self::StorageDataUnit, - >; - - /// Trait that allows the pallet to manage generic file metadatas - type FileMetadataManager: FileMetadataInterface< - AccountId = Self::AccountId, - StorageDataUnit = Self::StorageDataUnit, - >; - - /// Type to access the Balances pallet (using the fungible trait from frame_support) - type NativeBalance: Inspect - + Mutate - + hold::Inspect - // , Reason = Self::HoldReason> We will probably have to hold deposits - + hold::Mutate - + hold::Balanced - + freeze::Inspect - + freeze::Mutate; - - /// The overarching hold reason - type RuntimeHoldReason: From; - - /// Data type for the measurement of storage size - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Saturating - + CheckedDiv - + Zero - + Copy - + MaxEncodedLen - + HasCompact - + Into> - + Into; - - /// Type that represents the total number of registered Storage Providers. - type SpCount: Parameter - + Member - + MaybeSerializeDeserialize - + Ord - + AtLeast32BitUnsigned - + FullCodec - + Copy - + Default - + Debug - + scale_info::TypeInfo - + MaxEncodedLen; - - /// The type of the Merkle Patricia Root of the storage trie for BSPs and MSPs' buckets (a hash). - type MerklePatriciaRoot: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// The type of the identifier of the value proposition of a MSP (probably a hash of that value proposition) - type ValuePropId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// The type of the Bucket NFT Collection ID. - type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; - - /// The trait exposing data of which providers failed to respond to challenges for proofs of storage. - type ProvidersProofSubmitters: ProofSubmittersInterface< - ProviderId = HashId, - TickNumber = BlockNumberFor, - >; - - /// The type representing the reputation weight of a BSP. - type ReputationWeightType: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + Saturating - + Copy - + MaxEncodedLen - + HasCompact - + Zero - + One - + CheckedAdd - + Ord - + Bounded; - - /// The Treasury AccountId. - /// The account to which: - /// - The fees for submitting a challenge are transferred. - /// - The slashed funds are transferred. - #[pallet::constant] - type Treasury: Get; - - /// The minimum amount that an account has to deposit to become a storage provider. - #[pallet::constant] - type SpMinDeposit: Get>; - - /// The amount that a BSP receives as allocation of storage capacity when it deposits SpMinDeposit. - #[pallet::constant] - type SpMinCapacity: Get>; - - /// The slope of the collateral vs storage capacity curve. In other terms, how many tokens a Storage Provider should add as collateral to increase its storage capacity in one unit of StorageDataUnit. - #[pallet::constant] - type DepositPerData: Get>; - - /// The estimated maximum size of an unknown file. - /// - /// Used primarily to slash a Storage Provider when it fails to provide a chunk of data for an unknown file size. - #[pallet::constant] - type MaxFileSize: Get>; - - /// The maximum size of a multiaddress. - #[pallet::constant] - type MaxMultiAddressSize: Get; - - /// The maximum amount of multiaddresses that a Storage Provider can have. - #[pallet::constant] - type MaxMultiAddressAmount: Get; - - /// The maximum number of protocols the MSP can support (at least within the runtime). - #[pallet::constant] - type MaxProtocols: Get; - - /// The maximum amount of Buckets that a MSP can have. - #[pallet::constant] - type MaxBuckets: Get; - - /// The amount that an account has to deposit to create a bucket. - #[pallet::constant] - type BucketDeposit: Get>; - - /// Type that represents the byte limit of a bucket name. - #[pallet::constant] - type BucketNameLimit: Get; - - /// The maximum amount of blocks after which a sign up request expires so the randomness cannot be chosen - #[pallet::constant] - type MaxBlocksForRandomness: Get>; - - /// The minimum amount of blocks between capacity changes for a SP - #[pallet::constant] - type MinBlocksBetweenCapacityChanges: Get>; - - /// The default value of the root of the Merkle Patricia Trie of the runtime - #[pallet::constant] - type DefaultMerkleRoot: Get; - - /// The slash factor deducted from a Storage Provider's deposit for every single storage proof they fail to provide. - #[pallet::constant] - type SlashAmountPerMaxFileSize: Get>; - - /// Starting reputation weight for a newly registered BSP. - #[pallet::constant] - type StartingReputationWeight: Get; - } - - #[pallet::pallet] - pub struct Pallet(_); - - // Storage: - - /// The mapping from an AccountId that requested to sign up to a tuple of the metadata with type of the request, and the block - /// number when the request was made. - /// - /// This is used for the two-step process of registering: when a user requests to register as a SP (either MSP or BSP), - /// that request with the metadata and the deposit held is stored here. When the user confirms the sign up, the - /// request is removed from this storage and the user is registered as a SP. - /// - /// This storage is updated in: - /// - [request_msp_sign_up](crate::dispatchables::request_msp_sign_up) and [request_bsp_sign_up](crate::dispatchables::request_bsp_sign_up), which add a new entry to the map. - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up) and [cancel_sign_up](crate::dispatchables::cancel_sign_up), which remove an existing entry from the map. - #[pallet::storage] - pub type SignUpRequests = - StorageMap<_, Blake2_128Concat, T::AccountId, (StorageProvider, BlockNumberFor)>; - - /// The mapping from an AccountId to a MainStorageProviderId. - /// - /// This is used to get a Main Storage Provider's unique identifier needed to access its metadata. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Main Storage Provider. - /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which removes the corresponding entry from the map. - #[pallet::storage] - pub type AccountIdToMainStorageProviderId = - StorageMap<_, Blake2_128Concat, T::AccountId, MainStorageProviderId>; - - /// The mapping from a MainStorageProviderId to a MainStorageProvider. - /// - /// This is used to get a Main Storage Provider's metadata. - /// It returns `None` if the Main Storage Provider ID does not correspond to any registered Main Storage Provider. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Main Storage Provider. - /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which removes the corresponding entry from the map. - /// - [change_capacity](crate::dispatchables::change_capacity), which changes the entry's `capacity`. - /// - [add_value_prop](crate::dispatchables::add_value_prop), which appends a new value proposition to the entry's existing `value_prop` bounded vector. - #[pallet::storage] - pub type MainStorageProviders = - StorageMap<_, Blake2_128Concat, MainStorageProviderId, MainStorageProvider>; - - /// The mapping from a BucketId to that bucket's metadata. - /// - /// This is used to get a bucket's metadata, such as root, user ID, and MSP ID. - /// It returns `None` if the Bucket ID does not correspond to any registered bucket. - /// - /// This storage is updated in: - /// - [add_bucket](shp_traits::MutateProvidersInterface::add_bucket), which adds a new entry to the map. - /// - [change_root_bucket](shp_traits::MutateProvidersInterface::change_root_bucket), which changes the corresponding bucket's root. - /// - [remove_root_bucket](shp_traits::MutateProvidersInterface::remove_root_bucket), which removes the entry of the corresponding bucket. - #[pallet::storage] - pub type Buckets = StorageMap<_, Blake2_128Concat, BucketId, Bucket>; - - /// The mapping from a MainStorageProviderId to a vector of BucketIds. - /// - /// This is used to efficiently retrieve the list of buckets that a Main Storage Provider is currently storing. - /// - /// This storage is updated in: - /// - [add_bucket](shp_traits::MutateProvidersInterface::add_bucket) - /// - [remove_root_bucket](shp_traits::MutateProvidersInterface::remove_root_bucket) - #[pallet::storage] - pub type MainStorageProviderIdsToBuckets = StorageMap< - _, - Blake2_128Concat, - MainStorageProviderId, - BoundedVec, T::MaxBuckets>, - >; - - /// The mapping from an AccountId to a BackupStorageProviderId. - /// - /// This is used to get a Backup Storage Provider's unique identifier needed to access its metadata. - /// - /// This storage is updated in: - /// - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Backup Storage Provider. - /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which removes the corresponding entry from the map. - #[pallet::storage] - pub type AccountIdToBackupStorageProviderId = - StorageMap<_, Blake2_128Concat, T::AccountId, BackupStorageProviderId>; - - /// The mapping from a BackupStorageProviderId to a BackupStorageProvider. - /// - /// This is used to get a Backup Storage Provider's metadata. - /// It returns `None` if the Backup Storage Provider ID does not correspond to any registered Backup Storage Provider. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Backup Storage Provider. - /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which removes the corresponding entry from the map. - /// - [change_capacity](crate::dispatchables::change_capacity), which changes the entry's `capacity`. - #[pallet::storage] - pub type BackupStorageProviders = - StorageMap<_, Blake2_128Concat, BackupStorageProviderId, BackupStorageProvider>; - - /// The amount of Main Storage Providers that are currently registered in the runtime. - /// - /// This is used to keep track of the total amount of MSPs in the system. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds one to this storage if the account to confirm is a Main Storage Provider. - /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which subtracts one from this storage. - #[pallet::storage] - pub type MspCount = StorageValue<_, T::SpCount, ValueQuery>; - - /// The amount of Backup Storage Providers that are currently registered in the runtime. - /// - /// This is used to keep track of the total amount of BSPs in the system. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds one to this storage if the account to confirm is a Backup Storage Provider. - /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which subtracts one from this storage. - #[pallet::storage] - pub type BspCount = StorageValue<_, T::SpCount, ValueQuery>; - - /// The total amount of storage capacity all BSPs have. - /// - /// This is used to keep track of the total amount of storage capacity all BSPs have in the system, which is also the - /// total amount of storage capacity that can be used by users if we factor in the replication factor. - /// - /// This storage is updated in: - /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds the capacity of the registered Storage Provider to this storage if the account to confirm is a Backup Storage Provider. - /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which subtracts the capacity of the Backup Storage Provider to sign off from this storage. - #[pallet::storage] - pub type TotalBspsCapacity = StorageValue<_, StorageDataUnit, ValueQuery>; - - /// The total amount of storage capacity of BSPs that is currently in use. - /// - /// This is used to keep track of the total amount of storage capacity that is currently in use by users, which is useful for - /// system metrics and also to calculate the current price of storage. - #[pallet::storage] - pub type UsedBspsCapacity = StorageValue<_, StorageDataUnit, ValueQuery>; - - /// The total global reputation weight of all BSPs. - #[pallet::storage] - pub type GlobalBspsReputationWeight = StorageValue<_, ReputationWeightType, ValueQuery>; - - // Events & Errors: - - /// The events that can be emitted by this pallet - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Event emitted when a Main Storage Provider has requested to sign up successfully. Provides information about - /// that MSP's account id, its multiaddresses, the total data it can store according to its stake, and its value proposition. - MspRequestSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageDataUnit, - value_prop: ValueProposition, - }, - - /// Event emitted when a Main Storage Provider has confirmed its sign up successfully. Provides information about - /// that MSP's account id, the total data it can store according to its stake, its multiaddress, and its value proposition. - MspSignUpSuccess { - who: T::AccountId, - msp_id: MainStorageProviderId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageDataUnit, - value_prop: ValueProposition, - }, - - /// Event emitted when a Backup Storage Provider has requested to sign up successfully. Provides information about - /// that BSP's account id, its multiaddresses, and the total data it can store according to its stake. - BspRequestSignUpSuccess { - who: T::AccountId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageDataUnit, - }, - - /// Event emitted when a Backup Storage Provider has confirmed its sign up successfully. Provides information about - /// that BSP's account id, the total data it can store according to its stake, and its multiaddress. - BspSignUpSuccess { - who: T::AccountId, - bsp_id: BackupStorageProviderId, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - capacity: StorageDataUnit, - }, - - /// Event emitted when a sign up request has been canceled successfully. Provides information about - /// the account id of the user that canceled the request. - SignUpRequestCanceled { who: T::AccountId }, - - /// Event emitted when a Main Storage Provider has signed off successfully. Provides information about - /// that MSP's account id. - MspSignOffSuccess { - who: T::AccountId, - msp_id: MainStorageProviderId, - }, - - /// Event emitted when a Backup Storage Provider has signed off successfully. Provides information about - /// that BSP's account id. - BspSignOffSuccess { - who: T::AccountId, - bsp_id: BackupStorageProviderId, - }, - - /// Event emitted when a SP has changed its capacity successfully. Provides information about - /// that SP's account id, its old total data that could store, and the new total data. - CapacityChanged { - who: T::AccountId, - provider_id: StorageProviderId, - old_capacity: StorageDataUnit, - new_capacity: StorageDataUnit, - next_block_when_change_allowed: BlockNumberFor, - }, - - /// Event emitted when an SP has been slashed. - Slashed { - provider_id: HashId, - amount_slashed: BalanceOf, - }, - } - - /// The errors that can be thrown by this pallet to inform users about what went wrong - #[pallet::error] - pub enum Error { - // Sign up errors: - /// Error thrown when a user tries to sign up as a SP but is already registered as a MSP or BSP. - AlreadyRegistered, - /// Error thrown when a user tries to confirm a sign up that was not requested previously. - SignUpNotRequested, - /// Error thrown when a user tries to request to sign up when it already has a sign up request pending. - SignUpRequestPending, - /// Error thrown when a user tries to sign up without any multiaddress. - NoMultiAddress, - /// Error thrown when a user tries to sign up as a SP but any of the provided multiaddresses is invalid. - InvalidMultiAddress, - /// Error thrown when a user tries to sign up or change its capacity to store less storage than the minimum required by the runtime. - StorageTooLow, - - // Deposit errors: - /// Error thrown when a user does not have enough balance to pay the deposit that it would incur by signing up as a SP or changing its capacity. - NotEnoughBalance, - /// Error thrown when the runtime cannot hold the required deposit from the account to register it as a SP or change its capacity. - CannotHoldDeposit, - - // Sign off errors: - /// Error thrown when a user tries to sign off as a SP but still has used storage. - StorageStillInUse, - - // Randomness errors: - /// Error thrown when a user tries to confirm a sign up but the randomness is too fresh to be used yet. - RandomnessNotValidYet, - /// Error thrown when a user tries to confirm a sign up but too much time has passed since the request. - SignUpRequestExpired, - - // Capacity change errors: - /// Error thrown when a user tries to change its capacity to less than its used storage. - NewCapacityLessThanUsedStorage, - /// Error thrown when a user tries to change its capacity to the same value it already has. - NewCapacityEqualsCurrentCapacity, - /// Error thrown when a user tries to change its capacity to zero (there are specific extrinsics to sign off as a SP). - NewCapacityCantBeZero, - /// Error thrown when a SP tries to change its capacity but it has not been enough time since the last time it changed it. - NotEnoughTimePassed, - /// Error thrown when a SP tries to change its capacity but the new capacity is not enough to store the used storage. - NewUsedCapacityExceedsStorageCapacity, - - // General errors: - /// Error thrown when a user tries to interact as a SP but is not registered as a MSP or BSP. - NotRegistered, - /// Error thrown when trying to get a root from a MSP without passing a User ID. - NoUserId, - /// Error thrown when trying to get a root from a MSP without passing a Bucket ID. - NoBucketId, - /// Error thrown when a user has a SP ID assigned to it but the SP data does not exist in storage (Inconsistency error). - SpRegisteredButDataNotFound, - /// Error thrown when a bucket ID is not found in storage. - BucketNotFound, - /// Error thrown when a bucket ID already exists in storage. - BucketAlreadyExists, - /// Error thrown when a bucket ID could not be added to the list of buckets of a MSP. - AppendBucketToMspFailed, - /// Error thrown when an attempt was made to slash an unslashable Storage Provider. - ProviderNotSlashable, - - // Payment streams interface errors: - /// Error thrown when failing to decode the metadata from a received trie value that was removed. - InvalidEncodedFileMetadata, - /// Error thrown when failing to decode the owner Account ID from the received metadata. - InvalidEncodedAccountId, - /// Error thrown when trying to update a payment stream that does not exist. - PaymentStreamNotFound, - } - - /// This enum holds the HoldReasons for this pallet, allowing the runtime to identify each held balance with different reasons separately - /// - /// This allows us to hold tokens and be able to identify in the future that those held tokens were - /// held because of this pallet - #[pallet::composite_enum] - pub enum HoldReason { - /// Deposit that a Storage Provider has to pay to be registered as such - StorageProviderDeposit, - /// Deposit that a user has to pay to create a bucket - BucketDeposit, - // Only for testing, another unrelated hold reason - #[cfg(test)] - AnotherUnrelatedHold, - } - - /// Dispatchables (extrinsics) exposed by this pallet - #[pallet::call] - impl Pallet { - /// Dispatchable extrinsic that allows users to request to sign up as a Main Storage Provider. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to sign up as a Main Storage Provider. - /// - /// Funds proportional to the capacity requested are reserved (held) from the account. - /// - /// Parameters: - /// - `capacity`: The total amount of data that the Main Storage Provider will be able to store. - /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the - /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) - /// - `value_prop`: The value proposition that the signer will provide as a Main Storage Provider to - /// users and wants to register on-chain. It could be data limits, communication protocols to access the user's - /// data, and more. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is not already registered as either a MSP or BSP - /// 3. Check that the multiaddress is valid - /// 4. Check that the data to be stored is greater than the minimum required by the runtime. - /// 5. Calculate how much deposit will the signer have to pay using the amount of data it wants to store - /// 6. Check that the signer has enough funds to pay the deposit - /// 7. Hold the deposit from the signer - /// 8. Update the Sign Up Requests storage to add the signer as requesting to sign up as a MSP - /// - /// Emits `MspRequestSignUpSuccess` event when successful. - #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn request_msp_sign_up( - origin: OriginFor, - capacity: StorageDataUnit, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - value_prop: ValueProposition, - payment_account: T::AccountId, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Set up a structure with the information of the new MSP - let msp_info = MainStorageProvider { - buckets: BoundedVec::default(), - capacity, - capacity_used: StorageDataUnit::::default(), - multiaddresses: multiaddresses.clone(), - value_prop: value_prop.clone(), - last_capacity_change: frame_system::Pallet::::block_number(), - owner_account: who.clone(), - payment_account, - }; - - // Sign up the new MSP (if possible), updating storage - Self::do_request_msp_sign_up(&msp_info)?; - - // Emit the corresponding event - Self::deposit_event(Event::::MspRequestSignUpSuccess { - who, - multiaddresses, - capacity, - value_prop, - }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic that allows users to sign up as a Backup Storage Provider. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to sign up as a Backup Storage Provider. - /// - /// Funds proportional to the capacity requested are reserved (held) from the account. - /// - /// Parameters: - /// - `capacity`: The total amount of data that the Backup Storage Provider will be able to store. - /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the - /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is not already registered as either a MSP or BSP - /// 3. Check that the multiaddress is valid - /// 4. Check that the data to be stored is greater than the minimum required by the runtime - /// 5. Calculate how much deposit will the signer have to pay using the amount of data it wants to store - /// 6. Check that the signer has enough funds to pay the deposit - /// 7. Hold the deposit from the signer - /// 8. Update the Sign Up Requests storage to add the signer as requesting to sign up as a BSP - /// - /// Emits `BspRequestSignUpSuccess` event when successful. - #[pallet::call_index(1)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn request_bsp_sign_up( - origin: OriginFor, - capacity: StorageDataUnit, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - payment_account: T::AccountId, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Set up a structure with the information of the new BSP - let bsp_info = BackupStorageProvider { - capacity, - capacity_used: StorageDataUnit::::default(), - multiaddresses: multiaddresses.clone(), - root: T::DefaultMerkleRoot::get(), - last_capacity_change: frame_system::Pallet::::block_number(), - owner_account: who.clone(), - payment_account, - reputation_weight: T::StartingReputationWeight::get(), - }; - - // Sign up the new BSP (if possible), updating storage - Self::do_request_bsp_sign_up(&bsp_info)?; - - // Emit the corresponding event - Self::deposit_event(Event::::BspRequestSignUpSuccess { - who, - multiaddresses, - capacity, - }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic that allows users to confirm their sign up as a Storage Provider, either MSP or BSP. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that requested to sign up as a Storage Provider, except when providing a - /// `provider_account` parameter, in which case the origin can be any account. - /// - /// Parameters: - /// - `provider_account`: The account that requested to sign up as a Storage Provider. If not provided, the signer - /// will be considered the account that requested to sign up. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed - /// 2. Check that the account received has requested to register as a SP - /// 3. Check that the current randomness is sufficiently fresh to be used as a salt for that request - /// 4. Check that the request has not expired - /// 5. Register the signer as a MSP or BSP with the data provided in the request - /// - /// Emits `MspSignUpSuccess` or `BspSignUpSuccess` event when successful, depending on the type of sign up. - /// - /// Notes: - /// - This extrinsic could be called by the user itself or by a third party - /// - The deposit that the user has to pay to register as a SP is held when the user requests to register as a SP - /// - If this extrinsic is successful, it will be free for the caller, to incentive state debloating - #[pallet::call_index(2)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn confirm_sign_up( - origin: OriginFor, - provider_account: Option, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer - let who = ensure_signed(origin)?; - - // Execute checks and logic, update storage and emit event - // We emit the event in the interior logic to not have to check again which type of sign up it is outside of it - match provider_account { - Some(provider_account) => Self::do_confirm_sign_up(&provider_account)?, - None => Self::do_confirm_sign_up(&who)?, - } - - // Return a successful DispatchResultWithPostInfo. If the extrinsic executed correctly, it will be free for the caller - Ok(Pays::No.into()) - } - - /// Dispatchable extrinsic that allows a user with a pending Sign Up Request to cancel it, getting the deposit back. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that requested to sign up as a Storage Provider. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer has requested to sign up as a SP - /// 3. Delete the request from the Sign Up Requests storage - /// 4. Return the deposit to the signer - /// - /// Emits `SignUpRequestCanceled` event when successful. - #[pallet::call_index(3)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn cancel_sign_up(origin: OriginFor) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Execute checks and logic, update storage - Self::do_cancel_sign_up(&who)?; - - // Emit the corresponding event - Self::deposit_event(Event::::SignUpRequestCanceled { who }); - - Ok(().into()) - } - - /// Dispatchable extrinsic that allows users to sign off as a Main Storage Provider. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to sign off as a Main Storage Provider. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is registered as a MSP - /// 3. Check that the MSP has no storage assigned to it (no buckets or data used by it) - /// 4. Update the MSPs storage, removing the signer as an MSP - /// 5. Return the deposit to the signer - /// 6. Decrement the storage that holds total amount of MSPs currently in the system - /// - /// Emits `MspSignOffSuccess` event when successful. - #[pallet::call_index(4)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn msp_sign_off(origin: OriginFor) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Execute checks and logic, update storage - let msp_id = Self::do_msp_sign_off(&who)?; - - // Emit the corresponding event - Self::deposit_event(Event::::MspSignOffSuccess { who, msp_id }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic that allows users to sign off as a Backup Storage Provider. - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to sign off as a Backup Storage Provider. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is registered as a BSP - /// 3. Check that the BSP has no storage assigned to it - /// 4. Update the BSPs storage, removing the signer as an BSP - /// 5. Update the total capacity of all BSPs, removing the capacity of the signer - /// 6. Return the deposit to the signer - /// 7. Decrement the storage that holds total amount of BSPs currently in the system - /// - /// Emits `BspSignOffSuccess` event when successful. - #[pallet::call_index(5)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn bsp_sign_off(origin: OriginFor) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Execute checks and logic, update storage - let bsp_id = Self::do_bsp_sign_off(&who)?; - - // Emit the corresponding event - Self::deposit_event(Event::::BspSignOffSuccess { who, bsp_id }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic that allows users to change their amount of stored data - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to change its capacity. - /// - /// Parameters: - /// - `new_capacity`: The new total amount of data that the Storage Provider wants to be able to store. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is registered as a SP - /// 3. Check that enough time has passed since the last time the SP changed its capacity - /// 4. Check that the new capacity is greater than the minimum required by the runtime - /// 5. Check that the new capacity is greater than the data used by this SP - /// 6. Calculate the new deposit needed for this new capacity - /// 7. Check to see if the new deposit needed is greater or less than the current deposit - /// a. If the new deposit is greater than the current deposit: - /// i. Check that the signer has enough funds to pay this extra deposit - /// ii. Hold the extra deposit from the signer - /// b. If the new deposit is less than the current deposit, return the held difference to the signer - /// 7. Update the SPs storage to change the total data - /// 8. If user is a BSP, update the total capacity of the network (sum of all capacities of BSPs) - /// - /// Emits `CapacityChanged` event when successful. - #[pallet::call_index(6)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn change_capacity( - origin: OriginFor, - new_capacity: StorageDataUnit, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was signed and get the signer. - let who = ensure_signed(origin)?; - - // Execute checks and logic, update storage - let (provider_id, old_capacity) = Self::do_change_capacity(&who, new_capacity)?; - - // Emit the corresponding event - Self::deposit_event(Event::::CapacityChanged { - who, - provider_id, - old_capacity, - new_capacity, - next_block_when_change_allowed: frame_system::Pallet::::block_number() - + T::MinBlocksBetweenCapacityChanges::get(), - }); - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic only callable by an MSP that allows it to add a value proposition to its service - /// - /// The dispatch origin for this call must be Signed. - /// The origin must be the account that wants to add a value proposition. - /// - /// Parameters: - /// - `new_value_prop`: The value proposition that the MSP wants to add to its service. - /// - /// This extrinsic will perform the following checks and logic: - /// 1. Check that the extrinsic was signed and get the signer. - /// 2. Check that the signer is registered as a MSP - /// 3. Check that the MSP has not reached the maximum amount of value propositions - /// 4. Check that the value proposition is valid (size and any other relevant checks) - /// 5. Update the MSPs storage to add the value proposition (with its identifier) - /// - /// Emits `ValuePropAdded` event when successful. - #[pallet::call_index(7)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn add_value_prop( - _origin: OriginFor, - _new_value_prop: ValueProposition, - ) -> DispatchResultWithPostInfo { - // TODO: implement this - - Ok(().into()) - } - - /// Dispatchable extrinsic that allows to forcefully and automatically sing up a Main Storage Provider. - /// - /// The dispatch origin for this call must be Root. - /// The `who` parameter is the account that wants to sign up as a Main Storage Provider. - /// - /// Funds proportional to the capacity requested are reserved (held) from the account passed as the `who` parameter. - /// - /// Parameters: - /// - `who`: The account that wants to sign up as a Main Storage Provider. - /// - `msp_id`: The Main Storage Provider ID that the account passed as the `who` parameter is requesting to sign up as. - /// - `capacity`: The total amount of data that the Main Storage Provider will be able to store. - /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the - /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) - /// - `value_prop`: The value proposition that the signer will provide as a Main Storage Provider to - /// users and wants to register on-chain. It could be data limits, communication protocols to access the user's - /// data, and more. - /// - /// This extrinsic will perform the steps of: - /// 1. [request_msp_sign_up](crate::dispatchables::request_msp_sign_up) - /// 2. [confirm_sign_up](crate::dispatchables::confirm_sign_up) - /// - /// Emits `MspRequestSignUpSuccess` and `MspSignUpSuccess` events when successful. - #[pallet::call_index(8)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn force_msp_sign_up( - origin: OriginFor, - who: T::AccountId, - msp_id: MainStorageProviderId, - capacity: StorageDataUnit, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - value_prop: ValueProposition, - payment_account: T::AccountId, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was sent with root origin. - ensure_root(origin)?; - - // Set up a structure with the information of the new MSP - let msp_info = MainStorageProvider { - buckets: BoundedVec::default(), - capacity, - capacity_used: StorageDataUnit::::default(), - multiaddresses: multiaddresses.clone(), - value_prop: value_prop.clone(), - last_capacity_change: frame_system::Pallet::::block_number(), - owner_account: who.clone(), - payment_account, - }; - - // Sign up the new MSP (if possible), updating storage - Self::do_request_msp_sign_up(&msp_info)?; - - // Emit the corresponding event - Self::deposit_event(Event::::MspRequestSignUpSuccess { - who: who.clone(), - multiaddresses, - capacity, - value_prop, - }); - - // Confirm the sign up of the account as a Main Storage Provider with the given ID - Self::do_msp_sign_up( - &who, - msp_id, - &msp_info, - frame_system::Pallet::::block_number(), - )?; - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic that allows to forcefully and automatically sing up a Backup Storage Provider. - /// - /// The dispatch origin for this call must be Root. - /// The `who` parameter is the account that wants to sign up as a Backup Storage Provider. - /// - /// Funds proportional to the capacity requested are reserved (held) from the account passed as the `who` parameter. - /// - /// Parameters: - /// - `who`: The account that wants to sign up as a Backup Storage Provider. - /// - `bsp_id`: The Backup Storage Provider ID that the account passed as the `who` parameter is requesting to sign up as. - /// - `capacity`: The total amount of data that the Backup Storage Provider will be able to store. - /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the - /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) - /// - /// This extrinsic will perform the steps of: - /// 1. [request_bsp_sign_up](crate::dispatchables::request_bsp_sign_up) - /// 2. [confirm_sign_up](crate::dispatchables::confirm_sign_up) - /// - /// Emits `BspRequestSignUpSuccess` and `BspSignUpSuccess` events when successful. - #[pallet::call_index(9)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn force_bsp_sign_up( - origin: OriginFor, - who: T::AccountId, - bsp_id: BackupStorageProviderId, - capacity: StorageDataUnit, - multiaddresses: BoundedVec, MaxMultiAddressAmount>, - payment_account: T::AccountId, - weight: Option>, - ) -> DispatchResultWithPostInfo { - // Check that the extrinsic was sent with root origin. - ensure_root(origin)?; - - // Set up a structure with the information of the new BSP - let bsp_info = BackupStorageProvider { - capacity, - capacity_used: StorageDataUnit::::default(), - multiaddresses: multiaddresses.clone(), - root: T::DefaultMerkleRoot::get(), - last_capacity_change: frame_system::Pallet::::block_number(), - owner_account: who.clone(), - payment_account, - reputation_weight: weight.unwrap_or(T::StartingReputationWeight::get()), - }; - - // Sign up the new BSP (if possible), updating storage - Self::do_request_bsp_sign_up(&bsp_info)?; - - // Emit the corresponding event - Self::deposit_event(Event::::BspRequestSignUpSuccess { - who: who.clone(), - multiaddresses, - capacity, - }); - - // Confirm the sign up of the account as a Backup Storage Provider with the given ID - Self::do_bsp_sign_up( - &who, - bsp_id, - &bsp_info, - frame_system::Pallet::::block_number(), - )?; - - // Return a successful DispatchResultWithPostInfo - Ok(().into()) - } - - /// Dispatchable extrinsic to slash a _slashable_ Storage Provider. - /// - /// A Storage Provider is _slashable_ iff it has failed to respond to challenges for providing proofs of storage. - /// In the context of the StorageHub protocol, the proofs-dealer pallet marks a Storage Provider as _slashable_ when it fails to respond to challenges. - #[pallet::call_index(10)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn slash(origin: OriginFor, provider_id: HashId) -> DispatchResultWithPostInfo { - // Check that the extrinsic was sent with root origin. - ensure_signed(origin)?; - - Self::do_slash(&provider_id) - } - } -} - -/// Helper functions (getters, setters, etc.) for this pallet -impl Pallet { - /// A helper function to get the information of a sign up request of a user. - pub fn get_sign_up_request( - who: &T::AccountId, - ) -> Result<(StorageProvider, BlockNumberFor), Error> { - SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested) - } - - /// A helper function to get the total capacity of a storage provider. - pub fn get_total_capacity_of_sp(who: &T::AccountId) -> Result, Error> { - if let Some(m_id) = AccountIdToMainStorageProviderId::::get(who) { - let msp = MainStorageProviders::::get(m_id).ok_or(Error::::NotRegistered)?; - Ok(msp.capacity) - } else if let Some(b_id) = AccountIdToBackupStorageProviderId::::get(who) { - let bsp = BackupStorageProviders::::get(b_id).ok_or(Error::::NotRegistered)?; - Ok(bsp.capacity) - } else { - Err(Error::::NotRegistered) - } - } - - /// A helper function to get the total capacity of all BSPs which is the total capacity of the network. - pub fn get_total_bsp_capacity() -> StorageDataUnit { - TotalBspsCapacity::::get() - } - - /// A helper function to get the total used capacity of all BSPs. - pub fn get_used_bsp_capacity() -> StorageDataUnit { - UsedBspsCapacity::::get() - } - - /// A helper function to get the total data used by a Main Storage Provider. - pub fn get_used_storage_of_msp( - who: &MainStorageProviderId, - ) -> Result, Error> { - let msp = MainStorageProviders::::get(who).ok_or(Error::::NotRegistered)?; - Ok(msp.capacity_used) - } - - /// A helper function to get the total data used by a Backup Storage Provider. - pub fn get_used_storage_of_bsp( - who: &BackupStorageProviderId, - ) -> Result, Error> { - let bsp = BackupStorageProviders::::get(who).ok_or(Error::::NotRegistered)?; - Ok(bsp.capacity_used) - } - - /// A helper function to get the total amount of Backup Storage Providers that have registered. - pub fn get_bsp_count() -> T::SpCount { - BspCount::::get() - } - - /// A helper function to get the total amount of Main Storage Providers that have registered. - pub fn get_msp_count() -> T::SpCount { - MspCount::::get() - } -} +//! # Storage Providers Pallet +//! +//! This pallet provides the functionality to manage Main Storage Providers (MSPs) +//! and Backup Storage Providers (BSPs) in a decentralized storage network. +//! +//! The functionality allows users to sign up and sign off as MSPs or BSPs and change +//! their parameters. This is the way that users can offer their storage capacity to +//! the network and get rewarded for it. +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod types; +mod utils; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +use frame_system::pallet_prelude::BlockNumberFor; +pub use pallet::*; +pub use scale_info::Type; +use types::{ + BackupStorageProvider, BackupStorageProviderId, BalanceOf, BucketId, HashId, + MainStorageProviderId, MerklePatriciaRoot, StorageDataUnit, StorageProvider, +}; + +#[frame_support::pallet] +pub mod pallet { + use super::types::*; + use codec::{FullCodec, HasCompact}; + use frame_support::traits::Randomness; + use frame_support::{ + dispatch::DispatchResultWithPostInfo, + pallet_prelude::*, + sp_runtime::traits::{ + AtLeast32BitUnsigned, CheckEqual, CheckedAdd, MaybeDisplay, One, Saturating, + SimpleBitOps, Zero, + }, + traits::{fungible::*, Incrementable}, + Blake2_128Concat, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + use scale_info::prelude::fmt::Debug; + use shp_traits::{FileMetadataInterface, PaymentStreamsInterface, ProofSubmittersInterface}; + use sp_runtime::traits::{Bounded, CheckedDiv}; + + /// 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 to access randomness to salt AccountIds and get the corresponding HashId + type ProvidersRandomness: Randomness, BlockNumberFor>; + + /// Trait that allows the pallet to update payment streams of its Providers and Users + type PaymentStreams: PaymentStreamsInterface< + Balance = Self::NativeBalance, + AccountId = Self::AccountId, + ProviderId = HashId, + Units = Self::StorageDataUnit, + >; + + /// Trait that allows the pallet to manage generic file metadatas + type FileMetadataManager: FileMetadataInterface< + AccountId = Self::AccountId, + StorageDataUnit = Self::StorageDataUnit, + >; + + /// Type to access the Balances pallet (using the fungible trait from frame_support) + type NativeBalance: Inspect + + Mutate + + hold::Inspect + // , Reason = Self::HoldReason> We will probably have to hold deposits + + hold::Mutate + + hold::Balanced + + freeze::Inspect + + freeze::Mutate; + + /// The overarching hold reason + type RuntimeHoldReason: From; + + /// Data type for the measurement of storage size + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Saturating + + CheckedDiv + + Zero + + Copy + + MaxEncodedLen + + HasCompact + + Into> + + Into; + + /// Type that represents the total number of registered Storage Providers. + type SpCount: Parameter + + Member + + MaybeSerializeDeserialize + + Ord + + AtLeast32BitUnsigned + + FullCodec + + Copy + + Default + + Debug + + scale_info::TypeInfo + + MaxEncodedLen; + + /// The type of the Merkle Patricia Root of the storage trie for BSPs and MSPs' buckets (a hash). + type MerklePatriciaRoot: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// The type of the identifier of the value proposition of a MSP (probably a hash of that value proposition) + type ValuePropId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// The type of the Bucket NFT Collection ID. + type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; + + /// The trait exposing data of which providers failed to respond to challenges for proofs of storage. + type ProvidersProofSubmitters: ProofSubmittersInterface< + ProviderId = HashId, + TickNumber = BlockNumberFor, + >; + + /// The type representing the reputation weight of a BSP. + type ReputationWeightType: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + Saturating + + Copy + + MaxEncodedLen + + HasCompact + + Zero + + One + + CheckedAdd + + Ord + + Bounded; + + /// The Treasury AccountId. + /// The account to which: + /// - The fees for submitting a challenge are transferred. + /// - The slashed funds are transferred. + #[pallet::constant] + type Treasury: Get; + + /// The minimum amount that an account has to deposit to become a storage provider. + #[pallet::constant] + type SpMinDeposit: Get>; + + /// The amount that a BSP receives as allocation of storage capacity when it deposits SpMinDeposit. + #[pallet::constant] + type SpMinCapacity: Get>; + + /// The slope of the collateral vs storage capacity curve. In other terms, how many tokens a Storage Provider should add as collateral to increase its storage capacity in one unit of StorageDataUnit. + #[pallet::constant] + type DepositPerData: Get>; + + /// The estimated maximum size of an unknown file. + /// + /// Used primarily to slash a Storage Provider when it fails to provide a chunk of data for an unknown file size. + #[pallet::constant] + type MaxFileSize: Get>; + + /// The maximum size of a multiaddress. + #[pallet::constant] + type MaxMultiAddressSize: Get; + + /// The maximum amount of multiaddresses that a Storage Provider can have. + #[pallet::constant] + type MaxMultiAddressAmount: Get; + + /// The maximum number of protocols the MSP can support (at least within the runtime). + #[pallet::constant] + type MaxProtocols: Get; + + /// The maximum amount of Buckets that a MSP can have. + #[pallet::constant] + type MaxBuckets: Get; + + /// The amount that an account has to deposit to create a bucket. + #[pallet::constant] + type BucketDeposit: Get>; + + /// Type that represents the byte limit of a bucket name. + #[pallet::constant] + type BucketNameLimit: Get; + + /// The maximum amount of blocks after which a sign up request expires so the randomness cannot be chosen + #[pallet::constant] + type MaxBlocksForRandomness: Get>; + + /// The minimum amount of blocks between capacity changes for a SP + #[pallet::constant] + type MinBlocksBetweenCapacityChanges: Get>; + + /// The default value of the root of the Merkle Patricia Trie of the runtime + #[pallet::constant] + type DefaultMerkleRoot: Get; + + /// The slash factor deducted from a Storage Provider's deposit for every single storage proof they fail to provide. + #[pallet::constant] + type SlashAmountPerMaxFileSize: Get>; + + /// Starting reputation weight for a newly registered BSP. + #[pallet::constant] + type StartingReputationWeight: Get; + } + + #[pallet::pallet] + pub struct Pallet(_); + + // Storage: + + /// The mapping from an AccountId that requested to sign up to a tuple of the metadata with type of the request, and the block + /// number when the request was made. + /// + /// This is used for the two-step process of registering: when a user requests to register as a SP (either MSP or BSP), + /// that request with the metadata and the deposit held is stored here. When the user confirms the sign up, the + /// request is removed from this storage and the user is registered as a SP. + /// + /// This storage is updated in: + /// - [request_msp_sign_up](crate::dispatchables::request_msp_sign_up) and [request_bsp_sign_up](crate::dispatchables::request_bsp_sign_up), which add a new entry to the map. + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up) and [cancel_sign_up](crate::dispatchables::cancel_sign_up), which remove an existing entry from the map. + #[pallet::storage] + pub type SignUpRequests = + StorageMap<_, Blake2_128Concat, T::AccountId, (StorageProvider, BlockNumberFor)>; + + /// The mapping from an AccountId to a MainStorageProviderId. + /// + /// This is used to get a Main Storage Provider's unique identifier needed to access its metadata. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Main Storage Provider. + /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which removes the corresponding entry from the map. + #[pallet::storage] + pub type AccountIdToMainStorageProviderId = + StorageMap<_, Blake2_128Concat, T::AccountId, MainStorageProviderId>; + + /// The mapping from a MainStorageProviderId to a MainStorageProvider. + /// + /// This is used to get a Main Storage Provider's metadata. + /// It returns `None` if the Main Storage Provider ID does not correspond to any registered Main Storage Provider. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Main Storage Provider. + /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which removes the corresponding entry from the map. + /// - [change_capacity](crate::dispatchables::change_capacity), which changes the entry's `capacity`. + /// - [add_value_prop](crate::dispatchables::add_value_prop), which appends a new value proposition to the entry's existing `value_prop` bounded vector. + #[pallet::storage] + pub type MainStorageProviders = + StorageMap<_, Blake2_128Concat, MainStorageProviderId, MainStorageProvider>; + + /// The mapping from a BucketId to that bucket's metadata. + /// + /// This is used to get a bucket's metadata, such as root, user ID, and MSP ID. + /// It returns `None` if the Bucket ID does not correspond to any registered bucket. + /// + /// This storage is updated in: + /// - [add_bucket](shp_traits::MutateProvidersInterface::add_bucket), which adds a new entry to the map. + /// - [change_root_bucket](shp_traits::MutateProvidersInterface::change_root_bucket), which changes the corresponding bucket's root. + /// - [remove_root_bucket](shp_traits::MutateProvidersInterface::remove_root_bucket), which removes the entry of the corresponding bucket. + #[pallet::storage] + pub type Buckets = StorageMap<_, Blake2_128Concat, BucketId, Bucket>; + + /// The mapping from a MainStorageProviderId to a vector of BucketIds. + /// + /// This is used to efficiently retrieve the list of buckets that a Main Storage Provider is currently storing. + /// + /// This storage is updated in: + /// - [add_bucket](shp_traits::MutateProvidersInterface::add_bucket) + /// - [remove_root_bucket](shp_traits::MutateProvidersInterface::remove_root_bucket) + #[pallet::storage] + pub type MainStorageProviderIdsToBuckets = StorageMap< + _, + Blake2_128Concat, + MainStorageProviderId, + BoundedVec, T::MaxBuckets>, + >; + + /// The mapping from an AccountId to a BackupStorageProviderId. + /// + /// This is used to get a Backup Storage Provider's unique identifier needed to access its metadata. + /// + /// This storage is updated in: + /// + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Backup Storage Provider. + /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which removes the corresponding entry from the map. + #[pallet::storage] + pub type AccountIdToBackupStorageProviderId = + StorageMap<_, Blake2_128Concat, T::AccountId, BackupStorageProviderId>; + + /// The mapping from a BackupStorageProviderId to a BackupStorageProvider. + /// + /// This is used to get a Backup Storage Provider's metadata. + /// It returns `None` if the Backup Storage Provider ID does not correspond to any registered Backup Storage Provider. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds a new entry to the map if the account to confirm is a Backup Storage Provider. + /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which removes the corresponding entry from the map. + /// - [change_capacity](crate::dispatchables::change_capacity), which changes the entry's `capacity`. + #[pallet::storage] + pub type BackupStorageProviders = + StorageMap<_, Blake2_128Concat, BackupStorageProviderId, BackupStorageProvider>; + + /// The amount of Main Storage Providers that are currently registered in the runtime. + /// + /// This is used to keep track of the total amount of MSPs in the system. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds one to this storage if the account to confirm is a Main Storage Provider. + /// - [msp_sign_off](crate::dispatchables::msp_sign_off), which subtracts one from this storage. + #[pallet::storage] + pub type MspCount = StorageValue<_, T::SpCount, ValueQuery>; + + /// The amount of Backup Storage Providers that are currently registered in the runtime. + /// + /// This is used to keep track of the total amount of BSPs in the system. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds one to this storage if the account to confirm is a Backup Storage Provider. + /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which subtracts one from this storage. + #[pallet::storage] + pub type BspCount = StorageValue<_, T::SpCount, ValueQuery>; + + /// The total amount of storage capacity all BSPs have. + /// + /// This is used to keep track of the total amount of storage capacity all BSPs have in the system, which is also the + /// total amount of storage capacity that can be used by users if we factor in the replication factor. + /// + /// This storage is updated in: + /// - [confirm_sign_up](crate::dispatchables::confirm_sign_up), which adds the capacity of the registered Storage Provider to this storage if the account to confirm is a Backup Storage Provider. + /// - [bsp_sign_off](crate::dispatchables::bsp_sign_off), which subtracts the capacity of the Backup Storage Provider to sign off from this storage. + #[pallet::storage] + pub type TotalBspsCapacity = StorageValue<_, StorageDataUnit, ValueQuery>; + + /// The total amount of storage capacity of BSPs that is currently in use. + /// + /// This is used to keep track of the total amount of storage capacity that is currently in use by users, which is useful for + /// system metrics and also to calculate the current price of storage. + #[pallet::storage] + pub type UsedBspsCapacity = StorageValue<_, StorageDataUnit, ValueQuery>; + + /// The total global reputation weight of all BSPs. + #[pallet::storage] + pub type GlobalBspsReputationWeight = StorageValue<_, ReputationWeightType, ValueQuery>; + + // Events & Errors: + + /// The events that can be emitted by this pallet + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event emitted when a Main Storage Provider has requested to sign up successfully. Provides information about + /// that MSP's account id, its multiaddresses, the total data it can store according to its stake, and its value proposition. + MspRequestSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageDataUnit, + value_prop: ValueProposition, + }, + + /// Event emitted when a Main Storage Provider has confirmed its sign up successfully. Provides information about + /// that MSP's account id, the total data it can store according to its stake, its multiaddress, and its value proposition. + MspSignUpSuccess { + who: T::AccountId, + msp_id: MainStorageProviderId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageDataUnit, + value_prop: ValueProposition, + }, + + /// Event emitted when a Backup Storage Provider has requested to sign up successfully. Provides information about + /// that BSP's account id, its multiaddresses, and the total data it can store according to its stake. + BspRequestSignUpSuccess { + who: T::AccountId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageDataUnit, + }, + + /// Event emitted when a Backup Storage Provider has confirmed its sign up successfully. Provides information about + /// that BSP's account id, the total data it can store according to its stake, and its multiaddress. + BspSignUpSuccess { + who: T::AccountId, + bsp_id: BackupStorageProviderId, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + capacity: StorageDataUnit, + }, + + /// Event emitted when a sign up request has been canceled successfully. Provides information about + /// the account id of the user that canceled the request. + SignUpRequestCanceled { who: T::AccountId }, + + /// Event emitted when a Main Storage Provider has signed off successfully. Provides information about + /// that MSP's account id. + MspSignOffSuccess { + who: T::AccountId, + msp_id: MainStorageProviderId, + }, + + /// Event emitted when a Backup Storage Provider has signed off successfully. Provides information about + /// that BSP's account id. + BspSignOffSuccess { + who: T::AccountId, + bsp_id: BackupStorageProviderId, + }, + + /// Event emitted when a SP has changed its capacity successfully. Provides information about + /// that SP's account id, its old total data that could store, and the new total data. + CapacityChanged { + who: T::AccountId, + provider_id: StorageProviderId, + old_capacity: StorageDataUnit, + new_capacity: StorageDataUnit, + next_block_when_change_allowed: BlockNumberFor, + }, + + /// Event emitted when an SP has been slashed. + Slashed { + provider_id: HashId, + amount_slashed: BalanceOf, + }, + } + + /// The errors that can be thrown by this pallet to inform users about what went wrong + #[pallet::error] + pub enum Error { + // Sign up errors: + /// Error thrown when a user tries to sign up as a SP but is already registered as a MSP or BSP. + AlreadyRegistered, + /// Error thrown when a user tries to confirm a sign up that was not requested previously. + SignUpNotRequested, + /// Error thrown when a user tries to request to sign up when it already has a sign up request pending. + SignUpRequestPending, + /// Error thrown when a user tries to sign up without any multiaddress. + NoMultiAddress, + /// Error thrown when a user tries to sign up as a SP but any of the provided multiaddresses is invalid. + InvalidMultiAddress, + /// Error thrown when a user tries to sign up or change its capacity to store less storage than the minimum required by the runtime. + StorageTooLow, + + // Deposit errors: + /// Error thrown when a user does not have enough balance to pay the deposit that it would incur by signing up as a SP or changing its capacity. + NotEnoughBalance, + /// Error thrown when the runtime cannot hold the required deposit from the account to register it as a SP or change its capacity. + CannotHoldDeposit, + + // Sign off errors: + /// Error thrown when a user tries to sign off as a SP but still has used storage. + StorageStillInUse, + + // Randomness errors: + /// Error thrown when a user tries to confirm a sign up but the randomness is too fresh to be used yet. + RandomnessNotValidYet, + /// Error thrown when a user tries to confirm a sign up but too much time has passed since the request. + SignUpRequestExpired, + + // Capacity change errors: + /// Error thrown when a user tries to change its capacity to less than its used storage. + NewCapacityLessThanUsedStorage, + /// Error thrown when a user tries to change its capacity to the same value it already has. + NewCapacityEqualsCurrentCapacity, + /// Error thrown when a user tries to change its capacity to zero (there are specific extrinsics to sign off as a SP). + NewCapacityCantBeZero, + /// Error thrown when a SP tries to change its capacity but it has not been enough time since the last time it changed it. + NotEnoughTimePassed, + /// Error thrown when a SP tries to change its capacity but the new capacity is not enough to store the used storage. + NewUsedCapacityExceedsStorageCapacity, + + // General errors: + /// Error thrown when a user tries to interact as a SP but is not registered as a MSP or BSP. + NotRegistered, + /// Error thrown when trying to get a root from a MSP without passing a User ID. + NoUserId, + /// Error thrown when trying to get a root from a MSP without passing a Bucket ID. + NoBucketId, + /// Error thrown when a user has a SP ID assigned to it but the SP data does not exist in storage (Inconsistency error). + SpRegisteredButDataNotFound, + /// Error thrown when a bucket ID is not found in storage. + BucketNotFound, + /// Error thrown when a bucket ID already exists in storage. + BucketAlreadyExists, + /// Error thrown when a bucket ID could not be added to the list of buckets of a MSP. + AppendBucketToMspFailed, + /// Error thrown when an attempt was made to slash an unslashable Storage Provider. + ProviderNotSlashable, + + // Payment streams interface errors: + /// Error thrown when failing to decode the metadata from a received trie value that was removed. + InvalidEncodedFileMetadata, + /// Error thrown when failing to decode the owner Account ID from the received metadata. + InvalidEncodedAccountId, + /// Error thrown when trying to update a payment stream that does not exist. + PaymentStreamNotFound, + } + + /// This enum holds the HoldReasons for this pallet, allowing the runtime to identify each held balance with different reasons separately + /// + /// This allows us to hold tokens and be able to identify in the future that those held tokens were + /// held because of this pallet + #[pallet::composite_enum] + pub enum HoldReason { + /// Deposit that a Storage Provider has to pay to be registered as such + StorageProviderDeposit, + /// Deposit that a user has to pay to create a bucket + BucketDeposit, + // Only for testing, another unrelated hold reason + #[cfg(test)] + AnotherUnrelatedHold, + } + + /// Dispatchables (extrinsics) exposed by this pallet + #[pallet::call] + impl Pallet { + /// Dispatchable extrinsic that allows users to request to sign up as a Main Storage Provider. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to sign up as a Main Storage Provider. + /// + /// Funds proportional to the capacity requested are reserved (held) from the account. + /// + /// Parameters: + /// - `capacity`: The total amount of data that the Main Storage Provider will be able to store. + /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the + /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) + /// - `value_prop`: The value proposition that the signer will provide as a Main Storage Provider to + /// users and wants to register on-chain. It could be data limits, communication protocols to access the user's + /// data, and more. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is not already registered as either a MSP or BSP + /// 3. Check that the multiaddress is valid + /// 4. Check that the data to be stored is greater than the minimum required by the runtime. + /// 5. Calculate how much deposit will the signer have to pay using the amount of data it wants to store + /// 6. Check that the signer has enough funds to pay the deposit + /// 7. Hold the deposit from the signer + /// 8. Update the Sign Up Requests storage to add the signer as requesting to sign up as a MSP + /// + /// Emits `MspRequestSignUpSuccess` event when successful. + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn request_msp_sign_up( + origin: OriginFor, + capacity: StorageDataUnit, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + value_prop: ValueProposition, + payment_account: T::AccountId, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Set up a structure with the information of the new MSP + let msp_info = MainStorageProvider { + buckets: BoundedVec::default(), + capacity, + capacity_used: StorageDataUnit::::default(), + multiaddresses: multiaddresses.clone(), + value_prop: value_prop.clone(), + last_capacity_change: frame_system::Pallet::::block_number(), + owner_account: who.clone(), + payment_account, + }; + + // Sign up the new MSP (if possible), updating storage + Self::do_request_msp_sign_up(&msp_info)?; + + // Emit the corresponding event + Self::deposit_event(Event::::MspRequestSignUpSuccess { + who, + multiaddresses, + capacity, + value_prop, + }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic that allows users to sign up as a Backup Storage Provider. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to sign up as a Backup Storage Provider. + /// + /// Funds proportional to the capacity requested are reserved (held) from the account. + /// + /// Parameters: + /// - `capacity`: The total amount of data that the Backup Storage Provider will be able to store. + /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the + /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is not already registered as either a MSP or BSP + /// 3. Check that the multiaddress is valid + /// 4. Check that the data to be stored is greater than the minimum required by the runtime + /// 5. Calculate how much deposit will the signer have to pay using the amount of data it wants to store + /// 6. Check that the signer has enough funds to pay the deposit + /// 7. Hold the deposit from the signer + /// 8. Update the Sign Up Requests storage to add the signer as requesting to sign up as a BSP + /// + /// Emits `BspRequestSignUpSuccess` event when successful. + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn request_bsp_sign_up( + origin: OriginFor, + capacity: StorageDataUnit, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + payment_account: T::AccountId, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Set up a structure with the information of the new BSP + let bsp_info = BackupStorageProvider { + capacity, + capacity_used: StorageDataUnit::::default(), + multiaddresses: multiaddresses.clone(), + root: T::DefaultMerkleRoot::get(), + last_capacity_change: frame_system::Pallet::::block_number(), + owner_account: who.clone(), + payment_account, + reputation_weight: T::StartingReputationWeight::get(), + }; + + // Sign up the new BSP (if possible), updating storage + Self::do_request_bsp_sign_up(&bsp_info)?; + + // Emit the corresponding event + Self::deposit_event(Event::::BspRequestSignUpSuccess { + who, + multiaddresses, + capacity, + }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic that allows users to confirm their sign up as a Storage Provider, either MSP or BSP. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that requested to sign up as a Storage Provider, except when providing a + /// `provider_account` parameter, in which case the origin can be any account. + /// + /// Parameters: + /// - `provider_account`: The account that requested to sign up as a Storage Provider. If not provided, the signer + /// will be considered the account that requested to sign up. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed + /// 2. Check that the account received has requested to register as a SP + /// 3. Check that the current randomness is sufficiently fresh to be used as a salt for that request + /// 4. Check that the request has not expired + /// 5. Register the signer as a MSP or BSP with the data provided in the request + /// + /// Emits `MspSignUpSuccess` or `BspSignUpSuccess` event when successful, depending on the type of sign up. + /// + /// Notes: + /// - This extrinsic could be called by the user itself or by a third party + /// - The deposit that the user has to pay to register as a SP is held when the user requests to register as a SP + /// - If this extrinsic is successful, it will be free for the caller, to incentive state debloating + #[pallet::call_index(2)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn confirm_sign_up( + origin: OriginFor, + provider_account: Option, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer + let who = ensure_signed(origin)?; + + // Execute checks and logic, update storage and emit event + // We emit the event in the interior logic to not have to check again which type of sign up it is outside of it + match provider_account { + Some(provider_account) => Self::do_confirm_sign_up(&provider_account)?, + None => Self::do_confirm_sign_up(&who)?, + } + + // Return a successful DispatchResultWithPostInfo. If the extrinsic executed correctly, it will be free for the caller + Ok(Pays::No.into()) + } + + /// Dispatchable extrinsic that allows a user with a pending Sign Up Request to cancel it, getting the deposit back. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that requested to sign up as a Storage Provider. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer has requested to sign up as a SP + /// 3. Delete the request from the Sign Up Requests storage + /// 4. Return the deposit to the signer + /// + /// Emits `SignUpRequestCanceled` event when successful. + #[pallet::call_index(3)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn cancel_sign_up(origin: OriginFor) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Execute checks and logic, update storage + Self::do_cancel_sign_up(&who)?; + + // Emit the corresponding event + Self::deposit_event(Event::::SignUpRequestCanceled { who }); + + Ok(().into()) + } + + /// Dispatchable extrinsic that allows users to sign off as a Main Storage Provider. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to sign off as a Main Storage Provider. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is registered as a MSP + /// 3. Check that the MSP has no storage assigned to it (no buckets or data used by it) + /// 4. Update the MSPs storage, removing the signer as an MSP + /// 5. Return the deposit to the signer + /// 6. Decrement the storage that holds total amount of MSPs currently in the system + /// + /// Emits `MspSignOffSuccess` event when successful. + #[pallet::call_index(4)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn msp_sign_off(origin: OriginFor) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Execute checks and logic, update storage + let msp_id = Self::do_msp_sign_off(&who)?; + + // Emit the corresponding event + Self::deposit_event(Event::::MspSignOffSuccess { who, msp_id }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic that allows users to sign off as a Backup Storage Provider. + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to sign off as a Backup Storage Provider. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is registered as a BSP + /// 3. Check that the BSP has no storage assigned to it + /// 4. Update the BSPs storage, removing the signer as an BSP + /// 5. Update the total capacity of all BSPs, removing the capacity of the signer + /// 6. Return the deposit to the signer + /// 7. Decrement the storage that holds total amount of BSPs currently in the system + /// + /// Emits `BspSignOffSuccess` event when successful. + #[pallet::call_index(5)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn bsp_sign_off(origin: OriginFor) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Execute checks and logic, update storage + let bsp_id = Self::do_bsp_sign_off(&who)?; + + // Emit the corresponding event + Self::deposit_event(Event::::BspSignOffSuccess { who, bsp_id }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic that allows users to change their amount of stored data + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to change its capacity. + /// + /// Parameters: + /// - `new_capacity`: The new total amount of data that the Storage Provider wants to be able to store. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is registered as a SP + /// 3. Check that enough time has passed since the last time the SP changed its capacity + /// 4. Check that the new capacity is greater than the minimum required by the runtime + /// 5. Check that the new capacity is greater than the data used by this SP + /// 6. Calculate the new deposit needed for this new capacity + /// 7. Check to see if the new deposit needed is greater or less than the current deposit + /// a. If the new deposit is greater than the current deposit: + /// i. Check that the signer has enough funds to pay this extra deposit + /// ii. Hold the extra deposit from the signer + /// b. If the new deposit is less than the current deposit, return the held difference to the signer + /// 7. Update the SPs storage to change the total data + /// 8. If user is a BSP, update the total capacity of the network (sum of all capacities of BSPs) + /// + /// Emits `CapacityChanged` event when successful. + #[pallet::call_index(6)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn change_capacity( + origin: OriginFor, + new_capacity: StorageDataUnit, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Execute checks and logic, update storage + let (provider_id, old_capacity) = Self::do_change_capacity(&who, new_capacity)?; + + // Emit the corresponding event + Self::deposit_event(Event::::CapacityChanged { + who, + provider_id, + old_capacity, + new_capacity, + next_block_when_change_allowed: frame_system::Pallet::::block_number() + + T::MinBlocksBetweenCapacityChanges::get(), + }); + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic only callable by an MSP that allows it to add a value proposition to its service + /// + /// The dispatch origin for this call must be Signed. + /// The origin must be the account that wants to add a value proposition. + /// + /// Parameters: + /// - `new_value_prop`: The value proposition that the MSP wants to add to its service. + /// + /// This extrinsic will perform the following checks and logic: + /// 1. Check that the extrinsic was signed and get the signer. + /// 2. Check that the signer is registered as a MSP + /// 3. Check that the MSP has not reached the maximum amount of value propositions + /// 4. Check that the value proposition is valid (size and any other relevant checks) + /// 5. Update the MSPs storage to add the value proposition (with its identifier) + /// + /// Emits `ValuePropAdded` event when successful. + #[pallet::call_index(7)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn add_value_prop( + _origin: OriginFor, + _new_value_prop: ValueProposition, + ) -> DispatchResultWithPostInfo { + // TODO: implement this + + Ok(().into()) + } + + /// Dispatchable extrinsic that allows to forcefully and automatically sing up a Main Storage Provider. + /// + /// The dispatch origin for this call must be Root. + /// The `who` parameter is the account that wants to sign up as a Main Storage Provider. + /// + /// Funds proportional to the capacity requested are reserved (held) from the account passed as the `who` parameter. + /// + /// Parameters: + /// - `who`: The account that wants to sign up as a Main Storage Provider. + /// - `msp_id`: The Main Storage Provider ID that the account passed as the `who` parameter is requesting to sign up as. + /// - `capacity`: The total amount of data that the Main Storage Provider will be able to store. + /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the + /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) + /// - `value_prop`: The value proposition that the signer will provide as a Main Storage Provider to + /// users and wants to register on-chain. It could be data limits, communication protocols to access the user's + /// data, and more. + /// + /// This extrinsic will perform the steps of: + /// 1. [request_msp_sign_up](crate::dispatchables::request_msp_sign_up) + /// 2. [confirm_sign_up](crate::dispatchables::confirm_sign_up) + /// + /// Emits `MspRequestSignUpSuccess` and `MspSignUpSuccess` events when successful. + #[pallet::call_index(8)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn force_msp_sign_up( + origin: OriginFor, + who: T::AccountId, + msp_id: MainStorageProviderId, + capacity: StorageDataUnit, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + value_prop: ValueProposition, + payment_account: T::AccountId, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was sent with root origin. + ensure_root(origin)?; + + // Set up a structure with the information of the new MSP + let msp_info = MainStorageProvider { + buckets: BoundedVec::default(), + capacity, + capacity_used: StorageDataUnit::::default(), + multiaddresses: multiaddresses.clone(), + value_prop: value_prop.clone(), + last_capacity_change: frame_system::Pallet::::block_number(), + owner_account: who.clone(), + payment_account, + }; + + // Sign up the new MSP (if possible), updating storage + Self::do_request_msp_sign_up(&msp_info)?; + + // Emit the corresponding event + Self::deposit_event(Event::::MspRequestSignUpSuccess { + who: who.clone(), + multiaddresses, + capacity, + value_prop, + }); + + // Confirm the sign up of the account as a Main Storage Provider with the given ID + Self::do_msp_sign_up( + &who, + msp_id, + &msp_info, + frame_system::Pallet::::block_number(), + )?; + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic that allows to forcefully and automatically sing up a Backup Storage Provider. + /// + /// The dispatch origin for this call must be Root. + /// The `who` parameter is the account that wants to sign up as a Backup Storage Provider. + /// + /// Funds proportional to the capacity requested are reserved (held) from the account passed as the `who` parameter. + /// + /// Parameters: + /// - `who`: The account that wants to sign up as a Backup Storage Provider. + /// - `bsp_id`: The Backup Storage Provider ID that the account passed as the `who` parameter is requesting to sign up as. + /// - `capacity`: The total amount of data that the Backup Storage Provider will be able to store. + /// - `multiaddresses`: The vector of multiaddresses that the signer wants to register (according to the + /// [Multiaddr spec](https://github.com/multiformats/multiaddr)) + /// + /// This extrinsic will perform the steps of: + /// 1. [request_bsp_sign_up](crate::dispatchables::request_bsp_sign_up) + /// 2. [confirm_sign_up](crate::dispatchables::confirm_sign_up) + /// + /// Emits `BspRequestSignUpSuccess` and `BspSignUpSuccess` events when successful. + #[pallet::call_index(9)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn force_bsp_sign_up( + origin: OriginFor, + who: T::AccountId, + bsp_id: BackupStorageProviderId, + capacity: StorageDataUnit, + multiaddresses: BoundedVec, MaxMultiAddressAmount>, + payment_account: T::AccountId, + weight: Option>, + ) -> DispatchResultWithPostInfo { + // Check that the extrinsic was sent with root origin. + ensure_root(origin)?; + + // Set up a structure with the information of the new BSP + let bsp_info = BackupStorageProvider { + capacity, + capacity_used: StorageDataUnit::::default(), + multiaddresses: multiaddresses.clone(), + root: T::DefaultMerkleRoot::get(), + last_capacity_change: frame_system::Pallet::::block_number(), + owner_account: who.clone(), + payment_account, + reputation_weight: weight.unwrap_or(T::StartingReputationWeight::get()), + }; + + // Sign up the new BSP (if possible), updating storage + Self::do_request_bsp_sign_up(&bsp_info)?; + + // Emit the corresponding event + Self::deposit_event(Event::::BspRequestSignUpSuccess { + who: who.clone(), + multiaddresses, + capacity, + }); + + // Confirm the sign up of the account as a Backup Storage Provider with the given ID + Self::do_bsp_sign_up( + &who, + bsp_id, + &bsp_info, + frame_system::Pallet::::block_number(), + )?; + + // Return a successful DispatchResultWithPostInfo + Ok(().into()) + } + + /// Dispatchable extrinsic to slash a _slashable_ Storage Provider. + /// + /// A Storage Provider is _slashable_ iff it has failed to respond to challenges for providing proofs of storage. + /// In the context of the StorageHub protocol, the proofs-dealer pallet marks a Storage Provider as _slashable_ when it fails to respond to challenges. + #[pallet::call_index(10)] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + pub fn slash(origin: OriginFor, provider_id: HashId) -> DispatchResultWithPostInfo { + // Check that the extrinsic was sent with root origin. + ensure_signed(origin)?; + + Self::do_slash(&provider_id) + } + } +} + +/// Helper functions (getters, setters, etc.) for this pallet +impl Pallet { + /// A helper function to get the information of a sign up request of a user. + pub fn get_sign_up_request( + who: &T::AccountId, + ) -> Result<(StorageProvider, BlockNumberFor), Error> { + SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested) + } + + /// A helper function to get the total capacity of a storage provider. + pub fn get_total_capacity_of_sp(who: &T::AccountId) -> Result, Error> { + if let Some(m_id) = AccountIdToMainStorageProviderId::::get(who) { + let msp = MainStorageProviders::::get(m_id).ok_or(Error::::NotRegistered)?; + Ok(msp.capacity) + } else if let Some(b_id) = AccountIdToBackupStorageProviderId::::get(who) { + let bsp = BackupStorageProviders::::get(b_id).ok_or(Error::::NotRegistered)?; + Ok(bsp.capacity) + } else { + Err(Error::::NotRegistered) + } + } + + /// A helper function to get the total capacity of all BSPs which is the total capacity of the network. + pub fn get_total_bsp_capacity() -> StorageDataUnit { + TotalBspsCapacity::::get() + } + + /// A helper function to get the total used capacity of all BSPs. + pub fn get_used_bsp_capacity() -> StorageDataUnit { + UsedBspsCapacity::::get() + } + + /// A helper function to get the total data used by a Main Storage Provider. + pub fn get_used_storage_of_msp( + who: &MainStorageProviderId, + ) -> Result, Error> { + let msp = MainStorageProviders::::get(who).ok_or(Error::::NotRegistered)?; + Ok(msp.capacity_used) + } + + /// A helper function to get the total data used by a Backup Storage Provider. + pub fn get_used_storage_of_bsp( + who: &BackupStorageProviderId, + ) -> Result, Error> { + let bsp = BackupStorageProviders::::get(who).ok_or(Error::::NotRegistered)?; + Ok(bsp.capacity_used) + } + + /// A helper function to get the total amount of Backup Storage Providers that have registered. + pub fn get_bsp_count() -> T::SpCount { + BspCount::::get() + } + + /// A helper function to get the total amount of Main Storage Providers that have registered. + pub fn get_msp_count() -> T::SpCount { + MspCount::::get() + } +} diff --git a/pallets/providers/src/mock.rs b/pallets/providers/src/mock.rs index b61582bd9..a8badc9a5 100644 --- a/pallets/providers/src/mock.rs +++ b/pallets/providers/src/mock.rs @@ -1,407 +1,407 @@ -use crate as pallet_storage_providers; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{Everything, Randomness}, - weights::{constants::RocksDbWeight, Weight}, - BoundedBTreeSet, -}; -use frame_system as system; -use pallet_proofs_dealer::SlashableProviders; -use shp_file_metadata::FileMetadata; -use shp_traits::{ - CommitmentVerifier, FileMetadataInterface, MaybeDebug, ProofSubmittersInterface, - ReadChallengeableProvidersInterface, TrieMutation, TrieProofDeltaApplier, -}; -use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, IdentityLookup}, - BuildStorage, DispatchError, Perbill, SaturatedConversion, -}; -use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; -use std::collections::BTreeSet; -use system::pallet_prelude::BlockNumberFor; - -type Block = frame_system::mocking::MockBlock; -type Balance = u128; -type AccountId = u64; -const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; -const UNITS: Balance = 1_000_000_000_000; -const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; -// We mock the Randomness trait to use a simple randomness function when testing the pallet -const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; - -// Configure a mock runtime to test the pallet. -construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - StorageProviders: pallet_storage_providers, - ProofsDealer: pallet_proofs_dealer, - PaymentStreams: pallet_payment_streams, - } -); - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; - pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::StorageProviderDeposit); - pub const BucketHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::BucketDeposit); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_balances::Config for Test { - type Balance = Balance; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxLocks = ConstU32<10>; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = (); - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<10>; -} - -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId { - 0 - } -} - -// Proofs dealer pallet: -impl pallet_proofs_dealer::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersPallet = StorageProviders; - type NativeBalance = Balances; - type MerkleTrieHash = H256; - type MerkleTrieHashing = BlakeTwo256; - type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type StakeToBlockNumber = SaturatingBalanceToBlockNumber; - type RandomChallengesPerBlock = ConstU32<10>; - type MaxCustomChallengesPerBlock = ConstU32<10>; - type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight - type TargetTicksStorageOfSubmitters = ConstU32<3>; - type ChallengeHistoryLength = ConstU64<30>; - type ChallengesQueueLength = ConstU32<25>; - type CheckpointChallengePeriod = ConstU64<20>; - type ChallengesFee = ConstU128<1_000_000>; - type Treasury = TreasuryAccount; - type RandomnessProvider = MockRandomness; - type StakeToChallengePeriod = ConstU128; - type MinChallengePeriod = ConstU64<4>; - type ChallengeTicksTolerance = ConstU64<10>; - type BlockFullnessPeriod = ConstU64<10>; - type BlockFullnessHeadroom = BlockFullnessHeadroom; - type MinNotFullBlocksRatio = MinNotFullBlocksRatio; -} - -// Converter from the Balance type to the BlockNumber type for math. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct SaturatingBalanceToBlockNumber; - -impl Convert> for SaturatingBalanceToBlockNumber { - fn convert(block_number: Balance) -> BlockNumberFor { - block_number.saturated_into() - } -} - -pub struct BlockFullnessHeadroom; -impl Get for BlockFullnessHeadroom { - fn get() -> Weight { - Weight::from_parts(10_000, 0) - + ::DbWeight::get().reads_writes(0, 1) - } -} - -pub struct MinNotFullBlocksRatio; -impl Get for MinNotFullBlocksRatio { - fn get() -> Perbill { - Perbill::from_percent(50) - } -} - -/// Structure to mock a verifier that returns `true` when `proof` is not empty -/// and `false` otherwise. -pub struct MockVerifier { - _phantom: core::marker::PhantomData<(C, T)>, -} - -/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. -impl CommitmentVerifier for MockVerifier -where - C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, -{ - type Proof = CompactProof; - type Commitment = H256; - type Challenge = H256; - - fn verify_proof( - _root: &Self::Commitment, - _challenges: &[Self::Challenge], - proof: &CompactProof, - ) -> Result, DispatchError> { - if proof.encoded_nodes.len() > 0 { - Ok(proof - .encoded_nodes - .iter() - .map(|node| H256::from_slice(&node[..])) - .collect()) - } else { - Err("Proof is empty".into()) - } - } -} - -impl TrieProofDeltaApplier - for MockVerifier -where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - type Proof = CompactProof; - type Key = ::Out; - - fn apply_delta( - root: &Self::Key, - _mutations: &[(Self::Key, TrieMutation)], - _proof: &Self::Proof, - ) -> Result< - ( - MemoryDB, - Self::Key, - Vec<(Self::Key, Option>)>, - ), - DispatchError, - > { - // Just return the root as is with no mutations - Ok((MemoryDB::::default(), *root, Vec::new())) - } -} - -// Payment streams pallet: -impl pallet_payment_streams::Config for Test { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = StorageProviders; - type RuntimeHoldReason = RuntimeHoldReason; - type Units = u64; - type NewStreamDeposit = ConstU64<10>; - type UserWithoutFundsCooldown = ConstU64<100>; - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = MockSubmittingProviders; -} -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; -impl Convert, Balance> for BlockNumberToBalance { - fn convert(block_number: BlockNumberFor) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -// Storage providers pallet: -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = MockRandomness; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type FileMetadataManager = MockFileMetadataManager; - type StorageDataUnit = u64; - type SpCount = u32; - type MerklePatriciaRoot = H256; - type ValuePropId = H256; - type ReadAccessGroupId = u32; - type PaymentStreams = PaymentStreams; - type ProvidersProofSubmitters = MockSubmittingProviders; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = ConstU128<{ 10 * UNITS }>; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = ConstU32<100>; - type MaxMultiAddressAmount = ConstU32<5>; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = ConstU128<10>; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; - type MinBlocksBetweenCapacityChanges = ConstU64<10>; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = ConstU128<10>; - type StartingReputationWeight = ConstU32<1>; -} - -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} - -pub struct MockFileMetadataManager; -impl FileMetadataInterface for MockFileMetadataManager { - type AccountId = AccountId; - type Metadata = FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type StorageDataUnit = u64; - - fn encode(metadata: &Self::Metadata) -> Vec { - metadata.encode() - } - - fn decode(data: &[u8]) -> Result { - as Decode>::decode(&mut &data[..]) - } - - fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { - metadata.file_size - } - - fn get_file_owner(metadata: &Self::Metadata) -> Result { - Self::AccountId::decode(&mut metadata.owner.as_slice()) - } -} - -pub struct MockRandomness; -impl Randomness> for MockRandomness { - fn random(subject: &[u8]) -> (H256, BlockNumberFor) { - // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks - - // Concatenate the subject with the block number to get a unique hash for each block - let subject_concat_block = [ - subject, - &frame_system::Pallet::::block_number().to_le_bytes(), - ] - .concat(); - - let hashed_subject = blake2_256(&subject_concat_block); - - ( - H256::from_slice(&hashed_subject), - frame_system::Pallet::::block_number() - .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), - ) - } -} - -/// This function is used to test the randomness of the providers pallet. -pub fn test_randomness_output( - who: &::AccountId, -) -> (::Hash, BlockNumberFor) { - ::ProvidersRandomness::random(who.encode().as_ref()) -} - -// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. -pub struct MockSubmittingProviders; -impl ProofSubmittersInterface for MockSubmittingProviders { - type ProviderId = ::Hash; - type TickNumber = BlockNumberFor; - type MaxProofSubmitters = ConstU32<1000>; - fn get_proof_submitters_for_tick( - block_number: &Self::TickNumber, - ) -> Option> { - let mut set = BoundedBTreeSet::::new(); - // We convert the block number + 1 to the corresponding Provider ID, to simulate that the Provider submitted a proof - ::get_provider_id( - *block_number + 1, - ) - .map(|id| set.try_insert(id)); - Some(set) - } - - fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option { - SlashableProviders::::get(provider_id) - } - - fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) { - SlashableProviders::::remove(provider_id); - } -} - -// Build genesis storage according to the mock runtime. -pub fn _new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::::default() - .build_storage() - .unwrap() - .into() -} - -pub mod accounts { - use super::UNITS; - - pub const ALICE: (u64, u128) = (0, 5_000_000 * UNITS); - pub const BOB: (u64, u128) = (1, 10_000_000 * UNITS); - pub const CHARLIE: (u64, u128) = (2, 20_000_000 * UNITS); - pub const DAVID: (u64, u128) = (3, 30_000_000 * UNITS); - pub const EVE: (u64, u128) = (4, 400_000_000 * UNITS); - pub const FERDIE: (u64, u128) = (5, 5_000_000_000 * UNITS); - pub const GEORGE: (u64, u128) = (6, 600_000_000_000 * UNITS); -} - -// Externalities builder with predefined balances for accounts and starting at block number 1 -pub struct ExtBuilder; -impl ExtBuilder { - pub fn build() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - accounts::ALICE, - accounts::BOB, - accounts::CHARLIE, - accounts::DAVID, - accounts::EVE, - accounts::FERDIE, - accounts::GEORGE, - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} +use crate as pallet_storage_providers; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Everything, Randomness}, + weights::{constants::RocksDbWeight, Weight}, + BoundedBTreeSet, +}; +use frame_system as system; +use pallet_proofs_dealer::SlashableProviders; +use shp_file_metadata::FileMetadata; +use shp_traits::{ + CommitmentVerifier, FileMetadataInterface, MaybeDebug, ProofSubmittersInterface, + ReadChallengeableProvidersInterface, TrieMutation, TrieProofDeltaApplier, +}; +use sp_core::{hashing::blake2_256, ConstU128, ConstU32, ConstU64, Get, Hasher, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, IdentityLookup}, + BuildStorage, DispatchError, Perbill, SaturatedConversion, +}; +use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; +use std::collections::BTreeSet; +use system::pallet_prelude::BlockNumberFor; + +type Block = frame_system::mocking::MockBlock; +type Balance = u128; +type AccountId = u64; +const EPOCH_DURATION_IN_BLOCKS: BlockNumberFor = 10; +const UNITS: Balance = 1_000_000_000_000; +const STAKE_TO_CHALLENGE_PERIOD: Balance = 100 * UNITS; +// We mock the Randomness trait to use a simple randomness function when testing the pallet +const BLOCKS_BEFORE_RANDOMNESS_VALID: BlockNumberFor = 3; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + StorageProviders: pallet_storage_providers, + ProofsDealer: pallet_proofs_dealer, + PaymentStreams: pallet_payment_streams, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; + pub const StorageProvidersHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::StorageProviderDeposit); + pub const BucketHoldReason: RuntimeHoldReason = RuntimeHoldReason::StorageProviders(pallet_storage_providers::HoldReason::BucketDeposit); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<10>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = (); + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<10>; +} + +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId { + 0 + } +} + +// Proofs dealer pallet: +impl pallet_proofs_dealer::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersPallet = StorageProviders; + type NativeBalance = Balances; + type MerkleTrieHash = H256; + type MerkleTrieHashing = BlakeTwo256; + type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type StakeToBlockNumber = SaturatingBalanceToBlockNumber; + type RandomChallengesPerBlock = ConstU32<10>; + type MaxCustomChallengesPerBlock = ConstU32<10>; + type MaxSubmittersPerTick = ConstU32<1000>; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight + type TargetTicksStorageOfSubmitters = ConstU32<3>; + type ChallengeHistoryLength = ConstU64<30>; + type ChallengesQueueLength = ConstU32<25>; + type CheckpointChallengePeriod = ConstU64<20>; + type ChallengesFee = ConstU128<1_000_000>; + type Treasury = TreasuryAccount; + type RandomnessProvider = MockRandomness; + type StakeToChallengePeriod = ConstU128; + type MinChallengePeriod = ConstU64<4>; + type ChallengeTicksTolerance = ConstU64<10>; + type BlockFullnessPeriod = ConstU64<10>; + type BlockFullnessHeadroom = BlockFullnessHeadroom; + type MinNotFullBlocksRatio = MinNotFullBlocksRatio; +} + +// Converter from the Balance type to the BlockNumber type for math. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct SaturatingBalanceToBlockNumber; + +impl Convert> for SaturatingBalanceToBlockNumber { + fn convert(block_number: Balance) -> BlockNumberFor { + block_number.saturated_into() + } +} + +pub struct BlockFullnessHeadroom; +impl Get for BlockFullnessHeadroom { + fn get() -> Weight { + Weight::from_parts(10_000, 0) + + ::DbWeight::get().reads_writes(0, 1) + } +} + +pub struct MinNotFullBlocksRatio; +impl Get for MinNotFullBlocksRatio { + fn get() -> Perbill { + Perbill::from_percent(50) + } +} + +/// Structure to mock a verifier that returns `true` when `proof` is not empty +/// and `false` otherwise. +pub struct MockVerifier { + _phantom: core::marker::PhantomData<(C, T)>, +} + +/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. +impl CommitmentVerifier for MockVerifier +where + C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, +{ + type Proof = CompactProof; + type Commitment = H256; + type Challenge = H256; + + fn verify_proof( + _root: &Self::Commitment, + _challenges: &[Self::Challenge], + proof: &CompactProof, + ) -> Result, DispatchError> { + if proof.encoded_nodes.len() > 0 { + Ok(proof + .encoded_nodes + .iter() + .map(|node| H256::from_slice(&node[..])) + .collect()) + } else { + Err("Proof is empty".into()) + } + } +} + +impl TrieProofDeltaApplier + for MockVerifier +where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + type Proof = CompactProof; + type Key = ::Out; + + fn apply_delta( + root: &Self::Key, + _mutations: &[(Self::Key, TrieMutation)], + _proof: &Self::Proof, + ) -> Result< + ( + MemoryDB, + Self::Key, + Vec<(Self::Key, Option>)>, + ), + DispatchError, + > { + // Just return the root as is with no mutations + Ok((MemoryDB::::default(), *root, Vec::new())) + } +} + +// Payment streams pallet: +impl pallet_payment_streams::Config for Test { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = StorageProviders; + type RuntimeHoldReason = RuntimeHoldReason; + type Units = u64; + type NewStreamDeposit = ConstU64<10>; + type UserWithoutFundsCooldown = ConstU64<100>; + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = MockSubmittingProviders; +} +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; +impl Convert, Balance> for BlockNumberToBalance { + fn convert(block_number: BlockNumberFor) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +// Storage providers pallet: +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = MockRandomness; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type FileMetadataManager = MockFileMetadataManager; + type StorageDataUnit = u64; + type SpCount = u32; + type MerklePatriciaRoot = H256; + type ValuePropId = H256; + type ReadAccessGroupId = u32; + type PaymentStreams = PaymentStreams; + type ProvidersProofSubmitters = MockSubmittingProviders; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = ConstU128<{ 10 * UNITS }>; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = ConstU32<100>; + type MaxMultiAddressAmount = ConstU32<5>; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = ConstU128<10>; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = ConstU64<{ EPOCH_DURATION_IN_BLOCKS * 2 }>; + type MinBlocksBetweenCapacityChanges = ConstU64<10>; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = ConstU128<10>; + type StartingReputationWeight = ConstU32<1>; +} + +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} + +pub struct MockFileMetadataManager; +impl FileMetadataInterface for MockFileMetadataManager { + type AccountId = AccountId; + type Metadata = FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type StorageDataUnit = u64; + + fn encode(metadata: &Self::Metadata) -> Vec { + metadata.encode() + } + + fn decode(data: &[u8]) -> Result { + as Decode>::decode(&mut &data[..]) + } + + fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { + metadata.file_size + } + + fn get_file_owner(metadata: &Self::Metadata) -> Result { + Self::AccountId::decode(&mut metadata.owner.as_slice()) + } +} + +pub struct MockRandomness; +impl Randomness> for MockRandomness { + fn random(subject: &[u8]) -> (H256, BlockNumberFor) { + // Simple randomness mock that changes each block but its randomness is only valid after 3 blocks + + // Concatenate the subject with the block number to get a unique hash for each block + let subject_concat_block = [ + subject, + &frame_system::Pallet::::block_number().to_le_bytes(), + ] + .concat(); + + let hashed_subject = blake2_256(&subject_concat_block); + + ( + H256::from_slice(&hashed_subject), + frame_system::Pallet::::block_number() + .saturating_sub(BLOCKS_BEFORE_RANDOMNESS_VALID), + ) + } +} + +/// This function is used to test the randomness of the providers pallet. +pub fn test_randomness_output( + who: &::AccountId, +) -> (::Hash, BlockNumberFor) { + ::ProvidersRandomness::random(who.encode().as_ref()) +} + +// Mocked list of Providers that submitted proofs that can be used to test the pallet. It just returns the block number passed to it as the only submitter. +pub struct MockSubmittingProviders; +impl ProofSubmittersInterface for MockSubmittingProviders { + type ProviderId = ::Hash; + type TickNumber = BlockNumberFor; + type MaxProofSubmitters = ConstU32<1000>; + fn get_proof_submitters_for_tick( + block_number: &Self::TickNumber, + ) -> Option> { + let mut set = BoundedBTreeSet::::new(); + // We convert the block number + 1 to the corresponding Provider ID, to simulate that the Provider submitted a proof + ::get_provider_id( + *block_number + 1, + ) + .map(|id| set.try_insert(id)); + Some(set) + } + + fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option { + SlashableProviders::::get(provider_id) + } + + fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) { + SlashableProviders::::remove(provider_id); + } +} + +// Build genesis storage according to the mock runtime. +pub fn _new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default() + .build_storage() + .unwrap() + .into() +} + +pub mod accounts { + use super::UNITS; + + pub const ALICE: (u64, u128) = (0, 5_000_000 * UNITS); + pub const BOB: (u64, u128) = (1, 10_000_000 * UNITS); + pub const CHARLIE: (u64, u128) = (2, 20_000_000 * UNITS); + pub const DAVID: (u64, u128) = (3, 30_000_000 * UNITS); + pub const EVE: (u64, u128) = (4, 400_000_000 * UNITS); + pub const FERDIE: (u64, u128) = (5, 5_000_000_000 * UNITS); + pub const GEORGE: (u64, u128) = (6, 600_000_000_000 * UNITS); +} + +// Externalities builder with predefined balances for accounts and starting at block number 1 +pub struct ExtBuilder; +impl ExtBuilder { + pub fn build() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + accounts::ALICE, + accounts::BOB, + accounts::CHARLIE, + accounts::DAVID, + accounts::EVE, + accounts::FERDIE, + accounts::GEORGE, + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/pallets/providers/src/utils.rs b/pallets/providers/src/utils.rs index 26b4560d7..936e42db6 100644 --- a/pallets/providers/src/utils.rs +++ b/pallets/providers/src/utils.rs @@ -1,1555 +1,1555 @@ -use crate::types::{Bucket, MainStorageProvider, MultiAddress, StorageProvider}; -use crate::*; -use codec::Encode; -use frame_support::{ - dispatch::{DispatchResultWithPostInfo, Pays}, - ensure, - pallet_prelude::DispatchResult, - sp_runtime::{ - traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating, Zero}, - ArithmeticError, BoundedVec, DispatchError, - }, - traits::{ - fungible::{Inspect, InspectHold, MutateHold}, - tokens::{Fortitude, Precision, Preservation, Restriction}, - Get, Randomness, - }, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use pallet_storage_providers_runtime_api::{ - GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, - QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, -}; -use shp_traits::{ - FileMetadataInterface, MutateBucketsInterface, MutateChallengeableProvidersInterface, - MutateProvidersInterface, MutateStorageProvidersInterface, PaymentStreamsInterface, - ProofSubmittersInterface, ReadBucketsInterface, ReadChallengeableProvidersInterface, - ReadProvidersInterface, ReadStorageProvidersInterface, SystemMetricsInterface, -}; -use sp_std::vec::Vec; -use types::{ProviderId, StorageProviderId}; - -macro_rules! expect_or_err { - // Handle Option type - ($optional:expr, $error_msg:expr, $error_type:path) => {{ - match $optional { - Some(value) => value, - None => { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - } - }}; - // Handle boolean type - ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ - if !$condition { - #[cfg(test)] - unreachable!($error_msg); - - #[allow(unreachable_code)] - { - Err($error_type)? - } - } - }}; -} - -impl Pallet -where - T: pallet::Config, -{ - /// This function holds the logic that checks if a user can request to sign up as a Main Storage Provider - /// and, if so, stores the request in the SignUpRequests mapping - pub fn do_request_msp_sign_up(msp_info: &MainStorageProvider) -> DispatchResult { - // todo!("If this comment is present, it means this function is still incomplete even though it compiles.") - - let who = &msp_info.owner_account; - - // Check that the user does not have a pending sign up request - ensure!( - SignUpRequests::::get(&who).is_none(), - Error::::SignUpRequestPending - ); - - // Check that the account is not already registered either as a Main Storage Provider or a Backup Storage Provider - ensure!( - AccountIdToMainStorageProviderId::::get(who).is_none() - && AccountIdToBackupStorageProviderId::::get(who).is_none(), - Error::::AlreadyRegistered - ); - - // Check that the multiaddresses vector is not empty (SPs have to register with at least one) - ensure!( - !msp_info.multiaddresses.is_empty(), - Error::::NoMultiAddress - ); - - // TODO: Check that the multiaddresses are valid - /* for multiaddress in msp_info.multiaddresses.iter() { - let multiaddress_vec = multiaddress.to_vec(); - let valid_multiaddress = Multiaddr::try_from(multiaddress_vec); - match valid_multiaddress { - Ok(_) => (), - Err(_) => return Err(Error::::InvalidMultiAddress.into()), - } - } */ - - // Check that the data to be stored is bigger than the minimum required by the runtime - ensure!( - msp_info.capacity >= T::SpMinCapacity::get(), - Error::::StorageTooLow - ); - - // Calculate how much deposit will the signer have to pay to register with this amount of data - let capacity_over_minimum = msp_info - .capacity - .checked_sub(&T::SpMinCapacity::get()) - .ok_or(Error::::StorageTooLow)?; - let deposit_for_capacity_over_minimum = T::DepositPerData::get() - .checked_mul(&capacity_over_minimum.into()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - let deposit = T::SpMinDeposit::get() - .checked_add(&deposit_for_capacity_over_minimum) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Check if the user has enough balance to pay the deposit - let user_balance = - T::NativeBalance::reducible_balance(who, Preservation::Preserve, Fortitude::Polite); - ensure!(user_balance >= deposit, Error::::NotEnoughBalance); - - // Check if we can hold the deposit from the user - ensure!( - T::NativeBalance::can_hold(&HoldReason::StorageProviderDeposit.into(), who, deposit), - Error::::CannotHoldDeposit - ); - - // Hold the deposit from the user - T::NativeBalance::hold(&HoldReason::StorageProviderDeposit.into(), who, deposit)?; - - // Store the sign up request in the SignUpRequests mapping - SignUpRequests::::insert( - who, - ( - StorageProvider::MainStorageProvider(msp_info.clone()), - frame_system::Pallet::::block_number(), - ), - ); - - Ok(()) - } - - /// This function holds the logic that checks if a user can request to sign up as a Backup Storage Provider - /// and, if so, stores the request in the SignUpRequests mapping - pub fn do_request_bsp_sign_up(bsp_info: &BackupStorageProvider) -> DispatchResult { - // todo!("If this comment is present, it means this function is still incomplete even though it compiles.") - - let who = &bsp_info.owner_account; - - // Check that the user does not have a pending sign up request - ensure!( - SignUpRequests::::get(&who).is_none(), - Error::::SignUpRequestPending - ); - - // Check that the account is not already registered either as a Main Storage Provider or a Backup Storage Provider - ensure!( - AccountIdToMainStorageProviderId::::get(who).is_none() - && AccountIdToBackupStorageProviderId::::get(who).is_none(), - Error::::AlreadyRegistered - ); - - // Check that the multiaddresses vector is not empty (SPs have to register with at least one) - ensure!( - !bsp_info.multiaddresses.is_empty(), - Error::::NoMultiAddress - ); - - // TODO: Check that the multiaddresses are valid - /* for multiaddress in bsp_info.multiaddresses.iter() { - let multiaddress_vec = multiaddress.to_vec(); - let valid_multiaddress = Multiaddr::try_from(multiaddress_vec); - match valid_multiaddress { - Ok(_) => (), - Err(_) => return Err(Error::::InvalidMultiAddress.into()), - } - } */ - - // Check that the data to be stored is bigger than the minimum required by the runtime - ensure!( - bsp_info.capacity >= T::SpMinCapacity::get(), - Error::::StorageTooLow - ); - - // Calculate how much deposit will the signer have to pay to register with this amount of data - let capacity_over_minimum = bsp_info - .capacity - .checked_sub(&T::SpMinCapacity::get()) - .ok_or(Error::::StorageTooLow)?; - let deposit_for_capacity_over_minimum = T::DepositPerData::get() - .checked_mul(&capacity_over_minimum.into()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - let deposit = T::SpMinDeposit::get() - .checked_add(&deposit_for_capacity_over_minimum) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Check if the user has enough balance to pay the deposit - let user_balance = - T::NativeBalance::reducible_balance(who, Preservation::Preserve, Fortitude::Polite); - ensure!(user_balance >= deposit, Error::::NotEnoughBalance); - - // Check if we can hold the deposit from the user - ensure!( - T::NativeBalance::can_hold(&HoldReason::StorageProviderDeposit.into(), who, deposit), - Error::::CannotHoldDeposit - ); - - // Hold the deposit from the user - T::NativeBalance::hold(&HoldReason::StorageProviderDeposit.into(), who, deposit)?; - - // Store the sign up request in the SignUpRequests mapping - SignUpRequests::::insert( - who, - ( - StorageProvider::BackupStorageProvider(bsp_info.clone()), - frame_system::Pallet::::block_number(), - ), - ); - - Ok(()) - } - - /// This function holds the logic that checks if a user can cancel a sign up request as a Storage Provider - /// and, if so, removes the request from the SignUpRequests mapping - pub fn do_cancel_sign_up(who: &T::AccountId) -> DispatchResult { - // Check that the signer has requested to sign up as a Storage Provider - SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested)?; - - // Remove the sign up request from the SignUpRequests mapping - SignUpRequests::::remove(who); - - // Return the deposit to the signer - // We return all held funds as there's no possibility of the user having another _valid_ hold with this pallet - T::NativeBalance::release_all( - &HoldReason::StorageProviderDeposit.into(), - who, - frame_support::traits::tokens::Precision::Exact, - )?; - - Ok(()) - } - - /// This function dispatches the logic to confirm the sign up of a user as a Storage Provider - /// It checks if the user has requested to sign up, and if so, it dispatches the corresponding logic - /// according to the type of Storage Provider that the user is trying to sign up as - pub fn do_confirm_sign_up(who: &T::AccountId) -> DispatchResult { - // Check that the signer has requested to sign up as a Storage Provider - let (sp, request_block) = - SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested)?; - - // Get the ProviderId by using the AccountId as the seed for a random generator - let (sp_id, block_number_when_random) = - T::ProvidersRandomness::random(who.encode().as_ref()); - - // Check that the maximum block number after which the randomness is invalid is greater than or equal to the block number when the - // request was made to ensure that the randomness was not known when the request was made - ensure!( - block_number_when_random >= request_block, - Error::::RandomnessNotValidYet - ); - - // Check what type of Storage Provider the signer is trying to sign up as and dispatch the corresponding logic - match sp { - StorageProvider::MainStorageProvider(msp_info) => { - Self::do_msp_sign_up(who, sp_id, &msp_info, request_block)?; - } - StorageProvider::BackupStorageProvider(bsp_info) => { - Self::do_bsp_sign_up(who, sp_id, &bsp_info, request_block)?; - } - } - - Ok(()) - } - - /// This function holds the logic that confirms the sign up of a user as a Main Storage Provider - /// It updates the storage to add the new Main Storage Provider, increments the counter of Main Storage Providers, - /// and removes the sign up request from the SignUpRequests mapping - pub fn do_msp_sign_up( - who: &T::AccountId, - msp_id: MainStorageProviderId, - msp_info: &MainStorageProvider, - request_block: BlockNumberFor, - ) -> DispatchResult { - // Check that the current block number is not greater than the block number when the request was made plus the maximum amount of - // blocks that we allow the user to wait for valid randomness (should be at least more than an epoch if using BABE's RandomnessFromOneEpochAgo) - // We do this to ensure that a user cannot wait indefinitely for randomness that suits them - ensure!( - frame_system::Pallet::::block_number() - < request_block + T::MaxBlocksForRandomness::get(), - Error::::SignUpRequestExpired - ); - - // Insert the MainStorageProviderId into the mapping - AccountIdToMainStorageProviderId::::insert(who, msp_id); - - // Save the MainStorageProvider information in storage - MainStorageProviders::::insert(&msp_id, msp_info); - - // Increment the counter of Main Storage Providers registered - let new_amount_of_msps = MspCount::::get() - .checked_add(&T::SpCount::one()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - MspCount::::set(new_amount_of_msps); - - // Remove the sign up request from the SignUpRequests mapping - SignUpRequests::::remove(who); - - // Emit the corresponding event - Self::deposit_event(Event::::MspSignUpSuccess { - who: who.clone(), - msp_id, - multiaddresses: msp_info.multiaddresses.clone(), - capacity: msp_info.capacity, - value_prop: msp_info.value_prop.clone(), - }); - - Ok(()) - } - - /// This function holds the logic that confirms the sign up of a user as a Backup Storage Provider - /// It updates the storage to add the new Backup Storage Provider, increments the counter of Backup Storage Providers, - /// increments the total capacity of the network (which is the sum of all BSPs capacities), and removes the sign up request - /// from the SignUpRequests mapping - pub fn do_bsp_sign_up( - who: &T::AccountId, - bsp_id: BackupStorageProviderId, - bsp_info: &BackupStorageProvider, - request_block: BlockNumberFor, - ) -> DispatchResult { - // Check that the current block number is not greater than the block number when the request was made plus the maximum amount of - // blocks that we allow the user to wait for valid randomness (should be at least more than an epoch if using BABE's RandomnessFromOneEpochAgo) - // We do this to ensure that a user cannot wait indefinitely for randomness that suits them - ensure!( - frame_system::Pallet::::block_number() - < request_block + T::MaxBlocksForRandomness::get(), - Error::::SignUpRequestExpired - ); - - // Insert the BackupStorageProviderId into the mapping - AccountIdToBackupStorageProviderId::::insert(who, bsp_id); - - // Save the BackupStorageProvider information in storage - BackupStorageProviders::::insert(&bsp_id, bsp_info.clone()); - - // Increment the total capacity of the network (which is the sum of all BSPs capacities) - TotalBspsCapacity::::mutate(|n| match n.checked_add(&bsp_info.capacity) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(()) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), - })?; - - // Increment the counter of Backup Storage Providers registered - let new_amount_of_bsps = BspCount::::get() - .checked_add(&T::SpCount::one()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - BspCount::::set(new_amount_of_bsps); - - // Remove the sign up request from the SignUpRequests mapping - SignUpRequests::::remove(who); - - // Increase global reputation weight - GlobalBspsReputationWeight::::mutate(|n| { - *n = n.saturating_add(bsp_info.reputation_weight); - }); - - // Emit the corresponding event - Self::deposit_event(Event::::BspSignUpSuccess { - who: who.clone(), - bsp_id, - multiaddresses: bsp_info.multiaddresses.clone(), - capacity: bsp_info.capacity, - }); - - Ok(()) - } - - /// This function holds the logic that checks if a user can sign off as a Main Storage Provider - /// and, if so, updates the storage to remove the user as a Main Storage Provider, decrements the counter of Main Storage Providers, - /// and returns the deposit to the user - pub fn do_msp_sign_off(who: &T::AccountId) -> Result, DispatchError> { - // Check that the signer is registered as a MSP and get its info - let msp_id = - AccountIdToMainStorageProviderId::::get(who).ok_or(Error::::NotRegistered)?; - - let msp = expect_or_err!( - MainStorageProviders::::get(&msp_id), - "MSP is registered (has a MSP ID), it should also have metadata", - Error::::SpRegisteredButDataNotFound - ); - - // Check that the MSP has no storage assigned to it (no buckets or data used by it) - ensure!( - msp.capacity_used == T::StorageDataUnit::zero(), - Error::::StorageStillInUse - ); - - // Update the MSPs storage, removing the signer as an MSP - AccountIdToMainStorageProviderId::::remove(who); - MainStorageProviders::::remove(&msp_id); - - // Return the deposit to the signer (if all funds cannot be returned, it will fail and revert with the reason) - T::NativeBalance::release_all( - &HoldReason::StorageProviderDeposit.into(), - who, - frame_support::traits::tokens::Precision::Exact, - )?; - - // Decrement the storage that holds total amount of MSPs currently in the system - MspCount::::mutate(|n| { - let new_amount_of_msps = n.checked_sub(&T::SpCount::one()); - match new_amount_of_msps { - Some(new_amount_of_msps) => { - *n = new_amount_of_msps; - Ok(msp_id) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), - } - })?; - - Ok(msp_id) - } - - /// This function holds the logic that checks if a user can sign off as a Backup Storage Provider - /// and, if so, updates the storage to remove the user as a Backup Storage Provider, decrements the counter of Backup Storage Providers, - /// decrements the total capacity of the network (which is the sum of all BSPs capacities), and returns the deposit to the user - pub fn do_bsp_sign_off( - who: &T::AccountId, - ) -> Result, DispatchError> { - // Check that the signer is registered as a BSP and get its info - let bsp_id = - AccountIdToBackupStorageProviderId::::get(who).ok_or(Error::::NotRegistered)?; - - let bsp = expect_or_err!( - BackupStorageProviders::::get(&bsp_id), - "BSP is registered (has a BSP ID), it should also have metadata", - Error::::SpRegisteredButDataNotFound - ); - - // Check that the BSP has no storage assigned to it (it is not currently storing any files) - ensure!( - bsp.capacity_used == T::StorageDataUnit::zero(), - Error::::StorageStillInUse - ); - - // Update the BSPs storage, removing the signer as an BSP - AccountIdToBackupStorageProviderId::::remove(who); - BackupStorageProviders::::remove(&bsp_id); - - // Update the total capacity of the network (which is the sum of all BSPs capacities) - TotalBspsCapacity::::mutate(|n| match n.checked_sub(&bsp.capacity) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(bsp_id) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), - })?; - - // Return the deposit to the signer (if all funds cannot be returned, it will fail and revert with the reason) - T::NativeBalance::release_all( - &HoldReason::StorageProviderDeposit.into(), - who, - frame_support::traits::tokens::Precision::Exact, - )?; - - // Decrement the storage that holds total amount of BSPs currently in the system - BspCount::::mutate(|n| { - let new_amount_of_bsps = n.checked_sub(&T::SpCount::one()); - match new_amount_of_bsps { - Some(new_amount_of_bsps) => { - *n = new_amount_of_bsps; - Ok(bsp_id) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), - } - })?; - - // Decrease global reputation weight - GlobalBspsReputationWeight::::mutate(|n| { - *n = n.saturating_sub(bsp.reputation_weight); - }); - - Ok(bsp_id) - } - - /// This function is in charge of dispatching the logic to change the capacity of a Storage Provider - /// It checks if the signer is registered as a SP and dispatches the corresponding function - /// that checks if the user can change its capacity and, if so, updates the storage to reflect the new capacity - pub fn do_change_capacity( - who: &T::AccountId, - new_capacity: StorageDataUnit, - ) -> Result<(StorageProviderId, StorageDataUnit), DispatchError> { - // Check that the new capacity is not zero (there are specific functions to sign off as a SP) - ensure!( - new_capacity != T::StorageDataUnit::zero(), - Error::::NewCapacityCantBeZero - ); - - // Check that the signer is registered as a SP and dispatch the corresponding function, getting its old capacity - let old_capacity = if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { - ( - StorageProviderId::MainStorageProvider(msp_id), - Self::do_change_capacity_msp(who, msp_id, new_capacity)?, - ) - } else if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who) { - ( - StorageProviderId::BackupStorageProvider(bsp_id), - Self::do_change_capacity_bsp(who, bsp_id, new_capacity)?, - ) - } else { - return Err(Error::::NotRegistered.into()); - }; - - Ok(old_capacity) - } - - /// This function holds the logic that checks if a user can change its capacity as a Main Storage Provider - /// and, if so, updates the storage to reflect the new capacity, modifying the user's deposit accordingly - /// and returning the old capacity if successful - pub fn do_change_capacity_msp( - account_id: &T::AccountId, - msp_id: MainStorageProviderId, - new_capacity: StorageDataUnit, - ) -> Result, DispatchError> { - // Check that the MSP is registered and get its info - let mut msp = MainStorageProviders::::get(&msp_id).ok_or(Error::::NotRegistered)?; - - // Check that the new capacity is different from the current capacity - ensure!( - new_capacity != msp.capacity, - Error::::NewCapacityEqualsCurrentCapacity - ); - - // Check that enough time has passed since the last capacity change - ensure!( - frame_system::Pallet::::block_number() - >= msp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get(), - Error::::NotEnoughTimePassed - ); - - // Check that the new capacity is bigger than the minimum required by the runtime - ensure!( - new_capacity >= T::SpMinCapacity::get(), - Error::::StorageTooLow - ); - - // Check that the new capacity is bigger than the current used capacity by the MSP - ensure!( - new_capacity >= msp.capacity_used, - Error::::NewCapacityLessThanUsedStorage - ); - - // Calculate how much deposit will the signer have to pay to register with this amount of data - let capacity_over_minimum = new_capacity - .checked_sub(&T::SpMinCapacity::get()) - .ok_or(Error::::StorageTooLow)?; - let deposit_for_capacity_over_minimum = T::DepositPerData::get() - .checked_mul(&capacity_over_minimum.into()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - let new_deposit = T::SpMinDeposit::get() - .checked_add(&deposit_for_capacity_over_minimum) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Check how much has the MSP already deposited for the current capacity - let current_deposit = T::NativeBalance::balance_on_hold( - &HoldReason::StorageProviderDeposit.into(), - account_id, - ); - - // Check if the new deposit is bigger or smaller than the current deposit - // Note: we do not check directly capacities as, for example, a bigger new_capacity could entail a smaller deposit - // because of changes in storage pricing, so we check the difference in deposits instead - if new_deposit > current_deposit { - // If the new deposit is bigger than the current deposit, more balance has to be held from the user - Self::hold_balance(account_id, current_deposit, new_deposit)?; - } else if new_deposit < current_deposit { - // If the new deposit is smaller than the current deposit, some balance has to be released to the user - Self::release_balance(account_id, current_deposit, new_deposit)?; - } - - // Get the MSP's old capacity - let old_capacity = msp.capacity; - - // Update the MSP's storage, modifying the capacity and the last capacity change block number - msp.capacity = new_capacity; - msp.last_capacity_change = frame_system::Pallet::::block_number(); - MainStorageProviders::::insert(&msp_id, msp); - - // Return the old capacity - Ok(old_capacity) - } - - /// This function holds the logic that checks if a user can change its capacity as a Backup Storage Provider - /// and, if so, updates the storage to reflect the new capacity, modifying the user's deposit accordingly - /// and returning the old capacity if successful - pub fn do_change_capacity_bsp( - account_id: &T::AccountId, - bsp_id: BackupStorageProviderId, - new_capacity: StorageDataUnit, - ) -> Result, DispatchError> { - // Check that the BSP is registered and get its info - let mut bsp = BackupStorageProviders::::get(&bsp_id).ok_or(Error::::NotRegistered)?; - - // Check that the new capacity is different from the current capacity - ensure!( - new_capacity != bsp.capacity, - Error::::NewCapacityEqualsCurrentCapacity - ); - - // Check that enough time has passed since the last capacity change - ensure!( - frame_system::Pallet::::block_number() - >= bsp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get(), - Error::::NotEnoughTimePassed - ); - - // Check that the new capacity is bigger than the minimum required by the runtime - ensure!( - new_capacity >= T::SpMinCapacity::get(), - Error::::StorageTooLow - ); - - // Check that the new capacity is bigger than the current used capacity by the BSP - ensure!( - new_capacity >= bsp.capacity_used, - Error::::NewCapacityLessThanUsedStorage - ); - - // Calculate how much deposit will the signer have to pay to register with this amount of data - let capacity_over_minimum = new_capacity - .checked_sub(&T::SpMinCapacity::get()) - .ok_or(Error::::StorageTooLow)?; - let deposit_for_capacity_over_minimum = T::DepositPerData::get() - .checked_mul(&capacity_over_minimum.into()) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - let new_deposit = T::SpMinDeposit::get() - .checked_add(&deposit_for_capacity_over_minimum) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; - - // Check how much has the used already deposited for the current capacity - let current_deposit = T::NativeBalance::balance_on_hold( - &HoldReason::StorageProviderDeposit.into(), - account_id, - ); - - // Check if the new deposit is bigger or smaller than the current deposit - // Note: we do not check directly capacities as, for example, a bigger new_capacity could entail a smaller deposit - // because of changes in storage pricing, so we check the difference in deposits instead - if new_deposit > current_deposit { - // If the new deposit is bigger than the current deposit, more balance has to be held from the user - Self::hold_balance(account_id, current_deposit, new_deposit)?; - } else if new_deposit < current_deposit { - // If the new deposit is smaller than the current deposit, some balance has to be released to the user - Self::release_balance(account_id, current_deposit, new_deposit)?; - } - - // Get the BSP's old capacity - let old_capacity = bsp.capacity; - - // Update the total capacity of the network (which is the sum of all BSPs capacities) - if new_capacity > old_capacity { - // If the new capacity is bigger than the old capacity, get the difference doing new_capacity - old_capacity - let difference = new_capacity - .checked_sub(&old_capacity) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; - // Increment the total capacity of the network by the difference - TotalBspsCapacity::::mutate(|n| match n.checked_add(&difference) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(()) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), - })?; - } else { - // If the new capacity is smaller than the old capacity, get the difference doing old_capacity - new_capacity - let difference = old_capacity - .checked_sub(&new_capacity) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; - // Decrement the total capacity of the network - TotalBspsCapacity::::mutate(|n| match n.checked_sub(&difference) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(()) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), - })?; - } - - // Update the BSP's storage, modifying the capacity and the last capacity change block number - bsp.capacity = new_capacity; - bsp.last_capacity_change = frame_system::Pallet::::block_number(); - BackupStorageProviders::::insert(&bsp_id, bsp); - - // Return the old capacity - Ok(old_capacity) - } - - /// Slash a Storage Provider. - /// - /// The amount slashed is calculated as the product of the [`SlashAmountPerChunkOfStorageData`] and the accrued failed proof submissions. - /// The amount is then slashed from the Storage Provider's held deposit and transferred to the treasury. - /// - /// This will return an error when the Storage Provider is not slashable. In the context of the StorageHub protocol, - /// a Storage Provider is slashable when the proofs-dealer pallet has marked them as such. - /// - /// Successfully slashing a Storage Provider should be a free operation. - pub(crate) fn do_slash(provider_id: &HashId) -> DispatchResultWithPostInfo { - let account_id = if let Some(provider) = MainStorageProviders::::get(provider_id) { - provider.owner_account - } else if let Some(provider) = BackupStorageProviders::::get(provider_id) { - provider.owner_account - } else { - return Err(Error::::ProviderNotSlashable.into()); - }; - - // Calculate slashable amount. - // Doubling the slash for each failed proof submission is necessary since it is more probabilistic for a Storage Provider to have - // responded with two file key proofs given a random or custom challenge. - let slashable_amount = Self::compute_worst_case_scenario_slashable_amount(provider_id)?; - - let amount_slashed = T::NativeBalance::transfer_on_hold( - &HoldReason::StorageProviderDeposit.into(), - &account_id, - &T::Treasury::get(), - slashable_amount, - Precision::BestEffort, - Restriction::Free, - Fortitude::Polite, - )?; - - // Clear the accrued failed proof submissions for the Storage Provider - ::clear_accrued_failed_proof_submissions(&provider_id); - - // Provider held funds have been completely depleted. - if amount_slashed <= slashable_amount { - // TODO: Force sign off the provider. - } - - Self::deposit_event(Event::::Slashed { - provider_id: *provider_id, - amount_slashed, - }); - - Ok(Pays::No.into()) - } - - fn hold_balance( - account_id: &T::AccountId, - previous_deposit: BalanceOf, - new_deposit: BalanceOf, - ) -> DispatchResult { - // Get the user's reducible balance - let user_balance = T::NativeBalance::reducible_balance( - account_id, - Preservation::Preserve, - Fortitude::Polite, - ); - - // Get the difference between the new deposit and the current deposit - let difference = new_deposit - .checked_sub(&previous_deposit) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; - - // Check if the user has enough balance to pay the difference - ensure!(user_balance >= difference, Error::::NotEnoughBalance); - - // Check if we can hold the difference from the user - ensure!( - T::NativeBalance::can_hold( - &HoldReason::StorageProviderDeposit.into(), - account_id, - difference, - ), - Error::::CannotHoldDeposit - ); - - // Hold the difference from the user - T::NativeBalance::hold( - &HoldReason::StorageProviderDeposit.into(), - account_id, - difference, - )?; - - Ok(()) - } - - fn release_balance( - account_id: &T::AccountId, - previous_deposit: BalanceOf, - new_deposit: BalanceOf, - ) -> DispatchResult { - // Get the difference between the current deposit and the new deposit - let difference = previous_deposit - .checked_sub(&new_deposit) - .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; - - // Release the difference from the user - T::NativeBalance::release( - &HoldReason::StorageProviderDeposit.into(), - account_id, - difference, - Precision::Exact, - )?; - - Ok(()) - } - - /// Compute the worst case scenario slashable amount for a Storage Provider. - /// - /// Every failed proof submission counts as for two files which should have been proven due to the low probability of a challenge - /// being an exact match to a file key stored by the Storage Provider. The StorageHub protocol requires the Storage Provider to - /// submit a proof of storage for the neighbouring file keys of the missing challenged file key. - /// - /// The slashing amount is calculated based on an assumption that every file is the maximum size allowed by the protocol. - pub fn compute_worst_case_scenario_slashable_amount( - provider_id: &HashId, - ) -> Result, DispatchError> { - let accrued_failed_submission_count = ::get_accrued_failed_proof_submissions(&provider_id) - .ok_or(Error::::ProviderNotSlashable)?.into(); - - Ok(T::SlashAmountPerMaxFileSize::get() - .saturating_mul(accrued_failed_submission_count) - .saturating_mul(2u32.into())) - } -} - -impl From> for BackupStorageProvider { - fn from(msp: MainStorageProvider) -> Self { - BackupStorageProvider { - capacity: msp.capacity, - capacity_used: msp.capacity_used, - multiaddresses: msp.multiaddresses, - root: T::DefaultMerkleRoot::get(), - last_capacity_change: msp.last_capacity_change, - owner_account: msp.owner_account, - payment_account: msp.payment_account, - reputation_weight: T::StartingReputationWeight::get(), - } - } -} - -/// Implement the ReadBucketsInterface trait for the Storage Providers pallet. -impl ReadBucketsInterface for pallet::Pallet { - type AccountId = T::AccountId; - type BucketId = BucketId; - type BucketNameLimit = T::BucketNameLimit; - type ProviderId = HashId; - type ReadAccessGroupId = T::ReadAccessGroupId; - type MerkleHash = MerklePatriciaRoot; - type StorageDataUnit = T::StorageDataUnit; - - fn bucket_exists(bucket_id: &Self::BucketId) -> bool { - Buckets::::contains_key(bucket_id) - } - - fn derive_bucket_id( - msp_id: &Self::ProviderId, - owner: &Self::AccountId, - bucket_name: BoundedVec, - ) -> Self::BucketId { - let concat = msp_id - .encode() - .into_iter() - .chain( - owner - .encode() - .into_iter() - .chain(bucket_name.encode().into_iter()), - ) - .collect::>(); - - <::Hashing as sp_runtime::traits::Hash>::hash(&concat) - } - - fn get_msp_of_bucket(bucket_id: &Self::BucketId) -> Option { - Buckets::::get(bucket_id).map(|bucket| bucket.msp_id) - } - - fn get_read_access_group_id_of_bucket( - bucket_id: &Self::BucketId, - ) -> Result, DispatchError> { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(bucket.read_access_group_id) - } - - fn is_bucket_owner( - who: &Self::AccountId, - bucket_id: &Self::BucketId, - ) -> Result { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(&bucket.user_id == who) - } - - fn is_bucket_private(bucket_id: &Self::BucketId) -> Result { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(bucket.private) - } - - fn is_bucket_stored_by_msp(msp_id: &Self::ProviderId, bucket_id: &Self::BucketId) -> bool { - if let Some(bucket) = Buckets::::get(bucket_id) { - bucket.msp_id == *msp_id - } else { - false - } - } - - fn get_root_bucket(bucket_id: &Self::BucketId) -> Option { - Buckets::::get(bucket_id).map(|bucket| bucket.root) - } - - fn get_bucket_owner(bucket_id: &Self::BucketId) -> Result { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(bucket.user_id) - } - - fn get_bucket_size(bucket_id: &Self::BucketId) -> Result { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(bucket.size) - } - - fn get_msp_bucket(bucket_id: &Self::BucketId) -> Result { - let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; - Ok(bucket.msp_id) - } -} - -/// Implement the MutateBucketsInterface trait for the Storage Providers pallet. -impl MutateBucketsInterface for pallet::Pallet { - type AccountId = T::AccountId; - type BucketId = BucketId; - type ProviderId = HashId; - type ReadAccessGroupId = T::ReadAccessGroupId; - type MerkleHash = MerklePatriciaRoot; - type StorageDataUnit = T::StorageDataUnit; - - fn add_bucket( - provider_id: Self::ProviderId, - user_id: Self::AccountId, - bucket_id: Self::BucketId, - privacy: bool, - maybe_read_access_group_id: Option, - ) -> DispatchResult { - // Check if bucket already exists - ensure!( - !Buckets::::contains_key(&bucket_id), - Error::::BucketAlreadyExists - ); - - // Check if the MSP exists - ensure!( - MainStorageProviders::::contains_key(&provider_id), - Error::::NotRegistered - ); - - let user_balance = T::NativeBalance::reducible_balance( - &user_id, - Preservation::Preserve, - Fortitude::Polite, - ); - - let deposit = T::BucketDeposit::get(); - ensure!(user_balance >= deposit, Error::::NotEnoughBalance); - ensure!( - T::NativeBalance::can_hold(&HoldReason::BucketDeposit.into(), &user_id, deposit), - Error::::CannotHoldDeposit - ); - - // Hold the bucket deposit - T::NativeBalance::hold(&HoldReason::BucketDeposit.into(), &user_id, deposit)?; - - let bucket = Bucket { - root: T::DefaultMerkleRoot::get(), - msp_id: provider_id, - private: privacy, - read_access_group_id: maybe_read_access_group_id, - user_id, - size: T::StorageDataUnit::zero(), - }; - - Buckets::::insert(&bucket_id, &bucket); - - MainStorageProviderIdsToBuckets::::try_append(&provider_id, bucket_id) - .map_err(|_| Error::::AppendBucketToMspFailed)?; - - Ok(()) - } - - fn change_msp_bucket(bucket_id: &Self::BucketId, new_msp: &Self::ProviderId) -> DispatchResult { - Buckets::::try_mutate(bucket_id, |bucket| { - let bucket = bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.msp_id = *new_msp; - - Ok(()) - }) - } - - fn change_root_bucket(bucket_id: Self::BucketId, new_root: Self::MerkleHash) -> DispatchResult { - Buckets::::try_mutate(&bucket_id, |bucket| { - let bucket = bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.root = new_root; - - Ok(()) - }) - } - - fn remove_root_bucket(bucket_id: Self::BucketId) -> DispatchResult { - let bucket = Buckets::::take(&bucket_id).ok_or(Error::::BucketNotFound)?; - - MainStorageProviderIdsToBuckets::::mutate_exists( - &bucket.msp_id, - |buckets| match buckets { - Some(b) => { - b.retain(|b| b != &bucket_id); - - if b.is_empty() { - *buckets = None; - } - } - _ => {} - }, - ); - - // Release the bucket deposit hold - T::NativeBalance::release( - &HoldReason::BucketDeposit.into(), - &bucket.user_id, - T::BucketDeposit::get(), - Precision::Exact, - )?; - - Ok(()) - } - - fn update_bucket_privacy(bucket_id: Self::BucketId, privacy: bool) -> DispatchResult { - Buckets::::try_mutate(&bucket_id, |maybe_bucket| { - let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.private = privacy; - - Ok(()) - }) - } - - fn update_bucket_read_access_group_id( - bucket_id: Self::BucketId, - maybe_read_access_group_id: Option, - ) -> DispatchResult { - Buckets::::try_mutate(&bucket_id, |maybe_bucket| { - let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.read_access_group_id = maybe_read_access_group_id; - - Ok(()) - }) - } - - fn increase_bucket_size( - bucket_id: &Self::BucketId, - delta: Self::StorageDataUnit, - ) -> DispatchResult { - Buckets::::try_mutate(&bucket_id, |maybe_bucket| { - let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.size = bucket.size.saturating_add(delta); - - Ok(()) - }) - } - - fn decrease_bucket_size( - bucket_id: &Self::BucketId, - delta: Self::StorageDataUnit, - ) -> DispatchResult { - Buckets::::try_mutate(&bucket_id, |maybe_bucket| { - let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; - bucket.size = bucket.size.saturating_sub(delta); - - Ok(()) - }) - } -} - -/// Implement the ReadStorageProvidersInterface trait for the Storage Providers pallet. -impl ReadStorageProvidersInterface for pallet::Pallet { - type ProviderId = HashId; - type StorageDataUnit = T::StorageDataUnit; - type SpCount = T::SpCount; - type MultiAddress = MultiAddress; - type MaxNumberOfMultiAddresses = T::MaxMultiAddressAmount; - type ReputationWeight = T::ReputationWeightType; - - fn is_bsp(who: &Self::ProviderId) -> bool { - BackupStorageProviders::::contains_key(&who) - } - - fn is_msp(who: &Self::ProviderId) -> bool { - MainStorageProviders::::contains_key(&who) - } - - fn get_global_bsps_reputation_weight() -> Self::ReputationWeight { - GlobalBspsReputationWeight::::get() - } - - fn get_bsp_reputation_weight( - who: &Self::ProviderId, - ) -> Result { - if let Some(bsp) = BackupStorageProviders::::get(who) { - Ok(bsp.reputation_weight) - } else { - Err(Error::::NotRegistered.into()) - } - } - - fn get_number_of_bsps() -> Self::SpCount { - Self::get_bsp_count() - } - - fn get_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { - if let Some(bsp) = BackupStorageProviders::::get(who) { - bsp.capacity - } else if let Some(msp) = MainStorageProviders::::get(who) { - msp.capacity - } else { - Zero::zero() - } - } - - fn get_used_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { - if let Some(bsp) = BackupStorageProviders::::get(who) { - bsp.capacity_used - } else if let Some(msp) = MainStorageProviders::::get(who) { - msp.capacity_used - } else { - Zero::zero() - } - } - - fn available_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { - if let Some(bsp) = BackupStorageProviders::::get(who) { - bsp.capacity.saturating_sub(bsp.capacity_used) - } else if let Some(msp) = MainStorageProviders::::get(who) { - msp.capacity.saturating_sub(msp.capacity_used) - } else { - Zero::zero() - } - } - - fn get_bsp_multiaddresses( - who: &Self::ProviderId, - ) -> Result, DispatchError> - { - if let Some(bsp) = BackupStorageProviders::::get(who) { - Ok(BoundedVec::from(bsp.multiaddresses)) - } else { - Err(Error::::NotRegistered.into()) - } - } -} - -/// Implement the MutateStorageProvidersInterface trait for the Storage Providers pallet. -impl MutateStorageProvidersInterface for pallet::Pallet { - type ProviderId = HashId; - type StorageDataUnit = T::StorageDataUnit; - - fn decrease_capacity_used( - provider_id: &Self::ProviderId, - delta: Self::StorageDataUnit, - ) -> DispatchResult { - if MainStorageProviders::::contains_key(&provider_id) { - let mut msp = - MainStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; - msp.capacity_used = msp.capacity_used.saturating_sub(delta); - MainStorageProviders::::insert(&provider_id, msp); - } else if BackupStorageProviders::::contains_key(&provider_id) { - let mut bsp = - BackupStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; - bsp.capacity_used = bsp.capacity_used.saturating_sub(delta); - BackupStorageProviders::::insert(&provider_id, bsp); - UsedBspsCapacity::::mutate(|n| match n.checked_sub(&delta) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(()) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), - })?; - } else { - return Err(Error::::NotRegistered.into()); - } - Ok(()) - } - - fn increase_capacity_used( - provider_id: &Self::ProviderId, - delta: Self::StorageDataUnit, - ) -> DispatchResult { - if MainStorageProviders::::contains_key(&provider_id) { - let mut msp = - MainStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; - - let new_used_capacity = msp.capacity_used.saturating_add(delta); - if msp.capacity < new_used_capacity { - return Err(Error::::NewUsedCapacityExceedsStorageCapacity.into()); - } - msp.capacity_used = new_used_capacity; - MainStorageProviders::::insert(&provider_id, msp); - } else if BackupStorageProviders::::contains_key(&provider_id) { - let mut bsp = - BackupStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; - bsp.capacity_used = bsp.capacity_used.saturating_add(delta); - BackupStorageProviders::::insert(&provider_id, bsp); - UsedBspsCapacity::::mutate(|n| match n.checked_add(&delta) { - Some(new_total_bsp_capacity) => { - *n = new_total_bsp_capacity; - Ok(()) - } - None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), - })?; - } else { - return Err(Error::::NotRegistered.into()); - } - Ok(()) - } -} - -/// Implement the ReadProvidersInterface for the Storage Providers pallet. -impl ReadProvidersInterface for pallet::Pallet { - type AccountId = T::AccountId; - type Balance = T::NativeBalance; - type MerkleHash = MerklePatriciaRoot; - type ProviderId = HashId; - - fn get_default_root() -> Self::MerkleHash { - T::DefaultMerkleRoot::get() - } - - fn get_owner_account(who: Self::ProviderId) -> Option { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(bsp.owner_account) - } else if let Some(msp) = MainStorageProviders::::get(&who) { - Some(msp.owner_account) - } else if let Some(bucket) = Buckets::::get(&who) { - let msp_for_bucket = bucket.msp_id; - if let Some(msp) = MainStorageProviders::::get(&msp_for_bucket) { - Some(msp.owner_account) - } else { - None - } - } else { - None - } - } - - fn get_payment_account(who: Self::ProviderId) -> Option { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(bsp.payment_account) - } else if let Some(msp) = MainStorageProviders::::get(&who) { - Some(msp.payment_account) - } else { - None - } - } - - fn get_provider_id(who: Self::AccountId) -> Option { - if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who.clone()) { - Some(bsp_id) - } else if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { - Some(msp_id) - } else { - None - } - } - - fn get_root(who: Self::ProviderId) -> Option { - if let Some(bucket) = Buckets::::get(&who) { - Some(bucket.root) - } else if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(bsp.root) - } else { - None - } - } - - fn get_stake( - who: Self::ProviderId, - ) -> Option<>::Balance> - { - if let Some(bucket) = Buckets::::get(&who) { - match MainStorageProviders::::get(bucket.msp_id) { - Some(related_msp) => Some(T::NativeBalance::balance_on_hold( - &HoldReason::BucketDeposit.into(), - &related_msp.owner_account, - )), - None => None, - } - } else if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(T::NativeBalance::balance_on_hold( - &HoldReason::StorageProviderDeposit.into(), - &bsp.owner_account, - )) - } else { - None - } - } - - fn is_provider(who: Self::ProviderId) -> bool { - BackupStorageProviders::::contains_key(&who) - || MainStorageProviders::::contains_key(&who) - || Buckets::::contains_key(&who) - } -} - -/// Implement the MutateProvidersInterface for the Storage Providers pallet. -impl MutateProvidersInterface for pallet::Pallet { - type MerkleHash = MerklePatriciaRoot; - type ProviderId = HashId; - - fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult { - if let Some(bucket) = Buckets::::get(&who) { - Buckets::::insert( - &who, - Bucket { - root: new_root, - ..bucket - }, - ); - } else if let Some(bsp) = BackupStorageProviders::::get(&who) { - BackupStorageProviders::::insert( - &who, - BackupStorageProvider { - root: new_root, - ..bsp - }, - ); - } else { - return Err(Error::::NotRegistered.into()); - } - Ok(()) - } -} - -/// Implement the ReadChallengeableProvidersInterface for the Storage Providers pallet. -impl ReadChallengeableProvidersInterface for pallet::Pallet { - type AccountId = T::AccountId; - type Balance = T::NativeBalance; - type MerkleHash = MerklePatriciaRoot; - type ProviderId = HashId; - - fn get_default_root() -> Self::MerkleHash { - T::DefaultMerkleRoot::get() - } - - fn get_owner_account(who: Self::ProviderId) -> Option { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(bsp.owner_account) - } else { - None - } - } - - fn get_provider_id(who: Self::AccountId) -> Option { - if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who.clone()) { - Some(bsp_id) - } else { - None - } - } - - fn get_root(who: Self::ProviderId) -> Option { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(bsp.root) - } else { - None - } - } - - fn get_stake( - who: Self::ProviderId, - ) -> Option<>::Balance> - { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - Some(T::NativeBalance::balance_on_hold( - &HoldReason::StorageProviderDeposit.into(), - &bsp.owner_account, - )) - } else { - None - } - } - - fn is_provider(who: Self::ProviderId) -> bool { - BackupStorageProviders::::contains_key(&who) - } - - fn get_min_stake( - ) -> >::Balance { - T::SpMinDeposit::get() - } -} - -/// Implement the MutateChallengeableProvidersInterface for the Storage Providers pallet. -impl MutateChallengeableProvidersInterface for pallet::Pallet { - type MerkleHash = MerklePatriciaRoot; - type ProviderId = HashId; - - fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult { - if let Some(bsp) = BackupStorageProviders::::get(&who) { - BackupStorageProviders::::insert( - &who, - BackupStorageProvider { - root: new_root, - ..bsp - }, - ); - } else { - return Err(Error::::NotRegistered.into()); - } - Ok(()) - } - - fn update_provider_after_key_removal( - who: &Self::ProviderId, - removed_trie_value: &Vec, - ) -> DispatchResult { - // Get the removed file's metadata - let file_metadata = - <::FileMetadataManager as FileMetadataInterface>::decode( - removed_trie_value, - ) - .map_err(|_| Error::::InvalidEncodedFileMetadata)?; - - // Get the file size as a StorageDataUnit type and the owner as an AccountId type - let file_size = - <::FileMetadataManager as FileMetadataInterface>::get_file_size( - &file_metadata, - ); - let owner = - <::FileMetadataManager as FileMetadataInterface>::get_file_owner( - &file_metadata, - ) - .map_err(|_| Error::::InvalidEncodedAccountId)?; - - // Decrease the used capacity of the provider - Self::decrease_capacity_used(who, file_size)?; - - // Update the provider's payment stream with the user - let previous_amount_provided = - ::get_dynamic_rate_payment_stream_amount_provided( - who, - &owner, - ) - .ok_or(Error::::PaymentStreamNotFound)?; - let new_amount_provided = previous_amount_provided.saturating_sub(file_size); - ::update_dynamic_rate_payment_stream( - who, - &owner, - &new_amount_provided, - )?; - - Ok(()) - } -} - -/// Implement the SystemMetricsInterface for the Storage Providers pallet. -impl SystemMetricsInterface for pallet::Pallet { - type ProvidedUnit = StorageDataUnit; - - fn get_total_capacity() -> Self::ProvidedUnit { - Self::get_total_bsp_capacity() - } - - fn get_total_used_capacity() -> Self::ProvidedUnit { - Self::get_used_bsp_capacity() - } -} - -/// Runtime API implementation for the Storage Providers pallet. -impl Pallet -where - T: pallet::Config, -{ - pub fn get_bsp_info( - bsp_id: &BackupStorageProviderId, - ) -> Result, GetBspInfoError> { - BackupStorageProviders::::get(bsp_id).ok_or(GetBspInfoError::BspNotRegistered) - } - - pub fn get_storage_provider_id(who: &T::AccountId) -> Option> { - if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who) { - Some(StorageProviderId::BackupStorageProvider(bsp_id)) - } else if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { - Some(StorageProviderId::MainStorageProvider(msp_id)) - } else { - None - } - } - - pub fn query_storage_provider_capacity( - provider_id: &ProviderId, - ) -> Result, QueryStorageProviderCapacityError> { - if MainStorageProviders::::contains_key(provider_id) { - let msp = MainStorageProviders::::get(provider_id) - .ok_or(QueryStorageProviderCapacityError::ProviderNotRegistered)?; - Ok(msp.capacity) - } else if BackupStorageProviders::::contains_key(provider_id) { - let bsp = BackupStorageProviders::::get(provider_id) - .ok_or(QueryStorageProviderCapacityError::ProviderNotRegistered)?; - Ok(bsp.capacity) - } else { - Err(QueryStorageProviderCapacityError::ProviderNotRegistered) - } - } - - pub fn query_available_storage_capacity( - provider_id: &ProviderId, - ) -> Result, QueryAvailableStorageCapacityError> { - if MainStorageProviders::::contains_key(provider_id) { - let msp = MainStorageProviders::::get(provider_id) - .ok_or(QueryAvailableStorageCapacityError::ProviderNotRegistered)?; - Ok(msp.capacity.saturating_sub(msp.capacity_used)) - } else if BackupStorageProviders::::contains_key(provider_id) { - let bsp = BackupStorageProviders::::get(provider_id) - .ok_or(QueryAvailableStorageCapacityError::ProviderNotRegistered)?; - Ok(bsp.capacity.saturating_sub(bsp.capacity_used)) - } else { - Err(QueryAvailableStorageCapacityError::ProviderNotRegistered) - } - } - - pub fn query_earliest_change_capacity_block( - provider_id: &BackupStorageProviderId, - ) -> Result, QueryEarliestChangeCapacityBlockError> { - let bsp = BackupStorageProviders::::get(provider_id) - .ok_or(QueryEarliestChangeCapacityBlockError::ProviderNotRegistered)?; - Ok(bsp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get()) - } - - pub fn get_worst_case_scenario_slashable_amount( - provider_id: &ProviderId, - ) -> Result, DispatchError> { - Self::compute_worst_case_scenario_slashable_amount(provider_id) - } - - pub fn get_slash_amount_per_max_file_size() -> BalanceOf { - T::SlashAmountPerMaxFileSize::get() - } - - pub fn query_msp_id_of_bucket_id( - bucket_id: &BucketId, - ) -> Result, QueryMspIdOfBucketIdError> { - let bucket = - Buckets::::get(bucket_id).ok_or(QueryMspIdOfBucketIdError::BucketNotFound)?; - Ok(bucket.msp_id) - } -} +use crate::types::{Bucket, MainStorageProvider, MultiAddress, StorageProvider}; +use crate::*; +use codec::Encode; +use frame_support::{ + dispatch::{DispatchResultWithPostInfo, Pays}, + ensure, + pallet_prelude::DispatchResult, + sp_runtime::{ + traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating, Zero}, + ArithmeticError, BoundedVec, DispatchError, + }, + traits::{ + fungible::{Inspect, InspectHold, MutateHold}, + tokens::{Fortitude, Precision, Preservation, Restriction}, + Get, Randomness, + }, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use pallet_storage_providers_runtime_api::{ + GetBspInfoError, QueryAvailableStorageCapacityError, QueryEarliestChangeCapacityBlockError, + QueryMspIdOfBucketIdError, QueryStorageProviderCapacityError, +}; +use shp_traits::{ + FileMetadataInterface, MutateBucketsInterface, MutateChallengeableProvidersInterface, + MutateProvidersInterface, MutateStorageProvidersInterface, PaymentStreamsInterface, + ProofSubmittersInterface, ReadBucketsInterface, ReadChallengeableProvidersInterface, + ReadProvidersInterface, ReadStorageProvidersInterface, SystemMetricsInterface, +}; +use sp_std::vec::Vec; +use types::{ProviderId, StorageProviderId}; + +macro_rules! expect_or_err { + // Handle Option type + ($optional:expr, $error_msg:expr, $error_type:path) => {{ + match $optional { + Some(value) => value, + None => { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + } + }}; + // Handle boolean type + ($condition:expr, $error_msg:expr, $error_type:path, bool) => {{ + if !$condition { + #[cfg(test)] + unreachable!($error_msg); + + #[allow(unreachable_code)] + { + Err($error_type)? + } + } + }}; +} + +impl Pallet +where + T: pallet::Config, +{ + /// This function holds the logic that checks if a user can request to sign up as a Main Storage Provider + /// and, if so, stores the request in the SignUpRequests mapping + pub fn do_request_msp_sign_up(msp_info: &MainStorageProvider) -> DispatchResult { + // todo!("If this comment is present, it means this function is still incomplete even though it compiles.") + + let who = &msp_info.owner_account; + + // Check that the user does not have a pending sign up request + ensure!( + SignUpRequests::::get(&who).is_none(), + Error::::SignUpRequestPending + ); + + // Check that the account is not already registered either as a Main Storage Provider or a Backup Storage Provider + ensure!( + AccountIdToMainStorageProviderId::::get(who).is_none() + && AccountIdToBackupStorageProviderId::::get(who).is_none(), + Error::::AlreadyRegistered + ); + + // Check that the multiaddresses vector is not empty (SPs have to register with at least one) + ensure!( + !msp_info.multiaddresses.is_empty(), + Error::::NoMultiAddress + ); + + // TODO: Check that the multiaddresses are valid + /* for multiaddress in msp_info.multiaddresses.iter() { + let multiaddress_vec = multiaddress.to_vec(); + let valid_multiaddress = Multiaddr::try_from(multiaddress_vec); + match valid_multiaddress { + Ok(_) => (), + Err(_) => return Err(Error::::InvalidMultiAddress.into()), + } + } */ + + // Check that the data to be stored is bigger than the minimum required by the runtime + ensure!( + msp_info.capacity >= T::SpMinCapacity::get(), + Error::::StorageTooLow + ); + + // Calculate how much deposit will the signer have to pay to register with this amount of data + let capacity_over_minimum = msp_info + .capacity + .checked_sub(&T::SpMinCapacity::get()) + .ok_or(Error::::StorageTooLow)?; + let deposit_for_capacity_over_minimum = T::DepositPerData::get() + .checked_mul(&capacity_over_minimum.into()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + let deposit = T::SpMinDeposit::get() + .checked_add(&deposit_for_capacity_over_minimum) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Check if the user has enough balance to pay the deposit + let user_balance = + T::NativeBalance::reducible_balance(who, Preservation::Preserve, Fortitude::Polite); + ensure!(user_balance >= deposit, Error::::NotEnoughBalance); + + // Check if we can hold the deposit from the user + ensure!( + T::NativeBalance::can_hold(&HoldReason::StorageProviderDeposit.into(), who, deposit), + Error::::CannotHoldDeposit + ); + + // Hold the deposit from the user + T::NativeBalance::hold(&HoldReason::StorageProviderDeposit.into(), who, deposit)?; + + // Store the sign up request in the SignUpRequests mapping + SignUpRequests::::insert( + who, + ( + StorageProvider::MainStorageProvider(msp_info.clone()), + frame_system::Pallet::::block_number(), + ), + ); + + Ok(()) + } + + /// This function holds the logic that checks if a user can request to sign up as a Backup Storage Provider + /// and, if so, stores the request in the SignUpRequests mapping + pub fn do_request_bsp_sign_up(bsp_info: &BackupStorageProvider) -> DispatchResult { + // todo!("If this comment is present, it means this function is still incomplete even though it compiles.") + + let who = &bsp_info.owner_account; + + // Check that the user does not have a pending sign up request + ensure!( + SignUpRequests::::get(&who).is_none(), + Error::::SignUpRequestPending + ); + + // Check that the account is not already registered either as a Main Storage Provider or a Backup Storage Provider + ensure!( + AccountIdToMainStorageProviderId::::get(who).is_none() + && AccountIdToBackupStorageProviderId::::get(who).is_none(), + Error::::AlreadyRegistered + ); + + // Check that the multiaddresses vector is not empty (SPs have to register with at least one) + ensure!( + !bsp_info.multiaddresses.is_empty(), + Error::::NoMultiAddress + ); + + // TODO: Check that the multiaddresses are valid + /* for multiaddress in bsp_info.multiaddresses.iter() { + let multiaddress_vec = multiaddress.to_vec(); + let valid_multiaddress = Multiaddr::try_from(multiaddress_vec); + match valid_multiaddress { + Ok(_) => (), + Err(_) => return Err(Error::::InvalidMultiAddress.into()), + } + } */ + + // Check that the data to be stored is bigger than the minimum required by the runtime + ensure!( + bsp_info.capacity >= T::SpMinCapacity::get(), + Error::::StorageTooLow + ); + + // Calculate how much deposit will the signer have to pay to register with this amount of data + let capacity_over_minimum = bsp_info + .capacity + .checked_sub(&T::SpMinCapacity::get()) + .ok_or(Error::::StorageTooLow)?; + let deposit_for_capacity_over_minimum = T::DepositPerData::get() + .checked_mul(&capacity_over_minimum.into()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + let deposit = T::SpMinDeposit::get() + .checked_add(&deposit_for_capacity_over_minimum) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Check if the user has enough balance to pay the deposit + let user_balance = + T::NativeBalance::reducible_balance(who, Preservation::Preserve, Fortitude::Polite); + ensure!(user_balance >= deposit, Error::::NotEnoughBalance); + + // Check if we can hold the deposit from the user + ensure!( + T::NativeBalance::can_hold(&HoldReason::StorageProviderDeposit.into(), who, deposit), + Error::::CannotHoldDeposit + ); + + // Hold the deposit from the user + T::NativeBalance::hold(&HoldReason::StorageProviderDeposit.into(), who, deposit)?; + + // Store the sign up request in the SignUpRequests mapping + SignUpRequests::::insert( + who, + ( + StorageProvider::BackupStorageProvider(bsp_info.clone()), + frame_system::Pallet::::block_number(), + ), + ); + + Ok(()) + } + + /// This function holds the logic that checks if a user can cancel a sign up request as a Storage Provider + /// and, if so, removes the request from the SignUpRequests mapping + pub fn do_cancel_sign_up(who: &T::AccountId) -> DispatchResult { + // Check that the signer has requested to sign up as a Storage Provider + SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested)?; + + // Remove the sign up request from the SignUpRequests mapping + SignUpRequests::::remove(who); + + // Return the deposit to the signer + // We return all held funds as there's no possibility of the user having another _valid_ hold with this pallet + T::NativeBalance::release_all( + &HoldReason::StorageProviderDeposit.into(), + who, + frame_support::traits::tokens::Precision::Exact, + )?; + + Ok(()) + } + + /// This function dispatches the logic to confirm the sign up of a user as a Storage Provider + /// It checks if the user has requested to sign up, and if so, it dispatches the corresponding logic + /// according to the type of Storage Provider that the user is trying to sign up as + pub fn do_confirm_sign_up(who: &T::AccountId) -> DispatchResult { + // Check that the signer has requested to sign up as a Storage Provider + let (sp, request_block) = + SignUpRequests::::get(who).ok_or(Error::::SignUpNotRequested)?; + + // Get the ProviderId by using the AccountId as the seed for a random generator + let (sp_id, block_number_when_random) = + T::ProvidersRandomness::random(who.encode().as_ref()); + + // Check that the maximum block number after which the randomness is invalid is greater than or equal to the block number when the + // request was made to ensure that the randomness was not known when the request was made + ensure!( + block_number_when_random >= request_block, + Error::::RandomnessNotValidYet + ); + + // Check what type of Storage Provider the signer is trying to sign up as and dispatch the corresponding logic + match sp { + StorageProvider::MainStorageProvider(msp_info) => { + Self::do_msp_sign_up(who, sp_id, &msp_info, request_block)?; + } + StorageProvider::BackupStorageProvider(bsp_info) => { + Self::do_bsp_sign_up(who, sp_id, &bsp_info, request_block)?; + } + } + + Ok(()) + } + + /// This function holds the logic that confirms the sign up of a user as a Main Storage Provider + /// It updates the storage to add the new Main Storage Provider, increments the counter of Main Storage Providers, + /// and removes the sign up request from the SignUpRequests mapping + pub fn do_msp_sign_up( + who: &T::AccountId, + msp_id: MainStorageProviderId, + msp_info: &MainStorageProvider, + request_block: BlockNumberFor, + ) -> DispatchResult { + // Check that the current block number is not greater than the block number when the request was made plus the maximum amount of + // blocks that we allow the user to wait for valid randomness (should be at least more than an epoch if using BABE's RandomnessFromOneEpochAgo) + // We do this to ensure that a user cannot wait indefinitely for randomness that suits them + ensure!( + frame_system::Pallet::::block_number() + < request_block + T::MaxBlocksForRandomness::get(), + Error::::SignUpRequestExpired + ); + + // Insert the MainStorageProviderId into the mapping + AccountIdToMainStorageProviderId::::insert(who, msp_id); + + // Save the MainStorageProvider information in storage + MainStorageProviders::::insert(&msp_id, msp_info); + + // Increment the counter of Main Storage Providers registered + let new_amount_of_msps = MspCount::::get() + .checked_add(&T::SpCount::one()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + MspCount::::set(new_amount_of_msps); + + // Remove the sign up request from the SignUpRequests mapping + SignUpRequests::::remove(who); + + // Emit the corresponding event + Self::deposit_event(Event::::MspSignUpSuccess { + who: who.clone(), + msp_id, + multiaddresses: msp_info.multiaddresses.clone(), + capacity: msp_info.capacity, + value_prop: msp_info.value_prop.clone(), + }); + + Ok(()) + } + + /// This function holds the logic that confirms the sign up of a user as a Backup Storage Provider + /// It updates the storage to add the new Backup Storage Provider, increments the counter of Backup Storage Providers, + /// increments the total capacity of the network (which is the sum of all BSPs capacities), and removes the sign up request + /// from the SignUpRequests mapping + pub fn do_bsp_sign_up( + who: &T::AccountId, + bsp_id: BackupStorageProviderId, + bsp_info: &BackupStorageProvider, + request_block: BlockNumberFor, + ) -> DispatchResult { + // Check that the current block number is not greater than the block number when the request was made plus the maximum amount of + // blocks that we allow the user to wait for valid randomness (should be at least more than an epoch if using BABE's RandomnessFromOneEpochAgo) + // We do this to ensure that a user cannot wait indefinitely for randomness that suits them + ensure!( + frame_system::Pallet::::block_number() + < request_block + T::MaxBlocksForRandomness::get(), + Error::::SignUpRequestExpired + ); + + // Insert the BackupStorageProviderId into the mapping + AccountIdToBackupStorageProviderId::::insert(who, bsp_id); + + // Save the BackupStorageProvider information in storage + BackupStorageProviders::::insert(&bsp_id, bsp_info.clone()); + + // Increment the total capacity of the network (which is the sum of all BSPs capacities) + TotalBspsCapacity::::mutate(|n| match n.checked_add(&bsp_info.capacity) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(()) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), + })?; + + // Increment the counter of Backup Storage Providers registered + let new_amount_of_bsps = BspCount::::get() + .checked_add(&T::SpCount::one()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + BspCount::::set(new_amount_of_bsps); + + // Remove the sign up request from the SignUpRequests mapping + SignUpRequests::::remove(who); + + // Increase global reputation weight + GlobalBspsReputationWeight::::mutate(|n| { + *n = n.saturating_add(bsp_info.reputation_weight); + }); + + // Emit the corresponding event + Self::deposit_event(Event::::BspSignUpSuccess { + who: who.clone(), + bsp_id, + multiaddresses: bsp_info.multiaddresses.clone(), + capacity: bsp_info.capacity, + }); + + Ok(()) + } + + /// This function holds the logic that checks if a user can sign off as a Main Storage Provider + /// and, if so, updates the storage to remove the user as a Main Storage Provider, decrements the counter of Main Storage Providers, + /// and returns the deposit to the user + pub fn do_msp_sign_off(who: &T::AccountId) -> Result, DispatchError> { + // Check that the signer is registered as a MSP and get its info + let msp_id = + AccountIdToMainStorageProviderId::::get(who).ok_or(Error::::NotRegistered)?; + + let msp = expect_or_err!( + MainStorageProviders::::get(&msp_id), + "MSP is registered (has a MSP ID), it should also have metadata", + Error::::SpRegisteredButDataNotFound + ); + + // Check that the MSP has no storage assigned to it (no buckets or data used by it) + ensure!( + msp.capacity_used == T::StorageDataUnit::zero(), + Error::::StorageStillInUse + ); + + // Update the MSPs storage, removing the signer as an MSP + AccountIdToMainStorageProviderId::::remove(who); + MainStorageProviders::::remove(&msp_id); + + // Return the deposit to the signer (if all funds cannot be returned, it will fail and revert with the reason) + T::NativeBalance::release_all( + &HoldReason::StorageProviderDeposit.into(), + who, + frame_support::traits::tokens::Precision::Exact, + )?; + + // Decrement the storage that holds total amount of MSPs currently in the system + MspCount::::mutate(|n| { + let new_amount_of_msps = n.checked_sub(&T::SpCount::one()); + match new_amount_of_msps { + Some(new_amount_of_msps) => { + *n = new_amount_of_msps; + Ok(msp_id) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), + } + })?; + + Ok(msp_id) + } + + /// This function holds the logic that checks if a user can sign off as a Backup Storage Provider + /// and, if so, updates the storage to remove the user as a Backup Storage Provider, decrements the counter of Backup Storage Providers, + /// decrements the total capacity of the network (which is the sum of all BSPs capacities), and returns the deposit to the user + pub fn do_bsp_sign_off( + who: &T::AccountId, + ) -> Result, DispatchError> { + // Check that the signer is registered as a BSP and get its info + let bsp_id = + AccountIdToBackupStorageProviderId::::get(who).ok_or(Error::::NotRegistered)?; + + let bsp = expect_or_err!( + BackupStorageProviders::::get(&bsp_id), + "BSP is registered (has a BSP ID), it should also have metadata", + Error::::SpRegisteredButDataNotFound + ); + + // Check that the BSP has no storage assigned to it (it is not currently storing any files) + ensure!( + bsp.capacity_used == T::StorageDataUnit::zero(), + Error::::StorageStillInUse + ); + + // Update the BSPs storage, removing the signer as an BSP + AccountIdToBackupStorageProviderId::::remove(who); + BackupStorageProviders::::remove(&bsp_id); + + // Update the total capacity of the network (which is the sum of all BSPs capacities) + TotalBspsCapacity::::mutate(|n| match n.checked_sub(&bsp.capacity) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(bsp_id) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), + })?; + + // Return the deposit to the signer (if all funds cannot be returned, it will fail and revert with the reason) + T::NativeBalance::release_all( + &HoldReason::StorageProviderDeposit.into(), + who, + frame_support::traits::tokens::Precision::Exact, + )?; + + // Decrement the storage that holds total amount of BSPs currently in the system + BspCount::::mutate(|n| { + let new_amount_of_bsps = n.checked_sub(&T::SpCount::one()); + match new_amount_of_bsps { + Some(new_amount_of_bsps) => { + *n = new_amount_of_bsps; + Ok(bsp_id) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), + } + })?; + + // Decrease global reputation weight + GlobalBspsReputationWeight::::mutate(|n| { + *n = n.saturating_sub(bsp.reputation_weight); + }); + + Ok(bsp_id) + } + + /// This function is in charge of dispatching the logic to change the capacity of a Storage Provider + /// It checks if the signer is registered as a SP and dispatches the corresponding function + /// that checks if the user can change its capacity and, if so, updates the storage to reflect the new capacity + pub fn do_change_capacity( + who: &T::AccountId, + new_capacity: StorageDataUnit, + ) -> Result<(StorageProviderId, StorageDataUnit), DispatchError> { + // Check that the new capacity is not zero (there are specific functions to sign off as a SP) + ensure!( + new_capacity != T::StorageDataUnit::zero(), + Error::::NewCapacityCantBeZero + ); + + // Check that the signer is registered as a SP and dispatch the corresponding function, getting its old capacity + let old_capacity = if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { + ( + StorageProviderId::MainStorageProvider(msp_id), + Self::do_change_capacity_msp(who, msp_id, new_capacity)?, + ) + } else if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who) { + ( + StorageProviderId::BackupStorageProvider(bsp_id), + Self::do_change_capacity_bsp(who, bsp_id, new_capacity)?, + ) + } else { + return Err(Error::::NotRegistered.into()); + }; + + Ok(old_capacity) + } + + /// This function holds the logic that checks if a user can change its capacity as a Main Storage Provider + /// and, if so, updates the storage to reflect the new capacity, modifying the user's deposit accordingly + /// and returning the old capacity if successful + pub fn do_change_capacity_msp( + account_id: &T::AccountId, + msp_id: MainStorageProviderId, + new_capacity: StorageDataUnit, + ) -> Result, DispatchError> { + // Check that the MSP is registered and get its info + let mut msp = MainStorageProviders::::get(&msp_id).ok_or(Error::::NotRegistered)?; + + // Check that the new capacity is different from the current capacity + ensure!( + new_capacity != msp.capacity, + Error::::NewCapacityEqualsCurrentCapacity + ); + + // Check that enough time has passed since the last capacity change + ensure!( + frame_system::Pallet::::block_number() + >= msp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get(), + Error::::NotEnoughTimePassed + ); + + // Check that the new capacity is bigger than the minimum required by the runtime + ensure!( + new_capacity >= T::SpMinCapacity::get(), + Error::::StorageTooLow + ); + + // Check that the new capacity is bigger than the current used capacity by the MSP + ensure!( + new_capacity >= msp.capacity_used, + Error::::NewCapacityLessThanUsedStorage + ); + + // Calculate how much deposit will the signer have to pay to register with this amount of data + let capacity_over_minimum = new_capacity + .checked_sub(&T::SpMinCapacity::get()) + .ok_or(Error::::StorageTooLow)?; + let deposit_for_capacity_over_minimum = T::DepositPerData::get() + .checked_mul(&capacity_over_minimum.into()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + let new_deposit = T::SpMinDeposit::get() + .checked_add(&deposit_for_capacity_over_minimum) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Check how much has the MSP already deposited for the current capacity + let current_deposit = T::NativeBalance::balance_on_hold( + &HoldReason::StorageProviderDeposit.into(), + account_id, + ); + + // Check if the new deposit is bigger or smaller than the current deposit + // Note: we do not check directly capacities as, for example, a bigger new_capacity could entail a smaller deposit + // because of changes in storage pricing, so we check the difference in deposits instead + if new_deposit > current_deposit { + // If the new deposit is bigger than the current deposit, more balance has to be held from the user + Self::hold_balance(account_id, current_deposit, new_deposit)?; + } else if new_deposit < current_deposit { + // If the new deposit is smaller than the current deposit, some balance has to be released to the user + Self::release_balance(account_id, current_deposit, new_deposit)?; + } + + // Get the MSP's old capacity + let old_capacity = msp.capacity; + + // Update the MSP's storage, modifying the capacity and the last capacity change block number + msp.capacity = new_capacity; + msp.last_capacity_change = frame_system::Pallet::::block_number(); + MainStorageProviders::::insert(&msp_id, msp); + + // Return the old capacity + Ok(old_capacity) + } + + /// This function holds the logic that checks if a user can change its capacity as a Backup Storage Provider + /// and, if so, updates the storage to reflect the new capacity, modifying the user's deposit accordingly + /// and returning the old capacity if successful + pub fn do_change_capacity_bsp( + account_id: &T::AccountId, + bsp_id: BackupStorageProviderId, + new_capacity: StorageDataUnit, + ) -> Result, DispatchError> { + // Check that the BSP is registered and get its info + let mut bsp = BackupStorageProviders::::get(&bsp_id).ok_or(Error::::NotRegistered)?; + + // Check that the new capacity is different from the current capacity + ensure!( + new_capacity != bsp.capacity, + Error::::NewCapacityEqualsCurrentCapacity + ); + + // Check that enough time has passed since the last capacity change + ensure!( + frame_system::Pallet::::block_number() + >= bsp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get(), + Error::::NotEnoughTimePassed + ); + + // Check that the new capacity is bigger than the minimum required by the runtime + ensure!( + new_capacity >= T::SpMinCapacity::get(), + Error::::StorageTooLow + ); + + // Check that the new capacity is bigger than the current used capacity by the BSP + ensure!( + new_capacity >= bsp.capacity_used, + Error::::NewCapacityLessThanUsedStorage + ); + + // Calculate how much deposit will the signer have to pay to register with this amount of data + let capacity_over_minimum = new_capacity + .checked_sub(&T::SpMinCapacity::get()) + .ok_or(Error::::StorageTooLow)?; + let deposit_for_capacity_over_minimum = T::DepositPerData::get() + .checked_mul(&capacity_over_minimum.into()) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + let new_deposit = T::SpMinDeposit::get() + .checked_add(&deposit_for_capacity_over_minimum) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?; + + // Check how much has the used already deposited for the current capacity + let current_deposit = T::NativeBalance::balance_on_hold( + &HoldReason::StorageProviderDeposit.into(), + account_id, + ); + + // Check if the new deposit is bigger or smaller than the current deposit + // Note: we do not check directly capacities as, for example, a bigger new_capacity could entail a smaller deposit + // because of changes in storage pricing, so we check the difference in deposits instead + if new_deposit > current_deposit { + // If the new deposit is bigger than the current deposit, more balance has to be held from the user + Self::hold_balance(account_id, current_deposit, new_deposit)?; + } else if new_deposit < current_deposit { + // If the new deposit is smaller than the current deposit, some balance has to be released to the user + Self::release_balance(account_id, current_deposit, new_deposit)?; + } + + // Get the BSP's old capacity + let old_capacity = bsp.capacity; + + // Update the total capacity of the network (which is the sum of all BSPs capacities) + if new_capacity > old_capacity { + // If the new capacity is bigger than the old capacity, get the difference doing new_capacity - old_capacity + let difference = new_capacity + .checked_sub(&old_capacity) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; + // Increment the total capacity of the network by the difference + TotalBspsCapacity::::mutate(|n| match n.checked_add(&difference) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(()) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), + })?; + } else { + // If the new capacity is smaller than the old capacity, get the difference doing old_capacity - new_capacity + let difference = old_capacity + .checked_sub(&new_capacity) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; + // Decrement the total capacity of the network + TotalBspsCapacity::::mutate(|n| match n.checked_sub(&difference) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(()) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), + })?; + } + + // Update the BSP's storage, modifying the capacity and the last capacity change block number + bsp.capacity = new_capacity; + bsp.last_capacity_change = frame_system::Pallet::::block_number(); + BackupStorageProviders::::insert(&bsp_id, bsp); + + // Return the old capacity + Ok(old_capacity) + } + + /// Slash a Storage Provider. + /// + /// The amount slashed is calculated as the product of the [`SlashAmountPerChunkOfStorageData`] and the accrued failed proof submissions. + /// The amount is then slashed from the Storage Provider's held deposit and transferred to the treasury. + /// + /// This will return an error when the Storage Provider is not slashable. In the context of the StorageHub protocol, + /// a Storage Provider is slashable when the proofs-dealer pallet has marked them as such. + /// + /// Successfully slashing a Storage Provider should be a free operation. + pub(crate) fn do_slash(provider_id: &HashId) -> DispatchResultWithPostInfo { + let account_id = if let Some(provider) = MainStorageProviders::::get(provider_id) { + provider.owner_account + } else if let Some(provider) = BackupStorageProviders::::get(provider_id) { + provider.owner_account + } else { + return Err(Error::::ProviderNotSlashable.into()); + }; + + // Calculate slashable amount. + // Doubling the slash for each failed proof submission is necessary since it is more probabilistic for a Storage Provider to have + // responded with two file key proofs given a random or custom challenge. + let slashable_amount = Self::compute_worst_case_scenario_slashable_amount(provider_id)?; + + let amount_slashed = T::NativeBalance::transfer_on_hold( + &HoldReason::StorageProviderDeposit.into(), + &account_id, + &T::Treasury::get(), + slashable_amount, + Precision::BestEffort, + Restriction::Free, + Fortitude::Polite, + )?; + + // Clear the accrued failed proof submissions for the Storage Provider + ::clear_accrued_failed_proof_submissions(&provider_id); + + // Provider held funds have been completely depleted. + if amount_slashed <= slashable_amount { + // TODO: Force sign off the provider. + } + + Self::deposit_event(Event::::Slashed { + provider_id: *provider_id, + amount_slashed, + }); + + Ok(Pays::No.into()) + } + + fn hold_balance( + account_id: &T::AccountId, + previous_deposit: BalanceOf, + new_deposit: BalanceOf, + ) -> DispatchResult { + // Get the user's reducible balance + let user_balance = T::NativeBalance::reducible_balance( + account_id, + Preservation::Preserve, + Fortitude::Polite, + ); + + // Get the difference between the new deposit and the current deposit + let difference = new_deposit + .checked_sub(&previous_deposit) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; + + // Check if the user has enough balance to pay the difference + ensure!(user_balance >= difference, Error::::NotEnoughBalance); + + // Check if we can hold the difference from the user + ensure!( + T::NativeBalance::can_hold( + &HoldReason::StorageProviderDeposit.into(), + account_id, + difference, + ), + Error::::CannotHoldDeposit + ); + + // Hold the difference from the user + T::NativeBalance::hold( + &HoldReason::StorageProviderDeposit.into(), + account_id, + difference, + )?; + + Ok(()) + } + + fn release_balance( + account_id: &T::AccountId, + previous_deposit: BalanceOf, + new_deposit: BalanceOf, + ) -> DispatchResult { + // Get the difference between the current deposit and the new deposit + let difference = previous_deposit + .checked_sub(&new_deposit) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Underflow))?; + + // Release the difference from the user + T::NativeBalance::release( + &HoldReason::StorageProviderDeposit.into(), + account_id, + difference, + Precision::Exact, + )?; + + Ok(()) + } + + /// Compute the worst case scenario slashable amount for a Storage Provider. + /// + /// Every failed proof submission counts as for two files which should have been proven due to the low probability of a challenge + /// being an exact match to a file key stored by the Storage Provider. The StorageHub protocol requires the Storage Provider to + /// submit a proof of storage for the neighbouring file keys of the missing challenged file key. + /// + /// The slashing amount is calculated based on an assumption that every file is the maximum size allowed by the protocol. + pub fn compute_worst_case_scenario_slashable_amount( + provider_id: &HashId, + ) -> Result, DispatchError> { + let accrued_failed_submission_count = ::get_accrued_failed_proof_submissions(&provider_id) + .ok_or(Error::::ProviderNotSlashable)?.into(); + + Ok(T::SlashAmountPerMaxFileSize::get() + .saturating_mul(accrued_failed_submission_count) + .saturating_mul(2u32.into())) + } +} + +impl From> for BackupStorageProvider { + fn from(msp: MainStorageProvider) -> Self { + BackupStorageProvider { + capacity: msp.capacity, + capacity_used: msp.capacity_used, + multiaddresses: msp.multiaddresses, + root: T::DefaultMerkleRoot::get(), + last_capacity_change: msp.last_capacity_change, + owner_account: msp.owner_account, + payment_account: msp.payment_account, + reputation_weight: T::StartingReputationWeight::get(), + } + } +} + +/// Implement the ReadBucketsInterface trait for the Storage Providers pallet. +impl ReadBucketsInterface for pallet::Pallet { + type AccountId = T::AccountId; + type BucketId = BucketId; + type BucketNameLimit = T::BucketNameLimit; + type ProviderId = HashId; + type ReadAccessGroupId = T::ReadAccessGroupId; + type MerkleHash = MerklePatriciaRoot; + type StorageDataUnit = T::StorageDataUnit; + + fn bucket_exists(bucket_id: &Self::BucketId) -> bool { + Buckets::::contains_key(bucket_id) + } + + fn derive_bucket_id( + msp_id: &Self::ProviderId, + owner: &Self::AccountId, + bucket_name: BoundedVec, + ) -> Self::BucketId { + let concat = msp_id + .encode() + .into_iter() + .chain( + owner + .encode() + .into_iter() + .chain(bucket_name.encode().into_iter()), + ) + .collect::>(); + + <::Hashing as sp_runtime::traits::Hash>::hash(&concat) + } + + fn get_msp_of_bucket(bucket_id: &Self::BucketId) -> Option { + Buckets::::get(bucket_id).map(|bucket| bucket.msp_id) + } + + fn get_read_access_group_id_of_bucket( + bucket_id: &Self::BucketId, + ) -> Result, DispatchError> { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(bucket.read_access_group_id) + } + + fn is_bucket_owner( + who: &Self::AccountId, + bucket_id: &Self::BucketId, + ) -> Result { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(&bucket.user_id == who) + } + + fn is_bucket_private(bucket_id: &Self::BucketId) -> Result { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(bucket.private) + } + + fn is_bucket_stored_by_msp(msp_id: &Self::ProviderId, bucket_id: &Self::BucketId) -> bool { + if let Some(bucket) = Buckets::::get(bucket_id) { + bucket.msp_id == *msp_id + } else { + false + } + } + + fn get_root_bucket(bucket_id: &Self::BucketId) -> Option { + Buckets::::get(bucket_id).map(|bucket| bucket.root) + } + + fn get_bucket_owner(bucket_id: &Self::BucketId) -> Result { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(bucket.user_id) + } + + fn get_bucket_size(bucket_id: &Self::BucketId) -> Result { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(bucket.size) + } + + fn get_msp_bucket(bucket_id: &Self::BucketId) -> Result { + let bucket = Buckets::::get(bucket_id).ok_or(Error::::BucketNotFound)?; + Ok(bucket.msp_id) + } +} + +/// Implement the MutateBucketsInterface trait for the Storage Providers pallet. +impl MutateBucketsInterface for pallet::Pallet { + type AccountId = T::AccountId; + type BucketId = BucketId; + type ProviderId = HashId; + type ReadAccessGroupId = T::ReadAccessGroupId; + type MerkleHash = MerklePatriciaRoot; + type StorageDataUnit = T::StorageDataUnit; + + fn add_bucket( + provider_id: Self::ProviderId, + user_id: Self::AccountId, + bucket_id: Self::BucketId, + privacy: bool, + maybe_read_access_group_id: Option, + ) -> DispatchResult { + // Check if bucket already exists + ensure!( + !Buckets::::contains_key(&bucket_id), + Error::::BucketAlreadyExists + ); + + // Check if the MSP exists + ensure!( + MainStorageProviders::::contains_key(&provider_id), + Error::::NotRegistered + ); + + let user_balance = T::NativeBalance::reducible_balance( + &user_id, + Preservation::Preserve, + Fortitude::Polite, + ); + + let deposit = T::BucketDeposit::get(); + ensure!(user_balance >= deposit, Error::::NotEnoughBalance); + ensure!( + T::NativeBalance::can_hold(&HoldReason::BucketDeposit.into(), &user_id, deposit), + Error::::CannotHoldDeposit + ); + + // Hold the bucket deposit + T::NativeBalance::hold(&HoldReason::BucketDeposit.into(), &user_id, deposit)?; + + let bucket = Bucket { + root: T::DefaultMerkleRoot::get(), + msp_id: provider_id, + private: privacy, + read_access_group_id: maybe_read_access_group_id, + user_id, + size: T::StorageDataUnit::zero(), + }; + + Buckets::::insert(&bucket_id, &bucket); + + MainStorageProviderIdsToBuckets::::try_append(&provider_id, bucket_id) + .map_err(|_| Error::::AppendBucketToMspFailed)?; + + Ok(()) + } + + fn change_msp_bucket(bucket_id: &Self::BucketId, new_msp: &Self::ProviderId) -> DispatchResult { + Buckets::::try_mutate(bucket_id, |bucket| { + let bucket = bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.msp_id = *new_msp; + + Ok(()) + }) + } + + fn change_root_bucket(bucket_id: Self::BucketId, new_root: Self::MerkleHash) -> DispatchResult { + Buckets::::try_mutate(&bucket_id, |bucket| { + let bucket = bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.root = new_root; + + Ok(()) + }) + } + + fn remove_root_bucket(bucket_id: Self::BucketId) -> DispatchResult { + let bucket = Buckets::::take(&bucket_id).ok_or(Error::::BucketNotFound)?; + + MainStorageProviderIdsToBuckets::::mutate_exists( + &bucket.msp_id, + |buckets| match buckets { + Some(b) => { + b.retain(|b| b != &bucket_id); + + if b.is_empty() { + *buckets = None; + } + } + _ => {} + }, + ); + + // Release the bucket deposit hold + T::NativeBalance::release( + &HoldReason::BucketDeposit.into(), + &bucket.user_id, + T::BucketDeposit::get(), + Precision::Exact, + )?; + + Ok(()) + } + + fn update_bucket_privacy(bucket_id: Self::BucketId, privacy: bool) -> DispatchResult { + Buckets::::try_mutate(&bucket_id, |maybe_bucket| { + let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.private = privacy; + + Ok(()) + }) + } + + fn update_bucket_read_access_group_id( + bucket_id: Self::BucketId, + maybe_read_access_group_id: Option, + ) -> DispatchResult { + Buckets::::try_mutate(&bucket_id, |maybe_bucket| { + let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.read_access_group_id = maybe_read_access_group_id; + + Ok(()) + }) + } + + fn increase_bucket_size( + bucket_id: &Self::BucketId, + delta: Self::StorageDataUnit, + ) -> DispatchResult { + Buckets::::try_mutate(&bucket_id, |maybe_bucket| { + let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.size = bucket.size.saturating_add(delta); + + Ok(()) + }) + } + + fn decrease_bucket_size( + bucket_id: &Self::BucketId, + delta: Self::StorageDataUnit, + ) -> DispatchResult { + Buckets::::try_mutate(&bucket_id, |maybe_bucket| { + let bucket = maybe_bucket.as_mut().ok_or(Error::::BucketNotFound)?; + bucket.size = bucket.size.saturating_sub(delta); + + Ok(()) + }) + } +} + +/// Implement the ReadStorageProvidersInterface trait for the Storage Providers pallet. +impl ReadStorageProvidersInterface for pallet::Pallet { + type ProviderId = HashId; + type StorageDataUnit = T::StorageDataUnit; + type SpCount = T::SpCount; + type MultiAddress = MultiAddress; + type MaxNumberOfMultiAddresses = T::MaxMultiAddressAmount; + type ReputationWeight = T::ReputationWeightType; + + fn is_bsp(who: &Self::ProviderId) -> bool { + BackupStorageProviders::::contains_key(&who) + } + + fn is_msp(who: &Self::ProviderId) -> bool { + MainStorageProviders::::contains_key(&who) + } + + fn get_global_bsps_reputation_weight() -> Self::ReputationWeight { + GlobalBspsReputationWeight::::get() + } + + fn get_bsp_reputation_weight( + who: &Self::ProviderId, + ) -> Result { + if let Some(bsp) = BackupStorageProviders::::get(who) { + Ok(bsp.reputation_weight) + } else { + Err(Error::::NotRegistered.into()) + } + } + + fn get_number_of_bsps() -> Self::SpCount { + Self::get_bsp_count() + } + + fn get_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { + if let Some(bsp) = BackupStorageProviders::::get(who) { + bsp.capacity + } else if let Some(msp) = MainStorageProviders::::get(who) { + msp.capacity + } else { + Zero::zero() + } + } + + fn get_used_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { + if let Some(bsp) = BackupStorageProviders::::get(who) { + bsp.capacity_used + } else if let Some(msp) = MainStorageProviders::::get(who) { + msp.capacity_used + } else { + Zero::zero() + } + } + + fn available_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit { + if let Some(bsp) = BackupStorageProviders::::get(who) { + bsp.capacity.saturating_sub(bsp.capacity_used) + } else if let Some(msp) = MainStorageProviders::::get(who) { + msp.capacity.saturating_sub(msp.capacity_used) + } else { + Zero::zero() + } + } + + fn get_bsp_multiaddresses( + who: &Self::ProviderId, + ) -> Result, DispatchError> + { + if let Some(bsp) = BackupStorageProviders::::get(who) { + Ok(BoundedVec::from(bsp.multiaddresses)) + } else { + Err(Error::::NotRegistered.into()) + } + } +} + +/// Implement the MutateStorageProvidersInterface trait for the Storage Providers pallet. +impl MutateStorageProvidersInterface for pallet::Pallet { + type ProviderId = HashId; + type StorageDataUnit = T::StorageDataUnit; + + fn decrease_capacity_used( + provider_id: &Self::ProviderId, + delta: Self::StorageDataUnit, + ) -> DispatchResult { + if MainStorageProviders::::contains_key(&provider_id) { + let mut msp = + MainStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; + msp.capacity_used = msp.capacity_used.saturating_sub(delta); + MainStorageProviders::::insert(&provider_id, msp); + } else if BackupStorageProviders::::contains_key(&provider_id) { + let mut bsp = + BackupStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; + bsp.capacity_used = bsp.capacity_used.saturating_sub(delta); + BackupStorageProviders::::insert(&provider_id, bsp); + UsedBspsCapacity::::mutate(|n| match n.checked_sub(&delta) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(()) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Underflow)), + })?; + } else { + return Err(Error::::NotRegistered.into()); + } + Ok(()) + } + + fn increase_capacity_used( + provider_id: &Self::ProviderId, + delta: Self::StorageDataUnit, + ) -> DispatchResult { + if MainStorageProviders::::contains_key(&provider_id) { + let mut msp = + MainStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; + + let new_used_capacity = msp.capacity_used.saturating_add(delta); + if msp.capacity < new_used_capacity { + return Err(Error::::NewUsedCapacityExceedsStorageCapacity.into()); + } + msp.capacity_used = new_used_capacity; + MainStorageProviders::::insert(&provider_id, msp); + } else if BackupStorageProviders::::contains_key(&provider_id) { + let mut bsp = + BackupStorageProviders::::get(&provider_id).ok_or(Error::::NotRegistered)?; + bsp.capacity_used = bsp.capacity_used.saturating_add(delta); + BackupStorageProviders::::insert(&provider_id, bsp); + UsedBspsCapacity::::mutate(|n| match n.checked_add(&delta) { + Some(new_total_bsp_capacity) => { + *n = new_total_bsp_capacity; + Ok(()) + } + None => Err(DispatchError::Arithmetic(ArithmeticError::Overflow)), + })?; + } else { + return Err(Error::::NotRegistered.into()); + } + Ok(()) + } +} + +/// Implement the ReadProvidersInterface for the Storage Providers pallet. +impl ReadProvidersInterface for pallet::Pallet { + type AccountId = T::AccountId; + type Balance = T::NativeBalance; + type MerkleHash = MerklePatriciaRoot; + type ProviderId = HashId; + + fn get_default_root() -> Self::MerkleHash { + T::DefaultMerkleRoot::get() + } + + fn get_owner_account(who: Self::ProviderId) -> Option { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(bsp.owner_account) + } else if let Some(msp) = MainStorageProviders::::get(&who) { + Some(msp.owner_account) + } else if let Some(bucket) = Buckets::::get(&who) { + let msp_for_bucket = bucket.msp_id; + if let Some(msp) = MainStorageProviders::::get(&msp_for_bucket) { + Some(msp.owner_account) + } else { + None + } + } else { + None + } + } + + fn get_payment_account(who: Self::ProviderId) -> Option { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(bsp.payment_account) + } else if let Some(msp) = MainStorageProviders::::get(&who) { + Some(msp.payment_account) + } else { + None + } + } + + fn get_provider_id(who: Self::AccountId) -> Option { + if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who.clone()) { + Some(bsp_id) + } else if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { + Some(msp_id) + } else { + None + } + } + + fn get_root(who: Self::ProviderId) -> Option { + if let Some(bucket) = Buckets::::get(&who) { + Some(bucket.root) + } else if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(bsp.root) + } else { + None + } + } + + fn get_stake( + who: Self::ProviderId, + ) -> Option<>::Balance> + { + if let Some(bucket) = Buckets::::get(&who) { + match MainStorageProviders::::get(bucket.msp_id) { + Some(related_msp) => Some(T::NativeBalance::balance_on_hold( + &HoldReason::BucketDeposit.into(), + &related_msp.owner_account, + )), + None => None, + } + } else if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(T::NativeBalance::balance_on_hold( + &HoldReason::StorageProviderDeposit.into(), + &bsp.owner_account, + )) + } else { + None + } + } + + fn is_provider(who: Self::ProviderId) -> bool { + BackupStorageProviders::::contains_key(&who) + || MainStorageProviders::::contains_key(&who) + || Buckets::::contains_key(&who) + } +} + +/// Implement the MutateProvidersInterface for the Storage Providers pallet. +impl MutateProvidersInterface for pallet::Pallet { + type MerkleHash = MerklePatriciaRoot; + type ProviderId = HashId; + + fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult { + if let Some(bucket) = Buckets::::get(&who) { + Buckets::::insert( + &who, + Bucket { + root: new_root, + ..bucket + }, + ); + } else if let Some(bsp) = BackupStorageProviders::::get(&who) { + BackupStorageProviders::::insert( + &who, + BackupStorageProvider { + root: new_root, + ..bsp + }, + ); + } else { + return Err(Error::::NotRegistered.into()); + } + Ok(()) + } +} + +/// Implement the ReadChallengeableProvidersInterface for the Storage Providers pallet. +impl ReadChallengeableProvidersInterface for pallet::Pallet { + type AccountId = T::AccountId; + type Balance = T::NativeBalance; + type MerkleHash = MerklePatriciaRoot; + type ProviderId = HashId; + + fn get_default_root() -> Self::MerkleHash { + T::DefaultMerkleRoot::get() + } + + fn get_owner_account(who: Self::ProviderId) -> Option { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(bsp.owner_account) + } else { + None + } + } + + fn get_provider_id(who: Self::AccountId) -> Option { + if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who.clone()) { + Some(bsp_id) + } else { + None + } + } + + fn get_root(who: Self::ProviderId) -> Option { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(bsp.root) + } else { + None + } + } + + fn get_stake( + who: Self::ProviderId, + ) -> Option<>::Balance> + { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + Some(T::NativeBalance::balance_on_hold( + &HoldReason::StorageProviderDeposit.into(), + &bsp.owner_account, + )) + } else { + None + } + } + + fn is_provider(who: Self::ProviderId) -> bool { + BackupStorageProviders::::contains_key(&who) + } + + fn get_min_stake( + ) -> >::Balance { + T::SpMinDeposit::get() + } +} + +/// Implement the MutateChallengeableProvidersInterface for the Storage Providers pallet. +impl MutateChallengeableProvidersInterface for pallet::Pallet { + type MerkleHash = MerklePatriciaRoot; + type ProviderId = HashId; + + fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult { + if let Some(bsp) = BackupStorageProviders::::get(&who) { + BackupStorageProviders::::insert( + &who, + BackupStorageProvider { + root: new_root, + ..bsp + }, + ); + } else { + return Err(Error::::NotRegistered.into()); + } + Ok(()) + } + + fn update_provider_after_key_removal( + who: &Self::ProviderId, + removed_trie_value: &Vec, + ) -> DispatchResult { + // Get the removed file's metadata + let file_metadata = + <::FileMetadataManager as FileMetadataInterface>::decode( + removed_trie_value, + ) + .map_err(|_| Error::::InvalidEncodedFileMetadata)?; + + // Get the file size as a StorageDataUnit type and the owner as an AccountId type + let file_size = + <::FileMetadataManager as FileMetadataInterface>::get_file_size( + &file_metadata, + ); + let owner = + <::FileMetadataManager as FileMetadataInterface>::get_file_owner( + &file_metadata, + ) + .map_err(|_| Error::::InvalidEncodedAccountId)?; + + // Decrease the used capacity of the provider + Self::decrease_capacity_used(who, file_size)?; + + // Update the provider's payment stream with the user + let previous_amount_provided = + ::get_dynamic_rate_payment_stream_amount_provided( + who, + &owner, + ) + .ok_or(Error::::PaymentStreamNotFound)?; + let new_amount_provided = previous_amount_provided.saturating_sub(file_size); + ::update_dynamic_rate_payment_stream( + who, + &owner, + &new_amount_provided, + )?; + + Ok(()) + } +} + +/// Implement the SystemMetricsInterface for the Storage Providers pallet. +impl SystemMetricsInterface for pallet::Pallet { + type ProvidedUnit = StorageDataUnit; + + fn get_total_capacity() -> Self::ProvidedUnit { + Self::get_total_bsp_capacity() + } + + fn get_total_used_capacity() -> Self::ProvidedUnit { + Self::get_used_bsp_capacity() + } +} + +/// Runtime API implementation for the Storage Providers pallet. +impl Pallet +where + T: pallet::Config, +{ + pub fn get_bsp_info( + bsp_id: &BackupStorageProviderId, + ) -> Result, GetBspInfoError> { + BackupStorageProviders::::get(bsp_id).ok_or(GetBspInfoError::BspNotRegistered) + } + + pub fn get_storage_provider_id(who: &T::AccountId) -> Option> { + if let Some(bsp_id) = AccountIdToBackupStorageProviderId::::get(who) { + Some(StorageProviderId::BackupStorageProvider(bsp_id)) + } else if let Some(msp_id) = AccountIdToMainStorageProviderId::::get(who) { + Some(StorageProviderId::MainStorageProvider(msp_id)) + } else { + None + } + } + + pub fn query_storage_provider_capacity( + provider_id: &ProviderId, + ) -> Result, QueryStorageProviderCapacityError> { + if MainStorageProviders::::contains_key(provider_id) { + let msp = MainStorageProviders::::get(provider_id) + .ok_or(QueryStorageProviderCapacityError::ProviderNotRegistered)?; + Ok(msp.capacity) + } else if BackupStorageProviders::::contains_key(provider_id) { + let bsp = BackupStorageProviders::::get(provider_id) + .ok_or(QueryStorageProviderCapacityError::ProviderNotRegistered)?; + Ok(bsp.capacity) + } else { + Err(QueryStorageProviderCapacityError::ProviderNotRegistered) + } + } + + pub fn query_available_storage_capacity( + provider_id: &ProviderId, + ) -> Result, QueryAvailableStorageCapacityError> { + if MainStorageProviders::::contains_key(provider_id) { + let msp = MainStorageProviders::::get(provider_id) + .ok_or(QueryAvailableStorageCapacityError::ProviderNotRegistered)?; + Ok(msp.capacity.saturating_sub(msp.capacity_used)) + } else if BackupStorageProviders::::contains_key(provider_id) { + let bsp = BackupStorageProviders::::get(provider_id) + .ok_or(QueryAvailableStorageCapacityError::ProviderNotRegistered)?; + Ok(bsp.capacity.saturating_sub(bsp.capacity_used)) + } else { + Err(QueryAvailableStorageCapacityError::ProviderNotRegistered) + } + } + + pub fn query_earliest_change_capacity_block( + provider_id: &BackupStorageProviderId, + ) -> Result, QueryEarliestChangeCapacityBlockError> { + let bsp = BackupStorageProviders::::get(provider_id) + .ok_or(QueryEarliestChangeCapacityBlockError::ProviderNotRegistered)?; + Ok(bsp.last_capacity_change + T::MinBlocksBetweenCapacityChanges::get()) + } + + pub fn get_worst_case_scenario_slashable_amount( + provider_id: &ProviderId, + ) -> Result, DispatchError> { + Self::compute_worst_case_scenario_slashable_amount(provider_id) + } + + pub fn get_slash_amount_per_max_file_size() -> BalanceOf { + T::SlashAmountPerMaxFileSize::get() + } + + pub fn query_msp_id_of_bucket_id( + bucket_id: &BucketId, + ) -> Result, QueryMspIdOfBucketIdError> { + let bucket = + Buckets::::get(bucket_id).ok_or(QueryMspIdOfBucketIdError::BucketNotFound)?; + Ok(bucket.msp_id) + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af52714ac..7be9d2c71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,23 +21,23 @@ importers: api-augment: dependencies: '@polkadot/api': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@polkadot/api-base': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@polkadot/rpc-core': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@polkadot/typegen': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@polkadot/types': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@polkadot/types-codec': - specifier: 12.4.2 - version: 12.4.2 + specifier: 14.0.1 + version: 14.0.1 '@storagehub/types-bundle': specifier: workspace:* version: link:../types-bundle @@ -499,18 +499,34 @@ packages: resolution: {integrity: sha512-BkG2tQpUUO0iUm65nSqP8hwHkNfN8jQw8apqflJNt9H8EkEL6v7sqwbLvGqtlxM9wzdxbg7lrWp3oHg4rOP31g==} engines: {node: '>=18'} + '@polkadot/api-augment@14.0.1': + resolution: {integrity: sha512-+ZHq3JaQZ/3Q45r6/YQBeLfoP8S5ibgkOvLKnKA9cJeF7oP5Qgi6pAEnGW0accfnT9PyCEco9fD/ZOLR9Yka7w==} + engines: {node: '>=18'} + '@polkadot/api-base@12.4.2': resolution: {integrity: sha512-XYI7Po8i6C4lYZah7Xo0v7zOAawBUfkmtx0YxsLY/665Sup8oqzEj666xtV9qjBzR9coNhQonIFOn+9fh27Ncw==} engines: {node: '>=18'} + '@polkadot/api-base@14.0.1': + resolution: {integrity: sha512-OVnDiztKx/1ktae9eCzO1q8lmKEfnQ71fipo8JkDJOMIN4vT1IqL9KQo4e/Xz8UtOfTJ0H8kZ6evaLqdA3ZYOA==} + engines: {node: '>=18'} + '@polkadot/api-derive@12.4.2': resolution: {integrity: sha512-R0AMANEnqs5AiTaiQX2FXCxUlOibeDSgqlkyG1/0KDsdr6PO/l3dJOgEO+grgAwh4hdqzk4I9uQpdKxG83f2Gw==} engines: {node: '>=18'} + '@polkadot/api-derive@14.0.1': + resolution: {integrity: sha512-ADQMre3DRRW/0rhJqxOVhQ1vqtyafP2dSZJ0qEAsto12q2WMSF8CZWo7pXe4DxiniDkZx3zVq4z5lqw2aBRLfg==} + engines: {node: '>=18'} + '@polkadot/api@12.4.2': resolution: {integrity: sha512-e1KS048471iBWZU10TJNEYOZqLO+8h8ajmVqpaIBOVkamN7tmacBxmHgq0+IA8VrGxjxtYNa1xF5Sqrg76uBEg==} engines: {node: '>=18'} + '@polkadot/api@14.0.1': + resolution: {integrity: sha512-CDSaUiJpXu9aE6MaTg14K+9Trf8K2PBHcD3Xl5m5KOvJperWgYFxoCqV3rXLIBWt69LgHhMYlq5JSPRHxejIsw==} + engines: {node: '>=18'} + '@polkadot/keyring@13.0.2': resolution: {integrity: sha512-NeLbhyKDT5W8LI9seWTZGePxNTOVpDhv2018HSrEDwJq9Ie0C4TZhUf3KNERCkSveuThXjfQJMs+1CF33ZXPWw==} engines: {node: '>=18'} @@ -518,61 +534,123 @@ packages: '@polkadot/util': 13.0.2 '@polkadot/util-crypto': 13.0.2 + '@polkadot/keyring@13.1.1': + resolution: {integrity: sha512-Wm+9gn946GIPjGzvueObLGBBS9s541HE6mvKdWGEmPFMzH93ESN931RZlOd67my5MWryiSP05h5SHTp7bSaQTA==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1 + '@polkadot/networks@13.0.2': resolution: {integrity: sha512-ABAL+vug/gIwkdFEzeh87JoJd0YKrxSYg/HjUrZ+Zis2ucxQEKpvtCpJ34ku+YrjacBfVqIAkkwd3ZdIPGq9aQ==} engines: {node: '>=18'} + '@polkadot/networks@13.1.1': + resolution: {integrity: sha512-eEQ4+Mfl1xFtApeU5PdXZ2XBhxNSvUz9yW+YQVGUCkXRjWFbqNRsTOYWGd9uFbiAOXiiiXbtqfZpxSDzIm4XOg==} + engines: {node: '>=18'} + '@polkadot/rpc-augment@12.4.2': resolution: {integrity: sha512-IEco5pnso+fYkZNMlMAN5i4XAxdXPv0PZ0HNuWlCwF/MmRvWl8pq5JFtY1FiByHEbeuHwMIUhHM5SDKQ85q9Hg==} engines: {node: '>=18'} + '@polkadot/rpc-augment@14.0.1': + resolution: {integrity: sha512-M0CbN/IScqiedYI2TmoQ+SoeEdJHfxGeQD1qJf9uYv9LILK+x1/5fyr5DrZ3uCGVmLuObWAJLnHTs0BzJcSHTQ==} + engines: {node: '>=18'} + '@polkadot/rpc-core@12.4.2': resolution: {integrity: sha512-yaveqxNcmyluyNgsBT5tpnCa/md0CGbOtRK7K82LWsz7gsbh0x80GBbJrQGxsUybg1gPeZbO1q9IigwA6fY8ag==} engines: {node: '>=18'} + '@polkadot/rpc-core@14.0.1': + resolution: {integrity: sha512-SfgC6WU7RxaFFgm/GUpsqTywyaDeb7+r5GU3GlwC+QR148h3a7UcQ3sssOpB0MiZ2gIXngJuyIcIQm/3GfHnJw==} + engines: {node: '>=18'} + '@polkadot/rpc-provider@12.4.2': resolution: {integrity: sha512-cAhfN937INyxwW1AdjABySdCKhC7QCIONRDHDea1aLpiuxq/w+QwjxauR9fCNGh3lTaAwwnmZ5WfFU2PtkDMGQ==} engines: {node: '>=18'} + '@polkadot/rpc-provider@14.0.1': + resolution: {integrity: sha512-mNfaKZUHPXGSY7TwgOfV05RN3Men21Dw7YXrSZDFkJYsZ55yOAYdmLg9anPZGHW100YnNWrXj+3uhQOw8JgqkA==} + engines: {node: '>=18'} + '@polkadot/typegen@12.4.2': resolution: {integrity: sha512-B23ULYylj9PotnFUHVwN6qx7yLdFgdmhSMC/JN9l4tpM0nJhiAFx8LDCH8de3pbrPsxCVg9hLGtmKYD7hi1VWw==} engines: {node: '>=18'} hasBin: true + '@polkadot/typegen@14.0.1': + resolution: {integrity: sha512-BYwpo7a9gHYw/PoR+XzTE0gZU0ionGVwEu7HXoejNT6cxsmT709S8OMaKcPzA8IvKwcKeBKie9QvXvFXVoQyKQ==} + engines: {node: '>=18'} + hasBin: true + '@polkadot/types-augment@12.4.2': resolution: {integrity: sha512-3fDCOy2BEMuAtMYl4crKg76bv/0pDNEuzpAzV4EBUMIlJwypmjy5sg3gUPCMcA+ckX3xb8DhkWU4ceUdS7T2KQ==} engines: {node: '>=18'} + '@polkadot/types-augment@14.0.1': + resolution: {integrity: sha512-PGo81444J5tGJxP3tu060Jx1kkeuo8SmBIt9S/w626Se49x4RLM5a7Pa5fguYVsg4TsJa9cgVPMuu6Y0F/2aCQ==} + engines: {node: '>=18'} + '@polkadot/types-codec@12.4.2': resolution: {integrity: sha512-DiPGRFWtVMepD9i05eC3orSbGtpN7un/pXOrXu0oriU+oxLkpvZH68ZsPNtJhKdQy03cAYtvB8elJOFJZYqoqQ==} engines: {node: '>=18'} + '@polkadot/types-codec@14.0.1': + resolution: {integrity: sha512-IyUlkrRZ6uppbHVlMJL+btKP7dfgW65K06ggQxH7Y/IyRAQVDNjXecAZrCUMB/gtjUXNPyTHEIfPGDlg8E6rig==} + engines: {node: '>=18'} + '@polkadot/types-create@12.4.2': resolution: {integrity: sha512-nOpeAKZLdSqNMfzS3waQXgyPPaNt8rUHEmR5+WNv6c/Ke/vyf710wjxiTewfp0wpBgtdrimlgG4DLX1J9Ms1LA==} engines: {node: '>=18'} + '@polkadot/types-create@14.0.1': + resolution: {integrity: sha512-R9/ac3CHKrFhvPKVUdpjnCDFSaGjfrNwtuY+AzvExAMIq7pM9dxo2N8UfnLbyFaG/n1hfYPXDIS3hLHvOZsLbw==} + engines: {node: '>=18'} + '@polkadot/types-known@12.4.2': resolution: {integrity: sha512-bvhO4KQu/dgPmdwQXsweSMRiRisJ7Bp38lZVEIFykfd2qYyRW3OQEbIPKYpx9raD+fDATU0bTiKQnELrSGhYXw==} engines: {node: '>=18'} + '@polkadot/types-known@14.0.1': + resolution: {integrity: sha512-oGypUOQNxZ6bq10czpVadZYeDM2NBB2kX3VFHLKLEpjaRbnVYtKXL6pl8B0uHR8GK/2Z8AmPOj6kuRjaC86qXg==} + engines: {node: '>=18'} + '@polkadot/types-support@12.4.2': resolution: {integrity: sha512-bz6JSt23UEZ2eXgN4ust6z5QF9pO5uNH7UzCP+8I/Nm85ZipeBYj2Wu6pLlE3Hw30hWZpuPxMDOKoEhN5bhLgw==} engines: {node: '>=18'} + '@polkadot/types-support@14.0.1': + resolution: {integrity: sha512-lcZEyOf5e3WLLtrFlLTvFfUpO0Vx/Gh5lhLLjdx1W9Xs0KJUlOxSAKxvjVieJJj6HifL0Jh6tDYOUeEc4TOrvA==} + engines: {node: '>=18'} + '@polkadot/types@12.4.2': resolution: {integrity: sha512-ivYtt7hYcRvo69ULb1BJA9BE1uefijXcaR089Dzosr9+sMzvsB1yslNQReOq+Wzq6h6AQj4qex6qVqjWZE6Z4A==} engines: {node: '>=18'} + '@polkadot/types@14.0.1': + resolution: {integrity: sha512-DOMzHsyVbCa12FT2Fng8iGiQJhHW2ONpv5oieU+Z2o0gFQqwNmIDXWncScG5mAUBNcDMXLuvWIKLKtUDOq8msg==} + engines: {node: '>=18'} + '@polkadot/util-crypto@13.0.2': resolution: {integrity: sha512-woUsJJ6zd/caL7U+D30a5oM/+WK9iNI00Y8aNUHSj6Zq/KPzK9uqDBaLGWwlgrejoMQkxxiU2X0f2LzP15AtQg==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': 13.0.2 + '@polkadot/util-crypto@13.1.1': + resolution: {integrity: sha512-FG68rrLPdfLcscEyH10vnGkakM4O2lqr71S3GDhgc9WXaS8y9jisLgMPg8jbMHiQBJ3iKYkmtPKiLBowRslj2w==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.1.1 + '@polkadot/util@13.0.2': resolution: {integrity: sha512-/6bS9sfhJLhs8QuqWaR1eRapzfDdGC5XAQZEPL9NN5sTTA7HxWos8rVleai0UERm8QUMabjZ9rK9KpzbXl7ojg==} engines: {node: '>=18'} + '@polkadot/util@13.1.1': + resolution: {integrity: sha512-M4iQ5Um8tFdDmD7a96nPzfrEt+kxyWOqQDPqXyaax4QBnq/WCbq0jo8IO61uz55mdMQnGZvq8jd8uge4V6JzzQ==} + engines: {node: '>=18'} + '@polkadot/wasm-bridge@7.3.2': resolution: {integrity: sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==} engines: {node: '>=18'} @@ -616,14 +694,26 @@ packages: resolution: {integrity: sha512-h2jKT/UaxiEal8LhQeH6+GCjO7GwEqVAD2SNYteCOXff6yNttqAZYJuHZsndbVjVNwqRNf8D5q/zZkD0HUd6xQ==} engines: {node: '>=18'} + '@polkadot/x-bigint@13.1.1': + resolution: {integrity: sha512-Cq4Y6fd9UWtRBZz8RX2tWEBL1IFwUtY6cL8p6HC9yhZtUR6OPjKZe6RIZQa9gSOoIuqZWd6PmtvSNGVH32yfkQ==} + engines: {node: '>=18'} + '@polkadot/x-fetch@13.0.2': resolution: {integrity: sha512-B/gf9iriUr6za/Ui7zIFBfHz7UBZ68rJEIteWHx1UHRCZPcLqv+hgpev6xIGrkfFljI0/lI7IwtN2qy6HYzFBg==} engines: {node: '>=18'} + '@polkadot/x-fetch@13.1.1': + resolution: {integrity: sha512-qA6mIUUebJbS+oWzq/EagZflmaoa9b25WvsxSFn7mCvzKngXzr+GYCY4XiDwKY/S+/pr/kvSCKZ1ia8BDqPBYQ==} + engines: {node: '>=18'} + '@polkadot/x-global@13.0.2': resolution: {integrity: sha512-OoNIXLB5y8vIKpk4R+XmpDPhipNXWSUvEwUnpQT7NAxNLmzgMq1FhbrwBWWPRNHPrQonp7mqxV/X+v5lv1HW/g==} engines: {node: '>=18'} + '@polkadot/x-global@13.1.1': + resolution: {integrity: sha512-DViIMmmEs29Qlsp058VTg2Mn7e3/CpGazNnKJrsBa0o1Ptxl13/4Z0fjqCpNi2GB+kaOsnREzxUORrHcU+PqcQ==} + engines: {node: '>=18'} + '@polkadot/x-randomvalues@13.0.2': resolution: {integrity: sha512-SGj+L0H/7TWZtSmtkWlixO4DFzXDdluI0UscN2h285os2Ns8PnmBbue+iJ8PVSzpY1BOxd66gvkkpboPz+jXFQ==} engines: {node: '>=18'} @@ -631,18 +721,37 @@ packages: '@polkadot/util': 13.0.2 '@polkadot/wasm-util': '*' + '@polkadot/x-randomvalues@13.1.1': + resolution: {integrity: sha512-cXj4omwbgzQQSiBtV1ZBw+XhJUU3iz/DS6ghUnGllSZEK+fGqiyaNgeFQzDY0tKjm6kYaDpvtOHR3mHsbzDuTg==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-util': '*' + '@polkadot/x-textdecoder@13.0.2': resolution: {integrity: sha512-mauglOkTJxLGmLwLc3J5Jlq/W+SHP53eiy3F8/8JxxfnXrZKgWoQXGpvXYPjFnMZj0MzDSy/6GjyGWnDCgdQFA==} engines: {node: '>=18'} + '@polkadot/x-textdecoder@13.1.1': + resolution: {integrity: sha512-LpZ9KYc6HdBH+i86bCmun4g4GWMiWN/1Pzs0hNdanlQMfqp3UGzl1Dqp0nozMvjWAlvyG7ip235VgNMd8HEbqg==} + engines: {node: '>=18'} + '@polkadot/x-textencoder@13.0.2': resolution: {integrity: sha512-Lq08H2OnVXj97uaOwg7tcmRS7a4VJYkHEeWO4FyEMOk6P6lU6W8OVNjjxG0se9PCEgmyZPUDbJI//1ynzP4cXw==} engines: {node: '>=18'} + '@polkadot/x-textencoder@13.1.1': + resolution: {integrity: sha512-w1mT15B9ptN5CJNgN/A0CmBqD5y9OePjBdU6gmAd8KRhwXCF0MTBKcEZk1dHhXiXtX+28ULJWLrfefC5gxy69Q==} + engines: {node: '>=18'} + '@polkadot/x-ws@13.0.2': resolution: {integrity: sha512-nC5e2eY5D5ZR5teQOB7ib+dWLbmNws86cTz3BjKCalSMBBIn6i3V9ElgABpierBmnSJe9D94EyrH1BxdVfDxUg==} engines: {node: '>=18'} + '@polkadot/x-ws@13.1.1': + resolution: {integrity: sha512-E/xFmJTiFzu+IK5M3/8W/9fnvNJFelcnunPv/IgO6UST94SDaTsN/Gbeb6SqPb6CsrTHRl3WD+AZ3ErGGwQfEA==} + engines: {node: '>=18'} + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -697,6 +806,9 @@ packages: '@substrate/ss58-registry@1.49.0': resolution: {integrity: sha512-leW6Ix4LD7XgvxT7+aobPWSw+WvPcN2Rxof1rmd0mNC5t2n99k1N7UNEvz7YEFSOUeHWmKIY7F5q8KeIqYoHfA==} + '@substrate/ss58-registry@1.50.0': + resolution: {integrity: sha512-mkmlMlcC+MSd9rA+PN8ljGAm5fVZskvVwkXIsbx4NFwaT8kt38r7e9cyDWscG3z2Zn40POviZvEMrJSk+r2SgQ==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -2277,8 +2389,7 @@ snapshots: '@noble/hashes@1.4.0': {} - '@noble/hashes@1.5.0': - optional: true + '@noble/hashes@1.5.0': {} '@noble/secp256k1@1.7.1': {} @@ -2337,6 +2448,20 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/api-augment@14.0.1': + dependencies: + '@polkadot/api-base': 14.0.1 + '@polkadot/rpc-augment': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-augment': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/api-base@12.4.2': dependencies: '@polkadot/rpc-core': 12.4.2 @@ -2349,6 +2474,18 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/api-base@14.0.1': + dependencies: + '@polkadot/rpc-core': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/util': 13.1.1 + rxjs: 7.8.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/api-derive@12.4.2': dependencies: '@polkadot/api': 12.4.2 @@ -2366,6 +2503,23 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/api-derive@14.0.1': + dependencies: + '@polkadot/api': 14.0.1 + '@polkadot/api-augment': 14.0.1 + '@polkadot/api-base': 14.0.1 + '@polkadot/rpc-core': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + rxjs: 7.8.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/api@12.4.2': dependencies: '@polkadot/api-augment': 12.4.2 @@ -2390,18 +2544,54 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/api@14.0.1': + dependencies: + '@polkadot/api-augment': 14.0.1 + '@polkadot/api-base': 14.0.1 + '@polkadot/api-derive': 14.0.1 + '@polkadot/keyring': 13.1.1(@polkadot/util-crypto@13.1.1(@polkadot/util@13.1.1))(@polkadot/util@13.1.1) + '@polkadot/rpc-augment': 14.0.1 + '@polkadot/rpc-core': 14.0.1 + '@polkadot/rpc-provider': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-augment': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/types-create': 14.0.1 + '@polkadot/types-known': 14.0.1 + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + eventemitter3: 5.0.1 + rxjs: 7.8.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/keyring@13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2)': dependencies: '@polkadot/util': 13.0.2 '@polkadot/util-crypto': 13.0.2(@polkadot/util@13.0.2) tslib: 2.6.3 + '@polkadot/keyring@13.1.1(@polkadot/util-crypto@13.1.1(@polkadot/util@13.1.1))(@polkadot/util@13.1.1)': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + tslib: 2.7.0 + '@polkadot/networks@13.0.2': dependencies: '@polkadot/util': 13.0.2 '@substrate/ss58-registry': 1.49.0 tslib: 2.6.3 + '@polkadot/networks@13.1.1': + dependencies: + '@polkadot/util': 13.1.1 + '@substrate/ss58-registry': 1.50.0 + tslib: 2.7.0 + '@polkadot/rpc-augment@12.4.2': dependencies: '@polkadot/rpc-core': 12.4.2 @@ -2414,6 +2604,18 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/rpc-augment@14.0.1': + dependencies: + '@polkadot/rpc-core': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/rpc-core@12.4.2': dependencies: '@polkadot/rpc-augment': 12.4.2 @@ -2427,6 +2629,19 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/rpc-core@14.0.1': + dependencies: + '@polkadot/rpc-augment': 14.0.1 + '@polkadot/rpc-provider': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/util': 13.1.1 + rxjs: 7.8.1 + tslib: 2.7.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/rpc-provider@12.4.2': dependencies: '@polkadot/keyring': 13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2) @@ -2448,6 +2663,27 @@ snapshots: - supports-color - utf-8-validate + '@polkadot/rpc-provider@14.0.1': + dependencies: + '@polkadot/keyring': 13.1.1(@polkadot/util-crypto@13.1.1(@polkadot/util@13.1.1))(@polkadot/util@13.1.1) + '@polkadot/types': 14.0.1 + '@polkadot/types-support': 14.0.1 + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + '@polkadot/x-fetch': 13.1.1 + '@polkadot/x-global': 13.1.1 + '@polkadot/x-ws': 13.1.1 + eventemitter3: 5.0.1 + mock-socket: 9.3.1 + nock: 13.5.5 + tslib: 2.7.0 + optionalDependencies: + '@substrate/connect': 0.8.11 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@polkadot/typegen@12.4.2': dependencies: '@polkadot/api': 12.4.2 @@ -2461,7 +2697,29 @@ snapshots: '@polkadot/types-support': 12.4.2 '@polkadot/util': 13.0.2 '@polkadot/util-crypto': 13.0.2(@polkadot/util@13.0.2) - '@polkadot/x-ws': 13.0.2 + '@polkadot/x-ws': 13.1.1 + handlebars: 4.7.8 + tslib: 2.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/typegen@14.0.1': + dependencies: + '@polkadot/api': 14.0.1 + '@polkadot/api-augment': 14.0.1 + '@polkadot/rpc-augment': 14.0.1 + '@polkadot/rpc-provider': 14.0.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-augment': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/types-create': 14.0.1 + '@polkadot/types-support': 14.0.1 + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + '@polkadot/x-ws': 13.1.1 handlebars: 4.7.8 tslib: 2.7.0 yargs: 17.7.2 @@ -2477,10 +2735,23 @@ snapshots: '@polkadot/util': 13.0.2 tslib: 2.7.0 + '@polkadot/types-augment@14.0.1': + dependencies: + '@polkadot/types': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + '@polkadot/types-codec@12.4.2': dependencies: '@polkadot/util': 13.0.2 - '@polkadot/x-bigint': 13.0.2 + '@polkadot/x-bigint': 13.1.1 + tslib: 2.7.0 + + '@polkadot/types-codec@14.0.1': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/x-bigint': 13.1.1 tslib: 2.7.0 '@polkadot/types-create@12.4.2': @@ -2489,6 +2760,12 @@ snapshots: '@polkadot/util': 13.0.2 tslib: 2.7.0 + '@polkadot/types-create@14.0.1': + dependencies: + '@polkadot/types-codec': 14.0.1 + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + '@polkadot/types-known@12.4.2': dependencies: '@polkadot/networks': 13.0.2 @@ -2498,11 +2775,25 @@ snapshots: '@polkadot/util': 13.0.2 tslib: 2.7.0 + '@polkadot/types-known@14.0.1': + dependencies: + '@polkadot/networks': 13.1.1 + '@polkadot/types': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/types-create': 14.0.1 + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + '@polkadot/types-support@12.4.2': dependencies: '@polkadot/util': 13.0.2 tslib: 2.7.0 + '@polkadot/types-support@14.0.1': + dependencies: + '@polkadot/util': 13.1.1 + tslib: 2.7.0 + '@polkadot/types@12.4.2': dependencies: '@polkadot/keyring': 13.0.2(@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2))(@polkadot/util@13.0.2) @@ -2514,6 +2805,17 @@ snapshots: rxjs: 7.8.1 tslib: 2.7.0 + '@polkadot/types@14.0.1': + dependencies: + '@polkadot/keyring': 13.1.1(@polkadot/util-crypto@13.1.1(@polkadot/util@13.1.1))(@polkadot/util@13.1.1) + '@polkadot/types-augment': 14.0.1 + '@polkadot/types-codec': 14.0.1 + '@polkadot/types-create': 14.0.1 + '@polkadot/util': 13.1.1 + '@polkadot/util-crypto': 13.1.1(@polkadot/util@13.1.1) + rxjs: 7.8.1 + tslib: 2.7.0 + '@polkadot/util-crypto@13.0.2(@polkadot/util@13.0.2)': dependencies: '@noble/curves': 1.5.0 @@ -2527,6 +2829,19 @@ snapshots: '@scure/base': 1.1.7 tslib: 2.6.3 + '@polkadot/util-crypto@13.1.1(@polkadot/util@13.1.1)': + dependencies: + '@noble/curves': 1.5.0 + '@noble/hashes': 1.5.0 + '@polkadot/networks': 13.1.1 + '@polkadot/util': 13.1.1 + '@polkadot/wasm-crypto': 7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1))) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/x-bigint': 13.1.1 + '@polkadot/x-randomvalues': 13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)) + '@scure/base': 1.1.7 + tslib: 2.7.0 + '@polkadot/util@13.0.2': dependencies: '@polkadot/x-bigint': 13.0.2 @@ -2537,6 +2852,16 @@ snapshots: bn.js: 5.2.1 tslib: 2.6.3 + '@polkadot/util@13.1.1': + dependencies: + '@polkadot/x-bigint': 13.1.1 + '@polkadot/x-global': 13.1.1 + '@polkadot/x-textdecoder': 13.1.1 + '@polkadot/x-textencoder': 13.1.1 + '@types/bn.js': 5.1.5 + bn.js: 5.2.1 + tslib: 2.7.0 + '@polkadot/wasm-bridge@7.3.2(@polkadot/util@13.0.2)(@polkadot/x-randomvalues@13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)))': dependencies: '@polkadot/util': 13.0.2 @@ -2544,11 +2869,23 @@ snapshots: '@polkadot/x-randomvalues': 13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)) tslib: 2.6.3 + '@polkadot/wasm-bridge@7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)))': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/x-randomvalues': 13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)) + tslib: 2.6.3 + '@polkadot/wasm-crypto-asmjs@7.3.2(@polkadot/util@13.0.2)': dependencies: '@polkadot/util': 13.0.2 tslib: 2.6.3 + '@polkadot/wasm-crypto-asmjs@7.3.2(@polkadot/util@13.1.1)': + dependencies: + '@polkadot/util': 13.1.1 + tslib: 2.6.3 + '@polkadot/wasm-crypto-init@7.3.2(@polkadot/util@13.0.2)(@polkadot/x-randomvalues@13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)))': dependencies: '@polkadot/util': 13.0.2 @@ -2559,12 +2896,28 @@ snapshots: '@polkadot/x-randomvalues': 13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)) tslib: 2.6.3 + '@polkadot/wasm-crypto-init@7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)))': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-bridge': 7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1))) + '@polkadot/wasm-crypto-asmjs': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/wasm-crypto-wasm': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/x-randomvalues': 13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)) + tslib: 2.6.3 + '@polkadot/wasm-crypto-wasm@7.3.2(@polkadot/util@13.0.2)': dependencies: '@polkadot/util': 13.0.2 '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.0.2) tslib: 2.6.3 + '@polkadot/wasm-crypto-wasm@7.3.2(@polkadot/util@13.1.1)': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + tslib: 2.6.3 + '@polkadot/wasm-crypto@7.3.2(@polkadot/util@13.0.2)(@polkadot/x-randomvalues@13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)))': dependencies: '@polkadot/util': 13.0.2 @@ -2576,26 +2929,57 @@ snapshots: '@polkadot/x-randomvalues': 13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)) tslib: 2.6.3 + '@polkadot/wasm-crypto@7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)))': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-bridge': 7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1))) + '@polkadot/wasm-crypto-asmjs': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/wasm-crypto-init': 7.3.2(@polkadot/util@13.1.1)(@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1))) + '@polkadot/wasm-crypto-wasm': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/x-randomvalues': 13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)) + tslib: 2.6.3 + '@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2)': dependencies: '@polkadot/util': 13.0.2 tslib: 2.6.3 + '@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1)': + dependencies: + '@polkadot/util': 13.1.1 + tslib: 2.6.3 + '@polkadot/x-bigint@13.0.2': dependencies: '@polkadot/x-global': 13.0.2 tslib: 2.6.3 + '@polkadot/x-bigint@13.1.1': + dependencies: + '@polkadot/x-global': 13.1.1 + tslib: 2.7.0 + '@polkadot/x-fetch@13.0.2': dependencies: '@polkadot/x-global': 13.0.2 node-fetch: 3.3.2 tslib: 2.7.0 + '@polkadot/x-fetch@13.1.1': + dependencies: + '@polkadot/x-global': 13.1.1 + node-fetch: 3.3.2 + tslib: 2.7.0 + '@polkadot/x-global@13.0.2': dependencies: tslib: 2.6.3 + '@polkadot/x-global@13.1.1': + dependencies: + tslib: 2.7.0 + '@polkadot/x-randomvalues@13.0.2(@polkadot/util@13.0.2)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.0.2))': dependencies: '@polkadot/util': 13.0.2 @@ -2603,16 +2987,33 @@ snapshots: '@polkadot/x-global': 13.0.2 tslib: 2.6.3 + '@polkadot/x-randomvalues@13.1.1(@polkadot/util@13.1.1)(@polkadot/wasm-util@7.3.2(@polkadot/util@13.1.1))': + dependencies: + '@polkadot/util': 13.1.1 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@13.1.1) + '@polkadot/x-global': 13.1.1 + tslib: 2.7.0 + '@polkadot/x-textdecoder@13.0.2': dependencies: '@polkadot/x-global': 13.0.2 tslib: 2.6.3 + '@polkadot/x-textdecoder@13.1.1': + dependencies: + '@polkadot/x-global': 13.1.1 + tslib: 2.7.0 + '@polkadot/x-textencoder@13.0.2': dependencies: '@polkadot/x-global': 13.0.2 tslib: 2.6.3 + '@polkadot/x-textencoder@13.1.1': + dependencies: + '@polkadot/x-global': 13.1.1 + tslib: 2.7.0 + '@polkadot/x-ws@13.0.2': dependencies: '@polkadot/x-global': 13.0.2 @@ -2622,6 +3023,15 @@ snapshots: - bufferutil - utf-8-validate + '@polkadot/x-ws@13.1.1': + dependencies: + '@polkadot/x-global': 13.1.1 + tslib: 2.7.0 + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -2683,6 +3093,8 @@ snapshots: '@substrate/ss58-registry@1.49.0': {} + '@substrate/ss58-registry@1.50.0': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} diff --git a/primitives/file-key-verifier/src/lib.rs b/primitives/file-key-verifier/src/lib.rs index 8d4c27d54..8f6b55e87 100644 --- a/primitives/file-key-verifier/src/lib.rs +++ b/primitives/file-key-verifier/src/lib.rs @@ -1,124 +1,124 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::sp_runtime::DispatchError; -use shp_file_metadata::ChunkId; -use shp_traits::CommitmentVerifier; -use sp_std::collections::btree_set::BTreeSet; -use sp_trie::{Trie, TrieDBBuilder, TrieLayout}; -use types::FileKeyProof; - -#[cfg(test)] -mod tests; - -pub mod types; - -/// A struct that implements the `CommitmentVerifier` trait, where the commitment -/// is a Merkle Patricia Trie root hash and the response to a challenge is given -/// by taking the modulo of the challenged hash with the number of chunks in the file, -/// and interpreting the result as a chunk index. -pub struct FileKeyVerifier< - T: TrieLayout, - const H_LENGTH: usize, - const CHUNK_SIZE: u64, - const SIZE_TO_CHALLENGES: u64, -> where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - pub _phantom: core::marker::PhantomData, -} - -/// Implement the `CommitmentVerifier` trait for the `FileKeyVerifier` struct. -impl< - T: TrieLayout, - const H_LENGTH: usize, - const CHUNK_SIZE: u64, - const SIZE_TO_CHALLENGES: u64, - > CommitmentVerifier for FileKeyVerifier -where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - type Proof = FileKeyProof; - type Commitment = ::Out; - type Challenge = ::Out; - - /// Verifies a proof against a root (i.e. commitment) and a set of challenges. - /// - /// Iterates over the challenges, computes the modulo of the challenged hashes with the number of chunks in the file, - /// and checks if the resulting leaf is in the proof. - fn verify_proof( - expected_file_key: &Self::Commitment, - challenges: &[Self::Challenge], - proof: &Self::Proof, - ) -> Result, DispatchError> { - // Check that `challenges` is not empty. - if challenges.is_empty() { - return Err("No challenges provided.".into()); - } - - // Construct file key from the fields in the proof. - let file_key = proof.file_metadata.file_key::(); - - // Check that the number of challenges is proportional to the size of the file. - let chunks_to_check = proof.file_metadata.chunks_to_check(); - if challenges.len() != chunks_to_check as usize { - return Err( - "Number of challenges does not match the number of chunks that should have been challenged for a file of this size.".into(), - ); - } - - // Check that the file key is equal to the root. - if &file_key != expected_file_key { - return Err( - "File key provided should be equal to the file key constructed from the proof." - .into(), - ); - }; - - // Convert the fingerprint from the proof to the output of the hasher. - let expected_root: &[u8; H_LENGTH] = &proof.file_metadata.fingerprint.into(); - let expected_root: Self::Commitment = expected_root - .try_into() - .map_err(|_| "Failed to convert fingerprint to a hasher output.")?; - - // This generates a partial trie based on the proof and checks that the root hash matches the `expected_root`. - let (memdb, root) = proof - .proof - .to_memory_db(Some(&expected_root)) - .map_err(|_| { - "Failed to convert proof to memory DB, root doesn't match with expected." - })?; - - let trie = TrieDBBuilder::::new(&memdb, &root).build(); - - // Initialise vector of proven challenges. We use a `BTreeSet` to ensure that the items are unique. - let mut proven_challenges = BTreeSet::new(); - let mut challenges_iter = challenges.iter(); - - // Iterate over the challenges, compute the modulo of the challenged hashes with the number of chunks in the file, - // and check if the resulting leaf is in the proof. - while let Some(challenge) = challenges_iter.next() { - // Calculate the chunks of the file based on its size. - let chunks = proof.file_metadata.chunks_count(); - - // Convert the challenge to a chunk ID. - let challenged_chunk = ChunkId::from_challenge(challenge.as_ref(), chunks); - - // Check that the chunk is in the proof. - let chunk = trie - .get(&challenged_chunk.as_trie_key()) - .map_err(|_| "The proof is invalid. The challenge does not exist in the trie.")?; - - // The chunk should be Some(leaf) for the proof to be valid. - if chunk.is_none() { - return Err( - "The proof is invalid. The challenged chunk was not found in the trie, possibly because the challenged chunk has an index higher than the amount of chunks in the file. This should not be possible, provided that the size of the file (and therefore number of chunks) is correct.".into(), - ); - } - - // Add the challenge to the proven challenges vector. - proven_challenges.insert(*challenge); - } - - Ok(proven_challenges) - } -} +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::sp_runtime::DispatchError; +use shp_file_metadata::ChunkId; +use shp_traits::CommitmentVerifier; +use sp_std::collections::btree_set::BTreeSet; +use sp_trie::{Trie, TrieDBBuilder, TrieLayout}; +use types::FileKeyProof; + +#[cfg(test)] +mod tests; + +pub mod types; + +/// A struct that implements the `CommitmentVerifier` trait, where the commitment +/// is a Merkle Patricia Trie root hash and the response to a challenge is given +/// by taking the modulo of the challenged hash with the number of chunks in the file, +/// and interpreting the result as a chunk index. +pub struct FileKeyVerifier< + T: TrieLayout, + const H_LENGTH: usize, + const CHUNK_SIZE: u64, + const SIZE_TO_CHALLENGES: u64, +> where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + pub _phantom: core::marker::PhantomData, +} + +/// Implement the `CommitmentVerifier` trait for the `FileKeyVerifier` struct. +impl< + T: TrieLayout, + const H_LENGTH: usize, + const CHUNK_SIZE: u64, + const SIZE_TO_CHALLENGES: u64, + > CommitmentVerifier for FileKeyVerifier +where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + type Proof = FileKeyProof; + type Commitment = ::Out; + type Challenge = ::Out; + + /// Verifies a proof against a root (i.e. commitment) and a set of challenges. + /// + /// Iterates over the challenges, computes the modulo of the challenged hashes with the number of chunks in the file, + /// and checks if the resulting leaf is in the proof. + fn verify_proof( + expected_file_key: &Self::Commitment, + challenges: &[Self::Challenge], + proof: &Self::Proof, + ) -> Result, DispatchError> { + // Check that `challenges` is not empty. + if challenges.is_empty() { + return Err("No challenges provided.".into()); + } + + // Construct file key from the fields in the proof. + let file_key = proof.file_metadata.file_key::(); + + // Check that the number of challenges is proportional to the size of the file. + let chunks_to_check = proof.file_metadata.chunks_to_check(); + if challenges.len() != chunks_to_check as usize { + return Err( + "Number of challenges does not match the number of chunks that should have been challenged for a file of this size.".into(), + ); + } + + // Check that the file key is equal to the root. + if &file_key != expected_file_key { + return Err( + "File key provided should be equal to the file key constructed from the proof." + .into(), + ); + }; + + // Convert the fingerprint from the proof to the output of the hasher. + let expected_root: &[u8; H_LENGTH] = &proof.file_metadata.fingerprint.into(); + let expected_root: Self::Commitment = expected_root + .try_into() + .map_err(|_| "Failed to convert fingerprint to a hasher output.")?; + + // This generates a partial trie based on the proof and checks that the root hash matches the `expected_root`. + let (memdb, root) = proof + .proof + .to_memory_db(Some(&expected_root)) + .map_err(|_| { + "Failed to convert proof to memory DB, root doesn't match with expected." + })?; + + let trie = TrieDBBuilder::::new(&memdb, &root).build(); + + // Initialise vector of proven challenges. We use a `BTreeSet` to ensure that the items are unique. + let mut proven_challenges = BTreeSet::new(); + let mut challenges_iter = challenges.iter(); + + // Iterate over the challenges, compute the modulo of the challenged hashes with the number of chunks in the file, + // and check if the resulting leaf is in the proof. + while let Some(challenge) = challenges_iter.next() { + // Calculate the chunks of the file based on its size. + let chunks = proof.file_metadata.chunks_count(); + + // Convert the challenge to a chunk ID. + let challenged_chunk = ChunkId::from_challenge(challenge.as_ref(), chunks); + + // Check that the chunk is in the proof. + let chunk = trie + .get(&challenged_chunk.as_trie_key()) + .map_err(|_| "The proof is invalid. The challenge does not exist in the trie.")?; + + // The chunk should be Some(leaf) for the proof to be valid. + if chunk.is_none() { + return Err( + "The proof is invalid. The challenged chunk was not found in the trie, possibly because the challenged chunk has an index higher than the amount of chunks in the file. This should not be possible, provided that the size of the file (and therefore number of chunks) is correct.".into(), + ); + } + + // Add the challenge to the proven challenges vector. + proven_challenges.insert(*challenge); + } + + Ok(proven_challenges) + } +} diff --git a/primitives/file-key-verifier/src/tests.rs b/primitives/file-key-verifier/src/tests.rs index b630c4cf3..84264607c 100644 --- a/primitives/file-key-verifier/src/tests.rs +++ b/primitives/file-key-verifier/src/tests.rs @@ -1,1061 +1,1061 @@ -use codec::Encode; -use num_bigint::BigUint; -use rand::Rng; -use shp_file_metadata::ChunkId; -use shp_file_metadata::FileMetadata; -use shp_file_metadata::Fingerprint; -use shp_traits::{AsCompact, CommitmentVerifier}; -use sp_runtime::traits::{BlakeTwo256, Keccak256}; -use sp_trie::{ - recorder::Recorder, CompactProof, LayoutV1, MemoryDB, TrieDBBuilder, TrieDBMutBuilder, - TrieLayout, TrieMut, -}; -use trie_db::{Hasher, Trie, TrieIterator}; - -use crate::{FileKeyProof, FileKeyVerifier}; - -/// The hash type of trie node keys -type HashT = <::Hash as Hasher>::Out; - -const H_LENGTH: usize = 32; -const CHUNK_SIZE: u64 = 2; -const FILE_SIZE: u64 = 2u64.pow(11); -const SIZE_TO_CHALLENGES: u64 = FILE_SIZE / 10; - -/// Build a Merkle Patricia Trie simulating a file split in chunks. -fn build_merkle_patricia_trie( - random: bool, - file_size: u64, -) -> ( - MemoryDB, - HashT, - FileMetadata, -) -where - ::Out: for<'a> TryFrom<&'a [u8; 32]>, -{ - let user_id = b"user_id"; - let bucket = b"bucket"; - let file_name = if random { - String::from("random_file") + &file_size.to_string() - } else { - String::from("large_file") + &file_size.to_string() - }; - let file_name = file_name.as_bytes(); - - println!("Chunking file into 32 byte chunks and building Merkle Patricia Trie..."); - - let data = if random { - create_random_test_data(file_size) - } else { - create_sequential_test_data(file_size) - }; - - let (memdb, fingerprint) = merklise_data::(&data); - - let file_path = format!( - "{}-{}-{}.txt", - String::from_utf8(user_id.to_vec()).unwrap(), - String::from_utf8(bucket.to_vec()).unwrap(), - String::from_utf8(file_name.to_vec()).unwrap() - ); - - let file_metadata = FileMetadata { - owner: user_id.to_vec(), - bucket_id: bucket.to_vec(), - location: file_path.as_bytes().to_vec(), - file_size, - fingerprint: fingerprint - .as_ref() - .try_into() - .expect("slice with incorrect length"), - }; - - let file_key = file_metadata.file_key::(); - - (memdb, file_key, file_metadata) -} - -/// Chunk data into [`CHUNK_SIZE`] byte chunks and store them in a Merkle Patricia Trie. -/// -/// The trie is stored in a [`MemoryDB`] and the [`Root`] is returned. -pub fn merklise_data(data: &[u8]) -> (MemoryDB, HashT) { - let mut buf = [0; CHUNK_SIZE as usize]; - let mut chunks = Vec::new(); - - // create list of key-value pairs consisting of chunk metadata and chunk data - let mut chunk_i = 0u64; - let mut offset = 0; - - while offset < data.len() { - // Determine the end of the current chunk - let end = std::cmp::min(offset + CHUNK_SIZE as usize, data.len()); - - // Copy the current chunk from data into the buffer - buf[..end - offset].copy_from_slice(&data[offset..end]); - - // Store the current chunk as a vector in the chunks vector - chunks.push((chunk_i, buf[..end - offset].to_vec())); - - // Increment the chunk index - chunk_i += 1; - - // Move the offset forward by CHUNK_SIZE - offset += CHUNK_SIZE as usize; - } - - let mut memdb = MemoryDB::::default(); - let mut root = Default::default(); - { - let mut t = TrieDBMutBuilder::::new(&mut memdb, &mut root).build(); - for (k, v) in &chunks { - t.insert(&ChunkId::new(*k).as_trie_key(), v).unwrap(); - } - } - - println!("Data fingerprint (root): {:?}", root); - - (memdb, root) -} - -/// Generate sequential test data of a given size. -pub fn create_sequential_test_data(size: u64) -> Vec { - let mut i = 0u8; - let content: Vec = (0..size) - .map(|_| { - i = i % (u8::MAX - 1); - i += 1; - i - }) - .collect(); - content -} - -/// Generate random test data of a given size. -pub fn create_random_test_data(size: u64) -> Vec { - let mut rng = rand::thread_rng(); - let content: Vec = (0..size).map(|_| rng.gen()).collect(); - content -} - -fn generate_challenges( - challenges_count: u32, - chunks_count: u64, -) -> (Vec>, Vec>) { - let mut challenges = Vec::new(); - let mut chunks_challenged = Vec::new(); - - for i in 0..challenges_count { - // Generate challenge as a hash. - let hash_arg = "chunk".to_string() + i.to_string().as_str(); - let challenge = T::Hash::hash(hash_arg.as_bytes()); - challenges.push(challenge); - - // Calculate the modulo of the challenge with the number of chunks in the file. - // The challenge is a big endian 32 byte array. - let challenged_chunk = BigUint::from_bytes_be(challenge.as_ref()) % chunks_count; - let challenged_chunk: u64 = challenged_chunk.try_into().expect( - "This is impossible. The modulo of a number with a u64 should always fit in a u64.", - ); - - chunks_challenged.push(AsCompact(challenged_chunk).encode()); - } - - (challenges, chunks_challenged) -} - -#[test] -fn generate_trie_works() { - let (memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - let trie = TrieDBBuilder::>::new(&memdb, &root).build(); - - println!("Trie root: {:?}", trie.root()); - - // Count the number of leaves in the trie. - // This should be the same as the number of chunks in the file. - let mut leaves_count = 0; - let mut trie_iter = trie.iter().unwrap(); - while let Some(_) = trie_iter.next() { - leaves_count += 1; - } - - let chunks_count = file_metadata.chunks_count(); - - assert_eq!(leaves_count, chunks_count); - - println!("Number of leaves: {:?}", leaves_count); -} - -#[test] -fn commitment_verifier_many_challenges_success() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - let proven_challenges = FileKeyVerifier::< - LayoutV1, - { BlakeTwo256::LENGTH }, - { CHUNK_SIZE }, - { SIZE_TO_CHALLENGES }, - >::verify_proof(&file_key, &challenges, &file_key_proof) - .expect("Failed to verify proof"); - - assert_eq!( - proven_challenges.into_iter().collect::>().sort(), - challenges.sort() - ); -} - -#[test] -fn commitment_verifier_many_challenges_random_file_success() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(true, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - let proven_challenges = FileKeyVerifier::< - LayoutV1, - { BlakeTwo256::LENGTH }, - { CHUNK_SIZE }, - { SIZE_TO_CHALLENGES }, - >::verify_proof(&file_key, &challenges, &file_key_proof) - .expect("Failed to verify proof"); - - assert_eq!( - proven_challenges.into_iter().collect::>().sort(), - challenges.sort() - ); -} - -#[test] -fn commitment_verifier_many_challenges_keccak_success() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - let proven_challenges = FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof) - .expect("Failed to verify proof"); - - assert_eq!( - proven_challenges.into_iter().collect::>().sort(), - challenges.sort() - ); -} - -#[test] -fn commitment_verifier_many_challenges_one_chunk_success() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, CHUNK_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - assert!(chunks_count == 1); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - let proven_challenges = FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof) - .expect("Failed to verify proof"); - - assert_eq!( - proven_challenges.into_iter().collect::>().sort(), - challenges.sort() - ); -} - -#[test] -fn commitment_verifier_many_challenges_two_chunks_success() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, CHUNK_SIZE + 1); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - assert!(chunks_count == 2); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - let proven_challenges = FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof) - .expect("Failed to verify proof"); - - assert_eq!( - proven_challenges.into_iter().collect::>().sort(), - challenges.sort() - ); -} - -#[test] -fn commitment_verifier_no_challenges_failure() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let (challenges, chunks_challenged) = - generate_challenges::>(0, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("No challenges provided.".into()) - ); -} - -#[test] -fn commitment_verifier_wrong_number_of_challenges_failure() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count - 1, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("Number of challenges does not match the number of chunks that should have been challenged for a file of this size.".into()) - ); -} - -#[test] -fn commitment_verifier_wrong_file_key_failure() { - let (memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&Default::default(), &challenges, &file_key_proof), - Err("File key provided should be equal to the file key constructed from the proof.".into()) - ); -} - -#[test] -fn commitment_verifier_wrong_file_key_no_compact_encoding_failure() { - let (memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - let file_key = BlakeTwo256::hash( - &[ - &file_metadata.owner.encode(), - &file_metadata.location.encode(), - &file_metadata.file_size.encode(), - &file_metadata.fingerprint.encode(), - ] - .into_iter() - .flatten() - .cloned() - .collect::>(), - ); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - { BlakeTwo256::LENGTH }, - { CHUNK_SIZE }, - { SIZE_TO_CHALLENGES }, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("File key provided should be equal to the file key constructed from the proof.".into()) - ); -} - -#[test] -fn commitment_verifier_wrong_file_key_vec_fingerprint_failure() { - let (memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - let file_key = BlakeTwo256::hash( - &[ - &file_metadata.owner.encode(), - &file_metadata.location.encode(), - &AsCompact(file_metadata.file_size).encode(), - &file_metadata.fingerprint.as_hash().to_vec().encode(), - ] - .into_iter() - .flatten() - .cloned() - .collect::>(), - ); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - { BlakeTwo256::LENGTH }, - { CHUNK_SIZE }, - { SIZE_TO_CHALLENGES }, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("File key provided should be equal to the file key constructed from the proof.".into()) - ); -} - -#[test] -fn commitment_verifier_wrong_file_key_encoding_as_bytes_failure() { - let (memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - let file_key = BlakeTwo256::hash( - &[ - &file_metadata.owner, - &file_metadata.location, - &file_metadata.file_size.to_be_bytes().to_vec(), - &file_metadata.fingerprint.as_hash().to_vec(), - ] - .into_iter() - .flatten() - .cloned() - .collect::>(), - ); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("File key provided should be equal to the file key constructed from the proof.".into()) - ); -} - -#[test] -fn commitment_verifier_empty_proof_failure() { - let (_memdb, _file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - - let file_key = file_metadata.file_key::(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, _) = - generate_challenges::>(challenges_count, chunks_count); - - // Generate proof - let proof = CompactProof { - encoded_nodes: vec![], - }; - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("Failed to convert proof to memory DB, root doesn't match with expected.".into()) - ); -} - -#[test] -fn commitment_verifier_empty_fingerprint_failure() { - let (_memdb, _file_key, mut file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - - file_metadata.fingerprint = Fingerprint::default(); - - let file_key = file_metadata.file_key::(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, _) = - generate_challenges::>(challenges_count, chunks_count); - - // Generate proof - let proof = CompactProof { - encoded_nodes: vec![], - }; - let file_key_proof = FileKeyProof { - file_metadata: FileMetadata { - fingerprint: Fingerprint::default(), - ..file_metadata - }, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("Failed to convert proof to memory DB, root doesn't match with expected.".into()) - ); -} - -#[test] -fn commitment_verifier_challenge_missing_from_proof_failure() { - let (memdb, file_key, file_metadata) = - build_merkle_patricia_trie::>(false, FILE_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (mut challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Change one challenge so that the proof is invalid. - challenges[0] = BlakeTwo256::hash("invalid_challenge".as_bytes()); - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("The proof is invalid. The challenge does not exist in the trie.".into()) - ); -} - -#[test] -fn commitment_verifier_challenge_with_none_value_failure() { - let (memdb, _file_key, mut file_metadata) = - build_merkle_patricia_trie::>(false, 2 * CHUNK_SIZE); - let root = file_metadata.fingerprint.as_hash().into(); - - file_metadata.file_size = FILE_SIZE; - - let file_key = file_metadata.file_key::(); - - // This recorder is used to record accessed keys in the trie and later generate a proof for them. - let recorder: Recorder = Recorder::default(); - - let chunks_count = file_metadata.chunks_count(); - let challenges_count = file_metadata.chunks_to_check(); - - let (challenges, chunks_challenged) = - generate_challenges::>(challenges_count, chunks_count); - - { - // Creating trie inside of closure to drop it before generating proof. - let mut trie_recorder = recorder.as_trie_recorder(root); - let trie = TrieDBBuilder::>::new(&memdb, &root) - .with_recorder(&mut trie_recorder) - .build(); - - // Create an iterator over the leaf nodes. - let mut iter = trie.into_double_ended_iter().unwrap(); - - for challenged_chunk in chunks_challenged { - // Seek to the challenge key. - iter.seek(&challenged_chunk).unwrap(); - - // Read the leaf node. - iter.next(); - } - } - - // Generate proof - let proof = recorder - .drain_storage_proof() - .to_compact_proof::(root) - .expect("Failed to create compact proof from recorder"); - // Using wrong file size (larger than it actually is) - let file_key_proof = FileKeyProof { - file_metadata, - proof, - }; - - // Verify proof - assert_eq!( - FileKeyVerifier::< - LayoutV1, - H_LENGTH, - CHUNK_SIZE, - SIZE_TO_CHALLENGES, - >::verify_proof(&file_key, &challenges, &file_key_proof), - Err("The proof is invalid. The challenged chunk was not found in the trie, possibly because the challenged chunk has an index higher than the amount of chunks in the file. This should not be possible, provided that the size of the file (and therefore number of chunks) is correct.".into()) - ); -} - -#[test] -fn chunk_id_convert_to_and_from_trie_key() { - let chunk_id = ChunkId::new(0x12345678u64); - let chunk_id_bytes = chunk_id.as_trie_key(); - let chunk_id_decoded = ChunkId::from_trie_key(&chunk_id_bytes).unwrap(); - assert_eq!(chunk_id, chunk_id_decoded); -} +use codec::Encode; +use num_bigint::BigUint; +use rand::Rng; +use shp_file_metadata::ChunkId; +use shp_file_metadata::FileMetadata; +use shp_file_metadata::Fingerprint; +use shp_traits::{AsCompact, CommitmentVerifier}; +use sp_runtime::traits::{BlakeTwo256, Keccak256}; +use sp_trie::{ + recorder::Recorder, CompactProof, LayoutV1, MemoryDB, TrieDBBuilder, TrieDBMutBuilder, + TrieLayout, TrieMut, +}; +use trie_db::{Hasher, Trie, TrieIterator}; + +use crate::{FileKeyProof, FileKeyVerifier}; + +/// The hash type of trie node keys +type HashT = <::Hash as Hasher>::Out; + +const H_LENGTH: usize = 32; +const CHUNK_SIZE: u64 = 2; +const FILE_SIZE: u64 = 2u64.pow(11); +const SIZE_TO_CHALLENGES: u64 = FILE_SIZE / 10; + +/// Build a Merkle Patricia Trie simulating a file split in chunks. +fn build_merkle_patricia_trie( + random: bool, + file_size: u64, +) -> ( + MemoryDB, + HashT, + FileMetadata, +) +where + ::Out: for<'a> TryFrom<&'a [u8; 32]>, +{ + let user_id = b"user_id"; + let bucket = b"bucket"; + let file_name = if random { + String::from("random_file") + &file_size.to_string() + } else { + String::from("large_file") + &file_size.to_string() + }; + let file_name = file_name.as_bytes(); + + println!("Chunking file into 32 byte chunks and building Merkle Patricia Trie..."); + + let data = if random { + create_random_test_data(file_size) + } else { + create_sequential_test_data(file_size) + }; + + let (memdb, fingerprint) = merklise_data::(&data); + + let file_path = format!( + "{}-{}-{}.txt", + String::from_utf8(user_id.to_vec()).unwrap(), + String::from_utf8(bucket.to_vec()).unwrap(), + String::from_utf8(file_name.to_vec()).unwrap() + ); + + let file_metadata = FileMetadata { + owner: user_id.to_vec(), + bucket_id: bucket.to_vec(), + location: file_path.as_bytes().to_vec(), + file_size, + fingerprint: fingerprint + .as_ref() + .try_into() + .expect("slice with incorrect length"), + }; + + let file_key = file_metadata.file_key::(); + + (memdb, file_key, file_metadata) +} + +/// Chunk data into [`CHUNK_SIZE`] byte chunks and store them in a Merkle Patricia Trie. +/// +/// The trie is stored in a [`MemoryDB`] and the [`Root`] is returned. +pub fn merklise_data(data: &[u8]) -> (MemoryDB, HashT) { + let mut buf = [0; CHUNK_SIZE as usize]; + let mut chunks = Vec::new(); + + // create list of key-value pairs consisting of chunk metadata and chunk data + let mut chunk_i = 0u64; + let mut offset = 0; + + while offset < data.len() { + // Determine the end of the current chunk + let end = std::cmp::min(offset + CHUNK_SIZE as usize, data.len()); + + // Copy the current chunk from data into the buffer + buf[..end - offset].copy_from_slice(&data[offset..end]); + + // Store the current chunk as a vector in the chunks vector + chunks.push((chunk_i, buf[..end - offset].to_vec())); + + // Increment the chunk index + chunk_i += 1; + + // Move the offset forward by CHUNK_SIZE + offset += CHUNK_SIZE as usize; + } + + let mut memdb = MemoryDB::::default(); + let mut root = Default::default(); + { + let mut t = TrieDBMutBuilder::::new(&mut memdb, &mut root).build(); + for (k, v) in &chunks { + t.insert(&ChunkId::new(*k).as_trie_key(), v).unwrap(); + } + } + + println!("Data fingerprint (root): {:?}", root); + + (memdb, root) +} + +/// Generate sequential test data of a given size. +pub fn create_sequential_test_data(size: u64) -> Vec { + let mut i = 0u8; + let content: Vec = (0..size) + .map(|_| { + i = i % (u8::MAX - 1); + i += 1; + i + }) + .collect(); + content +} + +/// Generate random test data of a given size. +pub fn create_random_test_data(size: u64) -> Vec { + let mut rng = rand::thread_rng(); + let content: Vec = (0..size).map(|_| rng.gen()).collect(); + content +} + +fn generate_challenges( + challenges_count: u32, + chunks_count: u64, +) -> (Vec>, Vec>) { + let mut challenges = Vec::new(); + let mut chunks_challenged = Vec::new(); + + for i in 0..challenges_count { + // Generate challenge as a hash. + let hash_arg = "chunk".to_string() + i.to_string().as_str(); + let challenge = T::Hash::hash(hash_arg.as_bytes()); + challenges.push(challenge); + + // Calculate the modulo of the challenge with the number of chunks in the file. + // The challenge is a big endian 32 byte array. + let challenged_chunk = BigUint::from_bytes_be(challenge.as_ref()) % chunks_count; + let challenged_chunk: u64 = challenged_chunk.try_into().expect( + "This is impossible. The modulo of a number with a u64 should always fit in a u64.", + ); + + chunks_challenged.push(AsCompact(challenged_chunk).encode()); + } + + (challenges, chunks_challenged) +} + +#[test] +fn generate_trie_works() { + let (memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + let trie = TrieDBBuilder::>::new(&memdb, &root).build(); + + println!("Trie root: {:?}", trie.root()); + + // Count the number of leaves in the trie. + // This should be the same as the number of chunks in the file. + let mut leaves_count = 0; + let mut trie_iter = trie.iter().unwrap(); + while let Some(_) = trie_iter.next() { + leaves_count += 1; + } + + let chunks_count = file_metadata.chunks_count(); + + assert_eq!(leaves_count, chunks_count); + + println!("Number of leaves: {:?}", leaves_count); +} + +#[test] +fn commitment_verifier_many_challenges_success() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + let proven_challenges = FileKeyVerifier::< + LayoutV1, + { BlakeTwo256::LENGTH }, + { CHUNK_SIZE }, + { SIZE_TO_CHALLENGES }, + >::verify_proof(&file_key, &challenges, &file_key_proof) + .expect("Failed to verify proof"); + + assert_eq!( + proven_challenges.into_iter().collect::>().sort(), + challenges.sort() + ); +} + +#[test] +fn commitment_verifier_many_challenges_random_file_success() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(true, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + let proven_challenges = FileKeyVerifier::< + LayoutV1, + { BlakeTwo256::LENGTH }, + { CHUNK_SIZE }, + { SIZE_TO_CHALLENGES }, + >::verify_proof(&file_key, &challenges, &file_key_proof) + .expect("Failed to verify proof"); + + assert_eq!( + proven_challenges.into_iter().collect::>().sort(), + challenges.sort() + ); +} + +#[test] +fn commitment_verifier_many_challenges_keccak_success() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + let proven_challenges = FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof) + .expect("Failed to verify proof"); + + assert_eq!( + proven_challenges.into_iter().collect::>().sort(), + challenges.sort() + ); +} + +#[test] +fn commitment_verifier_many_challenges_one_chunk_success() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, CHUNK_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + assert!(chunks_count == 1); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + let proven_challenges = FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof) + .expect("Failed to verify proof"); + + assert_eq!( + proven_challenges.into_iter().collect::>().sort(), + challenges.sort() + ); +} + +#[test] +fn commitment_verifier_many_challenges_two_chunks_success() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, CHUNK_SIZE + 1); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + assert!(chunks_count == 2); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + let proven_challenges = FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof) + .expect("Failed to verify proof"); + + assert_eq!( + proven_challenges.into_iter().collect::>().sort(), + challenges.sort() + ); +} + +#[test] +fn commitment_verifier_no_challenges_failure() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let (challenges, chunks_challenged) = + generate_challenges::>(0, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("No challenges provided.".into()) + ); +} + +#[test] +fn commitment_verifier_wrong_number_of_challenges_failure() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count - 1, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("Number of challenges does not match the number of chunks that should have been challenged for a file of this size.".into()) + ); +} + +#[test] +fn commitment_verifier_wrong_file_key_failure() { + let (memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&Default::default(), &challenges, &file_key_proof), + Err("File key provided should be equal to the file key constructed from the proof.".into()) + ); +} + +#[test] +fn commitment_verifier_wrong_file_key_no_compact_encoding_failure() { + let (memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + let file_key = BlakeTwo256::hash( + &[ + &file_metadata.owner.encode(), + &file_metadata.location.encode(), + &file_metadata.file_size.encode(), + &file_metadata.fingerprint.encode(), + ] + .into_iter() + .flatten() + .cloned() + .collect::>(), + ); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + { BlakeTwo256::LENGTH }, + { CHUNK_SIZE }, + { SIZE_TO_CHALLENGES }, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("File key provided should be equal to the file key constructed from the proof.".into()) + ); +} + +#[test] +fn commitment_verifier_wrong_file_key_vec_fingerprint_failure() { + let (memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + let file_key = BlakeTwo256::hash( + &[ + &file_metadata.owner.encode(), + &file_metadata.location.encode(), + &AsCompact(file_metadata.file_size).encode(), + &file_metadata.fingerprint.as_hash().to_vec().encode(), + ] + .into_iter() + .flatten() + .cloned() + .collect::>(), + ); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + { BlakeTwo256::LENGTH }, + { CHUNK_SIZE }, + { SIZE_TO_CHALLENGES }, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("File key provided should be equal to the file key constructed from the proof.".into()) + ); +} + +#[test] +fn commitment_verifier_wrong_file_key_encoding_as_bytes_failure() { + let (memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + let file_key = BlakeTwo256::hash( + &[ + &file_metadata.owner, + &file_metadata.location, + &file_metadata.file_size.to_be_bytes().to_vec(), + &file_metadata.fingerprint.as_hash().to_vec(), + ] + .into_iter() + .flatten() + .cloned() + .collect::>(), + ); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("File key provided should be equal to the file key constructed from the proof.".into()) + ); +} + +#[test] +fn commitment_verifier_empty_proof_failure() { + let (_memdb, _file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + + let file_key = file_metadata.file_key::(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, _) = + generate_challenges::>(challenges_count, chunks_count); + + // Generate proof + let proof = CompactProof { + encoded_nodes: vec![], + }; + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("Failed to convert proof to memory DB, root doesn't match with expected.".into()) + ); +} + +#[test] +fn commitment_verifier_empty_fingerprint_failure() { + let (_memdb, _file_key, mut file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + + file_metadata.fingerprint = Fingerprint::default(); + + let file_key = file_metadata.file_key::(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, _) = + generate_challenges::>(challenges_count, chunks_count); + + // Generate proof + let proof = CompactProof { + encoded_nodes: vec![], + }; + let file_key_proof = FileKeyProof { + file_metadata: FileMetadata { + fingerprint: Fingerprint::default(), + ..file_metadata + }, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("Failed to convert proof to memory DB, root doesn't match with expected.".into()) + ); +} + +#[test] +fn commitment_verifier_challenge_missing_from_proof_failure() { + let (memdb, file_key, file_metadata) = + build_merkle_patricia_trie::>(false, FILE_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (mut challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Change one challenge so that the proof is invalid. + challenges[0] = BlakeTwo256::hash("invalid_challenge".as_bytes()); + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("The proof is invalid. The challenge does not exist in the trie.".into()) + ); +} + +#[test] +fn commitment_verifier_challenge_with_none_value_failure() { + let (memdb, _file_key, mut file_metadata) = + build_merkle_patricia_trie::>(false, 2 * CHUNK_SIZE); + let root = file_metadata.fingerprint.as_hash().into(); + + file_metadata.file_size = FILE_SIZE; + + let file_key = file_metadata.file_key::(); + + // This recorder is used to record accessed keys in the trie and later generate a proof for them. + let recorder: Recorder = Recorder::default(); + + let chunks_count = file_metadata.chunks_count(); + let challenges_count = file_metadata.chunks_to_check(); + + let (challenges, chunks_challenged) = + generate_challenges::>(challenges_count, chunks_count); + + { + // Creating trie inside of closure to drop it before generating proof. + let mut trie_recorder = recorder.as_trie_recorder(root); + let trie = TrieDBBuilder::>::new(&memdb, &root) + .with_recorder(&mut trie_recorder) + .build(); + + // Create an iterator over the leaf nodes. + let mut iter = trie.into_double_ended_iter().unwrap(); + + for challenged_chunk in chunks_challenged { + // Seek to the challenge key. + iter.seek(&challenged_chunk).unwrap(); + + // Read the leaf node. + iter.next(); + } + } + + // Generate proof + let proof = recorder + .drain_storage_proof() + .to_compact_proof::(root) + .expect("Failed to create compact proof from recorder"); + // Using wrong file size (larger than it actually is) + let file_key_proof = FileKeyProof { + file_metadata, + proof, + }; + + // Verify proof + assert_eq!( + FileKeyVerifier::< + LayoutV1, + H_LENGTH, + CHUNK_SIZE, + SIZE_TO_CHALLENGES, + >::verify_proof(&file_key, &challenges, &file_key_proof), + Err("The proof is invalid. The challenged chunk was not found in the trie, possibly because the challenged chunk has an index higher than the amount of chunks in the file. This should not be possible, provided that the size of the file (and therefore number of chunks) is correct.".into()) + ); +} + +#[test] +fn chunk_id_convert_to_and_from_trie_key() { + let chunk_id = ChunkId::new(0x12345678u64); + let chunk_id_bytes = chunk_id.as_trie_key(); + let chunk_id_decoded = ChunkId::from_trie_key(&chunk_id_bytes).unwrap(); + assert_eq!(chunk_id, chunk_id_decoded); +} diff --git a/primitives/file-metadata/src/lib.rs b/primitives/file-metadata/src/lib.rs index 4df39a4cc..3c1dc8900 100644 --- a/primitives/file-metadata/src/lib.rs +++ b/primitives/file-metadata/src/lib.rs @@ -1,286 +1,286 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Compact, Decode, Encode}; -use core::fmt::Debug; -use num_bigint::BigUint; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use shp_traits::{AsCompact, FileMetadataInterface}; -use sp_arithmetic::traits::SaturatedConversion; -use sp_core::{crypto::AccountId32, H256}; -use sp_std::vec::Vec; - -/// A struct containing all the information about a file in StorageHub. -/// -/// It also provides utility functions like calculating the number of chunks in a file, -/// the last chunk ID, and generating a file key for a given file metadata. -#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, Encode, Decode, Serialize, Deserialize)] -pub struct FileMetadata -{ - pub owner: Vec, - pub bucket_id: Vec, - pub location: Vec, - #[codec(compact)] - pub file_size: u64, - pub fingerprint: Fingerprint, -} - -/// Maximum number of chunks a Storage Provider would need to prove for a file. -const MAX_CHUNKS_TO_CHECK: u32 = 10; - -impl - FileMetadata -{ - pub fn new( - owner: Vec, - bucket_id: Vec, - location: Vec, - size: u64, - fingerprint: Fingerprint, - ) -> Self { - Self { - owner, - bucket_id, - location, - file_size: size, - fingerprint, - } - } - - pub fn file_key(&self) -> T::Out { - T::hash(self.encode().as_slice()) - } - - pub fn chunks_to_check(&self) -> u32 { - // In here we downcast and saturate to u32, as we're going to saturate to MAX_CHUNKS_TO_CHECK anyway. - let chunks = (self.file_size / SIZE_TO_CHALLENGES - + (self.file_size % SIZE_TO_CHALLENGES != 0) as u64) - .saturated_into::(); - - // Cap chunks to check at MAX_CHUNKS_TO_CHECK. - // This maximum number of chunks is based on the issue raised in the audit https://github.com/Moonsong-Labs/internal-storage-hub-design-audit/issues/11. - chunks.min(MAX_CHUNKS_TO_CHECK) - } - - pub fn chunks_count(&self) -> u64 { - self.file_size / CHUNK_SIZE + (self.file_size % CHUNK_SIZE != 0) as u64 - } - - pub fn last_chunk_id(&self) -> ChunkId { - ChunkId::new(self.chunks_count() - 1) - } -} - -/// Interface for encoding and decoding FileMetadata, used by the runtime. -impl - FileMetadataInterface for FileMetadata -{ - type AccountId = AccountId32; - type Metadata = Self; - type StorageDataUnit = u64; - - fn encode(metadata: &Self::Metadata) -> Vec { - metadata.encode() - } - - fn decode(data: &[u8]) -> Result { - as Decode>::decode(&mut &data[..]) - } - - fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { - metadata.file_size - } - - fn get_file_owner(metadata: &Self::Metadata) -> Result { - Self::AccountId::decode(&mut metadata.owner.as_slice()) - } -} - -/// FileKey is the identifier for a file. -/// Computed as the hash of the SCALE-encoded FileMetadata. -#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct FileKey(Hash); - -impl From> for FileKey { - fn from(hash: Hash) -> Self { - Self(hash) - } -} - -impl Into> for FileKey { - fn into(self) -> Hash { - self.0 - } -} - -impl From for FileKey<32> { - fn from(hash: H256) -> Self { - let mut file_key = [0u8; 32]; - file_key.copy_from_slice(hash.as_bytes()); - Self(file_key) - } -} - -impl Into for FileKey<32> { - fn into(self) -> H256 { - H256::from_slice(&self.0) - } -} - -impl From<&[u8]> for FileKey { - fn from(bytes: &[u8]) -> Self { - let mut hash = [0u8; H_LENGTH]; - hash.copy_from_slice(&bytes); - Self(hash) - } -} - -impl AsRef<[u8]> for FileKey { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl From<&[u8; H_LENGTH]> for FileKey { - fn from(bytes: &[u8; H_LENGTH]) -> Self { - Self(*bytes) - } -} - -impl AsRef<[u8; H_LENGTH]> for FileKey { - fn as_ref(&self) -> &[u8; H_LENGTH] { - &self.0 - } -} - -/// A fingerprint is something that uniquely identifies the content of a file. -/// In the context of this crate, a fingerprint is the root hash of a Merkle Patricia Trie -/// of the merklised file. -#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] -pub struct Fingerprint(Hash); - -impl Default for Fingerprint { - fn default() -> Self { - Self([0u8; H_LENGTH]) - } -} - -impl Serialize for Fingerprint { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - self.0.to_vec().serialize(serializer) - } -} - -impl<'de, const H_LENGTH: usize> Deserialize<'de> for Fingerprint { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let vec = Vec::::deserialize(deserializer)?; - let mut hash = [0u8; H_LENGTH]; - hash.copy_from_slice(&vec); - Ok(Self(hash)) - } -} - -impl Fingerprint { - /// Returns the hash of the fingerprint. - pub fn as_hash(&self) -> Hash { - self.0 - } -} - -impl From> for Fingerprint { - fn from(hash: Hash) -> Self { - Self(hash) - } -} - -impl Into> for Fingerprint { - fn into(self) -> Hash { - self.0 - } -} - -impl From<&[u8]> for Fingerprint { - fn from(bytes: &[u8]) -> Self { - let mut hash = [0u8; H_LENGTH]; - hash.copy_from_slice(&bytes); - Self(hash) - } -} - -impl AsRef<[u8]> for Fingerprint { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -/// Typed u64 representing the index of a file [`Chunk`]. Indexed from 0. -#[derive(Debug, Clone, Copy, PartialEq, Eq, TypeInfo, Encode, Decode, Ord, PartialOrd, Hash)] -pub struct ChunkId(u64); - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ChunkIdError { - InvalidChunkId, -} - -impl ChunkId { - pub fn new(id: u64) -> Self { - Self(id) - } - - pub fn from_challenge(challenge: &[u8], chunks_count: u64) -> Self { - // Calculate the modulo of the challenge with the number of chunks in the file. - // The challenge is a big endian 32 byte array. - let challenged_chunk = BigUint::from_bytes_be(challenge) % chunks_count; - ChunkId::new(challenged_chunk.try_into().expect( - "This is impossible. The modulo of a number with a u64 should always fit in a u64.", - )) - } - - pub fn as_u64(&self) -> u64 { - self.0 - } - - pub fn as_trie_key(&self) -> Vec { - AsCompact(self.0).encode() - } - - pub fn from_trie_key(key: &Vec) -> Result { - let id = Compact::::decode(&mut &key[..]) - .map_err(|_| ChunkIdError::InvalidChunkId)? - .0; - Ok(Self(id)) - } -} - -// TODO: this is currently a placeholder in order to define Storage interface. -/// Typed chunk of a file. This is what is stored in the leaf of the stored Merkle tree. -pub type Chunk = Vec; - -/// A chunk with its ID. This is the actual data stored in the Merkle tree for each chunk. -#[derive(Clone, Debug, Encode, Decode, PartialEq)] -pub struct ChunkWithId { - pub chunk_id: ChunkId, - pub data: Chunk, -} - -/// A leaf in the in a trie. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Leaf { - pub key: K, - pub data: D, -} - -impl Leaf { - pub fn new(key: K, data: D) -> Self { - Self { key, data } - } -} - -/// A hash type of arbitrary length `H_LENGTH`. -pub type Hash = [u8; H_LENGTH]; +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Compact, Decode, Encode}; +use core::fmt::Debug; +use num_bigint::BigUint; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use shp_traits::{AsCompact, FileMetadataInterface}; +use sp_arithmetic::traits::SaturatedConversion; +use sp_core::{crypto::AccountId32, H256}; +use sp_std::vec::Vec; + +/// A struct containing all the information about a file in StorageHub. +/// +/// It also provides utility functions like calculating the number of chunks in a file, +/// the last chunk ID, and generating a file key for a given file metadata. +#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, Encode, Decode, Serialize, Deserialize)] +pub struct FileMetadata +{ + pub owner: Vec, + pub bucket_id: Vec, + pub location: Vec, + #[codec(compact)] + pub file_size: u64, + pub fingerprint: Fingerprint, +} + +/// Maximum number of chunks a Storage Provider would need to prove for a file. +const MAX_CHUNKS_TO_CHECK: u32 = 10; + +impl + FileMetadata +{ + pub fn new( + owner: Vec, + bucket_id: Vec, + location: Vec, + size: u64, + fingerprint: Fingerprint, + ) -> Self { + Self { + owner, + bucket_id, + location, + file_size: size, + fingerprint, + } + } + + pub fn file_key(&self) -> T::Out { + T::hash(self.encode().as_slice()) + } + + pub fn chunks_to_check(&self) -> u32 { + // In here we downcast and saturate to u32, as we're going to saturate to MAX_CHUNKS_TO_CHECK anyway. + let chunks = (self.file_size / SIZE_TO_CHALLENGES + + (self.file_size % SIZE_TO_CHALLENGES != 0) as u64) + .saturated_into::(); + + // Cap chunks to check at MAX_CHUNKS_TO_CHECK. + // This maximum number of chunks is based on the issue raised in the audit https://github.com/Moonsong-Labs/internal-storage-hub-design-audit/issues/11. + chunks.min(MAX_CHUNKS_TO_CHECK) + } + + pub fn chunks_count(&self) -> u64 { + self.file_size / CHUNK_SIZE + (self.file_size % CHUNK_SIZE != 0) as u64 + } + + pub fn last_chunk_id(&self) -> ChunkId { + ChunkId::new(self.chunks_count() - 1) + } +} + +/// Interface for encoding and decoding FileMetadata, used by the runtime. +impl + FileMetadataInterface for FileMetadata +{ + type AccountId = AccountId32; + type Metadata = Self; + type StorageDataUnit = u64; + + fn encode(metadata: &Self::Metadata) -> Vec { + metadata.encode() + } + + fn decode(data: &[u8]) -> Result { + as Decode>::decode(&mut &data[..]) + } + + fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit { + metadata.file_size + } + + fn get_file_owner(metadata: &Self::Metadata) -> Result { + Self::AccountId::decode(&mut metadata.owner.as_slice()) + } +} + +/// FileKey is the identifier for a file. +/// Computed as the hash of the SCALE-encoded FileMetadata. +#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct FileKey(Hash); + +impl From> for FileKey { + fn from(hash: Hash) -> Self { + Self(hash) + } +} + +impl Into> for FileKey { + fn into(self) -> Hash { + self.0 + } +} + +impl From for FileKey<32> { + fn from(hash: H256) -> Self { + let mut file_key = [0u8; 32]; + file_key.copy_from_slice(hash.as_bytes()); + Self(file_key) + } +} + +impl Into for FileKey<32> { + fn into(self) -> H256 { + H256::from_slice(&self.0) + } +} + +impl From<&[u8]> for FileKey { + fn from(bytes: &[u8]) -> Self { + let mut hash = [0u8; H_LENGTH]; + hash.copy_from_slice(&bytes); + Self(hash) + } +} + +impl AsRef<[u8]> for FileKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From<&[u8; H_LENGTH]> for FileKey { + fn from(bytes: &[u8; H_LENGTH]) -> Self { + Self(*bytes) + } +} + +impl AsRef<[u8; H_LENGTH]> for FileKey { + fn as_ref(&self) -> &[u8; H_LENGTH] { + &self.0 + } +} + +/// A fingerprint is something that uniquely identifies the content of a file. +/// In the context of this crate, a fingerprint is the root hash of a Merkle Patricia Trie +/// of the merklised file. +#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub struct Fingerprint(Hash); + +impl Default for Fingerprint { + fn default() -> Self { + Self([0u8; H_LENGTH]) + } +} + +impl Serialize for Fingerprint { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + self.0.to_vec().serialize(serializer) + } +} + +impl<'de, const H_LENGTH: usize> Deserialize<'de> for Fingerprint { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = Vec::::deserialize(deserializer)?; + let mut hash = [0u8; H_LENGTH]; + hash.copy_from_slice(&vec); + Ok(Self(hash)) + } +} + +impl Fingerprint { + /// Returns the hash of the fingerprint. + pub fn as_hash(&self) -> Hash { + self.0 + } +} + +impl From> for Fingerprint { + fn from(hash: Hash) -> Self { + Self(hash) + } +} + +impl Into> for Fingerprint { + fn into(self) -> Hash { + self.0 + } +} + +impl From<&[u8]> for Fingerprint { + fn from(bytes: &[u8]) -> Self { + let mut hash = [0u8; H_LENGTH]; + hash.copy_from_slice(&bytes); + Self(hash) + } +} + +impl AsRef<[u8]> for Fingerprint { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// Typed u64 representing the index of a file [`Chunk`]. Indexed from 0. +#[derive(Debug, Clone, Copy, PartialEq, Eq, TypeInfo, Encode, Decode, Ord, PartialOrd, Hash)] +pub struct ChunkId(u64); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ChunkIdError { + InvalidChunkId, +} + +impl ChunkId { + pub fn new(id: u64) -> Self { + Self(id) + } + + pub fn from_challenge(challenge: &[u8], chunks_count: u64) -> Self { + // Calculate the modulo of the challenge with the number of chunks in the file. + // The challenge is a big endian 32 byte array. + let challenged_chunk = BigUint::from_bytes_be(challenge) % chunks_count; + ChunkId::new(challenged_chunk.try_into().expect( + "This is impossible. The modulo of a number with a u64 should always fit in a u64.", + )) + } + + pub fn as_u64(&self) -> u64 { + self.0 + } + + pub fn as_trie_key(&self) -> Vec { + AsCompact(self.0).encode() + } + + pub fn from_trie_key(key: &Vec) -> Result { + let id = Compact::::decode(&mut &key[..]) + .map_err(|_| ChunkIdError::InvalidChunkId)? + .0; + Ok(Self(id)) + } +} + +// TODO: this is currently a placeholder in order to define Storage interface. +/// Typed chunk of a file. This is what is stored in the leaf of the stored Merkle tree. +pub type Chunk = Vec; + +/// A chunk with its ID. This is the actual data stored in the Merkle tree for each chunk. +#[derive(Clone, Debug, Encode, Decode, PartialEq)] +pub struct ChunkWithId { + pub chunk_id: ChunkId, + pub data: Chunk, +} + +/// A leaf in the in a trie. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Leaf { + pub key: K, + pub data: D, +} + +impl Leaf { + pub fn new(key: K, data: D) -> Self { + Self { key, data } + } +} + +/// A hash type of arbitrary length `H_LENGTH`. +pub type Hash = [u8; H_LENGTH]; diff --git a/primitives/traits/src/lib.rs b/primitives/traits/src/lib.rs index 6dc8d121c..e4748c24e 100644 --- a/primitives/traits/src/lib.rs +++ b/primitives/traits/src/lib.rs @@ -1,1059 +1,1059 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, FullCodec, HasCompact}; -use frame_support::{ - dispatch::DispatchResult, - pallet_prelude::{MaxEncodedLen, MaybeSerializeDeserialize, Member}, - sp_runtime::traits::{CheckEqual, MaybeDisplay, SimpleBitOps}, - traits::{fungible, Incrementable}, - BoundedBTreeSet, Parameter, -}; -use scale_info::{prelude::fmt::Debug, TypeInfo}; -use sp_core::Get; -use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Hash, One, - Saturating, Zero, - }, - BoundedVec, DispatchError, -}; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; - -#[cfg(feature = "std")] -pub trait MaybeDebug: Debug {} -#[cfg(feature = "std")] -impl MaybeDebug for T {} -#[cfg(not(feature = "std"))] -pub trait MaybeDebug {} -#[cfg(not(feature = "std"))] -impl MaybeDebug for T {} - -#[derive(Encode)] -pub struct AsCompact(#[codec(compact)] pub T); - -/// A trait to read information about buckets registered in the system, such as their owner and -/// the MSP ID of the MSP that's storing it, etc. -pub trait ReadBucketsInterface { - /// Type that can be used to identify accounts. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the buckets' IDs. - type BucketId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type that represents the unit of storage data in which the capacity is measured. - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - /// Type of the root of the buckets. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of a bucket's read-access group's ID (which is the read-access NFT collection's ID). - type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; - - /// Byte limit of a bucket's name. - type BucketNameLimit: Get; - - /// Check if a bucket exists. - fn bucket_exists(bucket_id: &Self::BucketId) -> bool; - - /// Return if a bucket (represented by its Bucket ID) is stored by a specific MSP. - fn is_bucket_stored_by_msp(msp_id: &Self::ProviderId, bucket_id: &Self::BucketId) -> bool; - - /// Get the read-access group's ID of a bucket, if there is one. - fn get_read_access_group_id_of_bucket( - bucket_id: &Self::BucketId, - ) -> Result, DispatchError>; - - /// Get the MSP ID of the MSP that's storing a bucket. - fn get_msp_of_bucket(bucket_id: &Self::BucketId) -> Option; - - /// Check if an account is the owner of a bucket. - fn is_bucket_owner( - who: &Self::AccountId, - bucket_id: &Self::BucketId, - ) -> Result; - - /// Check if a bucket is private. - fn is_bucket_private(bucket_id: &Self::BucketId) -> Result; - - /// Derive the Bucket Id of a bucket, from its MSP, owner and name. - fn derive_bucket_id( - msp_id: &Self::ProviderId, - owner: &Self::AccountId, - bucket_name: BoundedVec, - ) -> Self::BucketId; - - /// Get the root of a bucket. - fn get_root_bucket(bucket_id: &Self::BucketId) -> Option; - - /// Get the bucket owner. - fn get_bucket_owner(bucket_id: &Self::BucketId) -> Result; - - /// Get bucket size. - fn get_bucket_size(bucket_id: &Self::BucketId) -> Result; - - /// Get the MSP of a bucket. - fn get_msp_bucket(bucket_id: &Self::BucketId) -> Result; -} - -/// A trait to change the state of buckets registered in the system, such as updating their privacy -/// settings, changing their root, etc. -pub trait MutateBucketsInterface { - /// Type that can be used to identify accounts. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the buckets' IDs. - type BucketId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type that represents the unit of storage data in which the capacity is measured. - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - /// Type of a bucket's read-access group's ID (which is the read-access NFT collection's ID). - type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; - - /// Type of the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Update a bucket's privacy setting. - fn update_bucket_privacy(bucket_id: Self::BucketId, privacy: bool) -> DispatchResult; - - /// Update a bucket's read-access group's ID. If None is passed, no one other than the owner - /// will be able to access the bucket. - fn update_bucket_read_access_group_id( - bucket_id: Self::BucketId, - maybe_read_access_group_id: Option, - ) -> DispatchResult; - - /// Add a new bucket under the MSP corresponding to `provider_id`, that will be owned by the account `user_id`. - /// If `privacy` is true, the bucket will be private and optionally the `read_access_group_id` will be used to - /// determine the collection of NFTs that can access the bucket. - fn add_bucket( - provider_id: Self::ProviderId, - user_id: Self::AccountId, - bucket_id: Self::BucketId, - privacy: bool, - maybe_read_access_group_id: Option, - ) -> DispatchResult; - - /// Change MSP of a bucket. - fn change_msp_bucket(bucket_id: &Self::BucketId, new_msp: &Self::ProviderId) -> DispatchResult; - - /// Change the root of a bucket. - fn change_root_bucket(bucket_id: Self::BucketId, new_root: Self::MerkleHash) -> DispatchResult; - - /// Remove a root from a bucket of a MSP, removing the whole bucket from storage. - fn remove_root_bucket(bucket_id: Self::BucketId) -> DispatchResult; - - /// Increase the size of a bucket. - fn increase_bucket_size( - bucket_id: &Self::BucketId, - delta: Self::StorageDataUnit, - ) -> DispatchResult; - - /// Decrease the size of a bucket. - fn decrease_bucket_size( - bucket_id: &Self::BucketId, - delta: Self::StorageDataUnit, - ) -> DispatchResult; -} - -/// A trait to read information about Storage Providers present in the -/// `storage-providers` pallet, such as if they are a BSP or MSP, their multiaddresses, etc. -pub trait ReadStorageProvidersInterface { - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type that represents the unit of storage data in which the capacity is measured. - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - /// Type of the counter of the total number of registered Storage Providers. - type SpCount: Parameter - + Member - + MaybeSerializeDeserialize - + Ord - + AtLeast32BitUnsigned - + FullCodec - + Copy - + Default - + Debug - + scale_info::TypeInfo - + MaxEncodedLen; - - /// Type that represents a MultiAddress of a Storage Provider. - type MultiAddress: Parameter - + MaybeSerializeDeserialize - + Debug - + Ord - + Default - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Maximum number of MultiAddresses a provider can have. - type MaxNumberOfMultiAddresses: Get; - /// Type that represents the reputation weight of a Storage Provider. - type ReputationWeight: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + Ord - + FullCodec - + Copy - + Debug - + scale_info::TypeInfo - + MaxEncodedLen - + CheckedAdd - + One - + Saturating - + PartialOrd - + sp_runtime::traits::Zero; - - /// Check if provider is a BSP. - fn is_bsp(who: &Self::ProviderId) -> bool; - - /// Check if provider is a MSP. - fn is_msp(who: &Self::ProviderId) -> bool; - - /// Get the total global reputation weight of all BSPs. - fn get_global_bsps_reputation_weight() -> Self::ReputationWeight; - - /// Get the reputation weight of a registered Provider. - fn get_bsp_reputation_weight( - who: &Self::ProviderId, - ) -> Result; - - /// Get number of registered BSPs. - fn get_number_of_bsps() -> Self::SpCount; - - /// Get the capacity of a Provider (MSP or BSP). - fn get_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; - - /// Get the capacity currently in use of a Provider (MSP or BSP). - fn get_used_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; - - /// Get available capacity of a Provider (MSP or BSP). - fn available_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; - - /// Get multiaddresses of a BSP. - fn get_bsp_multiaddresses( - who: &Self::ProviderId, - ) -> Result, DispatchError>; -} - -/// A trait to mutate the state of Storage Providers present in the `storage-providers` pallet. -/// This includes increasing and decreasing the data used by a Storage Provider. -pub trait MutateStorageProvidersInterface { - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type that represents the unit of storage data in which the capacity is measured. - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - /// Increase the used capacity of a Storage Provider (MSP or BSP). To be called when confirming - /// that it's storing a new file. - fn increase_capacity_used( - provider_id: &Self::ProviderId, - delta: Self::StorageDataUnit, - ) -> DispatchResult; - - /// Decrease the used capacity of a Storage Provider (MSP or BSP). To be called when confirming - /// that it has deleted a previously stored file. - fn decrease_capacity_used( - provider_id: &Self::ProviderId, - delta: Self::StorageDataUnit, - ) -> DispatchResult; -} - -/// A trait to read information about generic challengeable Providers, such as their ID, owner, root, -/// stake, etc. -pub trait ReadChallengeableProvidersInterface { - /// Type that can be used to identify accounts. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - - /// Type of the registered challengeable Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// The Balance type of the runtime, which should correspond to the type of - /// the staking balance of a registered Provider. - type Balance: fungible::Inspect + fungible::hold::Inspect; - - /// Check if an account is a registered challengeable Provider. - fn is_provider(who: Self::ProviderId) -> bool; - - /// Get the Provider Id from Account Id, if it is a registered challengeable Provider. - fn get_provider_id(who: Self::AccountId) -> Option; - - /// Get the Account Id of the owner of a registered challengeable Provider. - fn get_owner_account(who: Self::ProviderId) -> Option; - - /// Get the root for a registered challengeable Provider. - fn get_root(who: Self::ProviderId) -> Option; - - /// Get the default value for the root of a Merkle Patricia Forest. - fn get_default_root() -> Self::MerkleHash; - - /// Get the stake for a registered challengeable Provider. - fn get_stake( - who: Self::ProviderId, - ) -> Option<>::Balance>; - - /// Get the minimum stake for a registered challengeable Provider. - fn get_min_stake() -> >::Balance; -} - -/// A trait to mutate the state of challengeable Providers, such as updating their root. -pub trait MutateChallengeableProvidersInterface { - /// Type of the registered challengeable Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Update the root for a registered challengeable Provider. - fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult; - - /// Update the information of a registered challengeable Provider after a successful trie element removal. - fn update_provider_after_key_removal( - who: &Self::ProviderId, - removed_trie_value: &Vec, - ) -> DispatchResult; -} - -/// A trait to read information about generic Providers, such as their ID, owner, root, stake, etc. -pub trait ReadProvidersInterface { - /// Type that can be used to identify accounts. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// The Balance type of the runtime, which should correspond to the type of - /// the staking balance of a registered Provider. - type Balance: fungible::Inspect + fungible::hold::Inspect; - - /// Check if an account is a registered Provider. - fn is_provider(who: Self::ProviderId) -> bool; - - /// Get the Provider Id from Account Id, if it is a registered Provider. - fn get_provider_id(who: Self::AccountId) -> Option; - - /// Get the Account Id of the owner of a registered Provider. - fn get_owner_account(who: Self::ProviderId) -> Option; - - /// Get the Account Id of the payment account of a registered Provider. - fn get_payment_account(who: Self::ProviderId) -> Option; - - /// Get the root for a registered Provider. - fn get_root(who: Self::ProviderId) -> Option; - - /// Get the default value for the root of a Merkle Patricia Forest. - fn get_default_root() -> Self::MerkleHash; - - /// Get the stake for a registered Provider. - fn get_stake( - who: Self::ProviderId, - ) -> Option<>::Balance>; -} - -/// A trait to mutate the state of a generic Provider, such as updating their root. -pub trait MutateProvidersInterface { - /// Type of the registered Providers' IDs. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Type of the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + FullCodec; - - /// Update the root for a registered Provider. - fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult; -} - -/// A trait to get system-wide metrics, such as the total available capacity of the network and -/// its total used capacity. -pub trait SystemMetricsInterface { - /// Type of the unit provided by Providers - type ProvidedUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - /// Get the total available capacity of units of the network. - fn get_total_capacity() -> Self::ProvidedUnit; - - /// Get the total used capacity of units of the network. - fn get_total_used_capacity() -> Self::ProvidedUnit; -} - -/// The interface for the ProofsDealer pallet. -/// -/// It is abstracted over the `Provider` type, `Proof` type, `ForestProof` type and `MerkleHash` type. -/// It provides the functions to verify a proof, submit a new proof challenge and -/// submit a new challenge with priority. -pub trait ProofsDealerInterface { - /// The type which represents a registered Provider. - type ProviderId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - /// The type that represents a proof just for the Merkle Patricia Forest. - type ForestProof: Parameter + Member + Debug; - /// The type that represents a proof for an inner key (leaf) of the Merkle Patricia Forest. - type KeyProof: Parameter + Member + Debug; - /// The type corresponding to the root and keys in the Merkle Patricia Forest of a - /// registered Provider. - type MerkleHash: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + SimpleBitOps - + Ord - + Default - + Copy - + CheckEqual - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen - + PartialEq - + Eq - + Clone - + FullCodec; - /// The hashing system (algorithm) being used for the Merkle Patricia Forests (e.g. Blake2). - type MerkleHashing: Hash; - /// The type that represents the randomness output. - type RandomnessOutput: Parameter + Member + Debug; - /// The numerical type used to represent ticks. - /// The Proofs Dealer pallet uses ticks to keep track of time, for things like sending out - /// challenges and making sure that Providers respond to them in time - type TickNumber: Parameter - + Member - + AtLeast32BitUnsigned - + Debug - + Default - + Copy - + MaxEncodedLen - + FullCodec - + MaybeSerializeDeserialize - + Zero - + One - + CheckedAdd - + CheckedSub - + CheckedDiv - + CheckedMul - + Saturating; - - /// Verify a proof just for the Merkle Patricia Forest, for a given Provider. - /// - /// This only verifies that something is included in the forest of the Provider. It is not a full - /// proof of the Provider's data. - fn verify_forest_proof( - provider_id: &Self::ProviderId, - challenges: &[Self::MerkleHash], - proof: &Self::ForestProof, - ) -> Result, DispatchError>; - - /// Verify a proof for a Merkle Patricia Forest, without requiring it to be associated with a Provider. - /// - /// WARNING: This function should be used with caution, as it does not verify the root against a specific Provider. - /// This means this function should only be used when the root is previously known to be correct, and in NO case should - /// it be used to verify proofs associated with a challengeable Provider. That is what `verify_forest_proof` is for. - /// - /// This only verifies that something is included in the forest that has the given root. It is not a full - /// proof of its data. - fn verify_generic_forest_proof( - root: &Self::MerkleHash, - challenges: &[Self::MerkleHash], - proof: &Self::ForestProof, - ) -> Result, DispatchError>; - - /// Verify a proof for a key within the Merkle Patricia Forest of a Provider. - /// - /// This only verifies a proof of the data at a specific key within the Provider's forest. It does - /// not verify if that key is included in the Merkle Patricia Forest of the Provider. - fn verify_key_proof( - key: &Self::MerkleHash, - challenges: &[Self::MerkleHash], - proof: &Self::KeyProof, - ) -> Result, DispatchError>; - - /// Submit a new proof challenge. - fn challenge(key_challenged: &Self::MerkleHash) -> DispatchResult; - - /// Submit a new challenge with priority. - fn challenge_with_priority( - key_challenged: &Self::MerkleHash, - mutation: Option, - ) -> DispatchResult; - - /// Given a randomness seed, a provider id and a count, generate a list of challenges. - fn generate_challenges_from_seed( - seed: Self::RandomnessOutput, - provider_id: &Self::ProviderId, - count: u32, - ) -> Vec; - - /// Apply delta (mutations) to the partial trie based on the proof and the commitment. - /// - /// The new root is returned. - fn apply_delta( - provider_id: &Self::ProviderId, - mutations: &[(Self::MerkleHash, TrieMutation)], - proof: &Self::ForestProof, - ) -> Result; - - /// Apply delta (mutations) to the partial trie based on the proof and the commitment. - /// - /// WARNING: This function should be used with caution, as it does not verify the root against a specific Provider. - /// This means this function should only be used when the root is previously known to be correct, and in NO case should - /// it be used to verify proofs associated with a challengeable Provider. That is what `apply_delta` is for. - /// - /// The new root is returned. - fn generic_apply_delta( - root: &Self::MerkleHash, - mutations: &[(Self::MerkleHash, TrieMutation)], - proof: &Self::ForestProof, - ) -> Result; - - /// Initialise a Provider's challenge cycle. - /// - /// Sets the last tick the Provider submitted a proof for to the current tick and sets the - /// deadline for submitting a proof to the current tick + the Provider's period (based on its - /// stake) + the challenges tick tolerance. - fn initialise_challenge_cycle(who: &Self::ProviderId) -> DispatchResult; - - /// Get the current tick. - /// - /// The Proofs Dealer pallet uses ticks to keep track of time, for things like sending out - /// challenges and making sure that Providers respond to them in time. - fn get_current_tick() -> Self::TickNumber; -} - -/// A trait to verify proofs based on commitments and challenges. -/// -/// It is abstracted over the `Proof`, `Commitment` and `Challenge` types. -pub trait CommitmentVerifier { - /// The type that represents the proof. - type Proof: Parameter + Member + Debug; - /// The type that represents the commitment (e.g. a Merkle root) - type Commitment: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; - /// The type that represents the challenges which a proof is being verified against. - type Challenge: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; - - /// Verify a proof based on a commitment and a set of challenges. - /// - /// The function returns a vector of keys that are verified by the proof, or an error if the proof - /// is invalid. - fn verify_proof( - commitment: &Self::Commitment, - challenges: &[Self::Challenge], - proof: &Self::Proof, - ) -> Result, DispatchError>; -} - -/// Enum representing the type of mutation (addition or removal of a key). -#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Debug)] -pub enum TrieMutation { - Add(TrieAddMutation), - Remove(TrieRemoveMutation), -} - -#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Debug, Default)] -pub struct TrieAddMutation { - pub value: Vec, -} - -impl Into for TrieAddMutation { - fn into(self) -> TrieMutation { - TrieMutation::Add(self) - } -} - -impl TrieAddMutation { - pub fn new(value: Vec) -> Self { - Self { value } - } -} - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug, Default)] -pub struct TrieRemoveMutation; - -impl Into for TrieRemoveMutation { - fn into(self) -> TrieMutation { - TrieMutation::Remove(self) - } -} - -/// A trait to apply mutations (delta) to a partial trie based on a proof and a commitment. -pub trait TrieProofDeltaApplier { - /// The type that represents the proof. - type Proof: Parameter + Member + Debug; - /// The type that represents the keys (e.g. a Merkle root, node keys, etc.) - type Key: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; - - /// Apply mutations (delta) to a partial trie based on a proof and a commitment. - /// - /// Returns the MemoryDB of the trie generated by the proof, the new root computed after applying the mutations - /// and a vector of the key-value pairs that were changed by the mutations. - fn apply_delta( - root: &Self::Key, - mutations: &[(Self::Key, TrieMutation)], - proof: &Self::Proof, - ) -> Result< - ( - sp_trie::MemoryDB, - Self::Key, - Vec<(Self::Key, Option>)>, - ), - DispatchError, - >; -} - -/// Interface used by the file system pallet in order to read storage from NFTs pallet (avoiding tight coupling). -pub trait InspectCollections { - type CollectionId; - - /// Check if a collection exists. - fn collection_exists(collection_id: &Self::CollectionId) -> bool; -} - -/// The interface of the Payment Streams pallet. -/// -/// It is to be used by other pallets to interact with the Payment Streams pallet to create, update and delete payment streams. -pub trait PaymentStreamsInterface { - /// The type which represents the balance of the runtime. - type Balance: fungible::Inspect - + fungible::Mutate - + fungible::hold::Inspect - + fungible::hold::Mutate; - /// The type which represents a User account identifier. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - /// The type which represents a Provider identifier. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + Ord - + MaxEncodedLen - + Copy; - /// The type which represents a block number. - type BlockNumber: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - /// The type which represents a fixed-rate payment stream. - type FixedRatePaymentStream: Encode - + Decode - + Parameter - + Member - + Debug - + MaxEncodedLen - + PartialEq - + Clone; - /// The type which represents a dynamic-rate payment stream. - type DynamicRatePaymentStream: Encode - + Decode - + Parameter - + Member - + Debug - + MaxEncodedLen - + PartialEq - + Clone; - /// The type of the units that the Provider provides to the User (for example, for storage could be terabytes) - type Units: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Saturating - + Copy - + MaxEncodedLen - + HasCompact - + Into<>::Balance>; - - /// Create a new fixed-rate payment stream from a User to a Provider. - fn create_fixed_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - rate: >::Balance, - ) -> DispatchResult; - - /// Update the rate of an existing fixed-rate payment stream. - fn update_fixed_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - new_rate: >::Balance, - ) -> DispatchResult; - - /// Delete a fixed-rate payment stream. - fn delete_fixed_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> DispatchResult; - - /// Get the fixed-rate payment stream information between a User and a Provider - fn get_fixed_rate_payment_stream_info( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> Option; - - /// Create a new dynamic-rate payment stream from a User to a Provider. - fn create_dynamic_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - amount_provided: &Self::Units, - ) -> DispatchResult; - - /// Update the amount provided of an existing dynamic-rate payment stream. - fn update_dynamic_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - new_amount_provided: &Self::Units, - ) -> DispatchResult; - - /// Delete a dynamic-rate payment stream. - fn delete_dynamic_rate_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> DispatchResult; - - /// Get the dynamic-rate payment stream information between a User and a Provider - fn get_dynamic_rate_payment_stream_info( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> Option; - - /// Get the amount provided of a dynamic-rate payment stream between a User and a Provider - fn get_dynamic_rate_payment_stream_amount_provided( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> Option; - - /// Check if a user has an active payment stream with a provider. - fn has_active_payment_stream( - provider_id: &Self::ProviderId, - user_account: &Self::AccountId, - ) -> bool; -} - -/// The interface of the Payment Streams pallet that allows for the reading of user's solvency. -pub trait ReadUserSolvencyInterface { - /// The type which represents a User account identifier. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - - /// Get if a user has been flagged as insolvent (without funds) - fn is_user_insolvent(user_account: &Self::AccountId) -> bool; -} - -/// The interface of the ProofsDealer pallet that allows other pallets to query and modify proof -/// submitters in the last ticks. -pub trait ProofSubmittersInterface { - /// The type which represents a provider identifier. - type ProviderId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + Ord - + MaxEncodedLen - + Copy; - /// The type which represents a tick number. - type TickNumber: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - /// The type which represents the maximum limit of the number of proof submitters for a tick. - type MaxProofSubmitters: Get; - - fn get_proof_submitters_for_tick( - tick_number: &Self::TickNumber, - ) -> Option>; - - fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option; - - fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId); -} - -/// A trait to encode, decode and read information from file metadata. -pub trait FileMetadataInterface { - /// The type which represents a User account identifier. - type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; - /// The type which represents a file's metadata - type Metadata: Parameter + Member + MaybeSerializeDeserialize + Debug + Encode + Decode; - - /// The type which represents the unit that we use to measure file size (e.g. bytes) - type StorageDataUnit: Parameter - + Member - + MaybeSerializeDeserialize - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + Copy - + MaxEncodedLen - + HasCompact - + Into; - - fn decode(data: &[u8]) -> Result; - - fn encode(metadata: &Self::Metadata) -> Vec; - - fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit; - - fn get_file_owner(metadata: &Self::Metadata) -> Result; -} +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, FullCodec, HasCompact}; +use frame_support::{ + dispatch::DispatchResult, + pallet_prelude::{MaxEncodedLen, MaybeSerializeDeserialize, Member}, + sp_runtime::traits::{CheckEqual, MaybeDisplay, SimpleBitOps}, + traits::{fungible, Incrementable}, + BoundedBTreeSet, Parameter, +}; +use scale_info::{prelude::fmt::Debug, TypeInfo}; +use sp_core::Get; +use sp_runtime::{ + traits::{ + AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Hash, One, + Saturating, Zero, + }, + BoundedVec, DispatchError, +}; +use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; + +#[cfg(feature = "std")] +pub trait MaybeDebug: Debug {} +#[cfg(feature = "std")] +impl MaybeDebug for T {} +#[cfg(not(feature = "std"))] +pub trait MaybeDebug {} +#[cfg(not(feature = "std"))] +impl MaybeDebug for T {} + +#[derive(Encode)] +pub struct AsCompact(#[codec(compact)] pub T); + +/// A trait to read information about buckets registered in the system, such as their owner and +/// the MSP ID of the MSP that's storing it, etc. +pub trait ReadBucketsInterface { + /// Type that can be used to identify accounts. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the buckets' IDs. + type BucketId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type that represents the unit of storage data in which the capacity is measured. + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + /// Type of the root of the buckets. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of a bucket's read-access group's ID (which is the read-access NFT collection's ID). + type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; + + /// Byte limit of a bucket's name. + type BucketNameLimit: Get; + + /// Check if a bucket exists. + fn bucket_exists(bucket_id: &Self::BucketId) -> bool; + + /// Return if a bucket (represented by its Bucket ID) is stored by a specific MSP. + fn is_bucket_stored_by_msp(msp_id: &Self::ProviderId, bucket_id: &Self::BucketId) -> bool; + + /// Get the read-access group's ID of a bucket, if there is one. + fn get_read_access_group_id_of_bucket( + bucket_id: &Self::BucketId, + ) -> Result, DispatchError>; + + /// Get the MSP ID of the MSP that's storing a bucket. + fn get_msp_of_bucket(bucket_id: &Self::BucketId) -> Option; + + /// Check if an account is the owner of a bucket. + fn is_bucket_owner( + who: &Self::AccountId, + bucket_id: &Self::BucketId, + ) -> Result; + + /// Check if a bucket is private. + fn is_bucket_private(bucket_id: &Self::BucketId) -> Result; + + /// Derive the Bucket Id of a bucket, from its MSP, owner and name. + fn derive_bucket_id( + msp_id: &Self::ProviderId, + owner: &Self::AccountId, + bucket_name: BoundedVec, + ) -> Self::BucketId; + + /// Get the root of a bucket. + fn get_root_bucket(bucket_id: &Self::BucketId) -> Option; + + /// Get the bucket owner. + fn get_bucket_owner(bucket_id: &Self::BucketId) -> Result; + + /// Get bucket size. + fn get_bucket_size(bucket_id: &Self::BucketId) -> Result; + + /// Get the MSP of a bucket. + fn get_msp_bucket(bucket_id: &Self::BucketId) -> Result; +} + +/// A trait to change the state of buckets registered in the system, such as updating their privacy +/// settings, changing their root, etc. +pub trait MutateBucketsInterface { + /// Type that can be used to identify accounts. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the buckets' IDs. + type BucketId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type that represents the unit of storage data in which the capacity is measured. + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + /// Type of a bucket's read-access group's ID (which is the read-access NFT collection's ID). + type ReadAccessGroupId: Member + Parameter + MaxEncodedLen + Copy + Incrementable; + + /// Type of the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Update a bucket's privacy setting. + fn update_bucket_privacy(bucket_id: Self::BucketId, privacy: bool) -> DispatchResult; + + /// Update a bucket's read-access group's ID. If None is passed, no one other than the owner + /// will be able to access the bucket. + fn update_bucket_read_access_group_id( + bucket_id: Self::BucketId, + maybe_read_access_group_id: Option, + ) -> DispatchResult; + + /// Add a new bucket under the MSP corresponding to `provider_id`, that will be owned by the account `user_id`. + /// If `privacy` is true, the bucket will be private and optionally the `read_access_group_id` will be used to + /// determine the collection of NFTs that can access the bucket. + fn add_bucket( + provider_id: Self::ProviderId, + user_id: Self::AccountId, + bucket_id: Self::BucketId, + privacy: bool, + maybe_read_access_group_id: Option, + ) -> DispatchResult; + + /// Change MSP of a bucket. + fn change_msp_bucket(bucket_id: &Self::BucketId, new_msp: &Self::ProviderId) -> DispatchResult; + + /// Change the root of a bucket. + fn change_root_bucket(bucket_id: Self::BucketId, new_root: Self::MerkleHash) -> DispatchResult; + + /// Remove a root from a bucket of a MSP, removing the whole bucket from storage. + fn remove_root_bucket(bucket_id: Self::BucketId) -> DispatchResult; + + /// Increase the size of a bucket. + fn increase_bucket_size( + bucket_id: &Self::BucketId, + delta: Self::StorageDataUnit, + ) -> DispatchResult; + + /// Decrease the size of a bucket. + fn decrease_bucket_size( + bucket_id: &Self::BucketId, + delta: Self::StorageDataUnit, + ) -> DispatchResult; +} + +/// A trait to read information about Storage Providers present in the +/// `storage-providers` pallet, such as if they are a BSP or MSP, their multiaddresses, etc. +pub trait ReadStorageProvidersInterface { + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type that represents the unit of storage data in which the capacity is measured. + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + /// Type of the counter of the total number of registered Storage Providers. + type SpCount: Parameter + + Member + + MaybeSerializeDeserialize + + Ord + + AtLeast32BitUnsigned + + FullCodec + + Copy + + Default + + Debug + + scale_info::TypeInfo + + MaxEncodedLen; + + /// Type that represents a MultiAddress of a Storage Provider. + type MultiAddress: Parameter + + MaybeSerializeDeserialize + + Debug + + Ord + + Default + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Maximum number of MultiAddresses a provider can have. + type MaxNumberOfMultiAddresses: Get; + /// Type that represents the reputation weight of a Storage Provider. + type ReputationWeight: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + Ord + + FullCodec + + Copy + + Debug + + scale_info::TypeInfo + + MaxEncodedLen + + CheckedAdd + + One + + Saturating + + PartialOrd + + sp_runtime::traits::Zero; + + /// Check if provider is a BSP. + fn is_bsp(who: &Self::ProviderId) -> bool; + + /// Check if provider is a MSP. + fn is_msp(who: &Self::ProviderId) -> bool; + + /// Get the total global reputation weight of all BSPs. + fn get_global_bsps_reputation_weight() -> Self::ReputationWeight; + + /// Get the reputation weight of a registered Provider. + fn get_bsp_reputation_weight( + who: &Self::ProviderId, + ) -> Result; + + /// Get number of registered BSPs. + fn get_number_of_bsps() -> Self::SpCount; + + /// Get the capacity of a Provider (MSP or BSP). + fn get_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; + + /// Get the capacity currently in use of a Provider (MSP or BSP). + fn get_used_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; + + /// Get available capacity of a Provider (MSP or BSP). + fn available_capacity(who: &Self::ProviderId) -> Self::StorageDataUnit; + + /// Get multiaddresses of a BSP. + fn get_bsp_multiaddresses( + who: &Self::ProviderId, + ) -> Result, DispatchError>; +} + +/// A trait to mutate the state of Storage Providers present in the `storage-providers` pallet. +/// This includes increasing and decreasing the data used by a Storage Provider. +pub trait MutateStorageProvidersInterface { + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type that represents the unit of storage data in which the capacity is measured. + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + /// Increase the used capacity of a Storage Provider (MSP or BSP). To be called when confirming + /// that it's storing a new file. + fn increase_capacity_used( + provider_id: &Self::ProviderId, + delta: Self::StorageDataUnit, + ) -> DispatchResult; + + /// Decrease the used capacity of a Storage Provider (MSP or BSP). To be called when confirming + /// that it has deleted a previously stored file. + fn decrease_capacity_used( + provider_id: &Self::ProviderId, + delta: Self::StorageDataUnit, + ) -> DispatchResult; +} + +/// A trait to read information about generic challengeable Providers, such as their ID, owner, root, +/// stake, etc. +pub trait ReadChallengeableProvidersInterface { + /// Type that can be used to identify accounts. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + + /// Type of the registered challengeable Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// The Balance type of the runtime, which should correspond to the type of + /// the staking balance of a registered Provider. + type Balance: fungible::Inspect + fungible::hold::Inspect; + + /// Check if an account is a registered challengeable Provider. + fn is_provider(who: Self::ProviderId) -> bool; + + /// Get the Provider Id from Account Id, if it is a registered challengeable Provider. + fn get_provider_id(who: Self::AccountId) -> Option; + + /// Get the Account Id of the owner of a registered challengeable Provider. + fn get_owner_account(who: Self::ProviderId) -> Option; + + /// Get the root for a registered challengeable Provider. + fn get_root(who: Self::ProviderId) -> Option; + + /// Get the default value for the root of a Merkle Patricia Forest. + fn get_default_root() -> Self::MerkleHash; + + /// Get the stake for a registered challengeable Provider. + fn get_stake( + who: Self::ProviderId, + ) -> Option<>::Balance>; + + /// Get the minimum stake for a registered challengeable Provider. + fn get_min_stake() -> >::Balance; +} + +/// A trait to mutate the state of challengeable Providers, such as updating their root. +pub trait MutateChallengeableProvidersInterface { + /// Type of the registered challengeable Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Update the root for a registered challengeable Provider. + fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult; + + /// Update the information of a registered challengeable Provider after a successful trie element removal. + fn update_provider_after_key_removal( + who: &Self::ProviderId, + removed_trie_value: &Vec, + ) -> DispatchResult; +} + +/// A trait to read information about generic Providers, such as their ID, owner, root, stake, etc. +pub trait ReadProvidersInterface { + /// Type that can be used to identify accounts. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// The Balance type of the runtime, which should correspond to the type of + /// the staking balance of a registered Provider. + type Balance: fungible::Inspect + fungible::hold::Inspect; + + /// Check if an account is a registered Provider. + fn is_provider(who: Self::ProviderId) -> bool; + + /// Get the Provider Id from Account Id, if it is a registered Provider. + fn get_provider_id(who: Self::AccountId) -> Option; + + /// Get the Account Id of the owner of a registered Provider. + fn get_owner_account(who: Self::ProviderId) -> Option; + + /// Get the Account Id of the payment account of a registered Provider. + fn get_payment_account(who: Self::ProviderId) -> Option; + + /// Get the root for a registered Provider. + fn get_root(who: Self::ProviderId) -> Option; + + /// Get the default value for the root of a Merkle Patricia Forest. + fn get_default_root() -> Self::MerkleHash; + + /// Get the stake for a registered Provider. + fn get_stake( + who: Self::ProviderId, + ) -> Option<>::Balance>; +} + +/// A trait to mutate the state of a generic Provider, such as updating their root. +pub trait MutateProvidersInterface { + /// Type of the registered Providers' IDs. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Type of the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + FullCodec; + + /// Update the root for a registered Provider. + fn update_root(who: Self::ProviderId, new_root: Self::MerkleHash) -> DispatchResult; +} + +/// A trait to get system-wide metrics, such as the total available capacity of the network and +/// its total used capacity. +pub trait SystemMetricsInterface { + /// Type of the unit provided by Providers + type ProvidedUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + /// Get the total available capacity of units of the network. + fn get_total_capacity() -> Self::ProvidedUnit; + + /// Get the total used capacity of units of the network. + fn get_total_used_capacity() -> Self::ProvidedUnit; +} + +/// The interface for the ProofsDealer pallet. +/// +/// It is abstracted over the `Provider` type, `Proof` type, `ForestProof` type and `MerkleHash` type. +/// It provides the functions to verify a proof, submit a new proof challenge and +/// submit a new challenge with priority. +pub trait ProofsDealerInterface { + /// The type which represents a registered Provider. + type ProviderId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + /// The type that represents a proof just for the Merkle Patricia Forest. + type ForestProof: Parameter + Member + Debug; + /// The type that represents a proof for an inner key (leaf) of the Merkle Patricia Forest. + type KeyProof: Parameter + Member + Debug; + /// The type corresponding to the root and keys in the Merkle Patricia Forest of a + /// registered Provider. + type MerkleHash: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + SimpleBitOps + + Ord + + Default + + Copy + + CheckEqual + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen + + PartialEq + + Eq + + Clone + + FullCodec; + /// The hashing system (algorithm) being used for the Merkle Patricia Forests (e.g. Blake2). + type MerkleHashing: Hash; + /// The type that represents the randomness output. + type RandomnessOutput: Parameter + Member + Debug; + /// The numerical type used to represent ticks. + /// The Proofs Dealer pallet uses ticks to keep track of time, for things like sending out + /// challenges and making sure that Providers respond to them in time + type TickNumber: Parameter + + Member + + AtLeast32BitUnsigned + + Debug + + Default + + Copy + + MaxEncodedLen + + FullCodec + + MaybeSerializeDeserialize + + Zero + + One + + CheckedAdd + + CheckedSub + + CheckedDiv + + CheckedMul + + Saturating; + + /// Verify a proof just for the Merkle Patricia Forest, for a given Provider. + /// + /// This only verifies that something is included in the forest of the Provider. It is not a full + /// proof of the Provider's data. + fn verify_forest_proof( + provider_id: &Self::ProviderId, + challenges: &[Self::MerkleHash], + proof: &Self::ForestProof, + ) -> Result, DispatchError>; + + /// Verify a proof for a Merkle Patricia Forest, without requiring it to be associated with a Provider. + /// + /// WARNING: This function should be used with caution, as it does not verify the root against a specific Provider. + /// This means this function should only be used when the root is previously known to be correct, and in NO case should + /// it be used to verify proofs associated with a challengeable Provider. That is what `verify_forest_proof` is for. + /// + /// This only verifies that something is included in the forest that has the given root. It is not a full + /// proof of its data. + fn verify_generic_forest_proof( + root: &Self::MerkleHash, + challenges: &[Self::MerkleHash], + proof: &Self::ForestProof, + ) -> Result, DispatchError>; + + /// Verify a proof for a key within the Merkle Patricia Forest of a Provider. + /// + /// This only verifies a proof of the data at a specific key within the Provider's forest. It does + /// not verify if that key is included in the Merkle Patricia Forest of the Provider. + fn verify_key_proof( + key: &Self::MerkleHash, + challenges: &[Self::MerkleHash], + proof: &Self::KeyProof, + ) -> Result, DispatchError>; + + /// Submit a new proof challenge. + fn challenge(key_challenged: &Self::MerkleHash) -> DispatchResult; + + /// Submit a new challenge with priority. + fn challenge_with_priority( + key_challenged: &Self::MerkleHash, + mutation: Option, + ) -> DispatchResult; + + /// Given a randomness seed, a provider id and a count, generate a list of challenges. + fn generate_challenges_from_seed( + seed: Self::RandomnessOutput, + provider_id: &Self::ProviderId, + count: u32, + ) -> Vec; + + /// Apply delta (mutations) to the partial trie based on the proof and the commitment. + /// + /// The new root is returned. + fn apply_delta( + provider_id: &Self::ProviderId, + mutations: &[(Self::MerkleHash, TrieMutation)], + proof: &Self::ForestProof, + ) -> Result; + + /// Apply delta (mutations) to the partial trie based on the proof and the commitment. + /// + /// WARNING: This function should be used with caution, as it does not verify the root against a specific Provider. + /// This means this function should only be used when the root is previously known to be correct, and in NO case should + /// it be used to verify proofs associated with a challengeable Provider. That is what `apply_delta` is for. + /// + /// The new root is returned. + fn generic_apply_delta( + root: &Self::MerkleHash, + mutations: &[(Self::MerkleHash, TrieMutation)], + proof: &Self::ForestProof, + ) -> Result; + + /// Initialise a Provider's challenge cycle. + /// + /// Sets the last tick the Provider submitted a proof for to the current tick and sets the + /// deadline for submitting a proof to the current tick + the Provider's period (based on its + /// stake) + the challenges tick tolerance. + fn initialise_challenge_cycle(who: &Self::ProviderId) -> DispatchResult; + + /// Get the current tick. + /// + /// The Proofs Dealer pallet uses ticks to keep track of time, for things like sending out + /// challenges and making sure that Providers respond to them in time. + fn get_current_tick() -> Self::TickNumber; +} + +/// A trait to verify proofs based on commitments and challenges. +/// +/// It is abstracted over the `Proof`, `Commitment` and `Challenge` types. +pub trait CommitmentVerifier { + /// The type that represents the proof. + type Proof: Parameter + Member + Debug; + /// The type that represents the commitment (e.g. a Merkle root) + type Commitment: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; + /// The type that represents the challenges which a proof is being verified against. + type Challenge: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; + + /// Verify a proof based on a commitment and a set of challenges. + /// + /// The function returns a vector of keys that are verified by the proof, or an error if the proof + /// is invalid. + fn verify_proof( + commitment: &Self::Commitment, + challenges: &[Self::Challenge], + proof: &Self::Proof, + ) -> Result, DispatchError>; +} + +/// Enum representing the type of mutation (addition or removal of a key). +#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Debug)] +pub enum TrieMutation { + Add(TrieAddMutation), + Remove(TrieRemoveMutation), +} + +#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Debug, Default)] +pub struct TrieAddMutation { + pub value: Vec, +} + +impl Into for TrieAddMutation { + fn into(self) -> TrieMutation { + TrieMutation::Add(self) + } +} + +impl TrieAddMutation { + pub fn new(value: Vec) -> Self { + Self { value } + } +} + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug, Default)] +pub struct TrieRemoveMutation; + +impl Into for TrieRemoveMutation { + fn into(self) -> TrieMutation { + TrieMutation::Remove(self) + } +} + +/// A trait to apply mutations (delta) to a partial trie based on a proof and a commitment. +pub trait TrieProofDeltaApplier { + /// The type that represents the proof. + type Proof: Parameter + Member + Debug; + /// The type that represents the keys (e.g. a Merkle root, node keys, etc.) + type Key: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>; + + /// Apply mutations (delta) to a partial trie based on a proof and a commitment. + /// + /// Returns the MemoryDB of the trie generated by the proof, the new root computed after applying the mutations + /// and a vector of the key-value pairs that were changed by the mutations. + fn apply_delta( + root: &Self::Key, + mutations: &[(Self::Key, TrieMutation)], + proof: &Self::Proof, + ) -> Result< + ( + sp_trie::MemoryDB, + Self::Key, + Vec<(Self::Key, Option>)>, + ), + DispatchError, + >; +} + +/// Interface used by the file system pallet in order to read storage from NFTs pallet (avoiding tight coupling). +pub trait InspectCollections { + type CollectionId; + + /// Check if a collection exists. + fn collection_exists(collection_id: &Self::CollectionId) -> bool; +} + +/// The interface of the Payment Streams pallet. +/// +/// It is to be used by other pallets to interact with the Payment Streams pallet to create, update and delete payment streams. +pub trait PaymentStreamsInterface { + /// The type which represents the balance of the runtime. + type Balance: fungible::Inspect + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate; + /// The type which represents a User account identifier. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + /// The type which represents a Provider identifier. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + Ord + + MaxEncodedLen + + Copy; + /// The type which represents a block number. + type BlockNumber: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + /// The type which represents a fixed-rate payment stream. + type FixedRatePaymentStream: Encode + + Decode + + Parameter + + Member + + Debug + + MaxEncodedLen + + PartialEq + + Clone; + /// The type which represents a dynamic-rate payment stream. + type DynamicRatePaymentStream: Encode + + Decode + + Parameter + + Member + + Debug + + MaxEncodedLen + + PartialEq + + Clone; + /// The type of the units that the Provider provides to the User (for example, for storage could be terabytes) + type Units: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Saturating + + Copy + + MaxEncodedLen + + HasCompact + + Into<>::Balance>; + + /// Create a new fixed-rate payment stream from a User to a Provider. + fn create_fixed_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + rate: >::Balance, + ) -> DispatchResult; + + /// Update the rate of an existing fixed-rate payment stream. + fn update_fixed_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + new_rate: >::Balance, + ) -> DispatchResult; + + /// Delete a fixed-rate payment stream. + fn delete_fixed_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> DispatchResult; + + /// Get the fixed-rate payment stream information between a User and a Provider + fn get_fixed_rate_payment_stream_info( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> Option; + + /// Create a new dynamic-rate payment stream from a User to a Provider. + fn create_dynamic_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + amount_provided: &Self::Units, + ) -> DispatchResult; + + /// Update the amount provided of an existing dynamic-rate payment stream. + fn update_dynamic_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + new_amount_provided: &Self::Units, + ) -> DispatchResult; + + /// Delete a dynamic-rate payment stream. + fn delete_dynamic_rate_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> DispatchResult; + + /// Get the dynamic-rate payment stream information between a User and a Provider + fn get_dynamic_rate_payment_stream_info( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> Option; + + /// Get the amount provided of a dynamic-rate payment stream between a User and a Provider + fn get_dynamic_rate_payment_stream_amount_provided( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> Option; + + /// Check if a user has an active payment stream with a provider. + fn has_active_payment_stream( + provider_id: &Self::ProviderId, + user_account: &Self::AccountId, + ) -> bool; +} + +/// The interface of the Payment Streams pallet that allows for the reading of user's solvency. +pub trait ReadUserSolvencyInterface { + /// The type which represents a User account identifier. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + + /// Get if a user has been flagged as insolvent (without funds) + fn is_user_insolvent(user_account: &Self::AccountId) -> bool; +} + +/// The interface of the ProofsDealer pallet that allows other pallets to query and modify proof +/// submitters in the last ticks. +pub trait ProofSubmittersInterface { + /// The type which represents a provider identifier. + type ProviderId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + Ord + + MaxEncodedLen + + Copy; + /// The type which represents a tick number. + type TickNumber: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + /// The type which represents the maximum limit of the number of proof submitters for a tick. + type MaxProofSubmitters: Get; + + fn get_proof_submitters_for_tick( + tick_number: &Self::TickNumber, + ) -> Option>; + + fn get_accrued_failed_proof_submissions(provider_id: &Self::ProviderId) -> Option; + + fn clear_accrued_failed_proof_submissions(provider_id: &Self::ProviderId); +} + +/// A trait to encode, decode and read information from file metadata. +pub trait FileMetadataInterface { + /// The type which represents a User account identifier. + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + Ord + MaxEncodedLen; + /// The type which represents a file's metadata + type Metadata: Parameter + Member + MaybeSerializeDeserialize + Debug + Encode + Decode; + + /// The type which represents the unit that we use to measure file size (e.g. bytes) + type StorageDataUnit: Parameter + + Member + + MaybeSerializeDeserialize + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + Copy + + MaxEncodedLen + + HasCompact + + Into; + + fn decode(data: &[u8]) -> Result; + + fn encode(metadata: &Self::Metadata) -> Vec; + + fn get_file_size(metadata: &Self::Metadata) -> Self::StorageDataUnit; + + fn get_file_owner(metadata: &Self::Metadata) -> Result; +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 38b5d2163..3401be354 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,262 +1,266 @@ -[package] -name = "storage-hub-runtime" -version = "0.1.0" -description = "StorageHub Runtime - a decentralised storage solution designed for the Polkadot ecosystem." -homepage = { workspace = true } -license = { workspace = true } -authors = { workspace = true } -repository = { workspace = true } -edition = { workspace = true } - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[build-dependencies] -substrate-wasm-builder = { workspace = true, optional = true } -docify = "0.2.8" - -[dependencies] -codec = { workspace = true } -num-bigint = { workspace = true } -hex-literal = { workspace = true, optional = true } -log = { workspace = true } -smallvec = { workspace = true } -scale-info = { workspace = true } - -# Local -pallet-bucket-nfts = { workspace = true } -pallet-file-system = { workspace = true } -pallet-file-system-runtime-api = { workspace = true } -pallet-parameters = { workspace = true } -pallet-payment-streams = { workspace = true } -pallet-payment-streams-runtime-api = { workspace = true } -pallet-proofs-dealer = { workspace = true } -pallet-proofs-dealer-runtime-api = { workspace = true } -pallet-randomness = { workspace = true } -pallet-storage-providers = { workspace = true } -pallet-storage-providers-runtime-api = { workspace = true } - -shp-constants = { workspace = true } -shp-file-key-verifier = { workspace = true } -shp-file-metadata = { workspace = true } -shp-forest-verifier = { workspace = true } -shp-traits = { workspace = true } - -# Substrate -frame-support = { workspace = true } -frame-benchmarking = { workspace = true, optional = true } -frame-executive = { workspace = true } -frame-metadata-hash-extension = { workspace = true } -frame-system = { workspace = true } -frame-system-benchmarking = { workspace = true, optional = true } -frame-system-rpc-runtime-api = { workspace = true } -frame-try-runtime = { workspace = true, optional = true } -pallet-aura = { workspace = true } -pallet-authorship = { workspace = true } -pallet-balances = { workspace = true } -pallet-message-queue = { workspace = true } -pallet-nfts = { 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 } -sp-api = { workspace = true } -sp-block-builder = { workspace = true } -sp-consensus-aura = { workspace = true } -sp-core = { 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 } -polkadot-parachain-primitives = { workspace = true } -polkadot-runtime-common = { workspace = true } -xcm = { workspace = true } -xcm-builder = { workspace = true } -xcm-executor = { workspace = true } - -# Cumulus -cumulus-pallet-aura-ext = { 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-aura = { workspace = true } -cumulus-primitives-core = { workspace = true } -cumulus-primitives-utility = { workspace = true } -cumulus-primitives-storage-weight-reclaim = { workspace = true } -pallet-collator-selection = { workspace = true } -parachains-common = { workspace = true } -parachain-info = { workspace = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-session-benchmarking/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", - "cumulus-primitives-aura/std", - "cumulus-primitives-core/std", - "cumulus-primitives-utility/std", - "cumulus-primitives-storage-weight-reclaim/std", - "frame-benchmarking?/std", - "frame-executive/std", - "frame-metadata-hash-extension/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "log/std", - "pallet-aura/std", - "pallet-authorship/std", - "pallet-balances/std", - "pallet-bucket-nfts/std", - "pallet-collator-selection/std", - "pallet-file-system/std", - "pallet-file-system-runtime-api/std", - "pallet-message-queue/std", - "pallet-nfts/std", - "pallet-parameters/std", - "pallet-payment-streams/std", - "pallet-payment-streams-runtime-api/std", - "pallet-proofs-dealer/std", - "pallet-proofs-dealer-runtime-api/std", - "pallet-randomness/std", - "pallet-session/std", - "pallet-storage-providers/std", - "pallet-storage-providers-runtime-api/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-xcm/std", - "parachain-info/std", - "parachains-common/std", - "polkadot-parachain-primitives/std", - "polkadot-runtime-common/std", - "scale-info/std", - "shp-constants/std", - "shp-file-key-verifier/std", - "shp-file-metadata/std", - "shp-forest-verifier/std", - "shp-traits/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/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-version/std", - "substrate-wasm-builder", - "xcm-builder/std", - "xcm-executor/std", - "xcm/std", -] - -runtime-benchmarks = [ - "cumulus-pallet-parachain-system/runtime-benchmarks", - "cumulus-pallet-session-benchmarking/runtime-benchmarks", - "cumulus-pallet-xcmp-queue/runtime-benchmarks", - "cumulus-primitives-aura/std", - "cumulus-primitives-core/runtime-benchmarks", - "cumulus-primitives-utility/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "hex-literal", - "pallet-balances/runtime-benchmarks", - "pallet-bucket-nfts/runtime-benchmarks", - "pallet-collator-selection/runtime-benchmarks", - "pallet-file-system/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "pallet-nfts/runtime-benchmarks", - "pallet-parameters/runtime-benchmarks", - "pallet-payment-streams/runtime-benchmarks", - "pallet-proofs-dealer/runtime-benchmarks", - "pallet-randomness/runtime-benchmarks", - "pallet-storage-providers/runtime-benchmarks", - "pallet-sudo/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "parachains-common/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "polkadot-runtime-common/runtime-benchmarks", - "shp-constants/runtime-benchmarks", - "shp-file-key-verifier/runtime-benchmarks", - "shp-file-metadata/runtime-benchmarks", - "shp-forest-verifier/runtime-benchmarks", - "shp-traits/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", -] - -try-runtime = [ - "cumulus-pallet-aura-ext/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/try-runtime", - "pallet-aura/try-runtime", - "pallet-authorship/try-runtime", - "pallet-balances/try-runtime", - "pallet-bucket-nfts/try-runtime", - "pallet-collator-selection/try-runtime", - "pallet-file-system/try-runtime", - "pallet-message-queue/try-runtime", - "pallet-nfts/try-runtime", - "pallet-parameters/try-runtime", - "pallet-proofs-dealer/try-runtime", - "pallet-randomness/try-runtime", - "pallet-session/try-runtime", - "pallet-storage-providers/try-runtime", - "pallet-sudo/try-runtime", - "pallet-timestamp/try-runtime", - "pallet-transaction-payment/try-runtime", - "pallet-xcm/try-runtime", - "parachain-info/try-runtime", - "polkadot-runtime-common/try-runtime", - "shp-constants/try-runtime", - "shp-file-key-verifier/try-runtime", - "shp-file-metadata/try-runtime", - "shp-forest-verifier/try-runtime", - "shp-traits/try-runtime", - "sp-runtime/try-runtime", -] - -# Enable the metadata hash generation. -# -# This is hidden behind a feature because it increases the compile time. -# The wasm binary needs to be compiled twice, once to fetch the metadata, -# generate the metadata hash and then a second time with the -# `RUNTIME_METADATA_HASH` environment variable set for the `CheckMetadataHash` -# extension. -metadata-hash = ["substrate-wasm-builder/metadata-hash"] - -# A convenience feature for enabling things when doing a build -# for an on-chain release. -on-chain-release-build = ["metadata-hash"] +[package] +name = "storage-hub-runtime" +version = "0.1.0" +description = "StorageHub Runtime - a decentralised storage solution designed for the Polkadot ecosystem." +homepage = { workspace = true } +license = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[build-dependencies] +substrate-wasm-builder = { workspace = true, optional = true } +docify = "0.2.8" + +[dependencies] +codec = { workspace = true } +num-bigint = { workspace = true } +hex-literal = { workspace = true, optional = true } +log = { workspace = true } +smallvec = { workspace = true } +scale-info = { workspace = true } + +# Local +pallet-bucket-nfts = { workspace = true } +pallet-file-system = { workspace = true } +pallet-file-system-runtime-api = { workspace = true } +pallet-parameters = { workspace = true } +pallet-payment-streams = { workspace = true } +pallet-payment-streams-runtime-api = { workspace = true } +pallet-proofs-dealer = { workspace = true } +pallet-proofs-dealer-runtime-api = { workspace = true } +pallet-randomness = { workspace = true } +pallet-storage-providers = { workspace = true } +pallet-storage-providers-runtime-api = { workspace = true } + +shp-constants = { workspace = true } +shp-file-key-verifier = { workspace = true } +shp-file-metadata = { workspace = true } +shp-forest-verifier = { workspace = true } +shp-traits = { workspace = true } + +# Substrate +frame-support = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-executive = { workspace = true } +frame-metadata-hash-extension = { workspace = true } +frame-system = { workspace = true } +frame-system-benchmarking = { workspace = true, optional = true } +frame-system-rpc-runtime-api = { workspace = true } +frame-try-runtime = { workspace = true, optional = true } +pallet-aura = { workspace = true } +pallet-authorship = { workspace = true } +pallet-balances = { workspace = true } +pallet-message-queue = { workspace = true } +pallet-nfts = { 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 } +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-core = { 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 } +sp-weights = { workspace = true } + +# Polkadot +pallet-xcm = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +polkadot-runtime-common = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } + +# Cumulus +cumulus-pallet-aura-ext = { 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-aura = { workspace = true } +cumulus-primitives-core = { workspace = true } +cumulus-primitives-utility = { workspace = true } +cumulus-primitives-storage-weight-reclaim = { workspace = true } +pallet-collator-selection = { workspace = true } +parachains-common = { workspace = true } +parachain-info = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", + "cumulus-primitives-core/std", + "cumulus-primitives-utility/std", + "cumulus-primitives-storage-weight-reclaim/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-metadata-hash-extension/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime?/std", + "log/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-bucket-nfts/std", + "pallet-collator-selection/std", + "pallet-file-system/std", + "pallet-file-system-runtime-api/std", + "pallet-message-queue/std", + "pallet-nfts/std", + "pallet-parameters/std", + "pallet-payment-streams/std", + "pallet-payment-streams-runtime-api/std", + "pallet-proofs-dealer/std", + "pallet-proofs-dealer-runtime-api/std", + "pallet-randomness/std", + "pallet-session/std", + "pallet-storage-providers/std", + "pallet-storage-providers-runtime-api/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "scale-info/std", + "shp-constants/std", + "shp-file-key-verifier/std", + "shp-file-metadata/std", + "shp-forest-verifier/std", + "shp-traits/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/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-version/std", + "substrate-wasm-builder", + "xcm-builder/std", + "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", + "xcm/std", +] + +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-aura/std", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "hex-literal", + "pallet-balances/runtime-benchmarks", + "pallet-bucket-nfts/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-file-system/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", + "pallet-parameters/runtime-benchmarks", + "pallet-payment-streams/runtime-benchmarks", + "pallet-proofs-dealer/runtime-benchmarks", + "pallet-randomness/runtime-benchmarks", + "pallet-storage-providers/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "shp-constants/runtime-benchmarks", + "shp-file-key-verifier/runtime-benchmarks", + "shp-file-metadata/runtime-benchmarks", + "shp-forest-verifier/runtime-benchmarks", + "shp-traits/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", +] + +try-runtime = [ + "cumulus-pallet-aura-ext/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/try-runtime", + "pallet-aura/try-runtime", + "pallet-authorship/try-runtime", + "pallet-balances/try-runtime", + "pallet-bucket-nfts/try-runtime", + "pallet-collator-selection/try-runtime", + "pallet-file-system/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-nfts/try-runtime", + "pallet-parameters/try-runtime", + "pallet-proofs-dealer/try-runtime", + "pallet-randomness/try-runtime", + "pallet-session/try-runtime", + "pallet-storage-providers/try-runtime", + "pallet-sudo/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-xcm/try-runtime", + "parachain-info/try-runtime", + "polkadot-runtime-common/try-runtime", + "shp-constants/try-runtime", + "shp-file-key-verifier/try-runtime", + "shp-file-metadata/try-runtime", + "shp-forest-verifier/try-runtime", + "shp-traits/try-runtime", + "sp-runtime/try-runtime", +] + +# Enable the metadata hash generation. +# +# This is hidden behind a feature because it increases the compile time. +# The wasm binary needs to be compiled twice, once to fetch the metadata, +# generate the metadata hash and then a second time with the +# `RUNTIME_METADATA_HASH` environment variable set for the `CheckMetadataHash` +# extension. +metadata-hash = ["substrate-wasm-builder/metadata-hash"] + +# A convenience feature for enabling things when doing a build +# for an on-chain release. +on-chain-release-build = ["metadata-hash"] diff --git a/runtime/src/apis.rs b/runtime/src/apis.rs index 321df11c1..b449d4be5 100644 --- a/runtime/src/apis.rs +++ b/runtime/src/apis.rs @@ -1,372 +1,372 @@ -use crate::*; -use frame_support::{ - genesis_builder_helper::{build_state, get_preset}, - weights::Weight, -}; -use pallet_aura::Authorities; -use pallet_file_system_runtime_api::*; -use pallet_payment_streams_runtime_api::*; -use pallet_proofs_dealer::types::{KeyFor, ProviderIdFor, RandomnessOutputFor}; -use pallet_proofs_dealer_runtime_api::*; -use pallet_storage_providers::types::{ - BackupStorageProvider, BackupStorageProviderId, BucketId, MainStorageProviderId, ProviderId, - StorageDataUnit, StorageProviderId, -}; -use pallet_storage_providers_runtime_api::*; -use shp_file_metadata::ChunkId; -use shp_traits::TrieRemoveMutation; -use sp_api::impl_runtime_apis; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, ExtrinsicInclusionMode, -}; -use sp_std::prelude::Vec; -use sp_version::RuntimeVersion; - -// Local module imports -use super::{ - AccountId, Balance, Block, Executive, InherentDataExt, Nonce, ParachainSystem, Runtime, - RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, -}; - -impl_runtime_apis! { - /// Allows the collator client to query its runtime to determine whether it should author a block - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - included_hash: ::Hash, - slot: cumulus_primitives_aura::Slot, - ) -> bool { - configs::ConsensusHook::can_build_upon(included_hash, slot) - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) - } - - fn authorities() -> Vec { - Authorities::::get().into_inner() - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { - 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() -> sp_std::vec::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, KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(account: AccountId) -> Nonce { - System::account_nonce(account) - } - } - - 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 pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi - for Runtime - { - fn query_call_info( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::RuntimeDispatchInfo { - TransactionPayment::query_call_info(call, len) - } - fn query_call_fee_details( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_call_fee_details(call, 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 cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info(header) - } - } - - #[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, configs::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() - } - } - - #[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 frame_system_benchmarking::Pallet as SystemBench; - use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - - 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::{BenchmarkError, Benchmarking, BenchmarkBatch}; - - use frame_system_benchmarking::Pallet as SystemBench; - 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 cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} - - use frame_support::traits::WhitelistedStorageKeys; - let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); - - let mut batches = Vec::::new(); - let params = (&config, &whitelist); - add_benchmarks!(params, batches); - - if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } - Ok(batches) - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) - } - - fn preset_names() -> Vec { - vec![] - } - } - - impl pallet_file_system_runtime_api::FileSystemApi, MainStorageProviderId, H256, BlockNumber, ChunkId> for Runtime { - fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: H256) -> Result { - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key) - } - - fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: H256) -> Result, QueryBspConfirmChunksToProveForFileError> { - FileSystem::query_bsp_confirm_chunks_to_prove_for_file(bsp_id, file_key) - } - - fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: H256) -> Result, QueryMspConfirmChunksToProveForFileError> { - FileSystem::query_msp_confirm_chunks_to_prove_for_file(msp_id, file_key) - } - } - - impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { - fn get_users_with_debt_over_threshold(provider_id: &ProviderIdFor, threshold: Balance) -> Result, GetUsersWithDebtOverThresholdError> { - PaymentStreams::get_users_with_debt_over_threshold(provider_id, threshold) - } - fn get_users_of_payment_streams_of_provider(provider_id: &ProviderIdFor) -> Vec { - PaymentStreams::get_users_of_payment_streams_of_provider(provider_id) - } - } - - impl pallet_proofs_dealer_runtime_api::ProofsDealerApi, BlockNumber, KeyFor, RandomnessOutputFor, TrieRemoveMutation> for Runtime { - fn get_last_tick_provider_submitted_proof(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_last_tick_provider_submitted_proof(provider_id) - } - - fn get_last_checkpoint_challenge_tick() -> BlockNumber { - ProofsDealer::get_last_checkpoint_challenge_tick() - } - - fn get_checkpoint_challenges( - tick: BlockNumber - ) -> Result, Option)>, GetCheckpointChallengesError> { - ProofsDealer::get_checkpoint_challenges(tick) - } - - fn get_challenge_seed(tick: BlockNumber) -> Result, GetChallengeSeedError> { - ProofsDealer::get_challenge_seed(tick) - } - - fn get_challenge_period(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_challenge_period(provider_id) - } - - fn get_checkpoint_challenge_period() -> BlockNumber { - ProofsDealer::get_checkpoint_challenge_period() - } - - fn get_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor, count: u32) -> Vec> { - ProofsDealer::get_challenges_from_seed(seed, provider_id, count) - } - - fn get_forest_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor) -> Vec> { - ProofsDealer::get_forest_challenges_from_seed(seed, provider_id) - } - - fn get_current_tick() -> BlockNumber { - ProofsDealer::get_current_tick() - } - - fn get_next_deadline_tick(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_next_deadline_tick(provider_id) - } - } - - impl pallet_storage_providers_runtime_api::StorageProvidersApi, BackupStorageProvider, AccountId, ProviderId, StorageProviderId, StorageDataUnit, Balance, BucketId> for Runtime { - fn get_bsp_info(bsp_id: &BackupStorageProviderId) -> Result, GetBspInfoError> { - Providers::get_bsp_info(bsp_id) - } - - fn get_storage_provider_id(who: &AccountId) -> Option> { - Providers::get_storage_provider_id(who) - } - - fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result, QueryMspIdOfBucketIdError> { - Providers::query_msp_id_of_bucket_id(bucket_id) - } - - fn query_storage_provider_capacity(provider_id: &ProviderId) -> Result, QueryStorageProviderCapacityError> { - Providers::query_storage_provider_capacity(provider_id) - } - - fn query_available_storage_capacity(provider_id: &ProviderId) -> Result, QueryAvailableStorageCapacityError> { - Providers::query_available_storage_capacity(provider_id) - } - - fn query_earliest_change_capacity_block(provider_id: &BackupStorageProviderId) -> Result { - Providers::query_earliest_change_capacity_block(provider_id) - } - - fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option { - Providers::get_worst_case_scenario_slashable_amount(&provider_id).ok() - } - - fn get_slash_amount_per_max_file_size() -> Balance { - Providers::get_slash_amount_per_max_file_size() - } - } -} +use crate::*; +use frame_support::{ + genesis_builder_helper::{build_state, get_preset}, + weights::Weight, +}; +use pallet_aura::Authorities; +use pallet_file_system_runtime_api::*; +use pallet_payment_streams_runtime_api::*; +use pallet_proofs_dealer::types::{KeyFor, ProviderIdFor, RandomnessOutputFor}; +use pallet_proofs_dealer_runtime_api::*; +use pallet_storage_providers::types::{ + BackupStorageProvider, BackupStorageProviderId, BucketId, MainStorageProviderId, ProviderId, + StorageDataUnit, StorageProviderId, +}; +use pallet_storage_providers_runtime_api::*; +use shp_file_metadata::ChunkId; +use shp_traits::TrieRemoveMutation; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256}; +use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, ExtrinsicInclusionMode, +}; +use sp_std::prelude::Vec; +use sp_version::RuntimeVersion; + +// Local module imports +use super::{ + AccountId, Balance, Block, Executive, InherentDataExt, Nonce, ParachainSystem, Runtime, + RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, +}; + +impl_runtime_apis! { + /// Allows the collator client to query its runtime to determine whether it should author a block + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + configs::ConsensusHook::can_build_upon(included_hash, slot) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) + } + + fn authorities() -> Vec { + Authorities::::get().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { + 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() -> sp_std::vec::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, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Nonce { + System::account_nonce(account) + } + } + + 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 pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_call_fee_details(call, 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 cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + #[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, configs::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() + } + } + + #[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 frame_system_benchmarking::Pallet as SystemBench; + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + + 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::{BenchmarkError, Benchmarking, BenchmarkBatch}; + + use frame_system_benchmarking::Pallet as SystemBench; + 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 cumulus_pallet_session_benchmarking::Pallet as SessionBench; + impl cumulus_pallet_session_benchmarking::Config for Runtime {} + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } + + impl pallet_file_system_runtime_api::FileSystemApi, MainStorageProviderId, H256, BlockNumber, ChunkId> for Runtime { + fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: H256) -> Result { + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key) + } + + fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: H256) -> Result, QueryBspConfirmChunksToProveForFileError> { + FileSystem::query_bsp_confirm_chunks_to_prove_for_file(bsp_id, file_key) + } + + fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: H256) -> Result, QueryMspConfirmChunksToProveForFileError> { + FileSystem::query_msp_confirm_chunks_to_prove_for_file(msp_id, file_key) + } + } + + impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { + fn get_users_with_debt_over_threshold(provider_id: &ProviderIdFor, threshold: Balance) -> Result, GetUsersWithDebtOverThresholdError> { + PaymentStreams::get_users_with_debt_over_threshold(provider_id, threshold) + } + fn get_users_of_payment_streams_of_provider(provider_id: &ProviderIdFor) -> Vec { + PaymentStreams::get_users_of_payment_streams_of_provider(provider_id) + } + } + + impl pallet_proofs_dealer_runtime_api::ProofsDealerApi, BlockNumber, KeyFor, RandomnessOutputFor, TrieRemoveMutation> for Runtime { + fn get_last_tick_provider_submitted_proof(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_last_tick_provider_submitted_proof(provider_id) + } + + fn get_last_checkpoint_challenge_tick() -> BlockNumber { + ProofsDealer::get_last_checkpoint_challenge_tick() + } + + fn get_checkpoint_challenges( + tick: BlockNumber + ) -> Result, Option)>, GetCheckpointChallengesError> { + ProofsDealer::get_checkpoint_challenges(tick) + } + + fn get_challenge_seed(tick: BlockNumber) -> Result, GetChallengeSeedError> { + ProofsDealer::get_challenge_seed(tick) + } + + fn get_challenge_period(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_challenge_period(provider_id) + } + + fn get_checkpoint_challenge_period() -> BlockNumber { + ProofsDealer::get_checkpoint_challenge_period() + } + + fn get_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor, count: u32) -> Vec> { + ProofsDealer::get_challenges_from_seed(seed, provider_id, count) + } + + fn get_forest_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor) -> Vec> { + ProofsDealer::get_forest_challenges_from_seed(seed, provider_id) + } + + fn get_current_tick() -> BlockNumber { + ProofsDealer::get_current_tick() + } + + fn get_next_deadline_tick(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_next_deadline_tick(provider_id) + } + } + + impl pallet_storage_providers_runtime_api::StorageProvidersApi, BackupStorageProvider, AccountId, ProviderId, StorageProviderId, StorageDataUnit, Balance, BucketId> for Runtime { + fn get_bsp_info(bsp_id: &BackupStorageProviderId) -> Result, GetBspInfoError> { + Providers::get_bsp_info(bsp_id) + } + + fn get_storage_provider_id(who: &AccountId) -> Option> { + Providers::get_storage_provider_id(who) + } + + fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result, QueryMspIdOfBucketIdError> { + Providers::query_msp_id_of_bucket_id(bucket_id) + } + + fn query_storage_provider_capacity(provider_id: &ProviderId) -> Result, QueryStorageProviderCapacityError> { + Providers::query_storage_provider_capacity(provider_id) + } + + fn query_available_storage_capacity(provider_id: &ProviderId) -> Result, QueryAvailableStorageCapacityError> { + Providers::query_available_storage_capacity(provider_id) + } + + fn query_earliest_change_capacity_block(provider_id: &BackupStorageProviderId) -> Result { + Providers::query_earliest_change_capacity_block(provider_id) + } + + fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option { + Providers::get_worst_case_scenario_slashable_amount(&provider_id).ok() + } + + fn get_slash_amount_per_max_file_size() -> Balance { + Providers::get_slash_amount_per_max_file_size() + } + } +} diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index ad7202aa6..50d1632a1 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -1,746 +1,757 @@ -mod runtime_params; -mod xcm_config; - -// Substrate and Polkadot dependencies -use core::marker::PhantomData; -use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; -use frame_support::{ - derive_impl, - dispatch::DispatchClass, - parameter_types, - traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, - TransformOrigin, - }, - weights::{ConstantMultiplier, Weight}, - PalletId, -}; -use frame_system::{ - limits::{BlockLength, BlockWeights}, - pallet_prelude::BlockNumberFor, - EnsureRoot, EnsureSigned, -}; -use num_bigint::BigUint; -use pallet_nfts::PalletFeatures; -use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; -use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; -use polkadot_runtime_common::{ - prod_or_fast, xcm_sender::NoPriceForMessageDelivery, BlockHashCount, SlowAdjustingFeeUpdate, -}; -use shp_file_key_verifier::FileKeyVerifier; -use shp_file_metadata::{ChunkId, FileMetadata}; -use shp_forest_verifier::ForestVerifier; -use shp_traits::{CommitmentVerifier, MaybeDebug}; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{blake2_256, ConstU128, Get, Hasher, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, ConvertBack, Verify}, - AccountId32, DispatchError, Perbill, SaturatedConversion, -}; -use sp_std::collections::btree_set::BTreeSet; -use sp_std::vec; -use sp_trie::{CompactProof, LayoutV1, TrieConfiguration, TrieLayout}; -use sp_version::RuntimeVersion; -use xcm::latest::prelude::BodyId; - -// Local module imports -use crate::{ - weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, - AccountId, Aura, Balance, Balances, Block, BlockNumber, BucketNfts, CollatorSelection, Hash, - MessageQueue, Nfts, Nonce, PalletInfo, ParachainSystem, PaymentStreams, PolkadotXcm, - ProofsDealer, Providers, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, - RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, SessionKeys, Signature, System, - WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, BLOCK_PROCESSING_VELOCITY, DAYS, - EXISTENTIAL_DEPOSIT, HOURS, MAXIMUM_BLOCK_WEIGHT, MICROUNIT, MINUTES, NORMAL_DISPATCH_RATIO, - RELAY_CHAIN_SLOT_DURATION_MILLIS, SLOT_DURATION, UNINCLUDED_SEGMENT_CAPACITY, UNIT, VERSION, -}; -use runtime_params::RuntimeParameters; -use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin}; - -pub type StorageProofsMerkleTrieLayout = LayoutV1; - -/// Type representing the storage data units in StorageHub. -pub type StorageDataUnit = u64; - -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 parametrise - // 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; -} - -/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from -/// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), -/// but overridden as needed. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for Runtime { - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The index type for storing how many extrinsics an account has signed. - type Nonce = Nonce; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The block type. - type Block = Block; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// Runtime version. - type Version = Version; - /// The data to be stored in an account. - type AccountData = pallet_balances::AccountData; - /// The weight of database operations that the runtime can invoke. - type DbWeight = RocksDbWeight; - /// 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>; -} - -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Aura; - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; - type WeightInfo = (); -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type EventHandler = (CollatorSelection,); -} - -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 WeightInfo = pallet_balances::weights::SubstrateWeight; - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; -} - -parameter_types! { - /// Relay Chain `TransactionByteFee` / 10 - pub const TransactionByteFee: Balance = 10 * MICROUNIT; -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; - type OperationalFeeMultiplier = ConstU8<5>; -} - -impl pallet_sudo::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = (); -} - -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; -} - -impl cumulus_pallet_parachain_system::Config for Runtime { - type WeightInfo = (); - 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(crate) type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, ->; - -impl parachain_info::Config for Runtime {} - -parameter_types! { - pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; - // TODO: Set appropiate weight limit - // The maximum weight to be used from remaining weight for processing enqueued messages on idle - // pub const IdleMaxServiceWeight: Weight = Some(Weight); -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - #[cfg(feature = "runtime-benchmarks")] - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< - cumulus_primitives_core::AggregateMessageOrigin, - >; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, - >; - type Size = u32; - // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: - type QueueChangeHandler = NarrowOriginToSibling; - type QueuePausedQuery = NarrowOriginToSibling; - type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; - type MaxStale = sp_core::ConstU32<8>; - type ServiceWeight = MessageQueueServiceWeight; - type IdleMaxServiceWeight = (); // TODO: Set appropiate weight limit -} - -impl cumulus_pallet_aura_ext::Config for Runtime {} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ChannelInfo = ParachainSystem; - type VersionWrapper = PolkadotXcm; - // Enqueue XCMP messages from siblings for later processing. - type XcmpQueue = TransformOrigin; - type MaxInboundSuspended = sp_core::ConstU32<1_000>; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); - type PriceForSiblingDelivery = NoPriceForMessageDelivery; -} - -parameter_types! { - pub const Period: BlockNumber = 6 * HOURS; - pub const Offset: BlockNumber = 0; -} - -impl pallet_session::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ValidatorId = ::AccountId; - // we don't have stash and controller, thus we don't need the convert as well. - type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ShouldEndSession = pallet_session::PeriodicSessions; - type NextSessionRotation = pallet_session::PeriodicSessions; - type SessionManager = CollatorSelection; - // Essentially just Aura, but let's be pedantic. - type SessionHandler = ::KeyTypeIdProviders; - type Keys = SessionKeys; - type WeightInfo = (); -} - -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); - type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = ConstU64; -} - -parameter_types! { - pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const SessionLength: BlockNumber = 6 * HOURS; - // StakingAdmin pluralistic body. - pub const StakingAdminBodyId: BodyId = BodyId::Defense; -} - -/// We allow root and the StakingAdmin to execute privileged collator selection operations. -pub type CollatorSelectionUpdateOrigin = EitherOfDiverse< - EnsureRoot, - EnsureXcm>, ->; - -impl pallet_collator_selection::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type UpdateOrigin = CollatorSelectionUpdateOrigin; - type PotId = PotId; - type MaxCandidates = ConstU32<100>; - type MinEligibleCollators = ConstU32<4>; - type MaxInvulnerables = ConstU32<20>; - // should be a multiple of session or things will get inconsistent - type KickThreshold = Period; - type ValidatorId = ::AccountId; - type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ValidatorRegistration = Session; - type WeightInfo = (); -} - -parameter_types! { - pub Features: PalletFeatures = PalletFeatures::all_enabled(); - pub const MaxAttributesPerCall: u32 = 10; - pub const CollectionDeposit: Balance = 100 * UNIT; - pub const ItemDeposit: Balance = 1 * UNIT; - pub const ApprovalsLimit: u32 = 20; - pub const ItemAttributesApprovalsLimit: u32 = 20; - pub const MaxTips: u32 = 10; - pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS; - pub const MetadataDepositBase: Balance = 10 * UNIT; - pub const MetadataDepositPerByte: Balance = 1 * UNIT; -} - -impl pallet_nfts::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u32; - type ItemId = u32; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type CollectionDeposit = CollectionDeposit; - type ItemDeposit = ItemDeposit; - type MetadataDepositBase = MetadataDepositBase; - type AttributeDepositBase = MetadataDepositBase; - type DepositPerByte = MetadataDepositPerByte; - type StringLimit = ConstU32<256>; - type KeyLimit = ConstU32<64>; - type ValueLimit = ConstU32<256>; - type ApprovalsLimit = ApprovalsLimit; - type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; - type MaxTips = MaxTips; - type MaxDeadlineDuration = MaxDeadlineDuration; - type MaxAttributesPerCall = MaxAttributesPerCall; - type Features = Features; - type OffchainSignature = Signature; - type OffchainPublic = ::Signer; - type WeightInfo = pallet_nfts::weights::SubstrateWeight; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type Locker = (); -} - -impl pallet_parameters::Config for Runtime { - type AdminOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type RuntimeParameters = RuntimeParameters; - type WeightInfo = (); -} - -/// Only callable after `set_validation_data` is called which forms this proof the same way -/// CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 -/* fn relay_chain_state_proof() -> RelayChainStateProof { - // CRITICAL TODO: Change this to the actual relay storage root after upgrading to polkadot-sdk v1.13.0 - let relay_storage_root = DefaultMerkleRoot::::get(); - /* let relay_storage_root = cumulus_pallet_parachain_system::ValidationData::::get() - .expect("set in `set_validation_data`") - .relay_parent_storage_root; */ - let root_vec: vec::Vec> = vec![relay_storage_root.as_bytes().to_vec()]; - let relay_chain_state = StorageProof::new(root_vec); - /* let relay_chain_state = cumulus_pallet_parachain_system::RelayStateProof::::get() - .expect("set in `set_validation_data`"); */ - RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) - .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") -} */ - -pub struct BabeDataGetter; -impl pallet_randomness::GetBabeData for BabeDataGetter { - // Tolerate panic here because this is only ever called in an inherent (so can be omitted) - fn get_epoch_index() -> u64 { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - const BENCHMARKING_NEW_EPOCH: u64 = 10u64; - return BENCHMARKING_NEW_EPOCH; - } - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove frame_system::Pallet::::block_number().into() - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::EPOCH_INDEX) - .ok() - .flatten() - .expect("expected to be able to read epoch index from relay chain state proof") */ - frame_system::Pallet::::block_number().into() - } - fn get_epoch_randomness() -> Hash { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - let benchmarking_babe_output = Hash::default(); - return benchmarking_babe_output; - } - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove H256::from_slice(&blake2_256(&Self::get_epoch_index().to_le_bytes())) - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) - .ok() - .flatten() - .expect("expected to be able to read epoch randomness from relay chain state proof") */ - H256::from_slice(&blake2_256(&Self::get_epoch_index().to_le_bytes())) - } - fn get_parent_randomness() -> Hash { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - let benchmarking_babe_output = Hash::default(); - return benchmarking_babe_output; - } - // Note: we use the `CURRENT_BLOCK_RANDOMNESS` key here as it also represents the parent randomness, the only difference - // is the block since this randomness is valid, but we don't care about that because we are setting that directly in the `randomness` pallet. - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::CURRENT_BLOCK_RANDOMNESS) - .ok() - .flatten() - .expect("expected to be able to read parent randomness from relay chain state proof") */ - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove H256::from_slice(&blake2_256(&Self::get_epoch_index().saturating_sub(1).to_le_bytes())) - H256::from_slice(&blake2_256( - &Self::get_epoch_index().saturating_sub(1).to_le_bytes(), - )) - } -} - -parameter_types! { - pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * HOURS, 2 * MINUTES); -} - -// TODO: If the next line is uncommented (which should be eventually), compilation breaks (most likely because of mismatched dependency issues) -/* parameter_types! { - pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * runtime_constants::time::EPOCH_DURATION_IN_SLOTS, 2 * MINUTES); -} */ - -/// Configure the randomness pallet -impl pallet_randomness::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type BabeDataGetter = BabeDataGetter; - type WeightInfo = (); -} - -parameter_types! { - pub const SpMinDeposit: Balance = 100 * UNIT; - pub const BucketDeposit: Balance = 100 * UNIT; -} - -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} -impl pallet_storage_providers::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = pallet_randomness::RandomnessFromOneEpochAgo; - type PaymentStreams = PaymentStreams; - type FileMetadataManager = FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = StorageDataUnit; - type SpCount = u32; - type MerklePatriciaRoot = Hash; - type ValuePropId = Hash; - type ReadAccessGroupId = ::CollectionId; - type ProvidersProofSubmitters = ProofsDealer; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = SpMinDeposit; - type SpMinCapacity = ConstU64<2>; - type DepositPerData = ConstU128<2>; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = ConstU32<100>; - type MaxMultiAddressAmount = ConstU32<5>; - type MaxProtocols = ConstU32<100>; - type MaxBuckets = ConstU32<10000>; - type BucketDeposit = BucketDeposit; - type BucketNameLimit = ConstU32<100>; - type MaxBlocksForRandomness = MaxBlocksForRandomness; - type MinBlocksBetweenCapacityChanges = ConstU32<10>; - type DefaultMerkleRoot = DefaultMerkleRoot; - type SlashAmountPerMaxFileSize = - runtime_params::dynamic_params::runtime_config::SlashAmountPerMaxFileSize; - type StartingReputationWeight = ConstU32<1>; -} - -parameter_types! { - pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); - pub const UserWithoutFundsCooldown: BlockNumber = 100; -} - -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; - -impl Convert for BlockNumberToBalance { - fn convert(block_number: BlockNumber) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -impl pallet_payment_streams::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = Providers; - type RuntimeHoldReason = RuntimeHoldReason; - type UserWithoutFundsCooldown = UserWithoutFundsCooldown; // Amount of blocks that a user will have to wait before being able to clear the out of funds flag - type NewStreamDeposit = ConstU32<10>; // Amount of blocks that the deposit of a new stream should be able to pay for - type Units = StorageDataUnit; // Storage unit - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = ProofsDealer; -} - -// TODO: remove this and replace with pallet treasury -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId32 { - AccountId32::from([0; 32]) - } -} - -pub struct BlockFullnessHeadroom; -impl Get for BlockFullnessHeadroom { - fn get() -> Weight { - // TODO: Change this to the benchmarked weight of a `submit_proof` extrinsic or more. - // TODO: Right now, it is set to the weight of a `transfer_keep_alive` extrinsic. - Weight::from_parts(297_297_000, 308) - } -} - -pub struct MinNotFullBlocksRatio; -impl Get for MinNotFullBlocksRatio { - fn get() -> Perbill { - // This means that we tolerate at most 50% of misbehaving collators. - Perbill::from_percent(50) - } -} - -parameter_types! { - pub const RandomChallengesPerBlock: u32 = 10; - pub const MaxCustomChallengesPerBlock: u32 = 10; - pub const ChallengeHistoryLength: BlockNumber = 100; - pub const ChallengesQueueLength: u32 = 100; - pub const ChallengesFee: Balance = 1 * UNIT; - pub const ChallengeTicksTolerance: u32 = 50; - pub const MaxSubmittersPerTick: u32 = 1000; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight - pub const TargetTicksStorageOfSubmitters: u32 = 3; -} - -impl pallet_proofs_dealer::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ProvidersPallet = Providers; - type NativeBalance = Balances; - type MerkleTrieHash = Hash; - type MerkleTrieHashing = BlakeTwo256; - type ForestVerifier = ForestVerifier; - type KeyVerifier = FileKeyVerifier< - StorageProofsMerkleTrieLayout, - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type StakeToBlockNumber = SaturatingBalanceToBlockNumber; - type RandomChallengesPerBlock = RandomChallengesPerBlock; - type MaxCustomChallengesPerBlock = MaxCustomChallengesPerBlock; - type MaxSubmittersPerTick = MaxSubmittersPerTick; - type TargetTicksStorageOfSubmitters = TargetTicksStorageOfSubmitters; - type ChallengeHistoryLength = ChallengeHistoryLength; - type ChallengesQueueLength = ChallengesQueueLength; - type CheckpointChallengePeriod = - runtime_params::dynamic_params::runtime_config::CheckpointChallengePeriod; - type ChallengesFee = ChallengesFee; - type Treasury = TreasuryAccount; - type RandomnessProvider = pallet_randomness::ParentBlockRandomness; - type StakeToChallengePeriod = - runtime_params::dynamic_params::runtime_config::StakeToChallengePeriod; - type MinChallengePeriod = runtime_params::dynamic_params::runtime_config::MinChallengePeriod; - type ChallengeTicksTolerance = ChallengeTicksTolerance; - type BlockFullnessPeriod = ChallengeTicksTolerance; // We purposely set this to `ChallengeTicksTolerance` so that spamming of the chain is evaluated for the same blocks as the tolerance BSPs are given. - type BlockFullnessHeadroom = BlockFullnessHeadroom; - type MinNotFullBlocksRatio = MinNotFullBlocksRatio; -} - -/// Structure to mock a verifier that returns `true` when `proof` is not empty -/// and `false` otherwise. -pub struct MockVerifier { - _phantom: core::marker::PhantomData, -} - -/// Implement the `TrieVerifier` trait for the `MockVerifier` struct. -impl CommitmentVerifier for MockVerifier -where - C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, -{ - type Proof = CompactProof; - type Commitment = H256; - type Challenge = C; - - fn verify_proof( - _root: &Self::Commitment, - challenges: &[Self::Challenge], - proof: &CompactProof, - ) -> Result, DispatchError> { - if proof.encoded_nodes.len() > 0 { - Ok(challenges.iter().cloned().collect()) - } else { - Err("Proof is empty".into()) - } - } -} - -type ThresholdType = u32; - -parameter_types! { - pub const MinWaitForStopStoring: BlockNumber = 10; -} - -/// Configure the pallet template in pallets/template. -impl pallet_file_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Providers = Providers; - type ProofDealer = ProofsDealer; - type PaymentStreams = PaymentStreams; - type UserSolvency = PaymentStreams; - type Fingerprint = Hash; - type ReplicationTargetType = u32; - type ThresholdType = ThresholdType; - type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; - type HashToThresholdType = HashToThresholdTypeConverter; - type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; - type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; - type Currency = Balances; - type Nfts = Nfts; - type CollectionInspector = BucketNfts; - type MaxBspsPerStorageRequest = ConstU32<5>; - type MaxBatchConfirmStorageRequests = ConstU32<10>; - type MaxBatchMspRespondStorageRequests = ConstU32<10>; - type MaxFilePathSize = ConstU32<512u32>; - type MaxPeerIdSize = ConstU32<100>; - type MaxNumberOfPeerIds = ConstU32<5>; - type MaxDataServerMultiAddresses = ConstU32<10>; - type MaxExpiredItemsInBlock = ConstU32<100>; - type StorageRequestTtl = ConstU32<40>; - type PendingFileDeletionRequestTtl = ConstU32<40u32>; - type MoveBucketRequestTtl = ConstU32<40u32>; - type MaxUserPendingDeletionRequests = ConstU32<10u32>; - type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; - type MinWaitForStopStoring = MinWaitForStopStoring; -} - -// Converter from the Balance type to the BlockNumber type for math. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct SaturatingBalanceToBlockNumber; - -impl Convert> for SaturatingBalanceToBlockNumber { - fn convert(block_number: Balance) -> BlockNumberFor { - block_number.saturated_into() - } -} - -// Converter from the ThresholdType to the BlockNumber type and vice versa. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct ThresholdTypeToBlockNumberConverter; - -impl Convert> for ThresholdTypeToBlockNumberConverter { - fn convert(threshold: ThresholdType) -> BlockNumberFor { - threshold.saturated_into() - } -} - -impl ConvertBack> for ThresholdTypeToBlockNumberConverter { - fn convert_back(block_number: BlockNumberFor) -> ThresholdType { - block_number.into() - } -} - -/// Converter from the [`Hash`] type to the [`ThresholdType`]. -pub struct HashToThresholdTypeConverter; -impl Convert<::Hash, ThresholdType> - for HashToThresholdTypeConverter -{ - fn convert(hash: ::Hash) -> ThresholdType { - // Get the hash as bytes - let hash_bytes = hash.as_ref(); - - // Get the 4 least significant bytes of the hash and interpret them as an u32 - let truncated_hash_bytes: [u8; 4] = - hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); - - ThresholdType::from_be_bytes(truncated_hash_bytes) - } -} - -// Converter from the MerkleHash (H256) type to the RandomnessOutput (H256) type. -pub struct MerkleHashToRandomnessOutputConverter; - -impl Convert for MerkleHashToRandomnessOutputConverter { - fn convert(hash: H256) -> H256 { - hash - } -} - -// Converter from the ChunkId type to the MerkleHash (H256) type. -pub struct ChunkIdToMerkleHashConverter; - -impl Convert for ChunkIdToMerkleHashConverter { - fn convert(chunk_id: ChunkId) -> H256 { - let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); - let mut bytes = chunk_id_biguint.to_bytes_be(); - - // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros - if bytes.len() < 32 { - let mut padded_bytes = vec![0u8; 32 - bytes.len()]; - padded_bytes.extend(bytes); - bytes = padded_bytes; - } - - H256::from_slice(&bytes) - } -} - -impl pallet_bucket_nfts::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Buckets = Providers; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); -} +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// 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 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. +// +// For more information, please refer to + +mod runtime_params; +pub mod xcm_config; + +// Substrate and Polkadot dependencies +use core::marker::PhantomData; +use cumulus_pallet_parachain_system::{RelayChainStateProof, RelayNumberMonotonicallyIncreases}; +use cumulus_primitives_core::{relay_chain::well_known_keys, AggregateMessageOrigin, ParaId}; +use frame_support::{ + derive_impl, + dispatch::DispatchClass, + parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, + TransformOrigin, + }, + weights::{ConstantMultiplier, Weight}, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + pallet_prelude::BlockNumberFor, + EnsureRoot, EnsureSigned, +}; +use num_bigint::BigUint; +use pallet_nfts::PalletFeatures; +use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; +use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; +use polkadot_runtime_common::{ + prod_or_fast, xcm_sender::NoPriceForMessageDelivery, BlockHashCount, SlowAdjustingFeeUpdate, +}; +use shp_file_key_verifier::FileKeyVerifier; +use shp_file_metadata::{ChunkId, FileMetadata}; +use shp_forest_verifier::ForestVerifier; +use shp_traits::{CommitmentVerifier, MaybeDebug}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{ConstU128, Get, Hasher, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, ConvertBack, Verify}, + AccountId32, DispatchError, Perbill, SaturatedConversion, +}; +use sp_std::collections::btree_set::BTreeSet; +use sp_std::vec; +use sp_trie::{CompactProof, LayoutV1, TrieConfiguration, TrieLayout}; +use sp_version::RuntimeVersion; +use xcm::latest::prelude::BodyId; + +// Local module imports +use crate::{ + weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, + AccountId, Aura, Balance, Balances, Block, BlockNumber, BucketNfts, CollatorSelection, Hash, + MessageQueue, Nfts, Nonce, PalletInfo, ParachainInfo, ParachainSystem, PaymentStreams, + PolkadotXcm, ProofsDealer, Providers, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, + RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, SessionKeys, Signature, System, + WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, BLOCK_PROCESSING_VELOCITY, DAYS, + EXISTENTIAL_DEPOSIT, HOURS, MAXIMUM_BLOCK_WEIGHT, MICROUNIT, MINUTES, NORMAL_DISPATCH_RATIO, + RELAY_CHAIN_SLOT_DURATION_MILLIS, SLOT_DURATION, UNINCLUDED_SEGMENT_CAPACITY, UNIT, VERSION, +}; +use runtime_params::RuntimeParameters; +use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin}; + +pub type StorageProofsMerkleTrieLayout = LayoutV1; + +/// Type representing the storage data units in StorageHub. +pub type StorageDataUnit = u64; + +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 parametrise + // 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; +} + +/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from +/// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), +/// but overridden as needed. +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Nonce; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The block type. + type Block = Block; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// 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>; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<0>; + type WeightInfo = (); +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type EventHandler = (CollatorSelection,); +} + +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 WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + /// Relay Chain `TransactionByteFee` / 10 + pub const TransactionByteFee: Balance = 10 * MICROUNIT; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type OperationalFeeMultiplier = ConstU8<5>; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); +} + +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; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = (); + 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(crate) type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; + // TODO: Set appropiate weight limit + // The maximum weight to be used from remaining weight for processing enqueued messages on idle + // pub const IdleMaxServiceWeight: Weight = Some(Weight); +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + type QueuePausedQuery = NarrowOriginToSibling; + type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); // TODO: Set appropiate weight limit +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + // Enqueue XCMP messages from siblings for later processing. + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = ConstU32<1_000>; + type MaxActiveOutboundChannels = ConstU32<128>; + type MaxPageSize = ConstU32<{ 1 << 16 }>; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; +} + +parameter_types! { + pub const Period: BlockNumber = 6 * HOURS; + pub const Offset: BlockNumber = 0; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + // we don't have stash and controller, thus we don't need the convert as well. + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = CollatorSelection; + // Essentially just Aura, but let's be pedantic. + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = ConstU64; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); + pub const SessionLength: BlockNumber = 6 * HOURS; + // StakingAdmin pluralistic body. + pub const StakingAdminBodyId: BodyId = BodyId::Defense; +} + +/// We allow root and the StakingAdmin to execute privileged collator selection operations. +pub type CollatorSelectionUpdateOrigin = EitherOfDiverse< + EnsureRoot, + EnsureXcm>, +>; + +impl pallet_collator_selection::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type UpdateOrigin = CollatorSelectionUpdateOrigin; + type PotId = PotId; + type MaxCandidates = ConstU32<100>; + type MinEligibleCollators = ConstU32<4>; + type MaxInvulnerables = ConstU32<20>; + // should be a multiple of session or things will get inconsistent + type KickThreshold = Period; + type ValidatorId = ::AccountId; + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +parameter_types! { + pub Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; + pub const CollectionDeposit: Balance = 100 * UNIT; + pub const ItemDeposit: Balance = 1 * UNIT; + pub const ApprovalsLimit: u32 = 20; + pub const ItemAttributesApprovalsLimit: u32 = 20; + pub const MaxTips: u32 = 10; + pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS; + pub const MetadataDepositBase: Balance = 10 * UNIT; + pub const MetadataDepositPerByte: Balance = 1 * UNIT; +} + +impl pallet_nfts::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u32; + type ItemId = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type CollectionDeposit = CollectionDeposit; + type ItemDeposit = ItemDeposit; + type MetadataDepositBase = MetadataDepositBase; + type AttributeDepositBase = MetadataDepositBase; + type DepositPerByte = MetadataDepositPerByte; + type StringLimit = ConstU32<256>; + type KeyLimit = ConstU32<64>; + type ValueLimit = ConstU32<256>; + type ApprovalsLimit = ApprovalsLimit; + type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; + type MaxTips = MaxTips; + type MaxDeadlineDuration = MaxDeadlineDuration; + type MaxAttributesPerCall = MaxAttributesPerCall; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; + type WeightInfo = pallet_nfts::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type Locker = (); +} + +impl pallet_parameters::Config for Runtime { + type AdminOrigin = EnsureRoot; + type RuntimeEvent = RuntimeEvent; + type RuntimeParameters = RuntimeParameters; + type WeightInfo = (); +} + +/// Only callable after `set_validation_data` is called which forms this proof the same way +fn relay_chain_state_proof() -> RelayChainStateProof { + let relay_storage_root = cumulus_pallet_parachain_system::ValidationData::::get() + .expect("set in `set_validation_data`") + .relay_parent_storage_root; + let relay_chain_state = cumulus_pallet_parachain_system::RelayStateProof::::get() + .expect("set in `set_validation_data`"); + RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) + .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") +} + +pub struct BabeDataGetter; +impl pallet_randomness::GetBabeData for BabeDataGetter { + // Tolerate panic here because this is only ever called in an inherent (so can be omitted) + fn get_epoch_index() -> u64 { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + const BENCHMARKING_NEW_EPOCH: u64 = 10u64; + return BENCHMARKING_NEW_EPOCH; + } + relay_chain_state_proof() + .read_optional_entry(well_known_keys::EPOCH_INDEX) + .ok() + .flatten() + .expect("expected to be able to read epoch index from relay chain state proof") + } + fn get_epoch_randomness() -> Hash { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + let benchmarking_babe_output = Hash::default(); + return benchmarking_babe_output; + } + relay_chain_state_proof() + .read_optional_entry(well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) + .ok() + .flatten() + .expect("expected to be able to read epoch randomness from relay chain state proof") + } + fn get_parent_randomness() -> Hash { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + let benchmarking_babe_output = Hash::default(); + return benchmarking_babe_output; + } + // Note: we use the `CURRENT_BLOCK_RANDOMNESS` key here as it also represents the parent randomness, the only difference + // is the block since this randomness is valid, but we don't care about that because we are setting that directly in the `randomness` pallet. + relay_chain_state_proof() + .read_optional_entry(well_known_keys::CURRENT_BLOCK_RANDOMNESS) + .ok() + .flatten() + .expect("expected to be able to read parent randomness from relay chain state proof") + } +} + +parameter_types! { + pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * HOURS, 2 * MINUTES); +} + +// TODO: If the next line is uncommented (which should be eventually), compilation breaks (most likely because of mismatched dependency issues) +/* parameter_types! { + pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * runtime_constants::time::EPOCH_DURATION_IN_SLOTS, 2 * MINUTES); +} */ + +/// Configure the randomness pallet +impl pallet_randomness::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BabeDataGetter = BabeDataGetter; + type WeightInfo = (); +} + +parameter_types! { + pub const SpMinDeposit: Balance = 100 * UNIT; + pub const BucketDeposit: Balance = 100 * UNIT; +} + +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} +impl pallet_storage_providers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = pallet_randomness::RandomnessFromOneEpochAgo; + type PaymentStreams = PaymentStreams; + type FileMetadataManager = FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = StorageDataUnit; + type SpCount = u32; + type MerklePatriciaRoot = Hash; + type ValuePropId = Hash; + type ReadAccessGroupId = ::CollectionId; + type ProvidersProofSubmitters = ProofsDealer; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = SpMinDeposit; + type SpMinCapacity = ConstU64<2>; + type DepositPerData = ConstU128<2>; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = ConstU32<100>; + type MaxMultiAddressAmount = ConstU32<5>; + type MaxProtocols = ConstU32<100>; + type MaxBuckets = ConstU32<10000>; + type BucketDeposit = BucketDeposit; + type BucketNameLimit = ConstU32<100>; + type MaxBlocksForRandomness = MaxBlocksForRandomness; + type MinBlocksBetweenCapacityChanges = ConstU32<10>; + type DefaultMerkleRoot = DefaultMerkleRoot; + type SlashAmountPerMaxFileSize = + runtime_params::dynamic_params::runtime_config::SlashAmountPerMaxFileSize; + type StartingReputationWeight = ConstU32<1>; +} + +parameter_types! { + pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); + pub const UserWithoutFundsCooldown: BlockNumber = 100; +} + +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; + +impl Convert for BlockNumberToBalance { + fn convert(block_number: BlockNumber) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +impl pallet_payment_streams::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = Providers; + type RuntimeHoldReason = RuntimeHoldReason; + type UserWithoutFundsCooldown = UserWithoutFundsCooldown; // Amount of blocks that a user will have to wait before being able to clear the out of funds flag + type NewStreamDeposit = ConstU32<10>; // Amount of blocks that the deposit of a new stream should be able to pay for + type Units = StorageDataUnit; // Storage unit + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = ProofsDealer; +} + +// TODO: remove this and replace with pallet treasury +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId32 { + AccountId32::from([0; 32]) + } +} + +pub struct BlockFullnessHeadroom; +impl Get for BlockFullnessHeadroom { + fn get() -> Weight { + // TODO: Change this to the benchmarked weight of a `submit_proof` extrinsic or more. + // TODO: Right now, it is set to the weight of a `transfer_keep_alive` extrinsic. + Weight::from_parts(297_297_000, 308) + } +} + +pub struct MinNotFullBlocksRatio; +impl Get for MinNotFullBlocksRatio { + fn get() -> Perbill { + // This means that we tolerate at most 50% of misbehaving collators. + Perbill::from_percent(50) + } +} + +parameter_types! { + pub const RandomChallengesPerBlock: u32 = 10; + pub const MaxCustomChallengesPerBlock: u32 = 10; + pub const ChallengeHistoryLength: BlockNumber = 100; + pub const ChallengesQueueLength: u32 = 100; + pub const ChallengesFee: Balance = 1 * UNIT; + pub const ChallengeTicksTolerance: u32 = 50; + pub const MaxSubmittersPerTick: u32 = 1000; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight + pub const TargetTicksStorageOfSubmitters: u32 = 3; +} + +impl pallet_proofs_dealer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProvidersPallet = Providers; + type NativeBalance = Balances; + type MerkleTrieHash = Hash; + type MerkleTrieHashing = BlakeTwo256; + type ForestVerifier = ForestVerifier; + type KeyVerifier = FileKeyVerifier< + StorageProofsMerkleTrieLayout, + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type StakeToBlockNumber = SaturatingBalanceToBlockNumber; + type RandomChallengesPerBlock = RandomChallengesPerBlock; + type MaxCustomChallengesPerBlock = MaxCustomChallengesPerBlock; + type MaxSubmittersPerTick = MaxSubmittersPerTick; + type TargetTicksStorageOfSubmitters = TargetTicksStorageOfSubmitters; + type ChallengeHistoryLength = ChallengeHistoryLength; + type ChallengesQueueLength = ChallengesQueueLength; + type CheckpointChallengePeriod = + runtime_params::dynamic_params::runtime_config::CheckpointChallengePeriod; + type ChallengesFee = ChallengesFee; + type Treasury = TreasuryAccount; + type RandomnessProvider = pallet_randomness::ParentBlockRandomness; + type StakeToChallengePeriod = + runtime_params::dynamic_params::runtime_config::StakeToChallengePeriod; + type MinChallengePeriod = runtime_params::dynamic_params::runtime_config::MinChallengePeriod; + type ChallengeTicksTolerance = ChallengeTicksTolerance; + type BlockFullnessPeriod = ChallengeTicksTolerance; // We purposely set this to `ChallengeTicksTolerance` so that spamming of the chain is evaluated for the same blocks as the tolerance BSPs are given. + type BlockFullnessHeadroom = BlockFullnessHeadroom; + type MinNotFullBlocksRatio = MinNotFullBlocksRatio; +} + +/// Structure to mock a verifier that returns `true` when `proof` is not empty +/// and `false` otherwise. +pub struct MockVerifier { + _phantom: core::marker::PhantomData, +} + +/// Implement the `TrieVerifier` trait for the `MockVerifier` struct. +impl CommitmentVerifier for MockVerifier +where + C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, +{ + type Proof = CompactProof; + type Commitment = H256; + type Challenge = C; + + fn verify_proof( + _root: &Self::Commitment, + challenges: &[Self::Challenge], + proof: &CompactProof, + ) -> Result, DispatchError> { + if proof.encoded_nodes.len() > 0 { + Ok(challenges.iter().cloned().collect()) + } else { + Err("Proof is empty".into()) + } + } +} + +type ThresholdType = u32; + +parameter_types! { + pub const MinWaitForStopStoring: BlockNumber = 10; +} + +/// Configure the pallet template in pallets/template. +impl pallet_file_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Providers = Providers; + type ProofDealer = ProofsDealer; + type PaymentStreams = PaymentStreams; + type UserSolvency = PaymentStreams; + type Fingerprint = Hash; + type ReplicationTargetType = u32; + type ThresholdType = ThresholdType; + type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; + type HashToThresholdType = HashToThresholdTypeConverter; + type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; + type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; + type Currency = Balances; + type Nfts = Nfts; + type CollectionInspector = BucketNfts; + type MaxBspsPerStorageRequest = ConstU32<5>; + type MaxBatchConfirmStorageRequests = ConstU32<10>; + type MaxBatchMspRespondStorageRequests = ConstU32<10>; + type MaxFilePathSize = ConstU32<512u32>; + type MaxPeerIdSize = ConstU32<100>; + type MaxNumberOfPeerIds = ConstU32<5>; + type MaxDataServerMultiAddresses = ConstU32<10>; + type MaxExpiredItemsInBlock = ConstU32<100>; + type StorageRequestTtl = ConstU32<40>; + type PendingFileDeletionRequestTtl = ConstU32<40u32>; + type MoveBucketRequestTtl = ConstU32<40u32>; + type MaxUserPendingDeletionRequests = ConstU32<10u32>; + type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; + type MinWaitForStopStoring = MinWaitForStopStoring; +} + +// Converter from the Balance type to the BlockNumber type for math. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct SaturatingBalanceToBlockNumber; + +impl Convert> for SaturatingBalanceToBlockNumber { + fn convert(block_number: Balance) -> BlockNumberFor { + block_number.saturated_into() + } +} + +// Converter from the ThresholdType to the BlockNumber type and vice versa. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct ThresholdTypeToBlockNumberConverter; + +impl Convert> for ThresholdTypeToBlockNumberConverter { + fn convert(threshold: ThresholdType) -> BlockNumberFor { + threshold.saturated_into() + } +} + +impl ConvertBack> for ThresholdTypeToBlockNumberConverter { + fn convert_back(block_number: BlockNumberFor) -> ThresholdType { + block_number.into() + } +} + +/// Converter from the [`Hash`] type to the [`ThresholdType`]. +pub struct HashToThresholdTypeConverter; +impl Convert<::Hash, ThresholdType> + for HashToThresholdTypeConverter +{ + fn convert(hash: ::Hash) -> ThresholdType { + // Get the hash as bytes + let hash_bytes = hash.as_ref(); + + // Get the 4 least significant bytes of the hash and interpret them as an u32 + let truncated_hash_bytes: [u8; 4] = + hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); + + ThresholdType::from_be_bytes(truncated_hash_bytes) + } +} + +// Converter from the MerkleHash (H256) type to the RandomnessOutput (H256) type. +pub struct MerkleHashToRandomnessOutputConverter; + +impl Convert for MerkleHashToRandomnessOutputConverter { + fn convert(hash: H256) -> H256 { + hash + } +} + +// Converter from the ChunkId type to the MerkleHash (H256) type. +pub struct ChunkIdToMerkleHashConverter; + +impl Convert for ChunkIdToMerkleHashConverter { + fn convert(chunk_id: ChunkId) -> H256 { + let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); + let mut bytes = chunk_id_biguint.to_bytes_be(); + + // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros + if bytes.len() < 32 { + let mut padded_bytes = vec![0u8; 32 - bytes.len()]; + padded_bytes.extend(bytes); + bytes = padded_bytes; + } + + H256::from_slice(&bytes) + } +} + +impl pallet_bucket_nfts::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Buckets = Providers; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); +} diff --git a/runtime/src/configs/runtime_params.rs b/runtime/src/configs/runtime_params.rs index dcacfd2f9..e8f7295ef 100644 --- a/runtime/src/configs/runtime_params.rs +++ b/runtime/src/configs/runtime_params.rs @@ -1,49 +1,49 @@ -use crate::{configs::SpMinDeposit, Balance, BlockNumber, Runtime, UNIT}; -use frame_support::dynamic_params::{dynamic_pallet_params, dynamic_params}; - -#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::)] -pub mod dynamic_params { - use super::*; - #[dynamic_pallet_params] - #[codec(index = 0)] - pub mod runtime_config { - use super::*; - - #[codec(index = 0)] - #[allow(non_upper_case_globals)] - pub static SlashAmountPerMaxFileSize: Balance = 20 * UNIT; - - #[codec(index = 1)] - #[allow(non_upper_case_globals)] - // This can be interpreted as "a Provider with 10k UNITs of stake would get the minimum challenge period". - pub static StakeToChallengePeriod: Balance = - 10_000 * UNIT * Into::::into(MinChallengePeriod::get()); // 300k UNITs - - #[codec(index = 2)] - #[allow(non_upper_case_globals)] - // The CheckpointChallengePeriod is set to be equal to the longest possible challenge period (i.e. the - // StakeToChallengePeriod divided by the SpMinDeposit). - pub static CheckpointChallengePeriod: BlockNumber = (StakeToChallengePeriod::get() - / SpMinDeposit::get()) // 300k UNITs / 100 UNITs = 3k ticks (i.e. 5 hours with 6 seconds per tick) - .try_into() - .expect( - "StakeToChallengePeriod / SpMinDeposit should be a number of ticks that can fit in BlockNumber numerical type", - ); - - #[codec(index = 3)] - #[allow(non_upper_case_globals)] - pub static MinChallengePeriod: BlockNumber = 30; - } -} - -#[cfg(feature = "runtime-benchmarks")] -impl Default for RuntimeParameters { - fn default() -> Self { - RuntimeParameters::RuntimeConfig( - dynamic_params::runtime_config::Parameters::SlashAmountPerMaxFileSize( - dynamic_params::runtime_config::SlashAmountPerMaxFileSize, - Some(20 * UNIT), - ), - ) - } -} +use crate::{configs::SpMinDeposit, Balance, BlockNumber, Runtime, UNIT}; +use frame_support::dynamic_params::{dynamic_pallet_params, dynamic_params}; + +#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::)] +pub mod dynamic_params { + use super::*; + #[dynamic_pallet_params] + #[codec(index = 0)] + pub mod runtime_config { + use super::*; + + #[codec(index = 0)] + #[allow(non_upper_case_globals)] + pub static SlashAmountPerMaxFileSize: Balance = 20 * UNIT; + + #[codec(index = 1)] + #[allow(non_upper_case_globals)] + // This can be interpreted as "a Provider with 10k UNITs of stake would get the minimum challenge period". + pub static StakeToChallengePeriod: Balance = + 10_000 * UNIT * Into::::into(MinChallengePeriod::get()); // 300k UNITs + + #[codec(index = 2)] + #[allow(non_upper_case_globals)] + // The CheckpointChallengePeriod is set to be equal to the longest possible challenge period (i.e. the + // StakeToChallengePeriod divided by the SpMinDeposit). + pub static CheckpointChallengePeriod: BlockNumber = (StakeToChallengePeriod::get() + / SpMinDeposit::get()) // 300k UNITs / 100 UNITs = 3k ticks (i.e. 5 hours with 6 seconds per tick) + .try_into() + .expect( + "StakeToChallengePeriod / SpMinDeposit should be a number of ticks that can fit in BlockNumber numerical type", + ); + + #[codec(index = 3)] + #[allow(non_upper_case_globals)] + pub static MinChallengePeriod: BlockNumber = 30; + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for RuntimeParameters { + fn default() -> Self { + RuntimeParameters::RuntimeConfig( + dynamic_params::runtime_config::Parameters::SlashAmountPerMaxFileSize( + dynamic_params::runtime_config::SlashAmountPerMaxFileSize, + Some(20 * UNIT), + ), + ) + } +} diff --git a/runtime/src/configs/xcm_config.rs b/runtime/src/configs/xcm_config.rs index b677c7e15..7ec4b19d7 100644 --- a/runtime/src/configs/xcm_config.rs +++ b/runtime/src/configs/xcm_config.rs @@ -1,237 +1,238 @@ -use crate::{ - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, -}; -use frame_support::{ - parameter_types, - traits::{ConstU32, Contains, Everything, Nothing}, - weights::Weight, -}; -use frame_system::EnsureRoot; -use pallet_xcm::XcmPassthrough; -use parachains_common::xcm_config::{ConcreteAssetFromSystem, ParentRelayOrSiblingParachains}; -use polkadot_parachain_primitives::primitives::Sibling; -use polkadot_runtime_common::impls::ToAuthor; -use xcm::latest::prelude::*; -use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, - HashedDescription, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, -}; -use xcm_executor::XcmExecutor; - -parameter_types! { - pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Polkadot); - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub const GovernanceLocation: Location = Location::parent(); -} - -/// Type for specifying how a `Location` 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 parent `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, - // Foreign locations alias into accounts according to a hash of their standard description. - HashedDescription>, -); - -/// Means for transacting assets on this chain. -pub type LocalAssetTransactor = ( - FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Do a simple punn to convert an AccountId32 Location into a native chain 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. - (), - >, - // TODO: Check safety of using a NonFungiblesAdapter for remote management of NFTs - /* NonFungiblesAdapter< - // Use this pallet: - Nfts, - ConvertedConcreteId, - LocationToAccountId, - AccountId, - NoChecking, - CheckingAccount, - >, */ -); - -/// 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, - // The Relay Chain (Parent) location should convert to a Root origin when needed. - ParentAsSuperuser, - // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when - // recognized. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognized. - 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, -); - -parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. TODO: Benchmark - pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; -} - -pub struct ParentOrParentsExecutivePlurality; -impl Contains for ParentOrParentsExecutivePlurality { - fn contains(location: &Location) -> bool { - matches!( - location.unpack(), - (1, []) - | ( - 1, - [Plurality { - id: BodyId::Executive, - .. - }] - ) - ) - } -} - -pub type Barrier = TrailingSetTopicAsId< - DenyThenTry< - DenyReserveTransferToRelayChain, - ( - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - // Allow XCMs with some computed origins to pass through. - WithComputedOrigin< - ( - // If the message is one that immediately attempts to pay for execution, then - // allow it. - AllowTopLevelPaidExecutionFrom, - // The locations listed below get free execution. - // Parent and its executive plurality get free execution. - AllowExplicitUnpaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - // HRMP notifications from the relay chain are OK. - AllowHrmpNotificationsFromRelayChain, - ), - UniversalLocation, - ConstU32<8>, - >, - ), - >, ->; - -/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: -/// -/// - DOT with the parent Relay Chain and sibling system parachains -pub type TrustedTeleporters = ConcreteAssetFromSystem; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = NativeAsset; - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; // TODO: Benchmark and add correct weights - type Trader = - UsingComponents>; - 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; - type TransactionalProcessor = FrameTransactionalProcessor; - // TODO: Implement these handlers if needed - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} - -/// Converts a local signed origin into an XCM location. -/// Forms the basis for local origins sending/executing XCMs. -pub type LocalOriginToLocation = SignedToAccountId32; - -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = WithUniqueTopic<( - // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, - // ..and XCMP to communicate with the sibling chains. - XcmpQueue, -)>; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - // We want to disallow users sending (arbitrary) XCMs from this chain. - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - // Anyone can execute XCM messages locally. - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Everything; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - 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 WeightInfo = pallet_xcm::TestWeightInfo; - type AdminOrigin = EnsureRoot; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} +use crate::{ + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, +}; +use frame_support::{ + parameter_types, + traits::{ConstU32, Contains, Everything, Nothing}, + weights::Weight, +}; +use frame_system::EnsureRoot; +use pallet_xcm::XcmPassthrough; +use parachains_common::xcm_config::{ConcreteAssetFromSystem, ParentRelayOrSiblingParachains}; +use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::impls::ToAuthor; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, +}; +use xcm_executor::XcmExecutor; + +parameter_types! { + pub const RelayLocation: Location = Location::parent(); + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub const GovernanceLocation: Location = Location::parent(); +} + +/// Type for specifying how a `Location` 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 parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, +); + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = ( + FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Do a simple punn to convert an AccountId32 Location into a native chain 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. + (), + >, + // TODO: Check safety of using a NonFungiblesAdapter for remote management of NFTs + /* NonFungiblesAdapter< + // Use this pallet: + Nfts, + ConvertedConcreteId, + LocationToAccountId, + AccountId, + NoChecking, + CheckingAccount, + >, */ +); + +/// 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, + // The Relay Chain (Parent) location should convert to a Root origin when needed. + ParentAsSuperuser, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognized. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognized. + 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, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. TODO: Benchmark + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct ParentOrParentsExecutivePlurality; +impl Contains for ParentOrParentsExecutivePlurality { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (1, []) + | ( + 1, + [Plurality { + id: BodyId::Executive, + .. + }] + ) + ) + } +} + +pub type Barrier = TrailingSetTopicAsId< + DenyThenTry< + DenyReserveTransferToRelayChain, + ( + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attempts to pay for execution, then + // allow it. + AllowTopLevelPaidExecutionFrom, + // The locations listed below get free execution. + // Parent and its executive plurality get free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + // HRMP notifications from the relay chain are OK. + AllowHrmpNotificationsFromRelayChain, + ), + UniversalLocation, + ConstU32<8>, + >, + ), + >, +>; + +/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: +/// +/// - DOT with the parent Relay Chain and sibling system parachains +pub type TrustedTeleporters = ConcreteAssetFromSystem; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; // TODO: Benchmark and add correct weights + type Trader = + UsingComponents>; + 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; + type TransactionalProcessor = FrameTransactionalProcessor; + // TODO: Implement these handlers if needed + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +/// Converts a local signed origin into an XCM location. +/// Forms the basis for local origins sending/executing XCMs. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = WithUniqueTopic<( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +)>; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We want to disallow users sending (arbitrary) XCMs from this chain. + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + 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 WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index df5a716b0..07e706ca0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,285 +1,274 @@ -#![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")); - -pub mod apis; -mod configs; -mod weights; - -use smallvec::smallvec; -use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, - traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiSignature, -}; - -use sp_std::prelude::*; -#[cfg(feature = "std")] -use sp_version::NativeVersion; -use sp_version::RuntimeVersion; - -use frame_support::{ - construct_runtime, - weights::{ - constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightToFeeCoefficient, - WeightToFeeCoefficients, WeightToFeePolynomial, - }, -}; -pub use parachains_common::BlockNumber; -pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; -use sp_std::prelude::Vec; - -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; - -use weights::ExtrinsicBaseWeight; - -pub use crate::configs::{StorageDataUnit, StorageProofsMerkleTrieLayout}; - -/// 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 Nonce = u32; - -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; - -/// 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, - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, - frame_metadata_hash_extension::CheckMetadataHash, -); - -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; - -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, ->; - -/// 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::*; - use sp_runtime::{ - generic, - traits::{BlakeTwo256, Hash as HashT}, - }; - - 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; - /// Opaque block hash type. - pub type Hash = ::Output; -} - -impl_opaque_keys! { - pub struct SessionKeys { - pub aura: Aura, - } -} - -#[sp_version::runtime_version] -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("storage-hub-runtime"), - impl_name: create_runtime_str!("storage-hub-runtime"), - authoring_version: 1, - spec_version: 1, - impl_version: 0, - apis: apis::RUNTIME_API_VERSIONS, - transaction_version: 1, - state_version: 1, -}; - -/// 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; -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; - -// 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; - -/// 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 2 seconds of compute with a 6 second average block. -pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( - WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), - cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, -); - -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the -/// relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 2; -/// How many parachain blocks are processed by the relay chain per parent. Limits the number of -/// blocks authored per slot. -const BLOCK_PROCESSING_VELOCITY: u32 = 1; -/// Relay chain slot duration, in milliseconds. -const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; - -/// 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(), - } -} - -// 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, - - // Monetary stuff. - Balances: pallet_balances = 10, - TransactionPayment: pallet_transaction_payment = 11, - - // Governance - Sudo: pallet_sudo = 15, - - // Collator support. The order of these 4 are important and shall not change. - Authorship: pallet_authorship = 20, - CollatorSelection: pallet_collator_selection = 21, - Session: pallet_session = 22, - Aura: pallet_aura = 23, - AuraExt: cumulus_pallet_aura_ext = 24, - - // XCM helpers. - XcmpQueue: cumulus_pallet_xcmp_queue = 30, - PolkadotXcm: pallet_xcm = 31, - CumulusXcm: cumulus_pallet_xcm = 32, - MessageQueue: pallet_message_queue = 33, - - // Storage Hub - Providers: pallet_storage_providers = 40, - FileSystem: pallet_file_system = 41, - ProofsDealer: pallet_proofs_dealer = 42, - Randomness: pallet_randomness = 43, - PaymentStreams: pallet_payment_streams = 44, - BucketNfts: pallet_bucket_nfts = 45, - - // Miscellaneous - Nfts: pallet_nfts = 50, - Parameters: pallet_parameters = 51, - } -); - -#[cfg(feature = "runtime-benchmarks")] -mod benches { - frame_benchmarking::define_benchmarks!( - [frame_system, SystemBench::] - [pallet_balances, Balances] - [pallet_session, SessionBench::] - [pallet_timestamp, Timestamp] - [pallet_message_queue, MessageQueue] - [pallet_sudo, Sudo] - [pallet_collator_selection, CollatorSelection] - [cumulus_pallet_parachain_system, ParachainSystem] - [cumulus_pallet_xcmp_queue, XcmpQueue] - [nfts, Nfts] - [pallet_parameters, Parameters] - ); -} - -cumulus_pallet_parachain_system::register_validate_block! { - Runtime = Runtime, - BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, -} +#![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")); + +pub mod apis; +pub mod configs; +mod weights; + +use smallvec::smallvec; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, +}; + +use sp_std::prelude::*; +use sp_version::RuntimeVersion; + +use frame_support::{ + construct_runtime, + weights::{ + constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightToFeeCoefficient, + WeightToFeeCoefficients, WeightToFeePolynomial, + }, +}; +pub use parachains_common::BlockNumber; +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; +use sp_std::prelude::Vec; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +use weights::ExtrinsicBaseWeight; + +pub use crate::configs::{xcm_config, StorageDataUnit, StorageProofsMerkleTrieLayout}; + +/// 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 Nonce = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// 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, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, + frame_metadata_hash_extension::CheckMetadataHash, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +/// 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::*; + use sp_runtime::{ + generic, + traits::{BlakeTwo256, Hash as HashT}, + }; + + 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; + /// Opaque block hash type. + pub type Hash = ::Output; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("storage-hub-runtime"), + impl_name: create_runtime_str!("storage-hub-runtime"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: apis::RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// 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; +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; + +// 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; + +/// 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 2 seconds of compute with a 6 second average block. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the +/// relay chain. +const UNINCLUDED_SEGMENT_CAPACITY: u32 = 2; +/// How many parachain blocks are processed by the relay chain per parent. Limits the number of +/// blocks authored per slot. +const BLOCK_PROCESSING_VELOCITY: u32 = 1; +/// Relay chain slot duration, in milliseconds. +const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; + +// 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, + + // Monetary stuff. + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Governance + Sudo: pallet_sudo = 15, + + // Collator support. The order of these 4 are important and shall not change. + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + PolkadotXcm: pallet_xcm = 31, + CumulusXcm: cumulus_pallet_xcm = 32, + MessageQueue: pallet_message_queue = 33, + + // Storage Hub + Providers: pallet_storage_providers = 40, + FileSystem: pallet_file_system = 41, + ProofsDealer: pallet_proofs_dealer = 42, + Randomness: pallet_randomness = 43, + PaymentStreams: pallet_payment_streams = 44, + BucketNfts: pallet_bucket_nfts = 45, + + // Miscellaneous + Nfts: pallet_nfts = 50, + Parameters: pallet_parameters = 51, + } +); + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, SystemBench::] + [pallet_balances, Balances] + [pallet_session, SessionBench::] + [pallet_timestamp, Timestamp] + [pallet_message_queue, MessageQueue] + [pallet_sudo, Sudo] + [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] + [cumulus_pallet_xcmp_queue, XcmpQueue] + [nfts, Nfts] + [pallet_parameters, Parameters] + ); +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, +} diff --git a/test/package.json b/test/package.json index 869302179..d15fc8193 100644 --- a/test/package.json +++ b/test/package.json @@ -1,85 +1,85 @@ -{ - "name": "@storagehub/test", - "version": "0.1.0", - "description": "Test package for storagehub", - "main": "index.js", - "type": "module", - "engines": { - "node": "22.x.x" - }, - "scripts": { - "bundle-types": "cd ../types-bundle && pnpm i && pnpm build && pnpm fmt:fix && cd ../test", - "typegen": "pnpm bundle-types && cd ../api-augment && pnpm scrape && pnpm generate:all && pnpm build && pnpm fmt:fix; cd ../test", - "fmt": "biome format .", - "fmt:fix": "biome format . --write", - "lint": "biome lint .", - "typecheck": "tsc --noEmit", - "crossbuild:mac": "DOCKER_DEFAULT_PLATFORM=linux/amd64 pnpm tsx scripts/crossBuildMac.ts", - "docker:build": "DOCKER_DEFAULT_PLATFORM=linux/amd64 pnpm tsx scripts/buildLocalDocker.ts", - "docker:start": "docker compose -f ../docker/local-node-compose.yml -p sh_dev_node up -d", - "docker:start:latest": "docker compose -f ../docker/latest-node-compose.yml -p sh_dev_node up -d", - "docker:stop": "docker compose -f ../docker/local-node-compose.yml -p sh_dev_node down", - "docker:stop:latest": "docker compose -f ../docker/latest-node-compose.yml -p sh_dev_node down", - "docker:start:bspnet": "NOISY=0 ROCKSDB=0 pnpm tsx scripts/bspNetBootstrap.ts", - "docker:stop:bspnet": "docker compose -f ../docker/local-dev-bsp-compose.yml down --remove-orphans && docker volume prune -f", - "docker:start:fullNet": "NOISY=0 ROCKSDB=0 pnpm tsx scripts/fullNetBootstrap.ts", - "docker:stop:fullNet": "docker compose -f ../docker/local-dev-full-compose.yml down --remove-orphans && docker volume prune -f", - "docker:start:bspnet:rocksdb": "NOISY=0 ROCKSDB=1 pnpm tsx scripts/bspNetBootstrap.ts", - "docker:stop:bspnet:rocksdb": "docker compose -f ../docker/local-dev-bsp-rocksdb-compose.yml down --remove-orphans && docker volume prune -f", - "docker:start:fullNet:rocksdb": "NOISY=0 ROCKSDB=1 pnpm tsx scripts/fullNetBootstrap.ts", - "docker:stop:fullNet:rocksdb": "docker compose -f ../docker/local-dev-full-rocksdb-compose.yml down --remove-orphans && docker volume prune -f", - "docker:start:noisynet": "NOISY=1 ROCKSDB=0 pnpm tsx scripts/bspNetBootstrap.ts", - "docker:stop:noisynet": "docker compose -f ../docker/noisy-bsp-compose.yml down --remove-orphans && docker volume prune -f", - "zombie:run:latest": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet spawn configs/simple.toml", - "zombie:run:local": "DOCKER_BUILDKIT=0 SH_IMAGE=storage-hub:local pnpm zombienet spawn configs/simple.toml", - "zombie:run:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet spawn configs/simpleNative.toml", - "zombie:run:full": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet spawn configs/fullNetwork.toml", - "zombie:run:full:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet spawn configs/fullNetworkNative.toml", - "zombie:setup:native": "pnpm tsx scripts/fullNetworkSetup.ts", - "zombie:test:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet test --provider native configs/simpleNative.zndsl", - "zombie:test:local": "SH_IMAGE=storage-hub:local pnpm zombienet test configs/simple.zndsl", - "zombie:test:latest": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet test configs/simple.zndsl", - "test:full": "node --no-deprecation --import tsx --test ./suites/zombie/**.spec.ts", - "test:bspnet": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test ./suites/integration/bsp/**.test.ts", - "test:bspnet:only": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --import tsx --test-concurrency 1 --test --test-only ./suites/integration/bsp/**.test.ts", - "test:user": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test ./suites/integration/user/**.test.ts", - "test:user:only": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test --test-only ./suites/integration/user/**.test.ts", - "test:node": "node --no-deprecation --import tsx --test ./suites/solo-node/**/**.test.ts", - "test:node:only": "node --no-deprecation --import tsx --test --test-only ./suites/solo-node/**/**.test.ts", - "test:node:single": "node --no-deprecation --import tsx --test --test-name-pattern=$FILTER ./suites/solo-node/**/**.test.ts" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@polkadot/api": "12.4.2", - "@polkadot/api-augment": "12.4.2", - "@polkadot/keyring": "13.0.2", - "@polkadot/types": "12.4.2", - "@polkadot/util": "13.0.2", - "@polkadot/util-crypto": "13.0.2", - "@polkadot/wasm-crypto": "7.3.2", - "@reporters/github": "1.7.0", - "@storagehub/api-augment": "workspace:*", - "@storagehub/types-bundle": "workspace:*", - "@zombienet/cli": "1.3.109", - "@zombienet/utils": "0.0.25", - "docker-compose": "0.24.8", - "dockerode": "4.0.2", - "dotenv": "16.4.5", - "inquirer": "10.2.0", - "json-bigint": "^1.0.0", - "testcontainers": "10.13.0", - "tiny-invariant": "^1.3.3", - "tsx": "4.19.0", - "yaml": "^2.5.1" - }, - "devDependencies": { - "@biomejs/biome": "1.8.3", - "@types/dockerode": "3.3.31", - "@types/inquirer": "9.0.7", - "@types/json-bigint": "1.0.4", - "@types/node": "22.5.2", - "typescript": "5.5.4" - } +{ + "name": "@storagehub/test", + "version": "0.1.0", + "description": "Test package for storagehub", + "main": "index.js", + "type": "module", + "engines": { + "node": "22.x.x" + }, + "scripts": { + "bundle-types": "cd ../types-bundle && pnpm i && pnpm build && pnpm fmt:fix && cd ../test", + "typegen": "pnpm bundle-types && cd ../api-augment && pnpm scrape && pnpm generate:all && pnpm build && pnpm fmt:fix; cd ../test", + "fmt": "biome format .", + "fmt:fix": "biome format . --write", + "lint": "biome lint .", + "typecheck": "tsc --noEmit", + "crossbuild:mac": "DOCKER_DEFAULT_PLATFORM=linux/amd64 pnpm tsx scripts/crossBuildMac.ts", + "docker:build": "DOCKER_DEFAULT_PLATFORM=linux/amd64 pnpm tsx scripts/buildLocalDocker.ts", + "docker:start": "docker compose -f ../docker/local-node-compose.yml -p sh_dev_node up -d", + "docker:start:latest": "docker compose -f ../docker/latest-node-compose.yml -p sh_dev_node up -d", + "docker:stop": "docker compose -f ../docker/local-node-compose.yml -p sh_dev_node down", + "docker:stop:latest": "docker compose -f ../docker/latest-node-compose.yml -p sh_dev_node down", + "docker:start:bspnet": "NOISY=0 ROCKSDB=0 pnpm tsx scripts/bspNetBootstrap.ts", + "docker:stop:bspnet": "docker compose -f ../docker/local-dev-bsp-compose.yml down --remove-orphans && docker volume prune -f", + "docker:start:fullNet": "NOISY=0 ROCKSDB=0 pnpm tsx scripts/fullNetBootstrap.ts", + "docker:stop:fullNet": "docker compose -f ../docker/local-dev-full-compose.yml down --remove-orphans && docker volume prune -f", + "docker:start:bspnet:rocksdb": "NOISY=0 ROCKSDB=1 pnpm tsx scripts/bspNetBootstrap.ts", + "docker:stop:bspnet:rocksdb": "docker compose -f ../docker/local-dev-bsp-rocksdb-compose.yml down --remove-orphans && docker volume prune -f", + "docker:start:fullNet:rocksdb": "NOISY=0 ROCKSDB=1 pnpm tsx scripts/fullNetBootstrap.ts", + "docker:stop:fullNet:rocksdb": "docker compose -f ../docker/local-dev-full-rocksdb-compose.yml down --remove-orphans && docker volume prune -f", + "docker:start:noisynet": "NOISY=1 ROCKSDB=0 pnpm tsx scripts/bspNetBootstrap.ts", + "docker:stop:noisynet": "docker compose -f ../docker/noisy-bsp-compose.yml down --remove-orphans && docker volume prune -f", + "zombie:run:latest": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet spawn configs/simple.toml", + "zombie:run:local": "DOCKER_BUILDKIT=0 SH_IMAGE=storage-hub:local pnpm zombienet spawn configs/simple.toml", + "zombie:run:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet spawn configs/simpleNative.toml", + "zombie:run:full": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet spawn configs/fullNetwork.toml", + "zombie:run:full:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet spawn configs/fullNetworkNative.toml", + "zombie:setup:native": "pnpm tsx scripts/fullNetworkSetup.ts", + "zombie:test:native": "pnpm tsx scripts/downloadPolkadot.ts 1.9.0 && pnpm zombienet test --provider native configs/simpleNative.zndsl", + "zombie:test:local": "SH_IMAGE=storage-hub:local pnpm zombienet test configs/simple.zndsl", + "zombie:test:latest": "SH_IMAGE=docker.io/moonsonglabs/storage-hub:latest pnpm zombienet test configs/simple.zndsl", + "test:full": "node --no-deprecation --import tsx --test ./suites/zombie/**.spec.ts", + "test:bspnet": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test ./suites/integration/bsp/**.test.ts", + "test:bspnet:only": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --import tsx --test-concurrency 1 --test --test-only ./suites/integration/bsp/**.test.ts", + "test:user": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test ./suites/integration/user/**.test.ts", + "test:user:only": "NODE_OPTIONS='--no-deprecation' pnpm tsx scripts/checkRunning.ts && node --no-deprecation --test-concurrency 1 --import tsx --test --test-only ./suites/integration/user/**.test.ts", + "test:node": "node --no-deprecation --import tsx --test ./suites/solo-node/**/**.test.ts", + "test:node:only": "node --no-deprecation --import tsx --test --test-only ./suites/solo-node/**/**.test.ts", + "test:node:single": "node --no-deprecation --import tsx --test --test-name-pattern=$FILTER ./suites/solo-node/**/**.test.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@polkadot/api": "12.4.2", + "@polkadot/api-augment": "12.4.2", + "@polkadot/keyring": "13.0.2", + "@polkadot/types": "12.4.2", + "@polkadot/util": "13.0.2", + "@polkadot/util-crypto": "13.0.2", + "@polkadot/wasm-crypto": "7.3.2", + "@reporters/github": "1.7.0", + "@storagehub/api-augment": "workspace:*", + "@storagehub/types-bundle": "workspace:*", + "@zombienet/cli": "1.3.109", + "@zombienet/utils": "0.0.25", + "docker-compose": "0.24.8", + "dockerode": "4.0.2", + "dotenv": "16.4.5", + "inquirer": "10.2.0", + "json-bigint": "^1.0.0", + "testcontainers": "10.13.0", + "tiny-invariant": "^1.3.3", + "tsx": "4.19.0", + "yaml": "^2.5.1" + }, + "devDependencies": { + "@biomejs/biome": "1.8.3", + "@types/dockerode": "3.3.31", + "@types/inquirer": "9.0.7", + "@types/json-bigint": "1.0.4", + "@types/node": "22.5.2", + "typescript": "5.5.4" + } } \ No newline at end of file diff --git a/xcm-simulator/Cargo.toml b/xcm-simulator/Cargo.toml index dc6e46aef..99d4f5f2f 100644 --- a/xcm-simulator/Cargo.toml +++ b/xcm-simulator/Cargo.toml @@ -85,11 +85,11 @@ xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } xcm-simulator = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } runtime-constants = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } -cumulus-pallet-dmp-queue = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } cumulus-pallet-session-benchmarking = { workspace = true } cumulus-pallet-xcm = { workspace = true } @@ -109,7 +109,6 @@ default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", @@ -176,7 +175,6 @@ std = [ ] 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", @@ -211,7 +209,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/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", diff --git a/xcm-simulator/src/mock_message_queue.rs b/xcm-simulator/src/mock_message_queue.rs index 830ed1d39..7ffc249f1 100644 --- a/xcm-simulator/src/mock_message_queue.rs +++ b/xcm-simulator/src/mock_message_queue.rs @@ -9,9 +9,12 @@ use polkadot_parachain_primitives::primitives::{ use sp_runtime::traits::{Get, Hash}; use sp_std::prelude::*; -use xcm::{latest::prelude::*, VersionedXcm}; +use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm}; pub use pallet::*; +use xcm_builder::InspectMessageQueues; + +use crate::ParachainXcmRouter; #[frame_support::pallet] pub mod pallet { @@ -203,3 +206,9 @@ pub mod pallet { } } } + +impl InspectMessageQueues for ParachainXcmRouter> { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + vec![] + } +} diff --git a/xcm-simulator/src/parachain/xcm_config/mod.rs b/xcm-simulator/src/parachain/xcm_config/mod.rs index 5ddeb03a2..fda4a173b 100644 --- a/xcm-simulator/src/parachain/xcm_config/mod.rs +++ b/xcm-simulator/src/parachain/xcm_config/mod.rs @@ -1,63 +1,64 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod asset_transactor; -pub mod barrier; -pub mod constants; -pub mod location_converter; -pub mod origin_converter; -pub mod reserve; -pub mod teleporter; -pub mod weigher; - -use crate::parachain::{MsgQueue, PolkadotXcm, RuntimeCall}; -use frame_support::traits::{Everything, Nothing}; -use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; - -// Generated from `decl_test_network!` -pub type XcmRouter = crate::ParachainXcmRouter; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = asset_transactor::AssetTransactor; - type OriginConverter = origin_converter::OriginConverter; - type IsReserve = reserve::TrustedReserves; - type IsTeleporter = teleporter::TrustedTeleporters; - type UniversalLocation = constants::UniversalLocation; - type Barrier = barrier::Barrier; - type Weigher = weigher::Weigher; - type Trader = FixedRateOfFungible; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = PolkadotXcm; - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = (); - type FeeManager = (); - type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod reserve; +pub mod teleporter; +pub mod weigher; + +use crate::parachain::{MsgQueue, PolkadotXcm, RuntimeCall}; +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; + +// Generated from `decl_test_network!` +pub type XcmRouter = crate::ParachainXcmRouter; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = origin_converter::OriginConverter; + type IsReserve = reserve::TrustedReserves; + type IsTeleporter = teleporter::TrustedTeleporters; + type UniversalLocation = constants::UniversalLocation; + type Barrier = barrier::Barrier; + type Weigher = weigher::Weigher; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} diff --git a/xcm-simulator/src/relay_chain/mod.rs b/xcm-simulator/src/relay_chain/mod.rs index 283d07e1d..0c250a0b0 100644 --- a/xcm-simulator/src/relay_chain/mod.rs +++ b/xcm-simulator/src/relay_chain/mod.rs @@ -128,7 +128,7 @@ type Block = frame_system::mocking::MockBlock; parameter_types! { /// Amount of weight that can be spent per block to service messages. pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); - pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueHeapSize: u32 = 105_472; pub const MessageQueueMaxStale: u32 = 16; } diff --git a/xcm-simulator/src/relay_chain/xcm_config/mod.rs b/xcm-simulator/src/relay_chain/xcm_config/mod.rs index 230b05040..f36e1eb8a 100644 --- a/xcm-simulator/src/relay_chain/xcm_config/mod.rs +++ b/xcm-simulator/src/relay_chain/xcm_config/mod.rs @@ -1,82 +1,83 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod asset_transactor; -pub mod barrier; -pub mod constants; -pub mod location_converter; -pub mod origin_converter; -pub mod weigher; - -use crate::{ - opaque::WildFungible, - relay_chain::{constants::TokenLocation, RuntimeCall, XcmPallet}, - AssetFilter, AssetId, - Junction::Parachain, - Location, - WildAsset::AllOf, -}; - -use frame_support::parameter_types; -use frame_support::traits::{Everything, Nothing}; -use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; -use xcm_executor::Config; - -// Generated from `decl_test_network!` -pub type XcmRouter = crate::RelayChainXcmRouter; - -parameter_types! { - pub const Dot: AssetFilter = AssetFilter::Wild(AllOf { fun: WildFungible, id: AssetId(TokenLocation::get()) }); - pub StorageHubLocation: Location = Parachain(1).into_location(); - pub DotForStorageHub: (AssetFilter, Location) = (Dot::get(), StorageHubLocation::get()); -} - -/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: -/// -/// - DOT with the parent Relay Chain and StorageHub -pub type TrustedTeleporters = xcm_builder::Case; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = asset_transactor::AssetTransactor; - type OriginConverter = origin_converter::OriginConverter; - type IsReserve = (); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = constants::UniversalLocation; - type Barrier = barrier::Barrier; - type Weigher = weigher::Weigher; - type Trader = FixedRateOfFungible; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = XcmPallet; - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = (); - type FeeManager = (); - type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use crate::{ + opaque::WildFungible, + relay_chain::{constants::TokenLocation, RuntimeCall, XcmPallet}, + AssetFilter, AssetId, + Junction::Parachain, + Location, + WildAsset::AllOf, +}; + +use frame_support::parameter_types; +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; +use xcm_executor::Config; + +// Generated from `decl_test_network!` +pub type XcmRouter = crate::RelayChainXcmRouter; + +parameter_types! { + pub const Dot: AssetFilter = AssetFilter::Wild(AllOf { fun: WildFungible, id: AssetId(TokenLocation::get()) }); + pub StorageHubLocation: Location = Parachain(1).into_location(); + pub DotForStorageHub: (AssetFilter, Location) = (Dot::get(), StorageHubLocation::get()); +} + +/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: +/// +/// - DOT with the parent Relay Chain and StorageHub +pub type TrustedTeleporters = xcm_builder::Case; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = origin_converter::OriginConverter; + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = constants::UniversalLocation; + type Barrier = barrier::Barrier; + type Weigher = weigher::Weigher; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = XcmPallet; + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} diff --git a/xcm-simulator/src/storagehub/apis.rs b/xcm-simulator/src/storagehub/apis.rs index a343b8d86..b04c5d920 100644 --- a/xcm-simulator/src/storagehub/apis.rs +++ b/xcm-simulator/src/storagehub/apis.rs @@ -1,373 +1,373 @@ -use crate::storagehub::*; -use crate::*; -use frame_support::{ - genesis_builder_helper::{build_state, get_preset}, - weights::Weight, -}; -use pallet_aura::Authorities; -use pallet_file_system_runtime_api::*; -use pallet_payment_streams_runtime_api::*; -use pallet_proofs_dealer::types::{KeyFor, ProviderIdFor, RandomnessOutputFor}; -use pallet_proofs_dealer_runtime_api::*; -use pallet_storage_providers::types::{ - BackupStorageProvider, BackupStorageProviderId, BucketId, MainStorageProviderId, ProviderId, - StorageDataUnit, StorageProviderId, -}; -use pallet_storage_providers_runtime_api::*; -use shp_file_metadata::ChunkId; -use shp_traits::TrieRemoveMutation; -use sp_api::impl_runtime_apis; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, ExtrinsicInclusionMode, -}; -use sp_std::prelude::Vec; -use sp_version::RuntimeVersion; - -// Local module imports -use super::{ - AccountId, Balance, Block, Executive, InherentDataExt, Nonce, ParachainSystem, Runtime, - RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, -}; - -impl_runtime_apis! { - /// Allows the collator client to query its runtime to determine whether it should author a block - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - included_hash: ::Hash, - slot: cumulus_primitives_aura::Slot, - ) -> bool { - storagehub::configs::ConsensusHook::can_build_upon(included_hash, slot) - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(storagehub::SLOT_DURATION) - } - - fn authorities() -> Vec { - Authorities::::get().into_inner() - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { - 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() -> sp_std::vec::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, KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(account: AccountId) -> Nonce { - System::account_nonce(account) - } - } - - 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 pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi - for Runtime - { - fn query_call_info( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::RuntimeDispatchInfo { - TransactionPayment::query_call_info(call, len) - } - fn query_call_fee_details( - call: RuntimeCall, - len: u32, - ) -> pallet_transaction_payment::FeeDetails { - TransactionPayment::query_call_fee_details(call, 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 cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info(header) - } - } - - #[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, configs::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() - } - } - - #[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 frame_system_benchmarking::Pallet as SystemBench; - use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - - 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::{BenchmarkError, Benchmarking, BenchmarkBatch}; - - use frame_system_benchmarking::Pallet as SystemBench; - 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 cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} - - use frame_support::traits::WhitelistedStorageKeys; - let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); - - let mut batches = Vec::::new(); - let params = (&config, &whitelist); - add_benchmarks!(params, batches); - - if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } - Ok(batches) - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) - } - - fn preset_names() -> Vec { - vec![] - } - } - - impl pallet_file_system_runtime_api::FileSystemApi, MainStorageProviderId, H256, BlockNumber, ChunkId> for Runtime { - fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: H256) -> Result { - FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key) - } - - fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: H256) -> Result, QueryBspConfirmChunksToProveForFileError> { - FileSystem::query_bsp_confirm_chunks_to_prove_for_file(bsp_id, file_key) - } - - fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: H256) -> Result, QueryMspConfirmChunksToProveForFileError> { - FileSystem::query_msp_confirm_chunks_to_prove_for_file(msp_id, file_key) - } - } - - impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { - fn get_users_with_debt_over_threshold(provider_id: &ProviderIdFor, threshold: Balance) -> Result, GetUsersWithDebtOverThresholdError> { - PaymentStreams::get_users_with_debt_over_threshold(provider_id, threshold) - } - fn get_users_of_payment_streams_of_provider(provider_id: &ProviderIdFor) -> Vec { - PaymentStreams::get_users_of_payment_streams_of_provider(provider_id) - } - } - - impl pallet_proofs_dealer_runtime_api::ProofsDealerApi, BlockNumber, KeyFor, RandomnessOutputFor, TrieRemoveMutation> for Runtime { - fn get_last_tick_provider_submitted_proof(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_last_tick_provider_submitted_proof(provider_id) - } - - fn get_last_checkpoint_challenge_tick() -> BlockNumber { - ProofsDealer::get_last_checkpoint_challenge_tick() - } - - fn get_checkpoint_challenges( - tick: BlockNumber - ) -> Result, Option)>, GetCheckpointChallengesError> { - ProofsDealer::get_checkpoint_challenges(tick) - } - - fn get_challenge_seed(tick: BlockNumber) -> Result, GetChallengeSeedError> { - ProofsDealer::get_challenge_seed(tick) - } - - fn get_challenge_period(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_challenge_period(provider_id) - } - - fn get_checkpoint_challenge_period() -> BlockNumber { - ProofsDealer::get_checkpoint_challenge_period() - } - - fn get_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor, count: u32) -> Vec> { - ProofsDealer::get_challenges_from_seed(seed, provider_id, count) - } - - fn get_forest_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor) -> Vec> { - ProofsDealer::get_forest_challenges_from_seed(seed, provider_id) - } - - fn get_current_tick() -> BlockNumber { - ProofsDealer::get_current_tick() - } - - fn get_next_deadline_tick(provider_id: &ProviderIdFor) -> Result { - ProofsDealer::get_next_deadline_tick(provider_id) - } - } - - impl pallet_storage_providers_runtime_api::StorageProvidersApi, BackupStorageProvider, AccountId, ProviderId, StorageProviderId, StorageDataUnit, Balance, BucketId> for Runtime { - fn get_bsp_info(bsp_id: &BackupStorageProviderId) -> Result, GetBspInfoError> { - Providers::get_bsp_info(bsp_id) - } - - fn get_storage_provider_id(who: &AccountId) -> Option> { - Providers::get_storage_provider_id(who) - } - - fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result, QueryMspIdOfBucketIdError> { - Providers::query_msp_id_of_bucket_id(bucket_id) - } - - fn query_storage_provider_capacity(provider_id: &ProviderId) -> Result, QueryStorageProviderCapacityError> { - Providers::query_storage_provider_capacity(provider_id) - } - - fn query_available_storage_capacity(provider_id: &ProviderId) -> Result, QueryAvailableStorageCapacityError> { - Providers::query_available_storage_capacity(provider_id) - } - - fn query_earliest_change_capacity_block(provider_id: &BackupStorageProviderId) -> Result { - Providers::query_earliest_change_capacity_block(provider_id) - } - - fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option { - Providers::get_worst_case_scenario_slashable_amount(&provider_id).ok() - } - - fn get_slash_amount_per_max_file_size() -> Balance { - Providers::get_slash_amount_per_max_file_size() - } - } -} +use crate::storagehub::*; +use crate::*; +use frame_support::{ + genesis_builder_helper::{build_state, get_preset}, + weights::Weight, +}; +use pallet_aura::Authorities; +use pallet_file_system_runtime_api::*; +use pallet_payment_streams_runtime_api::*; +use pallet_proofs_dealer::types::{KeyFor, ProviderIdFor, RandomnessOutputFor}; +use pallet_proofs_dealer_runtime_api::*; +use pallet_storage_providers::types::{ + BackupStorageProvider, BackupStorageProviderId, BucketId, MainStorageProviderId, ProviderId, + StorageDataUnit, StorageProviderId, +}; +use pallet_storage_providers_runtime_api::*; +use shp_file_metadata::ChunkId; +use shp_traits::TrieRemoveMutation; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256}; +use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, ExtrinsicInclusionMode, +}; +use sp_std::prelude::Vec; +use sp_version::RuntimeVersion; + +// Local module imports +use super::{ + AccountId, Balance, Block, Executive, InherentDataExt, Nonce, ParachainSystem, Runtime, + RuntimeCall, RuntimeGenesisConfig, SessionKeys, System, TransactionPayment, VERSION, +}; + +impl_runtime_apis! { + /// Allows the collator client to query its runtime to determine whether it should author a block + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + storagehub::configs::ConsensusHook::can_build_upon(included_hash, slot) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(storagehub::SLOT_DURATION) + } + + fn authorities() -> Vec { + Authorities::::get().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { + 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() -> sp_std::vec::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, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Nonce { + System::account_nonce(account) + } + } + + 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 pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_call_fee_details(call, 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 cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + #[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, configs::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() + } + } + + #[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 frame_system_benchmarking::Pallet as SystemBench; + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + + 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::{BenchmarkError, Benchmarking, BenchmarkBatch}; + + use frame_system_benchmarking::Pallet as SystemBench; + 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 cumulus_pallet_session_benchmarking::Pallet as SessionBench; + impl cumulus_pallet_session_benchmarking::Config for Runtime {} + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } + + impl pallet_file_system_runtime_api::FileSystemApi, MainStorageProviderId, H256, BlockNumber, ChunkId> for Runtime { + fn query_earliest_file_volunteer_tick(bsp_id: BackupStorageProviderId, file_key: H256) -> Result { + FileSystem::query_earliest_file_volunteer_tick(bsp_id, file_key) + } + + fn query_bsp_confirm_chunks_to_prove_for_file(bsp_id: BackupStorageProviderId, file_key: H256) -> Result, QueryBspConfirmChunksToProveForFileError> { + FileSystem::query_bsp_confirm_chunks_to_prove_for_file(bsp_id, file_key) + } + + fn query_msp_confirm_chunks_to_prove_for_file(msp_id: MainStorageProviderId, file_key: H256) -> Result, QueryMspConfirmChunksToProveForFileError> { + FileSystem::query_msp_confirm_chunks_to_prove_for_file(msp_id, file_key) + } + } + + impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { + fn get_users_with_debt_over_threshold(provider_id: &ProviderIdFor, threshold: Balance) -> Result, GetUsersWithDebtOverThresholdError> { + PaymentStreams::get_users_with_debt_over_threshold(provider_id, threshold) + } + fn get_users_of_payment_streams_of_provider(provider_id: &ProviderIdFor) -> Vec { + PaymentStreams::get_users_of_payment_streams_of_provider(provider_id) + } + } + + impl pallet_proofs_dealer_runtime_api::ProofsDealerApi, BlockNumber, KeyFor, RandomnessOutputFor, TrieRemoveMutation> for Runtime { + fn get_last_tick_provider_submitted_proof(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_last_tick_provider_submitted_proof(provider_id) + } + + fn get_last_checkpoint_challenge_tick() -> BlockNumber { + ProofsDealer::get_last_checkpoint_challenge_tick() + } + + fn get_checkpoint_challenges( + tick: BlockNumber + ) -> Result, Option)>, GetCheckpointChallengesError> { + ProofsDealer::get_checkpoint_challenges(tick) + } + + fn get_challenge_seed(tick: BlockNumber) -> Result, GetChallengeSeedError> { + ProofsDealer::get_challenge_seed(tick) + } + + fn get_challenge_period(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_challenge_period(provider_id) + } + + fn get_checkpoint_challenge_period() -> BlockNumber { + ProofsDealer::get_checkpoint_challenge_period() + } + + fn get_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor, count: u32) -> Vec> { + ProofsDealer::get_challenges_from_seed(seed, provider_id, count) + } + + fn get_forest_challenges_from_seed(seed: &RandomnessOutputFor, provider_id: &ProviderIdFor) -> Vec> { + ProofsDealer::get_forest_challenges_from_seed(seed, provider_id) + } + + fn get_current_tick() -> BlockNumber { + ProofsDealer::get_current_tick() + } + + fn get_next_deadline_tick(provider_id: &ProviderIdFor) -> Result { + ProofsDealer::get_next_deadline_tick(provider_id) + } + } + + impl pallet_storage_providers_runtime_api::StorageProvidersApi, BackupStorageProvider, AccountId, ProviderId, StorageProviderId, StorageDataUnit, Balance, BucketId> for Runtime { + fn get_bsp_info(bsp_id: &BackupStorageProviderId) -> Result, GetBspInfoError> { + Providers::get_bsp_info(bsp_id) + } + + fn get_storage_provider_id(who: &AccountId) -> Option> { + Providers::get_storage_provider_id(who) + } + + fn query_msp_id_of_bucket_id(bucket_id: &BucketId) -> Result, QueryMspIdOfBucketIdError> { + Providers::query_msp_id_of_bucket_id(bucket_id) + } + + fn query_storage_provider_capacity(provider_id: &ProviderId) -> Result, QueryStorageProviderCapacityError> { + Providers::query_storage_provider_capacity(provider_id) + } + + fn query_available_storage_capacity(provider_id: &ProviderId) -> Result, QueryAvailableStorageCapacityError> { + Providers::query_available_storage_capacity(provider_id) + } + + fn query_earliest_change_capacity_block(provider_id: &BackupStorageProviderId) -> Result { + Providers::query_earliest_change_capacity_block(provider_id) + } + + fn get_worst_case_scenario_slashable_amount(provider_id: ProviderId) -> Option { + Providers::get_worst_case_scenario_slashable_amount(&provider_id).ok() + } + + fn get_slash_amount_per_max_file_size() -> Balance { + Providers::get_slash_amount_per_max_file_size() + } + } +} diff --git a/xcm-simulator/src/storagehub/configs/mod.rs b/xcm-simulator/src/storagehub/configs/mod.rs index f54461c78..39836fcf6 100644 --- a/xcm-simulator/src/storagehub/configs/mod.rs +++ b/xcm-simulator/src/storagehub/configs/mod.rs @@ -1,798 +1,786 @@ -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// 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 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. -// -// For more information, please refer to - -pub mod xcm_config; - -// Substrate and Polkadot dependencies -use crate::mock_message_queue; -use crate::storagehub::configs::xcm_config::XcmConfig; -use crate::storagehub::MessageQueue; -use crate::storagehub::PolkadotXcm; -use core::marker::PhantomData; -use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; -use frame_support::{ - derive_impl, - dispatch::DispatchClass, - parameter_types, - traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, - TransformOrigin, - }, - weights::{ConstantMultiplier, Weight}, - PalletId, -}; -use frame_system::{ - limits::{BlockLength, BlockWeights}, - pallet_prelude::BlockNumberFor, - EnsureRoot, EnsureSigned, -}; -use num_bigint::BigUint; -use pallet_nfts::PalletFeatures; -use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; -use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; -use polkadot_runtime_common::{ - prod_or_fast, xcm_sender::NoPriceForMessageDelivery, BlockHashCount, SlowAdjustingFeeUpdate, -}; -use shp_file_metadata::ChunkId; -use shp_traits::{CommitmentVerifier, MaybeDebug, TrieMutation, TrieProofDeltaApplier}; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{blake2_256, Get, Hasher, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Convert, ConvertBack, Verify}, - AccountId32, DispatchError, Perbill, SaturatedConversion, -}; -use sp_std::collections::btree_set::BTreeSet; -use sp_std::vec; -use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; -use sp_version::RuntimeVersion; -use xcm::latest::prelude::BodyId; -use xcm_simulator::XcmExecutor; - -// Local module imports -use super::{ - weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, - AccountId, Aura, Balance, Balances, Block, BlockNumber, BucketNfts, CollatorSelection, Hash, - Nfts, Nonce, PalletInfo, ParachainSystem, PaymentStreams, ProofsDealer, Providers, Runtime, - RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, - Session, SessionKeys, Signature, System, WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, - BLOCK_PROCESSING_VELOCITY, DAYS, EXISTENTIAL_DEPOSIT, HOURS, MAXIMUM_BLOCK_WEIGHT, MICROUNIT, - MINUTES, NORMAL_DISPATCH_RATIO, RELAY_CHAIN_SLOT_DURATION_MILLIS, SLOT_DURATION, - UNINCLUDED_SEGMENT_CAPACITY, UNIT, VERSION, -}; -use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin}; - -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; -} - -/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from -/// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), -/// but overridden as needed. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for Runtime { - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The index type for storing how many extrinsics an account has signed. - type Nonce = Nonce; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The block type. - type Block = Block; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// Runtime version. - type Version = Version; - /// The data to be stored in an account. - type AccountData = pallet_balances::AccountData; - /// The weight of database operations that the runtime can invoke. - type DbWeight = RocksDbWeight; - /// 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>; -} - -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Aura; - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; - type WeightInfo = (); -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type EventHandler = (CollatorSelection,); -} - -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 WeightInfo = pallet_balances::weights::SubstrateWeight; - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; -} - -parameter_types! { - /// Relay Chain `TransactionByteFee` / 10 - pub const TransactionByteFee: Balance = 10 * MICROUNIT; -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; - type OperationalFeeMultiplier = ConstU8<5>; -} - -impl pallet_sudo::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = (); -} - -impl mock_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -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; -} - -impl cumulus_pallet_parachain_system::Config for Runtime { - type WeightInfo = (); - 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(crate) type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, ->; - -impl parachain_info::Config for Runtime {} - -parameter_types! { - pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - #[cfg(feature = "runtime-benchmarks")] - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< - cumulus_primitives_core::AggregateMessageOrigin, - >; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, - >; - type Size = u32; - // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: - type QueueChangeHandler = NarrowOriginToSibling; - type QueuePausedQuery = NarrowOriginToSibling; - type HeapSize = sp_core::ConstU32<{ 64 * 1024 }>; - type MaxStale = sp_core::ConstU32<8>; - type ServiceWeight = MessageQueueServiceWeight; - type IdleMaxServiceWeight = (); -} - -impl cumulus_pallet_aura_ext::Config for Runtime {} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ChannelInfo = ParachainSystem; - type VersionWrapper = PolkadotXcm; - // Enqueue XCMP messages from siblings for later processing. - type XcmpQueue = TransformOrigin; - type MaxInboundSuspended = sp_core::ConstU32<1_000>; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); - type PriceForSiblingDelivery = NoPriceForMessageDelivery; -} - -parameter_types! { - pub const Period: BlockNumber = 6 * HOURS; - pub const Offset: BlockNumber = 0; -} - -impl pallet_session::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ValidatorId = ::AccountId; - // we don't have stash and controller, thus we don't need the convert as well. - type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ShouldEndSession = pallet_session::PeriodicSessions; - type NextSessionRotation = pallet_session::PeriodicSessions; - type SessionManager = CollatorSelection; - // Essentially just Aura, but let's be pedantic. - type SessionHandler = ::KeyTypeIdProviders; - type Keys = SessionKeys; - type WeightInfo = (); -} - -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); - type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = ConstU64; -} - -parameter_types! { - pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const SessionLength: BlockNumber = 6 * HOURS; - // StakingAdmin pluralistic body. - pub const StakingAdminBodyId: BodyId = BodyId::Defense; -} - -/// We allow root and the StakingAdmin to execute privileged collator selection operations. -pub type CollatorSelectionUpdateOrigin = EitherOfDiverse< - EnsureRoot, - EnsureXcm>, ->; - -impl pallet_collator_selection::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type UpdateOrigin = CollatorSelectionUpdateOrigin; - type PotId = PotId; - type MaxCandidates = ConstU32<100>; - type MinEligibleCollators = ConstU32<4>; - type MaxInvulnerables = ConstU32<20>; - // should be a multiple of session or things will get inconsistent - type KickThreshold = Period; - type ValidatorId = ::AccountId; - type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ValidatorRegistration = Session; - type WeightInfo = (); -} - -parameter_types! { - pub Features: PalletFeatures = PalletFeatures::all_enabled(); - pub const MaxAttributesPerCall: u32 = 10; - pub const CollectionDeposit: Balance = 100 * UNIT; - pub const ItemDeposit: Balance = 1 * UNIT; - pub const ApprovalsLimit: u32 = 20; - pub const ItemAttributesApprovalsLimit: u32 = 20; - pub const MaxTips: u32 = 10; - pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS; - pub const MetadataDepositBase: Balance = 10 * UNIT; - pub const MetadataDepositPerByte: Balance = 1 * UNIT; -} - -impl pallet_nfts::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u32; - type ItemId = u32; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type CollectionDeposit = CollectionDeposit; - type ItemDeposit = ItemDeposit; - type MetadataDepositBase = MetadataDepositBase; - type AttributeDepositBase = MetadataDepositBase; - type DepositPerByte = MetadataDepositPerByte; - type StringLimit = ConstU32<256>; - type KeyLimit = ConstU32<64>; - type ValueLimit = ConstU32<256>; - type ApprovalsLimit = ApprovalsLimit; - type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; - type MaxTips = MaxTips; - type MaxDeadlineDuration = MaxDeadlineDuration; - type MaxAttributesPerCall = MaxAttributesPerCall; - type Features = Features; - type OffchainSignature = Signature; - type OffchainPublic = ::Signer; - type WeightInfo = pallet_nfts::weights::SubstrateWeight; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type Locker = (); -} - -/// Only callable after `set_validation_data` is called which forms this proof the same way -/// CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 -/* fn relay_chain_state_proof() -> RelayChainStateProof { - // CRITICAL TODO: Change this to the actual relay storage root after upgrading to polkadot-sdk v1.13.0 - let relay_storage_root = DefaultMerkleRoot::::get(); - /* let relay_storage_root = cumulus_pallet_parachain_system::ValidationData::::get() - .expect("set in `set_validation_data`") - .relay_parent_storage_root; */ - let root_vec: vec::Vec> = vec![relay_storage_root.as_bytes().to_vec()]; - let relay_chain_state = StorageProof::new(root_vec); - /* let relay_chain_state = cumulus_pallet_parachain_system::RelayStateProof::::get() - .expect("set in `set_validation_data`"); */ - RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) - .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") -} */ - -pub struct BabeDataGetter; -impl pallet_randomness::GetBabeData for BabeDataGetter { - // Tolerate panic here because this is only ever called in an inherent (so can be omitted) - fn get_epoch_index() -> u64 { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - const BENCHMARKING_NEW_EPOCH: u64 = 10u64; - return BENCHMARKING_NEW_EPOCH; - } - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove frame_system::Pallet::::block_number() - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::EPOCH_INDEX) - .ok() - .flatten() - .expect("expected to be able to read epoch index from relay chain state proof") */ - frame_system::Pallet::::block_number().into() - } - fn get_epoch_randomness() -> Hash { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - let benchmarking_babe_output = Hash::default(); - return benchmarking_babe_output; - } - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove H256::from_slice(&blake2_256(&Self::get_epoch_index().to_le_bytes())) - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) - .ok() - .flatten() - .expect("expected to be able to read epoch randomness from relay chain state proof") */ - H256::from_slice(&blake2_256(&Self::get_epoch_index().to_le_bytes())) - } - fn get_parent_randomness() -> Hash { - if cfg!(feature = "runtime-benchmarks") { - // storage reads as per actual reads - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 - /* let _relay_storage_root = - cumulus_pallet_parachain_system::ValidationData::::get(); - let _relay_chain_state = - cumulus_pallet_parachain_system::RelayStateProof::::get(); */ - let benchmarking_babe_output = Hash::default(); - return benchmarking_babe_output; - } - // Note: we use the `CURRENT_BLOCK_RANDOMNESS` key here as it also represents the parent randomness, the only difference - // is the block since this randomness is valid, but we don't care about that because we are setting that directly in the `randomness` pallet. - /* relay_chain_state_proof() - .read_optional_entry(well_known_keys::CURRENT_BLOCK_RANDOMNESS) - .ok() - .flatten() - .expect("expected to be able to read parent randomness from relay chain state proof") */ - // CRITICAL TODO: Uncomment this after upgrading to polkadot-sdk v1.13.0 and remove H256::from_slice(&blake2_256(&Self::get_epoch_index().saturating_sub(1).to_le_bytes())) - H256::from_slice(&blake2_256( - &Self::get_epoch_index().saturating_sub(1).to_le_bytes(), - )) - } -} - -parameter_types! { - pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * HOURS, 2 * MINUTES); -} - -// TODO: If the next line is uncommented (which should be eventually), compilation breaks (most likely because of mismatched dependency issues) -/* parameter_types! { - pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * runtime_constants::time::EPOCH_DURATION_IN_SLOTS, 2 * MINUTES); -} */ - -/// Configure the randomness pallet -impl pallet_randomness::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type BabeDataGetter = BabeDataGetter; - type WeightInfo = (); -} - -parameter_types! { - pub const BucketDeposit: Balance = 20 * UNIT; - pub const MaxMultiAddressSize: u32 = 100; - pub const MaxMultiAddressAmount: u32 = 5; - pub const MaxProtocols: u32 = 100; - pub const MaxBsps: u32 = 100; - pub const MaxMsps: u32 = 100; - pub const MaxBuckets: u32 = 10000; - pub const BucketNameLimit: u32 = 100; - pub const SpMinDeposit: Balance = 20 * UNIT; - pub const SpMinCapacity: u64 = 2; - pub const DepositPerData: Balance = 2; - pub const MinBlocksBetweenCapacityChanges: u32 = 10; - pub const SlashAmountPerChunkOfStorageData: Balance = 20 * UNIT; -} - -pub type HasherOutT = <::Hash as Hasher>::Out; -pub struct DefaultMerkleRoot(PhantomData); -impl Get> for DefaultMerkleRoot { - fn get() -> HasherOutT { - sp_trie::empty_trie_root::() - } -} -impl pallet_storage_providers::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ProvidersRandomness = pallet_randomness::RandomnessFromOneEpochAgo; - type PaymentStreams = PaymentStreams; - type FileMetadataManager = shp_file_metadata::FileMetadata< - { shp_constants::H_LENGTH }, - { shp_constants::FILE_CHUNK_SIZE }, - { shp_constants::FILE_SIZE_TO_CHALLENGES }, - >; - type NativeBalance = Balances; - type RuntimeHoldReason = RuntimeHoldReason; - type StorageDataUnit = u64; - type SpCount = u32; - type MerklePatriciaRoot = Hash; - type ValuePropId = Hash; - type ReadAccessGroupId = ::CollectionId; - type ProvidersProofSubmitters = ProofsDealer; - type ReputationWeightType = u32; - type Treasury = TreasuryAccount; - type SpMinDeposit = SpMinDeposit; - type SpMinCapacity = SpMinCapacity; - type DepositPerData = DepositPerData; - type MaxFileSize = ConstU64<{ u64::MAX }>; - type MaxMultiAddressSize = MaxMultiAddressSize; - type MaxMultiAddressAmount = MaxMultiAddressAmount; - type MaxProtocols = MaxProtocols; - type MaxBuckets = MaxBuckets; - type BucketDeposit = BucketDeposit; - type BucketNameLimit = BucketNameLimit; - type MaxBlocksForRandomness = MaxBlocksForRandomness; - type MinBlocksBetweenCapacityChanges = MinBlocksBetweenCapacityChanges; - type DefaultMerkleRoot = DefaultMerkleRoot>; - type SlashAmountPerMaxFileSize = SlashAmountPerChunkOfStorageData; - type StartingReputationWeight = ConstU32<1>; -} - -parameter_types! { - pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); - pub const UserWithoutFundsCooldown: BlockNumber = 100; -} - -// Converter from the BlockNumber type to the Balance type for math -pub struct BlockNumberToBalance; - -impl Convert for BlockNumberToBalance { - fn convert(block_number: BlockNumber) -> Balance { - block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type - } -} - -impl pallet_payment_streams::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; - type ProvidersPallet = Providers; - type RuntimeHoldReason = RuntimeHoldReason; - type UserWithoutFundsCooldown = UserWithoutFundsCooldown; // Amount of blocks that a user will have to wait before being able to clear the out of funds flag - type NewStreamDeposit = ConstU32<10>; // Amount of blocks that the deposit of a new stream should be able to pay for - type Units = u64; // Storage unit - type BlockNumberToBalance = BlockNumberToBalance; - type ProvidersProofSubmitters = ProofsDealer; -} - -// TODO: remove this and replace with pallet treasury -pub struct TreasuryAccount; -impl Get for TreasuryAccount { - fn get() -> AccountId32 { - AccountId32::from([0; 32]) - } -} - -pub struct BlockFullnessHeadroom; -impl Get for BlockFullnessHeadroom { - fn get() -> Weight { - // TODO: Change this to the benchmarked weight of a `submit_proof` extrinsic or more. - Weight::from_parts(10_000, 0) - + ::DbWeight::get().reads_writes(0, 1) - } -} - -pub struct MinNotFullBlocksRatio; -impl Get for MinNotFullBlocksRatio { - fn get() -> Perbill { - // This means that we tolerate at most 50% of misbehaving collators. - Perbill::from_percent(50) - } -} - -parameter_types! { - pub const RandomChallengesPerBlock: u32 = 10; - pub const MaxCustomChallengesPerBlock: u32 = 10; - pub const ChallengeHistoryLength: BlockNumber = 100; - pub const ChallengesQueueLength: u32 = 100; - pub const CheckpointChallengePeriod: u32 = 30; - pub const ChallengesFee: Balance = 1 * UNIT; - pub const StakeToChallengePeriod: Balance = 200 * UNIT; - pub const MinChallengePeriod: u32 = 30; - pub const ChallengeTicksTolerance: u32 = 50; - pub const MaxSubmittersPerTick: u32 = 1000; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight - pub const TargetTicksStorageOfSubmitters: u32 = 3; -} - -impl pallet_proofs_dealer::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ProvidersPallet = Providers; - type NativeBalance = Balances; - type MerkleTrieHash = Hash; - type MerkleTrieHashing = BlakeTwo256; - type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; - type StakeToBlockNumber = SaturatingBalanceToBlockNumber; - type RandomChallengesPerBlock = RandomChallengesPerBlock; - type MaxCustomChallengesPerBlock = MaxCustomChallengesPerBlock; - type MaxSubmittersPerTick = MaxSubmittersPerTick; - type TargetTicksStorageOfSubmitters = TargetTicksStorageOfSubmitters; - type ChallengeHistoryLength = ChallengeHistoryLength; - type ChallengesQueueLength = ChallengesQueueLength; - type CheckpointChallengePeriod = CheckpointChallengePeriod; - type ChallengesFee = ChallengesFee; - type Treasury = TreasuryAccount; - type RandomnessProvider = pallet_randomness::ParentBlockRandomness; - type StakeToChallengePeriod = StakeToChallengePeriod; - type MinChallengePeriod = MinChallengePeriod; - type ChallengeTicksTolerance = ChallengeTicksTolerance; - type BlockFullnessPeriod = ChallengeTicksTolerance; // We purposely set this to `ChallengeTicksTolerance` so that spamming of the chain is evaluated for the same blocks as the tolerance BSPs are given. - type BlockFullnessHeadroom = BlockFullnessHeadroom; - type MinNotFullBlocksRatio = MinNotFullBlocksRatio; -} - -/// Structure to mock a verifier that returns `true` when `proof` is not empty -/// and `false` otherwise. -pub struct MockVerifier { - _phantom: core::marker::PhantomData<(C, T)>, -} - -/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. -impl CommitmentVerifier for MockVerifier -where - C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, -{ - type Proof = CompactProof; - type Commitment = H256; - type Challenge = H256; - - fn verify_proof( - _root: &Self::Commitment, - _challenges: &[Self::Challenge], - proof: &CompactProof, - ) -> Result, DispatchError> { - if proof.encoded_nodes.len() > 0 { - Ok(proof - .encoded_nodes - .iter() - .map(|node| H256::from_slice(&node[..])) - .collect()) - } else { - Err("Proof is empty".into()) - } - } -} - -impl TrieProofDeltaApplier - for MockVerifier -where - ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, -{ - type Proof = CompactProof; - type Key = ::Out; - - fn apply_delta( - root: &Self::Key, - _mutations: &[(Self::Key, TrieMutation)], - _proof: &Self::Proof, - ) -> Result< - ( - MemoryDB, - Self::Key, - Vec<(Self::Key, Option>)>, - ), - DispatchError, - > { - // Just return the root as is with no mutations - Ok((MemoryDB::::default(), *root, Vec::new())) - } -} - -type ThresholdType = u32; - -parameter_types! { - pub const MaxBatchConfirmStorageRequests: u32 = 10; - pub const MinWaitForStopStoring: BlockNumber = 10; -} - -/// Configure the pallet template in pallets/template. -impl pallet_file_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Providers = Providers; - type ProofDealer = ProofsDealer; - type PaymentStreams = PaymentStreams; - type UserSolvency = PaymentStreams; - type Fingerprint = Hash; - type ReplicationTargetType = u32; - type ThresholdType = ThresholdType; - type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; - type HashToThresholdType = HashToThresholdTypeConverter; - type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; - type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; - type Currency = Balances; - type Nfts = Nfts; - type CollectionInspector = BucketNfts; - type MaxBspsPerStorageRequest = ConstU32<5>; - type MaxBatchConfirmStorageRequests = MaxBatchConfirmStorageRequests; - type MaxBatchMspRespondStorageRequests = ConstU32<10>; - type MaxFilePathSize = ConstU32<512u32>; - type MaxPeerIdSize = ConstU32<100>; - type MaxNumberOfPeerIds = ConstU32<5>; - type MaxDataServerMultiAddresses = ConstU32<10>; - type MaxExpiredItemsInBlock = ConstU32<100>; - type StorageRequestTtl = ConstU32<40>; - type PendingFileDeletionRequestTtl = ConstU32<40u32>; - type MoveBucketRequestTtl = ConstU32<40u32>; - type MaxUserPendingDeletionRequests = ConstU32<10u32>; - type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; - type MinWaitForStopStoring = MinWaitForStopStoring; -} - -// Converter from the Balance type to the BlockNumber type for math. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct SaturatingBalanceToBlockNumber; - -impl Convert> for SaturatingBalanceToBlockNumber { - fn convert(block_number: Balance) -> BlockNumberFor { - block_number.saturated_into() - } -} - -// Converter from the ThresholdType to the BlockNumber type and vice versa. -// It performs a saturated conversion, so that the result is always a valid BlockNumber. -pub struct ThresholdTypeToBlockNumberConverter; - -impl Convert> for ThresholdTypeToBlockNumberConverter { - fn convert(threshold: ThresholdType) -> BlockNumberFor { - threshold.saturated_into() - } -} - -impl ConvertBack> for ThresholdTypeToBlockNumberConverter { - fn convert_back(block_number: BlockNumberFor) -> ThresholdType { - block_number.into() - } -} - -/// Converter from the [`Hash`] type to the [`ThresholdType`]. -pub struct HashToThresholdTypeConverter; -impl Convert<::Hash, ThresholdType> - for HashToThresholdTypeConverter -{ - fn convert(hash: ::Hash) -> ThresholdType { - // Get the hash as bytes - let hash_bytes = hash.as_ref(); - - // Get the 4 least significant bytes of the hash and interpret them as an u32 - let truncated_hash_bytes: [u8; 4] = - hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); - - ThresholdType::from_be_bytes(truncated_hash_bytes) - } -} - -// Converter from the MerkleHash (H256) type to the RandomnessOutput (H256) type. -pub struct MerkleHashToRandomnessOutputConverter; - -impl Convert for MerkleHashToRandomnessOutputConverter { - fn convert(hash: H256) -> H256 { - hash - } -} - -// Converter from the ChunkId type to the MerkleHash (H256) type. -pub struct ChunkIdToMerkleHashConverter; - -impl Convert for ChunkIdToMerkleHashConverter { - fn convert(chunk_id: ChunkId) -> H256 { - let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); - let mut bytes = chunk_id_biguint.to_bytes_be(); - - // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros - if bytes.len() < 32 { - let mut padded_bytes = vec![0u8; 32 - bytes.len()]; - padded_bytes.extend(bytes); - bytes = padded_bytes; - } - - H256::from_slice(&bytes) - } -} - -impl pallet_bucket_nfts::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Buckets = Providers; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); -} +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// 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 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. +// +// For more information, please refer to + +pub mod xcm_config; + +// Substrate and Polkadot dependencies +use crate::mock_message_queue; +use crate::storagehub::{configs::xcm_config::XcmConfig, MessageQueue, ParachainInfo, PolkadotXcm}; +use core::marker::PhantomData; +use cumulus_pallet_parachain_system::RelayChainStateProof; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +use cumulus_primitives_core::relay_chain::well_known_keys; +use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use frame_support::{ + derive_impl, + dispatch::DispatchClass, + parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, + TransformOrigin, + }, + weights::{ConstantMultiplier, Weight}, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + pallet_prelude::BlockNumberFor, + EnsureRoot, EnsureSigned, +}; +use num_bigint::BigUint; +use pallet_nfts::PalletFeatures; +use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; +use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; +use polkadot_runtime_common::{ + prod_or_fast, xcm_sender::NoPriceForMessageDelivery, BlockHashCount, SlowAdjustingFeeUpdate, +}; +use shp_file_metadata::ChunkId; +use shp_traits::{CommitmentVerifier, MaybeDebug, TrieMutation, TrieProofDeltaApplier}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{Get, Hasher, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, Convert, ConvertBack, Verify}, + AccountId32, DispatchError, Perbill, SaturatedConversion, +}; +use sp_std::collections::btree_set::BTreeSet; +use sp_std::vec; +use sp_trie::{CompactProof, LayoutV1, MemoryDB, TrieConfiguration, TrieLayout}; +use sp_version::RuntimeVersion; +use xcm::latest::prelude::BodyId; +use xcm_simulator::XcmExecutor; + +// Local module imports +use super::{ + weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, + AccountId, Aura, Balance, Balances, Block, BlockNumber, BucketNfts, CollatorSelection, Hash, + Nfts, Nonce, PalletInfo, ParachainSystem, PaymentStreams, ProofsDealer, Providers, Runtime, + RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, + Session, SessionKeys, Signature, System, WeightToFee, XcmpQueue, AVERAGE_ON_INITIALIZE_RATIO, + BLOCK_PROCESSING_VELOCITY, DAYS, EXISTENTIAL_DEPOSIT, HOURS, MAXIMUM_BLOCK_WEIGHT, MICROUNIT, + MINUTES, NORMAL_DISPATCH_RATIO, RELAY_CHAIN_SLOT_DURATION_MILLIS, SLOT_DURATION, + UNINCLUDED_SEGMENT_CAPACITY, UNIT, VERSION, +}; +use xcm_config::{RelayLocation, XcmOriginToTransactDispatchOrigin}; + +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; +} + +/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from +/// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), +/// but overridden as needed. +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The index type for storing how many extrinsics an account has signed. + type Nonce = Nonce; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The block type. + type Block = Block; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// 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>; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; + type WeightInfo = (); +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type EventHandler = (CollatorSelection,); +} + +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 WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + /// Relay Chain `TransactionByteFee` / 10 + pub const TransactionByteFee: Balance = 10 * MICROUNIT; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type OperationalFeeMultiplier = ConstU8<5>; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); +} + +impl mock_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +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; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = (); + 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(crate) type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + type QueuePausedQuery = NarrowOriginToSibling; + type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + // Enqueue XCMP messages from siblings for later processing. + type XcmpQueue = TransformOrigin; + type MaxInboundSuspended = ConstU32<1_000>; + type MaxActiveOutboundChannels = ConstU32<128>; + // Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we + // need to set the page size larger than that until we reduce the channel size on-chain. + type MaxPageSize = ConstU32<{ 103 * 1024 }>; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; +} + +parameter_types! { + pub const Period: BlockNumber = 6 * HOURS; + pub const Offset: BlockNumber = 0; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + // we don't have stash and controller, thus we don't need the convert as well. + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = CollatorSelection; + // Essentially just Aura, but let's be pedantic. + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = ConstU64; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); + pub const SessionLength: BlockNumber = 6 * HOURS; + // StakingAdmin pluralistic body. + pub const StakingAdminBodyId: BodyId = BodyId::Defense; +} + +/// We allow root and the StakingAdmin to execute privileged collator selection operations. +pub type CollatorSelectionUpdateOrigin = EitherOfDiverse< + EnsureRoot, + EnsureXcm>, +>; + +impl pallet_collator_selection::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type UpdateOrigin = CollatorSelectionUpdateOrigin; + type PotId = PotId; + type MaxCandidates = ConstU32<100>; + type MinEligibleCollators = ConstU32<4>; + type MaxInvulnerables = ConstU32<20>; + // should be a multiple of session or things will get inconsistent + type KickThreshold = Period; + type ValidatorId = ::AccountId; + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +parameter_types! { + pub Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; + pub const CollectionDeposit: Balance = 100 * UNIT; + pub const ItemDeposit: Balance = 1 * UNIT; + pub const ApprovalsLimit: u32 = 20; + pub const ItemAttributesApprovalsLimit: u32 = 20; + pub const MaxTips: u32 = 10; + pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS; + pub const MetadataDepositBase: Balance = 10 * UNIT; + pub const MetadataDepositPerByte: Balance = 1 * UNIT; +} + +impl pallet_nfts::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u32; + type ItemId = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type CollectionDeposit = CollectionDeposit; + type ItemDeposit = ItemDeposit; + type MetadataDepositBase = MetadataDepositBase; + type AttributeDepositBase = MetadataDepositBase; + type DepositPerByte = MetadataDepositPerByte; + type StringLimit = ConstU32<256>; + type KeyLimit = ConstU32<64>; + type ValueLimit = ConstU32<256>; + type ApprovalsLimit = ApprovalsLimit; + type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; + type MaxTips = MaxTips; + type MaxDeadlineDuration = MaxDeadlineDuration; + type MaxAttributesPerCall = MaxAttributesPerCall; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = ::Signer; + type WeightInfo = pallet_nfts::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type Locker = (); +} + +/// Only callable after `set_validation_data` is called which forms this proof the same way +fn relay_chain_state_proof() -> RelayChainStateProof { + let relay_storage_root = cumulus_pallet_parachain_system::ValidationData::::get() + .expect("set in `set_validation_data`") + .relay_parent_storage_root; + let relay_chain_state = cumulus_pallet_parachain_system::RelayStateProof::::get() + .expect("set in `set_validation_data`"); + RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state) + .expect("Invalid relay chain state proof, already constructed in `set_validation_data`") +} + +pub struct BabeDataGetter; +impl pallet_randomness::GetBabeData for BabeDataGetter { + // Tolerate panic here because this is only ever called in an inherent (so can be omitted) + fn get_epoch_index() -> u64 { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + const BENCHMARKING_NEW_EPOCH: u64 = 10u64; + return BENCHMARKING_NEW_EPOCH; + } + relay_chain_state_proof() + .read_optional_entry(well_known_keys::EPOCH_INDEX) + .ok() + .flatten() + .expect("expected to be able to read epoch index from relay chain state proof") + } + fn get_epoch_randomness() -> Hash { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + let benchmarking_babe_output = Hash::default(); + return benchmarking_babe_output; + } + relay_chain_state_proof() + .read_optional_entry(well_known_keys::ONE_EPOCH_AGO_RANDOMNESS) + .ok() + .flatten() + .expect("expected to be able to read epoch randomness from relay chain state proof") + } + fn get_parent_randomness() -> Hash { + if cfg!(feature = "runtime-benchmarks") { + // storage reads as per actual reads + let _relay_storage_root = + cumulus_pallet_parachain_system::ValidationData::::get(); + let _relay_chain_state = + cumulus_pallet_parachain_system::RelayStateProof::::get(); + let benchmarking_babe_output = Hash::default(); + return benchmarking_babe_output; + } + // Note: we use the `CURRENT_BLOCK_RANDOMNESS` key here as it also represents the parent randomness, the only difference + // is the block since this randomness is valid, but we don't care about that because we are setting that directly in the `randomness` pallet. + relay_chain_state_proof() + .read_optional_entry(well_known_keys::CURRENT_BLOCK_RANDOMNESS) + .ok() + .flatten() + .expect("expected to be able to read parent randomness from relay chain state proof") + } +} + +parameter_types! { + pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * HOURS, 2 * MINUTES); +} + +// TODO: If the next line is uncommented (which should be eventually), compilation breaks (most likely because of mismatched dependency issues) +/* parameter_types! { + pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * runtime_constants::time::EPOCH_DURATION_IN_SLOTS, 2 * MINUTES); +} */ + +/// Configure the randomness pallet +impl pallet_randomness::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BabeDataGetter = BabeDataGetter; + type WeightInfo = (); +} + +parameter_types! { + pub const BucketDeposit: Balance = 20 * UNIT; + pub const MaxMultiAddressSize: u32 = 100; + pub const MaxMultiAddressAmount: u32 = 5; + pub const MaxProtocols: u32 = 100; + pub const MaxBsps: u32 = 100; + pub const MaxMsps: u32 = 100; + pub const MaxBuckets: u32 = 10000; + pub const BucketNameLimit: u32 = 100; + pub const SpMinDeposit: Balance = 20 * UNIT; + pub const SpMinCapacity: u64 = 2; + pub const DepositPerData: Balance = 2; + pub const MinBlocksBetweenCapacityChanges: u32 = 10; + pub const SlashAmountPerChunkOfStorageData: Balance = 20 * UNIT; +} + +pub type HasherOutT = <::Hash as Hasher>::Out; +pub struct DefaultMerkleRoot(PhantomData); +impl Get> for DefaultMerkleRoot { + fn get() -> HasherOutT { + sp_trie::empty_trie_root::() + } +} +impl pallet_storage_providers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProvidersRandomness = pallet_randomness::RandomnessFromOneEpochAgo; + type PaymentStreams = PaymentStreams; + type FileMetadataManager = shp_file_metadata::FileMetadata< + { shp_constants::H_LENGTH }, + { shp_constants::FILE_CHUNK_SIZE }, + { shp_constants::FILE_SIZE_TO_CHALLENGES }, + >; + type NativeBalance = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type StorageDataUnit = u64; + type SpCount = u32; + type MerklePatriciaRoot = Hash; + type ValuePropId = Hash; + type ReadAccessGroupId = ::CollectionId; + type ProvidersProofSubmitters = ProofsDealer; + type ReputationWeightType = u32; + type Treasury = TreasuryAccount; + type SpMinDeposit = SpMinDeposit; + type SpMinCapacity = SpMinCapacity; + type DepositPerData = DepositPerData; + type MaxFileSize = ConstU64<{ u64::MAX }>; + type MaxMultiAddressSize = MaxMultiAddressSize; + type MaxMultiAddressAmount = MaxMultiAddressAmount; + type MaxProtocols = MaxProtocols; + type MaxBuckets = MaxBuckets; + type BucketDeposit = BucketDeposit; + type BucketNameLimit = BucketNameLimit; + type MaxBlocksForRandomness = MaxBlocksForRandomness; + type MinBlocksBetweenCapacityChanges = MinBlocksBetweenCapacityChanges; + type DefaultMerkleRoot = DefaultMerkleRoot>; + type SlashAmountPerMaxFileSize = SlashAmountPerChunkOfStorageData; + type StartingReputationWeight = ConstU32<1>; +} + +parameter_types! { + pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit); + pub const UserWithoutFundsCooldown: BlockNumber = 100; +} + +// Converter from the BlockNumber type to the Balance type for math +pub struct BlockNumberToBalance; + +impl Convert for BlockNumberToBalance { + fn convert(block_number: BlockNumber) -> Balance { + block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type + } +} + +impl pallet_payment_streams::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type ProvidersPallet = Providers; + type RuntimeHoldReason = RuntimeHoldReason; + type UserWithoutFundsCooldown = UserWithoutFundsCooldown; // Amount of blocks that a user will have to wait before being able to clear the out of funds flag + type NewStreamDeposit = ConstU32<10>; // Amount of blocks that the deposit of a new stream should be able to pay for + type Units = u64; // Storage unit + type BlockNumberToBalance = BlockNumberToBalance; + type ProvidersProofSubmitters = ProofsDealer; +} + +// TODO: remove this and replace with pallet treasury +pub struct TreasuryAccount; +impl Get for TreasuryAccount { + fn get() -> AccountId32 { + AccountId32::from([0; 32]) + } +} + +pub struct BlockFullnessHeadroom; +impl Get for BlockFullnessHeadroom { + fn get() -> Weight { + // TODO: Change this to the benchmarked weight of a `submit_proof` extrinsic or more. + Weight::from_parts(10_000, 0) + + ::DbWeight::get().reads_writes(0, 1) + } +} + +pub struct MinNotFullBlocksRatio; +impl Get for MinNotFullBlocksRatio { + fn get() -> Perbill { + // This means that we tolerate at most 50% of misbehaving collators. + Perbill::from_percent(50) + } +} + +parameter_types! { + pub const RandomChallengesPerBlock: u32 = 10; + pub const MaxCustomChallengesPerBlock: u32 = 10; + pub const ChallengeHistoryLength: BlockNumber = 100; + pub const ChallengesQueueLength: u32 = 100; + pub const CheckpointChallengePeriod: u32 = 30; + pub const ChallengesFee: Balance = 1 * UNIT; + pub const StakeToChallengePeriod: Balance = 200 * UNIT; + pub const MinChallengePeriod: u32 = 30; + pub const ChallengeTicksTolerance: u32 = 50; + pub const MaxSubmittersPerTick: u32 = 1000; // TODO: Change this value after benchmarking for it to coincide with the implicit limit given by maximum block weight + pub const TargetTicksStorageOfSubmitters: u32 = 3; +} + +impl pallet_proofs_dealer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProvidersPallet = Providers; + type NativeBalance = Balances; + type MerkleTrieHash = Hash; + type MerkleTrieHashing = BlakeTwo256; + type ForestVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type KeyVerifier = MockVerifier, { BlakeTwo256::LENGTH }>; + type StakeToBlockNumber = SaturatingBalanceToBlockNumber; + type RandomChallengesPerBlock = RandomChallengesPerBlock; + type MaxCustomChallengesPerBlock = MaxCustomChallengesPerBlock; + type MaxSubmittersPerTick = MaxSubmittersPerTick; + type TargetTicksStorageOfSubmitters = TargetTicksStorageOfSubmitters; + type ChallengeHistoryLength = ChallengeHistoryLength; + type ChallengesQueueLength = ChallengesQueueLength; + type CheckpointChallengePeriod = CheckpointChallengePeriod; + type ChallengesFee = ChallengesFee; + type Treasury = TreasuryAccount; + type RandomnessProvider = pallet_randomness::ParentBlockRandomness; + type StakeToChallengePeriod = StakeToChallengePeriod; + type MinChallengePeriod = MinChallengePeriod; + type ChallengeTicksTolerance = ChallengeTicksTolerance; + type BlockFullnessPeriod = ChallengeTicksTolerance; // We purposely set this to `ChallengeTicksTolerance` so that spamming of the chain is evaluated for the same blocks as the tolerance BSPs are given. + type BlockFullnessHeadroom = BlockFullnessHeadroom; + type MinNotFullBlocksRatio = MinNotFullBlocksRatio; +} + +/// Structure to mock a verifier that returns `true` when `proof` is not empty +/// and `false` otherwise. +pub struct MockVerifier { + _phantom: core::marker::PhantomData<(C, T)>, +} + +/// Implement the `TrieVerifier` trait for the `MockForestManager` struct. +impl CommitmentVerifier for MockVerifier +where + C: MaybeDebug + Ord + Default + Copy + AsRef<[u8]> + AsMut<[u8]>, +{ + type Proof = CompactProof; + type Commitment = H256; + type Challenge = H256; + + fn verify_proof( + _root: &Self::Commitment, + _challenges: &[Self::Challenge], + proof: &CompactProof, + ) -> Result, DispatchError> { + if proof.encoded_nodes.len() > 0 { + Ok(proof + .encoded_nodes + .iter() + .map(|node| H256::from_slice(&node[..])) + .collect()) + } else { + Err("Proof is empty".into()) + } + } +} + +impl TrieProofDeltaApplier + for MockVerifier +where + ::Out: for<'a> TryFrom<&'a [u8; H_LENGTH]>, +{ + type Proof = CompactProof; + type Key = ::Out; + + fn apply_delta( + root: &Self::Key, + _mutations: &[(Self::Key, TrieMutation)], + _proof: &Self::Proof, + ) -> Result< + ( + MemoryDB, + Self::Key, + Vec<(Self::Key, Option>)>, + ), + DispatchError, + > { + // Just return the root as is with no mutations + Ok((MemoryDB::::default(), *root, Vec::new())) + } +} + +type ThresholdType = u32; + +parameter_types! { + pub const MaxBatchConfirmStorageRequests: u32 = 10; + pub const MinWaitForStopStoring: BlockNumber = 10; +} + +/// Configure the pallet template in pallets/template. +impl pallet_file_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Providers = Providers; + type ProofDealer = ProofsDealer; + type PaymentStreams = PaymentStreams; + type UserSolvency = PaymentStreams; + type Fingerprint = Hash; + type ReplicationTargetType = u32; + type ThresholdType = ThresholdType; + type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter; + type HashToThresholdType = HashToThresholdTypeConverter; + type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter; + type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter; + type Currency = Balances; + type Nfts = Nfts; + type CollectionInspector = BucketNfts; + type MaxBspsPerStorageRequest = ConstU32<5>; + type MaxBatchConfirmStorageRequests = MaxBatchConfirmStorageRequests; + type MaxBatchMspRespondStorageRequests = ConstU32<10>; + type MaxFilePathSize = ConstU32<512u32>; + type MaxPeerIdSize = ConstU32<100>; + type MaxNumberOfPeerIds = ConstU32<5>; + type MaxDataServerMultiAddresses = ConstU32<10>; + type MaxExpiredItemsInBlock = ConstU32<100>; + type StorageRequestTtl = ConstU32<40>; + type PendingFileDeletionRequestTtl = ConstU32<40u32>; + type MoveBucketRequestTtl = ConstU32<40u32>; + type MaxUserPendingDeletionRequests = ConstU32<10u32>; + type MaxUserPendingMoveBucketRequests = ConstU32<10u32>; + type MinWaitForStopStoring = MinWaitForStopStoring; +} + +// Converter from the Balance type to the BlockNumber type for math. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct SaturatingBalanceToBlockNumber; + +impl Convert> for SaturatingBalanceToBlockNumber { + fn convert(block_number: Balance) -> BlockNumberFor { + block_number.saturated_into() + } +} + +// Converter from the ThresholdType to the BlockNumber type and vice versa. +// It performs a saturated conversion, so that the result is always a valid BlockNumber. +pub struct ThresholdTypeToBlockNumberConverter; + +impl Convert> for ThresholdTypeToBlockNumberConverter { + fn convert(threshold: ThresholdType) -> BlockNumberFor { + threshold.saturated_into() + } +} + +impl ConvertBack> for ThresholdTypeToBlockNumberConverter { + fn convert_back(block_number: BlockNumberFor) -> ThresholdType { + block_number.into() + } +} + +/// Converter from the [`Hash`] type to the [`ThresholdType`]. +pub struct HashToThresholdTypeConverter; +impl Convert<::Hash, ThresholdType> + for HashToThresholdTypeConverter +{ + fn convert(hash: ::Hash) -> ThresholdType { + // Get the hash as bytes + let hash_bytes = hash.as_ref(); + + // Get the 4 least significant bytes of the hash and interpret them as an u32 + let truncated_hash_bytes: [u8; 4] = + hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed"); + + ThresholdType::from_be_bytes(truncated_hash_bytes) + } +} + +// Converter from the MerkleHash (H256) type to the RandomnessOutput (H256) type. +pub struct MerkleHashToRandomnessOutputConverter; + +impl Convert for MerkleHashToRandomnessOutputConverter { + fn convert(hash: H256) -> H256 { + hash + } +} + +// Converter from the ChunkId type to the MerkleHash (H256) type. +pub struct ChunkIdToMerkleHashConverter; + +impl Convert for ChunkIdToMerkleHashConverter { + fn convert(chunk_id: ChunkId) -> H256 { + let chunk_id_biguint = BigUint::from(chunk_id.as_u64()); + let mut bytes = chunk_id_biguint.to_bytes_be(); + + // Ensure the byte slice is exactly 32 bytes long by padding with leading zeros + if bytes.len() < 32 { + let mut padded_bytes = vec![0u8; 32 - bytes.len()]; + padded_bytes.extend(bytes); + bytes = padded_bytes; + } + + H256::from_slice(&bytes) + } +} + +impl pallet_bucket_nfts::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Buckets = Providers; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); +} diff --git a/xcm-simulator/src/storagehub/configs/xcm_config.rs b/xcm-simulator/src/storagehub/configs/xcm_config.rs index 044118748..bf36e49de 100644 --- a/xcm-simulator/src/storagehub/configs/xcm_config.rs +++ b/xcm-simulator/src/storagehub/configs/xcm_config.rs @@ -1,231 +1,232 @@ -use crate::storagehub::MsgQueue; -use crate::storagehub::{ - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, -}; -use frame_support::{ - parameter_types, - traits::{ConstU32, Contains, Everything, Nothing}, - weights::Weight, -}; -use frame_system::EnsureRoot; -use pallet_xcm::XcmPassthrough; -use parachains_common::xcm_config::{ConcreteAssetFromSystem, ParentRelayOrSiblingParachains}; -use polkadot_parachain_primitives::primitives::Sibling; -use polkadot_runtime_common::impls::ToAuthor; -use xcm::latest::prelude::*; -use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, - HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, -}; -use xcm_executor::XcmExecutor; - -parameter_types! { - pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Polkadot); - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub const GovernanceLocation: Location = Location::parent(); -} - -/// Type for specifying how a `Location` 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 parent `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, - // Foreign locations alias into accounts according to a hash of their standard description. - HashedDescription>, -); - -/// Means for transacting assets on this chain. -pub type LocalAssetTransactor = ( - FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Do a simple punn to convert an AccountId32 Location into a native chain 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. - (), - >, - // TODO: Check safety of using a NonFungiblesAdapter for remote management of NFTs - /* NonFungiblesAdapter< - // Use this pallet: - Nfts, - ConvertedConcreteId, - LocationToAccountId, - AccountId, - NoChecking, - CheckingAccount, - >, */ -); - -/// 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, - // The Relay Chain (Parent) location should convert to a Root origin when needed. - ParentAsSuperuser, - // But should also be able to convert to a native origin. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognized. - 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, -); - -parameter_types! { - // One XCM operation is 0 weight - just for testing. TODO: Benchmark - pub UnitWeightCost: Weight = Weight::from_parts(0, 0); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; -} - -pub struct ParentOrParentsExecutivePlurality; -impl Contains for ParentOrParentsExecutivePlurality { - fn contains(location: &Location) -> bool { - matches!( - location.unpack(), - (1, []) - | ( - 1, - [Plurality { - id: BodyId::Executive, - .. - }] - ) - ) - } -} - -pub type Barrier = TrailingSetTopicAsId< - DenyThenTry< - DenyReserveTransferToRelayChain, - ( - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - // Allow XCMs with some computed origins to pass through. - WithComputedOrigin< - ( - // If the message is one that immediately attempts to pay for execution, then - // allow it. - AllowTopLevelPaidExecutionFrom, - // The locations listed below get free execution. - // Parent and its executive plurality get free execution. - AllowExplicitUnpaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - // HRMP notifications from the relay chain are OK. - AllowHrmpNotificationsFromRelayChain, - ), - UniversalLocation, - ConstU32<8>, - >, - ), - >, ->; - -/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: -/// -/// - DOT with the parent Relay Chain and sibling system parachains -pub type TrustedTeleporters = ConcreteAssetFromSystem; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - // StorageHub does not recognize a reserve location for any asset Users must teleport DOT where allowed (from the Relay Chain or any system chain). - type IsReserve = (); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; // TODO: Benchmark and add correct weights - type Trader = - UsingComponents>; - 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; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} - -/// Converts a local signed origin into an XCM location. -/// Forms the basis for local origins sending/executing XCMs. -pub type LocalOriginToLocation = SignedToAccountId32; - -// Generated from `decl_test_network!` -pub type XcmRouter = crate::ParachainXcmRouter; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - // We want to disallow users sending (arbitrary) XCMs from this chain. - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - // Anyone can execute XCM messages locally. - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Everything; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - 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 WeightInfo = pallet_xcm::TestWeightInfo; - type AdminOrigin = EnsureRoot; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} +use crate::storagehub::MsgQueue; +use crate::storagehub::{ + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, PolkadotXcm, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, WeightToFee, +}; +use frame_support::{ + parameter_types, + traits::{ConstU32, Contains, Everything, Nothing}, + weights::Weight, +}; +use frame_system::EnsureRoot; +use pallet_xcm::XcmPassthrough; +use parachains_common::xcm_config::{ConcreteAssetFromSystem, ParentRelayOrSiblingParachains}; +use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::impls::ToAuthor; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, +}; +use xcm_executor::XcmExecutor; + +parameter_types! { + pub const RelayLocation: Location = Location::parent(); + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub const GovernanceLocation: Location = Location::parent(); +} + +/// Type for specifying how a `Location` 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 parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, +); + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = ( + FungibleAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Do a simple punn to convert an AccountId32 Location into a native chain 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. + (), + >, + // TODO: Check safety of using a NonFungiblesAdapter for remote management of NFTs + /* NonFungiblesAdapter< + // Use this pallet: + Nfts, + ConvertedConcreteId, + LocationToAccountId, + AccountId, + NoChecking, + CheckingAccount, + >, */ +); + +/// 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, + // The Relay Chain (Parent) location should convert to a Root origin when needed. + ParentAsSuperuser, + // But should also be able to convert to a native origin. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognized. + 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, +); + +parameter_types! { + // One XCM operation is 0 weight - just for testing. TODO: Benchmark + pub UnitWeightCost: Weight = Weight::from_parts(0, 0); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct ParentOrParentsExecutivePlurality; +impl Contains for ParentOrParentsExecutivePlurality { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (1, []) + | ( + 1, + [Plurality { + id: BodyId::Executive, + .. + }] + ) + ) + } +} + +pub type Barrier = TrailingSetTopicAsId< + DenyThenTry< + DenyReserveTransferToRelayChain, + ( + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attempts to pay for execution, then + // allow it. + AllowTopLevelPaidExecutionFrom, + // The locations listed below get free execution. + // Parent and its executive plurality get free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + // HRMP notifications from the relay chain are OK. + AllowHrmpNotificationsFromRelayChain, + ), + UniversalLocation, + ConstU32<8>, + >, + ), + >, +>; + +/// Cases where a remote origin is accepted as trusted Teleporter for a given asset: +/// +/// - DOT with the parent Relay Chain and sibling system parachains +pub type TrustedTeleporters = ConcreteAssetFromSystem; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + // StorageHub does not recognize a reserve location for any asset Users must teleport DOT where allowed (from the Relay Chain or any system chain). + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; // TODO: Benchmark and add correct weights + type Trader = + UsingComponents>; + 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; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +/// Converts a local signed origin into an XCM location. +/// Forms the basis for local origins sending/executing XCMs. +pub type LocalOriginToLocation = SignedToAccountId32; + +// Generated from `decl_test_network!` +pub type XcmRouter = crate::ParachainXcmRouter; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We want to disallow users sending (arbitrary) XCMs from this chain. + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + 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 WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} diff --git a/xcm-simulator/src/storagehub/mod.rs b/xcm-simulator/src/storagehub/mod.rs index 3f325eb13..cab8a2820 100644 --- a/xcm-simulator/src/storagehub/mod.rs +++ b/xcm-simulator/src/storagehub/mod.rs @@ -13,8 +13,6 @@ use sp_runtime::{ }; use sp_std::prelude::*; -#[cfg(feature = "std")] -use sp_version::NativeVersion; use sp_version::RuntimeVersion; use frame_support::{ @@ -199,15 +197,6 @@ const BLOCK_PROCESSING_VELOCITY: u32 = 1; /// Relay chain slot duration, in milliseconds. const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; -/// 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(), - } -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime { diff --git a/xcm-simulator/src/system_chain/xcm_config/mod.rs b/xcm-simulator/src/system_chain/xcm_config/mod.rs index d96e9ecbf..1d9f8cc1a 100644 --- a/xcm-simulator/src/system_chain/xcm_config/mod.rs +++ b/xcm-simulator/src/system_chain/xcm_config/mod.rs @@ -1,63 +1,64 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod asset_transactor; -pub mod barrier; -pub mod constants; -pub mod location_converter; -pub mod origin_converter; -pub mod reserve; -pub mod teleporter; -pub mod weigher; - -use crate::system_chain::{MsgQueue, PolkadotXcm, RuntimeCall}; -use frame_support::traits::{Everything, Nothing}; -use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; - -// Generated from `decl_test_network!` -pub type XcmRouter = crate::ParachainXcmRouter; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = asset_transactor::AssetTransactor; - type OriginConverter = origin_converter::OriginConverter; - type IsReserve = reserve::TrustedReserves; - type IsTeleporter = teleporter::TrustedTeleporters; - type UniversalLocation = constants::UniversalLocation; - type Barrier = barrier::Barrier; - type Weigher = weigher::Weigher; - type Trader = FixedRateOfFungible; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = PolkadotXcm; - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = (); - type FeeManager = (); - type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod reserve; +pub mod teleporter; +pub mod weigher; + +use crate::system_chain::{MsgQueue, PolkadotXcm, RuntimeCall}; +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{FixedRateOfFungible, FrameTransactionalProcessor}; + +// Generated from `decl_test_network!` +pub type XcmRouter = crate::ParachainXcmRouter; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = origin_converter::OriginConverter; + type IsReserve = reserve::TrustedReserves; + type IsTeleporter = teleporter::TrustedTeleporters; + type UniversalLocation = constants::UniversalLocation; + type Barrier = barrier::Barrier; + type Weigher = weigher::Weigher; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +}