diff --git a/Cargo.lock b/Cargo.lock index 8c992bea..35ec3be9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,11 +23,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ - "gimli 0.31.1", + "gimli 0.32.3", ] [[package]] @@ -79,7 +79,7 @@ checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "const-random", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -102,9 +102,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-core" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe6c56d58fbfa9f0f6299376e8ce33091fc6494239466814c3f54b55743cb09" +checksum = "5ca96214615ec8cf3fa2a54b32f486eb49100ca7fe7eb0b8c1137cd316e7250a" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f56873f3cac7a2c63d8e98a4314b8311aa96adb1a0f82ae923eb2119809d2c" +checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125a1c373261b252e53e04d6e92c37d881833afc1315fceab53fd46045695640" +checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -143,18 +143,18 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" dependencies = [ "alloy-rlp", - "bytes 1.10.1", + "bytes 1.11.0", "cfg-if", "const-hex", "derive_more 2.0.1", - "foldhash 0.1.5", - "hashbrown 0.15.5", - "indexmap 2.11.4", + "foldhash 0.2.0", + "hashbrown 0.16.0", + "indexmap 2.12.0", "itoa", "k256", "keccak-asm", @@ -175,46 +175,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" dependencies = [ "arrayvec 0.7.6", - "bytes 1.10.1", + "bytes 1.11.0", ] [[package]] name = "alloy-sol-macro" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20d867dcf42019d4779519a1ceb55eba8d7f3d0e4f0a89bcba82b8f9eb01e48" +checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74e91b0b553c115d14bd0ed41898309356dc85d0e3d4b9014c4e7715e48c8ad" +checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84194d31220803f5f62d0a00f583fd3a062b36382e2bea446f1af96727754565" +checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ "const-hex", "dunce", @@ -222,15 +222,15 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe8c27b3cf6b2bb8361904732f955bc7c05e00be5f469cec7e2280b6167f3ff0" +checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" dependencies = [ "serde", "winnow", @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5383d34ea00079e6dd89c652bcbdb764db160cef84e6250926961a0b2295d04" +checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -265,9 +265,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -295,22 +295,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -339,7 +339,16 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", +] + +[[package]] +name = "ar_archive_writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object 0.32.2", ] [[package]] @@ -521,7 +530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -559,7 +568,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -644,7 +653,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -723,7 +732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d55334c98d756b32dcceb60248647ab34f027690f87f9a362fd292676ee927" dependencies = [ "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -778,7 +787,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -790,7 +799,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure 0.13.2", ] @@ -802,7 +811,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure 0.13.2", ] @@ -814,7 +823,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -886,7 +895,7 @@ dependencies = [ "polling", "rustix 1.1.2", "slab", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -944,7 +953,7 @@ dependencies = [ "rustix 1.1.2", "signal-hook-registry", "slab", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -961,7 +970,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -970,7 +979,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-sink", "futures-util", "memchr", @@ -983,7 +992,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-sink", "futures-util", "memchr", @@ -1021,7 +1030,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1032,17 +1041,17 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line 0.24.2", + "addr2line 0.25.1", "cfg-if", "libc", "miniz_oxide", - "object 0.36.7", + "object 0.37.3", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1057,6 +1066,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + [[package]] name = "base58" version = "0.2.0" @@ -1083,9 +1102,9 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "binary-merkle-tree" -version = "16.0.0" +version = "16.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "181f5380e435b8ba6d901f8b16fc8908c6f0f8bea8973113d1c8718d89bb1809" +checksum = "95c9f6900c9fd344d53fbdfb36e1343429079d73f4168c8ef48884bf15616dbd" dependencies = [ "hash-db", "log", @@ -1119,7 +1138,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1205,9 +1224,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -1379,9 +1398,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -1397,9 +1416,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -1426,9 +1445,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ "serde_core", ] @@ -1464,9 +1483,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.38" +version = "1.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" dependencies = [ "find-msvc-tools", "jobserver", @@ -1500,9 +1519,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -1561,7 +1580,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -1649,9 +1668,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -1659,9 +1678,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -1672,21 +1691,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clatter" @@ -1722,9 +1741,9 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", @@ -1743,7 +1762,7 @@ version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "memchr", ] @@ -1787,9 +1806,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6407bff74dea37e0fa3dc1c1c974e5d46405f0c987bf9997a0762adce71eda6" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", "cpufeatures", @@ -1823,11 +1842,17 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -2045,7 +2070,7 @@ dependencies = [ "serde_derive", "serde_json", "tinytemplate", - "tokio 1.47.1", + "tokio 1.48.0", "walkdir", ] @@ -2128,9 +2153,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -2205,11 +2230,12 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa7c354e70d62b5bb6014cbad499b831e27629b8a2af2f86497cae7f74d309d" +checksum = "ac1d9d194bc0faaef14a95ed6e78d4afccd117e67ff03c0ed712298e1124921a" dependencies = [ - "bytes 1.10.1", + "array-bytes 6.2.3", + "bytes 1.11.0", "cumulus-pallet-parachain-system-proc-macro", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", @@ -2250,7 +2276,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2356,14 +2382,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "cxx" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f81de88da10862f22b5b3a60f18f6f42bbe7cb8faa24845dd7b1e4e22190e77" +checksum = "47ac4eaf7ebe29e92f1b091ceefec7710a53a6f6154b2460afda626c113b65b9" dependencies = [ "cc", "cxx-build", @@ -2376,50 +2402,49 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5edd58bf75c3fdfc80d79806403af626570662f7b6cc782a7fabe156166bd6d6" +checksum = "2abd4c3021eefbac5149f994c117b426852bca3a0aad227698527bca6d4ea657" dependencies = [ "cc", "codespan-reporting", - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro2", "quote", "scratch", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd46bf2b541a4e0c2d5abba76607379ee05d68e714868e3cb406dc8d591ce2d2" +checksum = "6f12fbc5888b2311f23e52a601e11ad7790d8f0dbb903ec26e2513bf5373ed70" dependencies = [ "clap", "codespan-reporting", - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "cxxbridge-flags" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c79b68f6a3a8f809d39b38ae8af61305a6113819b19b262643b9c21353b92d9" +checksum = "83d3dd7870af06e283f3f8ce0418019c96171c9ce122cfb9c8879de3d84388fd" [[package]] name = "cxxbridge-macro" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862b7fdb048ff9ef0779a0d0a03affd09746c4c875543746b640756be9cff2af" +checksum = "a26f0d82da663316786791c3d0e9f9edc7d1ee1f04bdad3d2643086a69d6256c" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2453,7 +2478,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2467,7 +2492,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2478,7 +2503,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2489,7 +2514,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2502,7 +2527,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -2528,7 +2553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2572,9 +2597,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -2598,7 +2623,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2609,7 +2634,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2620,7 +2645,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2633,7 +2658,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2662,7 +2687,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2673,7 +2698,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "unicode-xid", ] @@ -2772,7 +2797,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2796,7 +2821,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.106", + "syn 2.0.110", "termcolor", "toml 0.8.23", "walkdir", @@ -2844,7 +2869,7 @@ checksum = "7e8671d54058979a37a26f3511fbf8d198ba1aa35ffb202c42587d918d77213a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2918,7 +2943,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -2971,34 +2996,34 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "enum-ordinalize" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -3049,7 +3074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -3140,7 +3165,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3169,7 +3194,7 @@ checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ "arrayvec 0.7.6", "auto_impl", - "bytes 1.10.1", + "bytes 1.11.0", ] [[package]] @@ -3180,7 +3205,7 @@ checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" dependencies = [ "arrayvec 0.7.6", "auto_impl", - "bytes 1.10.1", + "bytes 1.11.0", ] [[package]] @@ -3200,11 +3225,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ "expander", - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3277,15 +3302,15 @@ dependencies = [ "log", "num-traits", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "scale-info", ] [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixed-hash" @@ -3314,6 +3339,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "fnv" version = "1.0.7" @@ -3463,7 +3494,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cb8796f93fa038f979a014234d632e9688a120e745f936e2635123c77537f7" dependencies = [ - "frame-metadata 20.0.0", + "frame-metadata 21.0.0", "parity-scale-codec", "scale-decode", "scale-info", @@ -3480,7 +3511,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3503,9 +3534,9 @@ dependencies = [ [[package]] name = "frame-executive" -version = "41.0.1" +version = "41.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e5477db02bf54b4413611f55ec59673fa7b071eef08b7097e739f999a215cd" +checksum = "11e495475817addd877fa08bc17fc92d7308f4d5e99be5488433ccf19d222122" dependencies = [ "aquamarine", "frame-support", @@ -3532,6 +3563,17 @@ dependencies = [ "serde", ] +[[package]] +name = "frame-metadata" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20dfd1d7eae1d94e32e869e2fb272d81f52dd8db57820a373adb83ea24d7d862" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "frame-metadata" version = "23.0.0" @@ -3636,7 +3678,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3649,7 +3691,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3660,14 +3702,14 @@ checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "frame-system" -version = "41.0.0" +version = "41.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce7df989cefbaab681101774748a1bbbcd23d0cc66e392f8f22d3d3159914db" +checksum = "4189198074d7964bd6cb9d1cf69d77d5d224026bce95bb0ca7efc7768bb8e29c" dependencies = [ "cfg-if", "docify", @@ -3832,7 +3874,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -3892,20 +3934,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "generator" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" -dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows 0.61.3", -] - [[package]] name = "generic-array" version = "0.12.4" @@ -3945,21 +3973,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -4004,6 +4032,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "glob" version = "0.3.3" @@ -4022,7 +4056,7 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "portable-atomic", "quanta", "rand 0.8.5", @@ -4047,15 +4081,15 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "fnv", "futures-core", "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.4", + "indexmap 2.12.0", "slab", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-util", "tracing", ] @@ -4067,26 +4101,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", - "bytes 1.10.1", + "bytes 1.11.0", "fnv", "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.11.4", + "indexmap 2.12.0", "slab", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-util", "tracing", ] [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -4154,6 +4189,15 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash 0.2.0", "serde", ] @@ -4237,7 +4281,7 @@ dependencies = [ "socket2 0.5.10", "thiserror 1.0.69", "tinyvec", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "url", ] @@ -4260,9 +4304,9 @@ dependencies = [ "once_cell", "rand 0.9.2", "ring 0.17.14", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "url", ] @@ -4279,12 +4323,12 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "resolv-conf", "smallvec", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", ] @@ -4300,12 +4344,12 @@ dependencies = [ "ipconfig", "moka", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.16", - "tokio 1.47.1", + "thiserror 2.0.17", + "tokio 1.48.0", "tracing", ] @@ -4354,7 +4398,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "fnv", "itoa", ] @@ -4365,7 +4409,7 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "fnv", "itoa", ] @@ -4376,7 +4420,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "http 0.2.12", "pin-project-lite 0.2.16", ] @@ -4387,7 +4431,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "http 1.3.1", ] @@ -4397,7 +4441,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-core", "http 1.3.1", "http-body 1.0.1", @@ -4447,7 +4491,7 @@ version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-channel", "futures-core", "futures-util", @@ -4459,7 +4503,7 @@ dependencies = [ "itoa", "pin-project-lite 0.2.16", "socket2 0.5.10", - "tokio 1.47.1", + "tokio 1.48.0", "tower-service", "tracing", "want", @@ -4467,12 +4511,12 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", - "bytes 1.10.1", + "bytes 1.11.0", "futures-channel", "futures-core", "h2 0.4.12", @@ -4484,7 +4528,7 @@ dependencies = [ "pin-project-lite 0.2.16", "pin-utils", "smallvec", - "tokio 1.47.1", + "tokio 1.48.0", "want", ] @@ -4495,34 +4539,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "rustls", "rustls-native-certs", "rustls-pki-types", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-rustls", "tower-service", ] [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-channel", "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.8.1", "libc", "pin-project-lite 0.2.16", - "socket2 0.6.0", - "tokio 1.47.1", + "socket2 0.6.1", + "tokio 1.48.0", "tower-service", "tracing", ] @@ -4539,7 +4583,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.0", + "windows-core 0.62.2", ] [[package]] @@ -4553,9 +4597,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -4566,9 +4610,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -4579,11 +4623,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -4594,42 +4637,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -4693,7 +4732,7 @@ dependencies = [ "netlink-sys", "rtnetlink", "system-configuration 0.6.1", - "tokio 1.47.1", + "tokio 1.48.0", "windows 0.53.0", ] @@ -4705,13 +4744,13 @@ checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4" dependencies = [ "async-trait", "attohttpc", - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "http 0.2.12", "hyper 0.14.32", "log", "rand 0.8.5", - "tokio 1.47.1", + "tokio 1.48.0", "url", "xmltree", ] @@ -4771,7 +4810,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -4806,12 +4845,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.0", "serde", "serde_core", ] @@ -4860,17 +4899,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ip_network" version = "0.4.1" @@ -4897,20 +4925,20 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -4965,26 +4993,26 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -5015,15 +5043,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -5031,9 +5059,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" +checksum = "e281ae70cc3b98dac15fced3366a880949e65fc66e345ce857a5682d152f3e62" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -5041,15 +5069,15 @@ dependencies = [ "jsonrpsee-server", "jsonrpsee-types", "jsonrpsee-ws-client", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", ] [[package]] name = "jsonrpsee-client-transport" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" +checksum = "cc4280b709ac3bb5e16cf3bad5056a0ec8df55fa89edfe996361219aadc2c7ea" dependencies = [ "base64 0.22.1", "futures-util", @@ -5061,7 +5089,7 @@ dependencies = [ "rustls-platform-verifier", "soketto", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-rustls", "tokio-util", "tracing", @@ -5070,54 +5098,54 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +checksum = "348ee569eaed52926b5e740aae20863762b16596476e943c9e415a6479021622" dependencies = [ "async-trait", - "bytes 1.10.1", + "bytes 1.11.0", "futures-timer", "futures-util", "http 1.3.1", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.8.5", "rustc-hash 2.1.1", "serde", "serde_json", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", "tracing", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" +checksum = "7398cddf5013cca4702862a2692b66c48a3bd6cf6ec681a47453c93d63cf8de5" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "jsonrpsee-server" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" +checksum = "21429bcdda37dcf2d43b68621b994adede0e28061f816b038b0f18c70c143d51" dependencies = [ "futures-util", "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -5127,7 +5155,7 @@ dependencies = [ "serde_json", "soketto", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", "tokio-util", "tower", @@ -5136,9 +5164,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +checksum = "b0f05e0028e55b15dbd2107163b3c744cd3bb4474f193f95d9708acbf5677e44" dependencies = [ "http 1.3.1", "serde", @@ -5148,9 +5176,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" +checksum = "78fc744f17e7926d57f478cf9ca6e1ee5d8332bf0514860b1a3cdf1742e614cc" dependencies = [ "http 1.3.1", "jsonrpsee-client-transport", @@ -5256,7 +5284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" dependencies = [ "kvdb", - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -5267,7 +5295,7 @@ checksum = "b644c70b92285f66bfc2032922a79000ea30af7bc2ab31902992a5dcb9b434f6" dependencies = [ "kvdb", "num_cpus", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "regex", "rocksdb", "smallvec", @@ -5290,9 +5318,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" @@ -5301,7 +5329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -5316,7 +5344,7 @@ version = "0.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbe80f9c7e00526cd6b838075b9c171919404a4732cb2fa8ece0a093223bfc4" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "either", "futures 0.3.31", "futures-timer", @@ -5384,7 +5412,7 @@ dependencies = [ "multihash 0.19.3", "multistream-select", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "quick-protobuf", "rand 0.8.5", @@ -5408,7 +5436,7 @@ dependencies = [ "hickory-resolver 0.24.4", "libp2p-core", "libp2p-identity", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "smallvec", "tracing", ] @@ -5462,7 +5490,7 @@ checksum = "ced237d0bd84bbebb7c2cad4c073160dacb4fe40534963c32ed6d4c6bb7702a3" dependencies = [ "arrayvec 0.7.6", "asynchronous-codec 0.7.0", - "bytes 1.10.1", + "bytes 1.11.0", "either", "fnv", "futures 0.3.31", @@ -5499,7 +5527,7 @@ dependencies = [ "rand 0.8.5", "smallvec", "socket2 0.5.10", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "void", ] @@ -5528,7 +5556,7 @@ version = "0.45.10" source = "git+https://github.com/Quantus-Network/qp-libp2p-noise?tag=v0.45.10#901f09f30b32f910395270bba3a566191dc2f61f" dependencies = [ "asynchronous-codec 0.6.2", - "bytes 1.10.1", + "bytes 1.11.0", "clatter", "futures 0.3.31", "libp2p-core", @@ -5569,21 +5597,21 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46352ac5cd040c70e88e7ff8257a2ae2f891a4076abad2c439584a31c15fd24e" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "futures-timer", "if-watch", "libp2p-core", "libp2p-identity", "libp2p-tls", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "quinn", "rand 0.8.5", "ring 0.17.14", "rustls", "socket2 0.5.10", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", ] @@ -5625,7 +5653,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "smallvec", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "void", "web-time", @@ -5640,7 +5668,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -5656,7 +5684,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "socket2 0.5.10", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", ] @@ -5690,7 +5718,7 @@ dependencies = [ "igd-next", "libp2p-core", "libp2p-swarm", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "void", ] @@ -5706,7 +5734,7 @@ dependencies = [ "futures-rustls", "libp2p-core", "libp2p-identity", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite 0.2.16", "rw-stream-sink", "soketto", @@ -5728,7 +5756,7 @@ dependencies = [ "thiserror 1.0.69", "tracing", "yamux 0.12.1", - "yamux 0.13.6", + "yamux 0.13.8", ] [[package]] @@ -5737,9 +5765,9 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", ] [[package]] @@ -5807,9 +5835,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "pkg-config", @@ -5833,9 +5861,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linked_hash_set" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" +checksum = "984fb35d06508d1e69fc91050cceba9c0b748f983e6739fa2c7a9237154c52c8" dependencies = [ "linked-hash-map", ] @@ -5875,9 +5903,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litep2p" @@ -5887,19 +5915,19 @@ checksum = "14fb10e63363204b89d91e1292df83322fd9de5d7fa76c3d5c78ddc2f8f3efa9" dependencies = [ "async-trait", "bs58", - "bytes 1.10.1", + "bytes 1.11.0", "cid 0.11.1", "ed25519-dalek", "futures 0.3.31", "futures-timer", "hickory-resolver 0.25.2", - "indexmap 2.11.4", + "indexmap 2.12.0", "libc", "mockall", "multiaddr 0.17.1", "multihash 0.17.0", "network-interface", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "prost 0.13.5", "prost-build", @@ -5910,8 +5938,8 @@ dependencies = [ "smallvec", "snow", "socket2 0.5.10", - "thiserror 2.0.16", - "tokio 1.47.1", + "thiserror 2.0.17", + "tokio 1.48.0", "tokio-stream", "tokio-tungstenite", "tokio-util", @@ -5921,18 +5949,17 @@ dependencies = [ "url", "x25519-dalek", "x509-parser 0.17.0", - "yamux 0.13.6", + "yamux 0.13.8", "yasna", "zeroize", ] [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -5942,19 +5969,6 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru" version = "0.12.5" @@ -6015,7 +6029,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -6027,7 +6041,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -6041,7 +6055,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -6052,7 +6066,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -6063,16 +6077,27 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.106", + "syn 2.0.110", +] + +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -6087,9 +6112,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memfd" @@ -6111,9 +6136,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -6187,13 +6212,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -6212,7 +6237,7 @@ dependencies = [ "hashlink", "lioness", "log", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "rand_chacha 0.3.1", "rand_distr", @@ -6257,25 +6282,24 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "moka" -version = "0.12.10" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "loom", - "parking_lot 0.12.4", + "equivalent", + "parking_lot 0.12.5", "portable-atomic", "rustc_version 0.4.1", "smallvec", "tagptr", - "thiserror 1.0.69", "uuid", ] @@ -6325,11 +6349,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -6387,7 +6412,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "log", "pin-project", @@ -6481,12 +6506,12 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -6495,11 +6520,11 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "libc", "log", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -6510,7 +6535,7 @@ checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" dependencies = [ "cc", "libc", - "thiserror 2.0.16", + "thiserror 2.0.17", "winapi", ] @@ -6576,12 +6601,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -6633,7 +6657,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -6709,6 +6733,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.36.7" @@ -6718,6 +6751,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.7.1" @@ -6748,9 +6790,9 @@ dependencies = [ [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -6806,21 +6848,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43dfaf083aef571385fccfdc3a2f8ede8d0a1863160455d4f2b014d8f7d04a3f" dependencies = [ "expander", - "indexmap 2.11.4", + "indexmap 2.12.0", "itertools 0.11.0", - "petgraph", + "petgraph 0.6.5", "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p3-dft" version = "0.3.0" @@ -7045,27 +7081,6 @@ dependencies = [ "sp-staking", ] -[[package]] -name = "pallet-balances" -version = "40.0.1" -dependencies = [ - "docify", - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "pallet-transaction-payment", - "parity-scale-codec", - "paste", - "qp-poseidon", - "qp-wormhole", - "scale-info", - "sp-core", - "sp-io", - "sp-metadata-ir", - "sp-runtime", -] - [[package]] name = "pallet-balances" version = "42.0.0" @@ -7128,7 +7143,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-balances 40.0.1", + "pallet-balances", "pallet-vesting", "parity-scale-codec", "scale-info", @@ -7166,7 +7181,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-balances 40.0.1", + "pallet-balances", "parity-scale-codec", "qp-wormhole", "scale-info", @@ -7230,9 +7245,9 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" -version = "41.0.0" +version = "41.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf43c766b69c37ab964cf076f605d3993357124fcdd14a8ba3ecc169e2d0fc9c" +checksum = "aae595fd2ce7b0889680e173fe7faf743f127143faf5f2e716bae12cd8c0b354" dependencies = [ "frame-benchmarking", "frame-support", @@ -7287,7 +7302,7 @@ dependencies = [ "log", "pallet-assets", "pallet-assets-holder", - "pallet-balances 40.0.1", + "pallet-balances", "pallet-preimage", "pallet-recovery", "pallet-scheduler", @@ -7304,9 +7319,9 @@ dependencies = [ [[package]] name = "pallet-revive" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474840408264f98eea7f187839ff2157f83a92bec6f3f3503dbf855c38f4de6b" +checksum = "1459d118aeccd7fef1dfb7e3ea002a376c8f07c61053eb2875acc38b1ceee4d9" dependencies = [ "alloy-core", "derive_more 0.99.20", @@ -7372,7 +7387,7 @@ checksum = "63c2dc2fc6961da23fefc54689ce81a8e006f6988bc465dcc9ab9db905d31766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -7546,7 +7561,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "log", - "pallet-balances 42.0.0", + "pallet-balances", "parity-scale-codec", "scale-info", "serde", @@ -7595,16 +7610,27 @@ dependencies = [ "hex", "lazy_static", "log", - "pallet-balances 40.0.1", + "pallet-assets", + "pallet-balances", "parity-scale-codec", + "qp-dilithium-crypto", + "qp-header", + "qp-plonky2", + "qp-poseidon", + "qp-poseidon-core", "qp-wormhole", "qp-wormhole-circuit", + "qp-wormhole-circuit-builder", + "qp-wormhole-prover", "qp-wormhole-verifier", "qp-zk-circuits-common", "scale-info", "sp-core", "sp-io", + "sp-metadata-ir", "sp-runtime", + "sp-state-machine", + "sp-trie", ] [[package]] @@ -7634,7 +7660,7 @@ dependencies = [ "log", "lz4", "memmap2 0.5.10", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "siphasher 0.3.11", "snap", @@ -7650,7 +7676,7 @@ dependencies = [ "arrayvec 0.7.6", "bitvec", "byte-slice-cast", - "bytes 1.10.1", + "bytes 1.11.0", "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", @@ -7667,7 +7693,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -7695,12 +7721,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -7719,15 +7745,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -7772,12 +7798,12 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] @@ -7797,20 +7823,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" dependencies = [ "pest", "pest_generator", @@ -7818,22 +7843,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "pest_meta" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" dependencies = [ "pest", "sha2 0.10.9", @@ -7845,8 +7870,18 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", - "indexmap 2.11.4", + "fixedbitset 0.4.2", + "indexmap 2.12.0", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset 0.5.7", + "indexmap 2.12.0", ] [[package]] @@ -7866,7 +7901,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8025,9 +8060,9 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "20.0.0" +version = "20.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498dd752f08ff2a3f92fa67e5f5cbca65a342c924465ca8cc7e4b3902a6f8613" +checksum = "01b9c91614ec9e0502c547792dd72b00cf3c73d7290f8e611008129207124e4f" dependencies = [ "bitvec", "bounded-vec", @@ -8160,9 +8195,9 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" -version = "20.0.2" +version = "20.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7692d2a6109ef7b4749c51d5c950fa15f8e38fed439d5d520ba49fa322466c45" +checksum = "d5eccffd0a38fc4cdbf9b3b8c0bc9d140fc2cb49e1f073208b88c9c9138ae039" dependencies = [ "bitflags 1.3.2", "bitvec", @@ -8175,7 +8210,7 @@ dependencies = [ "pallet-authority-discovery", "pallet-authorship", "pallet-babe", - "pallet-balances 42.0.0", + "pallet-balances", "pallet-broker", "pallet-message-queue", "pallet-mmr", @@ -8346,7 +8381,7 @@ dependencies = [ "polkavm-common 0.21.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8358,7 +8393,7 @@ dependencies = [ "polkavm-common 0.24.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8368,7 +8403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36837f6b7edfd6f4498f8d25d81da16cf03bd6992c3e56f3d477dfc90f4fefca" dependencies = [ "polkavm-derive-impl 0.21.0", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8378,7 +8413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba0ef0f17ad81413ea1ca5b1b67553aedf5650c88269b673d3ba015c83bc2651" dependencies = [ "polkavm-derive-impl 0.24.0", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8436,7 +8471,7 @@ dependencies = [ "hermit-abi 0.5.2", "pin-project-lite 0.2.16", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -8479,9 +8514,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -8509,7 +8544,7 @@ checksum = "b4a326caf27cbf2ac291ca7fd56300497ba9e76a8cc6a7d95b7a18b57f22b61d" dependencies = [ "cc", "dunce", - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] @@ -8575,7 +8610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8655,7 +8690,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.6", + "toml_edit 0.23.7", ] [[package]] @@ -8701,7 +8736,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8712,14 +8747,14 @@ checksum = "75eea531cfcd120e0851a3f8aed42c4841f78c889eefafd96339c72677ae42c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -8734,7 +8769,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "thiserror 1.0.69", ] @@ -8746,7 +8781,7 @@ checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" dependencies = [ "dtoa", "itoa", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "prometheus-client-derive-encode", ] @@ -8758,24 +8793,23 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "proptest" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.4", - "lazy_static", + "bitflags 2.10.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax 0.8.6", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -8787,7 +8821,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "prost-derive 0.12.6", ] @@ -8797,7 +8831,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "prost-derive 0.13.5", ] @@ -8812,12 +8846,12 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph", + "petgraph 0.7.1", "prettyplease", "prost 0.13.5", "prost-types", "regex", - "syn 2.0.106", + "syn 2.0.110", "tempfile", ] @@ -8831,7 +8865,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8844,7 +8878,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -8858,10 +8892,11 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] @@ -8882,11 +8917,29 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "qp-header" +version = "0.1.0" +dependencies = [ + "hex", + "log", + "p3-field", + "p3-goldilocks", + "parity-scale-codec", + "qp-poseidon", + "qp-poseidon-core", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-runtime", +] + [[package]] name = "qp-plonky2" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068162bd3730e381744eca219f98f0db22874afc8fa24631611e975c93d6cf68" +checksum = "39530b02faa85964bba211e030afa2d54995b403b0022f88e984c4c65679c4bc" dependencies = [ "ahash", "anyhow", @@ -8896,9 +8949,14 @@ dependencies = [ "keccak-hash 0.8.0", "log", "num", + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "p3-symmetric", "plonky2_maybe_rayon", "plonky2_util", "qp-plonky2-field", + "qp-poseidon-constants", "rand 0.8.5", "rand_chacha 0.3.1", "serde", @@ -8909,9 +8967,9 @@ dependencies = [ [[package]] name = "qp-plonky2-field" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b2f7e0854f9e14aa9b3f6d544ccf71087e751d6adbe6a7e50d2c75fb561d97" +checksum = "7e8d52dadf3bb92708c309922b62d7f3f2587d3047f9fe05a0c9f587e2890526" dependencies = [ "anyhow", "itertools 0.11.0", @@ -8926,9 +8984,9 @@ dependencies = [ [[package]] name = "qp-poseidon" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0353086f7af1df7d45a1ecb995cf84b583c8211d7122f542044b37388b5effcd" +checksum = "fef3bb816fc51b4ffc4524fa03376037d2531b17e2349ef6489a417cef029b3a" dependencies = [ "log", "p3-field", @@ -8958,14 +9016,15 @@ dependencies = [ [[package]] name = "qp-poseidon-core" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e658a373a7fb22babeda9ffcc8af0a894e6e3c008272ed735509eccb7769ead3" +checksum = "e33b3fb9032ac9b197265da8fc8bdedd21ba76592ceefbf4a591286d47baec2d" dependencies = [ "p3-field", "p3-goldilocks", "p3-poseidon2", "p3-symmetric", + "qp-plonky2", "qp-poseidon-constants", "rand_chacha 0.9.0", ] @@ -8998,7 +9057,7 @@ dependencies = [ "rand_core 0.9.3", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -9017,21 +9076,34 @@ version = "0.1.0" [[package]] name = "qp-wormhole-circuit" -version = "0.1.2" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea70a3a1bf545450cb6320782389cfbfe58fcf0ecf724aa666383807bf5548b" +checksum = "bcbccee20e314a1c52f36d8e78b1fa8205da46050c18da60d4964f41875bd4f6" dependencies = [ "anyhow", "hex", "qp-plonky2", + "qp-poseidon-core", "qp-zk-circuits-common", ] [[package]] name = "qp-wormhole-circuit-builder" -version = "0.1.2" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6486fbfd79005d520caff6a32a8fde4eab978110101c9612d140f3e0ebdfd0" +checksum = "aec473c32d69e2e0941d192ab8cf8712761ee2e84d3829fc211087f53be15bf3" +dependencies = [ + "anyhow", + "qp-plonky2", + "qp-wormhole-circuit", + "qp-zk-circuits-common", +] + +[[package]] +name = "qp-wormhole-prover" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd377a2fa936e6edb069ffecbf41afe10803b342e5cda8ff3aab199f77fde5d" dependencies = [ "anyhow", "qp-plonky2", @@ -9041,9 +9113,9 @@ dependencies = [ [[package]] name = "qp-wormhole-verifier" -version = "0.1.2" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f83ee7ce0fdb40f34eed7a31a575e857d24135a383cdfd6f2429a7ce048b665" +checksum = "08e114bc7ad7a7589c502960abc570685b726993ccce05443577a9ddd8dc4ee1" dependencies = [ "anyhow", "qp-plonky2", @@ -9053,12 +9125,14 @@ dependencies = [ [[package]] name = "qp-zk-circuits-common" -version = "0.1.2" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a2a62915be0513d045f4d92829c0a26d6f3e8897353e8f8356a7eabef025329" +checksum = "445cc21c39959d1b553c4d8ea94d058ceab84cd70e5d47ec82b11535494cec46" dependencies = [ "anyhow", + "hex", "qp-plonky2", + "qp-poseidon-core", "serde", ] @@ -9084,7 +9158,7 @@ dependencies = [ "libc", "once_cell", "raw-cpuid", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "web-sys", "winapi", ] @@ -9171,7 +9245,7 @@ dependencies = [ "log", "pallet-assets", "pallet-assets-holder", - "pallet-balances 40.0.1", + "pallet-balances", "pallet-conviction-voting", "pallet-merkle-airdrop", "pallet-mining-rewards", @@ -9189,11 +9263,17 @@ dependencies = [ "pallet-treasury", "pallet-utility", "pallet-vesting", + "pallet-wormhole", "parity-scale-codec", "primitive-types 0.13.1", "qp-dilithium-crypto", + "qp-header", "qp-poseidon", "qp-scheduler", + "qp-wormhole", + "qp-wormhole-circuit", + "qp-wormhole-verifier", + "qp-zk-circuits-common", "scale-info", "serde_json", "sp-api", @@ -9235,7 +9315,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" dependencies = [ "asynchronous-codec 0.7.0", - "bytes 1.10.1", + "bytes 1.11.0", "quick-protobuf", "thiserror 1.0.69", "unsigned-varint 0.8.0", @@ -9247,7 +9327,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "cfg_aliases 0.2.1", "futures-io", "pin-project-lite 0.2.16", @@ -9255,9 +9335,9 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.6.0", - "thiserror 2.0.16", - "tokio 1.47.1", + "socket2 0.6.1", + "thiserror 2.0.17", + "tokio 1.48.0", "tracing", "web-time", ] @@ -9268,8 +9348,8 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ - "bytes 1.10.1", - "getrandom 0.3.3", + "bytes 1.11.0", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring 0.17.14", @@ -9277,7 +9357,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -9292,16 +9372,16 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -9375,7 +9455,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "serde", ] @@ -9413,7 +9493,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -9465,11 +9545,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -9485,22 +9565,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -9530,47 +9610,32 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.10", - "regex-syntax 0.8.6", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.6", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -9579,7 +9644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", - "bytes 1.10.1", + "bytes 1.11.0", "encoding_rs", "futures-core", "futures-util", @@ -9599,7 +9664,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "system-configuration 0.5.1", - "tokio 1.47.1", + "tokio 1.48.0", "tower-service", "url", "wasm-bindgen", @@ -9668,7 +9733,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "rustc-hex", ] @@ -9678,7 +9743,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "rustc-hex", ] @@ -9724,7 +9789,7 @@ dependencies = [ "netlink-sys", "nix", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -9739,14 +9804,15 @@ dependencies = [ [[package]] name = "ruint" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", "ark-ff 0.4.2", - "bytes 1.10.1", + "ark-ff 0.5.0", + "bytes 1.11.0", "fastrlp 0.3.1", "fastrlp 0.4.0", "num-bigint", @@ -9759,7 +9825,7 @@ dependencies = [ "rand 0.9.2", "rlp 0.5.2", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -9841,33 +9907,33 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.6", + "rustls-webpki 0.103.8", "subtle 2.6.1", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -9877,9 +9943,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -9899,7 +9965,7 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.103.6", + "rustls-webpki 0.103.8", "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", @@ -9924,9 +9990,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring 0.17.14", "rustls-pki-types", @@ -9941,9 +10007,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -10046,7 +10112,7 @@ dependencies = [ "sp-runtime", "substrate-prometheus-endpoint", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -10095,7 +10161,7 @@ checksum = "c25df970b58c05e8381a1ead6f5708e3539aefa9212d311866fed64f141214e7" dependencies = [ "array-bytes 6.2.3", "docify", - "memmap2 0.9.8", + "memmap2 0.9.9", "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", @@ -10123,7 +10189,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -10171,7 +10237,7 @@ dependencies = [ "sp-version", "tempfile", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -10184,7 +10250,7 @@ dependencies = [ "futures 0.3.31", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-executor", "sc-transaction-pool-api", "sc-utils", @@ -10215,7 +10281,7 @@ dependencies = [ "log", "parity-db", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-client-api", "sc-state-db", "schnellru", @@ -10240,7 +10306,7 @@ dependencies = [ "futures 0.3.31", "log", "mockall", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-client-api", "sc-network-types", "sc-utils", @@ -10268,7 +10334,7 @@ dependencies = [ "num-rational", "num-traits", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-client-api", "sc-consensus", "sc-consensus-epochs", @@ -10315,7 +10381,7 @@ dependencies = [ "hex", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "primitive-types 0.13.1", "sc-client-api", "sc-consensus", @@ -10367,7 +10433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfd7a23eebd1fea90534994440f0ef516cbd8c0ef749e272c6c77fd729bbca71" dependencies = [ "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", @@ -10418,7 +10484,7 @@ checksum = "a5980897e2915ef027560886a2bb52f49a2cea4a9b9f5c75fead841201d03705" dependencies = [ "anyhow", "log", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rustix 0.36.17", "sc-allocator", "sc-executor-common", @@ -10451,7 +10517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c28fc85c00ddf64f32f68111c61521b1f260f8dfa1eb98f9ed4401aa5d0ce0" dependencies = [ "array-bytes 6.2.3", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde_json", "sp-application-crypto", "sp-core", @@ -10468,13 +10534,13 @@ dependencies = [ "array-bytes 6.2.3", "arrayvec 0.7.6", "blake2 0.10.6", - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "futures-timer", "log", "mixnet", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-client-api", "sc-network", "sc-network-types", @@ -10497,7 +10563,7 @@ dependencies = [ "async-channel 1.9.0", "async-trait", "asynchronous-codec 0.6.2", - "bytes 1.10.1", + "bytes 1.11.0", "cid 0.9.0", "criterion", "either", @@ -10513,7 +10579,7 @@ dependencies = [ "multistream-select", "once_cell", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "partial_sort", "pin-project", "prost 0.12.6", @@ -10534,7 +10600,7 @@ dependencies = [ "substrate-prometheus-endpoint", "tempfile", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", "tokio-util", "unsigned-varint 0.7.2", @@ -10608,7 +10674,7 @@ dependencies = [ "sp-runtime", "substrate-prometheus-endpoint", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", ] @@ -10639,7 +10705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "441af5d0adf306ff745ccf23c7426ec2edf24f6fee678fb63994e1f8d2fcc890" dependencies = [ "bs58", - "bytes 1.10.1", + "bytes 1.11.0", "ed25519-dalek", "libp2p-identity", "libp2p-kad", @@ -10660,18 +10726,18 @@ version = "46.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029f2eb16f9510a749201e7a2b405aafcc5afcc515509518d2efb17b164ca47e" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "fnv", "futures 0.3.31", "futures-timer", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls", "hyper-util", "num_cpus", "once_cell", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "rustls", "sc-client-api", @@ -10709,7 +10775,7 @@ dependencies = [ "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-block-builder", "sc-chain-spec", "sc-client-api", @@ -10729,7 +10795,7 @@ dependencies = [ "sp-session", "sp-statement-store", "sp-version", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -10765,7 +10831,7 @@ dependencies = [ "governor", "http 1.3.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "ip_network", "jsonrpsee", "log", @@ -10773,7 +10839,7 @@ dependencies = [ "serde", "serde_json", "substrate-prometheus-endpoint", - "tokio 1.47.1", + "tokio 1.48.0", "tower", "tower-http", ] @@ -10792,7 +10858,7 @@ dependencies = [ "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "sc-chain-spec", "sc-client-api", @@ -10808,7 +10874,7 @@ dependencies = [ "sp-version", "substrate-prometheus-endpoint", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", ] @@ -10842,7 +10908,7 @@ dependencies = [ "jsonrpsee", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.8.5", "sc-chain-spec", @@ -10888,7 +10954,7 @@ dependencies = [ "substrate-prometheus-endpoint", "tempfile", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tracing", "tracing-futures", ] @@ -10901,7 +10967,7 @@ checksum = "b81d0da325c141081336e8d8724f5342f2586bbb574fde81f716f7fab447325a" dependencies = [ "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sp-core", ] @@ -10936,7 +11002,7 @@ dependencies = [ "futures 0.3.31", "libp2p", "log", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.8.5", "sc-utils", @@ -10948,17 +11014,18 @@ dependencies = [ [[package]] name = "sc-tracing" -version = "40.0.0" +version = "40.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80e2558a0100794d5b4f4d75cb45cae65c061aaf386fd807d7a7e2c272251d2" +checksum = "b83b0097094cc643a224c093a50eae9b0a9cbfc00288e0544d0e9f0b229dd0bd" dependencies = [ "chrono", "console", + "frame-metadata 23.0.0", "is-terminal", "libc", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rustc-hash 1.1.0", "sc-client-api", "sc-tracing-proc-macro", @@ -10969,6 +11036,7 @@ dependencies = [ "sp-rpc", "sp-runtime", "sp-tracing", + "sp-trie", "thiserror 1.0.69", "tracing", "tracing-log", @@ -10984,7 +11052,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -10996,11 +11064,11 @@ dependencies = [ "async-trait", "futures 0.3.31", "futures-timer", - "indexmap 2.11.4", + "indexmap 2.12.0", "itertools 0.11.0", "linked-hash-map", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sc-client-api", "sc-transaction-pool-api", "sc-utils", @@ -11014,7 +11082,7 @@ dependencies = [ "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-stream", "tracing", ] @@ -11027,7 +11095,7 @@ checksum = "c27a9cb54784cf7a1a607d4314f1a4437279ce6d4070eb810f3e4fbfff9b1ba7" dependencies = [ "async-trait", "futures 0.3.31", - "indexmap 2.11.4", + "indexmap 2.12.0", "log", "parity-scale-codec", "serde", @@ -11047,7 +11115,7 @@ dependencies = [ "futures 0.3.31", "futures-timer", "log", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "prometheus", "sp-arithmetic", ] @@ -11076,7 +11144,7 @@ dependencies = [ "scale-decode-derive", "scale-type-resolver", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -11088,7 +11156,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11103,7 +11171,7 @@ dependencies = [ "scale-encode-derive", "scale-type-resolver", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -11116,7 +11184,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11142,7 +11210,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11164,15 +11232,15 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.106", - "thiserror 2.0.16", + "syn 2.0.110", + "thiserror 2.0.17", ] [[package]] name = "scale-value" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca8b26b451ecb7fd7b62b259fa28add63d12ec49bbcac0e01fcb4b5ae0c09aa" +checksum = "884aab179aba344c67ddcd1d7dd8e3f8fee202f2e570d97ec34ec8688442a5b3" dependencies = [ "base58", "blake2 0.10.6", @@ -11183,7 +11251,7 @@ dependencies = [ "scale-encode", "scale-type-resolver", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "yap", ] @@ -11193,7 +11261,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -11226,12 +11294,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -11347,11 +11409,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -11413,9 +11475,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -11433,22 +11495,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11487,15 +11549,14 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64 0.22.1", "chrono", "hex", - "serde", - "serde_derive", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -11503,14 +11564,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11631,7 +11692,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -11764,7 +11825,7 @@ dependencies = [ "itertools 0.13.0", "log", "lru", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.8.5", "rand_chacha 0.3.1", @@ -11807,12 +11868,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -11822,7 +11883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ "base64 0.22.1", - "bytes 1.10.1", + "bytes 1.11.0", "futures 0.3.31", "http 1.3.1", "httparse", @@ -11866,7 +11927,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -11929,7 +11990,7 @@ checksum = "849f1cfcf170048d59c8d3d1175feea2a5cd41fe39742660b9ed542f0d1be7b0" dependencies = [ "futures 0.3.31", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "schnellru", "sp-api", "sp-consensus", @@ -12068,7 +12129,7 @@ dependencies = [ "merlin", "parity-bip39", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "paste", "primitive-types 0.13.1", "rand 0.8.5", @@ -12114,7 +12175,7 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -12124,7 +12185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "722cbecdbf5b94578137dbd07feb51e95f7de221be0c1ff4dcfe0bb4cd986929" dependencies = [ "kvdb", - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -12135,7 +12196,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -12182,7 +12243,7 @@ version = "41.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f244e9a2818d21220ceb0915ac73a462814a92d0c354a124a818abdb7f4f66" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "docify", "ed25519-dalek", "libsecp256k1", @@ -12221,16 +12282,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "269d0ee360f6d072f9203485afea35583ac151521a525cc48b2a107fc576c2d9" dependencies = [ "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "sp-core", "sp-externalities", ] [[package]] name = "sp-maybe-compressed-blob" -version = "11.0.0" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c768c11afbe698a090386876911da4236af199cd38a5866748df4d8628aeff" +checksum = "c9d204064a17660455603ae152b02fc7ea4cfff2d14796f6483d7a35c4cca336" dependencies = [ "thiserror 1.0.69", "zstd 0.12.4", @@ -12359,7 +12420,7 @@ version = "30.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fcd9c219da8c85d45d5ae1ce80e73863a872ac27424880322903c6ac893c06e" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "impl-trait-for-tuples", "parity-scale-codec", "polkavm-derive 0.24.0", @@ -12384,7 +12445,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -12426,7 +12487,7 @@ dependencies = [ "hash-db", "log", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pretty_assertions", "rand 0.8.5", "smallvec", @@ -12442,9 +12503,9 @@ dependencies = [ [[package]] name = "sp-statement-store" -version = "21.0.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d11b0753df3d68f5bb0f4d0d3975788c3a4dd2d0e479e28b7af17b52f15160" +checksum = "031e2366f3633aac66c223747f47db82d774e726e117776eab66937c0bf9f4a8" dependencies = [ "aes-gcm", "curve25519-dalek", @@ -12548,7 +12609,7 @@ dependencies = [ "memory-db", "nohash-hasher", "parity-scale-codec", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "scale-info", "schnellru", @@ -12592,7 +12653,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -12671,9 +12732,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "staging-xcm" @@ -12758,8 +12819,8 @@ dependencies = [ "bitflags 1.3.2", "cfg_aliases 0.2.1", "libc", - "parking_lot 0.12.4", - "parking_lot_core 0.9.11", + "parking_lot 0.12.5", + "parking_lot_core 0.9.12", "static_init_macro", "winapi", ] @@ -12837,7 +12898,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -12900,12 +12961,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d23e4bc8e910a312820d589047ab683928b761242dbe31dee081fbdb37cbe0be" dependencies = [ "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "prometheus", "thiserror 1.0.69", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -13000,8 +13061,8 @@ dependencies = [ "subxt-macro", "subxt-metadata", "subxt-rpcs", - "thiserror 2.0.16", - "tokio 1.47.1", + "thiserror 2.0.17", + "tokio 1.48.0", "tokio-util", "tracing", "url", @@ -13021,8 +13082,8 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.106", - "thiserror 2.0.16", + "syn 2.0.110", + "thiserror 2.0.17", ] [[package]] @@ -13051,7 +13112,7 @@ dependencies = [ "serde_json", "sp-crypto-hashing", "subxt-metadata", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -13066,8 +13127,8 @@ dependencies = [ "serde", "serde_json", "smoldot-light", - "thiserror 2.0.16", - "tokio 1.47.1", + "thiserror 2.0.17", + "tokio 1.48.0", "tokio-stream", "tracing", ] @@ -13085,7 +13146,7 @@ dependencies = [ "scale-typegen", "subxt-codegen", "subxt-utils-fetchmetadata", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13100,7 +13161,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-crypto-hashing", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -13121,7 +13182,7 @@ dependencies = [ "serde_json", "subxt-core", "subxt-lightclient", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "url", ] @@ -13152,7 +13213,7 @@ dependencies = [ "sha2 0.10.9", "sp-crypto-hashing", "subxt-core", - "thiserror 2.0.16", + "thiserror 2.0.17", "zeroize", ] @@ -13164,7 +13225,7 @@ checksum = "fc868b55fe2303788dc7703457af390111940c3da4714b510983284501780ed5" dependencies = [ "hex", "parity-scale-codec", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -13180,9 +13241,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -13191,14 +13252,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b198d366dbec045acfcd97295eb653a7a2b40e4dc764ef1e79aafcad439d3c" +checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13227,7 +13288,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13262,7 +13323,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -13307,15 +13368,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.22.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -13354,11 +13415,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -13369,18 +13430,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13490,9 +13551,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -13536,22 +13597,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", - "bytes 1.10.1", - "io-uring", + "bytes 1.11.0", "libc", "mio", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite 0.2.16", "signal-hook-registry", - "slab", - "socket2 0.6.0", - "tokio-macros 2.5.0", - "windows-sys 0.59.0", + "socket2 0.6.1", + "tokio-macros 2.6.0", + "windows-sys 0.61.2", ] [[package]] @@ -13567,23 +13625,23 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "tokio-rustls" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -13594,7 +13652,7 @@ checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite 0.2.16", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-util", ] @@ -13609,23 +13667,23 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-pki-types", - "tokio 1.47.1", + "tokio 1.48.0", "tokio-rustls", "tungstenite", ] [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "futures-core", "futures-io", "futures-sink", "pin-project-lite 0.2.16", - "tokio 1.47.1", + "tokio 1.48.0", ] [[package]] @@ -13660,9 +13718,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] @@ -13673,7 +13731,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.0", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -13683,21 +13741,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.11.4", - "toml_datetime 0.7.2", + "indexmap 2.12.0", + "toml_datetime 0.7.3", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] @@ -13729,8 +13787,8 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.9.4", - "bytes 1.10.1", + "bitflags 2.10.0", + "bytes 1.11.0", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -13771,7 +13829,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13816,7 +13874,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -13832,15 +13890,15 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "parking_lot 0.12.4", - "regex", + "parking_lot 0.12.5", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -13925,7 +13983,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "data-encoding", "http 1.3.1", "httparse", @@ -13934,7 +13992,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", "utf-8", ] @@ -13959,9 +14017,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -14001,9 +14059,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" @@ -14022,9 +14080,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -14059,7 +14117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" dependencies = [ "asynchronous-codec 0.6.2", - "bytes 1.10.1", + "bytes 1.11.0", "futures-io", "futures-util", ] @@ -14070,7 +14128,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" dependencies = [ - "bytes 1.10.1", + "bytes 1.11.0", "tokio-util", ] @@ -14122,7 +14180,7 @@ version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "serde", "wasm-bindgen", @@ -14254,15 +14312,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -14278,14 +14327,14 @@ version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" dependencies = [ - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -14294,25 +14343,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -14323,9 +14358,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -14333,22 +14368,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.110", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -14673,9 +14708,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -14697,14 +14732,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.2", + "webpki-root-certs 1.0.4", ] [[package]] name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" dependencies = [ "rustls-pki-types", ] @@ -14727,9 +14762,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -14753,7 +14788,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -14782,28 +14817,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - [[package]] name = "windows-core" version = "0.52.0" @@ -14825,84 +14838,44 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", + "windows-link", + "windows-result 0.4.1", + "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - -[[package]] -name = "windows-numerics" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" @@ -14915,38 +14888,20 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" -dependencies = [ - "windows-link 0.2.0", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -14991,16 +14946,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -15051,28 +15006,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -15095,9 +15041,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -15119,9 +15065,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -15143,9 +15089,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -15155,9 +15101,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -15179,9 +15125,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -15203,9 +15149,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -15227,9 +15173,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -15251,9 +15197,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -15282,9 +15228,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -15337,7 +15283,7 @@ dependencies = [ "nom", "oid-registry 0.8.1", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -15350,14 +15296,14 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmltree" @@ -15377,7 +15323,7 @@ dependencies = [ "futures 0.3.31", "log", "nohash-hasher", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.8.5", "static_assertions", @@ -15385,14 +15331,14 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dd50a6d6115feb3e5d7d0efd45e8ca364b6c83722c1e9c602f5764e0e9597" +checksum = "deab71f2e20691b4728b349c6cee8fc7223880fa67b6b4f92225ec32225447e5" dependencies = [ "futures 0.3.31", "log", "nohash-hasher", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project", "rand 0.9.2", "static_assertions", @@ -15422,11 +15368,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -15434,13 +15379,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure 0.13.2", ] @@ -15461,7 +15406,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -15481,15 +15426,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure 0.13.2", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -15502,14 +15447,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -15518,9 +15463,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -15529,13 +15474,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f57e846a..b954d7e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "client/network", "miner-api", "node", - "pallets/balances", "pallets/merkle-airdrop", "pallets/mining-rewards", "pallets/qpow", @@ -22,6 +21,7 @@ members = [ "primitives/consensus/pow", "primitives/consensus/qpow", "primitives/dilithium-crypto", + "primitives/header", "primitives/scheduler", "primitives/state-machine", "primitives/trie", @@ -84,6 +84,8 @@ names = { version = "0.14.0", default-features = false } nohash-hasher = { version = "0.2.0" } num-traits = { version = "0.2", default-features = false, features = ["libm"] } once_cell = { version = "1.21.3" } +p3-field = { version = "0.3.0" } +p3-goldilocks = { version = "0.3.0" } parking_lot = { version = "0.12.1", default-features = false } partial_sort = { version = "0.2.0" } paste = { version = "1.0.15", default-features = false } @@ -127,7 +129,7 @@ wasm-timer = { version = "0.2.5" } zeroize = { version = "1.7.0", default-features = false } # Own dependencies -pallet-balances = { path = "./pallets/balances", default-features = false } +pallet-balances = { version = "42.0.0", default-features = false } pallet-merkle-airdrop = { path = "./pallets/merkle-airdrop", default-features = false } pallet-mining-rewards = { path = "./pallets/mining-rewards", default-features = false } pallet-qpow = { path = "./pallets/qpow", default-features = false } @@ -135,6 +137,7 @@ pallet-reversible-transfers = { path = "./pallets/reversible-transfers", default pallet-scheduler = { path = "./pallets/scheduler", default-features = false } pallet-wormhole = { path = "./pallets/wormhole", default-features = false } qp-dilithium-crypto = { path = "./primitives/dilithium-crypto", version = "0.2.0", default-features = false } +qp-header = { path = "./primitives/header", default-features = false } qp-scheduler = { path = "./primitives/scheduler", default-features = false } qp-wormhole = { path = "./primitives/wormhole", default-features = false } qpow-math = { path = "./qpow-math", default-features = false } @@ -145,16 +148,20 @@ sp-consensus-pow = { path = "./primitives/consensus/pow", default-features = fal sp-consensus-qpow = { path = "./primitives/consensus/qpow", default-features = false } # Quantus network dependencies -qp-poseidon = { version = "1.0.1", default-features = false } -qp-poseidon-core = { version = "1.0.1", default-features = false, features = ["p3"] } +qp-plonky2 = { version = "1.1.3", default-features = false } +qp-poseidon = { version = "1.0.6", default-features = false } +qp-poseidon-core = { version = "1.0.6", default-features = false, features = ["p2", "p3"] } qp-rusty-crystals-dilithium = { version = "2.0.0", default-features = false } qp-rusty-crystals-hdwallet = { version = "1.0.0" } -qp-wormhole-circuit = { version = "0.1.2", default-features = false } -qp-wormhole-circuit-builder = { version = "0.1.2", default-features = false } -qp-wormhole-verifier = { version = "0.1.2", default-features = false, features = [ +qp-wormhole-circuit = { version = "0.1.7", default-features = false } +qp-wormhole-circuit-builder = { version = "0.1.7", default-features = false } +qp-wormhole-prover = { version = "0.1.7", default-features = false, features = [ "no_random", ] } -qp-zk-circuits-common = { version = "0.1.2", default-features = false, features = [ +qp-wormhole-verifier = { version = "0.1.7", default-features = false, features = [ + "no_random", +] } +qp-zk-circuits-common = { version = "0.1.7", default-features = false, features = [ "no_random", ] } @@ -236,7 +243,6 @@ sc-network = { path = "client/network" } sp-state-machine = { path = "./primitives/state-machine" } sp-trie = { path = "./primitives/trie" } - [profile.release] opt-level = 3 panic = "unwind" diff --git a/node/build.rs b/node/build.rs index 2de7de2a..6b47474f 100644 --- a/node/build.rs +++ b/node/build.rs @@ -33,8 +33,9 @@ fn generate_circuit_binaries() { // Call the circuit-builder to generate binaries directly in the pallet directory // We don't need the prover binary for the chain, only verifier and common - qp_wormhole_circuit_builder::generate_circuit_binaries("../pallets/wormhole", false) - .expect("Failed to generate circuit binaries"); + // TODO: uncomment this once `no_random` issue is fixed in zk-circuits + // qp_wormhole_circuit_builder::generate_circuit_binaries("../pallets/wormhole", false) + // .expect("Failed to generate circuit binaries"); println!("cargo:trace=✅ Circuit binaries generated successfully"); } diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs index 2d3d33eb..2dd9a8ef 100644 --- a/node/src/benchmarking.rs +++ b/node/src/benchmarking.rs @@ -127,6 +127,9 @@ pub fn create_benchmark_extrinsic( quantus_runtime::transaction_extensions::ReversibleTransactionExtension::< runtime::Runtime, >::new(), + quantus_runtime::transaction_extensions::WormholeProofRecorderExtension::< + runtime::Runtime, + >::new(), ); let raw_payload = runtime::SignedPayload::from_raw( @@ -143,6 +146,7 @@ pub fn create_benchmark_extrinsic( (), None, (), + (), ), ); let signature = raw_payload.using_encoded(|e| sender.sign(e)); diff --git a/pallets/balances/Cargo.toml b/pallets/balances/Cargo.toml deleted file mode 100644 index 686b4e18..00000000 --- a/pallets/balances/Cargo.toml +++ /dev/null @@ -1,65 +0,0 @@ -[package] -authors.workspace = true -description = "FRAME pallet to manage balances" -edition.workspace = true -homepage.workspace = true -license = "Apache-2.0" -name = "pallet-balances" -readme = "README.md" -repository.workspace = true -version = "40.0.1" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { features = ["derive", "max-encoded-len"], workspace = true } -docify = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true -log.workspace = true -qp-poseidon = { workspace = true, features = ["serde"] } -qp-wormhole = { workspace = true } -scale-info = { features = ["derive"], workspace = true } -sp-metadata-ir = { workspace = true } -sp-runtime.workspace = true - -[dev-dependencies] -frame-support = { workspace = true, features = ["experimental"], default-features = true } -pallet-transaction-payment.features = ["std"] -pallet-transaction-payment.workspace = true -paste.workspace = true -sp-core.workspace = true -sp-io.workspace = true - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-transaction-payment/std", - "qp-poseidon/std", - "qp-wormhole/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-metadata-ir/std", - "sp-runtime/std", -] -# Enable support for setting the existential deposit to zero. -insecure_zero_ed = [] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/pallets/balances/README.md b/pallets/balances/README.md deleted file mode 100644 index b1e9c82f..00000000 --- a/pallets/balances/README.md +++ /dev/null @@ -1,127 +0,0 @@ -# Balances Module - -The Balances module provides functionality for handling accounts and balances. - -- [`Config`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/trait.Config.html) -- [`Call`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/enum.Call.html) -- [`Pallet`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/struct.Pallet.html) - -## Overview - -The Balances module provides functions for: - -- Getting and setting free balances. -- Retrieving total, reserved and unreserved balances. -- Repatriating a reserved balance to a beneficiary account that exists. -- Transferring a balance between accounts (when not reserved). -- Slashing an account balance. -- Account creation and removal. -- Managing total issuance. -- Setting and managing locks. - -### Terminology - -- **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents "dust accounts" -from filling storage. When the free plus the reserved balance (i.e. the total balance) fall below this, then the account - is said to be dead; and it loses its functionality as well as any prior history and all information on it is removed - from the chain's state. No account should ever have a total balance that is strictly between 0 and the existential - deposit (exclusive). If this ever happens, it indicates either a bug in this module or an erroneous raw mutation of - storage. - -- **Total Issuance:** The total number of units in existence in a system. - -- **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its total balance has -become zero (or, strictly speaking, less than the Existential Deposit). - -- **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters for - most operations. - -- **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance can - still be slashed, but only after all the free balance has been slashed. - -- **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting (i.e. a -difference between total issuance and account balances). Functions that result in an imbalance will return an object of -the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is simply dropped, it should -automatically maintain any book-keeping such as total issuance.) - -- **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple locks -always operate over the same funds, so they "overlay" rather than "stack". - -### Implementations - -The Balances module provides implementations for the following traits. If these traits provide the functionality that -you need, then you can avoid coupling with the Balances module. - -- [`Currency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Currency.html): Functions for dealing -with a fungible assets system. -- [`ReservableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.ReservableCurrency.html): -Functions for dealing with assets that can be reserved from an account. -- [`LockableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.LockableCurrency.html): Functions -for dealing with accounts that allow liquidity restrictions. -- [`Imbalance`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Imbalance.html): Functions for handling -imbalances between total issuance in the system and account balances. Must be used when a function creates new funds -(e.g. a reward) or destroys some funds (e.g. a system fee). -- [`IsDeadAccount`](https://docs.rs/frame-support/latest/frame_support/traits/trait.IsDeadAccount.html): Determiner to -say whether a given account is unused. - -## Interface - -### Dispatchable Functions - -- `transfer` - Transfer some liquid free balance to another account. -- `force_set_balance` - Set the balances of a given account. The origin of this call must be root. - -## Usage - -The following examples show how to use the Balances module in your custom module. - -### Examples from the FRAME - -The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`: - -```rust -use frame_support::traits::Currency; - -pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; - -``` - -The Staking module uses the `LockableCurrency` trait to lock a stash account's funds: - -```rust -use frame_support::traits::{WithdrawReasons, LockableCurrency}; -use sp_runtime::traits::Bounded; -pub trait Config: frame_system::Config { - type Currency: LockableCurrency>; -} - -fn update_ledger( - controller: &T::AccountId, - ledger: &StakingLedger -) { - T::Currency::set_lock( - STAKING_ID, - &ledger.stash, - ledger.total, - WithdrawReasons::all() - ); - // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. -} -``` - -## Genesis config - -The Balances module depends on the -[`GenesisConfig`](https://docs.rs/pallet-balances/latest/pallet_balances/pallet/struct.GenesisConfig.html). - -## Assumptions - -- Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. - -License: Apache-2.0 - - -## Release - -Polkadot SDK Stable 2412 diff --git a/pallets/balances/src/benchmarking.rs b/pallets/balances/src/benchmarking.rs deleted file mode 100644 index 06f3d24d..00000000 --- a/pallets/balances/src/benchmarking.rs +++ /dev/null @@ -1,350 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Balances pallet benchmarking. - -#![cfg(feature = "runtime-benchmarks")] - -use super::*; -use crate::Pallet as Balances; - -use frame_benchmarking::v2::*; -use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; -use types::ExtraFlags; - -const SEED: u32 = 0; -// existential deposit multiplier -const ED_MULTIPLIER: u32 = 10; - -fn minimum_balance, I: 'static>() -> T::Balance { - if cfg!(feature = "insecure_zero_ed") { - 100u32.into() - } else { - T::ExistentialDeposit::get() - } -} - -#[instance_benchmarks] -mod benchmarks { - use super::*; - - // Benchmark `transfer` extrinsic with the worst possible conditions: - // * Transfer will kill the sender account. - // * Transfer will create the recipient account. - #[benchmark] - fn transfer_allow_death() { - let existential_deposit: T::Balance = minimum_balance::(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()).max(1u32.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `transfer` with the best possible condition: - // * Both accounts exist and will continue to exist. - #[benchmark(extra)] - fn transfer_best_case() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give the sender account max funds for transfer (their account will never reasonably be - // killed). - let _ = - as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - - // Give the recipient account existential deposit (thus their account already exists). - let existential_deposit: T::Balance = minimum_balance::(); - let _ = - as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); - let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - - #[extrinsic_call] - transfer_allow_death(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert!(!Balances::::free_balance(&caller).is_zero()); - assert!(!Balances::::free_balance(&recipient).is_zero()); - } - - // Benchmark `transfer_keep_alive` with the worst possible condition: - // * The recipient account is created. - #[benchmark] - fn transfer_keep_alive() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give the sender account max funds, thus a transfer will not kill account. - let _ = - as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - let existential_deposit: T::Balance = minimum_balance::(); - let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert!(!Balances::::free_balance(&caller).is_zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `force_set_balance` coming from ROOT account. This always creates an account. - #[benchmark] - fn force_set_balance_creating() { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give the user some initial balance. - let existential_deposit: T::Balance = minimum_balance::(); - let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - - #[extrinsic_call] - force_set_balance(RawOrigin::Root, user_lookup, balance_amount); - - assert_eq!(Balances::::free_balance(&user), balance_amount); - } - - // Benchmark `force_set_balance` coming from ROOT account. This always kills an account. - #[benchmark] - fn force_set_balance_killing() { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give the user some initial balance. - let existential_deposit: T::Balance = minimum_balance::(); - let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - - #[extrinsic_call] - force_set_balance(RawOrigin::Root, user_lookup, Zero::zero()); - - assert!(Balances::::free_balance(&user).is_zero()); - } - - // Benchmark `force_transfer` extrinsic with the worst possible conditions: - // * Transfer will kill the sender account. - // * Transfer will create the recipient account. - #[benchmark] - fn force_transfer() { - let existential_deposit: T::Balance = minimum_balance::(); - let source: T::AccountId = account("source", 0, SEED); - let source_lookup = T::Lookup::unlookup(source.clone()); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&source, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - #[extrinsic_call] - _(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&source), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // This benchmark performs the same operation as `transfer` in the worst case scenario, - // but additionally introduces many new users into the storage, increasing the the merkle - // trie and PoV size. - #[benchmark(extra)] - fn transfer_increasing_users(u: Linear<0, 1_000>) { - // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let existential_deposit: T::Balance = minimum_balance::(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - // Create a bunch of users in storage. - for i in 0..u { - // The `account` function uses `blake2_256` to generate unique accounts, so these - // should be quite random and evenly distributed in the trie. - let new_user: T::AccountId = account("new_user", i, SEED); - let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); - } - - #[extrinsic_call] - transfer_allow_death(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `transfer_all` with the worst possible condition: - // * The recipient account is created - // * The sender is killed - #[benchmark] - fn transfer_all() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give some multiple of the existential deposit - let existential_deposit: T::Balance = minimum_balance::(); - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), recipient_lookup, false); - - assert!(Balances::::free_balance(&caller).is_zero()); - assert_eq!(Balances::::free_balance(&recipient), balance); - } - - #[benchmark] - fn force_unreserve() -> Result<(), BenchmarkError> { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give some multiple of the existential deposit - let ed = minimum_balance::(); - let balance = ed + ed; - let _ = as Currency<_>>::make_free_balance_be(&user, balance); - - // Reserve the balance - as ReservableCurrency<_>>::reserve(&user, ed)?; - assert_eq!(Balances::::reserved_balance(&user), ed); - assert_eq!(Balances::::free_balance(&user), ed); - - #[extrinsic_call] - _(RawOrigin::Root, user_lookup, balance); - - assert!(Balances::::reserved_balance(&user).is_zero()); - assert_eq!(Balances::::free_balance(&user), ed + ed); - - Ok(()) - } - - #[benchmark] - fn upgrade_accounts(u: Linear<1, 1_000>) { - let caller: T::AccountId = whitelisted_caller(); - let who = (0..u) - .map(|i| -> T::AccountId { - let user = account("old_user", i, SEED); - let account = AccountData { - free: minimum_balance::(), - reserved: minimum_balance::(), - frozen: Zero::zero(), - flags: ExtraFlags::old_logic(), - }; - frame_system::Pallet::::inc_providers(&user); - assert!(T::AccountStore::try_mutate_exists(&user, |a| -> DispatchResult { - *a = Some(account); - Ok(()) - }) - .is_ok()); - assert!(!Balances::::account(&user).flags.is_new_logic()); - assert_eq!(frame_system::Pallet::::providers(&user), 1); - assert_eq!(frame_system::Pallet::::consumers(&user), 0); - user - }) - .collect(); - - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), who); - - for i in 0..u { - let user: T::AccountId = account("old_user", i, SEED); - assert!(Balances::::account(&user).flags.is_new_logic()); - assert_eq!(frame_system::Pallet::::providers(&user), 1); - assert_eq!(frame_system::Pallet::::consumers(&user), 1); - } - } - - #[benchmark] - fn force_adjust_total_issuance() { - let ti = TotalIssuance::::get(); - let delta = 123u32.into(); - - #[extrinsic_call] - _(RawOrigin::Root, AdjustmentDirection::Increase, delta); - - assert_eq!(TotalIssuance::::get(), ti + delta); - } - - /// Benchmark `burn` extrinsic with the worst possible condition - burn kills the account. - #[benchmark] - fn burn_allow_death() { - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Burn enough to kill the account. - let burn_amount = balance - existential_deposit + 1u32.into(); - - #[extrinsic_call] - burn(RawOrigin::Signed(caller.clone()), burn_amount, false); - - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - } - - // Benchmark `burn` extrinsic with the case where account is kept alive. - #[benchmark] - fn burn_keep_alive() { - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Burn minimum possible amount which should not kill the account. - let burn_amount = 1u32.into(); - - #[extrinsic_call] - burn(RawOrigin::Signed(caller.clone()), burn_amount, true); - - assert_eq!(Balances::::free_balance(&caller), balance - burn_amount); - } - - impl_benchmark_test_suite! { - Balances, - crate::tests::ExtBuilder::default().build(), - crate::tests::Test, - } -} diff --git a/pallets/balances/src/impl_currency.rs b/pallets/balances/src/impl_currency.rs deleted file mode 100644 index 547cca75..00000000 --- a/pallets/balances/src/impl_currency.rs +++ /dev/null @@ -1,952 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementations for the `Currency` family of traits. -//! -//! Note that `WithdrawReasons` are intentionally not used for anything in this implementation and -//! are expected to be removed in the near future, once migration to `fungible::*` traits is done. - -use super::*; -use core::cmp::Ordering; -use frame_support::{ - ensure, - pallet_prelude::DispatchResult, - traits::{ - tokens::{ - fungible, Balance, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort, - }, - Currency, DefensiveSaturating, - ExistenceRequirement::{self, AllowDeath}, - Get, Imbalance, InspectLockableCurrency, LockIdentifier, LockableCurrency, - NamedReservableCurrency, ReservableCurrency, SignedImbalance, TryDrop, WithdrawReasons, - }, -}; -use frame_system::pallet_prelude::BlockNumberFor; -pub use imbalances::{NegativeImbalance, PositiveImbalance}; -use sp_runtime::traits::Bounded; - -// wrapping these imbalances in a private module is necessary to ensure absolute privacy -// of the inner member. -mod imbalances { - use super::*; - use core::mem; - use frame_support::traits::{tokens::imbalance::TryMerge, SameOrOther}; - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been created without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct PositiveImbalance, I: 'static = ()>(T::Balance); - - impl, I: 'static> PositiveImbalance { - /// Create a new positive imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - PositiveImbalance(amount) - } - } - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been destroyed without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct NegativeImbalance, I: 'static = ()>(T::Balance); - - impl, I: 'static> NegativeImbalance { - /// Create a new negative imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - NegativeImbalance(amount) - } - } - - impl, I: 'static> TryDrop for PositiveImbalance { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl, I: 'static> Default for PositiveImbalance { - fn default() -> Self { - Self::zero() - } - } - - impl, I: 'static> Imbalance for PositiveImbalance { - type Opposite = NegativeImbalance; - - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self(first), Self(second)) - } - fn extract(&mut self, amount: T::Balance) -> Self { - let new = self.0.min(amount); - self.0 -= new; - Self(new) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> SameOrOther { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - match a.cmp(&b) { - Ordering::Greater => SameOrOther::Same(Self(a - b)), - Ordering::Less => SameOrOther::Other(NegativeImbalance::new(b - a)), - Ordering::Equal => SameOrOther::None, - } - } - fn peek(&self) -> T::Balance { - self.0 - } - } - - impl, I: 'static> TryMerge for PositiveImbalance { - fn try_merge(self, other: Self) -> Result { - Ok(self.merge(other)) - } - } - - impl, I: 'static> TryDrop for NegativeImbalance { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl, I: 'static> Default for NegativeImbalance { - fn default() -> Self { - Self::zero() - } - } - - impl, I: 'static> Imbalance for NegativeImbalance { - type Opposite = PositiveImbalance; - - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self(first), Self(second)) - } - fn extract(&mut self, amount: T::Balance) -> Self { - let new = self.0.min(amount); - self.0 -= new; - Self(new) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> SameOrOther { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - match a.cmp(&b) { - Ordering::Greater => SameOrOther::Same(Self(a - b)), - Ordering::Less => SameOrOther::Other(PositiveImbalance::new(b - a)), - Ordering::Equal => SameOrOther::None, - } - } - fn peek(&self) -> T::Balance { - self.0 - } - } - - impl, I: 'static> TryMerge for NegativeImbalance { - fn try_merge(self, other: Self) -> Result { - Ok(self.merge(other)) - } - } - - impl, I: 'static> Drop for PositiveImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - if !self.0.is_zero() { - >::mutate(|v| *v = v.saturating_add(self.0)); - Pallet::::deposit_event(Event::::Issued { amount: self.0 }); - } - } - } - - impl, I: 'static> Drop for NegativeImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - if !self.0.is_zero() { - >::mutate(|v| *v = v.saturating_sub(self.0)); - Pallet::::deposit_event(Event::::Rescinded { amount: self.0 }); - } - } - } -} - -impl, I: 'static> Currency for Pallet -where - T::Balance: Balance, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).total() - } - - // Check if `value` amount of free balance can be slashed from `who`. - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true; - } - Self::free_balance(who) >= value - } - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - - fn active_issuance() -> Self::Balance { - >::active_issuance() - } - - fn deactivate(amount: Self::Balance) { - >::deactivate(amount); - } - - fn reactivate(amount: Self::Balance) { - >::reactivate(amount); - } - - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - - // Burn funds from the total issuance, returning a positive imbalance for the amount burned. - // Is a no-op if amount to be burned is zero. - fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { - if amount.is_zero() { - return PositiveImbalance::zero(); - } - >::mutate(|issued| { - *issued = issued.checked_sub(&amount).unwrap_or_else(|| { - amount = *issued; - Zero::zero() - }); - }); - - Pallet::::deposit_event(Event::::Rescinded { amount }); - PositiveImbalance::new(amount) - } - - // Create new funds into the total issuance, returning a negative imbalance - // for the amount issued. - // Is a no-op if amount to be issued it zero. - fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { - if amount.is_zero() { - return NegativeImbalance::zero(); - } - >::mutate(|issued| { - *issued = issued.checked_add(&amount).unwrap_or_else(|| { - amount = Self::Balance::max_value() - *issued; - Self::Balance::max_value() - }) - }); - - Pallet::::deposit_event(Event::::Issued { amount }); - NegativeImbalance::new(amount) - } - - fn free_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).free - } - - // Ensure that an account can withdraw from their free balance given any existing withdrawal - // restrictions like locks and vesting balance. - // Is a no-op if amount to be withdrawn is zero. - fn ensure_can_withdraw( - who: &T::AccountId, - amount: T::Balance, - _reasons: WithdrawReasons, - new_balance: T::Balance, - ) -> DispatchResult { - if amount.is_zero() { - return Ok(()); - } - ensure!(new_balance >= Self::account(who).frozen, Error::::LiquidityRestrictions); - Ok(()) - } - - // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. - // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. - fn transfer( - transactor: &T::AccountId, - dest: &T::AccountId, - value: Self::Balance, - existence_requirement: ExistenceRequirement, - ) -> DispatchResult { - if value.is_zero() || transactor == dest { - return Ok(()); - } - let keep_alive = match existence_requirement { - ExistenceRequirement::KeepAlive => Preserve, - ExistenceRequirement::AllowDeath => Expendable, - }; - >::transfer(transactor, dest, value, keep_alive)?; - Ok(()) - } - - /// Slash a target account `who`, returning the negative imbalance created and any left over - /// amount that could not be slashed. - /// - /// Is a no-op if `value` to be slashed is zero or the account does not exist. - /// - /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn - /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid - /// having to draw from reserved funds, however we err on the side of punishment if things are - /// inconsistent or `can_slash` wasn't used appropriately. - fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()); - } - if Self::total_balance(who).is_zero() { - return (NegativeImbalance::zero(), value); - } - - match Self::try_mutate_account_handling_dust( - who, - |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { - // Best value is the most amount we can slash following liveness rules. - let ed = T::ExistentialDeposit::get(); - let actual = match system::Pallet::::can_dec_provider(who) { - true => value.min(account.free), - false => value.min(account.free.saturating_sub(ed)), - }; - account.free.saturating_reduce(actual); - let remaining = value.saturating_sub(actual); - Ok((NegativeImbalance::new(actual), remaining)) - }, - ) { - Ok((imbalance, remaining)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(remaining), - }); - (imbalance, remaining) - }, - Err(_) => (Self::NegativeImbalance::zero(), value), - } - } - - /// Deposit some `value` into the free balance of an existing target account `who`. - /// - /// Is a no-op if the `value` to be deposited is zero. - fn deposit_into_existing( - who: &T::AccountId, - value: Self::Balance, - ) -> Result { - if value.is_zero() { - return Ok(PositiveImbalance::zero()); - } - - Self::try_mutate_account_handling_dust( - who, - |account, is_new| -> Result { - ensure!(!is_new, Error::::DeadAccount); - account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); - Ok(PositiveImbalance::new(value)) - }, - ) - } - - /// Deposit some `value` into the free balance of `who`, possibly creating a new account. - /// - /// This function is a no-op if: - /// - the `value` to be deposited is zero; or - /// - the `value` to be deposited is less than the required ED and the account does not yet - /// exist; or - /// - the deposit would necessitate the account to exist and there are no provider references; - /// or - /// - `value` is so large it would cause the balance of `who` to overflow. - fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - if value.is_zero() { - return Self::PositiveImbalance::zero(); - } - - Self::try_mutate_account_handling_dust( - who, - |account, is_new| -> Result { - let ed = T::ExistentialDeposit::get(); - ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); - - // defensive only: overflow should never happen, however in case it does, then this - // operation is a no-op. - account.free = match account.free.checked_add(&value) { - Some(x) => x, - None => return Ok(Self::PositiveImbalance::zero()), - }; - - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); - Ok(PositiveImbalance::new(value)) - }, - ) - .unwrap_or_else(|_| Self::PositiveImbalance::zero()) - } - - /// Withdraw some free balance from an account, respecting existence requirements. - /// - /// Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result { - if value.is_zero() { - return Ok(NegativeImbalance::zero()); - } - - Self::try_mutate_account_handling_dust( - who, - |account, _| -> Result { - let new_free_account = - account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; - - // bail if we need to keep the account alive and this would kill it. - let ed = T::ExistentialDeposit::get(); - let would_be_dead = new_free_account < ed; - let would_kill = would_be_dead && account.free >= ed; - ensure!(liveness == AllowDeath || !would_kill, Error::::Expendability); - - Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; - - account.free = new_free_account; - - Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value }); - Ok(NegativeImbalance::new(value)) - }, - ) - } - - /// Force the new free balance of a target account `who` to some new value `balance`. - fn make_free_balance_be( - who: &T::AccountId, - value: Self::Balance, - ) -> SignedImbalance { - Self::try_mutate_account_handling_dust( - who, - |account, - is_new| - -> Result, DispatchError> { - let ed = T::ExistentialDeposit::get(); - // If we're attempting to set an existing account to less than ED, then - // bypass the entire operation. It's a no-op if you follow it through, but - // since this is an instance where we might account for a negative imbalance - // (in the dust cleaner of set_account) before we account for its actual - // equal and opposite cause (returned as an Imbalance), then in the - // instance that there's no other accounts on the system at all, we might - // underflow the issuance and our arithmetic will be off. - ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); - - let imbalance = if account.free <= value { - SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) - }; - account.free = value; - Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free }); - Ok(imbalance) - }, - ) - .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) - } -} - -impl, I: 'static> ReservableCurrency for Pallet -where - T::Balance: Balance, -{ - /// Check if `who` can reserve `value` from their free balance. - /// - /// Always `true` if value to be reserved is zero. - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true; - } - Self::account(who).free.checked_sub(&value).is_some_and(|new_balance| { - new_balance >= T::ExistentialDeposit::get() && - Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance) - .is_ok() - }) - } - - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).reserved - } - - /// Move `value` from the free balance from `who` to their reserved balance. - /// - /// Is a no-op if value to be reserved is zero. - fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { - if value.is_zero() { - return Ok(()); - } - - Self::try_mutate_account_handling_dust(who, |account, _| -> DispatchResult { - account.free = - account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; - account.reserved = - account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, account.free) - })?; - - Self::deposit_event(Event::Reserved { who: who.clone(), amount: value }); - Ok(()) - } - - /// Unreserve some funds, returning any amount that was unable to be unreserved. - /// - /// Is a no-op if the value to be unreserved is zero or the account does not exist. - /// - /// NOTE: returns amount value which wasn't successfully unreserved. - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { - if value.is_zero() { - return Zero::zero(); - } - if Self::total_balance(who).is_zero() { - return value; - } - - let actual = match Self::mutate_account_handling_dust(who, |account| { - let actual = cmp::min(account.reserved, value); - account.reserved -= actual; - // defensive only: this can never fail since total issuance which is at least - // free+reserved fits into the same data type. - account.free = account.free.defensive_saturating_add(actual); - actual - }) { - Ok(x) => x, - Err(_) => { - // This should never happen since we don't alter the total amount in the account. - // If it ever does, then we should fail gracefully though, indicating that nothing - // could be done. - return value; - }, - }; - - Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual }); - value - actual - } - - /// Slash from reserved balance, returning the negative imbalance created, - /// and any amount that was unable to be slashed. - /// - /// Is a no-op if the value to be slashed is zero or the account does not exist. - fn slash_reserved( - who: &T::AccountId, - value: Self::Balance, - ) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()); - } - if Self::total_balance(who).is_zero() { - return (NegativeImbalance::zero(), value); - } - - // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an - // account is attempted to be illegally destroyed. - - match Self::mutate_account_handling_dust(who, |account| { - let actual = value.min(account.reserved); - account.reserved.saturating_reduce(actual); - - // underflow should never happen, but it if does, there's nothing to be done here. - (NegativeImbalance::new(actual), value.saturating_sub(actual)) - }) { - Ok((imbalance, not_slashed)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(not_slashed), - }); - (imbalance, not_slashed) - }, - Err(_) => (Self::NegativeImbalance::zero(), value), - } - } - - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - /// - /// This is `Polite` and thus will not repatriate any funds which would lead the total balance - /// to be less than the frozen amount. Returns `Ok` with the actual amount of funds moved, - /// which may be less than `value` since the operation is done an a `BestEffort` basis. - fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: Status, - ) -> Result { - let actual = - Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?; - Ok(value.saturating_sub(actual)) - } -} - -impl, I: 'static> NamedReservableCurrency for Pallet -where - T::Balance: Balance, -{ - type ReserveIdentifier = T::ReserveIdentifier; - - fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance { - let reserves = Self::reserves(who); - reserves - .binary_search_by_key(id, |data| data.id) - .map(|index| reserves[index].amount) - .unwrap_or_default() - } - - /// Move `value` from the free balance from `who` to a named reserve balance. - /// - /// Is a no-op if value to be reserved is zero. - fn reserve_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> DispatchResult { - if value.is_zero() { - return Ok(()); - } - - Reserves::::try_mutate(who, |reserves| -> DispatchResult { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - reserves[index].amount = reserves[index] - .amount - .checked_add(&value) - .ok_or(ArithmeticError::Overflow)?; - }, - Err(index) => { - reserves - .try_insert(index, ReserveData { id: *id, amount: value }) - .map_err(|_| Error::::TooManyReserves)?; - }, - }; - >::reserve(who, value)?; - Ok(()) - }) - } - - /// Unreserve some funds, returning any amount that was unable to be unreserved. - /// - /// Is a no-op if the value to be unreserved is zero. - fn unreserve_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> Self::Balance { - if value.is_zero() { - return Zero::zero(); - } - - Reserves::::mutate_exists(who, |maybe_reserves| -> Self::Balance { - if let Some(reserves) = maybe_reserves.as_mut() { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let remain = >::unreserve(who, to_change); - - // remain should always be zero but just to be defensive here. - let actual = to_change.defensive_saturating_sub(remain); - - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; - - if reserves[index].amount.is_zero() { - if reserves.len() == 1 { - // no more named reserves - *maybe_reserves = None; - } else { - // remove this named reserve - reserves.remove(index); - } - } - - value - actual - }, - Err(_) => value, - } - } else { - value - } - }) - } - - /// Slash from reserved balance, returning the negative imbalance created, - /// and any amount that was unable to be slashed. - /// - /// Is a no-op if the value to be slashed is zero. - fn slash_reserved_named( - id: &Self::ReserveIdentifier, - who: &T::AccountId, - value: Self::Balance, - ) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()); - } - - Reserves::::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let (imb, remain) = - >::slash_reserved(who, to_change); - - // remain should always be zero but just to be defensive here. - let actual = to_change.defensive_saturating_sub(remain); - - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; - - Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual }); - (imb, value - actual) - }, - Err(_) => (NegativeImbalance::zero(), value), - } - }) - } - - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// If `status` is `Reserved`, the balance will be reserved with given `id`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - fn repatriate_reserved_named( - id: &Self::ReserveIdentifier, - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: Status, - ) -> Result { - if value.is_zero() { - return Ok(Zero::zero()); - } - - if slashed == beneficiary { - return match status { - Status::Free => Ok(Self::unreserve_named(id, slashed, value)), - Status::Reserved => - Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))), - }; - } - - Reserves::::try_mutate(slashed, |reserves| -> Result { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let to_change = cmp::min(reserves[index].amount, value); - - let actual = if status == Status::Reserved { - // make it the reserved under same identifier - Reserves::::try_mutate( - beneficiary, - |reserves| -> Result { - match reserves.binary_search_by_key(id, |data| data.id) { - Ok(index) => { - let remain = - >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive - // here. - let actual = to_change.defensive_saturating_sub(remain); - - // this add can't overflow but just to be defensive. - reserves[index].amount = - reserves[index].amount.defensive_saturating_add(actual); - - Ok(actual) - }, - Err(index) => { - let remain = - >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive - // here - let actual = to_change.defensive_saturating_sub(remain); - - reserves - .try_insert( - index, - ReserveData { id: *id, amount: actual }, - ) - .map_err(|_| Error::::TooManyReserves)?; - - Ok(actual) - }, - } - }, - )? - } else { - let remain = >::repatriate_reserved( - slashed, - beneficiary, - to_change, - status, - )?; - - // remain should always be zero but just to be defensive here - to_change.defensive_saturating_sub(remain) - }; - - // `actual <= to_change` and `to_change <= amount`; qed; - reserves[index].amount -= actual; - - Ok(value - actual) - }, - Err(_) => Ok(value), - } - }) - } -} - -impl, I: 'static> LockableCurrency for Pallet -where - T::Balance: Balance, -{ - type Moment = BlockNumberFor; - - type MaxLocks = T::MaxLocks; - - // Set or alter a lock on the balance of `who`. - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - if reasons.is_empty() || amount.is_zero() { - Self::remove_lock(id, who); - return; - } - - let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - Self::update_locks(who, &locks[..]); - } - - // Extend a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - if amount.is_zero() || reasons.is_empty() { - return; - } - let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take().map(|nl| BalanceLock { - id: l.id, - amount: l.amount.max(nl.amount), - reasons: l.reasons | nl.reasons, - }) - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - Self::update_locks(who, &locks[..]); - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let mut locks = Self::locks(who); - locks.retain(|l| l.id != id); - Self::update_locks(who, &locks[..]); - } -} - -impl, I: 'static> InspectLockableCurrency for Pallet { - fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance { - Self::locks(who) - .into_iter() - .filter(|l| l.id == id) - .fold(Zero::zero(), |acc, l| acc + l.amount) - } -} diff --git a/pallets/balances/src/impl_fungible.rs b/pallets/balances/src/impl_fungible.rs deleted file mode 100644 index e5e43e04..00000000 --- a/pallets/balances/src/impl_fungible.rs +++ /dev/null @@ -1,385 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementation of `fungible` traits for Balances pallet. -use super::*; -use frame_support::traits::{ - tokens::{ - Fortitude, - Preservation::{self, Preserve, Protect}, - Provenance::{self, Minted}, - }, - AccountTouch, -}; - -impl, I: 'static> fungible::Inspect for Pallet { - type Balance = T::Balance; - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - fn active_issuance() -> Self::Balance { - TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) - } - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).total() - } - fn balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).free - } - fn reducible_balance( - who: &T::AccountId, - preservation: Preservation, - force: Fortitude, - ) -> Self::Balance { - let a = Self::account(who); - let mut untouchable = Zero::zero(); - if force == Polite { - // Frozen balance applies to total. Anything on hold therefore gets discounted from the - // limit given by the freezes. - untouchable = a.frozen.saturating_sub(a.reserved); - } - // If we want to keep our provider ref.. - if preservation == Preserve - // ..or we don't want the account to die and our provider ref is needed for it to live.. - || preservation == Protect && !a.free.is_zero() && - frame_system::Pallet::::providers(who) == 1 - // ..or we don't care about the account dying but our provider ref is required.. - || preservation == Expendable && !a.free.is_zero() && - !frame_system::Pallet::::can_dec_provider(who) - { - // ..then the ED needed.. - untouchable = untouchable.max(T::ExistentialDeposit::get()); - } - // Liquid balance is what is neither on hold nor frozen/required for provider. - a.free.saturating_sub(untouchable) - } - fn can_deposit( - who: &T::AccountId, - amount: Self::Balance, - provenance: Provenance, - ) -> DepositConsequence { - if amount.is_zero() { - return DepositConsequence::Success; - } - - if provenance == Minted && TotalIssuance::::get().checked_add(&amount).is_none() { - return DepositConsequence::Overflow; - } - - let account = Self::account(who); - let new_free = match account.free.checked_add(&amount) { - None => return DepositConsequence::Overflow, - Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum, - Some(x) => x, - }; - - match account.reserved.checked_add(&new_free) { - Some(_) => {}, - None => return DepositConsequence::Overflow, - }; - - // NOTE: We assume that we are a provider, so don't need to do any checks in the - // case of account creation. - - DepositConsequence::Success - } - fn can_withdraw( - who: &T::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - if amount.is_zero() { - return WithdrawConsequence::Success; - } - - if TotalIssuance::::get().checked_sub(&amount).is_none() { - return WithdrawConsequence::Underflow; - } - - let account = Self::account(who); - let new_free_balance = match account.free.checked_sub(&amount) { - Some(x) => x, - None => return WithdrawConsequence::BalanceLow, - }; - - let liquid = Self::reducible_balance(who, Expendable, Polite); - if amount > liquid { - return WithdrawConsequence::Frozen; - } - - // Provider restriction - total account balance cannot be reduced to zero if it cannot - // sustain the loss of a provider reference. - // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, - // then this will need to adapt accordingly. - let ed = T::ExistentialDeposit::get(); - let success = if new_free_balance < ed { - if frame_system::Pallet::::can_dec_provider(who) { - WithdrawConsequence::ReducedToZero(new_free_balance) - } else { - return WithdrawConsequence::WouldDie; - } - } else { - WithdrawConsequence::Success - }; - - let new_total_balance = new_free_balance.saturating_add(account.reserved); - - // Eventual free funds must be no less than the frozen balance. - if new_total_balance < account.frozen { - return WithdrawConsequence::Frozen; - } - - success - } -} - -impl, I: 'static> fungible::Unbalanced for Pallet { - fn handle_dust(dust: fungible::Dust) { - T::DustRemoval::on_unbalanced(dust.into_credit()); - } - fn write_balance( - who: &T::AccountId, - amount: Self::Balance, - ) -> Result, DispatchError> { - let max_reduction = - >::reducible_balance(who, Expendable, Force); - let (result, maybe_dust) = Self::mutate_account(who, |account| -> DispatchResult { - // Make sure the reduction (if there is one) is no more than the maximum allowed. - let reduction = account.free.saturating_sub(amount); - ensure!(reduction <= max_reduction, Error::::InsufficientBalance); - - account.free = amount; - Ok(()) - })?; - result?; - Ok(maybe_dust) - } - - fn set_total_issuance(amount: Self::Balance) { - TotalIssuance::::mutate(|t| *t = amount); - } - - fn deactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| { - // InactiveIssuance cannot be greater than TotalIssuance. - *b = b.saturating_add(amount).min(TotalIssuance::::get()); - }); - } - - fn reactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); - } -} - -impl, I: 'static> fungible::Mutate for Pallet { - fn done_mint_into(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Minted { who: who.clone(), amount }); - } - fn done_burn_from(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Burned { who: who.clone(), amount }); - } - fn done_shelve(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Suspended { who: who.clone(), amount }); - } - fn done_restore(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Restored { who: who.clone(), amount }); - } - fn done_transfer(source: &T::AccountId, dest: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Transfer { - from: source.clone(), - to: dest.clone(), - amount, - }); - } -} - -impl, I: 'static> fungible::MutateHold for Pallet {} - -impl, I: 'static> fungible::InspectHold for Pallet { - type Reason = T::RuntimeHoldReason; - - fn total_balance_on_hold(who: &T::AccountId) -> T::Balance { - Self::account(who).reserved - } - fn reducible_total_balance_on_hold(who: &T::AccountId, force: Fortitude) -> Self::Balance { - // The total balance must never drop below the freeze requirements if we're not forcing: - let a = Self::account(who); - let unavailable = if force == Force { - Self::Balance::zero() - } else { - // The freeze lock applies to the total balance, so we can discount the free balance - // from the amount which the total reserved balance must provide to satisfy it. - a.frozen.saturating_sub(a.free) - }; - a.reserved.saturating_sub(unavailable) - } - fn balance_on_hold(reason: &Self::Reason, who: &T::AccountId) -> T::Balance { - Holds::::get(who) - .iter() - .find(|x| &x.id == reason) - .map_or_else(Zero::zero, |x| x.amount) - } - fn hold_available(reason: &Self::Reason, who: &T::AccountId) -> bool { - if frame_system::Pallet::::providers(who) == 0 { - return false; - } - let holds = Holds::::get(who); - if holds.is_full() && !holds.iter().any(|x| &x.id == reason) { - return false; - } - true - } -} - -impl, I: 'static> fungible::UnbalancedHold for Pallet { - fn set_balance_on_hold( - reason: &Self::Reason, - who: &T::AccountId, - amount: Self::Balance, - ) -> DispatchResult { - let mut new_account = Self::account(who); - let mut holds = Holds::::get(who); - let mut increase = true; - let mut delta = amount; - - if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) { - delta = item.amount.max(amount) - item.amount.min(amount); - increase = amount > item.amount; - item.amount = amount; - holds.retain(|x| !x.amount.is_zero()); - } else if !amount.is_zero() { - holds - .try_push(IdAmount { id: *reason, amount }) - .map_err(|_| Error::::TooManyHolds)?; - } - - new_account.reserved = if increase { - new_account.reserved.checked_add(&delta).ok_or(ArithmeticError::Overflow)? - } else { - new_account.reserved.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? - }; - - let (result, maybe_dust) = Self::try_mutate_account(who, |a, _| -> DispatchResult { - *a = new_account; - Ok(()) - })?; - debug_assert!( - maybe_dust.is_none(), - "Does not alter main balance; dust only happens when it is altered; qed" - ); - Holds::::insert(who, holds); - Ok(result) - } -} - -impl, I: 'static> fungible::InspectFreeze for Pallet { - type Id = T::FreezeIdentifier; - - fn balance_frozen(id: &Self::Id, who: &T::AccountId) -> Self::Balance { - let locks = Freezes::::get(who); - locks.into_iter().find(|l| &l.id == id).map_or(Zero::zero(), |l| l.amount) - } - - fn can_freeze(id: &Self::Id, who: &T::AccountId) -> bool { - let l = Freezes::::get(who); - !l.is_full() || l.iter().any(|x| &x.id == id) - } -} - -impl, I: 'static> fungible::MutateFreeze for Pallet { - fn set_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult { - if amount.is_zero() { - return Self::thaw(id, who); - } - let mut locks = Freezes::::get(who); - if let Some(i) = locks.iter_mut().find(|x| &x.id == id) { - i.amount = amount; - } else { - locks - .try_push(IdAmount { id: *id, amount }) - .map_err(|_| Error::::TooManyFreezes)?; - } - Self::update_freezes(who, locks.as_bounded_slice()) - } - - fn extend_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult { - if amount.is_zero() { - return Ok(()); - } - let mut locks = Freezes::::get(who); - if let Some(i) = locks.iter_mut().find(|x| &x.id == id) { - i.amount = i.amount.max(amount); - } else { - locks - .try_push(IdAmount { id: *id, amount }) - .map_err(|_| Error::::TooManyFreezes)?; - } - Self::update_freezes(who, locks.as_bounded_slice()) - } - - fn thaw(id: &Self::Id, who: &T::AccountId) -> DispatchResult { - let mut locks = Freezes::::get(who); - locks.retain(|l| &l.id != id); - Self::update_freezes(who, locks.as_bounded_slice()) - } -} - -impl, I: 'static> fungible::Balanced for Pallet { - type OnDropCredit = fungible::DecreaseIssuance; - type OnDropDebt = fungible::IncreaseIssuance; - - fn done_deposit(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Deposit { who: who.clone(), amount }); - } - fn done_withdraw(who: &T::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::::Withdraw { who: who.clone(), amount }); - } - fn done_issue(amount: Self::Balance) { - if !amount.is_zero() { - Self::deposit_event(Event::::Issued { amount }); - } - } - fn done_rescind(amount: Self::Balance) { - Self::deposit_event(Event::::Rescinded { amount }); - } -} - -impl, I: 'static> fungible::BalancedHold for Pallet {} - -impl, I: 'static> - fungible::hold::DoneSlash for Pallet -{ - fn done_slash(reason: &T::RuntimeHoldReason, who: &T::AccountId, amount: T::Balance) { - T::DoneSlashHandler::done_slash(reason, who, amount); - } -} - -impl, I: 'static> AccountTouch<(), T::AccountId> for Pallet { - type Balance = T::Balance; - fn deposit_required(_: ()) -> Self::Balance { - Self::Balance::zero() - } - fn should_touch(_: (), _: &T::AccountId) -> bool { - false - } - fn touch(_: (), _: &T::AccountId, _: &T::AccountId) -> DispatchResult { - Ok(()) - } -} diff --git a/pallets/balances/src/impl_proofs.rs b/pallets/balances/src/impl_proofs.rs deleted file mode 100644 index 517225f5..00000000 --- a/pallets/balances/src/impl_proofs.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::*; -use qp_wormhole::TransferProofs; - -impl, I: 'static> TransferProofs - for Pallet -{ - fn transfer_proof_exists( - count: TransferCountType, - from: &T::AccountId, - to: &T::AccountId, - value: T::Balance, - ) -> bool { - TransferProof::::get((count, from, to, value)).is_some() - } - - fn store_transfer_proof(from: &T::AccountId, to: &T::AccountId, value: T::Balance) { - Pallet::::do_store_transfer_proof(from, to, value); - } - - fn transfer_proof_key( - transfer_count: u64, - from: T::AccountId, - to: T::AccountId, - value: T::Balance, - ) -> Vec { - Pallet::::transfer_proof_storage_key(transfer_count, from, to, value) - } -} diff --git a/pallets/balances/src/lib.rs b/pallets/balances/src/lib.rs deleted file mode 100644 index a0ee17d3..00000000 --- a/pallets/balances/src/lib.rs +++ /dev/null @@ -1,1325 +0,0 @@ -// This file is part of Substrate. - -#![allow(clippy::all)] -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Balances Pallet -//! -//! The Balances pallet provides functionality for handling accounts and balances for a single -//! token. -//! -//! It makes heavy use of concepts such as Holds and Freezes from the -//! [`frame_support::traits::fungible`] traits, therefore you should read and understand those docs -//! as a prerequisite to understanding this pallet. -//! -//! Also see the [`frame_tokens`] reference docs for higher level information regarding the -//! place of this palet in FRAME. -//! -//! ## Overview -//! -//! The Balances pallet provides functions for: -//! -//! - Getting and setting free balances. -//! - Retrieving total, reserved and unreserved balances. -//! - Repatriating a reserved balance to a beneficiary account that exists. -//! - Transferring a balance between accounts (when not reserved). -//! - Slashing an account balance. -//! - Account creation and removal. -//! - Managing total issuance. -//! - Setting and managing locks. -//! -//! ### Terminology -//! -//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after -//! its total balance has become less than the Existential Deposit. -//! -//! ### Implementations -//! -//! The Balances pallet provides implementations for the following [`fungible`] traits. If these -//! traits provide the functionality that you need, then you should avoid tight coupling with the -//! Balances pallet. -//! -//! - [`fungible::Inspect`] -//! - [`fungible::Mutate`] -//! - [`fungible::Unbalanced`] -//! - [`fungible::Balanced`] -//! - [`fungible::BalancedHold`] -//! - [`fungible::InspectHold`] -//! - [`fungible::MutateHold`] -//! - [`fungible::InspectFreeze`] -//! - [`fungible::MutateFreeze`] -//! - [`fungible::Imbalance`] -//! -//! It also implements the following [`Currency`] related traits, however they are deprecated and -//! will eventually be removed. -//! -//! - [`Currency`]: Functions for dealing with a fungible assets system. -//! - [`ReservableCurrency`] -//! - [`NamedReservableCurrency`](frame_support::traits::NamedReservableCurrency): Functions for -//! dealing with assets that can be reserved from an account. -//! - [`LockableCurrency`](frame_support::traits::LockableCurrency): Functions for dealing with -//! accounts that allow liquidity restrictions. -//! - [`Imbalance`](frame_support::traits::Imbalance): Functions for handling imbalances between -//! total issuance in the system and account balances. Must be used when a function creates new -//! funds (e.g. a reward) or destroys some funds (e.g. a system fee). -//! -//! ## Usage -//! -//! The following examples show how to use the Balances pallet in your custom pallet. -//! -//! ### Examples from the FRAME -//! -//! The Contract pallet uses the `Currency` trait to handle gas payment, and its types inherit from -//! `Currency`: -//! -//! ``` -//! use frame_support::traits::Currency; -//! # pub trait Config: frame_system::Config { -//! # type Currency: Currency; -//! # } -//! -//! pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -//! pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; -//! -//! # fn main() {} -//! ``` -//! -//! The Staking pallet uses the `LockableCurrency` trait to lock a stash account's funds: -//! -//! ``` -//! use frame_support::traits::{WithdrawReasons, LockableCurrency}; -//! use sp_runtime::traits::Bounded; -//! pub trait Config: frame_system::Config { -//! type Currency: LockableCurrency>; -//! } -//! # struct StakingLedger { -//! # stash: ::AccountId, -//! # total: <::Currency as frame_support::traits::Currency<::AccountId>>::Balance, -//! # phantom: std::marker::PhantomData, -//! # } -//! # const STAKING_ID: [u8; 8] = *b"staking "; -//! -//! fn update_ledger( -//! controller: &T::AccountId, -//! ledger: &StakingLedger -//! ) { -//! T::Currency::set_lock( -//! STAKING_ID, -//! &ledger.stash, -//! ledger.total, -//! WithdrawReasons::all() -//! ); -//! // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. -//! } -//! # fn main() {} -//! ``` -//! -//! ## Genesis config -//! -//! The Balances pallet depends on the [`GenesisConfig`]. -//! -//! ## Assumptions -//! -//! * Total issued balanced of all accounts should be less than `Config::Balance::max_value()`. -//! * Existential Deposit is set to a value greater than zero. -//! -//! Note, you may find the Balances pallet still functions with an ED of zero when the -//! `insecure_zero_ed` cargo feature is enabled. However this is not a configuration which is -//! generally supported, nor will it be. -//! -//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html - -#![cfg_attr(not(feature = "std"), no_std)] -mod benchmarking; -mod impl_currency; -mod impl_fungible; -mod impl_proofs; -pub mod migration; -mod tests; -mod types; -pub mod weights; - -extern crate alloc; - -use alloc::vec::Vec; -use codec::{Codec, Decode, Encode, MaxEncodedLen}; -use core::{cmp, fmt::Debug, marker::PhantomData, mem, result}; -use frame_support::{ - ensure, - pallet_prelude::DispatchResult, - traits::{ - tokens::{ - fungible, Balance as BalanceT, BalanceStatus as Status, DepositConsequence, - Fortitude::{self, Force, Polite}, - IdAmount, - Preservation::{Expendable, Preserve, Protect}, - WithdrawConsequence, - }, - Currency, Defensive, Get, OnUnbalanced, ReservableCurrency, StoredMap, - }, - BoundedSlice, StorageHasher, WeakBoundedVec, -}; -use frame_system as system; -pub use impl_currency::{NegativeImbalance, PositiveImbalance}; -pub use pallet::*; -use qp_poseidon::PoseidonHasher as PoseidonCore; -use scale_info::TypeInfo; -use sp_metadata_ir::StorageHasherIR; -use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Saturating, - StaticLookup, Zero, - }, - ArithmeticError, DispatchError, FixedPointOperand, Perbill, RuntimeDebug, TokenError, -}; -pub use types::{ - AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, Reasons, ReserveData, -}; -pub use weights::WeightInfo; - -const LOG_TARGET: &str = "runtime::balances"; - -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - -pub struct PoseidonStorageHasher(PhantomData); - -impl StorageHasher - for PoseidonStorageHasher -{ - // We are lying here, but maybe it's ok because it's just metadata - const METADATA: StorageHasherIR = StorageHasherIR::Identity; - type Output = [u8; 32]; - - fn hash(x: &[u8]) -> Self::Output { - PoseidonCore::hash_storage::(x) - } - - fn max_len() -> usize { - 32 - } -} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::{ - pallet_prelude::*, - traits::{fungible::Credit, tokens::Precision, VariantCount, VariantCountOf}, - }; - use frame_system::pallet_prelude::*; - - pub type CreditOf = Credit<::AccountId, Pallet>; - pub type TransferCountType = u64; - - /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. - pub mod config_preludes { - use super::*; - use frame_support::{derive_impl, traits::ConstU64}; - - pub struct TestDefaultConfig; - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] - impl frame_system::DefaultConfig for TestDefaultConfig {} - - #[frame_support::register_default_impl(TestDefaultConfig)] - impl DefaultConfig for TestDefaultConfig { - #[inject_runtime_type] - type RuntimeHoldReason = (); - #[inject_runtime_type] - type RuntimeFreezeReason = (); - - type Balance = u64; - type ExistentialDeposit = ConstU64<1>; - - type ReserveIdentifier = (); - type FreezeIdentifier = Self::RuntimeFreezeReason; - - type DustRemoval = (); - - type MaxLocks = ConstU32<100>; - type MaxReserves = ConstU32<100>; - type MaxFreezes = VariantCountOf; - - type WeightInfo = (); - type DoneSlashHandler = (); - } - } - - #[pallet::config(with_default)] - pub trait Config: frame_system::Config { - /// The overarching hold reason. - #[pallet::no_default_bounds] - type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; - - /// The overarching freeze reason. - #[pallet::no_default_bounds] - type RuntimeFreezeReason: VariantCount; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// The balance of an account. - type Balance: Parameter - + Member - + AtLeast32BitUnsigned - + Codec - + Default - + Copy - + MaybeSerializeDeserialize - + Debug - + MaxEncodedLen - + TypeInfo - + FixedPointOperand - + BalanceT; - - /// Handler for the unbalanced reduction when removing a dust account. - #[pallet::no_default_bounds] - type DustRemoval: OnUnbalanced>; - - /// The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! - /// - /// If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for - /// this pallet. However, you do so at your own risk: this will open up a major DoS vector. - /// In case you have multiple sources of provider references, you may also get unexpected - /// behaviour if you set this to zero. - /// - /// Bottom line: Do yourself a favour and make it at least one! - #[pallet::constant] - #[pallet::no_default_bounds] - type ExistentialDeposit: Get; - - /// The means of storing the balances of an account. - #[pallet::no_default] - type AccountStore: StoredMap>; - - /// The ID type for reserves. - /// - /// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` - type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy; - - /// The ID type for freezes. - type FreezeIdentifier: Parameter + Member + MaxEncodedLen + Copy; - - /// The maximum number of locks that should exist on an account. - /// Not strictly enforced, but used for weight estimation. - /// - /// Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/` - #[pallet::constant] - type MaxLocks: Get; - - /// The maximum number of named reserves that can exist on an account. - /// - /// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` - #[pallet::constant] - type MaxReserves: Get; - - /// The maximum number of individual freeze locks that can exist on an account at any time. - #[pallet::constant] - type MaxFreezes: Get; - - /// Allows callbacks to other pallets so they can update their bookkeeping when a slash - /// occurs. - type DoneSlashHandler: fungible::hold::DoneSlash< - Self::RuntimeHoldReason, - Self::AccountId, - Self::Balance, - >; - } - - /// The in-code storage version. - const STORAGE_VERSION: frame_support::traits::StorageVersion = - frame_support::traits::StorageVersion::new(1); - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// An account was created with some free balance. - Endowed { account: T::AccountId, free_balance: T::Balance }, - /// An account was removed whose balance was non-zero but below ExistentialDeposit, - /// resulting in an outright loss. - DustLost { account: T::AccountId, amount: T::Balance }, - /// Transfer succeeded. - Transfer { from: T::AccountId, to: T::AccountId, amount: T::Balance }, - /// A balance was set by root. - BalanceSet { who: T::AccountId, free: T::Balance }, - /// Some balance was reserved (moved from free to reserved). - Reserved { who: T::AccountId, amount: T::Balance }, - /// Some balance was unreserved (moved from reserved to free). - Unreserved { who: T::AccountId, amount: T::Balance }, - /// Some balance was moved from the reserve of the first account to the second account. - /// Final argument indicates the destination balance type. - ReserveRepatriated { - from: T::AccountId, - to: T::AccountId, - amount: T::Balance, - destination_status: Status, - }, - /// Some amount was deposited (e.g. for transaction fees). - Deposit { who: T::AccountId, amount: T::Balance }, - /// Some amount was withdrawn from the account (e.g. for transaction fees). - Withdraw { who: T::AccountId, amount: T::Balance }, - /// Some amount was removed from the account (e.g. for misbehavior). - Slashed { who: T::AccountId, amount: T::Balance }, - /// Some amount was minted into an account. - Minted { who: T::AccountId, amount: T::Balance }, - /// Some amount was burned from an account. - Burned { who: T::AccountId, amount: T::Balance }, - /// Some amount was suspended from an account (it can be restored later). - Suspended { who: T::AccountId, amount: T::Balance }, - /// Some amount was restored into an account. - Restored { who: T::AccountId, amount: T::Balance }, - /// An account was upgraded. - Upgraded { who: T::AccountId }, - /// Total issuance was increased by `amount`, creating a credit to be balanced. - Issued { amount: T::Balance }, - /// Total issuance was decreased by `amount`, creating a debt to be balanced. - Rescinded { amount: T::Balance }, - /// Some balance was locked. - Locked { who: T::AccountId, amount: T::Balance }, - /// Some balance was unlocked. - Unlocked { who: T::AccountId, amount: T::Balance }, - /// Some balance was frozen. - Frozen { who: T::AccountId, amount: T::Balance }, - /// Some balance was thawed. - Thawed { who: T::AccountId, amount: T::Balance }, - /// The `TotalIssuance` was forcefully changed. - TotalIssuanceForced { old: T::Balance, new: T::Balance }, - } - - #[pallet::error] - pub enum Error { - /// Vesting balance too high to send value. - VestingBalance, - /// Account liquidity restrictions prevent withdrawal. - LiquidityRestrictions, - /// Balance too low to send value. - InsufficientBalance, - /// Value too low to create account due to existential deposit. - ExistentialDeposit, - /// Transfer/payment would kill account. - Expendability, - /// A vesting schedule already exists for this account. - ExistingVestingSchedule, - /// Beneficiary account must pre-exist. - DeadAccount, - /// Number of named reserves exceed `MaxReserves`. - TooManyReserves, - /// Number of holds exceed `VariantCountOf`. - TooManyHolds, - /// Number of freezes exceed `MaxFreezes`. - TooManyFreezes, - /// The issuance cannot be modified since it is already deactivated. - IssuanceDeactivated, - /// The delta cannot be zero. - DeltaZero, - } - - /// The total units issued in the system. - #[pallet::storage] - #[pallet::whitelist_storage] - pub type TotalIssuance, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>; - - /// The total units of outstanding deactivated balance in the system. - #[pallet::storage] - #[pallet::whitelist_storage] - pub type InactiveIssuance, I: 'static = ()> = - StorageValue<_, T::Balance, ValueQuery>; - - /// The Balances pallet example of storing the balance of an account. - /// - /// # Example - /// - /// ```nocompile - /// impl pallet_balances::Config for Runtime { - /// type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData> - /// } - /// ``` - /// - /// You can also store the balance of an account in the `System` pallet. - /// - /// # Example - /// - /// ```nocompile - /// impl pallet_balances::Config for Runtime { - /// type AccountStore = System - /// } - /// ``` - /// - /// But this comes with tradeoffs, storing account balances in the system pallet stores - /// `frame_system` data alongside the account data contrary to storing account balances in the - /// `Balances` pallet, which uses a `StorageMap` to store balances data only. - /// NOTE: This is only used in the case that this pallet is used to store balances. - #[pallet::storage] - pub type Account, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::AccountId, AccountData, ValueQuery>; - - /// Any liquidity locks on some account balances. - /// NOTE: Should only be accessed when setting, changing and freeing a lock. - /// - /// Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/` - #[pallet::storage] - pub type Locks, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - WeakBoundedVec, T::MaxLocks>, - ValueQuery, - >; - - /// Named reserves on some account balances. - /// - /// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` - #[pallet::storage] - pub type Reserves, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BoundedVec, T::MaxReserves>, - ValueQuery, - >; - - /// Holds on account balances. - #[pallet::storage] - pub type Holds, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BoundedVec< - IdAmount, - VariantCountOf, - >, - ValueQuery, - >; - - /// Freeze locks on account balances. - #[pallet::storage] - pub type Freezes, I: 'static = ()> = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BoundedVec, T::MaxFreezes>, - ValueQuery, - >; - - /// Transfer proofs for a wormhole transfers - #[pallet::storage] - #[pallet::getter(fn transfer_proof)] - pub type TransferProof, I: 'static = ()> = StorageMap< - _, - PoseidonStorageHasher, - (u64, T::AccountId, T::AccountId, T::Balance), // (tx_count, from, to, amount) - (), - OptionQuery, // Returns None if not present - >; - - #[pallet::storage] - #[pallet::getter(fn transfer_count)] - pub type TransferCount, I: 'static = ()> = - StorageValue<_, TransferCountType, ValueQuery>; - - #[pallet::genesis_config] - pub struct GenesisConfig, I: 'static = ()> { - pub balances: Vec<(T::AccountId, T::Balance)>, - } - - impl, I: 'static> Default for GenesisConfig { - fn default() -> Self { - Self { balances: Default::default() } - } - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - let total = self.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n); - - >::put(total); - - for (_, balance) in &self.balances { - assert!( - *balance >= >::ExistentialDeposit::get(), - "the balance of any account should always be at least the existential deposit.", - ) - } - - // ensure no duplicates exist. - let endowed_accounts = self - .balances - .iter() - .map(|(x, _)| x) - .cloned() - .collect::>(); - - assert!( - endowed_accounts.len() == self.balances.len(), - "duplicate balances in genesis." - ); - - for &(ref who, free) in self.balances.iter() { - frame_system::Pallet::::inc_providers(who); - assert!(T::AccountStore::insert(who, AccountData { free, ..Default::default() }) - .is_ok()); - } - } - } - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn integrity_test() { - #[cfg(not(feature = "insecure_zero_ed"))] - assert!( - !>::ExistentialDeposit::get().is_zero(), - "The existential deposit must be greater than zero!" - ); - - assert!( - T::MaxFreezes::get() >= ::VARIANT_COUNT, - "MaxFreezes should be greater than or equal to the number of freeze reasons: {} < {}", - T::MaxFreezes::get(), ::VARIANT_COUNT, - ); - } - - #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { - Holds::::iter_keys().try_for_each(|k| { - if Holds::::decode_len(k).unwrap_or(0) > - T::RuntimeHoldReason::VARIANT_COUNT as usize - { - Err("Found `Hold` with too many elements") - } else { - Ok(()) - } - })?; - - Freezes::::iter_keys().try_for_each(|k| { - if Freezes::::decode_len(k).unwrap_or(0) > T::MaxFreezes::get() as usize { - Err("Found `Freeze` with too many elements") - } else { - Ok(()) - } - })?; - - Ok(()) - } - } - - #[pallet::call(weight(>::WeightInfo))] - impl, I: 'static> Pallet { - /// Transfer some liquid free balance to another account. - /// - /// `transfer_allow_death` will set the `FreeBalance` of the sender and receiver. - /// If the sender's account is below the existential deposit as a result - /// of the transfer, the account will be reaped. - /// - /// The dispatch origin for this call must be `Signed` by the transactor. - #[pallet::call_index(0)] - pub fn transfer_allow_death( - origin: OriginFor, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResult { - let source = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer(&source, &dest, value, Expendable)?; - Self::do_store_transfer_proof(&source, &dest, value); - Ok(()) - } - - /// Exactly as `transfer_allow_death`, except the origin must be root and the source account - /// may be specified. - #[pallet::call_index(2)] - pub fn force_transfer( - origin: OriginFor, - source: AccountIdLookupOf, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResult { - ensure_root(origin)?; - let source = T::Lookup::lookup(source)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer(&source, &dest, value, Expendable)?; - Self::do_store_transfer_proof(&source, &dest, value); - Ok(()) - } - - /// Same as the [`transfer_allow_death`] call, but with a check that the transfer will not - /// kill the origin account. - /// - /// 99% of the time you want [`transfer_allow_death`] instead. - /// - /// [`transfer_allow_death`]: struct.Pallet.html#method.transfer - #[pallet::call_index(3)] - pub fn transfer_keep_alive( - origin: OriginFor, - dest: AccountIdLookupOf, - #[pallet::compact] value: T::Balance, - ) -> DispatchResult { - let source = ensure_signed(origin)?; - let dest = T::Lookup::lookup(dest)?; - >::transfer(&source, &dest, value, Preserve)?; - Self::do_store_transfer_proof(&source, &dest, value); - Ok(()) - } - - /// Transfer the entire transferable balance from the caller account. - /// - /// NOTE: This function only attempts to transfer _transferable_ balances. This means that - /// any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be - /// transferred by this function. To ensure that this function results in a killed account, - /// you might need to prepare the account by removing any reference counters, storage - /// deposits, etc... - /// - /// The dispatch origin of this call must be Signed. - /// - /// - `dest`: The recipient of the transfer. - /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all - /// of the funds the account has, causing the sender account to be killed (false), or - /// transfer everything except at least the existential deposit, which will guarantee to - /// keep the sender account alive (true). - #[pallet::call_index(4)] - pub fn transfer_all( - origin: OriginFor, - dest: AccountIdLookupOf, - keep_alive: bool, - ) -> DispatchResult { - let transactor = ensure_signed(origin)?; - let keep_alive = if keep_alive { Preserve } else { Expendable }; - let reducible_balance = >::reducible_balance( - &transactor, - keep_alive, - Fortitude::Polite, - ); - let dest = T::Lookup::lookup(dest)?; - >::transfer( - &transactor, - &dest, - reducible_balance, - keep_alive, - )?; - Self::do_store_transfer_proof(&transactor, &dest, reducible_balance); - Ok(()) - } - - /// Unreserve some balance from a user by force. - /// - /// Can only be called by ROOT. - #[pallet::call_index(5)] - pub fn force_unreserve( - origin: OriginFor, - who: AccountIdLookupOf, - amount: T::Balance, - ) -> DispatchResult { - ensure_root(origin)?; - let who = T::Lookup::lookup(who)?; - let _leftover = >::unreserve(&who, amount); - Ok(()) - } - - /// Upgrade a specified account. - /// - /// - `origin`: Must be `Signed`. - /// - `who`: The account to be upgraded. - /// - /// This will waive the transaction fee if at least all but 10% of the accounts needed to - /// be upgraded. (We let some not have to be upgraded just in order to allow for the - /// possibility of churn). - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::upgrade_accounts(who.len() as u32))] - pub fn upgrade_accounts( - origin: OriginFor, - who: Vec, - ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; - if who.is_empty() { - return Ok(Pays::Yes.into()); - } - let mut upgrade_count = 0; - for i in &who { - let upgraded = Self::ensure_upgraded(i); - if upgraded { - upgrade_count.saturating_inc(); - } - } - let proportion_upgraded = Perbill::from_rational(upgrade_count, who.len() as u32); - if proportion_upgraded >= Perbill::from_percent(90) { - Ok(Pays::No.into()) - } else { - Ok(Pays::Yes.into()) - } - } - - /// Set the regular balance of a given account. - /// - /// The dispatch origin for this call is `root`. - #[pallet::call_index(8)] - #[pallet::weight( - T::WeightInfo::force_set_balance_creating() // Creates a new account. - .max(T::WeightInfo::force_set_balance_killing()) // Kills an existing account. - )] - pub fn force_set_balance( - origin: OriginFor, - who: AccountIdLookupOf, - #[pallet::compact] new_free: T::Balance, - ) -> DispatchResult { - ensure_root(origin)?; - let who = T::Lookup::lookup(who)?; - let existential_deposit = Self::ed(); - - let wipeout = new_free < existential_deposit; - let new_free = if wipeout { Zero::zero() } else { new_free }; - - // First we try to modify the account's balance to the forced balance. - let old_free = Self::mutate_account_handling_dust(&who, |account| { - let old_free = account.free; - account.free = new_free; - old_free - })?; - - // This will adjust the total issuance, which was not done by the `mutate_account` - // above. - match new_free.cmp(&old_free) { - cmp::Ordering::Greater => { - mem::drop(PositiveImbalance::::new(new_free - old_free)); - }, - cmp::Ordering::Less => { - mem::drop(NegativeImbalance::::new(old_free - new_free)); - }, - cmp::Ordering::Equal => {}, - } - - Self::deposit_event(Event::BalanceSet { who, free: new_free }); - Ok(()) - } - - /// Adjust the total issuance in a saturating way. - /// - /// Can only be called by root and always needs a positive `delta`. - /// - /// # Example - #[doc = docify::embed!("./src/tests/dispatchable_tests.rs", force_adjust_total_issuance_example)] - #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::force_adjust_total_issuance())] - pub fn force_adjust_total_issuance( - origin: OriginFor, - direction: AdjustmentDirection, - #[pallet::compact] delta: T::Balance, - ) -> DispatchResult { - ensure_root(origin)?; - - ensure!(delta > Zero::zero(), Error::::DeltaZero); - - let old = TotalIssuance::::get(); - let new = match direction { - AdjustmentDirection::Increase => old.saturating_add(delta), - AdjustmentDirection::Decrease => old.saturating_sub(delta), - }; - - ensure!(InactiveIssuance::::get() <= new, Error::::IssuanceDeactivated); - TotalIssuance::::set(new); - - Self::deposit_event(Event::::TotalIssuanceForced { old, new }); - - Ok(()) - } - - /// 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_. - #[pallet::call_index(10)] - #[pallet::weight(if *keep_alive {T::WeightInfo::burn_allow_death() } else {T::WeightInfo::burn_keep_alive()})] - pub fn burn( - origin: OriginFor, - #[pallet::compact] value: T::Balance, - keep_alive: bool, - ) -> DispatchResult { - let source = ensure_signed(origin)?; - let preservation = if keep_alive { Preserve } else { Expendable }; - >::burn_from( - &source, - value, - preservation, - Precision::Exact, - Polite, - )?; - Ok(()) - } - } - - impl, I: 'static> Pallet { - pub(crate) fn do_store_transfer_proof( - from: &T::AccountId, - to: &T::AccountId, - value: T::Balance, - ) { - if from != to { - let current_count = Self::transfer_count(); - TransferCount::::put(current_count.saturating_add(One::one())); - - TransferProof::::insert((current_count, from.clone(), to.clone(), value), ()); - } - } - - pub(crate) fn transfer_proof_storage_key( - transfer_count: u64, - from: T::AccountId, - to: T::AccountId, - amount: T::Balance, - ) -> Vec { - let key = (transfer_count, from, to, amount); - TransferProof::::hashed_key_for(&key) - } - } - - impl, I: 'static> Pallet { - /// Public function to get the total issuance. - pub fn total_issuance() -> T::Balance { - TotalIssuance::::get() - } - - /// Public function to get the inactive issuance. - pub fn inactive_issuance() -> T::Balance { - InactiveIssuance::::get() - } - - /// Public function to access the Locks storage. - pub fn locks(who: &T::AccountId) -> WeakBoundedVec, T::MaxLocks> { - Locks::::get(who) - } - - /// Public function to access the reserves storage. - pub fn reserves( - who: &T::AccountId, - ) -> BoundedVec, T::MaxReserves> { - Reserves::::get(who) - } - - fn ed() -> T::Balance { - T::ExistentialDeposit::get() - } - /// Ensure the account `who` is using the new logic. - /// - /// Returns `true` if the account did get upgraded, `false` if it didn't need upgrading. - pub fn ensure_upgraded(who: &T::AccountId) -> bool { - let mut a = T::AccountStore::get(who); - if a.flags.is_new_logic() { - return false; - } - a.flags.set_new_logic(); - if !a.reserved.is_zero() && a.frozen.is_zero() { - if system::Pallet::::providers(who) == 0 { - // Gah!! We have no provider refs :( - // This shouldn't practically happen, but we need a failsafe anyway: let's give - // them enough for an ED. - log::warn!( - target: LOG_TARGET, - "account with a non-zero reserve balance has no provider refs, account_id: '{:?}'.", - who - ); - a.free = a.free.max(Self::ed()); - system::Pallet::::inc_providers(who); - } - let _ = system::Pallet::::inc_consumers_without_limit(who).defensive(); - } - // Should never fail - we're only setting a bit. - let _ = T::AccountStore::try_mutate_exists(who, |account| -> DispatchResult { - *account = Some(a); - Ok(()) - }); - Self::deposit_event(Event::Upgraded { who: who.clone() }); - true - } - - /// Get the free balance of an account. - pub fn free_balance(who: impl core::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).free - } - - /// Get the balance of an account that can be used for transfers, reservations, or any other - /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. - pub fn usable_balance(who: impl core::borrow::Borrow) -> T::Balance { - >::reducible_balance(who.borrow(), Expendable, Polite) - } - - /// Get the balance of an account that can be used for paying transaction fees (not tipping, - /// or any other kind of fees, though). Will be at most `free_balance`. - /// - /// This requires that the account stays alive. - pub fn usable_balance_for_fees(who: impl core::borrow::Borrow) -> T::Balance { - >::reducible_balance(who.borrow(), Protect, Polite) - } - - /// Get the reserved balance of an account. - pub fn reserved_balance(who: impl core::borrow::Borrow) -> T::Balance { - Self::account(who.borrow()).reserved - } - - /// Get both the free and reserved balances of an account. - pub(crate) fn account(who: &T::AccountId) -> AccountData { - T::AccountStore::get(who) - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// It returns the result from the closure. Any dust is handled through the low-level - /// `fungible::Unbalanced` trap-door for legacy dust management. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub(crate) fn mutate_account_handling_dust( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData) -> R, - ) -> Result { - let (r, maybe_dust) = Self::mutate_account(who, f)?; - if let Some(dust) = maybe_dust { - >::handle_raw_dust(dust); - } - Ok(r) - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// It returns the result from the closure. Any dust is handled through the low-level - /// `fungible::Unbalanced` trap-door for legacy dust management. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub(crate) fn try_mutate_account_handling_dust>( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData, bool) -> Result, - ) -> Result { - let (r, maybe_dust) = Self::try_mutate_account(who, f)?; - if let Some(dust) = maybe_dust { - >::handle_raw_dust(dust); - } - Ok(r) - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// It returns both the result from the closure, and an optional amount of dust - /// which should be handled once it is known that all nested mutates that could affect - /// storage items what the dust handler touches have completed. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub(crate) fn mutate_account( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData) -> R, - ) -> Result<(R, Option), DispatchError> { - Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) - } - - /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disabled. - /// Returns `false` otherwise. - #[cfg(not(feature = "insecure_zero_ed"))] - fn have_providers_or_no_zero_ed(_: &T::AccountId) -> bool { - true - } - - /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disabled. - /// Returns `false` otherwise. - #[cfg(feature = "insecure_zero_ed")] - fn have_providers_or_no_zero_ed(who: &T::AccountId) -> bool { - frame_system::Pallet::::providers(who) > 0 - } - - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the - /// result of `f` is an `Err`. - /// - /// It returns both the result from the closure, and an optional amount of dust - /// which should be handled once it is known that all nested mutates that could affect - /// storage items what the dust handler touches have completed. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub(crate) fn try_mutate_account>( - who: &T::AccountId, - f: impl FnOnce(&mut AccountData, bool) -> Result, - ) -> Result<(R, Option), E> { - Self::ensure_upgraded(who); - let result = T::AccountStore::try_mutate_exists(who, |maybe_account| { - let is_new = maybe_account.is_none(); - let mut account = maybe_account.take().unwrap_or_default(); - let did_provide = - account.free >= Self::ed() && Self::have_providers_or_no_zero_ed(who); - let did_consume = - !is_new && (!account.reserved.is_zero() || !account.frozen.is_zero()); - - let result = f(&mut account, is_new)?; - - let does_provide = account.free >= Self::ed(); - let does_consume = !account.reserved.is_zero() || !account.frozen.is_zero(); - - if !did_provide && does_provide { - frame_system::Pallet::::inc_providers(who); - } - if did_consume && !does_consume { - frame_system::Pallet::::dec_consumers(who); - } - if !did_consume && does_consume { - frame_system::Pallet::::inc_consumers(who)?; - } - if does_consume && frame_system::Pallet::::consumers(who) == 0 { - // NOTE: This is a failsafe and should not happen for normal accounts. A normal - // account should have gotten a consumer ref in `!did_consume && does_consume` - // at some point. - log::error!(target: LOG_TARGET, "Defensively bumping a consumer ref."); - frame_system::Pallet::::inc_consumers(who)?; - } - if did_provide && !does_provide { - // This could reap the account so must go last. - frame_system::Pallet::::dec_providers(who).inspect_err(|_| { - // best-effort revert consumer change. - if did_consume && !does_consume { - let _ = frame_system::Pallet::::inc_consumers(who).defensive(); - } - if !did_consume && does_consume { - frame_system::Pallet::::dec_consumers(who); - } - })?; - } - - let maybe_endowed = if is_new { Some(account.free) } else { None }; - - // Handle any steps needed after mutating an account. - // - // This includes DustRemoval unbalancing, in the case than the `new` account's total - // balance is non-zero but below ED. - // - // Updates `maybe_account` to `Some` iff the account has sufficient balance. - // Evaluates `maybe_dust`, which is `Some` containing the dust to be dropped, iff - // some dust should be dropped. - // - // We should never be dropping if reserved is non-zero. Reserved being non-zero - // should imply that we have a consumer ref, so this is economically safe. - let ed = Self::ed(); - let maybe_dust = if account.free < ed && account.reserved.is_zero() { - if account.free.is_zero() { - None - } else { - Some(account.free) - } - } else { - assert!( - account.free.is_zero() || account.free >= ed || !account.reserved.is_zero() - ); - *maybe_account = Some(account); - None - }; - Ok((maybe_endowed, maybe_dust, result)) - }); - result.map(|(maybe_endowed, maybe_dust, result)| { - if let Some(endowed) = maybe_endowed { - Self::deposit_event(Event::Endowed { - account: who.clone(), - free_balance: endowed, - }); - } - if let Some(amount) = maybe_dust { - Pallet::::deposit_event(Event::DustLost { account: who.clone(), amount }); - } - (result, maybe_dust) - }) - } - - /// Update the account entry for `who`, given the locks. - pub(crate) fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { - let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from( - locks.to_vec(), - Some("Balances Update Locks"), - ); - - if locks.len() as u32 > T::MaxLocks::get() { - log::warn!( - target: LOG_TARGET, - "Warning: A user has more currency locks than expected. \ - A runtime configuration adjustment may be needed." - ); - } - let freezes = Freezes::::get(who); - let mut prev_frozen = Zero::zero(); - let mut after_frozen = Zero::zero(); - // No way this can fail since we do not alter the existential balances. - // TODO: Revisit this assumption. - let res = Self::mutate_account(who, |b| { - prev_frozen = b.frozen; - b.frozen = Zero::zero(); - for l in locks.iter() { - b.frozen = b.frozen.max(l.amount); - } - for l in freezes.iter() { - b.frozen = b.frozen.max(l.amount); - } - after_frozen = b.frozen; - }); - debug_assert!(res.is_ok()); - if let Ok((_, maybe_dust)) = res { - debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); - } - - match locks.is_empty() { - true => Locks::::remove(who), - false => Locks::::insert(who, bounded_locks), - } - - match prev_frozen.cmp(&after_frozen) { - cmp::Ordering::Greater => { - let amount = prev_frozen.saturating_sub(after_frozen); - Self::deposit_event(Event::Unlocked { who: who.clone(), amount }); - }, - cmp::Ordering::Less => { - let amount = after_frozen.saturating_sub(prev_frozen); - Self::deposit_event(Event::Locked { who: who.clone(), amount }); - }, - cmp::Ordering::Equal => {}, - } - } - - /// Update the account entry for `who`, given the locks. - pub(crate) fn update_freezes( - who: &T::AccountId, - freezes: BoundedSlice, T::MaxFreezes>, - ) -> DispatchResult { - let mut prev_frozen = Zero::zero(); - let mut after_frozen = Zero::zero(); - let (_, maybe_dust) = Self::mutate_account(who, |b| { - prev_frozen = b.frozen; - b.frozen = Zero::zero(); - for l in Locks::::get(who).iter() { - b.frozen = b.frozen.max(l.amount); - } - for l in freezes.iter() { - b.frozen = b.frozen.max(l.amount); - } - after_frozen = b.frozen; - })?; - debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); - if freezes.is_empty() { - Freezes::::remove(who); - } else { - Freezes::::insert(who, freezes); - } - match prev_frozen.cmp(&after_frozen) { - cmp::Ordering::Greater => { - let amount = prev_frozen.saturating_sub(after_frozen); - Self::deposit_event(Event::Thawed { who: who.clone(), amount }); - }, - cmp::Ordering::Less => { - let amount = after_frozen.saturating_sub(prev_frozen); - Self::deposit_event(Event::Frozen { who: who.clone(), amount }); - }, - cmp::Ordering::Equal => {}, - } - Ok(()) - } - - /// Move the reserved balance of one account into the balance of another, according to - /// `status`. This will respect freezes/locks only if `fortitude` is `Polite`. - /// - /// Is a no-op if the value to be moved is zero. - /// - /// NOTE: returns actual amount of transferred value in `Ok` case. - pub(crate) fn do_transfer_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: T::Balance, - precision: Precision, - fortitude: Fortitude, - status: Status, - ) -> Result { - if value.is_zero() { - return Ok(Zero::zero()); - } - - let max = >::reducible_total_balance_on_hold( - slashed, fortitude, - ); - let actual = match precision { - Precision::BestEffort => value.min(max), - Precision::Exact => value, - }; - ensure!(actual <= max, TokenError::FundsUnavailable); - if slashed == beneficiary { - return match status { - Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))), - Status::Reserved => Ok(actual), - }; - } - - let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account( - beneficiary, - |to_account, is_new| -> Result<((), Option), DispatchError> { - ensure!(!is_new, Error::::DeadAccount); - Self::try_mutate_account(slashed, |from_account, _| -> DispatchResult { - match status { - Status::Free => - to_account.free = to_account - .free - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - Status::Reserved => - to_account.reserved = to_account - .reserved - .checked_add(&actual) - .ok_or(ArithmeticError::Overflow)?, - } - from_account.reserved.saturating_reduce(actual); - Ok(()) - }) - }, - )?; - - if let Some(dust) = maybe_dust_1 { - >::handle_raw_dust(dust); - } - if let Some(dust) = maybe_dust_2 { - >::handle_raw_dust(dust); - } - - Self::deposit_event(Event::ReserveRepatriated { - from: slashed.clone(), - to: beneficiary.clone(), - amount: actual, - destination_status: status, - }); - Ok(actual) - } - } -} diff --git a/pallets/balances/src/migration.rs b/pallets/balances/src/migration.rs deleted file mode 100644 index ac2f7e2f..00000000 --- a/pallets/balances/src/migration.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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 Substrate. If not, see . - -use super::*; -use frame_support::{ - pallet_prelude::*, - traits::{OnRuntimeUpgrade, PalletInfoAccess}, - weights::Weight, -}; - -fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weight { - let on_chain_version = Pallet::::on_chain_storage_version(); - - if on_chain_version == 0 { - let total = accounts - .iter() - .map(Pallet::::total_balance) - .fold(T::Balance::zero(), |a, e| a.saturating_add(e)); - Pallet::::deactivate(total); - - // Remove the old `StorageVersion` type. - frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( - Pallet::::name().as_bytes(), - "StorageVersion".as_bytes(), - )); - - // Set storage version to `1`. - StorageVersion::new(1).put::>(); - - log::info!(target: LOG_TARGET, "Storage to version 1"); - T::DbWeight::get().reads_writes(2 + accounts.len() as u64, 3) - } else { - log::info!( - target: LOG_TARGET, - "Migration did not execute. This probably should be removed" - ); - T::DbWeight::get().reads(1) - } -} - -// NOTE: This must be used alongside the account whose balance is expected to be inactive. -// Generally this will be used for the XCM teleport checking account. -pub struct MigrateToTrackInactive(PhantomData<(T, A, I)>); -impl, A: Get, I: 'static> OnRuntimeUpgrade - for MigrateToTrackInactive -{ - fn on_runtime_upgrade() -> Weight { - migrate_v0_to_v1::(&[A::get()]) - } -} - -// NOTE: This must be used alongside the accounts whose balance is expected to be inactive. -// Generally this will be used for the XCM teleport checking accounts. -pub struct MigrateManyToTrackInactive(PhantomData<(T, A, I)>); -impl, A: Get>, I: 'static> OnRuntimeUpgrade - for MigrateManyToTrackInactive -{ - fn on_runtime_upgrade() -> Weight { - migrate_v0_to_v1::(&A::get()) - } -} - -pub struct ResetInactive(PhantomData<(T, I)>); -impl, I: 'static> OnRuntimeUpgrade for ResetInactive { - fn on_runtime_upgrade() -> Weight { - let on_chain_version = Pallet::::on_chain_storage_version(); - - if on_chain_version == 1 { - // Remove the old `StorageVersion` type. - frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( - Pallet::::name().as_bytes(), - "StorageVersion".as_bytes(), - )); - - InactiveIssuance::::kill(); - - // Set storage version to `0`. - StorageVersion::new(0).put::>(); - - log::info!(target: LOG_TARGET, "Storage to version 0"); - T::DbWeight::get().reads_writes(1, 3) - } else { - log::info!( - target: LOG_TARGET, - "Migration did not execute. This probably should be removed" - ); - T::DbWeight::get().reads(1) - } - } -} diff --git a/pallets/balances/src/tests/currency_tests.rs b/pallets/balances/src/tests/currency_tests.rs deleted file mode 100644 index cf22405a..00000000 --- a/pallets/balances/src/tests/currency_tests.rs +++ /dev/null @@ -1,1643 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests regarding the functionality of the `Currency` trait set implementations. - -use super::*; -use crate::{Event, NegativeImbalance}; -use frame_support::{ - traits::{ - BalanceStatus::{Free, Reserved}, - Currency, - ExistenceRequirement::{self, AllowDeath, KeepAlive}, - Hooks, InspectLockableCurrency, LockIdentifier, LockableCurrency, NamedReservableCurrency, - ReservableCurrency, WithdrawReasons, - }, - StorageNoopGuard, -}; -use frame_system::Event as SysEvent; -use sp_runtime::traits::DispatchTransaction; - -const ID_1: LockIdentifier = *b"1 "; -const ID_2: LockIdentifier = *b"2 "; - -pub const CALL: &::RuntimeCall = - &RuntimeCall::Balances(crate::Call::transfer_allow_death { - dest: sp_core::crypto::AccountId32::new([0u8; 32]), - value: 0, - }); - -#[test] -fn ed_should_work() { - ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 1000)); - assert_noop!( - >::transfer(&account_id(1), &account_id(10), 1000, KeepAlive), - TokenError::NotExpendable - ); - assert_ok!(>::transfer( - &account_id(1), - &account_id(10), - 1000, - AllowDeath - )); - }); -} - -#[test] -fn set_lock_with_amount_zero_removes_lock() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::all()); - Balances::set_lock(ID_1, &account_id(1), 0, WithdrawReasons::all()); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn set_lock_with_withdraw_reasons_empty_removes_lock() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::all()); - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::empty()); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn basic_locking_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_eq!(Balances::free_balance(account_id(1)), 10); - Balances::set_lock(ID_1, &account_id(1), 9, WithdrawReasons::all()); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 5, AllowDeath), - TokenError::Frozen - ); - }); -} - -#[test] -fn inspect_lock_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 10, WithdrawReasons::all()); - Balances::set_lock(ID_2, &account_id(1), 10, WithdrawReasons::all()); - Balances::set_lock(ID_1, &account_id(2), 20, WithdrawReasons::all()); - - assert_eq!( - >::balance_locked(ID_1, &account_id(1)), - 10 - ); - assert_eq!( - >::balance_locked(ID_2, &account_id(1)), - 10 - ); - assert_eq!( - >::balance_locked(ID_1, &account_id(2)), - 20 - ); - assert_eq!( - >::balance_locked(ID_2, &account_id(2)), - 0 - ); - assert_eq!( - >::balance_locked(ID_1, &account_id(3)), - 0 - ); - }) -} - -#[test] -fn account_should_be_reaped() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_eq!(Balances::free_balance(account_id(1)), 10); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 10, - AllowDeath - )); - assert_eq!(System::providers(&account_id(1)), 0); - assert_eq!(System::consumers(&account_id(1)), 0); - // Check that the account is dead. - assert!(!frame_system::Account::::contains_key(account_id(1))); - }); -} - -#[test] -fn reap_failed_due_to_provider_and_consumer() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // SCENARIO: only one provider and there are remaining consumers. - assert_ok!(System::inc_consumers(&account_id(1))); - assert!(!System::can_dec_provider(&account_id(1))); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 10, AllowDeath), - TokenError::Frozen - ); - assert!(System::account_exists(&account_id(1))); - assert_eq!(Balances::free_balance(account_id(1)), 10); - - // SCENARIO: more than one provider, but will not kill account due to other provider. - assert_eq!(System::inc_providers(&account_id(1)), frame_system::IncRefStatus::Existed); - assert_eq!(System::providers(&account_id(1)), 2); - assert!(System::can_dec_provider(&account_id(1))); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 10, - AllowDeath - )); - assert_eq!(System::providers(&account_id(1)), 1); - assert!(System::account_exists(&account_id(1))); - assert_eq!(Balances::free_balance(account_id(1)), 0); - }); -} - -#[test] -fn partial_locking_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn lock_removal_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - Balances::remove_lock(ID_1, &account_id(1)); - assert_eq!(System::consumers(&account_id(1)), 0); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn lock_replacement_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn double_locking_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - Balances::set_lock(ID_2, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn combination_locking_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_eq!(System::consumers(&account_id(1)), 0); - Balances::set_lock(ID_1, &account_id(1), Balance::MAX, WithdrawReasons::empty()); - assert_eq!(System::consumers(&account_id(1)), 0); - Balances::set_lock(ID_2, &account_id(1), 0, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 0); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 1, - AllowDeath - )); - }); -} - -#[test] -fn lock_value_extension_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - Balances::extend_lock(ID_1, &account_id(1), 2, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - Balances::extend_lock(ID_1, &account_id(1), 8, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 3, AllowDeath), - TokenError::Frozen - ); - }); -} - -#[test] -fn lock_should_work_reserve() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - pallet_transaction_payment::NextFeeMultiplier::::put( - Multiplier::saturating_from_integer(1), - ); - Balances::set_lock(ID_1, &account_id(1), 10, WithdrawReasons::RESERVE); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 1, AllowDeath), - TokenError::Frozen - ); - assert_noop!( - Balances::reserve(&account_id(1), 1), - Error::::LiquidityRestrictions, - ); - assert!(ChargeTransactionPayment::::validate_and_prepare( - ChargeTransactionPayment::from(1), - Some(account_id(1)).into(), - CALL, - &crate::tests::info_from_weight(Weight::from_parts(1, 0)), - 1, - 0, - ) - .is_err()); - assert!(ChargeTransactionPayment::::validate_and_prepare( - ChargeTransactionPayment::from(0), - Some(account_id(1)).into(), - CALL, - &crate::tests::info_from_weight(Weight::from_parts(1, 0)), - 1, - 0, - ) - .is_err()); - }); -} - -#[test] -fn lock_should_work_tx_fee() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 10, WithdrawReasons::TRANSACTION_PAYMENT); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 1, AllowDeath), - TokenError::Frozen - ); - assert_noop!( - Balances::reserve(&account_id(1), 1), - Error::::LiquidityRestrictions, - ); - assert!(ChargeTransactionPayment::::validate_and_prepare( - ChargeTransactionPayment::from(1), - Some(account_id(1)).into(), - CALL, - &crate::tests::info_from_weight(Weight::from_parts(1, 0)), - 1, - 0, - ) - .is_err()); - assert!(ChargeTransactionPayment::::validate_and_prepare( - ChargeTransactionPayment::from(0), - Some(account_id(1)).into(), - CALL, - &crate::tests::info_from_weight(Weight::from_parts(1, 0)), - 1, - 0, - ) - .is_err()); - }); -} - -#[test] -fn lock_block_number_extension_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - Balances::extend_lock(ID_1, &account_id(1), 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - System::set_block_number(2); - Balances::extend_lock(ID_1, &account_id(1), 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 3, AllowDeath), - TokenError::Frozen - ); - }); -} - -#[test] -fn lock_reasons_extension_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - Balances::set_lock(ID_1, &account_id(1), 10, WithdrawReasons::TRANSFER); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - Balances::extend_lock(ID_1, &account_id(1), 10, WithdrawReasons::empty()); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - Balances::extend_lock(ID_1, &account_id(1), 10, WithdrawReasons::RESERVE); - assert_noop!( - >::transfer(&account_id(1), &account_id(2), 6, AllowDeath), - TokenError::Frozen - ); - }); -} - -#[test] -fn reserved_balance_should_prevent_reclaim_count() { - ExtBuilder::default() - .existential_deposit(256) - .monied(true) - .build_and_execute_with(|| { - System::inc_account_nonce(account_id(2)); - assert_eq!(Balances::total_balance(&account_id(2)), 256 * 20); - assert_eq!(System::providers(&account_id(2)), 1); - System::inc_providers(&account_id(2)); - assert_eq!(System::providers(&account_id(2)), 2); - - assert_ok!(Balances::reserve(&account_id(2), 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(System::providers(&account_id(2)), 1); - assert_eq!(Balances::free_balance(account_id(2)), 255); // "free" account would be deleted. - assert_eq!(Balances::total_balance(&account_id(2)), 256 * 20); // reserve still exists. - assert_eq!(System::account_nonce(account_id(2)), 1); - - // account 4 tries to take index 1 for account 5. - assert_ok!(Balances::transfer_allow_death( - Some(account_id(4)).into(), - account_id(5), - 256 + 0x69 - )); - assert_eq!(Balances::total_balance(&account_id(5)), 256 + 0x69); - - assert!(Balances::slash_reserved(&account_id(2), 256 * 19 + 1).1.is_zero()); // account 2 gets slashed - - // "reserve" account reduced to 255 (below ED) so account no longer consuming - assert_ok!(System::dec_providers(&account_id(2))); - assert_eq!(System::providers(&account_id(2)), 0); - // account deleted - assert_eq!(System::account_nonce(account_id(2)), 0); // nonce zero - assert_eq!(Balances::total_balance(&account_id(2)), 0); - - // account 4 tries to take index 1 again for account 6. - assert_ok!(Balances::transfer_allow_death( - Some(account_id(4)).into(), - account_id(6), - 256 + 0x69 - )); - assert_eq!(Balances::total_balance(&account_id(6)), 256 + 0x69); - }); -} - -#[test] -fn reward_should_work() { - ExtBuilder::default().monied(true).build_and_execute_with(|| { - assert_eq!(Balances::total_balance(&account_id(1)), 10); - assert_ok!(Balances::deposit_into_existing(&account_id(1), 10).map(drop)); - assert_eq!( - events(), - [ - RuntimeEvent::Balances(crate::Event::Deposit { who: account_id(1), amount: 10 }), - RuntimeEvent::Balances(crate::Event::Issued { amount: 10 }), - ] - ); - assert_eq!(Balances::total_balance(&account_id(1)), 20); - assert_eq!(pallet_balances::TotalIssuance::::get(), 120); - }); -} - -#[test] -fn balance_works() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 42); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: account_id(1), - amount: 42, - })); - assert_eq!(Balances::free_balance(account_id(1)), 42); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(Balances::total_balance(&account_id(1)), 42); - assert_eq!(Balances::free_balance(account_id(2)), 0); - assert_eq!(Balances::reserved_balance(account_id(2)), 0); - assert_eq!(Balances::total_balance(&account_id(2)), 0); - }); -} - -#[test] -fn reserving_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - - assert_eq!(Balances::total_balance(&account_id(1)), 111); - assert_eq!(Balances::free_balance(account_id(1)), 111); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - - assert_ok!(Balances::reserve(&account_id(1), 69)); - - assert_eq!(Balances::total_balance(&account_id(1)), 111); - assert_eq!(Balances::free_balance(account_id(1)), 42); - assert_eq!(Balances::reserved_balance(account_id(1)), 69); - }); -} - -#[test] -fn deducting_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - assert_ok!(Balances::reserve(&account_id(1), 69)); - assert_eq!(Balances::free_balance(account_id(1)), 42); - }); -} - -#[test] -fn refunding_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 42); - System::set_block_number(2); - Balances::unreserve(&account_id(1), 69); - assert_eq!(Balances::free_balance(account_id(1)), 42); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - }); -} - -#[test] -fn slashing_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - assert_ok!(Balances::reserve(&account_id(1), 69)); - assert_eq!(Balances::slash_reserved(&account_id(1), 69).1, 0); - assert_eq!(Balances::free_balance(account_id(1)), 42); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(pallet_balances::TotalIssuance::::get(), 42); - }); -} - -#[test] -fn withdrawing_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(2), 111); - let _ = Balances::withdraw( - &account_id(2), - 11, - WithdrawReasons::TRANSFER, - ExistenceRequirement::KeepAlive, - ); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Withdraw { - who: account_id(2), - amount: 11, - })); - assert_eq!(Balances::free_balance(account_id(2)), 100); - assert_eq!(pallet_balances::TotalIssuance::::get(), 100); - }); -} - -#[test] -fn withdrawing_balance_should_fail_when_not_expendable() { - ExtBuilder::default().build_and_execute_with(|| { - ExistentialDeposit::set(10); - let _ = Balances::deposit_creating(&account_id(2), 20); - assert_ok!(Balances::reserve(&account_id(2), 5)); - assert_noop!( - Balances::withdraw( - &account_id(2), - 6, - WithdrawReasons::TRANSFER, - ExistenceRequirement::KeepAlive - ), - Error::::Expendability, - ); - assert_ok!(Balances::withdraw( - &account_id(2), - 5, - WithdrawReasons::TRANSFER, - ExistenceRequirement::KeepAlive - ),); - }); -} - -#[test] -fn slashing_incomplete_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 42); - assert_ok!(Balances::reserve(&account_id(1), 21)); - assert_eq!(Balances::slash_reserved(&account_id(1), 69).1, 48); - assert_eq!(Balances::free_balance(account_id(1)), 21); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(pallet_balances::TotalIssuance::::get(), 21); - }); -} - -#[test] -fn unreserving_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - assert_ok!(Balances::reserve(&account_id(1), 110)); - Balances::unreserve(&account_id(1), 41); - assert_eq!(Balances::reserved_balance(account_id(1)), 69); - assert_eq!(Balances::free_balance(account_id(1)), 42); - }); -} - -#[test] -fn slashing_reserved_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 112); - assert_ok!(Balances::reserve(&account_id(1), 111)); - assert_eq!(Balances::slash_reserved(&account_id(1), 42).1, 0); - assert_eq!(Balances::reserved_balance(account_id(1)), 69); - assert_eq!(Balances::free_balance(account_id(1)), 1); - assert_eq!(pallet_balances::TotalIssuance::::get(), 70); - }); -} - -#[test] -fn slashing_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - assert_ok!(Balances::reserve(&account_id(1), 42)); - assert_eq!(Balances::slash_reserved(&account_id(1), 69).1, 27); - assert_eq!(Balances::free_balance(account_id(1)), 69); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(pallet_balances::TotalIssuance::::get(), 69); - }); -} - -#[test] -fn repatriating_reserved_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - let _ = Balances::deposit_creating(&account_id(2), 1); - assert_ok!(Balances::reserve(&account_id(1), 110)); - assert_ok!(Balances::repatriate_reserved(&account_id(1), &account_id(2), 41, Free), 0); - System::assert_last_event(RuntimeEvent::Balances(crate::Event::ReserveRepatriated { - from: account_id(1), - to: account_id(2), - amount: 41, - destination_status: Free, - })); - assert_eq!(Balances::reserved_balance(account_id(1)), 69); - assert_eq!(Balances::free_balance(account_id(1)), 1); - assert_eq!(Balances::reserved_balance(account_id(2)), 0); - assert_eq!(Balances::free_balance(account_id(2)), 42); - }); -} - -#[test] -fn transferring_reserved_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - let _ = Balances::deposit_creating(&account_id(2), 1); - assert_ok!(Balances::reserve(&account_id(1), 110)); - assert_ok!(Balances::repatriate_reserved(&account_id(1), &account_id(2), 41, Reserved), 0); - assert_eq!(Balances::reserved_balance(account_id(1)), 69); - assert_eq!(Balances::free_balance(account_id(1)), 1); - assert_eq!(Balances::reserved_balance(account_id(2)), 41); - assert_eq!(Balances::free_balance(account_id(2)), 1); - }); -} - -#[test] -fn transferring_reserved_balance_to_yourself_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 110); - assert_ok!(Balances::reserve(&account_id(1), 50)); - assert_ok!(Balances::repatriate_reserved(&account_id(1), &account_id(1), 50, Free), 0); - assert_eq!(Balances::free_balance(account_id(1)), 110); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - - assert_ok!(Balances::reserve(&account_id(1), 50)); - assert_ok!(Balances::repatriate_reserved(&account_id(1), &account_id(1), 60, Free), 10); - assert_eq!(Balances::free_balance(account_id(1)), 110); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - }); -} - -#[test] -fn transferring_reserved_balance_to_nonexistent_should_fail() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - assert_ok!(Balances::reserve(&account_id(1), 110)); - assert_noop!( - Balances::repatriate_reserved(&account_id(1), &account_id(2), 42, Free), - Error::::DeadAccount - ); - }); -} - -#[test] -fn transferring_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 110); - let _ = Balances::deposit_creating(&account_id(2), 1); - assert_ok!(Balances::reserve(&account_id(1), 41)); - assert_ok!(Balances::repatriate_reserved(&account_id(1), &account_id(2), 69, Free), 28); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(Balances::free_balance(account_id(1)), 69); - assert_eq!(Balances::reserved_balance(account_id(2)), 0); - assert_eq!(Balances::free_balance(account_id(2)), 42); - }); -} - -#[test] -fn transferring_too_high_value_should_not_panic() { - ExtBuilder::default().build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), Balance::MAX); - Balances::make_free_balance_be(&account_id(2), 1); - - assert_err!( - >::transfer( - &account_id(1), - &account_id(2), - Balance::MAX, - AllowDeath - ), - ArithmeticError::Overflow, - ); - - assert_eq!(Balances::free_balance(account_id(1)), Balance::MAX); - assert_eq!(Balances::free_balance(account_id(2)), 1); - }); -} - -#[test] -fn account_create_on_free_too_low_with_other() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 100); - assert_eq!(pallet_balances::TotalIssuance::::get(), 100); - - // No-op. - let _ = Balances::deposit_creating(&account_id(2), 50); - assert_eq!(Balances::free_balance(account_id(2)), 0); - assert_eq!(pallet_balances::TotalIssuance::::get(), 100); - }) -} - -#[test] -fn account_create_on_free_too_low() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // No-op. - let _ = Balances::deposit_creating(&account_id(2), 50); - assert_eq!(Balances::free_balance(account_id(2)), 0); - assert_eq!(pallet_balances::TotalIssuance::::get(), 0); - }) -} - -#[test] -fn account_removal_on_free_too_low() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_eq!(pallet_balances::TotalIssuance::::get(), 0); - - // Setup two accounts with free balance above the existential threshold. - let _ = Balances::deposit_creating(&account_id(1), 110); - let _ = Balances::deposit_creating(&account_id(2), 110); - - assert_eq!(Balances::free_balance(account_id(1)), 110); - assert_eq!(Balances::free_balance(account_id(2)), 110); - assert_eq!(pallet_balances::TotalIssuance::::get(), 220); - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the existential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Balances::transfer_allow_death(Some(account_id(1)).into(), account_id(2), 20)); - - // Verify free balance removal of account 1. - assert_eq!(Balances::free_balance(account_id(1)), 0); - assert_eq!(Balances::free_balance(account_id(2)), 130); - - // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(pallet_balances::TotalIssuance::::get(), 130); - }); -} - -#[test] -fn burn_must_work() { - ExtBuilder::default().monied(true).build_and_execute_with(|| { - let init_total_issuance = pallet_balances::TotalIssuance::::get(); - let imbalance = >::burn(10); - assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance - 10); - drop(imbalance); - assert_eq!(pallet_balances::TotalIssuance::::get(), init_total_issuance); - }); -} - -#[test] -#[should_panic = "the balance of any account should always be at least the existential deposit."] -fn cannot_set_genesis_value_below_ed() { - EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = 11); - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - crate::GenesisConfig:: { balances: vec![(account_id(1), 10)] } - .assimilate_storage(&mut t) - .unwrap(); -} - -#[test] -#[should_panic = "duplicate balances in genesis."] -fn cannot_set_genesis_value_twice() { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - crate::GenesisConfig:: { - balances: vec![(account_id(1), 10), (account_id(2), 20), (account_id(1), 15)], - } - .assimilate_storage(&mut t) - .unwrap(); -} - -#[test] -fn existential_deposit_respected_when_reserving() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 101)); - // Check balance - assert_eq!(Balances::free_balance(account_id(1)), 101); - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - - // Reserve some free balance - assert_ok!(Balances::reserve(&account_id(1), 1)); - // Check balance, the account should be ok. - assert_eq!(Balances::free_balance(account_id(1)), 100); - assert_eq!(Balances::reserved_balance(account_id(1)), 1); - - // Cannot reserve any more of the free balance. - assert_noop!(Balances::reserve(&account_id(1), 1), DispatchError::ConsumerRemaining); - }); -} - -#[test] -fn slash_fails_when_account_needed() { - ExtBuilder::default().existential_deposit(50).build_and_execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 52)); - assert_ok!(Balances::reserve(&account_id(1), 1)); - // Check balance - assert_eq!(Balances::free_balance(account_id(1)), 51); - assert_eq!(Balances::reserved_balance(account_id(1)), 1); - - // Slash a small amount - let res = Balances::slash(&account_id(1), 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - // The account should be dead. - assert_eq!(Balances::free_balance(account_id(1)), 50); - assert_eq!(Balances::reserved_balance(account_id(1)), 1); - - // Slashing again doesn't work since we require the ED - let res = Balances::slash(&account_id(1), 1); - assert_eq!(res, (NegativeImbalance::new(0), 1)); - - // The account should be dead. - assert_eq!(Balances::free_balance(account_id(1)), 50); - assert_eq!(Balances::reserved_balance(account_id(1)), 1); - }); -} - -#[test] -fn account_deleted_when_just_dust() { - ExtBuilder::default().existential_deposit(50).build_and_execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 50)); - // Check balance - assert_eq!(Balances::free_balance(account_id(1)), 50); - - // Slash a small amount - let res = Balances::slash(&account_id(1), 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - // The account should be dead. - assert_eq!(Balances::free_balance(account_id(1)), 0); - }); -} - -#[test] -fn emit_events_with_reserve_and_unreserve() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 100); - - System::set_block_number(2); - assert_ok!(Balances::reserve(&account_id(1), 10)); - - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Reserved { - who: account_id(1), - amount: 10, - })); - - System::set_block_number(3); - assert!(Balances::unreserve(&account_id(1), 5).is_zero()); - - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { - who: account_id(1), - amount: 5, - })); - - System::set_block_number(4); - assert_eq!(Balances::unreserve(&account_id(1), 6), 1); - - // should only unreserve 5 - System::assert_last_event(RuntimeEvent::Balances(crate::Event::Unreserved { - who: account_id(1), - amount: 5, - })); - }); -} - -#[test] -fn emit_events_with_changing_locks() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 100); - System::reset_events(); - - // Locks = [] --> [10] - Balances::set_lock(*b"LOCK_000", &account_id(1), 10, WithdrawReasons::TRANSFER); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Locked { who: account_id(1), amount: 10 })] - ); - - // Locks = [10] --> [15] - Balances::set_lock(*b"LOCK_000", &account_id(1), 15, WithdrawReasons::TRANSFER); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Locked { who: account_id(1), amount: 5 })] - ); - - // Locks = [15] --> [15, 20] - Balances::set_lock(*b"LOCK_001", &account_id(1), 20, WithdrawReasons::TRANSACTION_PAYMENT); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Locked { who: account_id(1), amount: 5 })] - ); - - // Locks = [15, 20] --> [17, 20] - Balances::set_lock(*b"LOCK_000", &account_id(1), 17, WithdrawReasons::TRANSACTION_PAYMENT); - for event in events() { - match event { - RuntimeEvent::Balances(crate::Event::Locked { .. }) => { - assert!(false, "unexpected lock event") - }, - RuntimeEvent::Balances(crate::Event::Unlocked { .. }) => { - assert!(false, "unexpected unlock event") - }, - _ => continue, - } - } - - // Locks = [17, 20] --> [17, 15] - Balances::set_lock(*b"LOCK_001", &account_id(1), 15, WithdrawReasons::TRANSFER); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Unlocked { who: account_id(1), amount: 3 })] - ); - - // Locks = [17, 15] --> [15] - Balances::remove_lock(*b"LOCK_000", &account_id(1)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Unlocked { who: account_id(1), amount: 2 })] - ); - - // Locks = [15] --> [] - Balances::remove_lock(*b"LOCK_001", &account_id(1)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Unlocked { who: account_id(1), amount: 15 })] - ); - }); -} - -#[test] -fn emit_events_with_existential_deposit() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 100)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::NewAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::Endowed { - account: account_id(1), - free_balance: 100 - }), - RuntimeEvent::Balances(crate::Event::Issued { amount: 100 }), - RuntimeEvent::Balances(crate::Event::BalanceSet { who: account_id(1), free: 100 }), - ] - ); - - let res = Balances::slash(&account_id(1), 1); - assert_eq!(res, (NegativeImbalance::new(1), 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::DustLost { - account: account_id(1), - amount: 99 - }), - RuntimeEvent::Balances(crate::Event::Slashed { who: account_id(1), amount: 1 }), - RuntimeEvent::Balances(crate::Event::Rescinded { amount: 1 }), - ] - ); - }); -} - -#[test] -fn emit_events_with_no_existential_deposit_suicide() { - ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 100); - - assert_eq!( - events(), - [ - RuntimeEvent::Balances(crate::Event::BalanceSet { who: account_id(1), free: 100 }), - RuntimeEvent::System(system::Event::NewAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::Endowed { - account: account_id(1), - free_balance: 100 - }), - RuntimeEvent::Balances(crate::Event::Issued { amount: 100 }), - ] - ); - - let res = Balances::slash(&account_id(1), 100); - assert_eq!(res, (NegativeImbalance::new(100), 0)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::Slashed { who: account_id(1), amount: 100 }), - RuntimeEvent::Balances(crate::Event::Rescinded { amount: 100 }), - ] - ); - }); -} - -#[test] -fn slash_over_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // SCENARIO: Over-slash will kill account, and report missing slash amount. - Balances::make_free_balance_be(&account_id(1), 1_000); - // Slashed full free_balance, and reports 300 not slashed - assert_eq!(Balances::slash(&account_id(1), 1_300), (NegativeImbalance::new(1000), 300)); - // Account is dead - assert!(!System::account_exists(&account_id(1))); - }); -} - -#[test] -fn slash_full_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 1_000), (NegativeImbalance::new(1000), 0)); - // Account is still alive - assert!(!System::account_exists(&account_id(1))); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Slashed { - who: account_id(1), - amount: 1000, - })); - }); -} - -#[test] -fn slash_partial_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&account_id(1))); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Slashed { - who: account_id(1), - amount: 900, - })); - }); -} - -#[test] -fn slash_dusting_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 950), (NegativeImbalance::new(950), 0)); - assert!(!System::account_exists(&account_id(1))); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Slashed { - who: account_id(1), - amount: 950, - })); - }); -} - -#[test] -fn slash_does_not_take_from_reserve() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(Balances::reserve(&account_id(1), 100)); - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 900), (NegativeImbalance::new(800), 100)); - assert_eq!(Balances::reserved_balance(account_id(1)), 100); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Slashed { - who: account_id(1), - amount: 800, - })); - }); -} - -#[test] -fn slash_consumed_slash_full_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(System::inc_consumers(&account_id(1))); // <-- Reference counter added here is enough for all tests - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 900), (NegativeImbalance::new(900), 0)); - // Account is still alive - assert!(System::account_exists(&account_id(1))); - }); -} - -#[test] -fn slash_consumed_slash_over_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(System::inc_consumers(&account_id(1))); // <-- Reference counter added here is enough for all tests - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 1_000), (NegativeImbalance::new(900), 100)); - // Account is still alive - assert!(System::account_exists(&account_id(1))); - }); -} - -#[test] -fn slash_consumed_slash_partial_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(System::inc_consumers(&account_id(1))); // <-- Reference counter added here is enough for all tests - // Slashed completed in full - assert_eq!(Balances::slash(&account_id(1), 800), (NegativeImbalance::new(800), 0)); - // Account is still alive - assert!(System::account_exists(&account_id(1))); - }); -} - -#[test] -fn slash_on_non_existent_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // Slash on non-existent account is okay. - assert_eq!(Balances::slash(&account_id(123), 1_300), (NegativeImbalance::new(0), 1300)); - }); -} - -#[test] -fn slash_reserved_slash_partial_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(Balances::reserve(&account_id(1), 900)); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&account_id(1), 800), (NegativeImbalance::new(800), 0)); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_eq!(Balances::reserved_balance(account_id(1)), 100); - assert_eq!(Balances::free_balance(account_id(1)), 100); - }); -} - -#[test] -fn slash_reserved_slash_everything_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(Balances::reserve(&account_id(1), 900)); - assert_eq!(System::consumers(&account_id(1)), 1); - // Slashed completed in full - assert_eq!(Balances::slash_reserved(&account_id(1), 900), (NegativeImbalance::new(900), 0)); - assert_eq!(System::consumers(&account_id(1)), 0); - // Account is still alive - assert!(System::account_exists(&account_id(1))); - }); -} - -#[test] -fn slash_reserved_overslash_does_not_touch_free_balance() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // SCENARIO: Over-slash doesn't touch free balance. - Balances::make_free_balance_be(&account_id(1), 1_000); - assert_ok!(Balances::reserve(&account_id(1), 800)); - // Slashed done - assert_eq!( - Balances::slash_reserved(&account_id(1), 900), - (NegativeImbalance::new(800), 100) - ); - assert_eq!(Balances::free_balance(account_id(1)), 200); - }); -} - -#[test] -fn slash_reserved_on_non_existent_works() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - // Slash on non-existent account is okay. - assert_eq!( - Balances::slash_reserved(&account_id(123), 1_300), - (NegativeImbalance::new(0), 1300) - ); - }); -} - -#[test] -fn operations_on_dead_account_should_not_change_state() { - // These functions all use `mutate_account` which may introduce a storage change when - // the account never existed to begin with, and shouldn't exist in the end. - ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { - assert!(!frame_system::Account::::contains_key(account_id(137))); - - // Unreserve - assert_storage_noop!(assert_eq!(Balances::unreserve(&account_id(137), 42), 42)); - // Reserve - assert_noop!( - Balances::reserve(&account_id(137), 42), - Error::::InsufficientBalance - ); - // Slash Reserve - assert_storage_noop!(assert_eq!(Balances::slash_reserved(&account_id(137), 42).1, 42)); - // Repatriate Reserve - assert_noop!( - Balances::repatriate_reserved(&account_id(137), &account_id(138), 42, Free), - Error::::DeadAccount - ); - // Slash - assert_storage_noop!(assert_eq!(Balances::slash(&account_id(137), 42).1, 42)); - }); -} - -#[test] -#[should_panic = "The existential deposit must be greater than zero!"] -fn zero_ed_is_prohibited() { - // These functions all use `mutate_account` which may introduce a storage change when - // the account never existed to begin with, and shouldn't exist in the end. - ExtBuilder::default().existential_deposit(0).build_and_execute_with(|| { - Balances::integrity_test(); - }); -} - -#[test] -fn named_reserve_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - - let id_1 = TestId::Foo; - let id_2 = TestId::Bar; - let id_3 = TestId::Baz; - - // reserve - - assert_noop!( - Balances::reserve_named(&id_1, &account_id(1), 112), - Error::::InsufficientBalance - ); - - assert_ok!(Balances::reserve_named(&id_1, &account_id(1), 12)); - - assert_eq!(Balances::reserved_balance(account_id(1)), 12); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 12); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 0); - - assert_ok!(Balances::reserve_named(&id_1, &account_id(1), 2)); - - assert_eq!(Balances::reserved_balance(account_id(1)), 14); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 0); - - assert_ok!(Balances::reserve_named(&id_2, &account_id(1), 23)); - - assert_eq!(Balances::reserved_balance(account_id(1)), 37); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 23); - - assert_ok!(Balances::reserve(&account_id(1), 34)); - - assert_eq!(Balances::reserved_balance(account_id(1)), 71); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 14); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 23); - - assert_eq!(Balances::total_balance(&account_id(1)), 111); - assert_eq!(Balances::free_balance(account_id(1)), 40); - - assert_noop!( - Balances::reserve_named(&id_3, &account_id(1), 2), - Error::::TooManyReserves - ); - - // unreserve - - assert_eq!(Balances::unreserve_named(&id_1, &account_id(1), 10), 0); - - assert_eq!(Balances::reserved_balance(account_id(1)), 61); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 4); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 23); - - assert_eq!(Balances::unreserve_named(&id_1, &account_id(1), 5), 1); - - assert_eq!(Balances::reserved_balance(account_id(1)), 57); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 23); - - assert_eq!(Balances::unreserve_named(&id_2, &account_id(1), 3), 0); - - assert_eq!(Balances::reserved_balance(account_id(1)), 54); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 20); - - assert_eq!(Balances::total_balance(&account_id(1)), 111); - assert_eq!(Balances::free_balance(account_id(1)), 57); - - // slash_reserved_named - - assert_ok!(Balances::reserve_named(&id_1, &account_id(1), 10)); - - assert_eq!(Balances::slash_reserved_named(&id_1, &account_id(1), 25).1, 15); - - assert_eq!(Balances::reserved_balance(account_id(1)), 54); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 20); - assert_eq!(Balances::total_balance(&account_id(1)), 101); - - assert_eq!(Balances::slash_reserved_named(&id_2, &account_id(1), 5).1, 0); - - assert_eq!(Balances::reserved_balance(account_id(1)), 49); - assert_eq!(Balances::reserved_balance_named(&id_1, &account_id(1)), 0); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 15); - assert_eq!(Balances::total_balance(&account_id(1)), 96); - - // repatriate_reserved_named - - let _ = Balances::deposit_creating(&account_id(2), 100); - - assert_eq!( - Balances::repatriate_reserved_named( - &id_2, - &account_id(1), - &account_id(2), - 10, - Reserved - ) - .unwrap(), - 0 - ); - - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 5); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(2)), 10); - assert_eq!(Balances::reserved_balance(account_id(2)), 10); - - assert_eq!( - Balances::repatriate_reserved_named( - &id_2, - &account_id(2), - &account_id(1), - 11, - Reserved - ) - .unwrap(), - 1 - ); - - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 15); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(2)), 0); - assert_eq!(Balances::reserved_balance(account_id(2)), 0); - - assert_eq!( - Balances::repatriate_reserved_named(&id_2, &account_id(1), &account_id(2), 10, Free) - .unwrap(), - 0 - ); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 5); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(2)), 0); - assert_eq!(Balances::free_balance(account_id(2)), 110); - - // repatriate_reserved_named to self - - assert_eq!( - Balances::repatriate_reserved_named( - &id_2, - &account_id(1), - &account_id(1), - 10, - Reserved - ) - .unwrap(), - 5 - ); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 5); - - assert_eq!(Balances::free_balance(account_id(1)), 47); - - assert_eq!( - Balances::repatriate_reserved_named(&id_2, &account_id(1), &account_id(1), 15, Free) - .unwrap(), - 10 - ); - assert_eq!(Balances::reserved_balance_named(&id_2, &account_id(1)), 0); - - assert_eq!(Balances::free_balance(account_id(1)), 52); - }); -} - -#[test] -fn reserve_must_succeed_if_can_reserve_does() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 1); - let _ = Balances::deposit_creating(&account_id(2), 2); - assert!( - Balances::can_reserve(&account_id(1), 1) == - Balances::reserve(&account_id(1), 1).is_ok() - ); - assert!( - Balances::can_reserve(&account_id(2), 1) == - Balances::reserve(&account_id(2), 1).is_ok() - ); - }); -} - -#[test] -fn reserved_named_to_yourself_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 110); - - let id = TestId::Foo; - - assert_ok!(Balances::reserve_named(&id, &account_id(1), 50)); - assert_ok!( - Balances::repatriate_reserved_named(&id, &account_id(1), &account_id(1), 50, Free), - 0 - ); - assert_eq!(Balances::free_balance(account_id(1)), 110); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 0); - - assert_ok!(Balances::reserve_named(&id, &account_id(1), 50)); - assert_ok!( - Balances::repatriate_reserved_named(&id, &account_id(1), &account_id(1), 60, Free), - 10 - ); - assert_eq!(Balances::free_balance(account_id(1)), 110); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 0); - }); -} - -#[test] -fn ensure_reserved_named_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - - let id = TestId::Foo; - - assert_ok!(Balances::ensure_reserved_named(&id, &account_id(1), 15)); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 15); - - assert_ok!(Balances::ensure_reserved_named(&id, &account_id(1), 10)); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 10); - - assert_ok!(Balances::ensure_reserved_named(&id, &account_id(1), 20)); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 20); - }); -} - -#[test] -fn unreserve_all_named_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - - let id = TestId::Foo; - - assert_ok!(Balances::reserve_named(&id, &account_id(1), 15)); - - assert_eq!(Balances::unreserve_all_named(&id, &account_id(1)), 15); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 0); - assert_eq!(Balances::free_balance(account_id(1)), 111); - - assert_eq!(Balances::unreserve_all_named(&id, &account_id(1)), 0); - }); -} - -#[test] -fn slash_all_reserved_named_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - - let id = TestId::Foo; - - assert_ok!(Balances::reserve_named(&id, &account_id(1), 15)); - - assert_eq!(Balances::slash_all_reserved_named(&id, &account_id(1)).peek(), 15); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 0); - assert_eq!(Balances::free_balance(account_id(1)), 96); - - assert_eq!(Balances::slash_all_reserved_named(&id, &account_id(1)).peek(), 0); - }); -} - -#[test] -fn repatriate_all_reserved_named_should_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::deposit_creating(&account_id(1), 111); - let _ = Balances::deposit_creating(&account_id(2), 10); - let _ = Balances::deposit_creating(&account_id(3), 10); - - let id = TestId::Foo; - - assert_ok!(Balances::reserve_named(&id, &account_id(1), 15)); - - assert_ok!(Balances::repatriate_all_reserved_named( - &id, - &account_id(1), - &account_id(2), - Reserved - )); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(1)), 0); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(2)), 15); - - assert_ok!(Balances::repatriate_all_reserved_named( - &id, - &account_id(2), - &account_id(3), - Free - )); - assert_eq!(Balances::reserved_balance_named(&id, &account_id(2)), 0); - assert_eq!(Balances::free_balance(account_id(3)), 25); - }); -} - -#[test] -fn freezing_and_locking_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Consumer is shared between freezing and locking. - assert_eq!(System::consumers(&account_id(1)), 0); - assert_ok!(>::set_freeze( - &TestId::Foo, - &account_id(1), - 4 - )); - assert_eq!(System::consumers(&account_id(1)), 1); - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(System::consumers(&account_id(1)), 1); - - // Frozen and locked balances update correctly. - assert_eq!(Balances::account(&account_id(1)).frozen, 5); - assert_ok!(>::set_freeze( - &TestId::Foo, - &account_id(1), - 6 - )); - assert_eq!(Balances::account(&account_id(1)).frozen, 6); - assert_ok!(>::set_freeze( - &TestId::Foo, - &account_id(1), - 4 - )); - assert_eq!(Balances::account(&account_id(1)).frozen, 5); - Balances::set_lock(ID_1, &account_id(1), 3, WithdrawReasons::all()); - assert_eq!(Balances::account(&account_id(1)).frozen, 4); - Balances::set_lock(ID_1, &account_id(1), 5, WithdrawReasons::all()); - assert_eq!(Balances::account(&account_id(1)).frozen, 5); - - // Locks update correctly. - Balances::remove_lock(ID_1, &account_id(1)); - assert_eq!(Balances::account(&account_id(1)).frozen, 4); - assert_ok!(>::thaw(&TestId::Foo, &account_id(1))); - assert_eq!(Balances::account(&account_id(1)).frozen, 0); - assert_eq!(System::consumers(&account_id(1)), 0); - }); -} - -#[test] -fn self_transfer_noop() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_eq!(pallet_balances::TotalIssuance::::get(), 0); - let _ = Balances::deposit_creating(&account_id(1), 100); - - // The account is set up properly: - assert_eq!( - events(), - [ - Event::Deposit { who: account_id(1), amount: 100 }.into(), - SysEvent::NewAccount { account: account_id(1) }.into(), - Event::Endowed { account: account_id(1), free_balance: 100 }.into(), - Event::Issued { amount: 100 }.into(), - ] - ); - assert_eq!(Balances::free_balance(account_id(1)), 100); - assert_eq!(pallet_balances::TotalIssuance::::get(), 100); - - // Transfers to self are No-OPs: - let _g = StorageNoopGuard::new(); - for i in 0..200 { - let r = Balances::transfer_allow_death(Some(account_id(1)).into(), account_id(1), i); - - if i <= 100 { - assert_ok!(r); - } else { - assert!(r.is_err()); - } - - assert!(events().is_empty()); - assert_eq!( - Balances::free_balance(account_id(1)), - 100, - "Balance unchanged by self transfer" - ); - assert_eq!( - pallet_balances::TotalIssuance::::get(), - 100, - "TI unchanged by self transfers" - ); - } - }); -} diff --git a/pallets/balances/src/tests/dispatchable_tests.rs b/pallets/balances/src/tests/dispatchable_tests.rs deleted file mode 100644 index 94991c96..00000000 --- a/pallets/balances/src/tests/dispatchable_tests.rs +++ /dev/null @@ -1,410 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests regarding the functionality of the dispatchables/extrinsics. - -use super::*; -use crate::{ - AdjustmentDirection::{Decrease as Dec, Increase as Inc}, - Event, -}; -use frame_support::traits::{fungible::Unbalanced, tokens::Preservation::Expendable}; -use fungible::{hold::Mutate as HoldMutate, Inspect, Mutate}; - -/// Alice account ID for more readable tests. -fn alice() -> AccountId { - account_id(1) -} - -#[test] -fn default_indexing_on_new_accounts_should_not_work2() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - // account 5 should not exist - // ext_deposit is 10, value is 9, not satisfies for ext_deposit - assert_noop!( - Balances::transfer_allow_death(Some(account_id(1)).into(), account_id(5), 9), - TokenError::BelowMinimum, - ); - assert_eq!(Balances::free_balance(account_id(1)), 100); - }); -} - -#[test] -fn dust_account_removal_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .monied(true) - .build_and_execute_with(|| { - System::inc_account_nonce(account_id(2)); - assert_eq!(System::account_nonce(account_id(2)), 1); - assert_eq!(Balances::total_balance(&account_id(2)), 2000); - // index 1 (account 2) becomes zombie - assert_ok!(Balances::transfer_allow_death( - Some(account_id(2)).into(), - account_id(5), - 1901 - )); - assert_eq!(Balances::total_balance(&account_id(2)), 0); - assert_eq!(Balances::total_balance(&account_id(5)), 1901); - assert_eq!(System::account_nonce(account_id(2)), 0); - }); -} - -#[test] -fn balance_transfer_works() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::mint_into(&account_id(1), 111); - assert_ok!(Balances::transfer_allow_death(Some(account_id(1)).into(), account_id(2), 69)); - assert_eq!(Balances::total_balance(&account_id(1)), 42); - assert_eq!(Balances::total_balance(&account_id(2)), 69); - }); -} - -#[test] -fn force_transfer_works() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::mint_into(&account_id(1), 111); - assert_noop!( - Balances::force_transfer(Some(account_id(2)).into(), account_id(1), account_id(2), 69), - BadOrigin, - ); - assert_ok!(Balances::force_transfer( - RawOrigin::Root.into(), - account_id(1), - account_id(2), - 69 - )); - assert_eq!(Balances::total_balance(&account_id(1)), 42); - assert_eq!(Balances::total_balance(&account_id(2)), 69); - }); -} - -#[test] -fn balance_transfer_when_on_hold_should_not_work() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::mint_into(&account_id(1), 111); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 69)); - assert_noop!( - Balances::transfer_allow_death(Some(account_id(1)).into(), account_id(2), 69), - TokenError::FundsUnavailable, - ); - }); -} - -#[test] -fn transfer_keep_alive_works() { - ExtBuilder::default().existential_deposit(1).build_and_execute_with(|| { - let _ = Balances::mint_into(&account_id(1), 100); - assert_noop!( - Balances::transfer_keep_alive(Some(account_id(1)).into(), account_id(2), 100), - TokenError::NotExpendable - ); - assert_eq!(Balances::total_balance(&account_id(1)), 100); - assert_eq!(Balances::total_balance(&account_id(2)), 0); - }); -} - -#[test] -fn transfer_keep_alive_all_free_succeed() { - ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 300)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 100)); - assert_ok!(Balances::transfer_keep_alive(Some(account_id(1)).into(), account_id(2), 100)); - assert_eq!(Balances::total_balance(&account_id(1)), 200); - assert_eq!(Balances::total_balance(&account_id(2)), 100); - }); -} - -#[test] -fn transfer_all_works_1() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // setup - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 200)); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(2), 0)); - // transfer all and allow death - assert_ok!(Balances::transfer_all(Some(account_id(1)).into(), account_id(2), false)); - assert_eq!(Balances::total_balance(&account_id(1)), 0); - assert_eq!(Balances::total_balance(&account_id(2)), 200); - }); -} - -#[test] -fn transfer_all_works_2() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // setup - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 200)); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(2), 0)); - // transfer all and keep alive - assert_ok!(Balances::transfer_all(Some(account_id(1)).into(), account_id(2), true)); - assert_eq!(Balances::total_balance(&account_id(1)), 100); - assert_eq!(Balances::total_balance(&account_id(2)), 100); - }); -} - -#[test] -fn transfer_all_works_3() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // setup - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 210)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 10)); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(2), 0)); - // transfer all and allow death w/ reserved - assert_ok!(Balances::transfer_all(Some(account_id(1)).into(), account_id(2), false)); - assert_eq!(Balances::total_balance(&account_id(1)), 110); - assert_eq!(Balances::total_balance(&account_id(2)), 100); - }); -} - -#[test] -fn transfer_all_works_4() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // setup - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 210)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 10)); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(2), 0)); - // transfer all and keep alive w/ reserved - assert_ok!(Balances::transfer_all(Some(account_id(1)).into(), account_id(2), true)); - assert_eq!(Balances::total_balance(&account_id(1)), 110); - assert_eq!(Balances::total_balance(&account_id(2)), 100); - }); -} - -#[test] -fn set_balance_handles_killing_account() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::mint_into(&account_id(1), 111); - assert_ok!(frame_system::Pallet::::inc_consumers(&account_id(1))); - assert_noop!( - Balances::force_set_balance(RuntimeOrigin::root(), account_id(1), 0), - DispatchError::ConsumerRemaining, - ); - }); -} - -#[test] -fn set_balance_handles_total_issuance() { - ExtBuilder::default().build_and_execute_with(|| { - let old_total_issuance = pallet_balances::TotalIssuance::::get(); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(137), 69)); - assert_eq!(pallet_balances::TotalIssuance::::get(), old_total_issuance + 69); - assert_eq!(Balances::total_balance(&account_id(137)), 69); - assert_eq!(Balances::free_balance(account_id(137)), 69); - }); -} - -#[test] -fn upgrade_accounts_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - System::inc_providers(&account_id(7)); - assert_ok!(::AccountStore::try_mutate_exists( - &account_id(7), - |a| -> DispatchResult { - *a = Some(AccountData { - free: 5, - reserved: 5, - frozen: Zero::zero(), - flags: crate::types::ExtraFlags::old_logic(), - }); - Ok(()) - } - )); - assert!(!Balances::account(&account_id(7)).flags.is_new_logic()); - assert_eq!(System::providers(&account_id(7)), 1); - assert_eq!(System::consumers(&account_id(7)), 0); - assert_ok!(Balances::upgrade_accounts(Some(account_id(1)).into(), vec![account_id(7)])); - assert!(Balances::account(&account_id(7)).flags.is_new_logic()); - assert_eq!(System::providers(&account_id(7)), 1); - assert_eq!(System::consumers(&account_id(7)), 1); - - >::unreserve( - &account_id(7), - 5, - ); - assert_ok!(>::transfer( - &account_id(7), - &account_id(1), - 10, - Expendable - )); - assert_eq!(Balances::total_balance(&account_id(7)), 0); - assert_eq!(System::providers(&account_id(7)), 0); - assert_eq!(System::consumers(&account_id(7)), 0); - }); -} - -#[test] -#[docify::export] -fn force_adjust_total_issuance_example() { - ExtBuilder::default().build_and_execute_with(|| { - // First we set the TotalIssuance to 64 by giving Alice a balance of 64. - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), alice(), 64)); - let old_ti = pallet_balances::TotalIssuance::::get(); - assert_eq!(old_ti, 64, "TI should be 64"); - - // Now test the increase: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); - let new_ti = pallet_balances::TotalIssuance::::get(); - assert_eq!(old_ti + 32, new_ti, "Should increase by 32"); - - // If Alice tries to call it, it errors: - assert_noop!( - Balances::force_adjust_total_issuance(RawOrigin::Signed(alice()).into(), Inc, 69), - BadOrigin, - ); - }); -} - -#[test] -fn force_adjust_total_issuance_works() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(137), 64)); - let ti = pallet_balances::TotalIssuance::::get(); - - // Increase works: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); - assert_eq!(pallet_balances::TotalIssuance::::get(), ti + 32); - System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { - old: 64, - new: 96, - })); - - // Decrease works: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 64)); - assert_eq!(pallet_balances::TotalIssuance::::get(), ti - 32); - System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { - old: 96, - new: 32, - })); - }); -} - -#[test] -fn force_adjust_total_issuance_saturates() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(137), 64)); - let ti = pallet_balances::TotalIssuance::::get(); - let max = ::Balance::max_value(); - assert_eq!(ti, 64); - - // Increment saturates: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, max)); - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 123)); - assert_eq!(pallet_balances::TotalIssuance::::get(), max); - - // Decrement saturates: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, max)); - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 123)); - assert_eq!(pallet_balances::TotalIssuance::::get(), 0); - }); -} - -#[test] -fn force_adjust_total_issuance_rejects_zero_delta() { - ExtBuilder::default().build_and_execute_with(|| { - assert_noop!( - Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 0), - Error::::DeltaZero, - ); - assert_noop!( - Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 0), - Error::::DeltaZero, - ); - }); -} - -#[test] -fn force_adjust_total_issuance_rejects_more_than_inactive() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), account_id(137), 64)); - Balances::deactivate(16u32.into()); - - assert_eq!(pallet_balances::TotalIssuance::::get(), 64); - assert_eq!(Balances::active_issuance(), 48); - - // Works with up to 48: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 40),); - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 8),); - assert_eq!(pallet_balances::TotalIssuance::::get(), 16); - assert_eq!(Balances::active_issuance(), 0); - // Errors with more than 48: - assert_noop!( - Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 1), - Error::::IssuanceDeactivated, - ); - // Increasing again increases the inactive issuance: - assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 10),); - assert_eq!(pallet_balances::TotalIssuance::::get(), 26); - assert_eq!(Balances::active_issuance(), 10); - }); -} - -#[test] -fn burn_works() { - ExtBuilder::default().build().execute_with(|| { - // Prepare account with initial balance - let (account, init_balance) = (account_id(1), 37); - assert_ok!(Balances::force_set_balance( - RuntimeOrigin::root(), - account.clone(), - init_balance - )); - let init_issuance = pallet_balances::TotalIssuance::::get(); - let (keep_alive, allow_death) = (true, false); - - // 1. Cannot burn more than what's available - assert_noop!( - Balances::burn(Some(account.clone()).into(), init_balance + 1, allow_death), - TokenError::FundsUnavailable, - ); - - // 2. Burn some funds, without reaping the account - let burn_amount_1 = 1; - assert_ok!(Balances::burn(Some(account.clone()).into(), burn_amount_1, allow_death)); - System::assert_last_event(RuntimeEvent::Balances(Event::Burned { - who: account.clone(), - amount: burn_amount_1, - })); - assert_eq!(pallet_balances::TotalIssuance::::get(), init_issuance - burn_amount_1); - assert_eq!(Balances::total_balance(&account), init_balance - burn_amount_1); - - // 3. Cannot burn funds below existential deposit if `keep_alive` is `true` - let burn_amount_2 = - init_balance - burn_amount_1 - ::ExistentialDeposit::get() + 1; - assert_noop!( - Balances::burn(Some(account.clone()).into(), init_balance + 1, keep_alive), - TokenError::FundsUnavailable, - ); - - // 4. Burn some more funds, this time reaping the account - assert_ok!(Balances::burn(Some(account.clone()).into(), burn_amount_2, allow_death)); - System::assert_last_event(RuntimeEvent::Balances(Event::Burned { - who: account.clone(), - amount: burn_amount_2, - })); - assert_eq!( - pallet_balances::TotalIssuance::::get(), - init_issuance - burn_amount_1 - burn_amount_2 - ); - assert!(Balances::total_balance(&account).is_zero()); - }); -} diff --git a/pallets/balances/src/tests/fungible_conformance_tests.rs b/pallets/balances/src/tests/fungible_conformance_tests.rs deleted file mode 100644 index 5c0c19a5..00000000 --- a/pallets/balances/src/tests/fungible_conformance_tests.rs +++ /dev/null @@ -1,141 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::*; -use frame_support::traits::fungible::{conformance_tests, Inspect, Mutate}; -use paste::paste; - -macro_rules! generate_tests { - // Handle a conformance test that requires special testing with and without a dust trap. - (dust_trap_variation, $base_path:path, $scope:expr, $trait:ident, $ext_deposit:expr, $($test_name:ident),*) => { - $( - paste! { - #[test] - fn [<$trait _ $scope _ $test_name _existential_deposit_ $ext_deposit _dust_trap_on >]() { - // Some random trap account. - let trap_account = ::AccountId::from(65174286u64); - let builder = ExtBuilder::default().existential_deposit($ext_deposit).dust_trap(trap_account); - builder.build_and_execute_with(|| { - Balances::set_balance(&trap_account, Balances::minimum_balance()); - $base_path::$scope::$trait::$test_name::< - Balances, - ::AccountId, - >(Some(trap_account)); - }); - } - - #[test] - fn [< $trait _ $scope _ $test_name _existential_deposit_ $ext_deposit _dust_trap_off >]() { - let builder = ExtBuilder::default().existential_deposit($ext_deposit); - builder.build_and_execute_with(|| { - $base_path::$scope::$trait::$test_name::< - Balances, - ::AccountId, - >(None); - }); - } - } - )* - }; - // Regular conformance test - ($base_path:path, $scope:expr, $trait:ident, $ext_deposit:expr, $($test_name:ident),*) => { - $( - paste! { - #[test] - fn [< $trait _ $scope _ $test_name _existential_deposit_ $ext_deposit>]() { - let builder = ExtBuilder::default().existential_deposit($ext_deposit); - builder.build_and_execute_with(|| { - $base_path::$scope::$trait::$test_name::< - Balances, - ::AccountId, - >(); - }); - } - } - )* - }; - ($base_path:path, $ext_deposit:expr) => { - // regular::mutate - generate_tests!( - dust_trap_variation, - $base_path, - regular, - mutate, - $ext_deposit, - transfer_expendable_dust - ); - generate_tests!( - $base_path, - regular, - mutate, - $ext_deposit, - mint_into_success, - mint_into_overflow, - mint_into_below_minimum, - burn_from_exact_success, - burn_from_best_effort_success, - burn_from_exact_insufficient_funds, - restore_success, - restore_overflow, - restore_below_minimum, - shelve_success, - shelve_insufficient_funds, - transfer_success, - transfer_expendable_all, - transfer_protect_preserve, - set_balance_mint_success, - set_balance_burn_success, - can_deposit_success, - can_deposit_below_minimum, - can_deposit_overflow, - can_withdraw_success, - can_withdraw_reduced_to_zero, - can_withdraw_balance_low, - reducible_balance_expendable, - reducible_balance_protect_preserve - ); - // regular::unbalanced - generate_tests!( - $base_path, - regular, - unbalanced, - $ext_deposit, - write_balance, - decrease_balance_expendable, - decrease_balance_preserve, - increase_balance, - set_total_issuance, - deactivate_and_reactivate - ); - // regular::balanced - generate_tests!( - $base_path, - regular, - balanced, - $ext_deposit, - issue_and_resolve_credit, - rescind_and_settle_debt, - deposit, - withdraw, - pair - ); - }; -} - -generate_tests!(conformance_tests, 1); -generate_tests!(conformance_tests, 5); -generate_tests!(conformance_tests, 1000); diff --git a/pallets/balances/src/tests/fungible_tests.rs b/pallets/balances/src/tests/fungible_tests.rs deleted file mode 100644 index 4840258e..00000000 --- a/pallets/balances/src/tests/fungible_tests.rs +++ /dev/null @@ -1,883 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests regarding the functionality of the `fungible` trait set implementations. - -use super::*; -use frame_support::traits::{ - tokens::{ - Fortitude::{Force, Polite}, - Precision::{BestEffort, Exact}, - Preservation::{Expendable, Preserve, Protect}, - Restriction::Free, - }, - Consideration, Footprint, LinearStoragePrice, MaybeConsideration, -}; -use fungible::{ - FreezeConsideration, HoldConsideration, Inspect, InspectFreeze, InspectHold, - LoneFreezeConsideration, LoneHoldConsideration, Mutate, MutateFreeze, MutateHold, Unbalanced, -}; -use sp_core::ConstU128; - -#[test] -fn inspect_trait_reducible_balance_basic_works() { - ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { - Balances::set_balance(&account_id(1), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Polite), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Polite), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Polite), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Force), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Force), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Force), 90); - }); -} - -#[test] -fn inspect_trait_reducible_balance_other_provide_works() { - ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { - Balances::set_balance(&account_id(1), 100); - System::inc_providers(&account_id(1)); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Polite), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Polite), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Polite), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Force), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Force), 100); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Force), 90); - }); -} - -#[test] -fn inspect_trait_reducible_balance_frozen_works() { - ExtBuilder::default().existential_deposit(10).build_and_execute_with(|| { - Balances::set_balance(&account_id(1), 100); - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 50)); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Polite), 50); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Polite), 50); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Polite), 50); - assert_eq!(Balances::reducible_balance(&account_id(1), Expendable, Force), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Protect, Force), 90); - assert_eq!(Balances::reducible_balance(&account_id(1), Preserve, Force), 90); - }); -} - -#[test] -fn unbalanced_trait_set_balance_works() { - ExtBuilder::default().build_and_execute_with(|| { - assert_eq!(>::balance(&account_id(137)), 0); - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_eq!(>::balance(&account_id(137)), 100); - - assert_ok!(>::hold(&TestId::Foo, &account_id(137), 60)); - assert_eq!(>::balance(&account_id(137)), 40); - assert_eq!( - >::total_balance_on_hold(&account_id(137)), - 60 - ); - assert_eq!( - >::balance_on_hold(&TestId::Foo, &account_id(137)), - 60 - ); - - assert_noop!( - Balances::write_balance(&account_id(137), 0), - Error::::InsufficientBalance - ); - - assert_ok!(Balances::write_balance(&account_id(137), 1)); - assert_eq!(>::balance(&account_id(137)), 1); - assert_eq!( - >::balance_on_hold(&TestId::Foo, &account_id(137)), - 60 - ); - - assert_ok!(>::release( - &TestId::Foo, - &account_id(137), - 60, - Exact - )); - assert_eq!( - >::balance_on_hold(&TestId::Foo, &account_id(137)), - 0 - ); - assert_eq!( - >::total_balance_on_hold(&account_id(137)), - 0 - ); - }); -} - -#[test] -fn unbalanced_trait_set_total_issuance_works() { - ExtBuilder::default().build_and_execute_with(|| { - assert_eq!(>::total_issuance(), 0); - Balances::set_total_issuance(100); - assert_eq!(>::total_issuance(), 100); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_simple_works() { - ExtBuilder::default().build_and_execute_with(|| { - // An Account that starts at 100 - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_eq!(>::balance(&account_id(137)), 100); - // and reserves 50 - assert_ok!(>::hold(&TestId::Foo, &account_id(137), 50)); - assert_eq!(>::balance(&account_id(137)), 50); - // and is decreased by 20 - assert_ok!(Balances::decrease_balance(&account_id(137), 20, Exact, Expendable, Polite)); - assert_eq!(>::balance(&account_id(137)), 30); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_works_1() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_eq!(>::balance(&account_id(137)), 100); - - assert_noop!( - Balances::decrease_balance(&account_id(137), 101, Exact, Expendable, Polite), - TokenError::FundsUnavailable - ); - assert_eq!( - Balances::decrease_balance(&account_id(137), 100, Exact, Expendable, Polite), - Ok(100) - ); - assert_eq!(>::balance(&account_id(137)), 0); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_works_2() { - ExtBuilder::default().build_and_execute_with(|| { - // free: 40, reserved: 60 - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(137), 60)); - assert_eq!(>::balance(&account_id(137)), 40); - assert_eq!(Balances::total_balance_on_hold(&account_id(137)), 60); - assert_noop!( - Balances::decrease_balance(&account_id(137), 40, Exact, Expendable, Polite), - TokenError::FundsUnavailable - ); - assert_eq!( - Balances::decrease_balance(&account_id(137), 39, Exact, Expendable, Polite), - Ok(39) - ); - assert_eq!(>::balance(&account_id(137)), 1); - assert_eq!(Balances::total_balance_on_hold(&account_id(137)), 60); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_at_most_works_1() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_eq!(>::balance(&account_id(137)), 100); - - assert_eq!( - Balances::decrease_balance(&account_id(137), 101, BestEffort, Expendable, Polite), - Ok(100) - ); - assert_eq!(>::balance(&account_id(137)), 0); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_at_most_works_2() { - ExtBuilder::default().build_and_execute_with(|| { - assert_ok!(Balances::write_balance(&account_id(137), 99)); - assert_eq!( - Balances::decrease_balance(&account_id(137), 99, BestEffort, Expendable, Polite), - Ok(99) - ); - assert_eq!(>::balance(&account_id(137)), 0); - }); -} - -#[test] -fn unbalanced_trait_decrease_balance_at_most_works_3() { - ExtBuilder::default().build_and_execute_with(|| { - // free: 40, reserved: 60 - assert_ok!(Balances::write_balance(&account_id(137), 100)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(137), 60)); - assert_eq!(Balances::free_balance(account_id(137)), 40); - assert_eq!(Balances::total_balance_on_hold(&account_id(137)), 60); - assert_eq!( - Balances::decrease_balance(&account_id(137), 0, BestEffort, Expendable, Polite), - Ok(0) - ); - assert_eq!(Balances::free_balance(account_id(137)), 40); - assert_eq!(Balances::total_balance_on_hold(&account_id(137)), 60); - assert_eq!( - Balances::decrease_balance(&account_id(137), 10, BestEffort, Expendable, Polite), - Ok(10) - ); - assert_eq!(Balances::free_balance(account_id(137)), 30); - assert_eq!( - Balances::decrease_balance(&account_id(137), 200, BestEffort, Expendable, Polite), - Ok(29) - ); - assert_eq!(>::balance(&account_id(137)), 1); - assert_eq!(Balances::free_balance(account_id(137)), 1); - assert_eq!(Balances::total_balance_on_hold(&account_id(137)), 60); - }); -} - -#[test] -fn unbalanced_trait_increase_balance_works() { - ExtBuilder::default().build_and_execute_with(|| { - assert_noop!( - Balances::increase_balance(&account_id(137), 0, Exact), - TokenError::BelowMinimum - ); - assert_eq!(Balances::increase_balance(&account_id(137), 1, Exact), Ok(1)); - assert_noop!( - Balances::increase_balance(&account_id(137), Balance::MAX, Exact), - ArithmeticError::Overflow - ); - }); -} - -#[test] -fn unbalanced_trait_increase_balance_at_most_works() { - ExtBuilder::default().build_and_execute_with(|| { - assert_eq!(Balances::increase_balance(&account_id(137), 0, BestEffort), Ok(0)); - assert_eq!(Balances::increase_balance(&account_id(137), 1, BestEffort), Ok(1)); - assert_eq!( - Balances::increase_balance(&account_id(137), Balance::MAX, BestEffort), - Ok(Balance::MAX - 1) - ); - }); -} - -#[test] -fn freezing_and_holds_should_overlap() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 10)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 9)); - assert_eq!(Balances::account(&account_id(1)).free, 1); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_eq!(Balances::account(&account_id(1)).free, 1); - assert_eq!(Balances::account(&account_id(1)).frozen, 10); - assert_eq!(Balances::account(&account_id(1)).reserved, 9); - assert_eq!(Balances::total_balance_on_hold(&account_id(1)), 9); - }); -} - -#[test] -fn frozen_hold_balance_cannot_be_moved_without_force() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 10)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 9)); - assert_eq!(Balances::reducible_total_balance_on_hold(&account_id(1), Force), 9); - assert_eq!(Balances::reducible_total_balance_on_hold(&account_id(1), Polite), 0); - let e = TokenError::Frozen; - assert_noop!( - Balances::transfer_on_hold( - &TestId::Foo, - &account_id(1), - &account_id(2), - 1, - Exact, - Free, - Polite - ), - e - ); - assert_ok!(Balances::transfer_on_hold( - &TestId::Foo, - &account_id(1), - &account_id(2), - 1, - Exact, - Free, - Force - )); - }); -} - -#[test] -fn frozen_hold_balance_best_effort_transfer_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 5)); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 9)); - assert_eq!(Balances::reducible_total_balance_on_hold(&account_id(1), Force), 9); - assert_eq!(Balances::reducible_total_balance_on_hold(&account_id(1), Polite), 5); - assert_ok!(Balances::transfer_on_hold( - &TestId::Foo, - &account_id(1), - &account_id(2), - 10, - BestEffort, - Free, - Polite - )); - assert_eq!(Balances::total_balance(&account_id(1)), 5); - assert_eq!(Balances::total_balance(&account_id(2)), 25); - }); -} - -#[test] -fn partial_freezing_should_work() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 5)); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 5, - Expendable - )); - // After transferring 5, balance is 95. With 10 frozen, can transfer up to 85 more - // (95-10=85) - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 85, - Expendable - )); - // Now balance is 10. Transferring 1 more would leave 9, which is < 10 frozen, so should - // fail - assert_noop!( - >::transfer( - &account_id(1), - &account_id(2), - 1, - Expendable - ), - TokenError::Frozen - ); - }); -} - -#[test] -fn thaw_should_work() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), Balance::MAX)); - assert_ok!(Balances::thaw(&TestId::Foo, &account_id(1))); - assert_eq!(System::consumers(&account_id(1)), 0); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &account_id(1)), 0); - assert_eq!(Balances::account(&account_id(1)).frozen, 0); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 10, - Expendable - )); - }); -} - -#[test] -fn set_freeze_zero_should_work() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), Balance::MAX)); - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 0)); - assert_eq!(System::consumers(&account_id(1)), 0); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &account_id(1)), 0); - assert_eq!(Balances::account(&account_id(1)).frozen, 0); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 10, - Expendable - )); - }); -} - -#[test] -fn set_freeze_should_work() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), Balance::MAX)); - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 5)); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 5, - Expendable - )); - // After transferring 5, balance is 95. With 10 frozen, can transfer up to 85 more - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 85, - Expendable - )); - // Now balance is 10. Transferring 1 more would leave 9, which is < 10 frozen, so should - // fail - assert_noop!( - >::transfer( - &account_id(1), - &account_id(2), - 1, - Expendable - ), - TokenError::Frozen - ); - }); -} - -#[test] -fn extend_freeze_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 5)); - assert_ok!(Balances::extend_freeze(&TestId::Foo, &account_id(1), 10)); - assert_eq!(Balances::account(&account_id(1)).frozen, 10); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &account_id(1)), 10); - assert_noop!( - >::transfer( - &account_id(1), - &account_id(2), - 1, - Expendable - ), - TokenError::Frozen - ); - }); -} - -#[test] -fn debug_freeze_behavior() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .build_and_execute_with(|| { - println!("=== Debug freeze behavior ==="); - println!("Initial balance: {}", Balances::free_balance(account_id(1))); - - // Set a freeze - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 10)); - println!("After freeze(10):"); - println!(" Free balance: {}", Balances::free_balance(account_id(1))); - println!(" Frozen amount: {}", Balances::balance_frozen(&TestId::Foo, &account_id(1))); - println!(" Account frozen: {}", Balances::account(&account_id(1)).frozen); - - // Try transfers of different amounts - for amount in [1, 5, 10, 89, 90, 91] { - let balance_before = Balances::free_balance(account_id(1)); - let result = >::transfer( - &account_id(1), - &account_id(2), - amount, - Expendable, - ); - let balance_after = Balances::free_balance(account_id(1)); - - println!( - "Transfer {} units: {:?} (balance: {} -> {})", - amount, result, balance_before, balance_after - ); - - // Restore balance for next test - if result.is_ok() { - let _ = >::transfer( - &account_id(2), - &account_id(1), - amount, - Expendable, - ); - } - } - }); -} - -#[test] -fn double_freezing_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 5)); - assert_ok!(Balances::set_freeze(&TestId::Bar, &account_id(1), 5)); - assert_eq!(System::consumers(&account_id(1)), 1); - assert_ok!(>::transfer( - &account_id(1), - &account_id(2), - 5, - Expendable - )); - assert_noop!( - >::transfer( - &account_id(1), - &account_id(2), - 1, - Expendable - ), - TokenError::Frozen - ); - }); -} - -#[test] -fn can_hold_entire_balance_when_second_provider() { - ExtBuilder::default() - .existential_deposit(1) - .monied(false) - .build_and_execute_with(|| { - >::set_balance(&account_id(1), 100); - assert_noop!( - Balances::hold(&TestId::Foo, &account_id(1), 100), - TokenError::FundsUnavailable - ); - System::inc_providers(&account_id(1)); - assert_eq!(System::providers(&account_id(1)), 2); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 100)); - assert_eq!(System::providers(&account_id(1)), 1); - assert_noop!(System::dec_providers(&account_id(1)), DispatchError::ConsumerRemaining); - }); -} - -#[test] -fn unholding_frees_hold_slot() { - ExtBuilder::default() - .existential_deposit(1) - .monied(false) - .build_and_execute_with(|| { - >::set_balance(&account_id(1), 100); - assert_ok!(Balances::hold(&TestId::Foo, &account_id(1), 10)); - assert_ok!(Balances::hold(&TestId::Bar, &account_id(1), 10)); - assert_ok!(Balances::release(&TestId::Foo, &account_id(1), 10, Exact)); - assert_ok!(Balances::hold(&TestId::Baz, &account_id(1), 10)); - }); -} - -#[test] -fn sufficients_work_properly_with_reference_counting() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Only run PoC when the system pallet is enabled, since the underlying bug is in the - // system pallet it won't work with BalancesAccountStore - if UseSystem::get() { - // Start with a balance of 100 - >::set_balance(&account_id(1), 100); - // Emulate a sufficient, in reality this could be reached by transferring a - // sufficient asset to the account - System::inc_sufficients(&account_id(1)); - // Spend the same balance multiple times - assert_ok!(>::transfer( - &account_id(1), - &account_id(137), - 100, - Expendable - )); - assert_eq!(Balances::free_balance(account_id(1)), 0); - assert_noop!( - >::transfer( - &account_id(1), - &account_id(137), - 100, - Expendable - ), - TokenError::FundsUnavailable - ); - } - }); -} - -#[test] -fn emit_events_with_changing_freezes() { - ExtBuilder::default().build_and_execute_with(|| { - let _ = Balances::set_balance(&account_id(1), 100); - System::reset_events(); - - // Freeze = [] --> [10] - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 10)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Frozen { who: account_id(1), amount: 10 })] - ); - - // Freeze = [10] --> [15] - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 15)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Frozen { who: account_id(1), amount: 5 })] - ); - - // Freeze = [15] --> [15, 20] - assert_ok!(Balances::set_freeze(&TestId::Bar, &account_id(1), 20)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Frozen { who: account_id(1), amount: 5 })] - ); - - // Freeze = [15, 20] --> [17, 20] - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 17)); - for event in events() { - match event { - RuntimeEvent::Balances(crate::Event::Frozen { .. }) => { - assert!(false, "unexpected freeze event") - }, - RuntimeEvent::Balances(crate::Event::Thawed { .. }) => { - assert!(false, "unexpected thaw event") - }, - _ => continue, - } - } - - // Freeze = [17, 20] --> [17, 15] - assert_ok!(Balances::set_freeze(&TestId::Bar, &account_id(1), 15)); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Thawed { who: account_id(1), amount: 3 })] - ); - - // Freeze = [17, 15] --> [15] - assert_ok!(Balances::thaw(&TestId::Foo, &account_id(1))); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Thawed { who: account_id(1), amount: 2 })] - ); - - // Freeze = [15] --> [] - assert_ok!(Balances::thaw(&TestId::Bar, &account_id(1))); - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Thawed { who: account_id(1), amount: 15 })] - ); - }); -} - -#[test] -fn withdraw_precision_exact_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - assert_ok!(Balances::set_freeze(&TestId::Foo, &account_id(1), 10)); - assert_eq!(Balances::account(&account_id(1)).free, 10); - assert_eq!(Balances::account(&account_id(1)).frozen, 10); - - // `BestEffort` will not reduce anything - assert_ok!(>::withdraw( - &account_id(1), - 5, - BestEffort, - Preserve, - Polite - )); - - assert_eq!(Balances::account(&account_id(1)).free, 10); - assert_eq!(Balances::account(&account_id(1)).frozen, 10); - - assert_noop!( - >::withdraw( - &account_id(1), - 5, - Exact, - Preserve, - Polite - ), - TokenError::FundsUnavailable - ); - }); -} - -#[test] -fn freeze_consideration_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - type Consideration = FreezeConsideration< - AccountId, - Balances, - FooReason, - LinearStoragePrice, ConstU128<1>, Balance>, - Footprint, - >; - - let who = account_id(4); - // freeze amount taken somewhere outside of our (Consideration) scope. - let extend_freeze = 15; - - let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(ticket.is_none()); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); - - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); - - assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, extend_freeze)); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4 + extend_freeze); - - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 8 + extend_freeze); - - let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(ticket.is_none()); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), extend_freeze); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10 + extend_freeze); - - ticket.drop(&who).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), extend_freeze); - }); -} - -#[test] -fn hold_consideration_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - type Consideration = HoldConsideration< - AccountId, - Balances, - FooReason, - LinearStoragePrice, ConstU128<1>, Balance>, - Footprint, - >; - - let who = account_id(4); - // hold amount taken somewhere outside of our (Consideration) scope. - let extend_hold = 15; - - let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(ticket.is_none()); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - - let ticket = ticket.update(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); - - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); - - assert_ok!(Balances::hold(&TestId::Foo, &who, extend_hold)); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4 + extend_hold); - - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 8 + extend_hold); - - let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(ticket.is_none()); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), extend_hold); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10 + extend_hold); - - ticket.drop(&who).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), extend_hold); - }); -} - -#[test] -fn lone_freeze_consideration_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - type Consideration = LoneFreezeConsideration< - AccountId, - Balances, - FooReason, - LinearStoragePrice, ConstU128<1>, Balance>, - Footprint, - >; - - let who = account_id(4); - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); - - assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, 5)); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 15); - - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); - - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); - - ticket.drop(&who).unwrap(); - assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - }); -} - -#[test] -fn lone_hold_consideration_works() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - type Consideration = LoneHoldConsideration< - AccountId, - Balances, - FooReason, - LinearStoragePrice, ConstU128<1>, Balance>, - Footprint, - >; - - let who = account_id(4); - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); - - assert_ok!(Balances::hold(&TestId::Foo, &who, 5)); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 15); - - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); - - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); - - ticket.drop(&who).unwrap(); - assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - }); -} diff --git a/pallets/balances/src/tests/general_tests.rs b/pallets/balances/src/tests/general_tests.rs deleted file mode 100644 index a56f9f98..00000000 --- a/pallets/balances/src/tests/general_tests.rs +++ /dev/null @@ -1,143 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![cfg(test)] - -use crate::{ - system::AccountInfo, - tests::{account_id, ensure_ti_valid, Balances, ExtBuilder, System, Test, TestId, UseSystem}, - AccountData, ExtraFlags, TotalIssuance, -}; -use frame_support::{ - assert_noop, assert_ok, hypothetically, - traits::{ - fungible::{Mutate, MutateHold}, - tokens::Precision, - }, -}; -use sp_runtime::DispatchError; - -/// There are some accounts that have one consumer ref too few. These accounts are at risk of losing -/// their held (reserved) balance. They do not just lose it - it is also not accounted for in the -/// Total Issuance. Here we test the case that the account does not reap in such a case, but gets -/// one consumer ref for its reserved balance. -#[test] -fn regression_historic_acc_does_not_evaporate_reserve() { - ExtBuilder::default().build_and_execute_with(|| { - UseSystem::set(true); - let (alice, bob) = (account_id(0), account_id(1)); - // Alice is in a bad state with consumer == 0 && reserved > 0: - Balances::set_balance(&alice, 100); - TotalIssuance::::put(100); - ensure_ti_valid(); - - assert_ok!(Balances::hold(&TestId::Foo, &alice, 10)); - // This is the issue of the account: - System::dec_consumers(&alice); - - assert_eq!( - System::account(&alice), - AccountInfo { - data: AccountData { - free: 90, - reserved: 10, - frozen: 0, - flags: ExtraFlags(1u128 << 127), - }, - nonce: 0, - consumers: 0, // should be 1 on a good acc - providers: 1, - sufficients: 0, - } - ); - - ensure_ti_valid(); - - // Reaping the account is prevented by the new logic: - assert_noop!( - Balances::transfer_allow_death(Some(alice.clone()).into(), bob.clone(), 90), - DispatchError::ConsumerRemaining - ); - assert_noop!( - Balances::transfer_all(Some(alice.clone()).into(), bob.clone(), false), - DispatchError::ConsumerRemaining - ); - - // normal transfers still work: - hypothetically!({ - assert_ok!(Balances::transfer_keep_alive(Some(alice.clone()).into(), bob.clone(), 40)); - // Alice got back her consumer ref: - assert_eq!(System::consumers(&alice), 1); - ensure_ti_valid(); - }); - hypothetically!({ - assert_ok!(Balances::transfer_all(Some(alice.clone()).into(), bob.clone(), true)); - // Alice got back her consumer ref: - assert_eq!(System::consumers(&alice), 1); - ensure_ti_valid(); - }); - - // un-reserving all does not add a consumer ref: - hypothetically!({ - assert_ok!(Balances::release(&TestId::Foo, &alice, 10, Precision::Exact)); - assert_eq!(System::consumers(&alice), 0); - assert_ok!(Balances::transfer_keep_alive(Some(alice.clone()).into(), bob.clone(), 40)); - assert_eq!(System::consumers(&alice), 0); - ensure_ti_valid(); - }); - // un-reserving some does add a consumer ref: - hypothetically!({ - assert_ok!(Balances::release(&TestId::Foo, &alice, 5, Precision::Exact)); - assert_eq!(System::consumers(&alice), 1); - assert_ok!(Balances::transfer_keep_alive(Some(alice.clone()).into(), bob.clone(), 40)); - assert_eq!(System::consumers(&alice), 1); - ensure_ti_valid(); - }); - }); -} - -#[cfg(feature = "try-runtime")] -#[test] -fn try_state_works() { - use crate::{Config, Freezes, Holds}; - use frame_support::{ - storage, - traits::{Get, Hooks, VariantCount}, - }; - - ExtBuilder::default().build_and_execute_with(|| { - storage::unhashed::put( - &Holds::::hashed_key_for(account_id(1)), - &vec![0u8; ::RuntimeHoldReason::VARIANT_COUNT as usize + 1], - ); - - assert!(format!("{:?}", Balances::try_state(0).unwrap_err()) - .contains("Found `Hold` with too many elements")); - }); - - ExtBuilder::default().build_and_execute_with(|| { - let max_freezes: u32 = ::MaxFreezes::get(); - - storage::unhashed::put( - &Freezes::::hashed_key_for(account_id(1)), - &vec![0u8; max_freezes as usize + 1], - ); - - assert!(format!("{:?}", Balances::try_state(0).unwrap_err()) - .contains("Found `Freeze` with too many elements")); - }); -} diff --git a/pallets/balances/src/tests/mod.rs b/pallets/balances/src/tests/mod.rs deleted file mode 100644 index cc469066..00000000 --- a/pallets/balances/src/tests/mod.rs +++ /dev/null @@ -1,330 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests. - -#![cfg(test)] - -use crate::{self as pallet_balances, AccountData, Config, CreditOf, Error, Pallet, TotalIssuance}; -use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use frame_support::{ - assert_err, assert_noop, assert_ok, assert_storage_noop, derive_impl, - dispatch::{DispatchInfo, GetDispatchInfo}, - parameter_types, - traits::{ - fungible, ConstU32, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, StorageMapShim, - StoredMap, VariantCount, VariantCountOf, WhitelistedStorageKeys, - }, - weights::{IdentityFee, Weight}, -}; -use frame_system::{self as system, RawOrigin}; -use pallet_transaction_payment::{ChargeTransactionPayment, FungibleAdapter, Multiplier}; -use scale_info::TypeInfo; -use sp_core::hexdisplay::HexDisplay; -use sp_runtime::{ - traits::{BadOrigin, Zero}, - ArithmeticError, BuildStorage, DispatchError, DispatchResult, FixedPointNumber, RuntimeDebug, - TokenError, -}; -use std::collections::BTreeSet; - -mod currency_tests; -mod dispatchable_tests; -// mod fungible_conformance_tests; // Commented out due to AccountId32 incompatibility -mod fungible_tests; -mod general_tests; -mod reentrancy_tests; -mod transfer_counter_tests; -type Block = frame_system::mocking::MockBlock; -type AccountId = sp_core::crypto::AccountId32; - -#[derive( - Encode, - Decode, - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - MaxEncodedLen, - TypeInfo, - DecodeWithMemTracking, - RuntimeDebug, -)] -pub enum TestId { - Foo, - Bar, - Baz, -} - -impl VariantCount for TestId { - const VARIANT_COUNT: u32 = 3; -} - -frame_support::construct_runtime!( - pub enum Test { - System: frame_system, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - } -); - -type Balance = u128; - -parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - frame_support::weights::Weight::from_parts(1024, u64::MAX), - ); - pub static ExistentialDeposit: Balance = 1; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; - type AccountId = AccountId; - type Lookup = sp_runtime::traits::IdentityLookup; - type AccountData = super::AccountData; -} - -#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] -impl pallet_transaction_payment::Config for Test { - type OnChargeTransaction = FungibleAdapter, ()>; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; -} - -parameter_types! { - pub FooReason: TestId = TestId::Foo; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl Config for Test { - type Balance = Balance; - type DustRemoval = DustTrap; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = TestAccountStore; - type MaxReserves = ConstU32<2>; - type ReserveIdentifier = TestId; - type RuntimeHoldReason = TestId; - type RuntimeFreezeReason = TestId; - type FreezeIdentifier = TestId; - type MaxFreezes = VariantCountOf; -} - -#[derive(Clone)] -pub struct ExtBuilder { - existential_deposit: Balance, - monied: bool, - dust_trap: Option, -} -impl Default for ExtBuilder { - fn default() -> Self { - Self { existential_deposit: 1, monied: false, dust_trap: None } - } -} -impl ExtBuilder { - pub fn existential_deposit(mut self, existential_deposit: Balance) -> Self { - self.existential_deposit = existential_deposit; - self - } - pub fn monied(mut self, monied: bool) -> Self { - self.monied = monied; - if self.existential_deposit == 0 { - self.existential_deposit = 1; - } - self - } - pub fn dust_trap(mut self, account: u64) -> Self { - self.dust_trap = Some(account); - self - } - pub fn set_associated_consts(&self) { - DUST_TRAP_TARGET.with(|v| v.replace(self.dust_trap)); - EXISTENTIAL_DEPOSIT.with(|v| v.replace(self.existential_deposit)); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_associated_consts(); - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.monied { - vec![ - (account_id(1), 10 * self.existential_deposit), - (account_id(2), 20 * self.existential_deposit), - (account_id(3), 30 * self.existential_deposit), - (account_id(4), 40 * self.existential_deposit), - (account_id(12), 10 * self.existential_deposit), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } - pub fn build_and_execute_with(self, f: impl Fn()) { - let other = self.clone(); - UseSystem::set(false); - other.build().execute_with(&f); - UseSystem::set(true); - self.build().execute_with(f); - } -} - -parameter_types! { - static DustTrapTarget: Option = None; -} - -pub struct DustTrap; - -impl OnUnbalanced> for DustTrap { - fn on_nonzero_unbalanced(amount: CreditOf) { - match DustTrapTarget::get() { - None => drop(amount), - Some(a) => { - let account = account_id(a as u8); - let result = >::resolve(&account, amount); - debug_assert!(result.is_ok()); - }, - } - } -} - -parameter_types! { - pub static UseSystem: bool = false; -} - -type BalancesAccountStore = - StorageMapShim, AccountId, super::AccountData>; -type SystemAccountStore = frame_system::Pallet; - -pub struct TestAccountStore; -impl StoredMap> for TestAccountStore { - fn get(k: &AccountId) -> super::AccountData { - if UseSystem::get() { - >::get(k) - } else { - >::get(k) - } - } - fn try_mutate_exists>( - k: &AccountId, - f: impl FnOnce(&mut Option>) -> Result, - ) -> Result { - if UseSystem::get() { - >::try_mutate_exists(k, f) - } else { - >::try_mutate_exists(k, f) - } - } - fn mutate( - k: &AccountId, - f: impl FnOnce(&mut super::AccountData) -> R, - ) -> Result { - if UseSystem::get() { - >::mutate(k, f) - } else { - >::mutate(k, f) - } - } - fn mutate_exists( - k: &AccountId, - f: impl FnOnce(&mut Option>) -> R, - ) -> Result { - if UseSystem::get() { - >::mutate_exists(k, f) - } else { - >::mutate_exists(k, f) - } - } - fn insert(k: &AccountId, t: super::AccountData) -> Result<(), DispatchError> { - if UseSystem::get() { - >::insert(k, t) - } else { - >::insert(k, t) - } - } - fn remove(k: &AccountId) -> Result<(), DispatchError> { - if UseSystem::get() { - >::remove(k) - } else { - >::remove(k) - } - } -} - -pub fn events() -> Vec { - let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); - System::reset_events(); - evt -} - -/// create a transaction info struct from weight. Handy to avoid building the whole struct. -pub fn info_from_weight(w: Weight) -> DispatchInfo { - DispatchInfo { call_weight: w, ..Default::default() } -} - -/// Helper function to convert a u8 to an AccountId32 -pub fn account_id(id: u8) -> AccountId { - sp_core::crypto::AccountId32::from([id; 32]) -} - -/// Check that the total-issuance matches the sum of all accounts' total balances. -pub fn ensure_ti_valid() { - let mut sum = 0; - - for acc in frame_system::Account::::iter_keys() { - if UseSystem::get() { - let data = frame_system::Pallet::::account(acc); - sum += data.data.total(); - } else { - let data = crate::Account::::get(acc); - sum += data.total(); - } - } - - assert_eq!(TotalIssuance::::get(), sum, "Total Issuance wrong"); -} - -#[test] -fn weights_sane() { - let info = crate::Call::::transfer_allow_death { dest: account_id(10), value: 4 } - .get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::transfer_allow_death(), info.call_weight); - - let info = - crate::Call::::force_unreserve { who: account_id(10), amount: 4 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::force_unreserve(), info.call_weight); -} - -#[test] -fn check_whitelist() { - let whitelist: BTreeSet = AllPalletsWithSystem::whitelisted_storage_keys() - .iter() - .map(|s| HexDisplay::from(&s.key).to_string()) - .collect(); - // Inactive Issuance - assert!(whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f1ccde6872881f893a21de93dfe970cd5")); - // Total Issuance - assert!(whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80")); -} diff --git a/pallets/balances/src/tests/reentrancy_tests.rs b/pallets/balances/src/tests/reentrancy_tests.rs deleted file mode 100644 index 89b27528..00000000 --- a/pallets/balances/src/tests/reentrancy_tests.rs +++ /dev/null @@ -1,212 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests regarding the reentrancy functionality. - -use super::*; -use frame_support::traits::tokens::{ - Fortitude::Force, - Precision::BestEffort, - Preservation::{Expendable, Protect}, -}; -use fungible::Balanced; - -#[test] -fn transfer_dust_removal_tst1_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .dust_trap(1) - .build_and_execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 1000)); - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(2), 500)); - - // In this transaction, account 2 free balance - // drops below existential balance - // and dust balance is removed from account 2 - assert_ok!(Balances::transfer_allow_death( - RawOrigin::Signed(account_id(2)).into(), - account_id(3), - 450 - )); - - // As expected dust balance is removed. - assert_eq!(Balances::free_balance(account_id(2)), 0); - - // As expected beneficiary account 3 - // received the transferred fund. - assert_eq!(Balances::free_balance(account_id(3)), 450); - - // Dust balance is deposited to account 1 - // during the process of dust removal. - assert_eq!(Balances::free_balance(account_id(1)), 1050); - - // Verify the events - assert_eq!(System::events().len(), 14); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { - from: account_id(2), - to: account_id(3), - amount: 450, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: account_id(2), - amount: 50, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: account_id(1), - amount: 50, - })); - }); -} - -#[test] -fn transfer_dust_removal_tst2_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .dust_trap(1) - .build_and_execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 1000)); - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(2), 500)); - - // In this transaction, account 2 free balance - // drops below existential balance - // and dust balance is removed from account 2 - assert_ok!(Balances::transfer_allow_death( - RawOrigin::Signed(account_id(2)).into(), - account_id(1), - 450 - )); - - // As expected dust balance is removed. - assert_eq!(Balances::free_balance(account_id(2)), 0); - - // Dust balance is deposited to account 1 - assert_eq!(Balances::free_balance(account_id(1)), 1000 + 450 + 50); - // Verify the events - assert_eq!(System::events().len(), 12); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { - from: account_id(2), - to: account_id(1), - amount: 450, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: account_id(2), - amount: 50, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: account_id(1), - amount: 50, - })); - }); -} - -#[test] -fn repatriating_reserved_balance_dust_removal_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .dust_trap(1) - .build_and_execute_with(|| { - // Verification of reentrancy in dust removal - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 1000)); - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(2), 500)); - - // Reserve a value on account 2, - // Such that free balance is lower than - // Existential deposit. - assert_ok!(Balances::transfer_allow_death( - RuntimeOrigin::signed(account_id(2)), - account_id(1), - 450 - )); - - // Since free balance of account 2 is lower than - // existential deposit, dust amount is - // removed from the account 2 - assert_eq!(Balances::reserved_balance(account_id(2)), 0); - assert_eq!(Balances::free_balance(account_id(2)), 0); - - // account 1 is credited with reserved amount - // together with dust balance during dust - // removal. - assert_eq!(Balances::reserved_balance(account_id(1)), 0); - assert_eq!(Balances::free_balance(account_id(1)), 1500); - - // Verify the events - assert_eq!(System::events().len(), 12); - - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Transfer { - from: account_id(2), - to: account_id(1), - amount: 450, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::DustLost { - account: account_id(2), - amount: 50, - })); - System::assert_has_event(RuntimeEvent::Balances(crate::Event::Deposit { - who: account_id(1), - amount: 50, - })); - }); -} - -#[test] -fn emit_events_with_no_existential_deposit_suicide_with_dust() { - ExtBuilder::default().existential_deposit(2).build_and_execute_with(|| { - assert_ok!(Balances::force_set_balance(RawOrigin::Root.into(), account_id(1), 100)); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::NewAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::Endowed { - account: account_id(1), - free_balance: 100 - }), - RuntimeEvent::Balances(crate::Event::Issued { amount: 100 }), - RuntimeEvent::Balances(crate::Event::BalanceSet { who: account_id(1), free: 100 }), - ] - ); - - let res = Balances::withdraw(&account_id(1), 98, BestEffort, Protect, Force); - assert_eq!(res.unwrap().peek(), 98); - - // no events - assert_eq!( - events(), - [RuntimeEvent::Balances(crate::Event::Withdraw { who: account_id(1), amount: 98 })] - ); - - let res = Balances::withdraw(&account_id(1), 1, BestEffort, Expendable, Force); - assert_eq!(res.unwrap().peek(), 1); - - assert_eq!( - events(), - [ - RuntimeEvent::System(system::Event::KilledAccount { account: account_id(1) }), - RuntimeEvent::Balances(crate::Event::DustLost { - account: account_id(1), - amount: 1 - }), - RuntimeEvent::Balances(crate::Event::Withdraw { who: account_id(1), amount: 1 }), - ] - ); - }); -} diff --git a/pallets/balances/src/tests/transfer_counter_tests.rs b/pallets/balances/src/tests/transfer_counter_tests.rs deleted file mode 100644 index 92cb360e..00000000 --- a/pallets/balances/src/tests/transfer_counter_tests.rs +++ /dev/null @@ -1,336 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests for the global transfer counter functionality. - -use super::*; -use crate::{TransferCount, TransferProof}; -use sp_runtime::{ArithmeticError::Underflow, DispatchError::Arithmetic}; - -/// Alice account ID for more readable tests. -fn alice() -> AccountId { - account_id(1) -} -/// Bob account ID for more readable tests. -fn bob() -> AccountId { - account_id(2) -} -/// Charlie account ID for more readable tests. -fn charlie() -> AccountId { - account_id(3) -} - -#[test] -fn transfer_counter_starts_at_zero() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Transfer counter should start at 0 - assert_eq!(Balances::transfer_count(), 0); - }); -} - -#[test] -fn transfer_allow_death_increments_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Perform a transfer - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 5)); - - // Counter should increment to 1 - assert_eq!(Balances::transfer_count(), 1); - - // Perform another transfer - assert_ok!(Balances::transfer_allow_death(Some(bob()).into(), charlie(), 3)); - - // Counter should increment to 2 - assert_eq!(Balances::transfer_count(), 2); - }); -} - -#[test] -fn transfer_keep_alive_increments_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Perform a transfer_keep_alive - assert_ok!(Balances::transfer_keep_alive(Some(alice()).into(), bob(), 5)); - - // Counter should increment to 1 - assert_eq!(Balances::transfer_count(), 1); - }); -} - -#[test] -fn force_transfer_increments_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Perform a force transfer - assert_ok!(Balances::force_transfer(RuntimeOrigin::root(), alice(), bob(), 5)); - - // Counter should increment to 1 - assert_eq!(Balances::transfer_count(), 1); - }); -} - -#[test] -fn transfer_all_increments_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Perform a transfer_all - assert_ok!(Balances::transfer_all(Some(alice()).into(), bob(), false)); - - // Counter should increment to 1 - assert_eq!(Balances::transfer_count(), 1); - }); -} - -#[test] -fn self_transfer_does_not_increment_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Self transfer should not increment counter - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), alice(), 5)); - - // Counter should remain 0 since it's a self-transfer - assert_eq!(Balances::transfer_count(), 0); - }); -} - -#[test] -fn transfer_proof_storage_is_created() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Perform a transfer - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 5)); - - // Check that transfer proof was stored with correct key - let key = (0u64, alice(), bob(), 5); - assert!(TransferProof::::contains_key(&key)); - }); -} - -#[test] -fn multiple_transfers_create_sequential_proofs() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // First transfer - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 5)); - assert_eq!(Balances::transfer_count(), 1); - - // Check first proof exists - let key1 = (0u64, alice(), bob(), 5u128); - assert!(TransferProof::::contains_key(&key1)); - - // Second transfer - assert_ok!(Balances::transfer_allow_death(Some(bob()).into(), charlie(), 3)); - assert_eq!(Balances::transfer_count(), 2); - - // Check second proof exists - let key2 = (1u64, bob(), charlie(), 3u128); - assert!(TransferProof::::contains_key(&key2)); - - // Third transfer with different amount - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), charlie(), 1)); - assert_eq!(Balances::transfer_count(), 3); - - // Check third proof exists - let key3 = (2u64, alice(), charlie(), 1u128); - assert!(TransferProof::::contains_key(&key3)); - }); -} - -#[test] -fn failed_transfers_do_not_increment_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Attempt transfer with insufficient funds - assert_noop!( - Balances::transfer_allow_death(Some(alice()).into(), bob(), 1000), - Arithmetic(Underflow) - ); - - // Counter should remain 0 since transfer failed - assert_eq!(Balances::transfer_count(), 0); - }); -} - -#[test] -fn transfer_proof_storage_key_generation() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - let transfer_count = 5u64; - let from = alice(); - let to = bob(); - let amount = 100u128; - - // Generate storage key - let key = Balances::transfer_proof_storage_key( - transfer_count, - from.clone(), - to.clone(), - amount, - ); - - // Key should not be empty - assert!(!key.is_empty()); - - // The same parameters should generate the same key - let key2 = Balances::transfer_proof_storage_key( - transfer_count, - from.clone(), - to.clone(), - amount, - ); - assert_eq!(key, key2); - - // Different parameters should generate different keys - let key3 = Balances::transfer_proof_storage_key(transfer_count + 1, from, to, amount); - assert_ne!(key, key3); - }); -} - -#[test] -fn counter_saturates_at_max_value() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Set counter to near maximum value (u64::MAX - 1) - let near_max = u64::MAX - 1; - TransferCount::::put(near_max); - - assert_eq!(Balances::transfer_count(), near_max); - - // Perform a transfer - should increment to u64::MAX - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 5)); - assert_eq!(Balances::transfer_count(), u64::MAX); - - // Perform another transfer - should saturate at u64::MAX - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), charlie(), 3)); - assert_eq!(Balances::transfer_count(), u64::MAX); - }); -} - -#[test] -fn transfer_counter_persists_across_blocks() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Perform transfer in block 1 - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 5)); - assert_eq!(Balances::transfer_count(), 1); - - // Move to block 2 - System::set_block_number(2); - - // Counter should persist - assert_eq!(Balances::transfer_count(), 1); - - // Perform another transfer in block 2 - assert_ok!(Balances::transfer_allow_death(Some(bob()).into(), charlie(), 3)); - assert_eq!(Balances::transfer_count(), 2); - }); -} - -#[test] -fn zero_value_transfers_increment_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // Perform a zero-value transfer - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 0)); - - // Counter should increment even for zero-value transfers - assert_eq!(Balances::transfer_count(), 1); - - // Transfer proof should be created - let key = (0u64, alice(), bob(), 0u128); - assert!(TransferProof::::contains_key(&key)); - }); -} - -#[test] -fn different_transfer_types_all_increment_counter() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build_and_execute_with(|| { - // Initial counter should be 0 - assert_eq!(Balances::transfer_count(), 0); - - // transfer_allow_death - assert_ok!(Balances::transfer_allow_death(Some(alice()).into(), bob(), 1)); - assert_eq!(Balances::transfer_count(), 1); - - // transfer_keep_alive - assert_ok!(Balances::transfer_keep_alive(Some(alice()).into(), charlie(), 1)); - assert_eq!(Balances::transfer_count(), 2); - - // force_transfer - assert_ok!(Balances::force_transfer(RuntimeOrigin::root(), bob(), charlie(), 1)); - assert_eq!(Balances::transfer_count(), 3); - - // transfer_all (transfer remaining balance) - let remaining = Balances::free_balance(alice()); - if remaining > 1 { - assert_ok!(Balances::transfer_all(Some(alice()).into(), bob(), false)); - assert_eq!(Balances::transfer_count(), 4); - } - }); -} diff --git a/pallets/balances/src/types.rs b/pallets/balances/src/types.rs deleted file mode 100644 index a7ccfd3d..00000000 --- a/pallets/balances/src/types.rs +++ /dev/null @@ -1,164 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Types used in the pallet. - -use crate::{Config, CreditOf, Event, Pallet}; -use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use core::ops::BitOr; -use frame_support::traits::{Imbalance, LockIdentifier, OnUnbalanced, WithdrawReasons}; -use scale_info::TypeInfo; -use sp_runtime::{RuntimeDebug, Saturating}; - -/// Simplified reasons for withdrawing balance. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub enum Reasons { - /// Paying system transaction fees. - Fee = 0, - /// Any reason other than paying system transaction fees. - Misc = 1, - /// Any reason at all. - All = 2, -} - -impl From for Reasons { - fn from(r: WithdrawReasons) -> Reasons { - if r == WithdrawReasons::TRANSACTION_PAYMENT { - Reasons::Fee - } else if r.contains(WithdrawReasons::TRANSACTION_PAYMENT) { - Reasons::All - } else { - Reasons::Misc - } - } -} - -impl BitOr for Reasons { - type Output = Reasons; - fn bitor(self, other: Reasons) -> Reasons { - if self == other { - return self; - } - Reasons::All - } -} - -/// A single lock on a balance. There can be many of these on an account and they "overlap", so the -/// same balance is frozen by multiple locks. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct BalanceLock { - /// An identifier for this lock. Only one lock may be in existence for each identifier. - pub id: LockIdentifier, - /// The amount which the free balance may not drop below when this lock is in effect. - pub amount: Balance, - /// If true, then the lock remains in effect even for payment of transaction fees. - pub reasons: Reasons, -} - -/// Store named reserved balance. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct ReserveData { - /// The identifier for the named reserve. - pub id: ReserveIdentifier, - /// The amount of the named reserve. - pub amount: Balance, -} - -/// All balance information for an account. -#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct AccountData { - /// Non-reserved part of the balance which the account holder may be able to control. - /// - /// This is the only balance that matters in terms of most operations on tokens. - pub free: Balance, - /// Balance which is has active holds on it and may not be used at all. - /// - /// This is the sum of all individual holds together with any sums still under the (deprecated) - /// reserves API. - pub reserved: Balance, - /// The amount that `free + reserved` may not drop below when reducing the balance, except for - /// actions where the account owner cannot reasonably benefit from the balance reduction, such - /// as slashing. - pub frozen: Balance, - /// Extra information about this account. The MSB is a flag indicating whether the new ref- - /// counting logic is in place for this account. - pub flags: ExtraFlags, -} - -const IS_NEW_LOGIC: u128 = 0x80000000_00000000_00000000_00000000u128; - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct ExtraFlags(pub(crate) u128); -impl Default for ExtraFlags { - fn default() -> Self { - Self(IS_NEW_LOGIC) - } -} -impl ExtraFlags { - pub fn old_logic() -> Self { - Self(0) - } - pub fn set_new_logic(&mut self) { - self.0 |= IS_NEW_LOGIC - } - pub fn is_new_logic(&self) -> bool { - (self.0 & IS_NEW_LOGIC) == IS_NEW_LOGIC - } -} - -impl AccountData { - pub fn usable(&self) -> Balance { - self.free.saturating_sub(self.frozen) - } - - /// The total balance in this account including any that is reserved and ignoring any frozen. - pub fn total(&self) -> Balance { - self.free.saturating_add(self.reserved) - } -} - -pub struct DustCleaner, I: 'static = ()>( - pub(crate) Option<(T::AccountId, CreditOf)>, -); - -impl, I: 'static> Drop for DustCleaner { - fn drop(&mut self) { - if let Some((who, dust)) = self.0.take() { - Pallet::::deposit_event(Event::DustLost { account: who, amount: dust.peek() }); - T::DustRemoval::on_unbalanced(dust); - } - } -} - -/// Whether something should be interpreted as an increase or a decrease. -#[derive( - Encode, - Decode, - Clone, - PartialEq, - Eq, - RuntimeDebug, - MaxEncodedLen, - TypeInfo, - DecodeWithMemTracking, -)] -pub enum AdjustmentDirection { - /// Increase the amount. - Increase, - /// Decrease the amount. - Decrease, -} diff --git a/pallets/balances/src/weights.rs b/pallets/balances/src/weights.rs deleted file mode 100644 index 0c7a1354..00000000 --- a/pallets/balances/src/weights.rs +++ /dev/null @@ -1,300 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for `pallet_balances` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-11-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` - -// Executed Command: -// ./target/production/substrate-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./substrate/frame/balances/src/weights.rs -// --header=./substrate/HEADER-APACHE2 -// --template=./substrate/.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -/// Weight functions needed for `pallet_balances`. -pub trait WeightInfo { - fn transfer_allow_death() -> Weight; - fn transfer_keep_alive() -> Weight; - fn force_set_balance_creating() -> Weight; - fn force_set_balance_killing() -> Weight; - fn force_transfer() -> Weight; - fn transfer_all() -> Weight; - fn force_unreserve() -> Weight; - fn upgrade_accounts(u: u32, ) -> Weight; - fn force_adjust_total_issuance() -> Weight; - fn burn_allow_death() -> Weight; - fn burn_keep_alive() -> Weight; -} - -/// Weights for `pallet_balances` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_allow_death() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 50_023_000 picoseconds. - Weight::from_parts(51_105_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 39_923_000 picoseconds. - Weight::from_parts(40_655_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_set_balance_creating() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 15_062_000 picoseconds. - Weight::from_parts(15_772_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_set_balance_killing() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 21_797_000 picoseconds. - Weight::from_parts(22_287_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `155` - // Estimated: `6196` - // Minimum execution time: 51_425_000 picoseconds. - Weight::from_parts(52_600_000, 6196) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_all() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 49_399_000 picoseconds. - Weight::from_parts(51_205_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_unreserve() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 18_119_000 picoseconds. - Weight::from_parts(18_749_000, 3593) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:999 w:999) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `u` is `[1, 1000]`. - fn upgrade_accounts(u: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + u * (135 ±0)` - // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_783_000 picoseconds. - Weight::from_parts(17_076_000, 990) - // Standard Error: 15_126 - .saturating_add(Weight::from_parts(14_834_157, 0).saturating_mul(u.into())) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) - } - fn force_adjust_total_issuance() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_048_000 picoseconds. - Weight::from_parts(6_346_000, 0) - } - fn burn_allow_death() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 30_215_000 picoseconds. - Weight::from_parts(30_848_000, 0) - } - fn burn_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 20_813_000 picoseconds. - Weight::from_parts(21_553_000, 0) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_allow_death() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 50_023_000 picoseconds. - Weight::from_parts(51_105_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 39_923_000 picoseconds. - Weight::from_parts(40_655_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_set_balance_creating() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 15_062_000 picoseconds. - Weight::from_parts(15_772_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_set_balance_killing() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 21_797_000 picoseconds. - Weight::from_parts(22_287_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `155` - // Estimated: `6196` - // Minimum execution time: 51_425_000 picoseconds. - Weight::from_parts(52_600_000, 6196) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn transfer_all() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3593` - // Minimum execution time: 49_399_000 picoseconds. - Weight::from_parts(51_205_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn force_unreserve() -> Weight { - // Proof Size summary in bytes: - // Measured: `174` - // Estimated: `3593` - // Minimum execution time: 18_119_000 picoseconds. - Weight::from_parts(18_749_000, 3593) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `System::Account` (r:999 w:999) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `u` is `[1, 1000]`. - fn upgrade_accounts(u: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + u * (135 ±0)` - // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_783_000 picoseconds. - Weight::from_parts(17_076_000, 990) - // Standard Error: 15_126 - .saturating_add(Weight::from_parts(14_834_157, 0).saturating_mul(u.into())) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) - } - fn force_adjust_total_issuance() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_048_000 picoseconds. - Weight::from_parts(6_346_000, 0) - } - fn burn_allow_death() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 30_215_000 picoseconds. - Weight::from_parts(30_848_000, 0) - } - fn burn_keep_alive() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 20_813_000 picoseconds. - Weight::from_parts(21_553_000, 0) - } -} diff --git a/pallets/merkle-airdrop/src/mock.rs b/pallets/merkle-airdrop/src/mock.rs index 0a5c865c..a2486392 100644 --- a/pallets/merkle-airdrop/src/mock.rs +++ b/pallets/merkle-airdrop/src/mock.rs @@ -80,6 +80,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeFreezeReason = (); type DoneSlashHandler = (); + type RuntimeEvent = RuntimeEvent; } parameter_types! { @@ -121,6 +122,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 10_000_000), (MerkleAirdrop::account_id(), 1)], + dev_accounts: None, } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/mining-rewards/src/lib.rs b/pallets/mining-rewards/src/lib.rs index cd2a8a32..e135ff79 100644 --- a/pallets/mining-rewards/src/lib.rs +++ b/pallets/mining-rewards/src/lib.rs @@ -26,7 +26,7 @@ pub mod pallet { }, }; use frame_system::pallet_prelude::*; - use qp_wormhole::TransferProofs; + use qp_wormhole::TransferProofRecorder; use sp_consensus_pow::POW_ENGINE_ID; use sp_runtime::{ generic::DigestItem, @@ -48,9 +48,18 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - /// Currency type that also stores zk proofs - type Currency: Mutate - + qp_wormhole::TransferProofs, Self::AccountId>; + /// Currency type for minting rewards + type Currency: Mutate; + + /// Asset ID type for the proof recorder + type AssetId: Default; + + /// Proof recorder for storing wormhole transfer proofs + type ProofRecorder: qp_wormhole::TransferProofRecorder< + Self::AccountId, + Self::AssetId, + BalanceOf, + >; /// The base block reward given to miners #[pallet::constant] @@ -165,7 +174,12 @@ pub mod pallet { Some(miner) => { let _ = T::Currency::mint_into(&miner, reward).defensive(); - T::Currency::store_transfer_proof(&mint_account, &miner, reward); + let _ = T::ProofRecorder::record_transfer_proof( + None, + mint_account.clone(), + miner.clone(), + reward, + ); Self::deposit_event(Event::MinerRewarded { miner: miner.clone(), reward }); @@ -180,7 +194,12 @@ pub mod pallet { let treasury = T::TreasuryPalletId::get().into_account_truncating(); let _ = T::Currency::mint_into(&treasury, reward).defensive(); - T::Currency::store_transfer_proof(&mint_account, &treasury, reward); + let _ = T::ProofRecorder::record_transfer_proof( + None, + mint_account.clone(), + treasury.clone(), + reward, + ); Self::deposit_event(Event::TreasuryRewarded { reward }); diff --git a/pallets/mining-rewards/src/mock.rs b/pallets/mining-rewards/src/mock.rs index 03ce2a4c..1b036245 100644 --- a/pallets/mining-rewards/src/mock.rs +++ b/pallets/mining-rewards/src/mock.rs @@ -10,7 +10,7 @@ use sp_runtime::{ app_crypto::sp_core, testing::H256, traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Digest, DigestItem, + BuildStorage, DigestItem, }; // Configure a mock runtime to test the pallet @@ -67,6 +67,7 @@ impl frame_system::Config for Test { } impl pallet_balances::Config for Test { + type RuntimeEvent = RuntimeEvent; type RuntimeHoldReason = (); type RuntimeFreezeReason = (); type WeightInfo = (); @@ -87,8 +88,27 @@ parameter_types! { pub const MintingAccount: sp_core::crypto::AccountId32 = sp_core::crypto::AccountId32::new([99u8; 32]); } +// Mock proof recorder that does nothing +pub struct MockProofRecorder; +impl qp_wormhole::TransferProofRecorder + for MockProofRecorder +{ + type Error = (); + + fn record_transfer_proof( + _asset_id: Option, + _from: sp_core::crypto::AccountId32, + _to: sp_core::crypto::AccountId32, + _amount: u128, + ) -> Result<(), Self::Error> { + Ok(()) + } +} + impl pallet_mining_rewards::Config for Test { type Currency = Balances; + type AssetId = u32; + type ProofRecorder = MockProofRecorder; type WeightInfo = (); type MinerBlockReward = BlockReward; type TreasuryBlockReward = TreasuryBlockReward; @@ -115,6 +135,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pallet_balances::GenesisConfig:: { balances: vec![(miner(), ExistentialDeposit::get()), (miner2(), ExistentialDeposit::get())], + dev_accounts: None, } .assimilate_storage(&mut t) .unwrap(); @@ -126,13 +147,11 @@ pub fn new_test_ext() -> sp_io::TestExternalities { // Helper function to create a block digest with a miner pre-runtime digest pub fn set_miner_digest(miner: sp_core::crypto::AccountId32) { + // reset logs let miner_bytes = miner.encode(); let pre_digest = DigestItem::PreRuntime(POW_ENGINE_ID, miner_bytes); - let digest = Digest { logs: vec![pre_digest] }; - // Set the digest in the system - System::reset_events(); - System::initialize(&1, &sp_core::H256::default(), &digest); + System::deposit_log(pre_digest); } // Helper function to run a block diff --git a/pallets/mining-rewards/src/tests.rs b/pallets/mining-rewards/src/tests.rs index 04d9ef13..bd4604d4 100644 --- a/pallets/mining-rewards/src/tests.rs +++ b/pallets/mining-rewards/src/tests.rs @@ -1,6 +1,6 @@ use crate::{mock::*, weights::WeightInfo, Event}; use frame_support::traits::{Currency, Hooks}; -use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{testing::Digest, traits::AccountIdConversion}; const UNIT: u128 = 1_000_000_000_000; @@ -153,11 +153,15 @@ fn different_miners_get_different_rewards() { assert_eq!(Balances::free_balance(miner()), balance_after_block_1); // Block 2 - Second miner - System::set_block_number(2); + let block_1 = System::finalize(); + // reset logs and go to block 2 + System::initialize(&2, &block_1.hash(), &Digest { logs: vec![] }); set_miner_digest(miner2()); MiningRewards::collect_transaction_fees(20); MiningRewards::on_finalize(2); + println!("Balnce {}", Balances::free_balance(miner())); + // Check second miner balance // Current implementation: miner gets base reward (50) + all fees (20) assert_eq!( diff --git a/pallets/reversible-transfers/src/tests/mock.rs b/pallets/reversible-transfers/src/tests/mock.rs index 39aa6052..3c078314 100644 --- a/pallets/reversible-transfers/src/tests/mock.rs +++ b/pallets/reversible-transfers/src/tests/mock.rs @@ -349,6 +349,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { // Treasury account for fee collection tests (must meet existential deposit) (account_id(99), 1), ], + dev_accounts: None, } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/wormhole/Cargo.toml b/pallets/wormhole/Cargo.toml index 54dba6aa..a1a1d064 100644 --- a/pallets/wormhole/Cargo.toml +++ b/pallets/wormhole/Cargo.toml @@ -16,6 +16,8 @@ hex = { workspace = true, features = ["alloc"], optional = true } lazy_static.workspace = true log.workspace = true pallet-balances.workspace = true +qp-header = { workspace = true, features = ["serde"] } +qp-poseidon.workspace = true qp-wormhole.workspace = true qp-wormhole-circuit = { workspace = true, default-features = false } qp-wormhole-verifier = { workspace = true, default-features = false } @@ -25,10 +27,21 @@ scale-info = { workspace = true, default-features = false, features = [ ] } sp-core.workspace = true sp-io.workspace = true +sp-metadata-ir.workspace = true sp-runtime.workspace = true [dev-dependencies] hex = { workspace = true, features = ["alloc"] } +pallet-assets = { workspace = true, features = ["std"] } +qp-dilithium-crypto = { workspace = true, features = ["std"] } +qp-plonky2 = { workspace = true, default-features = false } +qp-poseidon-core.workspace = true +qp-wormhole-circuit.workspace = true +qp-wormhole-circuit-builder = { workspace = true, default-features = false } +qp-wormhole-prover.workspace = true +qp-zk-circuits-common.workspace = true +sp-state-machine = { path = "../../primitives/state-machine" } +sp-trie.workspace = true [features] default = ["std"] @@ -48,6 +61,8 @@ std = [ "lazy_static/spin_no_std", "log/std", "pallet-balances/std", + "qp-header/std", + "qp-poseidon/std", "qp-wormhole-circuit/std", "qp-wormhole-verifier/std", "qp-wormhole/std", diff --git a/pallets/wormhole/common.bin b/pallets/wormhole/common.bin index 81c43fbd..5fbea0f3 100644 Binary files a/pallets/wormhole/common.bin and b/pallets/wormhole/common.bin differ diff --git a/pallets/wormhole/proof_from_bins.hex b/pallets/wormhole/proof_from_bins.hex index df7aa294..5ff41c6c 100644 --- a/pallets/wormhole/proof_from_bins.hex +++ b/pallets/wormhole/proof_from_bins.hex @@ -1 +1 @@ -0cd7923d1ed5ed906791b8b058a35c72df31b87a57a37bd20b502d78ea58b22f239fe7688cc487930ffeca0f76a9a9e569cbb7486069c78782df142c148d06a4c5054b523b8ef1004937847e8288cd1a09e81ead9878e27d820a15e0bc0c7a51cc15a1cd9c722435ed9fb692de165bc393bee3e6f37396f5355e1d0aa3d640fcb7c82303e69f4738a1dc1aa9e65ff356d8f377d47c011a230b0745e6e8c4af0119c1fda5db0fb7e76c4dac654f992cc6d258415636feae1bd05a3adbe90d4d3691f96a4128df488183f1544d0ded07326c2bc4fd741d43eaf8b207c8a30153a622875d9f671696db986db636bb76b4024d562cc6b1a88ca9bee62089e011a0e05b9ad8356ce7579bfbc991d28a2126111d77fa8084cac3c0ad6e76ac1d255ee3f39c7356e56d99d6ae51e3359f8ed60353b01742676754482b29fa57d955e142c01c1f2ac33bd19bc9a75885eff8f272c453d480bd58c803aea60900c3c11f2978787f399dac8edbbc892e8a639adbf4cab564599fd8d165b9bd0922bc35322e81b30ac2e62c519ef89ec88ea7990b99ce57e66731ea8e943c453383accc56a09adb74132c97e65933db80b10dec0d0fbdcd5b5e5438ec1b8c675a2169f1e8f0e108928f4de7ce8a8ee683fe9bdac6fd047df83a96c20e9c11b3423321638eda08109c68b35b78291dd0e4b272eff882d5d85acd523f4485676af4d56f67cb5e64f3eb69a5238220cd3e2ce997b80a0dc97ec004033eb021e9b22c48e4767a1d7b461af05b47b9ac6da7040a25993fed6106bc2e1062f79889d6ba82303304543c13b7a1e6c46535b29f7da312f59e4d3647d69067ed76a20903bf64f28546822513b52bb3b091200fdf9136dc51d62ae560e3d68417895b2dd51619a4bcf3cfa650dbd46c9754c16293a547663f853b060e44636bd7841fde76771698483d11e6bca2fd5478f458fa84d4f3908cc4876eef1dc45854f541c37d4d7040ad6d1b4b330c558978b285535a6be0cb2cbe94d1eb718aed894f93544ed28eaa128577954fe46a4438dc0eeaf639ddeb1b90732770fe3214e130b091b56ff246b3e603470e1afce66a07e79093276cc814acef3b145426691bd7c353da2ee48511154aa1acbd4c42fea255df6b6d8ec3536ed4f700fc8a80ca7de9f234a22fe0d69166cbfb4bca141ba63aea73dea7a0f293423b2528be8b760a115f7d518af0702b89e64590220ba0dfd56873d7c3448e9d7372661246c22b63772cec13b46a50d466eef28e8b7a179e6b3a3a9de98a53a879542db057298373b91c01b8be376dfc5a66825ceba88df88c50addf20101b1052c7a734b4800cb5a5bd44708f0f758ab83b742c97f0a9c5d39e268d81927c86cee2b503a01d649f239d92b3188d714065f5fa3903465e0c45fb4d4e333b258fb158863050028536fd0536292f34e0fc7eb3752637b4ec43140be1bfe9891697d5d2864da75cf89be81f76b503fa72bd6fe9afe7f79a85bdc6cbc267c77e6ef27bd0941ef7507ff11a20de1816b6565b6fc880cb09e22e4bb4e4d3395e9a1be399d67a463748ff037f1765f0d9f2d08d593954464c9cbda760ca400115466852b0e56ec663a1a472279f80cd6570931a3edd7aa3227ad1ce9b3b4bcd5250dc88c4116eea672c1ae1a94f39afa13a74fe0416eb16b439e2136cb3f0c6f88ed6b5dc8969111a650615de1f82406cc4fc5884a91c432dc9d5028183c066ac0792cc4f015d33a0dc9ae3a8e4c25ced8ab6728de66d3924dab80110e7037b25523a367c748a2ec887d693c5054e7d72d828c7908f33c13ef75edbdb595e647669e2b156a7d2fe46d2c9b889a27dc68fa3dac36f0de65608440a0c0e8c6fb0509fe798e2e390b96715eb7cc203212cfa28430e59a48931dc637bc357555944cd7b70e32ceb76419940ecf470f050e8f534ead5bb151e5cecb2cea4820f11b6fa7cf3db234a26d00145d0c1100d178aa851631e26a34d60819d3d46a11c911f4aeaf418d3d82a915161ba53e6841b17a3cc02a506cc3402bfc9a0f09360f2d457060db9fef325aae1360712a1fba6740b9f31fe6a7f29bcb5a3c1477a5c1555c9281c5ad7c47c7a3155d785f22de51e02356fb6b47473ced4c08a024bd3d1efa2475f97582744ff11bac2b6780027c518ad58eca3f3cf07354276b8b5f667a879a0d448ec3ce94dda7f330fa934d84bd8f8c26c93f793f7b8e623f1bbf0129d34098139e10a262d2ec82bed0a3f85f242561c4ab60bc6dd5d2c1037fbec88d0b913e5da3ec232ca4fcfea304d9a61f4b4c9fc9d328fb71e13893807ff2cd78b147a909f0ae12b950efa8236b0ad9829274cd5acd2b4c8026cc6867363150275936c51041dbf91a6d74b59b8facaefa364e5b757b0c4b92b06e07862a1d8d3b1034711f1f4548e5baa28f400809912635b6b3ab953d9d948290b69234aeba5ced5097c115ec2cf35750fe8ecf637e57694fcbf171396fef220193b98533612219466f4457e9bb8e0de6cba5e027822354649c3b10c1286855bc654cf994e281042d6c653e534b647c3ca864e93b7c1c024bbfb6d8efb829daf16cab16f2b0ee5db948cbbe824c520d6e04b34a23c74b60b24e255c62630edd3aaf8f754281bd7a63c38d56bf4d4ae83bb4a220c4384defd4a05638355c46a30e1af8ab842ea87b4c589d922fb25a04dcbb9837ca573013e79047cb57b8bdef56a9578311af9c1cf40d09fb809ad9ef19a1a036739d1ac49258013cb72386ffc63c4972002f03757eb116d352d35e1ea94709e6b6a7c5ed8c3a48bae79f5449bb8790e9e714801a550ac07a5604affc0121ae33e64db3dd254446e7809c08613455119550c276189d9f6c7339d56c1bd5e6be9bf73ac8ecb258a24a87f2e546fbfc7e514df29b82954fed2ae35265eb98ca99d32ca4f206e2d170b32d5e96246f797ed31a67037576b825bea1c771136a767a328196809bb70a409268c2a3939eac370ca22375e79418928be52f51ce1ca2d7ceaac63971cf25ce8e852e0457ade667a870540f02c450af102259851b4483bcb48a4488a561d9d2d3f5a71d56ec734fa88842a8f4a6bddde42ece94feabc11019a5bce670fa9e54e52797e1fcba60398ac5a46409b187e8374ace8da07177365c757ae6eb56ec2acdf99080efb1698f172395dfe02c44b760d453c9251862f99a90eadd82a6f096ee61b655c98a64d8bf0587af35df711d5993863a2576d9dc39158e1e39b2930ee5bba96c30da7e79c60f881c65804a3525fb1ee9a860ef755e7bd8a8251105381885acd782e730666d1f19e3bc3f68f9bf8d13a78069ef552be833ddcc76cef661dca8584cf4c23a757043fa875c288024958a700cc3898a49906e49f8cdef0ecfc7e93d70abc779c9237a3d74ea87018c5cc9b1dd185b0f1f4bf915a1979d7d0ada3b4386dc9194f7ceaa16294b403cb461a5a40c5f64b25601490c7584ca0f22ec660433accb9b371afe8e05e383983c44ac0e50afd1acaef355207802bb31c32acb5ed77259c8df5f1c17479dcafddc4a0244829cbe4e0d6539a95a965d04f48e0ef0f1a6efff314834d5c8b5220f7cebda400e883d5da30d38f818dd50f76e9a7338ac39b8db9e1442902b638ba9550a044a4059cd5593f8b07b28d0750307b9c71c6c4599ce2daacc1f19ba635334a05f64f166a3ff523dcd84a6c30fb42e73de04317201870b9732f2c7ffcadf16ab6944d476d725d7854518b7063f6d256d5fc019f738b7124ce329766ff59bf8fd5dd58ece25cc68faa872a71cfdc6846fb225663d2d084f026a96cdb080d5d5d3e39f54d676420fe67ed967b0ffc7971bbf6474470084e3c5702bf895a8e06e6633ba8b19aa80c8ceec231b6863c0e6070049545cfb064f96d3a3e92bd3080e459f97a91ba5a5e21c64fbda0fae8f0636bccb944c4949f0149dad6e0e472f28b0a0588e902d809df9d784578279a6bdc19294c55d0a65d47882c86d64f80dc89d13712799f0daea18c32f69bc6c3dbcfbddb47e66b117ca45daeed5ed2a9eae36604e26da4fb2de5c50e793b712e7b0838af03d13682bc734b1847ca6d158179638bc4755f6b939f808d01b57bc0350edd95113abf435f3b402e774dc4a5b7ca156bdae272988f696e12e7a0da062b12a46859c52a8178a1c4457e0ae3e84f0a19f06196a4935f247bd6ecbba690fc1ed00aa7433693132ac6fdd2b5e28f6b20e7727d7905750985620cb4546cfde83242f4e543a42ee865ef970cb5ed5e4bee287d70f9c6421d55846921ab7c67783f5887def0a7f99e2875eb4050bdd075e2bc2088dee32a8a8c5ea6356d67dd36c59aeb045564246dae2d6dd03bb93ca8b8f19c758ea4ee10bef3dbf09fecb08712c88fab79ea50847919e5e84a9e7e365dd8e240eb1cd9427cf71d4f96aec21faa2ea084f3a623d7d31a33cf230d8251df0111146997024861f3adeebc2e23deec3ce8e5c28e95ddf6f32e18d97a979c9e8ee94c8782facd6e8353256870736f6c40a2cbc7a1b43055ad2b569a496b29cad97c05d3a2e5e3c73c877cdd519426e989705ff98ec5c3a40ab106bf606a13a94da9a876ff07f5328feb0a61327ec38ea8c02348a34f6e3fd065d3d88fd27ccf5c5a63343bca0ea63b28d80ced0694b72f7a84aad9911475516de120fd18f7b065b1e0e590d72696b00d9ea8625b63f6362453e53d5b1471f6b14a06bf4237c764653b204cfd5fa1f53e50b6e50a87452818e9ddf84c6cc9438bcaa8d3e096f7ab1a6a2808b2f684fa497f4b563f2c523c27900be7a0fe756ab5b44570953c545700b7fdc2d394ab986a1e12842d698142a0aa50199258de2a5eb64f49a35badc0bae3e8095ed04c0c8de03b6e642d0e3dde2874651fc497bf4f7fff535c60abecb459b48a455a21b2f7fbc14e3e8cc7b55f924ec7a1a051aecc47ff33be7e0c640becc1cf5ca844372f00725b94a8244cbd46b92e850f393d26a29c21b567a52ccd385735f8fe319590e2313b43a7861cd97be74f4c437029117d1c423b2bd57d104eb6029283141b1777040425d509ace5ea40d81e799051c1c7eae5b959261f45f9896a17562e96563827c6b37d618677011ac96bb34aac5026a1b5d78312c7e0814b6046a383f2159923c1cdffcec5e3ccec7b7253d738a37d6701c7479b3d543c6ca90727d2a7a1573e61492060f141aafcc67b9bdcc0c65029151ffcb24df92f12707dd42f2fba639ffeba47a9e217ead7710501b700fb8c318821be987cb92e3dc635e8e0d36ef67f8dac4896bce9c485521e2fd907befd9f08b1997ba3e09e91ee5294d1f5ec06aa1a11dbab1c83aa17b2df0d6ee437a366288609217412fc39ecbc9f9a7bfe62f020a09ce61e28c62d78b390305203aeed32efca6240df61697aac704502eba9c71a32a0441ec7b16b9ce8f641209ffd7b4e36357248a0f264b95d76cfd8aa55dde17b642c2f735b43902e3ad40f2fd9a7aae6a6dd4c190ba82cce2398192a3b13bb2d1e0ebd8f2ebab50c84472b2c2b4cf1d78f01c5a18da94f55adff36b879e0e8274b7d2bff4a5c6b3e928d328394c63c038fce97e5b29e92f11fa85ad7151ff1b9aa5103d91e9319ab832e5321ff92100faa5430cfd9d3bfb4b3bd07e1f93454eba275a9925eb43bbd6d27940cb1f938afc7f75d047cd97af1a4602557050ab2bcca7a3abd1dcdca5368057f39b863a121f5517a7d9d7db58d40ab83558715f3b7bf068b2c0ad8a910c135f0253cb9315b849167b16c0ebec17d952c2789fb0bad03813838e59be515be1836a6b2527151fac348109c809dc36f2004ddeb53c385073a4bfba261669fe036e841d6fed44dcd38a253c9a817b41f8db75c961ca1c320ff79b20da2b85881fe8d85771630a10a7cc3b6b98e99efb35911f61b8d9a241351d73e7ca8cc12c7e70e702c7eff34dff3a7b62efd7bd7efe371b31e188fb8ae26b579943845b014923b6e5aac05666e86507e30287b850a27f4cf679e4713e8ff2efb1bf84f6dd04ea0d9ad1f22b354f53a7635a2ab5e978b6cec1981dda46a6513108bc772e1e8492a1905f63d11d38aa39b62116246c110bac40a437c3b86440850297860c0abe45375586e6fcf08fb705c929130f650ee50a1af3d3f9dc1786cfde711e4e2df0078ea2059626dd105afab047a0da71fd17e9263cc9304728f6281a370da6cfb987effc28be1e9b3684ea671a2483877b2700df4ea8c7886484171baab7438e5e77cb207542e383b6ff9b88e1c6ddb1c8a1bd449333e43251dfc7b720fce44107435a034735875d1d6c87827639b56d234e0b12848e740de59226c15bd4116539b65ee8564a544913a53832c43482f406766d780b7c17c97bc33d060279700b09861c655e9034601f61b9ed4b952601b08256b2889c4b40df793095d9930256fc6304826e0b0e159a801c9462dc3abee9799c1bcc85c372003eb632a2dc60ad23509d681dc20913665af93b5e4c94326ee4a9de42de71439b020dd0d28008e192a47d0e761ecac4bbd82e0524142fcb988670db8be1165e19f4df30c57c35f7384d34ea77f97bcccbc03f1da2a35058775cebc4b3af74f1e26d89752107ae14464d6bb53bf7e7ca1938c179b0642379c332f92fb6a40129fcbcf2dcf205de173736379f479c473fa92445ecb56b2b045cedccd06e23068ea97dd0f77735a5b532cee276e53108ff5d21243fbe0d2201bca934582656534dc4a3c4eb308934565756cff905495b2d448da86bccd4900e74038afa6d7b4d111670112bdda674dd6c925d2fe6417849f643b2949cbfae12f30d1704266782298620a3dd9c3101f785331bb3ded704e8e931040fb0189cd6d04df8655c5f20b8a074550d1f2227f81c2bf62fd89ed3f54d92183ef830721b5c435351854adb475362e2c7ed1681b152fe9cf8fc29b302fb739125104debf14198c54d82a37c04434d92c5a4c2a1e43192681837840c7d20753d40e7b4f370f3ab18ad6f1803455fac40a0c66de4f1898b8672bdd210754c8938166d9648842208723b946ae1a4cf32726d8785952aab9236ab46812bfc879237110835fe2d4871b61f8f95241b2e789f011099ae29748a1ead9803ccb825e48f9d688afc90c2d7620a2ea5ddeaa3fe4c5257941cb6efd91fc6bb6651cd95a6be3d1c9e7f2f87de0b53807ae1e91b80e1679b093dd692aecb3f326316fc2e528645e5cd63171cae33d882572b496fbf759a514efcea0f2b1101560fa2f92edc04182deeb78c0d8eb13507434e7bb98ccc714dd40ffe468da3d5ad1c5a7b7b515f6cb5096f20d093b8e992ef726f8c28e439a2ba124d3f7469d69cad3ebf9292a701b3a714a3bd70b4a90a8e09e82d0bf54595e56a458fb2650e2b86ee95dbeffc5e2895adc473f95a04d8632bf44558ee274c11706e6919bb7f2379cc8d870c9e7c66a61cd8bad3d84efde3400e6e6c26049181393bcdaeccd5df2bc36600a1ff499d9759f18983137525bfa8f119335002dd7eae95ce588454e09ec3f4fa8cf72facb19ff73bfa56fcba45aa7a9a975d4f9f5cdc68d8a94f3f3225ac6662d96e3aa98ec1d30016a9fbb731c5c0ceabf4e7751c3bb84a81524ece32cc155d2b5ea662061d0f0d91c1b0a46da7d54c308bc7564164c16ab5b52be2b5db0f83e85babf2f76be3f01283240276cc9abcd47b4c77581d5a34b51f474ec3d5b5961914a1d087fce966434353254bf906e9da181cd97eebeb9818209a65ee4103b5cb1731d87e3ad394165599abd29a8b773d4e7c8af3e9602c4e85e5799373ad6e5d52d3147e25049b7be77b84adc6713545d31ce7d6ef56d1fc7ad27a4f37daf33e2c4dcb849226d664ac40e2ce441f8b8e4872960a9c5183bb65f843ca5fa9ba395f77600ecca06ae099a36b29faad3280722de540e98373a6786dbcdbdff6c3e375502af37b806ed8932b6e884659f189542170d6d0eb6efe69acd398a5922fea3035182a19046ea43225142239483512e5637ae129586f1c1aa1f7a7242859c2fd8cd46b76591b071541f8ccb03f7309a399df459bc666defde0b3cab29a69653c55c28ba3aa8355094b152bcb5992892d6eb1d1f23ec0033572002aeb7a3879ac743a7ccdd43f1567a359290413590b04610175a576c3fc04e1cbe2c54d19936f51d980b3b01bdfc890a7fb739fbbba643f72a5557b64db54a955ac412715f357cef22f7b4fd19b37ae395c7bfc5e9aaadbe0b6fec8ffe2a35cb770da9ed6821506bfc455dfd06deaef9a57498ded5d52b29c0db23130f456349d552cbe9152771c910ce186404898cecbaddcb5a7e7dff06588af5fd512969813f7c4dd9bc715106567708446f414ee62824519edd971feee8497721cde9b4a3a2dc93d5da23253e143be0fd8a9b6b83c9311385d7f3b9effc997c94338be7a522173029680a5cfa94318f793dda674c8593332143852c305bb55ccc7f6b47f79b5dab9c59310bd6781b018fdb582ed0ad1d320e21b76fee017c79f173e3a7969a8fdf71e52ebd7bd1c53ec7eff75a1266862b8d8155459a94e78735fcec79a04ce49b3303ddcb8ac3b35fc901cfa66ab46974425018862bf800328f36ae8e4d42ef99b0639659cc58fd3d60df29b8eea92b7c52460ba4d298d60f568dcbe4728694257087966cc83fefb27ab1bf2ca218df1f9597bd4e6987baa42f244ae13f1f3d66f407fb3609edf2e017623882f949eadf02c2704377cee896a578009a1fdfb3c1fbcb8f6b3a6b884afd08febbb313f6ef68bb5faf4bcb860dfbd60d01a21a1ec922ca58fbf2491d27f0a800337f61024c1691b602ea4673155943c090ae4f9cd1500deb3a7202970875900cdb8714bc00fa499d14093153c0cfb4394e7c5d7d58abf18366d9fed7f3a017e06547b03cd5d01665a7ea4c3e499104dcce2225d54c6c4f7c9264fe858180aed49e7e042868d2a22593486aa8f85207410e715a1f65e12d8015c611b47437998622d9f7d9a73f21c4e6c5241e0b53f5481a6b5df74b9be8dfa36de649e5985725a262841f9505134feb886d4b176cfcc98c5ee33c57594d9e46a91a2d25b544d694671e9fb6d6f709101201ecddd92027f88d6b7fd4778117040f9596f635c22f7ef62ace097e1236a63bab6293d0f42db93923f67b7bc79d65ee7cb9098ec61a07f1957fba6c93afe2de376985e35c3a6d6877cc08a34bef6306fbbc1dfa1845cbec896a7b4730ee15eef439e0eabad8383245cf0defa8666afa8977bcad32bdfef125705894c5ce0e40197342b263cd1911b8363c98c0196396c2f61c8d5995ff2788d738bdd901878aba69648a312ff46b31096b77ea8adf3f3b3aed9bd549a7fb69ac702b8e0759446ce1c1e51b624f736996fda2a994bc65f3acf72496aa96dd247483994a6d9c2f64b4774c623d3e6c812a9832c6d52dd84655e9bef2f7fbd03108d02d4aff338016c010f42f7a4db1deec8b92fc85f8fbd9fbec260a391d1ea309aa8ebf7f8f2993391f92f00999ccb1d706b0dbc000e6d28bf50eb44e4c4c0c02985195205fa206be3f1ca87f821c6db329f27476b4f68e381b8b63e2359b034e730a0b7ee57995743fe58597706c49af61ca2dbb44dafda00a617afd3669612bf94318a3b08aff27e756c9621a68d47811a084610850514a64fd2e9fbf2e9c80a5b400f5b0ca131af2f18cfe2e4c7686b94be4b60d8cc77e2510cb1b16a9942ffbda77d4fb6feda5b37c7d933444378ffe3337e4c6f35970dbc7b20bc9ce1342d844525f37cf4782ef397f891df9764c616d7ab06e375f707f70c0c84165d00be857bb04bba2e333ee3b69f6af260bab31d08960937df012085f90775bc3bd2a28fe64309b9f1abf05d1c76e81e430a89c9c876d3c2beaacfc4c2e13678a0e0e76b8a24aab65a0352b23fc67f0a31d9c54545166b2573e50b51c7640eae6b06f558be411576dc9e49c4876498250a19aabc1a789cf0627c0c748aa04b6d213e61767db22db80be36831848d113e584c754590de3c7abe87568ef59be4c17af6c6992d64c5b56afc1c2dddb87ce157ae63767b31240658f41393898c97eb321355c2bd865695e2b4b7616653cd27cdc956f86ade7c6fa81b7bf947f00474f06446d3b1f5d48d354e31de1685f67c63e1a64c8311de75526c505ff734fdf445b8e5e36c56050e80afaa8a6b7170df6326e0a91536c3efd5f0b6f4e8f771a4a085e2cd34a6c757207646995a078676fbdd7665c2a21bdb170efc96d08af33c576a9ef16ca674052d50c1245c777875e8acee2e44618473a956b686dee6794a0d92597b19b1d26cc01492e95e88b731d59ba360bdf4a381ce6fb29803e3cf1e81dd81bdbfe1e5ba0a5cc38c03514031027f971ba4c0c9f4dd260d24a610ade6ac82e0e20e20232ad6460b35168c337c6d074de3a5f3c737acb93a4fc95f1a03f519c38cc2e44d3ef07d0649a5ed3f100a0ef77be2aadb15a9de21527111f91d18496516d8d84d568f7f807e0158025935cc106244233f90e0251fc2e6e036f704bc2fb2dac60e9119be681d9dc03785b034b94e002bece05298c4dd49c5261149d551bd9680f4d83a9377ea6370f56db0224c1b0e248d7c6d3fcf57408ebff74ee35f37d3cddee9a2238625e14e7b9feb8ccb1765131171d609e5d4cad2131f2730b82472274ba09d51e122e17df98389b0b16ea3f717192f84ec49150dbd8813d9659750c3ece75fbd126aa5983a4b58b961962ded368892208fda2147a44a06bea1a0e6fc36772f3c79c920f7ac3b0370340f701df75ad85ca6c2757d1139d31357471c0c2ec44d1aa88a132dc600d447afacb8d8d0bf803bfd8575022e8787343a91a12e424f51cd096321151d27265ba128c6161ff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eecc0c00503fc1a466dec7ac1a42bae390d3864f9b6e232ce6f25332d2abbfb6ff44294138c33d59f8d4ce5bd7ba0fefe9d27f2466fc9186ede7a891dc35e35dfe0ce2dafbc30e266cdcd98faa636cb41b00ea90cb399bc2490304decb904bbd0cb015f7dae894b7fd46a9543dd4a041a60fa4cb536e751b08c82eed9124f99373dfb895284c397a3ddc8340a67f1add6ba260fc9ec3700034c7d4d6162e55362b6c9de5b3117da8d50849eef8f539c5174ce0d556536d2464d671bacb833c9d461f61abfa2cd704640efe6bc41687a58eca6e1d7a344ac4df86bcfc18fa1cd065ce3322da497b178c19849845e084062540d41ed2b8b5b5a3485b6ee3c73910d15086897976323fc72f1773851991e01edad4754beeec171c797e1dbd751efbb124a6d358ac6a39288e1f73be267fc0dd804d0223900b7a732d2bbe15bfd0ff08adb47892614d59bf9a8aa9d369d2731fd2c6080ba5b09c4aa44f685a215178238720c6644e1d60b2205b1393b9e08f6ab0ffec8dfb600cc6d766250fb9c9fba81189164ceb9c179dd9644cd3bbadd9c76e77a933f18546bd875441d56def29e5811634b8eb7c5ff9662097e62fb57778dba134c330022ea6cf4486ac62b776ac925f2024babf5678fcd8ec0cf2ef3c911a4ad569a1240b640718331f346a36bb1245f9292e5e6f82bf9ddc3db6dfdeec3ef2114753d847ebb11cd1ec628c6c390c0d5e009c5e6b3524f398907724c3e60ec223d6832f38f936f104831c7166b0eb11613970c32d1ab6bbe8d0ef58af33fdec040ae15c6f279cc60c3a54d3df72c190ac8063e1d17e2f2c27b604747cca97a5fef75896d9de456e30af515fd0c1c4c8175a84b3f1b9a4567d94b83bb83ed3f6b3ed5dccc7118f595f2d7517bc425e762689303539dfb911291fb20f7966c3aea6951efed097b3c333c0e00a930c01885d2344ac3f132924430695c680909c6ace223bbc7669533c36a59b581c880126ced2460e4fce7ad92820705343761d06d4ec2b4dd497c3c192fe4a1a662401a7ca629cf63a53204140078becb7b0fad070285b44fd699b94cbfa57abe0d47fbd04ad3f0d2194f35e248c840efa94931ab69d4064df0eea0bc7b044aa0697a46edd854ed053d7d7910223da20a77a734a26146bbf7893322d9408c8d8ff9f3956f98f950bd1e530bfdddde8cd2fb0f786dcf59f06bb33fe18d294f94a3960e5d9bcd409b858cc33efac0ada93a47232fad2036e4d84fef85342fbeef1d42bf27b638362ed97b7391c9e8f19919b348b7dd4f06d4bb32c5bf07aaf498ada38a337c4a7b70092b009fe43d91aa9df20673e6736d13317574785cd6ed1a2d70aba9ebc63ec5a7b21fbe564c46e1b8f5c6fdceaa1da6eb41da9ca3e88b7947500123dcafc8637698bd78452f343d0446ea951d1a3f3b1beef9bf9a7aa60f8de33694d1a84fa0ae7cdbc4fb303c79d0424dbcc556a0524276bab5720636b097143d6e72c5933f41bdb48eb6e062add5636c92764153ac200de390c3acc42b984d2252fcbb40508bf5f29783ce40350803ece656d6217e8ce0910d254de5383d1fb1e223a16ecac2c9b0b0adfda8d3e6a973eef9e4fb48b3265f7e56df08e4ae8959bc37914ecb21405e1e05be5efd809f69269aed8f527cc849f26fdf851b9000206fa6194c50e26123f92218c2c4cc5570c4bb3e3f537f1dcbe6c3f35a4e9200e8711732ec1b1a63a6c0b340231713f49e866fa9981ec69f29993d3d281b6cb74b093215ba1c1f44bede2a790246a909b8a102acd2229b83801877054ceb7ab7f93fe86a9e8ecac12bb0263b8b48dfd2eb75ea89b280e8074e85a5d44e0a8e18925739490fac4d09f312fd88969c82092e304fb870c28d7981b7e48633a95e0d7c6f632976325bee325d9938676bea235dde27ddddee2c636aa06c29e2e1fdd719b6b24c95a2f140383ece04820bfb8b322b1fa5cc95f49a4ff85d074e9dfeeb49491ae75532605957d717f149a485c68039fe92aead6afdbf141af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a852c741f4e4dfe42cc3c6cba7fa287ce710c077afa16c3278bc8e9bb738bc57690dbed322a7e6169a3d78d26906734cd1864c20406e849ff04867b1ec54bc431b16511664747dbaa6cb5fdc4234f093a819037fd32a9c002611d3a0b0f58e164ddc38d6e5a9b87f0f27e8317c46bba829076bf9f58b2b20bb0c325e35915ecc63ccd19a521a047f1adaaee4355e4209ce32a947103ee1502f0dd38cd6dfea61790ccc8a2c50751fd69d78d4742a4b060b1e246591edab1b1d7ba85e38cba8adf38ee73c67a6d845508b923ac6c42f3687c4663e11fe47cef96a6edc5d531a14ff595d74568d7227dcd1dbba78a4d131b17b823c21d6bc68ea771bba638d0e2a3990ac65e0d4a114e892036ce2abc96c28202a040dbecead784c2f1355b0c9a4989ee883f40e83a97d2fc00c169fd3a14f0434cec489f1afb454fd22668ff9f007fa26ae6a11f608143a63dbdcd1afbfa799acf2ed10e3a6db140cee01c1c4229321e904ba71650219acdf242a6604cc73993cced0b43394bb1e908c4245da0884ccdb552a291c40f70e1734448629c1dc1dcbc08bd3e41a95b51cc3a3d5ef5b3a6426327ba93b1e9098cdb21860e5e793e60cbb58e8947f34aee95deed6f816328800629b5f3e072eba04b7f090a19a999f6b7242bfc3f4110a6d59c7a5c2265460473777ef46743dcaf3fffc35f08e34aa1c4a8a05a6f4a82c713a36b2b9a13603a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ffe92bca0816f491dc855734b1aa4f81247586cc983027b654e00a6328cc8f4cc23b293da545e99580f491a67a7af57a8e857cb029f7b7058b40ad9002cfb2c40627160ab9326ecdecc63e9e748ff04c056ed07abea2ab4586aa9f2d5306c6dbf6490f0a356cf0743ae9673ad87f56a6d03e0e6a75a522cd3b008c0abb32bf75650cbffac66bb35fe0b66092899e7846bd987eb438b8c6d867cdc22f2cf38b8f896894506d2b927a55c08ee9d0ab5cf24c70dfdfbc6bc5291a0dd84e5f90a83eb3d78a38b496d90131007ac5bcdb7d9ea26e51eda9d87e18cdf5abf6ab0aeb7e1f0ef5ce9343a920a4f015f76779c4764467268b87bc664bb93e587074b6ec786c33826cc49ec1346711010e150a141b7f95a87e3e5a6daf605177fbe437892d37e24c616f1ab1748c94d6d5549151e8e9c7c7c95837fef91734e0e74f8e0e8cd151a94d5c19f702690ba313dc4ad9b7944856df49ecf040927e598718923de3c497d5cc49f0cd4c2958155fe519f32dde2d5dcf530783860a441940bf4f0b12f99488375a6c5702b81994eccd2c480db95ec4a6ef49e763c3d926db020a114e0912dd0695310dee7aafee4845d8ecce86a95d0b666f765e2b5e0ef1c3396cbbb115888f416889b2325d15b41fa72de07ed16699742c0557559d85b9933bebe1658c55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b89ccd57122f3bbd3bffc1438aa84d6ebe11ef3b2029acd6abb298269c9897d159384347b38ce3b0985b38006146832ba432188dadbdae226dce020a9dbc826db384b0d48534068d0c16c04142496f90c65608a5b3ad9570bc36047adf19d63aa9ab850e303e757ecbc77afd9d3ea752cef9cc4438355865031393f8795e10d436e8610eebae0db7c894493b4404388a98dbdcf302120f5c5f3b192899be059179de49120b735f8964f172f44f766028914a0c5f6b3fd00ae3b0f720e86a74207aa62eb9dbbffa65f7fe49f8b5308fc218b583bca586a676015d4845e8cb00a1885bde912746c32a07ed1f18e8d49ed74668da846dbdda896dba50e956c0ec43670887ece023f1f5453de106a4053f364ff60ef20fce2c8fbd4340140a4e7b287934acadcdee1e0f5df3f32686ce98bb0fdbb064e674eefbfa94b2a0a8c7648626eee389191801c1adb75a130a84fd0347e3ea22b4c35bff1ae879678b85d9489f0337057835e17a74ab2c6fdfb9cb3a078cbfa5c575bd0e6b003b74084a079617940e9f9af1f7cd36b627d57c531c7ee8f466c8e5da365f8754ddb44448f1acb7eaf501b5ec6c3ed7eac8008959103be7b9c5abcbee3fe239b80d7299144dece36d425fcc712561635c71728212a624e8625d3a999ae6e4280ee07d7defb7ec497cb68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b84416782bc6be7ba7a2ca95a7f1a2a0365c2201c2063e6187ab6dfcb88cc6a97dd7e1f2758b8b9411647b2b4270833f04cb3dbeafdcf64ddea259862440f78b7fc2b1bf2c63e8dcbc5f079c9f4a8534169146f1877dfc612f37cb01d4f5439dbe623f4cf07c33a9f6c235f2da8bf40300af9b56ba207deda3cbb9f5d3ae73de2bffdd2d788dd4802c53138197ab5217ab2259fcfc562113af05c580e004b865d63c269b92352193106687f3b287023a15885ee2ac15ccb9371610e10588a7da6be84d38d35960b646562a21e63eaec51cadcb73488d0da40fa8dfa89ff8accdee262af1e27c2189bf136c9d01d98896247bd118585e7509ed48550382d4b3bc5218270ac04f9a79f6dd0e464c28ec522159faac7718ddf1d68cd36326035a91bf24eb72029225bd8d6eec73fa9384aee88c1b13f9f142d2448404065075fc2ae35de15a45ce95a73256ad67ea83609a8667574c3655918ebeafe6cc398c475a2353c6c996c28ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b47506b60f9d8a2d0b68d1c61c05f5a2267b5374e28ee1995887451c1acbfc014c6c36f060999727328d4c7fe24c92ca2c0426023e4c81197dd7ca953ea02bf687ed939e139749076f73873250ae4e999aa0bc98163af1bd37b40c86b3414b8803fa4b38c860482331082074f75ea483f972f71126edf26d5d9c22eea78b10495d8b3136f9ded86e3dc2a31ad0cc0e447156befe4059b7b00558fdd1a56fc25e3e6844838e58da62de90b4de5fd2c753676b221a6b3064fd834e184e4a2056893336cdc172cef1367eeee25716914c6e48f34c8c6fccaf4ff34026b2e7b0fb7edf74d90bc3bc7b21b5886a7b13dcd8ec1d8d19114055c2868968a6e311ed3b75a4fed9f4a3e954877d4d41f5cb015ed87536efc6dd4beb1635799f9de158a12cef2e1b73da66fad19b5645a9815fb73de36c3072d1bd7f4d185ced4ed007ef978682e80e96fd357d325d4facf7c4c36a6f838e46d1300891f6ae0b0e430b6d88f47ecbedbb7d552487f6dcdb51ad8eb37b13d4f57066ccde0a692e65a61c546edfbd14cfd4c4bf7967a265e03c18aa804433662184e9545a9216d5f5f2fa2d27a76b71fcf0518db1ed904c945bf5d4137f77f1ca1f17c75193724867ffaefec5183a0bdef9bbf5dc581b9c1afd9bc43d597ad467745a52e358bc3d7ee7e8593e1bee8e6d068c2f9f733b16ff4fa87fd3e92905313bfdfaeb81361552f078610393c8c6d44657975903f9e17c14620b0dbed7208f92c72a531cb403b147087bf7d6fabc23b86663f2f0a9cc5f94ff343a3b89b24bf962c0a6fbbc240899895bd1aeb3f363f4d557a16c949a8bbbe3d696b128baa12a31b3307bb59c07cb2985eec6e58646dbd94a5b7175a23fed460e634ec40073893eb06be4efde3ff33e3ac9ae0a28409909f1646b9a82324309ff4c04a25af017c9e6e214414eb17124bf16810c33a17083580bc8da6c01f39c4ace7bc8910b75b622a267cd8cee24e496e157057f19faf26b4f478a880663787a0ed09341afe2b6bbbc372a3b44a96c6c41e2029075e535fefd1ac86c667a3114743849360f6e6938a6ba97835b9aa911ca7048a90dd7c6c86019bd4de2a2acb7d5b849daf61592245236b567f2ca1d8c324cb6f97453b78e6979cb890b4f6445b142939e0ae39431163cb8b22ea487c3357d24667d57ef8afe68c37915c9e8d7414ea06f3676e36faca0fdf735889739baec5918bb376183f0184c4e6dd1f551b1dae0ec26dcc30b8bc5b3dce61ec14fe547f88a6051b069e1174ebf3a32a1c99ce07f97f004a379a7aece6b401773bf491e3395b749ffe9e8a7e1dd19b5dbe488a91696e57125aaaba192c57ca33b08bd00070717863e8fef53bbd2fbec4f45264331ed4a6ed60c3293330120c977582b957b8771c23ff73a0ac60a59b4d6cba5a0a7598d528c78e3ebf750e4bbfeaccc168939856f1bec6686dd6f0fba19470673af1420bdc2d27274283277e2d7ba52c4a5e1e132ed11fdc5a8b1fdfb099af7d2bc96f05bf715e5351aba4de6bd29b269d889718ab0c64c3bbe0cc3aa4eab16363d24bde2bb6dc6e2efe59f4d3b6851a46560cbc7c128f11ecaad2c9aa2cdf7004b67213f27c38c37ef018264134fdaf1766011d3777319e5d50a0412e4ded49d9aa473aea43beaf53d254acd33c1149ce1798c9b507ac5f8e0a1678579f4a93d4d0196783f55dfb7efebde1913af9ea8bea9f90db6a5ed855c39a63af456b876fb21f37cc869d4de87ea2072e4f5a5b72eb5e3ad5f38e2e44eb2c64729f94ad883e2d9e82240083133d2811294cd31c0f1c978ba9c9e83c8c1ac2a41c066ac88d9d301671f5abddbad80eb9c3cb5b84f1049ca2c11dc7af8572576ca194a7679a2f3c06f10221bf16eb5a19bf1229d37ee9476989af86a3658a08cf2f48f6e022d5aa703c37c3dd4112b6384a65183a88585f71bea771ab5f453c8d32edc6236ae37cd558e5476e4f4b13c28f41d6a7e31ec074b367578bc78f869ac36eaa7d8e81d1c6537c12cd06a7d29f7fb1db2fbf6f2d1a7ea130972608b05280e4d980130268228ab5a51ef2e6b8738e5a3725ad3371e76bb4b50b96a1e45ebaa171b310acbaded02bac6642e3f9632a80bd8f1a63c4d4fad3611d2c9bbfe2ecbd3a7a2416cfb57c6143639c34829da01e3005f59111c171a01cc74ae33254e484c76f193a9b319d2e4ec4c36bc064ef91a90740f143cc1f5dec700308bf8d9de9a41e8de7d1a7919705e8121669bc5f1c31604b22285777f4fb47db1f1f25eab335205b842bdcbd9b9356ec2e504c56ab267884ca234ae586644bff0f842b04c27fddd2ab6ffb6e9b6b45ebaadd584ba2336f6f69d5af4c66469e28a9f530f129614bf12cee2eb075ba5192c6be6dfa1b3c3f8923068051e07004c7c05fb183304d9fc48671ddd527f1be5276f270bb2211d64c701fdee82da6db48767837f9a595efd16ea7af2e2a1155579edb3ae3e00b0ab67cbccefa8b3966101fae246f3a4a77d42ef379c16194d75a6b208effa87b91581acf9f147b9f8a17a7dc91643a0f839cce368dc5c863891c37b02390170e676aea788bc744b91f0d2ccdae52872595e80efeda57e0949089be88b49650ca265919f13c336a7e6c724f008e0139a236472e378869a6b47df15cc70226160128981f9be7caf7491928f956d3fdb991419f297cf011fddd672f3a53d79b8138f4375771cabbcfd3965d84f4aad1aaff593db2fc333a63f13213a10916ed583db9f3689366c1088f06136b214a47eba3f7f2bc90638e221c31344ddc64af15a0b05691188ea1a6eb9ac27a04b5242c5b9cedef275e11fd54314f6a835a57ff10fbb9b4959aacbd9a449c42fedd256a2f0aaf655989122a3711b1865e91b350a6fcba9b79828796c3705cefde448ca5746fddfefd0c6e3a7268a44fa1740c8c9a27156af6a9e4ee6b5d8c8db5323e50b291e95b4aee70329f5efc980e557006173d52c422753c799175b2f91cb8629e689534af28e2ab9da257b040c59fe5180bdab0d13e2cc2da613d1e182d124de45b2d79aaf1df670e3f521bafb8606954defb7c448dc72663d1e69344a73b84d9fa1cbb29ede28cb733a6d2eb0c3a68c39ec880f0c863d36e63d0fe63239cac3ce3eb32163e95c72dae79625da0a4d5d8a6d3e1aa5ba75e1f7c13a3c380a5ef03115c5560ca8ab4a877e0389a0030a8a159e14791399fd8c04665e06f50db53d44c6bd2899f50dcf8941c266e2c49b65610f4651330c68f6ee9c707ab381614bbc46c258b60ac502bb819e1b2f88a2507378d012071af04cc474b55ca870625b4f5eaaa6342512ea83c2ce8ce69c53ecb8444a563e3a24894670415329030b5f97c886c4d7f9d8038901fc4c3192d6b0f9ae2b24f8e2396952f9898f7033053deecb322ea8594b938c9a2eb84c13da102135b9fd4456ef0869bf4dae0ae3ae1d8ffc16209e225195022fd416ad02fede5fdeba81192d8cfe7ae235ec0df7878f2c1edfcfcdb9a87e2ded93e69835c5228ae6a34974f12a5153333014044ebad5aff0fa29b7a302d7eec28feed2b719f501088f950a28fda44e4828c88a12c95da2963d9519bb02967191b8d8cd76b38bdb0ee6fe1dccc34c4a8c71d042550da749d921374197801e77c475e8b655c209df0d3a162ba11d12b4713e2967fd6f03053a23b4b48e6d1e703f8d881d2a7fd85ecfa91aa8363bbc8db889d1caba535c4e16ef78ae570b11349c78929e3e4267e70f495c1cb5973ce0fbec5a9e10f3820a320f8467366692d258b034b00c4d6b7e1e83b29eac37d8a0169d2cd9814a6b884eedb1ea79285c4cf39f1df2d76588fbb850edcf62d6ffe2832ff520cc35a409bc10ccfa3bec6c881e42977a373813403e3db6021d9212d6c6e4d9bd03359bdd1d826e1214b15cde392c0a11c5c3fb0bbfb899ef8bde2d38b1442a6d760a869155b9df35b484859fdd7ec5fd9d42373b540d9ef0ac86f42fd386553ffc11684f98b1ff903fdb502a6e8649c52ca00d2212538a20d96e44d58381c39ee5429cf2917d34a8362950d98567169c36d6f03c6b3d9ad5883cb2d5ee636c41ed894c94f39ccb45b69b526885f3357e873707c2229d67d1b99a7af2a20f001d2195d19e9dcb65e32c898c3972e91ca799baddafd66d28f52f0986da289f6f01281c51135c2699481bde2f50a890ec809099e4477d4c05e36e4e8afab24746bef18a4e4b8dc2f02e443d16eb8e06cdc8c40b759ba2d53315e42cc6722d40bd8e64449e2d16c099fb4d9479579ff04e33da75d97a8fac7ed07ab79f65c2e87918204d98313283a53b942ca7efb4a0216c7e9967e1705f21bfd50456590f1024a502f81f65f0645721a738ee1e0c3aaa3af34bf8eeae2421a0110484be430552c1f0b6c5a20cdcc35a3d976d94d79270bec6485826a36b54e5510f73917c0bd30d1025dba524afdd0839a28011ef6b75d5b776194553aaf7fee5cd1839f309a431ed313aa08b2343bd5067bb8cebbd5e6ed60c38d1313c35c587b88e056e6784c697ccbf33f470a290225e687fe0f1fede56358d0154d921f07b885046b6422f7f81089ad567d3b3100e745f587215d6614798633c71baa8b42b8453677ef8061f16e94c977c39dc43b4ea3aac502de1c84ce38d7d5ffb95241b398dfaf354e2bbbe45d42fc7572c117b04d292fb920fc01fa18d0c565200feb9608d263f24975cc1dd9619bdc81ef0ea299d2ef50957272df53ac15cc2fb073a9adb395d1a69607e4dc25f4bdc05afe919ba1f443233f2dfbcdf92dcdcd94c797a34532d9e24c3aa2a47d825e54a07cd435e70863740744a5df204f72d4cf0e80433d4e33943d6e0fe9a6f236c8f36059db1e0a0f535a3f56fdb5a9dd9026fd9fe2872937c81382ca2ec94d68dab563ee4828f17f352be516cfbda69b1918321b220915396c499b6199ad3a63e156967f9c27f49934059a2c4559e148576d9176a548e469d842879b75769640ecb309b496d132540d07103e410288f6a4128fa9c800adefd2de765982fce20897f99653fa7eae7dca2dd9b6ae013a3fc052ab0243849be927899639d96a22f3496ac59b95e48bdc9254780a1127fadec41dcc19637ca90c41ab8c8d3b3c02f95f9bd8d6ec5fba6db2645902b8d227f4c3ae772b8396aeb527a8f3ef7ecb9f1420620e9d50aacafbe37aea4173a677ab9c82e34cc6d36667af1f0d76cf2e21726d210f859c49fcc61f8f29cd4c48627c26b66b9db69014ae1ea58c56ae4eb512e91065254a2fd5ef6c8b56365aa97c6479c44dbbdb1b0b5f51110d726962ded4cdd1d50c4851b253e06f1de293cce8ebe931a21bcd14236c72c7b001aca5892ea95db1278cbf15d5b6f813fd4d5fd570357b6bc8231b1eb6f360370548decc76d1e994e803b1a22cc4f18b3b308431c818178aac04adbc3832fe3b08aadcc23c34b01a9fd246597239382995061698efde7c9115a5c720a1a3691fc53e4725ccff520ac6ce02a60207083df132b0984465d0bc95d5f4f1c256017ced4bd9bae22eda3dc7f214d14ce17fde4522b4bcec03c24e6ebf26920abbf576a01485cfcb29b1ca0d6f44c8c94e665b05a9ad628615114613941fad77be0fdfac4203bc7c23234ecef4afbce36ff8cfddf5723c32f8aad6d1ba7669d1070ab5ce8dd6b4f88b5ad70e199d92e05ddde03d6b30fca1968ebda301facf867ac7e715f1622d38d4a67405aa1da349ec3c8f271171fe2ad5552cb0423ac1de902ca019e0ea6f48c7801c7cc52d67e86c88860849679819c871fa1ba6bcd37349bbf2fb0b38e80e5cee60676b777bdd7d068e87042385d464c7d03ab0236cb249635ce3aa6f9b5354a37097f40ab1985b90f6f33dc6a90095ca8494219e5613bda47ef0a868f4033f4396e4ee270079db69c61cebdfe156686eca06a538d79d873786714f0ab2b3c56717f1fc28bf10ff067f9b3bf8948e22ad6d6e28b11baa04762bde635bef43a5f21b5b1f1ad54e5d81f82655d64d304974460e2288435f733aaaa8cb8741e2af492b2c9c2f4e83121a86223d3e1ad2971596834328f6490ed7670a1f3fb3697f5d71d8ee4f7340410410f42f89977b77ed2e9a928a9511327371bac3b2b7adba3ca2e4f82e4f1efc6b186102604c56106ef18796ad5257c56baf98d1f17d63bff7f8d4af523505e80576fec48fc23be8a4778139c272ae0ba58bed4e0bd539c9c6aeaab74e4eff90974f043a64bb11f0660e007f152568687e8bfd45e9825c86c08696c191c75e02a2a5ebd1fc9ceccd70c1c1dcd26a45c02275ef1147373cb56d385b670fadaba3a2befadeaa6ebf91fef9619bfd499074606b520b73c20ec049bec80f5d724ef919e2d8f0dc9ec089b188bf2cb9f133fce0e7d8917afa5d7028f74a73f8d9e7902c5a4996819c7d9808718d448938f2c29f5aeb4bd28af7644babd0270a80872fda2acedef8e7e450a3434f83f870f2b780111f5b2dd5b05b050ea19ed475b1d6c0552b240e27cf0e96d87990cf95b1dfa252af0cfff6c7c1e8b478df3cda8ddcbebfe72c9a368289e436ee402173d39ed1ee0acca02d59a7d32a80286b068ebe0a20de439dc3d99764ffe547941af9b81592f04a707170e6c99b51479c1c9fa7147941be3f1ea6bd779324451d69c049761ad6c0be52c4a1429fd9da97a70a63192aa20e6b0e5f33b7f8c240da1efbd1d3c1559d14e7c1445c882300d3ba5ccfd4f5ece7cae9bfb858eebf28c47c217b242be1b8e91324dd62b0ad702bc58dca18f8b00b234fa5b85a09c4a6114ec9434871723014a326006d99430a8708890cc90616345610087828f0d4081b942ad63e0fcd28492705cc0c270011cdbb9012945ffdf0215f1b47280ff99e45b58e1db10171278618ea8419b61d8a0232e7241748b4b006091cca4f4637aaa10009cb129b7390f88d776a6ad29823afdaf4b06fe901ac4798f1ad1de11e414ceaf2dccd2b74c8d060c01b0f7725d6670ac55762617ead2b03384707c76742458f40bf891944e25d4a542e1e3cbb96d5b6dfd7cd2629bd857452deae95b9cb5853856e21504a4f2c7d5a4fd8e02e71a0813bfcbe4b7c3cd4b6303e37414e8cf27b0205d1636832b3d7d226ac2c1130e916589bf673be49f69ea3a642b6006557b4caf1b9a1b647a137d2df7c2e985a94e18c6ea642c345b92342c068e8bfb755c25f280cce448842235a2b2afb37d2eccf3fe3aa62c7bf302376b191e3e0b29fdb49057759160f599ce72b279c3fe75c29491e5617c2114160cf92094ff38697c7513206aed77a8901a728e116e1cbda7d4e205afcf282ee469257db2b5d802372c107426ca91b980781f5b5613738878973bbc5b5e5036ce5f0a611a23290b3b9e7f3845a0b8c11208d5a912b275d3e8f9b4ad964b42868197ed36177eacbc787d43976fb3ee26409f2d7ba98236fe55186064a9dc8e8eeef4dc4748d04ccfe4bf67f8691699aa5c9c51456df95468957187786eec6078a61476023ef015eab0f3c2e40fcf71dc23c7ea83669ea20e5036b7651a19f8e11ae1cbb45549e8469258cddbb87465a999360319b2605ee7598864083ec86ea3fbff498be469c38514a021f56de34c31f7a543df5d8532a33457b1e01cebdb5f5e5897889e87104dfae2a643bc3676e4e4cfa5891236db3f48a780b1b4a36e73590042ff9eba179ea4b4f30434d5f0b20bd49e1fdf39baa7b108c2caabf9c4c7f3ded64aa0493da89c4376c8921b4ddaabd4aab58e0a82553aa20f84459e5f204f1832d8afefd38dae1e84f5c2c761635fee3e595d0edb94c9071f9bf96ec6d6966814d583135645a3e61feb294909c0b2eaf46ee717702319da65074385d8fe43715f285b83829bcd43cf96fe4d9df66bb57b5161d4c54483f15dd5093f88e17e5fc782f9cdac9cb009b64b5751efaff30717ec358ed457a0a049b8a351e425dc484430a111ff916524f0f743ffdef7a1dc8154ffff88f0cd674f60409c4fd820383052b23789b32c8562e55d5fe5738fac78f8bfe1f12f0275b57c92dcc05e23635e43388085c3dd6a09c934fc40fc31d5f1ddfd1547c0b7e669c754a317c28fcb4b8610c0586ac08b26bba777febe2d11063bbdf0196c814e4c574338dea5dc4affc5bbbb3a0f411dc91dcf1087c8a17e5bec2f0f7a2816100a719e9dec764b458c590ffc35f44a14acd33db5d8771cf2e6b452ada581c11a4a9386d947aae04c1caa20370e8c1ebcef16e93f1321e6d73e149c72b045785fa245e1c16e230b758616aace92235be3a6eb05c18b52332cabfc93615d21abb1667bfde88aba619f1f9a6f66e908b18621246d2492d0ba51d93cdc9e96f8fd801a96eff7a8c7063c87bf255ae3ab5b3929004793c22ab9adef096b2907c02fb58b4a4ad7340511d369577909112fbaf5b92be357e95349105f478fa23064ce50705ee9a479e5bfd678044b5228c6e7278c432bbbefc7ad5ba378c67dba470d87a583fa3e2ce0ddc21d7b935387384e42e81d977cf3873c1bc6c180cb10dfacb19b8a0b8715f4700c09db7d9f77fc5fde331bfe7451927286fb4a4ddd4a8fe3ab722c18c180e371f9ff242e979a167a85037180ded26ef621a8be04dadf88ce3cc88d23f228ca9fa4d0510ffe66eae8cbb806c73a61e71989acab6b596328ce25519feddc8bc313375fc446d2b44bd5b25ab051588b0985dc6c958096e0e62405496a62500de278fa31156818846c073df4890884a4ae7bf192e92e7a793d8cacaef1dbbb481c1e263292ae39d245aaeba4a1ce6d1b07fd788e61a6066454c3d8f356ca8e50687ee0cf1f2decbfe24ca9f1f65b2043c46d7d488847459dcb3dcafc9dc40c5d175f24e062e6e259bfda54a02f91fca74f3f17740fb5251a5c3a608efc89e241f9a71475779f7ce639b1558289b325d1b5506f7fb88e64a782442213032fe42bd657c6eba4b0a24731fd1a6c99acf835170e8697a90fc7af46c9b045032c4ff7dae5d3c3e0172b3e515659d8e8852876a2b5ff6b89e8277542c00da3cde1a7e92098bdf7d7625385fe84d0ee6b76fc6d7cbc1d6549e7b77cf28e7d8472e6b419b47ae2b30e906854246f1fd2db237b3da587a4b1b634080ddc3b65a3e3e0569a7bca31a47e825f627cc27804451b285e850ad1612b83c1766b4bf41e321269afd5b2c35d73ce52383064930d8b38fc2d5219d624a51f0e68aa0fde14623e0a6bc28bed52242412dac4d9f0bfb52acad3f660ae4e58a55611befae981b01fb7a8931762ce78f8a651d43dd94b0722b9e0552f5332d39defb2c65a1412747c074031dc361cc5b68c2253bc832639106fed0f4b8e1beda6eb08ed0e500c46505add56fff971c63c6f2df81a8e42b855f3c0637a3a45c24ed6eb5cc7362e04eef531fe7ce276f6a124ceafb247d8a6434778265f1cf3ffad67590b7c7b726bef2676f746f41c20c965b8445de8d033930ec9dbc7db82563a28d629b5966f95d11aa9cac2b087fc901e07e1d4575e43a63224ce8e72a1120a3cf30db055781b1886e44951815a5e683937a86e647825ba9e4b3d7094e4fd33b07ade947ef178c9be4abbf86ce82e4bfde4f7b7168da17cc8160b7d2ba9c0ce51af0524bb3ee6c110faaf77f455aab7342e84a04620bd09359c9f93ad7bdcf87c0e2102ac406c662097ffa3e690307062b62826f60c9a675f286b47b3051b49f01fd7450bdf7bf0ec94342f12b6c274f92626585c7b26dd29e618219c78d4c46c7b2fa5ffa67cf273fabb216f42c595bf6e0cb3f75f0e9ddb2880a030108608791a0eed42dea392b488577823e3fe105d683225e66f8f6f9b1db2fc0b99ee45df379f1fdee3e582905ca6fe1073ebbc7b6b3e3a0984a5bc35b354b9d21c4415ff41620d6d5fc28f1326c1e3672814b857a983659f2d02acf17f803d74d49f1c55ea9ab981db58640969f30a7f64396295f1406c711432c87f6741008e9072e129284770b889b503350c2c32e277c1921b8e70959ed6464f15f24915e176a55a8c42444cc10089aae9220189c6c4b6a018ac4379be0aa0ad62eef123b6bdd21311d4fbd87510e2d7108563ce66d9eb5e82522b9969362fb0e287347bbf6230f6573829cd2f780c83e0abb7bcb7d98837e37330fe0e325049b32537cdcab5ae6cb6f3c982f8de18336446d179f68a9b2738332215262615c061fd7313f6ad87ff0b5605f8aec61b1e27403faf3b8dd03e23cb02f9cf8fbaf44f5bffdfbce29feefb8b8559227922535ffd1b502a2ba10394d3ee4d483cb058068a8f187999428275fe3e20718c83ba6b0f14b5d0922f4d1065a1384e564db7494ca23d447b51a0d0e588395557b7862259ecdcb2a3750a87d2f98a4ad5e7887f6b738f3b5f9ca66202451a5cb109a74cfa1ea4aef6e6e7746dad3b68f439f18341c8b12798151b2eb30e59ff245688c64893650695747f781b3a76acd8c694dc241eb6a99eb024766216db53ea11e868475f89fcf55d986d1dfd8eb00bff95068da62386e178341ddec9143da7f9a756ff7fd0632617b2decc66cd338b51eef6fa1c77d0816d91f8ca5d7113704b5247159c95c0970b28e127b71e321c1f67947d3217281a42704fed552c6fa00fb96cd180c2538dfc03b75a7a1ad9d098a519705f9130a095ab63ccfadbd3282f31045528e13d2744059ec7aed65f480ed72f84967a7487d18c9cd18e56d7fac64ac68e8975243ac34f91fc6d47c14aa704c31bbdaca3de027071a124209f0ad94e4c614a804c028e2e826226ff7a4932d370ea4b915ab0e5664c0fdeb21f86cf8613c2a1bce546c7a197f2efc3411c1d19f34e426e330eee08a8efb6fe837c2b0cc523709031cc2d80463343dc9825565d091f08700e9c82c6d9be3d4f0f761853df9d1ae2788957ab05e0f7536110002a155f9b88f34a30c301134a26608df9fd36c9a7732faa726daa06daaa678383370ccec477dfe8f477590611e2dbe2d09447816952c1dc80fcdb9a3717dea4bda2e50c8a4a8c41d79548b1ce5c91af73800ae79c2f7beae6ff709723157fab5eb3b62594194af78e25b4a031ea0548853f9131857f3de97b35c470c131e64593ac881cea81019cea4f97a4f721b2af78875b5a2e28f5af1882d0164e43b0768c36aa4173e8b37b957508ad447997fd25a58af40ddee23b975de1e44879df49577aa08c74398def25a2371623c451fdf2b27cf91b90ecfd3122facd4c31694e08f537721bf7977440965d0d34e562d0938b726e107bf7de898ec022ab7a52e437a5390c9e81f91fe694b066f2a32b39d441eaa28a128cfeaf50226cc271924a6d51efff53d85b4fff9dd446a010fc0202a283371205246667225f34e6e6838953e4347a40543d3cb1a07d9c79b3a1b70e5128f6faabd59dc0f7ac63b00616b1d44a5dd35370214e4585cfaa2e615a67d1bdf9953ac8c6e14a995fc14e66b70f1ac6f30ab8db1b147694b4f6c9d00702509a9bfe25f3de5d028125dff9150cdab1ebea1aeac992e9365dcebfa6b826e5c3c050687cb095aa1847c6fdba6261811658e4a349b4f533ab0de5c168b3d64ecd15074c1ee2bf2f1c93fa338380ea29e42b714fce60b7de1d52b74027bd062ef332bb63aa89f53017d5c9364c6364fb0809881d8a5a7121b26a0a2105b6c1b63a67c17cef3d93f8b7bf04afd05a64c12dc1f638096999e4426641d665a9b28d26eff66870a5fb0aed30d16ed779a78883b726cf054048a930768041a752d25aa8970804ae21135807ea33a8c07d4380152f745bc7a5b73765404541cbe01c107ab0367027d9a5cb1383b2f6f990cf20c4f80267cf7b041a0610976193b2e3b29408902b54938e0aef2567582769d91df8add18ffa76b26d8cf0b811d308b78fcf93fa9111ad386a149f923e9c82b311c428f3e1b2173d1965d6acb70fb46b4e4a812f8a7b2bcf1613eea95bb03d9618d7f8a344debd2af84209b95c9446fe0026dd1e6c7d6a4c81fc20e08644c54b3e38a25bcdbd04f4473533361a05ec9b0bc5a73a88b34e68d1f9423324557b80b901852352c3e7a14eaa77aa32a7c514152494dd92e7ca2d49c823fe833e2edfc7f264e77a3502e427500db3c62e72a418193b163464f343ea04a6761211969a7b0d4636ead45f236bc1762bd566cc44bae37535d2d5837d63fdad1a1617de10197eddb872901e509a6eac6000a995bca5165c06b3b9dddde1bdd192050aeeebf43f5c6030cf1e9c76d928c8c12decdf1d44993acbfce5bce6513be97fb7d955749763f5e7b8797d911464fac4cc711da28271331760a15d1e473d8ccc92a8ce9ea28db47091ba34c4fb04555e0a7d082c5437e52b7a9fd0ac66e61676a2a98bdef57770a8426bb9aeda762d8c0a048ee882c96aa241664fd459ac4e95657528c2e2c79e7c61cd08559586fcf7159af8f8c0269c6c9879c9432bdbc29631252eff6ff55c744949ea620c53aa9b4b0e890c50e9590400a20c20554547c20e8d771e6019c431708b55260f05918871f5192ce3526b68441c02ea2868841771de4c3ba6ca0c2a86c21d187ec5641f35c695dc3968c932980981b65204b7af3b96d7c4edb37eed53839c00aa5a2189a20b9dbf9ba302aa1880df92cc7a4bc4f36b3d1cb6a9125fc9555035694ce6cb69ea2f5725628449cc3d0c8a5f032770664cf8a0fe4ad05a4d8bdf9c69b4a71daaba910635fb2d2c58c1744ab3f6d38c7e711daced97cdf10a8ad0aa6aa11a05d630a789c82fe63ef425c4b945d26930f3c1714523addaf6d0e8ac33395fc554e6b9c8d8baaa21cc206f27459a435ab18801bb5bd48d137859efbad1b246fd4329bff6e2db0607c0f03e47943f9985a7587b7a0efd194cd75b5f943f3c489122871f799b1dbaa24f82e046badc6886a194d2e9a9b674df3f0de9b4d7c7961eba70e0facff9e3ed0a6bcd0afffd28ce40676a30a2a38ba5dcafb03e493eebe107557630243ac2c174ed53854853a57b78c8762bf6b5ca2ebe32bb88e653bfa1f8654752f0b9eb0921d8afcc50b91174ccc4f34c89faf300a8514d8fe4982944a554b8fe6f33165ff7aa4dca4a7a660c7297fca663f6e0923db2168fc9b8f32fda5d6940682d6f0ec2f1e0abc53fcc7075cff0724a3adf06b2d6659abee424be6311857c875c4fb4ed8e8e6f313c72a835627cfe7efe3e7e9448306ba6c1d626e0d38991b773394e2d06ac9110c2fe67176c4715bb939e4b255896e67a17f595b1997f6a3f2cc5d9e7f6c4bcde07f88becd04593a2502d789a9972a6139c67454752d9a14c08c4f26beff72b1dd6b3b1a3fb83ef38602a8cbdebdd40d23ebc06a6439012afb576433af4844474640b85d37b575b850a9457d5c117f7e9a1983f4f1b3e8d889cc17a297974ddf7381b387dcd254096051d39b01d4803092d2f3328030654503494180399f6e5fa388b74b4f8a5e47c0b4b7451b5ced4e44b8504eb896d8aaa0fb1f052da154d5e088c4bc42be1e55fea816079f5a1e06a58e9b99dd654db80e4100b7a2fc9c91eb686fae3ca638244300adf28b49df16a030237ed69f1ccf0f9201d24836de9d93da85364ab02c5bba7339262358aa5770d42bc338db0700f270eada20c91593966d8bf9cd2d71c6b896b51783fc4df3044c9311980b4bf19df247695c3e6d1aa30cc1d65440c1fe98261b8d6e19521e19e9088b33e2f4dc20fc337a5f6da816df420f0693aeea6eadde6a4bbb8d366689f00d81b55daf654bfcdd4e14c0eb7f925436459aa5aaba65e4d2d1461d40b1f624cbe6015927adeff082304c2d4d66fd621ed27e3072f8275467a10f7173385050dcef3a4a541850afa1a2ab9d0569cf1800a0030458d680c200a29ff543d9c4577b10126905c319b0905527c26a0b0a00f4f4d1e7cbfd53e60493cfc6f3b3faede72e766d555fa4cec819658bb0bea86f7a4f4d0b2f9bddc41f01c9226a0063d63f3d7caaae0682c850b08a3c97fff7a26f3e2a2e7e57f800a56ce81d268e2a844d8a066890ea452cbb383915dfc72046352707912db64ad45678a163f14bf113ec01adf9b5c26f1e185323a0bf803bfd8575022e8787343a91a12e424f51cd096321151d27265ba128c6161ff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eecc5082ac7ef37a2e3ff524d06b8a106f88535080d3b4916872d34e481138c8b11bfddc28c1e9466726718b3d58d5798c6b1bd129eb9334fbd9aa81341c3138527d82e3e756a7b124b378c9fc50af8466a1e9463be83d19b71d77ed74c8454699cd835db1c8915c6b44628950b15e4fd947fcf0896e9537ad4991cfb0032f7e6203f623425c67345a67fa5372a1d3480e6fe78a7345c2eee81f61a53fa46a41ed601d6b02bf0b429baae261fa8baa94da0daf042d8f4d068b4ca97b5f8b0351adb5cb5203eef2c6da5caf42e1dbb36a74991d3dff3fad198728e65133b619ea52fd34dc525d49592376a25a7c0a8adc24eaacf968eedef9b7c1731353e7113de3887072a383fcc9fd69a493b8da45d4432f46a03ec4068f71a2d24f6fc350b6f70b081e888e1d944e65526fb88080e6d7c2ec6df89e2ff7602450c68577054f6bd588d275cd08262283483b49a7ca2bb8978feb94fb25dba0e0aa0cfaeb4fd13fd94fc605a9d2a05991976215af2ce49f97e4b408ba5074374232ffc59b64aa7557dcd610868208b5c29904188a4903d17af846e47336a8bfd50e91936be62b6b30a9205753d7a23bc648b75210877be6c3eb1b3896f47d0e718d0f0147913b2181bfe7c62b659dcc982e40d318552b4a3885aa37357dfe0be8933d74097cebbd0ecd04ed451479dcad69f5283f17808655e47ddd018b6a0822f3ef0322c4333a24e00158abf16d84e73d405a48a659b85952f7d0cfb9acbac82756524b744f489ffa5fef36f4cce3cd1ea462ce2037d9e1c3c19f05990632d4e183eb5fde656570a3eb5566621897fe9ab42287abca9c3d76f216360224ccdb5de321af3196a0e5eb6d08005c3839c84a90b14437eda5326b4871d0fe9aed062b5138643178490d259ca2617ad13f9f7b58a8e51c61da1df3e774eecf5c73aa31d0ebac1e7482a4eacd1c6a4fabc66f28252a7f42cdb3043baba3e853ba1fd8b8046f1e694e55e517910758667f851a5b980f7afe0fcc520a516f624042f44e37946692cb34fc1438c5f0ec113861b69cbdeaa97466f2a6d68f6726da77164213e68be395d6f78eec23dc80ed7655523305bb53516e2659d0f36b942126a29d6bd980fc592838af536ba2c0fd13ad21c143fc1fdb014f6b4da22ed25b0e3b8f272bf3584659e1bd553815b76a35ad7abe5b8ef1e4192e1df8b304672e4f3aabe9c7b8e2125aeaef8286e3066f827e78c210a225ccc3fe052b37a1b73231b2d4fa7dbe3e30a70843bab87f458f6247c4c462fe5a6fd616d43e9551859fe4fc5e09c136aac1833fe01f9f117b8b369a170597022d0e1e44e63401ee40e8810440b5479e4e206d06852a24e619227292d5368ccc6940c63f64b95222f8b36bc5194b534a255e30e8162b64c6f21e0a900b185d07aff1e6a7d8c9255db08cad7e0b73e80b59341ea15970cd5f62f502e32453df354fdfb8cd462f998c4c216f53473c999184c8ffbecc491b559fbb9a6e20f0d3fbed3f143d8957001b83e4202a600c8d79209ba033c826de0bdfc8b746e5be9b63709216f6464c49465871b257002b6b05be91728c32d23eb33161a1f25ebc3bf3bba526bfa085e985a328feadd6f38b77769befd5823bcb7ef9f496d64556e3d8f2ea962d0c6e96dee26ac73fd9e4cc3647daac103fff05ca47cf195964347690e6a41117baab879596cf44157d5c04ede401e01cde0ec2f88791459b9e92e94ebdcf6aa501df9913134d6cccc8d88d8d5ff34023ee083f8f9b80e135606a144667b19dbdd0646d43ceae257201161b558fbe0ef3fd04c1e71f9f9c347010dc51030646ab02e43b3b76ed71c331591ed05a7b00bba2494504627d24df5ce43667ab8f73040556b882425cce8fa6882ed3dacc551c110ceef9baa851d16441f1e64dac9c4f0e783bf62e40b3078d088c7d06a2d3a11573da1fcdbdfea371f408eda85125abe3259e39778e22775b45d074e9dfeeb49491ae75532605957d717f149a485c68039fe92aead6afdbf141af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a804659a3d62f51c2bfcab88dfb27e4b694da75af2e14f47541836209cee4d895dc82e48c6a5f0d73812fe022ab1b2e9691108a0234a77bf19cf0e1a964691f0672d0fd710632b74a23475e5e812f956434a82effd058ca57d0a4ec06e0e4fdf068bdbd34b884831cc2121abe0c0a85913f31fb79876aa0ae1592b93cb0c1eabd75d6097aadc91229f52f45458f2899bc37bf6007945be71d10a626bc05279c3700cd47ee3efbba9ab6fb127f43554b71a015e9d9c8f3d493a35de69223ec66e7df3c0f079b8416340872d4ddcb85b88e94af3824412eb6b3fc95a2b7102cf0665c748db354ce056dfdab3f09325681be0ca79a05f9652eda7b974f3712e6f6246fa8328af5bc314dfae6491852ff077a51e7b150404b80775cd26bc8af90ec8b5b8d2e4b87c4fa75d28ca65ad084cb4c4d26031e9665e2c3f05e54d1b59de3cc96d664a4855f5218cd81a6a83751abbf673f7a495fb69f20accccb90bdb63fb0c9a205de4752593e3bcf2e6654dbc7481f3328bdd52e0a52f0f64738a08f897c4052b669a8ba69a47a6f470821c817bf2d517a0517bd7e5ddc7b4c2d413f821d8112eff9a541facdb41a8a946949513ab4b5ca8c78328946b31091e31d51ab7f0ce79144b3ee94f7003278153213ab8c18540ad701d4ad3d7192f741cb2e6a4b3f4473777ef46743dcaf3fffc35f08e34aa1c4a8a05a6f4a82c713a36b2b9a13603a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ff90d7fbec16a736df098b0c0e7bd70d347d1769636c8abcc2ce2355c09aa7262c35e4a1aaf6cd8828692623aa865bc55f039b39a6bea14dbc6be47da2a30b44f1324cb51b8fbfa1754ca20fd91c296a0a33d3d4cf8364b694f84aa39938ed3ce2fef456bf9cf7cff823b36ed0a2cf7cbb51c85935fd0a9b42e1122922c8b86caa0cf8207ca88b2c7d816fb775d42df5fe462bc26488dec62418a7a1fcdd8745e5b1e4d99ffaa87dddb9debd0d250fd5a2b920812fa6d2781d7a1b5746d83a226298eb0c23f00a684678c2e624f367a2c73870079f2f4dc013306c5cfe096a0648383ea7b9926d6efb895b7bb8ed647aaedebe27461c57b1f8f721f289d8684fc72dcdeb1eb30bc8d843c54e636c6e41fb9c58eede381229a55451d0474264fe0cb428ef965fc15b51a089473b4e4181d83de2b72e4dbbea1cfb0d3e0afaf6d0ee4fe8cebabf3b0c0d8664547fb557e749385492c67dc975f491cdab0772cdae41622f65008910bcd6270cbc2a636130927ecf194a1df4b0b40939261455dc0337c10f468f12913db0ec4bb36f761a7daa2c65ec4810498f8c4f6b3946d1a3c86d2b925871ba187e859b9c06d64909142d61e5900612a0068adf8ce89f2902560a53888f416889b2325d15b41fa72de07ed16699742c0557559d85b9933bebe1658c55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b80bf5dc3bf57fe2d80b98314ebb00c7ba0f5cf1b1fe86d3d68865323ae786748f95a6fa942b6374dd861ebabab4993fa12c4a2d02ab1deb5d4224d0ab4f2aebda36187222c349b94169715eb4d87119e89dd2dfed69ef36228ab1d589ca08e94483edcedd2766516864055c4424c2ae26e42535e200178e7375e6217564597e12727618608e81b40a712cb80657ccc0069c62018583ff058b87df964f2237e0bc67e91516dc721659e9fab6cfd78f6edb3b118465be2d6d09f3acd91db7ca3fd7cde2ab5ada58a26dcae65a516c11917858ff706fbbcb4233ddd9ef5273c6612be4bfa8e8695a80ee856a3326ac375dce3fb8bb6b40e874f9d5fe51c3c15d2bcd0806546a6dae7546db78cd1a02916e8aff3499d316b740ae62836ca339c00c922f146fbb6d1a9698a98ee501b428b2e2d19de0835c16a7e95be304a0f62afb847c428f74c32ec4a947407274d4d86b94255563c57204c8895f40a84df7c204d74c5959230554f8c3c032cfe9ded5d88e444cdfa4d7cd984d47e0b07e8c5c0d4d4312b4692b7b0bf0ed71a9882c5be8cbc71b0f92d8b74b02506f95b4ad88431107f0aace6535155cce050d9c434c198b1a4be68425a6fc5f832e47145702248694425fcc712561635c71728212a624e8625d3a999ae6e4280ee07d7defb7ec497cb68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b8441678225acf81e13cec69fff09ae080c15e1a922c2e785656c84c4edc0ecf12cc8b472bfad70026bb0ef585147a6dfd1e010b1e67304f62cc895f5bc927a0d514fb5590af644d9100f5df8bbcf4bf222f1d081946cc15313d62c4559f0d852a4e31365d4a7a1b5cfbb23c8179e04034156a71c09ae5b8f642c4eb61cbbc1766e38502f0ff69c7f3cbbfa19ff099cf87a2e92230c40d550bf6639e8576b16be8b49432524aeb235fab34162e0b1f6786bc5be5e60a74444ed1e05db7e89d751868aef1247b89dfbe5656dee42913a734b57a1eb00124755fca220241c0d858b865163775539606180ad9cd33b9cbab7fd58a53565dfd36b4323e773ff627a68522808d0045910b1b28d79fa63844a01a5521dbb2fc1ce240a28834dcffd84fe0f0f494ea96e4c3dc334631cf4c27587749c95fb8957ddefaedf2cec32fe3c3e638f533cf2e95a73256ad67ea83609a8667574c3655918ebeafe6cc398c475a2353c6c996c28ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b465d81c52a82be9215ad0eafd152af44955f7e247a9e2eb41c882e826eea0275b079b3b200b3901790de169992608fb073ffe8723fd09ae407a7b5d8c7603b1f4297ae12dc502188dd6ec7f159c18835360386c0c62627b9cac9d50a8a6b9f4c5a5b81052fa60ed8d865ff08c4aae969f46509b758b57eea9c4094729bb64a57401d7dd857459a4f41889851991ef43f5e2e7f583e4fe5a0afeddaf9f2c617da3e6b7ce1332d4c69ebaef48823e499958377a7d18c604c0df092f4a8e5789042a4b4824220965c3fcd82a2cdc5d44bab0cb098f00499f349836d7cc27cd43c8d061930639e3d6bd8f3750d210d6e8a4abada978e0c0d4205c9cfe0ad889e574f01674f83ac2c96921c973b9ce77e72f652047bd70a390536c379bc15e2beea4bcb14a5604717bd101360e2c2fef38bbf320b0efdc28bf5c723198cd4778d3dccfddb7e0bdb882860d78f0110eba90c1944c80813fc3f11fd1e2dd2cf3554d387117d1d9de243c3e6eb61746a9e66947f91e41cb4d80824cfb4f750000cfce5db241793e7c34e392281fd7470a61c385193f04ef541a79d98ea4a7cecd607c7db3033cb0e5a8f1e9f557eb46c56af5c777b8c8e09aece2a2d4fc843927d6634b5ee17bf463ebdd244481834d817cded8817b2bf7c6fbdf7257992afb067395de26a2672bdd0af5b8e453791c6a18b3266a96f4d42c39edd10e9f752d1cb5d8a05e5456c20da51b5066630aa01c34d697b89decedf2d97f877e1c578255d8b2bdaab2296af01642588b1661fde78cffdc660fa80ad0212aff47d90c616f2cd5e60f5eb482d687aa76663467535c68a1358f6f886bd1ea7ac3b89072fceba4f5d25b128bcf1ecee154da4dfcd458f5c96bf5ccaa782d30c8de59133eb2259ea3d67e536203cb58a48d8a59317085a374bcde4a209f34f651105715f1732bbef99e020c8f50b9a14a158f3c9f184fbaaba347d80d4027300e5fd9b3bdfae24b17043a5b8178be6b226f07581139bde975d9775115199052d1766547ebb99a891dbc51020b17254a3c8e73054dd58a876bdd37bf39b9b8f0e8fbf73aa7f6c6bf01c96fa091d78c314b6c5e4ab2c94fd6740e48f51f20402e148ff7cb71c35b11526a7ff961b627dd1b86655b31703915c8fbc58d370129c936d1bcd99023b969c70b819184ca84229fd8bcdcadf0e6ba04e5275ad381d9f9f0152cfabc40452c7446f597b4eee387665513999a5e50a5ece4fa685ac4eea43c9eb9cb6e12fb54ebde8eea765e806a4ad0749600973137461a68e7179a5b54c5becda7daac89e9275cee3efa21830c86f7db28a3b73d7ab76e7b9dbcc9ad6dad42c0a367df68fe7397584c6920f661259bcf70cbde792e299f5dff37db0794039a38e6a35ec4a6dfcc8e6713bdc87fa6044680c540a5595362c49413bf0d051c13810e57b92bc1c5d5aa73fb5990f191583b43e1bd25a1d03dfb8c765ffa3f9c096d8b5c7960c71ac59ba4f778700496a602d47ae7f13689511c7ab6912c2d26283bf375c138d911103606f253beaa58dae49c3ada585d1293bba568334093387e2da0d3ba752ae267b7793c0732cf10eacbfd6adaaf5181af37175aba027cf0561f82b10b1d2f8543f66e5faf07565d3f04243786255708344d8658332efcc8a0cb9ec8997409be29d801733bda1f04abbc983b41d98c6cd5030f454339ec298955d24a8f59cb9144b71798c3001d603bdb04722e3272196f4f295052a8c1f7bf12a084e4d69e86da7b98c0756a30a980bd415814819509888355f8c77390a2391cc01b8576e17068ecff2bd6176cb140dd36fc8806d15fa62decd55476e01c0329ae20722b818398a6bbeb51c93a8facd7b68be829756f8d98352c309a45007e9ea306ddbc9fa0b6f39de35428d5809e9c5270dda229842fdfc949ff7a58eec700825e87bd3c39e4f82df43ded27c0843c8b02c4ae18a6cf774c3a97c14456d04c51285cee2ba12ea43c0f2e9df674d8000992e50a4d00c3cafc9ee8626820cfa5f89e070d55954f69b8b01c1314a00e7aec2e690aa4697b34e10b01e768f7325188474fd73b2a05a830931ef841339778877a1b0d4e660ed949651c8e3ff7ba3806aa81ba12f302eea4eb4642b9455e5bc8fc93d03b472062a5c8331911afd28e9f8fb78b26c1d7c03f0ed5823f4a4338bd4904807ccd3dbc77184410f30318df1b9eb1b37023a56aadfeccf43fbb3fcfb1cb16c7f3b9f30d007f6aff0c8c011f1de25769fc7f35ce7d06db5b45a0503e43dc49f50e85802e2913ba3a90b69d785b4ac7af9948012a26547e58be38d9f7f17e802392cf7476eaafa916d45f77685f68cfba55ac23e6efd2eec64b4fafcf8ff3477b885e413ace2965bde8d050e83b7c93ba632201b546a42797d5d52b5bfd99925c03be09136ce419142094a32bbb93f6c3a97db489c973e2f069247cbbc8dbfc23a3434c171e25058ab2cad6aa88e7d1a36fc7345a10c38929e60b78d91ff2c0b155db35103b16e9fcee5ebfc56af635103d1f5c49d5e839bb8d476008637a6094660dd684c1ab300672d51a46c25ff7944c98e5507a75a275bb73d280c6e4fa315ad056eb2774effb852527610cc9dcecee79f44b225d42be059b4efc0ed8cef7f579a575e7698f1079a6d01d1fb0eb286a930a2351074cbd0296780f1d9b554e8e811addaf6237ef6ad6021a03f43c8429589baab989b7cf267c1da5a49c9fffc4015b09b68d08bc3dcbf511f129e6b4d5b93a5ea95e570009b1b8a1da513490fc70ffc0565d27437ca31133373ff7138be8fc08bdb166681df8645e4475580c39f8be53754f3f71b96acf190e8d9dd85f72999f0b681780b89a11a76f2284f28804669bd4ab64f86a019689013d43d130783646072b1d09242a29d3ab940105aafd232eb66b05a54d6085127f7a15b6c10cd972055625ba60bc35f80f310700b89d263cc2fe9c4018c5c82183a24dd1090c669a7f55f1759dc9befc2c66ad0e4f4dada31d6995ca4f7811db9a0cb8c334c7ca3b31fbff9d166789da8f93d4108a0a8a39bb1a5c204b50678c249986eac4e66cecc32488aab3fb9dc864a82b455ccacd688bc0f94daa866e951bb5babc96a08ccc4a74f9d7f172681ef5034a9cb4e74f6d89fff81b58773237c82135be125b10a04ec7e1282f333057f393cb2ee7acefc5b1fb5fe4bc0a2403d0dedd47c62b1ec2aa9df189e7dda6d7764649163dbfcc0f27ccf7f34d396c7890f81cb03289b25fa30e9cd99415828fed0ea36b13e63b4b159466739d54820b357e1909e08cc16355ba9f5e53816f12d509c6ecdfacf3622b79760db45a8fae85f6936fc0824ad85094c84658acc892dba033e6bf32bb7913f479982a9e61fc406f67f6564de1f568edecbebd434b582fcd490b5929aa9c50f02c0b6067f904d383c52c9dc27d3a7a4be54cd23bf7700db6ce5cdc7958e7c70611bd8d3874a8e1f6004701e46722a8974c05596f923787e0904e3c3092cd7ead879347545adc4aab8ad5a707ae57c27b8f455ac049f03bf57b1539160dd9855a6223fb34adbe8133ed5517fa7ca237a4427e89f7c88471a602bdacf798fc74d51273588595e415e1ba8d2d75ad16994ec137f49b6e1c1d8ccab7fe00c72dbc01627ba84dbe40868f3237e0afa42a328f8d7795cbe8293fc39534117d337bfa281de0d94c3ad4670b00dde0c32d086f5b0435cae1f7a7a63861cbead6f5e4a58416c085a868e074cb09f3d78d3b4b3d71eadf3bece614185108b8141cd92c701f91c1d840d4fa55280ca1eae50bdc8da4033292f38c2b4b23f8f22a487a51f53b882eec1563ba41d797e3b51ab383b561d010c7835e867d241c4e199554536bdab907354a1bed1adc33072dec48141acac7a92e6eb5b9876aca9ea502726a51bff40456ff6f03044257215384acd392aafb220031579ee624283c1513a070a43608dfff51986b13bde4217e333538d41c64e6421cf9fea6941cb689abc9b5230e35ad99627318baf59335cead36edfae76fec3a61a924d798a8e6395784b89a167fc2c36d25b9a63ab1e527a912b653138d79e6b1c7da02f41f833c1f85be8e8620fda024e821bda67f946a6ffe9badb8e4939d626cfbe2305eb3d562ef8f4699388f63b9d6102f2d8696b239bf80e24f13c238f9b95af24877aeb45edb9c6e7894d8afac86c08a8bb773fa47eef5f357329290ebfbb1f43ffbec1105305667ad1f69e8989801c2bdf6772e8ec43dd31144993d0b2f22be407b3ad304b9b3e12010a1c10acd01af2ea16f8e0b0d747994356cc7a28f7f362e20e7e2b67a48ade6f0fe7dd68b135d1932164e63ab2d11bbd188b441d6a8874a087253c0d8d985133114944e47ae231aa7d2b5c5a0df525aa9004103c5d652f6a554f5a2aebc91e72265a2e91bf9d29bad948c8e04f4329aaa933ab25dd3ab1294c2cedf0a6d5b9aa0114109d9a5c45b148a5337773a6e9d0aa520bc8350956ab11c0982ba06b0b848bcb4e070aa03cc650c4bcf042abebcfe9c6a303696e30ce87ec35944b6cdc3383a252d5df790446aea1be7d1e6922b58a0ab2127f88436d7da7c0c182eaf5801f0b75f9fcaabb0ac794dbc0323dd30072a2d77ad71a5c86da1a9dcc68fe9c7da726c0e672809a2bf099ceb458b26b5a7f9e56440e39b0dac22b2f2087479372dc62669e21a89fed198098c36adf187d8f529bc716fb289451d1be7bc340e58e24ae3e1adb71ddd50a753f48300fef327e6259094f385eb863bce9a494c8076bbdffbb0cec4e2b52e1555ca19bac293a4ce6bcc311a9382d54daabb6b16aa6aea1e64c7c2de4084593f8ea00e483ab76731b18b38015d3579ab28c526560800bae3ed8959e885cf27050843d262b1b23e16b3f630eff22b92932c198b17d2affaaf6a7ba8a1987db5f996f353d52bfc1f15b2bf722ae123b3a39749ecd31daf901aab73d7a7075a23b5fd8cef3477927a4c8eb40615d14ec1563942fcbf8217c6a3da6a4c68ddc945361eaf2f68e138cbd26684d3777ce78a9ea13e46f6b2a199c805772a2267d976eec43f4d1fcff47ee46814b74199e61ecf79e7390803d6231f74c9598c41eb4959c38cc2d4a07a53cfaac526a85ee0d11f13feaa1478b0c12e7f56ee5d51cb8ecb320e4c9130fbc725547407dd31fb054ba22c99c306a1bd2f011d94832a189da93212663e93d6bb0410791fd19681139d973cec70b648b9c986950a4cf73522e96e6dd698c541b05fdc923187713717684cbc7a7b793b75bc9d373e23fd44321fe04ee2be10c7e6f7e28f58b0dcbff8b6c4facb4287f65b2d19384b291a935c65459be3ae2e89bf53e48da8b7b1b392b6dc971617489fc236d48fde759b727b40d8dc3977448ce291c68be796a5e5b22292f8fd1fad35a98c4744cecf6759e83a0856555b5d532c213eab6d61449623c44c4712774d3827edd59804e8a800784fc35ec485437fafff752bb2b20e9d98ab6a289b0386bcb60273af6e174838e24b7d64128de6046fb410d38950ee4d47ae28910ccc95335b52c1c3734233d2d0f6ffde1759d136dcec07a0d15db4f2f04566c818336376e9bb8c02ede4ca388ea254d654b2c1e2782a95fa708c9b2497a17683f0b078d9fa1412ed6c2022dc53027efce6b5b5e77b085ea011212a7049ff6c8409da72f0f01f8ee220f32cb50a5d0e3fef3e79f1bd240f914f12ec64a9188f8d5490442ab05a327ad06b78ebab1233a5b26bdd787e1e96cad0e55344f2ec58bc6dca607d464e427f2135e45dd432552abba84f372dd577a2a64b3dc06bd1bf1a34e6e4917f8550f4968f2b0c1ef1962ccd78c3464dc1d012ff005dd0f4d9f80d669c242734e22586ca837c0a5cd20740fd18a05d35e0f182fe7c38dbace92239c332ee0f744ac69a443cdfd6733f0fdac2905156475ad1d3061238ba04283251f0dd56943f0716764ce9a9a27533efb728793f9b587239cddafd5e6654b06869e76284540f12cd5421d6038d91c833910580a98d4444dee5bdcc29a340b3e0c5dd8362d871264717e99bf2f3ade0020e4a120629219fdae037d7ae98bc6426de72a5dcf6fc0d9958ee0847de71e18163e7c54963c868c26265bd5f60d9710fb27fd9b86792ed87c9bb95c9275fd8d5046426883efe9c4f4eb97106dbda71d6215d9addbd0a6981b04dca7c5e25da1279c94aa35053a2f3d6db6c8d04e37e3c350fc8178f6eac53492c4739761b42e98636d4727c0c6ffe7ca1ea922ef6b5299bff64a59bdc1350bd3c8529d7cbcb0fb8a55f982371f18346c6ab5347002a1220b62559322f8c5ec14f29327b72245d88f4b8d29cf4c22d9db9df45ba36cc2df46aa23393fa7bdb7399dde369c0cce216646792c63952c6fa74ed5a58501e984a20c189dcfc960472081b3f4c6c918b3a3d786a214b7b61fb79c3a1bbe424022c6db66899b971b841d136880c348badc54a32b8bcf14f5e4d26831a82df5787015698a4fcb21ad7aa084dc4be99f83f238ffd9e1c5121122342f6f7c253299ee9aab50efd3cea5992ba09c9f32ea9487d9cfc4efff3fcb8e95629c4fb3032b3c2006ec11af7f9a323f706eef19b9895e7daeb90b4b425ca8f730b490dd9637148205f407a11fdbe2d9b2ee2594d1fb3ad3fdd374e6660e18146700b78e376dca2dfe62a3c6f5de8e963ae1908aa5d0d860b82ce719ddf5ff29744b7306db110a1ebae2221780f0ec7a9ed8afb70e8693d4c6fb7374dbc763627cb35b671f33324a88974578d6351bf2eaff281f9529baa198c489b724c0a705fea1187546cc9f02da19b4258732c472acd177c399f0c2ba5351967a3450a7569d0a581af2779fafacdac7cb0980f118ddfd7588fa45c58b626b8cdd21efb631ba692c16f260290dc32287ba33d1c742ce7943179a2d76e247cc9ec46c91dc0cedfea2578cd72cffd34c3c687b21479a1e035967403963b5713cc143d9fcd52756861081c7c7cf24690c7d13fc2add7d7a0d5491780ff661ba364efeeb3bfdbbe76d36a4406e7938c6e5b85b0689f1c1bb4e6b5129430edb93d328e0649da89913e19b38e2d60c0f6eb54eff5c74e53240145f73f773875e721fbc7d2880555fa6db15951a28c94a1fe32876c3510e48a6fc31672e819d8417880cb36f20e36f0a2b158ddaaf1aab64104800e9b368892e893d57a8c7bbff388c9f7606061d8dc93f22b59a466e9cfd36e3b4bf0d5f13995156fac9bac4abafd99d3e63f434897512deafcf529b428e46ffa09456314872d90a11af4022c0c2b5749e29a8ea79dcaa1bf3e90f3d9ee0c5431a6fb3413b184f39d2cdfaeb2a97029e1dd747b1eaf1c0904029ff23a2653bd7617149ef5754293314db51bec15eeec29f900ac47f05baf868371570c59c2ae6a81fe48261ce8a28b859155f388cf83d8dc409d263ef6d5270b856db81e4f7d026803bfab0c2361b155def8287a1b26dd17c978e9f0159a05a288b9190e5fa2fd66e2c95d7c6d52e4e5884b5e898af6377e012333732461883e64cc58b895fb76a34e8fb8899b6f0c0f3f999f218e6dd5b6c81be336c80fd2e1e49810f96f5c89eedead4ce449b3359d80fa29e118936f0dda10d660116ed83bfd8497dbdfecc1968d75afd1c9625257f0fd953c2f56039b81608a070051eeccc20032ea7ed3d629f583fcaa35a8f33327e75e3607e00bca203cf83224114cb33d2036cd31720cfb470a4e4838f608a00b1f751fcb8027ae8294188c7e9610800ccf47e55c0d5fa83bebebbdebf0b7d7b207be7dff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eeccfabcba0a2c0265614613ac4974d3f4a677223d81864bfc31431b483c1e7433a6639b13bb0464964623ef11afbe1d0e8f40b60454fb396db5adc596551487de88b44a61165a141d7cbd2ae9fdf22a7e929ac9837b137010c48237767220e08c4288d000b885a5236680bfbbe966f5a38fae25faa76500303be544beaca5dcf6dca08f507e8ffafe968c5bec3878e4a450bbec417b0d11b6367c222344c4388dd75d4773cd78f93e6c70793ec7aa86dc58d32a683de44895874a0f15498454229f5c3754f7db194b666873ef44bd44060498ef42578b440187b3cd92ed6c5d509f3a7fd42806946b51270bcb1eefdaf1276c4200ade72645fb596270d03b449ab3c468fa81a569ab5f6e0a433e87d3bc269ec844902397cc6908fad2a5254af1b2df453a7d803e9c044e15b256c284add19343fb0532fc1d52d3e3006d0389ed6175e9ba49faee1e5886668511445d345f29909706d0c1777289d6b9285fe4b32c6f17271f7ae5eaff85a3d7ec6dff17b5808593dfee6e98ab81cdee3761716270be76e8963a5b32fdb9f13b7d09dab1b358144fa0f7d8e2fc309210d58d3ba3f334eb0a15e23f5b572577cf388e5101f43020709220da1d22e444294429f70066c028363564058d55d50f2396cae4104b4a689952e0d7d7ec5029daaf6e0fcd8197e59304945a9b3eef4db7e31fcdf660eb8f5f2db87fcefcf7c98efb6360704382671e60c24de4981455f0b5da3885939be116716edc3faceb2ea75675915fc80fb4ca48e91952bd70413d14bd71f114e3f673f1051e495c7ae71baa8e0787202f52b499dcb495efb783122c5e117e987c87aaecfb67f2933fb9c82bf5ff76574091f520f3eb497804b064bb2263bc59d7b2f54744975ebdf86f21f84ae0a4aefc93ef135125a1dc045ef713ff24f217b3fb89e486b3a75a1bdc69604503c4a92a58e2d9052c1f6c965ced51813571931358b4033221acd9be6db3d50b8bc74e97f9bea005272a6d9c170c0387607967c89992454368ef8720fb05c42493667c466b5146acca8cc79ff5775b32f9cb44cb75f3353aa0709933ccb8b587e849b019b9a89c2c4a3051aa203dba39dba04bc23f7b7d3294453cf28fc3e1ff02573a567c6f541100a16ff979528956bccd34f4671bedecd1fa5a74d8b59ea70f00fb420b5a5281d578a52f1f3d0fafe9fea1c6217860d8dd2b3d4099553933d66ed6bc25adeffcfa902106c524d3f0eb7e9c016140e2656c0d20d35d947db9505946b5e5181ff043432662947e4a18fdd1c8840002dc4f8ec50112bf672fa868058318155f2a322f2e9b676ec8fcca1039db5862461298db1c214459ca9211ce245263e503b51cf3886d14ca1be40c7276c4040b86f57d0966d7edba3808bd134dc15aca9f6bd77b5c66da03eaee4709851ad7e3899287b48b194e7ffb7b6f11e4bb67e26dc0d2f38afaac379bfcc652ce33a981847e6ae87cb151f9a274f62c49480593e95696f3cafa9e2934095c2741af19f5262ae3f011520c9eb140a0e04d09b3192353aa81aa3ff5370956270957cb5dc85e16c7eed0d42793c2a6499f3a2e601bd8bbfffaba64c43a98c88dfe946503d2803c065d44235de8542ffb437af51fbb391a4da40a38d9a6f2e02e29299e5f3dd0afb9d0cb8fad956d19502f41b33e002e459beae89855b01030b7abc2da9f202e7386691c129440427d348d8da43e300b5e9fce8aef1831c069da34e40a85b995dc1122cfd758a975e478a98e27c685e897494889e9717b227ef9fa9cc0d864d177520645fe4f692298bd257249238a90bebc84e71de9c0d1d7fcc7e1e49c5508c9988b7f3a022adc2d05c00da5e874567c0fc44fed7a62e7aa5526dc85b09dbdad742af542babd7b9b99e867210a93e069742e884c3572bebd25011bb3ce56be713309f3815f9e301a604b07e6b8b462eac38b7462e5197560e036bf92abfa96abd37b98b7f4720ca1b75b5e3580abcfa5d3fd50a0e0e89f75adaf8633d5c004432483989c27af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a8cf7b186a7c6c241f9ac8c2c19bb0637858b0ccaed5c8ef1abe3a7b73046f6569cf29b424cc269c9cffa60303230bec610bc8a259d776ceb8bcb8174f8513dbeb3b62a32faa35a83117dca0ef948a9d8177028f0fbabe64afc63deb037679b99a26dc1da1d948f1fd97a0275dafc64c5ee755f6b9fea489d70a8a602430fa2c7c96d73cd927f80fa6ea1fcbe7ece682e020657742bec9f815fedf4df45188bfdc0c7a70265a81f523b93f2f22c73bd5db40937556a012b0bed21c93649b4474fb952f5289c857ab7f1527120605d25afe6dafdad962d6146e241b225acf74a2fbb5c05e1c0055e571dfc41c1d990589d50be1fe53563e17be7379aa012db6373f6367412781a2958235e9fb1730a78b8417bcfa179851eb0dad99a512ed290c7884573cbe935834e94802953f7d1379cd3b1f7e1a0b2528b3ca102c2eaa1d365fed4a03022930207f9e5cfbc69365090aaad873f89de12910b6fdd42eedf3cfbd0a470a797971b9b7f4d85923ffe4a6567bc2b557a8dbdae2bdb8b72153eafc3fd641d60ba01ad1e1a3b8a2025df7b4ecaffba945d20e4759fe8feab4d0b8584defa8f66aeda1f71038c92444df4d5183dffdc104d9b7d451d664e4a9b4a4d0f6da1b62b9f61f1a2810de2124965006321f19de41c1cadc89c9a5937d3faa14a7640cf7096e5cf38f2cd3b774800b131d4eac349fcedf2c96ad749bbe8d37597078a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ff5d067a1bd5a8d1fa8ed4583bdcf551aec7933aca4ed50d3d400458f3deca0d76f6aa610469a3ba3a346138cad0a1fde9831608d271bfb21775cc7bb05c9518565eb2c1c66853e9a923e0ce37e3abc3e5ffa60e438fc67f9f357f41c686bd481398518a1600d70815f0db4b236f0519a8b7e7bbbae36f3ad08e354a1d23b061fd0cd0b2df5f15fc507c7e862fdc0cc3dcb8322286e456813d4093919eff509e8fe5e69abef5312c8b775f767fbd0eef3ca79f6ce89e5704b62311bd7e94c6fd494a55e1075e3ac68c9e95fceaa2596fc4c6d4b8783261205e69a8920f09ac456c048298c9afae6a14dbe5209a02908a62193335b4aebfc677d5024d768fa6a4600ef2dea5580232260f172464684ff53c45cc59c8facca309210356ee62bbf1c62106edf9230bb56ab116ebb4a462baf7f23fc981442beda5e82969ee13baaec3bf2a8c4adf7f79affa28c1ec0104198dd5883e144ef7646e86e74dbc4ea63452d4d6b98c97e5341b24687092aacc4bdb993247e1193ce7ed9b8ee18e70b67c99480a07df0a04eb8298f8451bb9a87dc7f7c93c771b001e8af383d54d67a6a1a31cee9eee55d59cbc32c1b87883bc8042f420f7b0870e0ed3c86e3c4f02c95a6cff3fa7c2cfa7a5b8fab0faf892491ff77eaad9c14b231841e69410e19d5a6332ca55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b846b724f7afd1217886941f4cde8e09cb462842c1a86bd67f743677ef5011bcd6e81f585c33e28cd94b5640a2e5741152497b3bf2cc01f6fd7cfb927faf71fb843d32f701dc4167e47708e943970f1b14e963718490c98473168926b2a304780291276c040d98a22481d4ea906979d48f54f753f27b6a0ab1452e9092b1b684c9a7ce847e438e97077a9fe1a5dd1023500edd21ded5d95ca6b8086730f6eeb2a030465e11eee8233099e2a423897f5f841a9727e76596951005a10dd502e9cbe8f17ea8a3df4a87775945755c1e0a68a4c2720ce9155f1b3bf09259d92bd38eae085d4f412ddf2f7c23b389d6c090b5e7a1da3d1790f38dc63ee686c29fd57a5a089d064c78b1d025b91f74cc3f60c5c1d470dc9ff4ad02d7793bf27ed18fb2610aa2d360af52ed8969abaf587d9cd1135f287dfa75b6ec2c958c32bff4d7044f9aaf89a1b036b286cc77b2b3d0d27e265f0e62a2caed98682b3cc64c026d1c78d8505d18f91d689776f415eaafb91aac5542b6cbb4934871a1938a34a0fb625e3251cc499680f48ee0701806e1a07d32b2f9b993a6e481674693adb4c7027dd8d5fb5d592018d184b2a51d39eea742a0f1caadfa0ff1109b3317847e674c598a1946ce9599d650c876e32e7ff5201040020cad6df129806332d8d00e675bddfb81b68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b84416782c4979e055c2b21204329d7cf0bb21d51fabea950ea07fbd0349287c2af9bda7a39a286d74e3025551d9f2eab9435802126a70526a5f4b73d6c9f9949eb6c1c069ef2d5657e250f58013a9698985a8a6e449dc8f3d814c123ea418342694c0863dad3f39c821185c41d652974ad512020d26cee272c267773db64df55e43d66dc97cd1e60f2f61c0f522a5661895ee6351e34e5ba09cd4d170d140453e07ba34cda497d68a20aaa2d581e059247f1cc2c34e18b777969cb7ea52e68e6604d0a4c71f0e231276f4f34a1cc744f6bcddf90123898509d05d4e3a0cf9219d44c1bc05a5ed44151ec7fa05c961b7ed8feb69d06859ed52bc92992af0471682768068f04f7a282ed8766cd6ae9dad660f3c5616ea71964193b83823fe0508667ee3cbcf59b8a2d81e7d3c1082df1874acd4e8d039c5d0c96f5f7bf523caa45d837ef51bab81c9fc65ef5439800a459344fe0fa75a38408b8b10c5cdc6fb723125010396428ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b476ba379312b8cb5fac53d179c015ef8ebde2eeefccea77726b6e5b14b4945597fdddf8e4aae6be3592de7a66bdd89110fffefa68471030dd718a5d3774c8bec489d38772a4a712b5dc23b077191c7af3e29ba10c7fd46b6e82153eb3f8453d9c3d1137390b4c4d271fd898dea734fa04d8d8a70e98ab417a06a3a19ac11719a6e45cd77aab4b2d73393bbf56ecafbf0df01d5ebedae0b06df08c26a9ada121e1b5de7163176782d5693260650dd2bcfcf0cf015e8dfcd1e27549895b52938f728281a2d135d0ef0bafa8c9404ef94f42487a6e31fe94075e648cabe8e7f0b5290319786d9aab1071a090fd001ea9981ecc789cac8ef2240ebbb1c71a9050690ef309997fad5bca14daed18298172fbbd31357901a799e2ecbf2812c7c7dcbe0af4c672f092e5037b82a9b17e06845fe99a5702d8f9c1393c3db4118884931fcb0230f6837d728cd9fe9a12cf118c315b02ef3e436546b36835bddd1ed2b3832413927da7498798a142f64ebd00e7f57cbe65c284546510a440280c293769c0eaa5e096e518b97423a2afb899fb36f966575983f86097842a503de3c44f3813ac9e2f1bfacbe05f3f0e269bc94a00352942db8057725773c97fd45ee88a8fb3d58b5a943618271268db54770fb442248a8267dfc76c3f64d6e24ff16920cfeb56c3fb81f54cd1df33acbe57933d5628b4c1ee717c4e49cc06a3c4d27920b2467c06a81757d5e2b9bd4496bfc345b85e75e5b78ca9feb5537ffc2484da1723f88fcbe0fb8c2ccf51b809e784a92d456fc8438548f68374710203b35fe6b2da6e53cc6fe61206cbd87fa880a97ea08afc54a9f5c4397d9f6d6b860b53b617a41c243af942ee6ba6f6f6760733a38bc70da4b7734fbc4d0395b3cd35e4ed5fef558f5ccbdeef419c70e757931ffda951cf44bf20922e12b00bc3c4f91ba910f1b5f00cd0c220ddb1282db3b64734e0580a11319f99930cca8676ea76702c1ee5b5d7dcf2f030a298946818c5ef87b917d954c75091b01483c9917660cba32ab395d5eea461d5e1afd040a260f10fb3850cad79dbc175423523ce3b6153ba11e8056655d455482b5d90cd11768f7e465831926a1c890f6335b26464f53146ebbaf258b2ecea728cf380b499f189f3cadeb9511561887f427abdeab247b685378949fac4b550547648b12bbc0c32158367f2c98a4d1b9f401f081fa850997051f2f86a404d581305813c4d31bd8027a91834a8e25f94d8e4d3d0e8a95d2101fc01264de0e3a02da4edbca60c681f6fd020b5ebba6b6cc783be53f238a142ce083932e65ca792c832b518c76699b4fe611bc4109e2aa5ffded90e9198d2a4c9debaa063b8f645b1fd1bd8b9c04679ea30c5129974c81b464d418bb729834b1e3aa7241bbea080498a6d1d7a8f82fa07fb2cd8df02e9bef862c007fe1277d0363b9597d609359ce857a5e326f2623d81b8cda9e9179d82e0829c4bca4bc680b28a1930c808fd52e16bdee5998615e0d592445155a8b8e7e9d22fdb5c8c734eb2c348ab4dfaa10bee6999ef1cf4cabf13369236fbf8e44446601cc54511dd69934ebe2a2277db2016e789f36680a2afcef2dd3b4249b82b06a2e1aa60d20426d044f822aefa936da14f0d2613688069aeda4a39331f56fd0a79b8fe2e7b07f31f6a6f10045be7bdc93d7786756416c653a8900c0dc17c334718794d95f2b390b426207f70f7f03a2b4d98e741fc17d221e86ec72ac84d315bc53aaf08a8361196901e1aea23a3c903f1c316d89466ce47f1cb0bde65c268986e6d3f166604053ffec7ae1bcb48b6b33e98ec2cd915a2aa4a4e1b57cad375ef8aa5b90af78d6cd2407ccfa24433eb7e0ed26395470cb431cbc0ba2642676ebbdc959c2b99534a999bfc320418ba2a615a1f4a4f8eb5e55bed4bc66267b041846acb2d973acfbfae1d62973fd910141bb702da69d7786e8420d0f00f16e84c16cb2857a5bb17df94de3360b04958c4e0eccca31716b9514018251fde0f222b5ce923e0102a76a4aaec1cef73ddcc06b0c57ed661f1b8c4c894be967206662cf9ed5837481a04f97a8a745dbc32ea23c0058ba2fa3dda279d534ba7f8aa2c22b9eb5973d40d4c990f5f695fc9e986d087533836b89d863801b1782625a1ebfe3d620abd0d306671c976b8d72b737c49bfcc920bef260b98103d9aca44913fcf3fae58f2a839f38d4253159b41d838a70138a256c15c99de989ff1c069ca210554ac6e073d1d9eac1afcb2f1e77e12311f6a931e40d1e55f1539464795bdb531de9bc960e77b0b3477ff2bd2c0ae025a9c59367d6aca2b4ebecf2b0d6d604718474d0387f51a2445c23b0fa42d234ab02513e19835b4a249e2456b31cefeb98941e9b701e2cc2a96580d747122eff6dea1b261fb2c5dd56707c412c1202be74cdb45581405a752721063d8502622c0d23a1bec154a14b8b54b06a67ad8610401a41d05ac6e3ab638e1a9af5fc6a8404a4894beecbade3d00ea0e926cf7a27afab8c7844bf2d9aeb864caf2c69640fe6a0d554342226e7a100b67344e0054d70730d8f49e768b6abb5ecdef583f7203a6d2a76d672826342d901aa8cebf2b711e324978a7d792649e203d7438dc0ec24305db409be3509a0e69b9ee486349eeebfe437b64e827229f2cc68386ad79e4fa253c55019b743c84fea37cbc6566cd2e6cfa7cab496f422fa27b05cce4ddac714d28a89bee0b28b5cb44e53ced07206745cb0dea47dbf70f3fa5b2ba11638c6b8171c2bb89cfb072ded67493db07d0bef1eaffc407ccf9e5f391176fc40c3a1e24f51703ca06c817149b091713e9ee92ccb2010b35a3bed5ecb1b3bda58300a07a6cf1381ecc36902c04bae4c96d81cc217852a7c80504d36c96b6f1db5ba109a9a855a2053882f18d419db6d9c7b13fc352374e94f8fe9da556f82a892b25ca61eebbd59aaf9b7e9d6d28fec207e94b1ceb5ce2a801c6c93327ad31227a16f7f0ec56df29e98eabe6c85263e5e1464ef3ae301691ba0c70da51c9bb7eab2832562e7af553d281940c1c409d2a11f5bfb92a16bba26c1596d911e38aec02b85f74a66a3b9aa33062c5c55e75988b2c29a53a3c240a26277f7869adbf60c7e7810ead6078f91092833e72a9d8eca7dbd59e7eacd87aa36fd07171936eafc687e2df657c5a90a56c006366e9071967c263b682f9297fed35f144df24c591485939a1bb043a9f596f21b814adbb772cc8ec8546d41b1acd8c52771b03cee20b9c5f19076cf6c134614bfb4815e1b8e5be23145b8d6d1e9bdaade88cb70120608eeab2aafb280df6acd6d02483ff6bd4636b2657fa536a747c65cc26fcf29949c2c771200a8b0db777b5c292fc48014f4692695f4580852183a52684ea4b90b7bab88f9ecfe848da2e4ac59fe6fc6f647dd61e407d18c521dba80962e99cd7a5dfbd8dbf8cc0eed447b2235f0698485cee98b79fe88862f16b5ff026a8139de426624bb67f8d558b9582f6a0515d11ac6251e717815710894843dba4a564185df5813d1bb3c7763e60712f64fa8347d58ac234f1d9e694f044a6bb7f531284755b6c7f7d83a82ccb652ab20820d7b6a6002462ee15cf269010e803e2f8992eab457a126ed5b93d9077644db43d0b86de4211a42525e942fbfe4a7766d010838ee183598cbfaeddeddad45499ff6558411b9e2fda0e2119a04a93377e1840602de552c15c190152d7dcb3e74286e2f1e4dd81dbbf8cef5155e111b97058869df558ef8c0724fe73c51286d6d8bae8962b25b14af518845eea820c08e08ad99b9a875c0c2b5b2b1e95aaec71616ccd9c574bdad3fc8ee6797e0062062e4f1793b678eb73c05b58c2a9e8913c8e3bf2795a7053d4a0b6b1d14d6a6e49653411e8a00feb19d735253f0559bc2302a85fd00fe24ffe85a7aca3d3a3d4b238e0738375db4b72a2bcf117b7fd0c565de9cfcacbc95133536aaec71280d1f351ca7978eea0392970ec6c7467d8741098b284c3b9d87a02ca27b4f23f9b8d0be763d21c10a46b3a7fd635f69b17cae18607d444897b68cc539524286924898c0503bf6bcd4c8c9a787cba1d56be0485f3fdbcb4582bc6b8498397f894d9576bcbd3075bb5648fa152a279804dc796b0f1d4b2218e14d43b740f9c5a8f90fba6a63aaf8684036c92582909655f3313df31aa1d99efc636e345f04952339b8b5ab0efd244328d06b85f65762ddb4c609f02d567b855fb849abc9c33c8c50298590a89a9cdcfd176fb355437081e00110a087c1d832a3d30af14f97794e68f5621160c2bc495d7423b7640b5333d73b66c0f5372d1a0d625eafc25fab5507a8a995cdb8724c92e3948fd28a09b11c8f4eb82d48061716ccd901470325f3f1fa1f0fad67f87b966217f8cc6f5d32aa08033905baa200dea0db6ce30fecf06e4f967a432b0684e9ee9a92739908101a5f2f1ba31f308ff88768a10a85b88e2bd20e1d5bd45760738fbf7cb43ea1f8b6e142db56afcc1e6e2d3239c616a0b1770b90c3d5a91cb6037b6eb2e94e1f0e2fa9bb132cbdacd9fa64bb1c3b76a5a1417510ac2ccdc3adbcb631416ba0a0026f224823c9212164f6d8d1efe42f5c6f6c18e4aaf48ff156300ad0fb0e89127a13a1fdf6ef67f01cd5cf529eb988d1e0c761fe838689e34e891ebdf8859e669b26090d3cc4d7d9638da0f36a52158b6a2656bd356c69dc814d262130338cd6a91fdd9e4a8879585bbf0f7b29e644687616284a09d114facf9dc297fa3373d8ac84f808f4de9277d9b45640f6ab6083ea36624df06daed3c85f877da83975d27fefa2bb128629a3dafb4c655f6b933dbd3f2b779107403bf3ce6ffa6245e84bd5695d034bb8873ab2fb6f4a66bd6ab7c443cabebffec097183ce4fc884cbd3784e757d593e942046be1d40521a050e05bf0136e6c48083ef8ffcae41f2f64d336906b45e4f38a898ba7c7f301dae23601632199e48b68c7a5d05aab505e1c7485d020984bcbc81ae9a7421f3fca5eb661f71bfdd9f3eb6eec2ab1a9bfae017597bdc0a9a54adeb1befb0b4e2fcac9c8d9e9630ecb0d9ee7efbe35cb16705f090891333a02189b0dc8f337563de245887b26f36b8b7ab4d5fd9924066f70731c5f30c144f0a387264ff0405e53eea6d9830654244c08e05e36e1d1347d92dcbcb8c52c0ef40c9eb72cf8671f00e28f8b3e13d84fc28982fbdc9ef22f53577d97bda52a8baa1a8342631beee0cce782f3a8f3a280a20aa545625af313ef6f380cdd8f58e160ddbae19459f5e1fb1c23a4613a93dbf9367d29b9349b9aea133bf5ea64a0c2a951edfd2b400d8110123359ac97d5ad8823b6857afcccd6eb419147c7d75f1ddf38b6aa3dc5494dbec0f26dbe6aa2f05e62311271720cd9a520d86a0d52e38cf8a9fd3dd0ea2c5bda0191460ef8ec7d708cf77a8af2193ec5d32671dedffe1861eb897659e32db8bb53e9fa5f33a6f08e4b07047c365b2af4ed6dbf100c60c86266a2ae02625f4bf98abe4043209c3b481f36ec2461b6d97e5374df8712aec33443e5ffefba1239c175d07fc2a7c8196c754ac3f100f1ff2730a3e80dc516fb241a3f864fe9d5a40ed11f120b6992586ded635ebddad116ae1ce676ea8b039973733dc0e032eaa26cfbdd4761b2937ce6e9fd167ecd6e8f70f00ec610f8dc2e40997e725101da54058b9f234bb4e1fd45511181d6d54ed309d488911f9ee19eca5b6d7a275a8ff9d5ab669d74e460d20a7a2646393ba74ad6c1ef85b1d9871c6fd127e0a0928ffb193d38bb8632efff6f788febe03d743b3d4e69816fbe1cd81e542958e7288759f70d9a63b529b6b3983b9b5e54b0f52e0ff183d24e307536d049c5f336a090c89026cbd2fa0929c36cb1cd1fe93168d6af2f024d7ead5eb6a1f4af0e065ac45bfeba6df85ed44db6ce0fa117fe729eb52e1377a6563e5ff3e02bfe4ad8d827d7b502bb582a15e231139cb0c924b0cfa41178c75b516cae08414974295e21cb6b34bbbe8b8fb3271f998faa062adc58c8dc9174fcd231f3bfc8979271d164e3a998e7b81e31a1046b8df7fa59f484407530ea964992daca6c41fedb0c86781e00e014e2cb870af3d50b51d8a810b9e5001fcbf06c95adbdfef997930161b6284ee5071b6d66449c6ec2804c1f385c36862e37afd88a240c0344b46345b5b01523c3a31615682b92c7f34d212bd5216f154994f832548fea5c300a5486fceb19e3d63f791838d2f5de31b8c5b0cfb374e018e9aaf2221071147c33850ee8e7ae521e88d37160be4233812bad643b3c8dd60af255b4deb0882cdd99b2026ff14d0672f5388585df724c4ad0c1073cb031f6c5e48c7f78f119829c10c629f30fbef784fa403a93c2f8e9b2f946e003b10e644b2e5bc2e7b582bea367c7d363821c6a690b562f85ecc3b611d68459c84e315a1809d9cc78aee141676c303f97f7a0a5367bfb31a13cc7ab1307133e25de01005546b3cc630155740a5e9e58be903211fa130cb8123018de5094b65497b5f54b90fc67b239a99af11994741a3d6f38c859e4a68597de7f4f75a81342fff9b077293ec7c472f0f9c53eff29b20a7a86bd5d9b2cd36c1a30c9bc72ba4ae724064a2aec27022cc1b36be6505bea2e15f5c41595bee7b07bb5ecb44dc4d8f662e8f179caf56302838cc5337c83c2614bb0a19b026ab64b587777a948c00089d295f4948f151486a505cc5190c5cd40e9abf396ee70c1731ee1e8c909821beba4379125e51883d515f34699607bd596a9caeb8875e8f6dc782db09e1acc1329a2a9f4c641815417d6a3b14a656741a1f307a184ab02af1c577d2e4205a1eab99b2a1ae1aed28b4e6715a63e38a350b5d535d94a52d7c8f15902261e5779da7c66dd7e94d5a7dace1e6ee18fff6fd58c8167bec229e1ccffb65835e567a393263bade03144ef8217baafd838fa1dae16d84bfd912839f30ae3c1392ebc1b2206eb80efa634b80e729c9fc099202e19d91538624736d374a582ad5cc4b85b09a4261eed56540b3a07b20ad876a84a00eddcd9205bf736f8d8284e6b92ef471ba7be1d52c26cdf1b5765ce6ae6b7da43fe99333f86e9cffadb70108c3d5cfce1fe93cef1195eefe72f798d9c4f962e31220e64f432b82d6ce7ec2a586026957baa98139016056450cbbdb555c8155930114a6215c52c54157134c11e20c062ac36fd4b06b019c8051f50e106228b71f411a2efbcb6357cebe37c28656a7b60339e54dbe738dd445764de48c595cc6b16a2ab716568282cee070928d78c5680c586dc0a91ebad15e8858e4c0b165a30db4f8c73f5b32c3c7bb10409e1406fe06f2dc3f308a2fa7439bccf26f595d48da8e05c7a1402ac34fb72ef23c7454710321aad6f629e1ae4416374fe9932f91408253d88f6f7a6736c03962d2fd9e495d22f29222c477e2b023dee79bbdf746bc5015770962461f118eb035f650608da5ac42e9fa1929c2e8c4c5bb7984d41e4bfffd5faf6f23346702f2992e15afd645ffbbc1538fa4f2abf4ba6c3539a95d6fac905a07ab25203a5b4744bd09b66fac1efff0a5925301819fe1084d33a4dc0cfbcc61232c985adb1831b0bb338fc2e68e95a45377b2be10085580f74568fa558fc8c2c4df50941ddfebf4ea905bdc272e9fb691e8f5f1f75e4cbe57ab141aa16781364169a066fe3387ad2dcf033142b6edb536cfb6d31a8da3499fa4f7c182fc268e183b1cb2f679cf39f2be8a594acb8027ae8294188c7e9610800ccf47e55c0d5fa83bebebbdebf0b7d7b207be7dff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eecca7aaccbe14ba4ccdfbd5083d881c204d5fbf84537175cd52f65052721ebdb2422c4095921c6a3ef3e7229e5e946715988baf1e7d8800a3087e276c58f787db357327bc41f42986f31e0ed8ecd94610b7e45994254876f38b39bd486a470058cf9c84fa6e771ebb8ed30aa726d612feaacbfc26e79f7625b4fccbc1ccae3630645e2e19925887e47feac7a62575caf76c2e529ad7794a86a821be40b0f72846e7558fdc95f31f2e193d28b95ddc8d21cdfe2163f566b01e52dd86e62076e1fa6486a2f44b84c3b2e4284afcd1f49af22ccdc1521b239c54f053a56be3cac54499388ba89830e1cea0fc21aaba4ffbf37fd71db12d2607713cd0a5febcbb3d9cd098339feb0c1ce3c6b3a2a9b8ce26fb48bc5b946595b17ac02eedf7fa932e5a5c807707b707773aaf7f4d10c308c92567c0e7c9eacec0dd1ce95914eb1a3ab1a4d3eec8d30ed84ce2e5b21494a6a7b512b0ce5f90b08fd25f1af6fcf9f9f5ad98a79861563d6f5a23f4a74a2e62a88b0742a6124855aa2f7543735710f54d17e57b9bf8d0e869fb0148f22b8ba7c7dab937aa101a408f80aff68199d16ae7f4012ad96831902951860a354e35b21b7edb673c7c05d5a1e213ac7d013b30bc5d81bbf964e638217b6313989f094b7eb955b8c5a1379160eb1e5a885f365133a951bd8a337982e1f95ba30a2503f61a5bdf33bcd7294e050025757fc07b7fb53eb4eac510059b3c7d66447479c63ebefed43c45ae04d6d282d80b39ddd77cb0408531eacac965ea0e8410707625770588e39f7e9ad64047436754f9f3c262eb4196f2c3146a63dd3293ecdf5cfd33158131165bb242bfc8bccbddb18d8edff3848611431e8696a7b300e6a7f650a4f668b17389b3b42dd5c7ab90e051261f285426edbdf8c23b2b9e48809a86c9bc07e65876e64c6311c7b6f5972840128452ae10809bae285613a3b2e7747de22a172588c05c29e24f361897ec52e79aa715bc8a5a5b49cb67c2965ba31e26a7886d20c453ed9185c740c96f559386016a008edba59771e368b89ee1deab73c24ebe10baf9dca6903edafa8adc7bfd4ea87c09b8370f7a496b1cec76117d05e5a4a03e04080f36d62f6a3bb441d1f68015388e49b183d6d60e3dcd50ccaecfb7cef9a2560caed4ea5409ac4ce22b576fd50ab4d6f9847ffd510189633fd9e31d9935397faf845eb7c30f3a724b12cc90ba58e7f8a8f999193aa8fc2863b56e91a91bcfe464257dcf4211366a3e6e3e39a17520570472c05e2852bac8dd126fa2150c54f7219378db507f8a77000c135ee5d8ed5041897b08bda4438f41bbbbd7f0c2f64f4decda15833dcd232032ae8a15b6e15a03b1d3ecaaba3f7438e764784ff8180994b04c5ab5d2ef8e9c8b8135f7d0b7059362bf5b033e5eafcfb74fe467fa519720185f0de62c2ddc9db885720bb4b93ea66f5b92bb692c25d2c1b36470320d5740fd7baca55b518a0838d5d5fb5c77a65bc073e5ad787e9e23815a49a61b21a2e6c714404856ce3b0c8ba4400c10c159684d2747e4759c8284a15105773171598553ff53686b3233a4249d0bacc12c25abd211ca68e1fa2dc13c3a12832e1e0ca3cca1607bf9d4d77362e150c8ff5246c09a1e94ea1bf558f041e53822beb4a7d02938fb8f9e6c986ed957f2bd6f98b6f10425797d24da8491041f8d77f5314f03744680bb48486760dca142fbc4c3248e5d4655d2e114476078c911ea4ed506c9317f6de72638df0c8acea5626b32e60f30472ecd351d9e591acd55db8d6297e4cc78a24e798ff1875c5761b4390659fe2978eac25d7a01c07bb1529a1c808913436c39b43aa384a79e16012fa6c224323c0e1b6cc7425c815765147105aaa9334134a32f2b97a87fee9cf49efe942cf48a0942ae60580907984a780cc709a385757ee49b22a5dc043c5105440e7aa53d2cba9003e5843961f96425e499574045069f2f4ebd7f4c94720ca1b75b5e3580abcfa5d3fd50a0e0e89f75adaf8633d5c004432483989c27af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a8afefeb85edfa1c99f810049b24480af2812fead91472ffd1238cc359b20762cbc7bbef7e36fbd704d5549785c629746446c0c6f684feeb7ae23ec02a36176c71a3eb89a021bc18e15d5a09015b69fe99aa7ba9f5ea0bfdd83414784c08dcfac773ab8cba9d029a02398e5fc150b111d87d60d02868514745ff73ead657734a67bee7f30b4623f1d60b95f4b5db29303ca4aeb395e9f1e23fd4522e2b7cae95500caa76d389e250bdcbd9bb71fde61f1526316d4f8bf358985001b3cf2657d5829525e4fc105609b3ee5d1278551db82dccc13f134c5ef90a2ee853e5bc30fd918e0ea23f1830ec0e3677ded18e1d0ca72b55381985b28d80ab4f079bb94ffb4672b639202cd90889217f26764eaa5f409194a62d1b7adfc727dc193206ff9e093ce359377dde666d9f3f8d4108033b7237aff72a32d56aefca74f1a0eb536cc6957564ccc71acce3fb4766d3cc8edc8f9f79c4a783c51599ff6f8a2b536f7be300225e141c6c133b747c376a0b1ac328c1b31033c51bf47c099673e02dd0d88d169b9a44cd64838296f39d4f8b1d21c3c42160d6b52337e7c40cdda259f98bf74995b7ed84bd38e69ccb6451404c869b97056de72236aa20c23bd497301a7ff671514e3a707e9ec8291cf7f5bfeb840311b963c21a23a7ff31b6b98a729e1004470cf7096e5cf38f2cd3b774800b131d4eac349fcedf2c96ad749bbe8d37597078a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ffebf252472909a0de6c0199fa327131a3f8f9f2c8f628a6b2b4371f38c53858942fd4992fe5e1e49020df6476429b5b4660782d3d5efd3e7c4d786e47b58890d1aea2f340319a86e8dbe58a323ebbfa8e5d8b7385dfe63e140d1513514a09f16496badc4a742c9b824918d91dd33bd6bb28229f2d1446476c2aa5edccc1b7c02a0c3a02a887d8866391e80da4017b3804c48ab6b3317a162e8974736a5db683296f793540b6531e3e5223c40fac03f6248f2f1cf114a67431a16f1988278bcef05da245457213890bbb258e17e99b71ed1720b14e9a41ab9aad7147d39822da82f00c740188343fa1e73014615fb6387c1edecc187ea04b79c9174d67f03661648b6b9e1da421ac1bd7679a80849e3f42b0a401e40b42e6b7db3ef9fd46c6984715144f3d4d49e86822d8521a2d22f66861d81176693d4f36c89790347eda16175fd7084191ceea259d210b45872fff52d4a562bdaaa6cb0202e7d005af430b010f898ffe59c9a9e8834229f73c4dcccdb66baa640fbe9a124f13d4f92581f826337199ef0f01e964e820c158cafab8e260c66c94388325825beda7ce3ba7ddb3b662201aecef868ca754ac4fcf56e0f96f7956c525b8d85427e96a86f066e1c76f3fa7c2cfa7a5b8fab0faf892491ff77eaad9c14b231841e69410e19d5a6332ca55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b81dceb83e396bbd765cf7ed68c520e7ea48b0021d1e5748a63f13fc8c5492b65339b462f5a6cf0c541263034ddebd05ac88d651265cf2f830e46ef1c7dc0521f6e29b807f5e64f7d326d3a58fa38aafa84d8e14ba2c0ddfff509d3cb3c037d35a65f8f69286f46d699633ed1e2827b669f2d55ff959c9a2dbec5d0908b5cf18503a437ec7ad868521d27a35ee3a628a5f5f57beb5a28ba150f0c2dcaf03b0bcda185f083bcd820b1fe91af49c25cb60517d9be44248347cf9a25927e449794b972fc60c5e88aae2fe999c1b67a2c2d571d0061c261ecaf86ac87fa273839a7fe0986a3ddd18455d9663c2b01ea95c7970aada5cd7757f8538025dc210f37aa37008a4eb9617fcff257a80af5966b762670ffb67153ebc82a280166c5ee218cf522fcfa322bf681729c19c69b8ae8842a15d4754142f34a18d4b5f77286edd5f6214f897b8a07ee42ad72ea9e5442e04a46a036528c288ceef309d48c8f0a3ce9c626015aa25e25cfb4e8ee12b372ed341525736587d1226009d239e2ae82ba05b0db29fe1ff59d4fa805432d92c42a50ef597414e8691531de48fc40af2805badf1cfafdc586dc2de9c7cb577f2899b674c9656360bdb591a84d5d70d380364fe8246ce9599d650c876e32e7ff5201040020cad6df129806332d8d00e675bddfb81b68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b844167828ed4d6fba36ee0f68b1825286328ea0dda909f87c1e5487218a7624e00a5b21770b1d406680493d4884e19df0ca7b9c4647b43f62768439ca0e217d30572a0ae4c1df036df76985bbdd377c67a7ae9be899f0b956df22fc4b4a2945acf2c682226a2fdb8ea28b670649bfdb91a1e1e0790a438e5bf3c6d31f561f4d69cc153f8ce751a8e7e7e1059d6c6b9dd7d867b456cd72968eb76231480be253f1f9ce282e72c87d8f43c1cccf3156f24f1a0773f653172448472d31b771db8df6b017533f1f845e38f6fdddb7614ceff04dc8b51110957527ecd6ebfc4f3b1515b4fd9d0e4cee6c7a2c5d5fcda6f2cceae2ba560560a4e51118db6d713df8a154354b9fa040a4a526526a9c7579b5facda6e171750e0011dde659ad1d98dcb4f1926f7269eede6baf00e7f6d3293f37407d225e7a439636b0e2705b2abf870c637571071d9b81c9fc65ef5439800a459344fe0fa75a38408b8b10c5cdc6fb723125010396428ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b45b73cd70d36fcfb9c2f8a8d0c67612b805e61672f9c40c26e6c0a58ecda4b725d7b212a0c744d0beccd3581293d77200dbbbef786c16ba1a6cab501d3afd1f3d4f31350f922857922c16f0385df195bd7a3c4fb686590d3c35a98e08180e0df4fc8ec9a15adfe98d7551e89aefdb9dbbaf75f156ac2b4faf2cafbc7eba30cca6d9e6a6a2adfab80a82b030ad4d7f189a0244fd32e546c2a4df8244e6f7807e04eb27c87414ac69c0124fb75c629a6047ace20c67f7ab86d723ff6f3849c19b420d9c53993e2765488c8c59baf1c18e12bb317694a292b3aaa66de14d535f332d80d435c22831f7359591f3174123d3981216c3a31146842134fdc203a1a8c9eb5a163a655156c2045558f951e0591f0bdeb71539ac94d1a97b4dfc55207a082bcdff19ba67c11cb459a5106152e6e38fad787e461084018873235abba59a19dad81d40670989e8ab784932fa4ed0d62414bca7e8856426cc3902d61fa4facd9c49c09ad3064edcb51fbed3a386931239e4ab366af0b2782c01aa7cfd1edc38253161c6c9b0c243e82e2a5e2a857a81b2edcac5260a62b19079ac3ddb52cbca7eafc2c907f17e16c3d5489d355dcd1a8f64bf611caab463226af780408fa2094dfb5aee84c0132f5100613bc86de528c177b02c8613e26c92b02d537b49dc158813234b7aa81e79507e9cd6378d3c24076e86911dcaa3fed844443f288f8479c49a937d0235a4077e50248fcf47ce8010e4bb499f6ddcd9bd5a6cb32d7a595b3b7131fdceaebd99937ecac768c90f698f62bcdffd7181250678d26e0f8eeecbfd674b177238a493d8bd6851e7922bccf166c7c0c9a6888ab17cbf8c8edceb705688ad6e5bd1032e9466b73e8c9038097056cab79ba770c6523a3a98911c955949909d97da5df2241f64fd229dea32981769d7cb90dd98e38bff798d0eef06e78d0c7cbba598125c7def50de207ca294bd883b70be641e7473a57e6ce0d6690c2dc8c4c97e0d41950f4dc4598365b0a174cd8a8207ded77d9fb05bcd2789df0a5268e429b8c9c5e0891270df76ebac8956449397cac1805fe4d6cb56faeaf88d9cbe2b7811b2d42f864cbdbbdb42ee1c5e8bf92eb954323b0566c0119f53ec9a8e274cfbf37fa3c3811019995c613ad8972239471f99f5eb96955097e98996d2a907d02964475ab4b01ae59e3d728f4bd091ad3358310621c8ddadc5f87f3bb9178f6d06c4b06904a6fdef2d1e84eb30cb87b0f68557e53f5dde293ab4734faf44e2ac558603f712ffd9109ac3ab94aea885942f9f8f44dcd58d4d9b77c802b652fa6a7ec2d60ac4ee8d68ab73914c4e6f7588f71032090e97a0d8d93f43cf42ea3060ad45b39dadc61c104794e47606fd1e9f2c966e5fb5655b81288e933d16e9575f0fffaeb7fd32a4f27087d65f37d7c0c5a6d2d0edd9241578fde0be6f0677726804a7b52357eedc94186764d279912314d42a7701e235bd27a762989cedf765ea036829715fff4be6c300ffab9dd78cd5ae2ae453a73226273a2d3d9cab7ba583dee54d3f96b009dc202aa68c5b808009bb991fd0b3c537c4a1ae8f67ed3d340f3c3e9aea93143f8fad47b34051b30be44cbac374fa1aea32a13cd0c84d6dca0c17711f030a2af01ceb71af8b45f070de37095e55fcf56da02c4de33487318c49501dc53bda7980b1317a9857080b1634395e415db7a98158bed537279daefff436274ef60b725b74231a75828702ae2ee7a0e7cbf9577b6e4dc34e8fcc8538f2351c64d7ea123ca68624356176a9719ea9c3fadaa4e4517d6f991db049e3b0631c797b1bc6a0bb5fafbb153e8afd4467d86c9665429d23061663c218c9f769a7d4d3d3ab0900a5721dc1e536c9498588ffef6fb5a5d6fac3d00f6647a2b6bc6475e85689ff836e7ffbbb717ff77e64a8af09be4a40a50c0a18bdd0b91d004505a550cddcba00785983b28580d195ad4e7e40cae2d261e4f82e7a404b9eeabb56820d458d5b10494bb2360dedf7d559cd1673a0cefba96547a0fb0591dc7b3d6d9952813e7244b30fa84ed1054e357ed99cee8038680ba13ab1a1784c7cc73a86b66d2d560d0c8d7577596cdd21e37ed72b192398e7f767b7bc16f4e76a396ea5fefbd0bd470fc7443fa81aa020118ffbf906f0896301de4b87bec56de149fab9330c732fba533d7fc254c4c93063dbe70e79e3594d3730b0f901dfdf34b83e6d00372214313ed6a1a86d8b8dc3a47ee8f1c6ed428bf991c0e3c04df01dd45f3f26d0bc8cbd105cdfbe0bc2d511774428d4d746f9b5830816b73c34e1ca1a7980a1b90b9d4803b8a26d96a29e3b2df0d813f84853a03971b531b2f2f2285972e15ac4eb84f9318339b99bdd82167c7224c9d3857b7fc84249f59f21b474a6edace78bba0ac105e1593fd1ce4079dc24dfc3a332e9a819a18c1a16262e4c2b7af8c76ff2fd2d79e0d1cd15d1cc5469ebfca78cd4ac8d71471ae0b98b23113e705e5b743dd3a3c8caf39a7d97f0e5f1146f1faa21f83b7092870736d538cfd5bb0f3ec73b9437ac6e84c3807d4a3ba4db98f3a3081d19d33acb494cf621a7c78f90ec60cb696454689014ef82a775b6bd0413a7414c008a3117c657bb702acb33be27f2df174f51d78413d5616bb69c3d299c38f7fe87900ddfa96b5f1a935f37782c629698eed5be708f8a49cc6d8519fe46e45f21ad8189c095658fbe79d6473bb50ced5da93c8665180b68e1a2fc85d35fd52eaba5f139076c515423915eb6f4daefdaf5047f4bd369cc68ee55795120d8554982186b33445c56d36474f4de92627387fcd47756ad50959de5853f6b3457a2e86935c4978bd42b4dc59d3dd2b08e051a01141c9f29b11557789a04c0b29d9213ca724d11467087bcbc655c8f273ee2737875da5bdac25cf3465c3b2bce39963217e168d7ec889a53abe3bd0adb42170f4718007fd14efac88f57d0091079b39ce54153941f7402604a5d43d57cea0883941e4a2a9446ebdb67b69ab8c45b60cc5f146e020ce1436d08972739f60d378c6954b39d57901c79a47b4379785904ce22f4ea1ca84943a7e3d04e4bc46fefce80983bddaebb0748490fdfd40a414f06d16a792b2b221a58ba8350074213846399109e5f243f40715871fe5ea4b8d5535e6ef8d3bc1aa67865d22d102de3e9f9fa980f10d55e9940e6fccad754038e8d53f8a7b0e0f4ea9a6afd5e16320adefb7552182eb94674ecd1814b040e1dc5b4687dfe8046069232b3703cf9dde1ca94c3914ffde5aca165456c0184e5162cbc647ffb20405e96986a9819e08535433bf9a7dcebe05777bbb5d19963dfcec2037c2b1b6b4b23047d374a9524cd6b7c2fc98edc6375af015bf4db8d1cb9177f3e276fbd90d96e375e129157eb332c5a9e6865a54b5d65a0d2e47929d944dd315e547329b60347d0d232d030c05b6fc4e95f257b12a9c299403f816bd5d2217816d004adf97a72025f550ccd4566844604a581d29018219cfb10f05116d71293aac0735335c13e257f2c6414dedc29525779fa9815480a67d923b6fe1f4cef9e240d1090956968c7c75defe82d1b197a60c28dd34db0b6c454538bbe5a41edb46d36de71ab37577ef748545f0180c8fb9ab9f05b9e9064233fccebc3bfb665476f86b0e44d9121a786aacf2c5a477613afd9d874830b5845dffc99664d70dafc20f7cf201a190a961a62e8f48bcfb6bc981520e78068f156a49a13c29162de25fad7aa90c6f99f92b9ad8bc54b411670e975594bb8e3d0432773fef46512009c578b06b677c20ce1ae12fd732536e607b7de09ecdee01697eef433e9f5b236d9426272917eae3f9258ebe08e27aef03498698bd2531167165db4d3a0a6d624fe473c12cd5b892a9d137802c869f9867ce261337af0db876f973a3120081d85d6456a734a7025a7a86bf62ecc58862f703be98587f0eff087e0d7d10326a8b814e16dc8c349ef02ed74005fe913b7d5560d06af0d026ddb6613512e01dd2f1aadda68553c30a2364c598c12c4a83f617ed463b857ba0360acb232cbe6babb65e1070e4343c1e43b72aa9791c49d25528391abc7ebfd22cb8b5a13db81ae9715e016d0c0cbe4c4ea5e6663b51a8b268e09f556bece39e7138564fed536d4d2172511a4a780baf4401dc6e7ebac1a579933fd5144435f57524919544f063ed95eb97d6720ca65601b04e2d2e53f83145788b019a326dc03e6da78381ec4c0992fac2f7f99e717d50789b36ec1301693459f8fe275447b767200b67f2512bed25af428ca088319eed887853904c8f541655a4c2e444213325a600c865bc501fabef9f433c5cab06c42855d101aa3414aa40d43cda5def7f846274f2f6a35c9b662ed2fd32bddc68d08c6ee2f121fe582489651f9ce9fe8ac169048f627691eb5f134eca53d5c7d39e6cdfac308abb294665396460cd69824c16717e70f9efa31ac8f9ef65985533b90bb8cb8acf65b090ac27fde9fed9218c47ffc2c2071cf8fa365f1c502869684ac0c91b4ad2a9087de8ce3f75fd5576d4b3eec4488159fd1f40d7ff89f90d1f31c9283b1805ed0e29b2bd435ff1fa902c84908693cad40c1432da7080dfa9cec76ce15f4deb5ef71e7cbb4531bf1375a6872f6a33685d66882abdc0d6afcfe2f3470921196734296e12e75feb3125e044454763ec586c607d40313d658720c8b595eb2b299974961af3499b2cd107cc4fb426b9976f9968cf6368b8de2a51d014e24b7027c663be28adbfc754e9c7be4cde666f5daa378a197bfe39fc7ebff7b5ea247ed8d296109d0a7b0c85266a7b78629464841cdaf8608a7072598d941551339908a871c8b617ccff61df0d7cba2706a2f881a6ccc39c6046f5e0895efe8b73a4b4df0f00267d7cd70349faeb1f719c69878f52b39972c89baaeb69a4004d100bfc727e1e1c38aa87f2bd28978b7da7808f0c4a806c82412377b9f1b92fc11e28f2d67f83dfd639f65e8744db57f8762dd23c4734f1d6faab116db6574c43102c57ce1abab614bc72c1970aa8e432fffaad63d4fd1e307366af6219cc238e957633fa92043e5664ea2c89e3672890a20a02ce73872ac790ad07d4b45a385701c99f01efac4ce19151b8f693ac6d4a7c82c6bd9917aca6c6e8f01fc9e9f3f7a37dd9fb59f7865b1d380b6e9f1eeb96d66f760e4102b8e6c5d41e7411acf8a9ddce961652c84536256b5baeac3e3f69615168e7402eac40a04c844d4593551d635df5ba3b75642915936cfd41fce97a7f8c343fab5aa5c89b2f447db2764fc3d3316d698771a34d69d7fed7532c595c7e2a74f723e26c52f2f8134afc8092e0dda8ab14e1286f6ea27a7f94a569328859a0ff374aaa4795739562d49ecc13adb083ae76677d9a1de349e2d59f12dc55f103a73c00e0e22058cf3e603359d56f06108d6a97caf6d0e4c2deb80bfe0641b21e37ab03759845cb8b240cee7db07f43cf9873d073b023b66ae869e33ee9fc6d3eaa69b2ac5fd77a233ff53d684d95451db6351e30df919b40e54c5cd2b50db507cc356982578264f11a1e301266742a27a0986f20cd1c483fca622de4f99140830920d03e5b0fafd86fe981a2a77a8dff484fd65d8f518208c65e3c8207edb73379fbf1b6b50a8004d21cc0b5cd944fe3e5effee176715176bb11646d0dd05fa7e0a0af824b512c4807f6aac66e95f4e0c4c8f58a9528d071ff07492da1caa58b4599a67c5c728a946da6302942e576f786bd9ae2e5157c405931e65f87954d5527164dd80773206c5636b4c98d80d60a496313a7ea57d35f2988519933458855d149c12bb737dcd84c48d0d421cc1e3b8af4ee2c1970ca62a3ace99b68241ef4587b80eb09f281e724219efbed62edbbc4f09aa0e0b3aa55379896168fd44d51db92f539317385fd6cc17b0066858cf02c34919286415fc9df26bbc704aaa790be154081758c6993e8a7ace156b6460c939a4a3ef8d80065943e2e99e570d619cac15060900a252cb612e1cebd8d226f9a6426651b98b7b151bb4b621fb0314bc67d440ff59657463e6253edd9be8b728a786b7329c8a12bbd622e86b7819fd3cfa4c2f4e7098e804a40bfd9cbedd916d76c5991fc9baf0c42defb0f9a8a43b9ff4ff78a0c89e460ed80c40635d31ea2404ebb009067822c6343cd42b25e3cfd51ef7a6635743a451a47cdf2aaa07eb9efa4bf4d1d7ad57d310d6fd2e73d66c9a26dc86f657bda8a8083a5a4f375b5b270b23f6e09efd088c9b43d3c91d82939ad0af31e69d9792e2cf3cc65390c47151856dccf78e4dff1ca61548aa7b984975af1dfa48855b88352fa83882b04e6f67fc6463339ed890e006b306cedef8ed4e3624ffa33ec9d14b09710ba75738324394c7ab46463bbc1786f816d6d72836de2fde8f05817d318f8cd2034034f51adc725f10160775691af224db9891f3edd470426d0182fa1dacbe738e187d4665609582a398a884296593706170803c4164b938b92e6e8528830491b273fd9f8e33877a17ef2b2094601bbf4d1bab34774657d10824f6970c37458f8cc420809e9fccf58f1ec1355d71469ffb935f420aaa0864d283471aa6a89e9eddf3a3ae7ac851056d8ecd6bcd1e3b3b4d205a0fc624399604cd93b774df17121ccb734ecd3763a0c03ef5ba3ce4122e7e31ca055ad49fa660443711c64df02e76d52cec1bc67c61d4d297008909bf3d8d26707af299be38981fb9313ec7da3541691953dbbfad44ba9acaba672d0856ea10887ecc32b08493fc12441e7e568cfff954ad0549ac00956a40ca4f1b65bf4d8371fa2ad0c3b10762880e5f8d16579053b98afca78f6f57a8818ab283272ed2fd12c64b6ab7a6e1fc68b43b628974accf8c550648ef4ac186d3883b4bed88ae9385ae7d66088bb5ad87642d40e452495fbd8419d0b982aa0e9841caf48ff86e95ee9c9669cad788da5b6e7bdcd1b5a78803f1bda8f01be95115fe8082860697381678a28ebbfb5223d3eac15395bda94224311041d86a69997a3c1d261fd03a49d03ba47b4271b0d002aaed0d3d36fb5771e0d666904b540e7c91670db990e61e5130df1d1f6da8456bb77f7ff682f78ad870a562f90cd021d86bbfe0fef93787bde0ce71685b5ffd76af6315ff9b313188b6e60e4d0dfd5e5432ba0f90bb2d0a8afc955bd53f1e902879a61856c8a267ab8d1dbb38e5d2099cb168d06e643aab7e0e2709caf94b15cce3c1cea70e38a3782da866c968b65f5e294d3443999b12647f60d8d2bcf460cf8dda1ee5ab595a7e2c49d0e40ca34aab0ab798babe0fff708f8629b6a6d48b9fb0880eaa7a9fcd144a8355f0dae97835ba2c3ff10d1c38d2bff1edda59b1c3dc46f8817973dddfff4c723a80c465f091bc956fecb3ac43e130eaf944bc478e5fa4c928dfcb0a52cb9ad2a9f31941f9b37876b10ef82aefb7252ed361a538ebdd89de3fceeac009fe5aac408103151abad2ac82051bb8fb2988454038c018cec874b2ad3b5e05896ba076091d787059018a0d242530c379cc58f8bf36e86bdb0aeaa209b88b8218b4b999db5e1cbf6251faa3da76deba6436f118615b736f98950a4e35a71874b3ba8557a850b4beb2b80d14559065bd20e33b6464f9e5333e76375f4dbf73d10b409fcf1d9d55f945b86c292b824ac79c3dce3dafca512ea66e803ef5ea5ffbb9188fe652d10321fe4f9a1ff6b8bfad43d18aa1b788bdd74f37c286fb265c2591a5200c1348b38a7a5aa4819e564a74a38d35c8bbec29882b8071ce2367a4d2cc4f3130b22d8fcabd13fd19e2f96f1a2db9b411b27473b47e9e6158d0971314862ad2046cb5759da120ad7850fd3afc31653e57a208c27bb36962f71c7893969d5ed7ec12b5ab0648c7829c775fae22a6de7d35c4439affa97221b4dd155218cbd1915005bdb8f80303299035e2e878b59bb1fbdcf95c26369cb5641bb4074c25fd213bc91f4a0ff39d59b1da5522d39d9aa6413ff7e1de7b45803bf621ee790e559826647df1e6949dc94d2e94fafa96eff7390444b4d4971820056d19cde5a9ef4d41e1201562b1e4debf967347ca5b6b7e18828e7112a4cca080e5b5a2fbfa375ae977a8b35d5a1432709b7ca8d32f17798bb747b48e2fbfcbb948919670ad76c75d334593a7f57d258841ba95bf90c2591133d5836a7fdc2a0beaa837a2c8eafd9db358899942db2e8872edf4416e258a2a7e9466a61f9e52003f4bdf6afac3c38918f8ffda117ef471cf373dec7e2a579a2b2efc9d2425d0786d6c53e3766725cf89ba0458716f91eb0c9ab8afca5860926db82d2de5bdbfabe988d4e96b04600d517185d36997d4bad5b52b6253f04d993df9f32ea28468521a49cd4e154264d0ff2913a9cdd0585e882d1f786906c936f00bbb9eaf2f79b94f536e14a9f3de0dc7bccffa421ce6b583b342c60cbffb34d7452b5965df9c4b9108929383e144b0a7659b8cc5161f1c622a6084521b03e0baa097a6eebb9d063c8c0249873beb35e7b207196504d9b36e17e138790d470d89687b3243b7527571e9b7c406a6aaa1de9cc5277c63c10abf1a1304871a52fc559effd6f0ce6a0bdbb8334fdb47c3de61a83094a4c0817779fe696f6e16edfdfc709f02ea10412d1db4d99d9ac466dd7140bf229b00069aed03c3d9082be127f95dfd640a834227a22c4853dd3374acdaa4503482ee7a427f9621671b3a975afe85630dfa03285b9dde2542b3d8ff7c28c71784c4d58aff402750bced31a6d2dac2187bbdf54877369b3bad790b93bf2d19202d6bea35f596b2840b4917c0fe1c8ed9f8ae726394441fdae50c97580cf2cc5b0febb0488c9ea27f17713d99ca3845a24ff57b903085eef5ff475380e53478f8351e776217da01f8b3692e66b2d196e12be3b569d8ae4f4ffd26359425abec031ad2c381b42b795d51490751c173cc40a0c6ca6a3fd40f4fafc18a9fa9b1f736cc4916e83095e0531a03c418222eb577baf6a33db9e19c9c963d32de7219d60e3bca1d369523b6eb6a0fbe5108d795e1c2162c721f2c1b252558cbd7d41244d9178a0e7573dbb67a0c992cafba6b9aabdcec0e117fa9b324ae79d5fb28b0ef3db497542f7bd6d58111bcd835ca91855b73b11809acc721b529a96665d6d14dabc16f0d1ef010a3b151e51c7105ec23bff6f0cc059e498a234ab430d98ecabda0896d761a99e0036164d05d6acc1b60f7adc036676df2ff70bb44385df444b5cf7dc416beeed7c9e1bee52b44c2d45acd8a6a0e7b97b47a7f3e9a6e9726f7b920f2c13a4f96e6ec3f31dcbe8920fbdf94613dea65ba1307528982610695df302e719f17ca749b67c4409317be9440ca91b51ee75fcbbec8940c2bb490a290e858872c798a89384bf2b3793e5848fd15266737c71d967f27b228aac4b2ca98d4885034aebb46a22eba26f7f7df08e7d9371d007b696409771b3f979612a3c265da50b40912eb069475b2e0ef0753ffc547af5a5f4840db8ea01c046fd44698410b9331f2f04142444e56e881e3f283934f4ae1b0d3efdb740658d3fab326caed82493abbe73c50ca8e4375a99ad11935b6a015269c1887785e4c276141a3269fd47070c7cb5203c2c56d0ce50ecec09389d773cd5d6c51a378e284a31d02d0f56ba5e9ea5534ea1b053bd5f15eec5d6d2a4e4f67b33de521e693e194f2953561e1461f6f689259bcf27c574a43203a72439f177bff8567fad4817842a62916e30e1f6915a311ac7ff67865b8453aa7f881c14a342a043a42a01ca4b190efeff38dc02a7030010623bc062221120b9d2001685bc236e464aa4791ce2a33d172e7439450b00c7c57adb587ce7560f24b7b16355d86c0e34e4783287285e9a12ed103746a4ce9a9883b00e3d0e568c6038650bb565a16f9a21bf60e6e7b4b31d65a543ba7f1eecd79c13d1224dd102ddfb0609d47f5fc1a2a14b2eaf65ee3a409d4725e10fb63ca0049f53159c58dd0a5b47034009dc259aabc6a32de186143d4968fd7790e04f70ec8d978448e2b352b32134f5f12b9f7d5927514795ed1a96441ebbfa12b1502edb3a575b997e0be029a618d35fb74a954839d23ae7787fa53a59915436e5a41085bcf386b073562b6ea6732126cf5b7210d886a0cbaf66ed5f5ead7290a68a42aa818f5e092f82fa9b0fc9a40a0d08ea95b4723afb2c0aeed9149390e67e0569f1e20234a3a73b73e2f04073fa7ec9e0395982075fb697e42596346252bc58bdd1cfc7dea0f6e5a0715a8465eb2b94e622962ebb44765042956a6b12fca76024c3d2b7a62da8d5b713a98ff9b23720dc8f967705c02018c7b654b6ca7eb37238ab40e9e94719b166a71d69a697d11d9e9937b224d3d322c4414b306aa37ce1171d214b4f30ce7a0e7d106ecd05e66614588f5cb4f9a50fac5af31aaade489a6c82b0251de81ff58d3b025a0a31bda889648a5fd0248c9274d422ec64ab295a89e3a59dc5fe8bbd1278b6465c209753440d80f06d7f100cf1008db2426c61b205fd52a15299d7eabf5dfde72f497eb2669b4504ee2e74efed68b255145f1b6e23a829278a7546c5562c43b4a42013f4bd64972983dac1a4d5c1746c6f6b4a98490bf7b7b0c85bfc7bc89964bcae15f0c493ec702e0effd3f7fe62abab865d08e5c3c8629ddd9e3c4c9b819bbf53c49eca868dfcb365fc31a025999723cb341697a08a1f468ead456764c5b057d13dbd4106e3ab0364c320f91017a1c8de3452b41e3eea28d0a49ee78aa46895ec959b1141109b1d216cba83d30647c500dc9e204e50960228020a9fd5403b70ac68241be2cb00a92a3ca58b36e4713f008d51adb2cb8c7b7e14d68e43161093381b25a16a9076d8b0c91a5e4467447af704fd80741630a51b192ff57074ec34f8c3c9b6b066c8c5b2df7b24ccccff5c5c2574603de7273e07bb7c4bed4db0a5878ceb20a26cbf13b7e53bd9c7a53b79d3ebb89302d6ed38c514c4b6434c0a20b66cb0a2fb7572c1820296abbcee13803f60c9263b563221e835f2e02b92cb7b8db43e04e4df55c9ab808559235a71bbe0d343e52a7855e0b1dc931dcc0dbb27a9d8fe93d259bcf876294d8abad30779ffdffd0d6a6049d11c77854439a61b635377ff137bcc8a9b4f99de9effd9e2a70d23c8e3a54191e8bd40992f1b6789d2f411d9d529b0540786c2ea090c5a695f93432eba8fa238f44842a54f8f22a25415a988e2b99e817deb3364bef599979bf6066a8882772ccc4ebfe2ee28268973d89f4d96d0860a10efafdbc15b6a6ac7201460e55346b81537fc3bd29519858371dd42b6c67e6e003cd4a6e4e69a5541789303469656896219b142bfb50684f9cd5d23245147e3748d22d6565af9803af07f3f436ce918cf44f865b279e2fc0b9a878ac8480447b811ea8657891ce68f05a4dc6077e65f9caaf2cb92af3cbdd2754b21813278c6b2d6f2ebed274b2b940627283344cb2c183859dea0b3f27b13ab06ff109a4fa6a67aced5bddc2dc87c1e1d86a37f77b1ecd0bfe7c27858e2abd07dafc29bed8704f1d09db16068fbff71cac1f3e8c527953e571c7e55c8bbcae938e856265424ca0e08214b6f981bcd45ad47b533ddcb207e2a4c7cca6dbd5b670300b37fdc840000f73f2ad996933b318563b66b70271dc43146ebad5e8ce77df2c4bebbd6a5924dbb983a8694d2565c36106f20426bd63c149e6e4caf058b5ed45e6ec8d2f11fe0e918f6fb18601cdb14dddfcce0947861e0c89a316fe134c28963526ef3e8ee85e0856f5ee2fc51475cfcfa84a4cebaee4513f7f48636f642186060185bcaefa9ea6ef2f94687c55f7a21d8ff0ae6b3b7dac8a07eb3a2dd4e0f70f9cb099379e5c8105db3811ccb35761e81a37cc6d272ac4bc72b587fbb77bb5a452805a790e16d4c2bcaa3e4379d79e6ef86bf0d4cd1c68aa7ab4aa9b202e75d5344523ea403ed960ebcb83ff8e1157a8018a6d040747dc2cf2db434335a8ce9c91ac6b18dab6b024fc63e045ad2bd246ca5cf3a227476e040caf5277bca9003e0f329b6dfa1f3be9ebd6abac892808ea5a4fd782902b7b95e44e9b797efac9376886e00dfbe9c191aa372ac80dc0cc5f5227cb08f6aba918da362584fe6c90bce76232ad4f9125a8e26bc2fbc268dcfd1c0355faa7783023c8bb38fdafb4fcae67cda920205f5e41d3886a6874e67fa9f32e08fa59bd8baa11d911b866bb7c632dd0050f51b38cb6ede01b1993dd04f0160f0dd1eb25c71d73c463e934d68ee72bac780d49695429ac96b03fa0dba1cd897ecf9adb3d59dfdba2b55982067bdf2e40e9b615ff59ccae4d4b5014669034120714c71eff59f84f7f8ceb1cd1ea1ec72f020ec8b7a9bd4d43a6c28c8d38a747909f5c379e2c8210ce317badaef5da06efb466e64015666907218228c5068804c2ba22840af16095f1c4bad9366c720bfd4794de71fb69dfadd75d6ed3fabf916715358a0c7587b3c187d5e0fb9a0b5130bba73ec7e404aacd9e47f82aa35270121d2b096902f6b25b94a111acb9850ff10ab5bb007a7bee36f49a46244604275c272077e10d6d7f2181b776c729f7b267487ed32ae6cd30f4027db21f028037ccb7133f8a336676483443d0f993e17cf18cedee94a732b51adae8489ec420cf358b128743fbb3068c076e3aaac961bae3cdadc0b2da2ed3c413c64514543c3024745a27d4c13e6ccec21b63d735d08c28ada684d19c3be9df3332a84c33ea69e7818d9016ce5adab6ab7bcfa180181384b84c8756454471488ce71991e8badfb1a53f21ad6b151566af124faa56fd54eb4e71aac8550ab38566e41ad82f697819ae4a574b208acb04b9cbaaf9e7e50ac747e2d82ac51637c736c6f9c81ac61cc92eb148db1fd443e0c6b228dbc76d33e98db6063001c223b4578382471d208ee1bb66dfdf336a96f6d2094d80d65e7925a06bd3cfdf813dcdef02ffd6c2dde9d9b8f67cde5d7656f1f714b7c9f9402c3adc1d56481cc443c3fe233598c17a0c76339b16b377580e84656aff236519c3720667a40fb6040288873883ad71dd0b2a6f20c6a6eaaf4799f6a936fee1e562e3f18e0b1cb284fae95f7aa5fee839d6322e7c0ce7e3ee17bdd6b560fd62f8c5d988efcf0e1f636a5d9ccad3a2376f68254f216f125fc1fd2f77999c7435c3c0d6f29741951f3b4b39ca9a3f4237492bc116436398f40612805b9c2ad1d4eaf9b4e7bf2553fba8953a1b535faa0cafaf77c933f7dea03b4993e070ea8664991bfc2e6edc96e9ed7e5f693b125fc9116eff22e16804e8ea68c725cf18811062ddaa784a1b4e42f624d6babbf3c128744816c795d567f494c6a8891436a82a6b5f81788ddb7458ef80ceaa06f527ce82848703ede1e121c5d5a30d7460c01967a5984f49765f52137d55e69f65f6c7b58eeb2d7ce5be68d8d97bf3c95e4e6617b096e70cf7456884bf10a222e7785348450a491e488a2b94d3a3a8e8b2483fb948392057c91927343868c2d9fdd7324ed55bb2658840fa076baa7d7204c1fd177c143d7fdbdd2047ad9cfa21da5146ba2a97faf5b0963ed6b21faf6468112f51e6a8b719a41e769ddd6efb9c3876b5d971782531fb96e8d595631b9d8f60b2576dc299746c2739c80c507180d37e1ee47f0865d062c3e62caae60fedc17f5e232077bc1f578fbe3c0a0cf28620f76d61a8014069f6e1943e8513ee00d027a32dedc5ee647378d47ea41b9455ee0f0bd040956b4f2d99c98cb25cb9cdfbca43689784ec34218e6189791743c3e72e8139f6f8af7e5c469f724e2accc593b38d77f2012e65036c7494279a169f7dca48ac89cd897a1aa625d35e4410d991dc58e6ae438ba68204f581e7f1809623c462fdc516f833e517f7040b796ef6a188ef70b832fff1bc784156967b97b3c2b807b2001993973196a53cd3e4d150979c35a948cf805a3fc80235cf3751752dadddb6bfb08bd704d7e7e778d99e9c81e3772393a23b9cbbfbe55dda040d171e9a1e67a41836f4a8e72a7fe8d6fe65c37e99fda1e5fc3446ed4fe813f6c94c1ec8e074b2a20ab4f7a46a81ba054bc252dfe2d77b7e46e1b070981d298a7a5aa4819e564a74a38d35c8bbec29882b8071ce2367a4d2cc4f3130b22d8fcabd13fd19e2f96f1a2db9b411b27473b47e9e6158d0971314862ad2046cb5756b5feb21378d1399ddeff734e0aea7b7f994aa49b36b3ba457be11e140c0c98f73d79cc810c5ab4f77fb3e3cde392f0aef147f8269e75232ea1377e1fa02f882ae45d9d3147152c977e6e7178ccaa1edfffc70f637a1e52e75958a39e1802449b4ff7c165157924fbdeba85e9aed88417d4597377c7cf0a36659bd62b0d139601d3ff55c45adf74abd93fa744fa0fe4a4faae5cbcd1828ce736e233ef46a5ec45622be2bc1e877fb70837d612d2f428ca2d46a208675177d32adca5d39a3ee8ef7a04492404ac7dba821f3613f2182fd3eb908e0b1a24e67cac2af434dde56f6d3723a48a800d14a9eebe548237bdb8d942f25eff6f465311b76f9a5b616152465bfc98f6992b9dddd44adc3cd39b0d63f33620b058ef5a3830ea937f24baae8de4784929d19a462e2409708ca54efa14457df798b34fe1c44836cc03a9fcca440b460b8873b4c89a2e5a93b6e4f2d547d4516c14c4b5deadab6fe378c891a655550cb8d07dfcbfddccb2c4394ae94a11b7cb8a549a31f5deec787fda80022fe5bd4a35a38f8ed3c0c90ec152e99350d1ca358535fb7290c85bd6d370126ec8be2eb82933fdf046b7458ca5fcbde3420aaff8c61f99189abc2b172e30a53c529815581cb7aac5ac223a8f10f2f9162d8336afc9cd9eba09c284c0fa6b26b2449b5d516ca37c1bbc01316c169ab3c0da23ee14ae89e4c8c6a6e3a2baaa91990341bd5b5ddd741aa4a38e6713650bf2336ca4f5004e916022e228b0627c8f40e6b031f411ef0f5619388b6d4800722c60ca88fe94f2a8d2092797635423e41a31d0bbbdff0dd5599980663db709cffa1d102c0f405b53f2fee4db69db258f239dd2f73595afd05087ec5d84e7eed274154c4346c37e150ffcef6763401096c78677cb8052e4a62e9827eae5b5e5abd9c1fd1628179b87dbd2672c144b6d2fb8a243a55761be41f22b98603d95c53a1b3a5649b6da054616946a1e5888f6f22e5563fd78cdb938dfaafa2ffcdff1ea486b93815bbe79c220b0762ce00e56b9b8edae686bff2a63be9b12d8ecd712bbf35a4543b8cebcbe4f8df90d61b1fdadcf59e0cde1ec217f271b20db1c5299fd8362ccc7753fc8ac1efe16babc5f49cfd18f6ff7c0eeb2779d6ab07cdf3828488ff0678d7ab2739941131ba1b5658d8ff2e4ae6cbdff34d1c8f97a0a44721eb28c72cdf9ec22589f91f7a4098cc212325b504990820047257d52723e4aa3d7235df98c9998f13e8c9475c218f4ae59c9901bc0e81dbb1dce356a87885357ae9551a5be46dda26f3b70a1ad083d9d0fc1f701d5748a2657803a1610ef57536415a86496914bf5a6e4755f66960c07b72fb9a9fff576154339732774c18f4f6370aa63525cd25771ed6daf3bc8533f18ebbdba77f6707b31a32a58ec838cfd202f840d7ad6c89dfd3fe13cdccbef5dbaf1c930578db634986f6d928ccfa635158059d8509a5c2f16055ae665d8650ee22b2ece6ee79b876b9f66e72fc3f18e9e16b4e20c1e307b45277bf2e0cc30b34e28f4d02cc79026b05cf06aefe0e6bbb3b395feca65a7b287c59de6c484d36fceed7bdd4f84029fa12629cde22c30fe48755992ddd2f3b8e57bfa61b585e4d7f79b28c17844bf8e859f65c2772612a4aacbfc788ca9c2002ed699361c44c7f96c9ab2639d54013555ac8222749e8bf169636ac8d096c98bc322f45aa10a2dd75c559b57f394b9b4027a2c038acbe5541dc9e75a358e5b9cdf30d8f24f82498fa64ceeff4eb57b80afd8d3a0223a1c628c7ff275ca0057f8f71591db9a3d9902d5ac5d63e18b36b7ab850295098f380d084a1307c952d4b31499fdb2382987e7557d62e9770b76b0f04b781977c2f8dad8502063cd0391ecbd693b4dc35a5185cddd181a93568bc611686c29ccb8a9da7530ba50a2606362f55589097e2a0c6d1e9cc5d4577e2af2b1b1e281cfa853796d33eba053812271668bdca1405ce2a33d172e7439450b00c7c57adb587ce7560f24b7b16355d86c0e34e4783287285e9a12ed103746a4ce9a9883b00e3d0e568c6038650bb565a16f9a21bf60e865c9f730cbd987413271f7a025af2af89b8fbfb4e197f4c9639b99b115ecf1483d10151105259f7d7f2ce2d1702d402e8b3c2dcf1ef69331f90994c873e75809edefc2cf8d00188f96c158585df4d4d755f4c8e1b9d049e21add430428c06f641a4c0cb2ddd6b2276203e185a7a3d1f3b700638a094d52f10e70a8bbd48204806636cac36e6edfd891c879e2cd34ab66232510da4228aa8f01fe8358bc074820c81592d9fd7b68055aa8a0095c01d279a4c1ec243bc37bd342767850ce5debd4cff6bcb8671a2e3952f75fbf3f922803062942f59395c566e14d33103760dc8714b11cb2894843f8266548ef7865ea0fa2148dcb71c3b073c63cb49fc6b174ea06dbc7c478b3e8ddefefa0497e0073f313547f43be989e0b829772270cb8fe27b1689b0c96bf075dd45edaecba0b9488f212d5f7aa9a9e8fbcf0219d5816943971f11b523d21337bd5186376bba00190f44fb3692a5df82a4948c5113088ef54618e4b5f757c3d8954c8884d02017fe9eba262f0e1191525129a4e0e48a7abad3964e326426fe9dd2a545e6aae9a71602ae091020a8548bad963f7680eb7e7b4464b9caa15e3499c774592d0f3e4d62e9dec31e6f5be000002e33c8c14c16c99e4a322975345ab5ee56e3452ad0f1b063dd8d6da08a52bf913aa498ebc2598cb0ac1a4d5c1746c6f6b4a98490bf7b7b0c85bfc7bc89964bcae15f0c493ec702e0effd3f7fe62abab865d08e5c3c8629ddd9e3c4c9b819bbf53c49eca868dfcb36bf116be2145a2a4ee551edd6f62eef0b4b8a954a7f0a662c73bed1179aa3d9000f07cbc8b116ebd8c44b31f1f6dee774b703e0ed244341a52090e49a70d230c74751cbef75519d1868d194b3c98dd827f13f6381ca32f369161174da396edab4afbfde9a911698ab89f7bb89162e82314a1df56735e389ec37858f8b0638fea60cb5caab126de8d7098175678fc18b86f107949db54aac720200e9a297514c8b07a31f861715dd02bb0b0b5f53f416014b3aa33d9eee6a0f9aad77349103f84c5b187c35f383252753e739974245c3d5d0f9f98c656b745bcfa91eb3fb33d8ace232bd7ee4eadc1c3bc8b55df98cf738c662bdd8197ae728350759ed759542b0d8adcc5454f89bc695e96c053077566d5ce7e87c5fbca1b1d30a390306b5925cf9376747920372e857d1135d8a0bce28d67266188c3ccba106878032c0634f096a61d21ae78b76c4327ff231612760d50909f82351d0956366f6340f5b2f251e2b69ed11594e173d10421803e5d4d96512397180c790e00d75c95a69c3d94cea14ca006c367e44c7b35d818ddebe448375a7d7b7a2840a0e10add7320ba77012d5af8ad428e0306a04eb8ee12d4832f062eefbf2355c53e2a0bfa7263961f23fd34a6e4e69a5541789303469656896219b142bfb50684f9cd5d23245147e3748d22d6565af9803af07f3f436ce918cf44f865b279e2fc0b9a878ac8480447b811e6665a686db6d452d23331c6b727a355dd85203a1111c4932ea0009cc02e5f4bf28741acd230faabf8193bba5d44292f803fd0931dac87886455bfa1f46a562a74aefc968b5bffc93af8fb71d0a6f94ac7c2fb71f7a1ea1b9783eda68adf3af19e70fc8dace29d83004f320db2e643259df8a97d00e6f673050430d5282f3d76aee023e507b7a9b9bc46f8be9eb0d46c547180bd028ee82fdfdceb7fa6677bdf82841dfbaf8434e56a2826b47ae5946358e8d08bf9d51a12e7292916bf576f460231c82106c60110f7a91af7b9eb93c674f18c3fc5e8af00a2eb5c1185faf9a53c13204bd26ef8e2d0962e29435e9b00c37f7af2e2d2ae1b14ac323fbcad0a7d108e14cf9d025179ea0d70a2453a092037e67a1c1d5e627c5ec5897caf61626fbccd4584e189f811536f990b7a22af1797f77e52f701a426d7c9616c2dadbab3976e4cf8caf1f4cc99887ca065c633610386fef0be6e499d718659a38a3277c9d6dff787acdccd80ef06e247c0859bb20a3b6a6a0817edb83980a3e08b2e1db4ac7f4894d8c11aa1376d655db7b420b265d84435bba974dc16ea037cb5d8896d40c4eb7b632311b14b024231f4cbf42f965ff5b2b2e9033fcf596e48e06cfd2e939e9ebd6abac892808ea5a4fd782902b7b95e44e9b797efac9376886e00dfbe9c191aa372ac80dc0cc5f5227cb08f6aba918da362584fe6c90bce76232ad4f9125057f68a54eb045798250c7050560988d2d9043aeffa0e942aecf23912aef4e495b6b1eaaf3581f92ef3dc4f5d888b73d0bc88273bf5c1f357f6cf5634d6c8c0f265134313d4bb2b80ad255e893af0a735633a2331f582547c477e22a20bd1849a7d63d9c43ed244ab25cc47fed12898195bb2d2316d660b425680f2a8ee2b2f56547433e5fffacee0023bc5c86953f0bd09e54314a95a5dfe803c51f85bae1ffc6c0e045af4f3840b3808aa360b2ae6fb6466667a54b5f635471e2bf7dd398dcfdf0a7b6d17edb9879d2068073b5f49ddac168ebd54c39436d6205f9c0bbafecbbe74da4741bcbe110f023ce0d111beff14659a31e8a0021a39c161363bcb9db0462c3004af6b8b6093a42db50621b55b832a60609f677712ddf74e173360b863d7d3941b241216ea7907b6a49545b359e3d78fd1f8fe2379881b93b84c5c9cc3730f4027db21f028037ccb7133f8a336676483443d0f993e17cf18cedee94a732b51adae8489ec420cf358b128743fbb3068c076e3aaac961bae3cdadc0b2da2e2d810ad8078ea9f525ae8a48339a8b775eeaf57df6592eab270b02f9b66fd18daf34b798a29f841cf1c8e4010870681822b0630467d09f0adf290fe88a314a1b86f9bbd115d05edd42026aefa91d65fbf55c1f051d47a7ef8bedcdf2bce667a1024807aca835aa941ecc2cf87f5772da636a715d3984fdef64d7c76545f65f85a927b36e0ff304eb9047eb52326ddb8cb0d07de629eea336d4c9dad8dde261868be394d4f67e38cab35f65e605bd4f50a8fe82e4aa028da23d17032d10198661d79ef29654e6d2b7fc2f3c1a93ceb925735421b647b188c1f50084831b79c0bd7eed1c3cbc16ccb0873dc3582f2d79b98bb945ead686f1c11fde1c56b8974efb6624694ba8a58ac7ebc78edc33a259cbeba26340ed4aca4b89f1256d646492823350a5a043b3a0cf8e7c76df9f1131560fb0975c4e6368f22e889659148d6c4f53906671fb98d269e359d5ed747b9446ed0efe446b1cd030fe7133c942836c75994a29ea78c303f92a12f9e1d466d46ea68345dd78aacb57c05b6c8b1b2c27118909ed4639517f5c544399917caff7282271d7fd8a91aa7472b22a8abf45a82b7839ff8e12e82588073c9a657e794e1970fb7b56937b668330547046d6c5c3fc688ebc0142299f6215375d8732c44e6c87d0e2e2f84600f2d6734b8e895e788d420a8cef55da5a3362d936426b777b98b98637cb9752dc7485a451cca59cf2b385c797ce1d62b312553fee3f30b6368cf0b0500bbe2ba1f6a883d6d13e96adb455141b7805ac48fb0463d27e848f719d9a0929dcb772f9637b83b16d350dcd66445f48005073237b58f82d7f8eb036b568d3746e9bebbcecd6269d005569c84eda11bfdf388b82a077d0f92ba5ff293ccebf13e804d49bea122ff9e5abb5f574183f10c5b9d3a32d10eb9a7fa92be6f6c891705fc4cbc3eb28b79270c8d7ff740c0b33196b0eda2c25a158e02ff59b7b389f4dd1fbf6cfb8774f50447a9766960f633a38d40545eaaad6daa4854a15769d8b27dd7910c531b1b57b148fd0dc723fb873ca85c5ca54423778921e33dec76251084e1b3517fd362e531ff7fa5052a62414cc2dcef4a35eb63c7e44b3f30ca01ab8f3a0218ed4a008086ae2f25e3583ac51ae5db10f636b015b0675264807e82f1d30dbb101e2fbd1650550adc46152ad4718287a90786d200922c7144dc8c91117b1d0fee728bdbc826a9ef3b0e6389e61bc625dc0d048936f5a08a469d860162a073bf43c9b2bd291959269f6b6ac44ccb343e9c104e2dc980a71bc21e5f7353a48a7b16ae0410f01c2af383295575cf7cc1ee2526076ec1d5296d0ca4568d721af3df16e7e712d72e799d349291dbfe0c89db907dd8a5fd07c190e02f5e80af89d1e469a9163b0172877a32661bf5f0fffaeb7fd32a4f27087d65f37d7c0c5a6d2d0edd9241578fde0be6f0677726804a7b52357eedc94186764d279912314d42a7701e235bd27a762989cedf76550bca0453989bb2ec08fb8c95cdd4d4cadee43def463e85e465a5bb785e6d973437d95465e04daa076f0b70771a50635a6d63c0d2dd98368924b8c924a6011f50affd17b4a1850fe18d27214b78da692badbbe0918f89da5c2eb503f5b3224a40eee8e45a35d7c4913e99d62cca19e2cf82fbd11e6fdfe9d635ff538d3bd675091f38b41027b9888d0ece7561a93b8d643f8cffc578077269d15778271a68ea621d32fcc1c84d3b7630398eaa4d2905b53be8c9212ef8936a3ea9e3887170bff179d093eb2ea11f36952256df128a0884077fad10829a816bce348b4ea8c922e039140e0412a713be1af708673f27e1e559531cb157a59336110896a0e3e99747922ab83fdc33803baa934733dde4878619b375e2583cd9fda3bc49a54ba8864107adf08929cb0756fbdc231f6402395759c108ecbfd0c2dd50cd5ce634fb6e70b962926933cf2bad1e5b9577d9194cab7ef0bb1777815c89670f34566ad229a2cc475cb8b8adf89e72a827316e121d1a9b9933d7e0d5369aa8bda0d4313f7847d190c36933b3deba849256305971c583861b015c50e2641c45340da91fcfa054443939e9432c35cac6b0ca575ae6619d10c71d31a2e023045ba287ea62c219eaf73ca384c04b0d50fc8a93f36847bc0a18360f4799a872524bd8970c5cba8b2cf3e6148dcac666ba0134937a950df0ca6096875924b97aaf2bcb3767aa6726bcd32d02f928a4845cfb72ddfdb77c0d5d75c816d04e54216c3c8400c61db358cc9b58c2ab35cbc509bd362670732aab185b02ee22a7f4d293219a3acd04a7b2c243edc15705ec4b5a076cf68f0bc007d7349411f6548bf36cbd65e5e1b7b6cfb83a05adc2292a09a18989384e71d0135659d3673d2eb1cd6efbca38dc1fd2cddd0209052f47e5ac74dc10b6591c9f5217fef96569747c5c55a6f87bf9e5f6c933244f05b998327ab4f2d10c8f6c85b4c807c33c474954d4adbc23d65f5ff86cd1d3002c445154d75db03d9e76a6440bfbc9990a2cd19cb1f8684f1eaa471f3464c5a2bbe1bab18d1b4b573079e088b3b06749e15f8e3e3fe75a75500e615d1513c2012efea5d1e8e2d543fe697fe8543e943e38fa906b4da099d93bb95666846604b97142dc481418082f544f9e8db47264376551d2963b2aebf359dcb5bef91ba4e34b35af1676fcacea0204c05171bcabd66ba1a7743c832316dee99989e9b02c66a56898759c4e6b6a24cee66e0cc1e2a8ee09eae99b91347cd9592f7a0a553cdec4bada9a5844dd07d4d6c1fe42106ca53196e7e78619d463f35f69feb4309e5a59660cc34800ae42c0434dd5da0f3789c135fe1a6e3a25108852d7589829263f4057c92a0b13d7290812e90448265e61eca7baa7630fa4f95c6f62a85624eedb526557183e0c916c906788469d46f57652fecc5d54275f59365f8c8cd30ba0e1a70816dfde69a939242f99facd9347ed4f60c76efb1844dc2452a7ed0bf5a8556a4176b18df5824cbd20a8f2af5fbc4d39be752873f0c9ea8978389e308ef8b42ca185ca002cc011bd19da3f83fe7b57f3ca5999aaee925318b757e9fed2b5688548546213d1e44d03c3b7e7a1317087161b3ba4d856a30b1155a6ebcc753e77ca2fda0cd6f174f9672284e95ce374638f8a0f975d72201b83c60f572f2ac7bf54ef846deb6cbdbd6c13a2044e576f432a5561a35d67afbe2905172ca4ef7de0ca7a8b4ed5500c5fdddf33d1c1edb875de29485d449b3ac1fa348986d290c70cf9bf6118c7e0ae480403d4a2e04f1c1a402627b9cd8406118362707e2ffe0abd8082846dafe765572c744f6699f9d3650430f81edfa9921d7ab11958e68e6716fe230a987189b25d2b2836b91650506ee2eecbea889a2a28576469cc22d181d09acf3123651a620499de366f898de7bf35a105e35e623763f43cca6e4259a0681fb838bd2f7c71b33be201dd24b7be6b534127f42c65d72025f550ccd4566844604a581d29018219cfb10f05116d71293aac0735335c13e257f2c6414dedc29525779fa9815480a67d923b6fe1f4cef9e240d10909569db7fa58a0c42d6e84511c28f7c5771194d5f31aab0c9c77dc453548d87c288c7aa2ff6a61d62e928d060c2433e97271f99689d2b828cbb1e28641ad8b64c25fc37c0781747ce42c76ea71fecc0717e0fdf7875e9c09c59a03632be10d8a593b90d2f7a34cd367b8162fc4e47a5ef078bc39cb6d1527e471f4a23f8c073575d192d012f2ce3d15dacf24c0d744d327c425e19bf2ccfe8da201c62a59edbb156b20c8d265d437718d63b38ace7bbd3fee95320f58d627168243f39890c0e30b4951e22c1c473f5ef2227a61e69ca2d6018b950f65495f28d0f91977a0d11f3b485f94e9407a83fe3a3ccd6de3e899f0cb00b3078b5538b2bd76ebd6d2162b3c5f331dd38dd460ba01a456b3bee81fb00699802d17178fd034e8792e3dc0bea74d68da23729f224aea3f10bc36e12e84209445184b13f7d1a086edf0932a8e6b22a59f0f56bfd9c2d262b6be50b53c3719a1440ee4ecc41ee49ced05caa2de06adc96fc25bb12b274f649afcdf6f95374f350ef177cf6a0d829641bec0bf93705d1384e73ba84eee135611d386c0454c8518e8a944869f1914885a54a132df974543ba3c9645a2d9025ee000907b58f336204d0fbf4ea55e7a3f6a89f90bedefb7d987e9359731f8e1ecb0ffa0d47f84ca8d008cdde36b0be0a0951fe7c4612031b8789b36ec1301693459f8fe275447b767200b67f2512bed25af428ca088319eed887853904c8f541655a4c2e444213325a600c865bc501fabef9f433c5cab06c42025d12f1082af5308e3c160bf7f12b82d3a19f0a2bbf0dbb7729da10066d1d5cb3ce2979373c1bb6c7cfa6a384a8c7d69f6234d0d5b3c55d8a5445868eeb01141a0c3cc29c733125ee1cfa04f03f48a0a69c821e5f5cc62494fbb1de5a6a9410915c7ecbbb14c79b03f5f69f3ad4edb749752b9397906b938a70863572461e000c92550f8b384701bd734a03c4f798fc1d7f2eac83c26d1e8d084adef0dbbdc5400a3c730b2d7a53aa8f12d6862a9a143e1766ccd8a1e64bc23280d30c6fa51f556537893298a4413c174c95cc6b2def0308d72f61c8e654d01baa7a8727ab9d305f876fc1a2e4167f3373aea34293d5320e93506659ab13376a705c0b0f91043a57a5fc69c6c4c0949e81f7ef100c92bf8d1d4ab7c53e2cef11172944a80837f0a80f8dff2d48f4e17cd3b26b8dddb21c5a09c6936c7ef645d04e6fa01327f4d78382f6d79d34b7f51ed061f90b70378cedff114658d55604bfd1aa278aa8a6d8fbeea3d166b75d630b8a8e57a22999a5bd97467db4a156185dcabeb4c53eb22617ebc0c3f466abbc6ba3f6a567e627707d41dc7196df33f65f56c6d3a61c8978babac208c0d04eb9ee7af5d67507f1b9d42c0bf1fdc4c7fb5d8354aca3bf218d8f2d67f83dfd639f65e8744db57f8762dd23c4734f1d6faab116db6574c43102c57ce1abab614bc72c1970aa8e432fffaad63d4fd1e307366af6219cc238e95720d1c44bdd133bf6d82b707a449335858645390ad8662e14ef6f7013e4b9f6ead604bceb643cf7090fec7cf88dae6adbf97f6f9b72155a83320b3abab63b7c7e5d42b478f7399c7e7e74f68e714e268bdbaab7882361a09f51d9f8097914458bcc8a095682bebb289982d974273a6eb498d19798891254e5b522d01be798698264df051ae7ebc60e021fa5a04092343cceaeab8faa3e29eaa114f5d4906366f148adc7c6f577c5ce4846683f31d9acd11f1ff579fad7d4b6f3c717ffd849d671def5d983ac3aee6d05346d12cd5df32b062913c44c2561ea9ead30be72d2564c200900b844e0ff9fd0c286373fa08337cf5346471a028358c842d42b575be04308d9c5360fe20ce1e5252576b15bf32e73bb5627573e522e5b311e023c76ce7644487b1ec2d36e4b86c021f1668ed5709b0808f365d2b093a6b56a66fc605d6df9a0e59b157c7a859e5d0fdadbe88e1cd3c19dfc2d41a1444f66692b20dbf6f7fb18b2d0f2f676e548c5374af18460fd9d16fabe15a2c9d8d4faa40465705a982a7376a2636295019fa8f7a0b76345328750c86789189262ba56f11d45e82fec2d6bca9d0ba245f470b83bd676a2a7fdc98502aa6487658b8e9e0c859d42932c9c4c8f58a9528d071ff07492da1caa58b4599a67c5c728a946da6302942e576f786bd9ae2e5157c405931e65f87954d5527164dd80773206c5636b4c98d80d60a48b1ca2c778e7fd1bb357f81bc3d8199e7294ddb0805a0f2b432306b7f2b7d6ca31d03256056e7370ec291c9208e15882b859e1d896834966e28e85627e94e94eb147e22277e29a1c4b12f8d71d81861b90db5a1fcc5e1618a9ab4d377ccff13f74730b92205505c3391163421eb36f7898b14385867bc295cfb1f5201f774086a86290ddede4d49aba80e17f7b5c682997a28006ec0a932c6e9cc6504fe33e261377152f396f3ff943bd6ea8d514066a447cf98d1fbe87f89b108a775ec5405d01ec22ff93154eac1be3013493f419fcd5c0702d02d1ea8ccc109e4948b6265b304d41e00faddbca9e896743e98cecee32ef609f2b3f1723feed6b253708182104b7cad00fe4116bbbec46ce12930cb9b37416db3396abe4bbde4b4338a6162a18a96d177d329582bbd317793cd7e22ed5bc10c083c39db281573ba4eb6f16f96d23f6e09efd088c9b43d3c91d82939ad0af31e69d9792e2cf3cc65390c47151856dccf78e4dff1ca61548aa7b984975af1dfa48855b88352fa83882b04e6f67fc73403ba76b13f74a8777f9debad7c7f39119503c14ecabb7a269fc2497895be6640e76d77eb0fdb3d81996283e5e1d4265301b4431e9dccddde41ff48a0f65a79052c51969497d5b16c6419cd1897ad51cf55c4bc245630738d69a2d8f9e45f2fc8731963b3e848f0071a0d7c139a7bbe8fdbabd4faa63610aba909dcfded9e376b2bc227fc9f8d9aac01e2cb9f7ce376acb10b361a04fcd441b1f88d16cce876e35023580ef1a9543580b8fdb6cd47c4a220fe149cb7a09202aec0b792c67647ddbaabb313aad34f3a1e03a8ad82bf04798526b6a9f16d066c9bc5cfccbfd1f0ed535ba7d7362d24faecc4c77a9e0d9b67c2a0db6c625ff24c39b97ef907b7eb16f195c10a1366e2a25f15b8cc3125dc6a680dd3989eb129267c68eb0b549475de81e851732c0af82290914daa0e9edf5ec98da289f8986804d8b7ea23aca2259519e56255cb4049839e2cf3dcdf3c036d4d55584c03b5711c0a4edf43532f2fcb15639838ed23a5e005496f7957ef6e8eb32d94250668937733ecba3bd914601141b72b35e190b762d0e74b0c43135965a1d98f65352139c94ac44d268aa69c0689a700d24bfbaed2df3825aaacfd06cc6fb833b46ae468bfea561c26ec0fb493e7e5813fdb1bd4a408aecc99d3f33da6d05ae31ae42f869cd5f4a5edbad0b8ed3a9340d5c1469f537502e9170fbf55c613121d64308392105973403ffdd4a07ac20daa307aca173f279f6bba3f150b514d43da2468a7fef5d64ea3579ea2c49a5346d6c46aa4057eaaa7b0c854d4b14752bd77c2487dbb3b01c6c3858b738abfbea56151e91ae45a658e4e0252bc1cfb7e56d6ae9554f571785f846f5ec66369fb438bf01c1ebfcdf25f739ec388dc489c02f8dabde721e3c610e01d2621f345fa22e4dfc66d9a96050dbdf3c72fd00b315707ea44f215993def0b63729860c71af458f8faac95b6c3049c91c62061a5179b9e27825ad05007705b6fca7a0647d38ab673d0f89ebac4acf60bd743b6d77307297e29cd225b1183a08f0a6910551b6c068c84a559cbbe83967b5fa9f5e07a526e577fd387653ad3b86f9778667f41dc38c2eea0eb505a7521814421265dd2af9da55babd7d819c26af78e58046827fa485ce83587e46b673df145e0e732ab72096d9ab611bdb94fd553843f921c5923f4ef9e4db08c2f1429e51f73211c3aeed9f9764093c1ee788f447c27f4757ba80eb437cb1c740697367a962b208def00002a213911a688f6a21996eb3bf19abd8096714cf19a16298a8e5a1097de5c40fa5fc2dc48a45041af75c4682f22f05b28e43764eac2308d7f9a6b96081528ab257a0c5db59560e0f76448d8b2d70a40cc7cb62e24163db31ba7e2db7bbffe4c0737c1cec558ea2e7c6b31d2ecd34e6afa4b0fcb4a0633c7f55eedddabaffcf79ce7dd04e923c4c0bf83cd1dec9fa2fcecbd25c301e07e3e18faafd2137ed6f4741ecfdc6950c684c1b9c633b4615710855c10daca717e4deb4bebd9513e1d76c6dde83ec61940137338987d416b1dbaa0de2ea5bd3532841a9821562712224d8efcde96d2dbcec6450c62317b6a1038439eab77a5aff87d62a16d5b7b733b3d99b795700510908a3fcd3403ac23fd9d7ec97f1d27368349fcd500ca1fb9e539992cdf8cb8cb715dcafb0895a110d89774a33fb1d55acdb0df08da543443ec818d3e9bebe218c3a1828fd4f118a522aacb994b4e247cba867b60fcf2d09e93d0c913ffb7ad9661746ef1842564eec7021e7f4d5be4c073c0af8005cb5846c371db31ec24b47ee4513eb88431e9357523790bd1b2528e107c5a2c4303b3fe4017f584c5700008c43497372f3aa4dac946bcc2058650aaeca70a79b7154564898f70ff44e07c4abbdf3acd1ec5144faf6a6a3de497570f48012ebb5f49291a57866b46d0569961d7814505b68d4f995a2b2cefc794d78e113f57c15138f53473dd91a49894c429ed6b7f1bbc9ba2d994c35f04a1f87b0648e3e67d48b871bf1712764330d2731ebd4d1f6e312c7f9027c935eae645457c0a632bb4a83c6b74febba59c62142565f6cffbd68175ff73784e967dd758b830b678484b8dde2a5c8be8b395bbd28399a6038a7e34683b295ec6fb55780194a628f64e03579f7e306b495aacf08466795129b02e4cbc8fd4417038e0f87fce4cb478f9520a41c941eb6762f7e7479ca9d63c6e6b6761e89364b437e4f2830e431563f62c2fe101a3c8f50634a6e252c9a1ffd4858f14f5db6275cfd5fe4118a4e342363427c98149c0dd6389c0b732cde12456b077bfe6d95d96834bc343e1159531397a5a75de58280d8b8883bc46d38762bda2168e659e85678a97e2ef5aa4eb251f4b79b279301664dbf7a1a1c5d1985dec147447c394f86c786bf3af94a25a22236c4f8186738efe640fef1cf31fa06b8cf5ac23d3df2080aa582ce7487f9c69d5b08c4add642fa6bf6e1450741556d97fad26091d17a885a6112c2351f59a1b6b3568351cd3bd6240cfe5783d38eea5851d73536d8deecd75096f66bd264dd0110739babdbce98305da710e0cf2aec771847a69b5da366a8171e743a29f015e74611556d762cd588586784d69912f1c6a125ed972a5495f1aec178dc2067e425e20b36de9d26b9d1a0f87410d4b7ea5222c1dbb41194cdde0926516ab517db83f31c970c59e7fb835e275bfb692663c804e2b8d6305535fb4923b7cee46d4796f57702de22973cea27a43a1eb4bcc65ae739d07199e21f4e1cb38654ebf6082d93364fc43f1d2feb4f1d3c0da9bcba7669b7f6c406d9fb5aac98f5b69e4690fd403b2e8110127e6682e52e8d9efefdff64d8cb266ceeb719a5c1fbfb8550a584732d2fb1462423b62159ba738ae9fa6b92cd884c1cea08b214b43b77808037bda7dcfffc3543dd86fc37a33abf6c098aa4b91c05db3d5647c09ac3186ded75246b884f8046963ba2bdd07d83edf8e9a78fe15a37b5718d14892f4408e16c6fb61d894e344420310f680d23f7a00c5eebff166e83195a69b90f5f1f74f86cec553edc1b5b017a6ef73893a0d37a698328e2d59fb89f3bcce475e8527d6d5318bbcbd7bdde1a41e2af9cf6737c6b342a34e0c9df1738867eb51def67a79310f8f7ab4d643aadf242c8ece493c2c43de9262850e6dda0059ede4ec41ee9a68711bd21cc9a8ef7904ab61bcac0a192cdbebf7a51b41fbcb45729df9c5065117b6c94f194e5a35e3c15b8d3c27d6798c6c2b0d2e10feeb8b480fed7c9029eec51e2116e43314f7b2e0ae6713624ef1959dce7ab5eb037dc5f06819d16e88cd0ca9114e849ccff0e9117b1b0addc7172c58c0abba456fe91eb4724abbeb664d7c8ec86ee33511f248d92a78a3bc3dd49cfcdbd7a92b61c927e70f8abb947785e1e97916c4845c9fe72a4b604387ae25c96df8d285d73eb9bd4ab51a55b4981e237a0355bfc67784be1c75de4450097d76b01e86dc28bf3b7676273e10a8fd832c49d598c62aacf4239a0c470602129fd8b44872d051dad68ba9c7481d9fced7819503f1d7bab7a5a16268f41e060c8360f785ec1df0dbb95d6a7ce61a8fecfda4b35b1f3401f8bece473dd0fe177afb1a437ea33a3ae748f168bf96c30ea969185d7ab5221428538364b1c3d73ffdc04481750b0de466664e78f6a721b01d4ee9800a6e769a81cb0de509b3183cbcb01bc151b6b46f52ca91c446a9ea8fcc480fa5b7f38b59fd631557acabb020a5cac1cad1edf33e9432bbab059824a5e289146ec9ad86fd5ac817ecf9af5b403ee50ee0cf14489c6d7d753afd0b984feca5b305b1009826c64c1e242d3975129edc906a9bc5f7fd14931dfd800b470b66d8572fe33c05f61affcf471c54717b56ca99702cbd69cd4ee32e36f392af7a2591de9f9dce3658003945867912901368b232ea55fa7671098e448fc7056e29ed0578580c3b5c290c10a1ab7016ad1e6348794f412b770317056908226dac6797c562c26ba3c7429fbb1aacd0efe9ce31f780984f804093bcc98e1022ad0e45d741ed4e22c5bf612b08e5baa434beca1115eaef4b2d035ebbfbb3894d4eb52e13451d7864f0a75f8ed9e0a67ce48c7ecb5992bf1d9334c282b19398331a211acf31ab2bdbf6f13d92e7305c248f660d230c635f3f5454b3b16b876bb57a530608187a45f2268b00532eac7df2378d40decb087c92984ddc16d80a0ca6baac80523f7bc5a7be193496c483bb7e36f727d82d7d2db80443e9a0d6165e897501b7d35eccf671796b4cb674fec0160d4ed9d2c6fd26555987672eb335eb533cd1d1ee1ed9db2e25ef6309f4a1f84b9b5b5723f45760a9197588d91b756caf24b8b96e2a3b129372755b5977ab0f9823162f462a732019063b90b2771b6c30f41022d7ec1e90c413516c89fcbb8f9fa3f60ede1562a71e4c3fccfbd8b6685cb178083907d9e6ea8f593cd495219efe494845fc61ead6ea4a26e0c050ed9913a46bc8265635fa7fba3144c7cbd450b1addd29cb03d4ef10c32238872e47be61d684b65d4f17a17a3a206091b89c99b6f75cad5e15b8a0d1a90f5b86884bdc621da59b6bb59c221982fff333f3ed9e8e27e403e3e7f00a81a48aaa86954d711d611bd25f29bbeb8794366ee5d8d81baba9285cc96eebf9f8353dda44e184ac07539bf2f664a8482d3767a190e26b6ede6d3582d06c0e3f7122de14811267956e1bb001bf2ce064dffc2e5e3da9f662344a59d1cce933a8d552d2c17e5f7d9321d79ab4196738e7aedf25dd595f81b7fbe087198e35c8dfcc2405162eb7667967cacd19fb253991c5e5453343e79bc70305f01f2092aaa5eab608764277242406f8c24033a712d5003cc80afe451109d78bfd3f693946bf900579cfeaa4628d1bd9d977cfba06365bc74c6f40e038390da04327344ccccd447ffafaf2b1ca4b3e0b27c599d15aaac52feffbaf0476603bb5b554973759d95d3aaf9472490584c7a705f7d6e5d12852ac04daf5132342ce50196d51465b159b2bc31ebcd66d499fe2ee39fca1da762b4882c37c6b13bb6329ce071a8f2379380d4ac92081f63eb3f279925c909918f062c2121f3fcf93b334c70dd42cd495a8a8da439ccaa27ceccdbcf5c34d35fb1db9b66ec8aa0b27efcb4d6910a0e1d477b2d7a14a188e971f696b221c3a7faaa93ab208332cc8f01ea344baa38878d6a39edf8c89e5ed339089ef5594d18da15fcf1d260f7de9488bf3908da1d757fc0e878b62579292e4136f32b288cfdb842f2f7e038570e72c823a60785acfa52c17c29c4a048d458ac6cab8de67620d3d60fafe1258e0591bfa4f421ed56bf5cd5db6236d702dab6e462b2fb994e6d1ae720afb0947c70d6375dfa66bf9b68f7705c88c160d5366e794745ce501657be113f89635da99fb27eed31737b5398eb4373086799a37db8604d864d71556f361f9a5b728c71b78c5f98a3303d95617b93a6e86a2f194631f5e5671e2f240dc288833a3ef343be429b7e2699cf747f6f817f75f74b1f056d08f3b67993ff439da20e778a18819fe69e9fbc42d4e358840b393b173a0fa2177625d48727169b356f5477026bedcfc3b957f4674091f699be14f869afab40f334ed7343c23e1ab97f64effec5b800688151b26bf428bbd016cdc3076598b1e67c0f2bcfec5c4ebc48ef527b7151264ee8331ee123227a12f6e4f0adde8ba7bb2f1ed93876e4e2ea6e2145870a03934fdfab67946adbb492994f7a4d5cd3fa15e92d6c14f65ccb77746850dad39abdd756522302bdc5c52f0ff149e95ad215d7fff3954041319da9462d694580256078f86a06d183cdcc1b012ab5af105d2e130c8b9b12cccd8d76d072126cacbf1def1089e2aed2fca7550a13f4bd642904b00e1d301f7d930aced6108a85a91f01aa3906aa824fb9377e48a7f0582d3733e397abace873643d91ecce89cd098baa93af95e226d7ab2d0f82c36100327a7bb3a3f7b22789eb69ceea881b38201d85813a99e51a4228c0b008107b22c7d38bb7da2d0584aeb23e5d95c67b1cfa6f5b35bf5a578bdec0b7d97edc026ffce161459591eb0defc28ec283a1893b2a8d5a84cb41c21a5bf089bab14c7764a0eb76a360f3328118e5e0f617e54855b7da4f533fd76f4552c826525317d5046b5d42ce55243b98e1497c86410eb53c6ffca8468c56fefb469d09bc69923d63693f9fd52e4c4eec588e66e04a88fc7b3749b8ca3cdce3d53d4224a65339bf023f09418c3ffa7d2a6b509260a40107e4b1d5b5b641d4109bcc221499d0ba757914b996b0694cfccbac616c3aa77bf77e40b6ff0a3352e5c6db7ffe405913b1ffe7d1d49b4c0b6e345a159f35707060ccf32ab451e6177b38a9b411ae29d3409407e76874f83f380bfb10bdcee2032afd1a52d638c90a81f297591879d8ae116941b97fb920280bba66d372821d91415b3093bd81c8d41e7dbcb7ab360813074f44b02e05b7f45920108bd1577a9702ce92987d1b06e448329aafd185db4f368313683e4521bf51319c6273436daf134d15ccf60ed6ef3dbdb0ff3b118a13e90a5d99ccf675e010056cdfa834352edec11d38b94ecc36a9160ea1ffdd19abffa4c804a934f1caee95b714e7533c7a5a242414d12fa369e8c211ead1f2a29ac89da55c8776243ae27721d64c5f17f4bf7f60eb8547ae267c88abc564455850627ac253b0be02473f382cfae5bf62a9c72fb060eed0aac2fe8873f5d3bae9eaf5da36ceda7c35dff15a015cca319655fd92c6d3ff8ae880fe1e448eb0f18bbe1611d3cc6664f427ab9344b292434307c6ff2303f88bbe807cac7321931fc330926445388010f5a8970db8a01db83c37e3d380483542778e47ff420aebd5577d274787de5198bf4f545e329c6f90359a7e204c7a52871fa6119a2ae406b9811724eddd8229184f57b74f38f9310934c935c23372b19238833402de731a8e928413a56809501c9245cdeb4f6673a74d79f18de7480ca1196ee88156d8defc02a302801e0bca0addad9b5fe1a4520e499edfca95312efaea19a8a165f021240f14d9965e3563d37bbc8a7e06adc0fb5a3614b0168f9464cf471a17db7d7ee23b30c259eb3ea37061b6cb6f13405b9a64e5a62382330085d5a112963886a3c23178a5f3e1926f86b5c2c06eb78991193adbab238d85b40310d4c56185cb4bdaa8512f31c6f9b7b104afb462a976d03177e286fb39ae946dbb3996ac92fbebc3bccaee60927dfbad1e41199d6138c9c7393cac45ba3b4845bcaa2374dde32e635ace4d240ce2fa191a2939dc81b56fe315f3d011596c1e440ad6e791cb90188255eaeef409a00847a589cb368b9744c41b9df2887577f897043b215da41d6113b6c71443674d4c16b36fd37829ee920b1079b9ecfe5eb3fcfc68b444c6e4bfef5ab445bcad2f14d4d0afe4a79af9c622a336df99f5ab08a4e3c2288deb668383e14fe84e98ab8ad62ca37a38a4ad29d4d7d8ba440bf1afddbccbf84321f69714390d2b1e9e1b7458012af33c306f672c5748a822c9c9f5c318dd26a5688e3fbe669cdfdf01eeb28e65a89e60f60ea891923b97941466d953e0e17615ecb827033b3f25b0f400235f92faa2742c8bdc3ca8cfbea3914214977cdec83ba6633ecb85e02db2ad5e574763ea68cfb8719db8e8cbe74062dc34e2328c5d47ac0f2b1164159f74d0604579f81fdbf29c1d0666f324c699cd3192b54dccb30944f0c948ff98219e073413fbad7ea7bcffbc38d939dbbb8ace4b1a3b63d18bbb6a46d33c504753b625fa2fcecbd25c301e07e3e18faafd2137ed6f4741ecfdc6950c684c1b9c633b4650837381bb3e70f18f0247be28d8151296d577e3a7919caec9fd49c6bdaa569f34a193b270bf5bcf387df8a0d099406bb17638e920f532fa7e0c672266d32dc0eeef0a5f0a68de000a981c928a111cd29aae2d06314e12f892d4890718bb41ae08dcabbd9572fbac3b2dae9a8d40910aa1a9f4b620eefc6705c6546b3865f152d30b4def11b8a9aec50a6b62aa0164efc4996bc35612cda411f19e7af51a199eb9108d4764ffeb5ce11b509cc1129edcab6429220475ab578fbc283627077c2a7f87ed0ee843a6b3da23ba4897bc479a2ee7b4c8bde7e201b4040e2f7414b7dac603e3e28e11124e9a8f810fe63db244267b746b2e85bc77cbeb520abd5156174797c8fe3e357f94532dda93043881003f957748c0d59361089cfd1bdcf314f6c48c9700f7c0fc608805229b2a32de1f483cb674da16560c8a378a1baba01154f1961d844dc5d492df82221d5dbbbb6059a8c73422d4a78c2b57863ec6d62b75492698e74670bc08ba15eb40ee35f95985eb3a5f2d2609ac7cc0bd49791cbeb32e4ca3d021c4740a49995c604775c084f1172ac2cffec336acaddfc65a222168e532ce866f77706151237d0f2a8db6c5c8560ff06397ea242ce63ad596091f2db0038183e8d08bcffe9486abeca1789056b1878158ddbcf3aa7d196852afa8249995bfd413db50e0987705d181c8fc2ac982877d59469f5e8732b6eb30f648e96d5f6295937f6d3c560f4fa9be81834e394dc17c06fdf2778cb08fcf0a837f1e92e8f9ff879bad48c9b5c6044bd0c71133a45d3fc96eb7507eac8464d5bc022534726bfda330b8d2651e4bad97be544e29b448f66d0c11225de2194a35af7accec28300787e161f2f3ad3d507918bf94fe39270c9835deae109ab8da3862912975e00f5e5b558c133a1c3a932e2398cb68b3196fd1f6e4373bd3f03381986ffb1b329b27997163da1e6a086f2ddfe1e203ce6253aae68713ef74aa6abc23046d8b992bdaa3e530f6c2d8de253a68ded1900f6b9bc55d0dd09e8068460ff858dd0a04fca32a50a05885a4cdc82b5757277b81f42f53db586dae9736c8729b98a1baef3abd48f00867fe77891d5ba69c80c2001e0b1b575df2f79a0d6a27154cc0b9f55ba5dde1c62a8bbdb5ccdc306b67623e68ccfe63869e2016a20a96c11a09c95441081a116245e021eff6a6a540afa6c6c1ee4e74a9328b63e7d5e04f34d077d44ad22f3f69af026ac68faf7c93b69f70a077d81408e4903a269f9b26107bfde12d567a180da6773aeb8ce533e9096b587d8653c0810d5df1e6415751f8bf144bba41156014acab9b17f54243f1eb8fd22987077b9a81f3493efe8babd6f3d64be83a52670954f3a0c92c490344173b441244bc9e2b7b54917250c40fcda39080a19d195d659cc8cdb9702afcbeb8e62dcaba760d1ad33122759025c36d427ba4237a178649c0eb9836dfe8678f688d615ced38b22aa0c2f3ced7cb8897e4c36da59a113eaa460582ab32c70f67a73388feaedac6a7950c46ccc9b71750a7a779bd8df9d2768212d94fffaccab96e404be14f893eb3b0a8519b6f095a98c437419362dc4df02dab4b9e10500f6545903892e805b8766dcfe2d51451f4a5fdac9834c36374e4c4ba7d50da1c677ba5e4ff64396be46cc6b2e87833fe6197e36d4ccbc1a08a4c841b8cd3b9b67b141c01daf8c0cf53786a93fbce9cf31c46f3273fdab53ddb5b8a9aa90cf0a02f0c768b9942ddb1cbc49c0ed79580f31d5e0eb050e0c9605ea8799f67689eea895177261fcc5f4225f802b0e723dbdae31120c9da665f2e0b4e9a4b7d94e706df5a9773b6ca3e7c4fbe5b524b0c884c2e0ad2cda6edc32dba770e3bb31307c96871746df6ac387d17d2a2fbbb53e083bb15b17681f1d69db6cff2f6c1184c60bf6a7ddd86014af0da10afb017c7e54ed1159ca0b1c54faeeca1f999bc534e99acb59c9814c58acbb134e608ec038257690e34250ec16f730942ff9d2eb52453d8ce18fbe9329ba6a575ed6a44872d051dad68ba9c7481d9fced7819503f1d7bab7a5a16268f41e060c8360fa004d0723567d569dc2ac603813755e361ad59e59ebe8ea2d63ab2833041661ab64acf0366dd75918b9120389ae13bea64f987aad7eddf31e1ff9417c61bd6d683bcfdf760d6b2a55ea31d484b86100473b3ab092060f36f315330bc9100ebbcaa2bed9fa03083c738e2e8401870b6b2e6c8ec48996aed809ce4061ee3f91cbcd4dd36986ebc536ae3cecbdfbe8939def0757b74b6366abe8a7c8db63a7944210c0f42297aa7396ac8909d53622b1c08783221690cb2600484410656720075a7c3f5323d12cb94ec26b90a97261ad5d243b5706f41ec23c2353d7695c88bbcba61953b5034fab3aa327fa923aac44f8e8ff56076210af3f6d1242082a2da135eddbf378c67e26bddf8ffd4149db1baebcc101e08fa397ef76dfe7ebe14ba5b2ea033c59f89e4605fdac1f9d64108def6309e27813e0c7e3ea6411de47342b0e0692e00cd7b43a710238b89dd2b9ab4067f6f5789eff907d87cbd816f679f39be97a0635a0ed6483df5d2c729105ec806eac54944146dd0c56e2c79a915e887a97a3129731fe10d438416fdffb6a4a1876c742725e4e80e641b4c38190ebacb86aa672b3a8edc570c40775973b0846452b023ef8556b200811a90c6ddbc74bbeb3d2536b6b0c2d157017a7059230608ab37d356e77f376a770c88eab94c731c8f3036d0a5cbb8dd54cec3d74307af386b45db019ce4a89b7b508dacc831bd2fec2d55987672eb335eb533cd1d1ee1ed9db2e25ef6309f4a1f84b9b5b5723f45760ac083bf174d81a0903548be75ed6840cde83b5dee612bb8272e4aad07cbf6552a474cf32e53d6cef0b7a1117faa8a580b8270b131bbb67f2999a2218d22c18a0aa4aefb45a44ac8f6b6c6b370dcdf26835698bf11b40fc5b89926dd65a851727c204940a41e55fc73b7df2db8927cde4a35d9414046000367a6267fe5756321710c7944e7f6a20fc9336ec7e988bd9c08e0f34445285de84d22dba9c39f55c45788f8b99252ba2da2baad92f0eded1731fe24acee6ed2d4908e960390d533e24735831640021f7399e1dda6d91d297ab7ec294568f8ef84f32e8d00faaa76e8aa5c48be52776334af1d8bfd2479c1f0210b1481c3cd0da701a14d8d716af7a2e49bed5363349ed0328a30b6eab6419590886844d4121a17263c29985dacefcd0ea68d8d9734f7f8af2777ff8e52389878be692d4ebbf6f55d0043da781cb58ffb1b311afe0195a4274f21a1d49513186ea9b922a7b099293939b7042490042977375f83d6a033eaf45bea27a6f07c566d5b416cfffbda18d53ed0d9eaff4a3edcf31e6c48fb6d9df1da8d4900ddd856ad4e27c20c49a5331b7664b66cad847d4b2820bb575661bf6ddf64d8f109c7a4ae986258d48e526c97a2f37cbb436189978b96c19c3f388e8de086cd720e072cec6c8538ececaf2c4884ef808b31ce50b601bc31ebcd66d499fe2ee39fca1da762b4882c37c6b13bb6329ce071a8f2379380cfc78be99c4e60c0c96bdabb9593eaebc23e3b984d2a78eaf4f7e988f31345a4aad0f30cae7a8a7f43c5949e96cd6d56f4e2c8492f195f00e2b6eac26cbf1677038d3883ce855125202094ef6224928ded4f30bf62f6b20855e935d84b764b7a5710b2c945b98616dc9b13a443b3a624e6dc630e019271d072518bf530b0cfad01f419e21ab441a1d91e63ff00e2c598acfa96916e7018593cd486774af9ab91d22bb6ad666505da840d03289762c4311256cc61c9037931d606b9f3ecb3da54804b765ffff9a6fd13f8d6bde45208e56961252e430958473a29928a09adf8caa139963d66866f983e1cd3631425d2cc247c4b96ca40b3a88b061150354f17710823d0a30e7d6b446cf46ce76d1fda7860db7ae8ff578b9d81a367a06587ef147e126bac107f36e21974eebadbbb54578c47efb608e9676f74daf127a7c00900c7d1ab268aefdf59eee628d4a628928eb681c3d203b4a4e1f6cffb771a7230f38d45fc1980bd3e0eb08c628a957080928e28eea9494880b435cf41e369988433f1821b12b004be6e7cc93e3df0bc5485aca00eb06c09a2a353b90ef27e37e333d208f4d6266af6fac1fc4b0029947b5cd95cc0fc3df16dfef0761ce5647aa22042bc0a68988819357707da9ba1ef93bf9d2399f2133ead2f6e1851895fd15edb854d5cd3fa15e92d6c14f65ccb77746850dad39abdd756522302bdc5c52f0ff1499383406f19e9cb14f28b5adc57b42481505b01eb00872bfe3e2f5e26d5e079cf316e7c8ec51ac9e3e4e47fcf9304b8b168b55d02dff5f8800b30145e50d914dd380420e8defea633f99e07d16a269673a47ec358f93556fcea25c260526ea6fc99d96b39cff71a11f51065bb0daa6f9657bda9db8887aaa6b2fa2b37f39579ef31462cd67bb9e6f4d8a2ba03cfdeb5f8f8c61489a8cae1ee5aee7bdf2230ada404bbe0b59d5e2cecb2b3fecaaaf85870b79045229912b6b699f4be91b4684bbdcab628087e6e32a18fbfa233e600a3684481b47f6582fbc0e260222a0786c6c81cf9dcac383109165e8846d667541e87e8da61b0d1eefadab8a8d9baac62965304e153c3b40d81b58463a0f6eec8753c2709f96e68654d5ec7c1102e6eba8469be77389d95f4c552704b8dfa343604f4c5c869fed2e929cad53834eae0207f2522860331ca638265c8a9cc0445dfff5f136a1f9e2beb8911e8cd12b50acc1e01bc14b996b0694cfccbac616c3aa77bf77e40b6ff0a3352e5c6db7ffe405913b1ffb656ae3bb89ace00442f035481791dfd5135776a0ed4b6f9d53659a39f52d937ef229dfaee10338692c5ac25d49d3dd6d87631dc85edb1e64f38ce1016c1b442af61321602009618f7287d2f6da992134ff4e17ef678e4d3b0a63f1627bced39f42b4edc91ae344ff4f532f3529d110f2575f3428e8b766dfceead3e51a52306b934e79bc6239172749062f5cd6671256567e61223f7b0fa732d312bc4c8318a59a0ac2898126ebbbd8af34552d43b2c8d0a13e17cf661bd7f06fec0792695fbe7f33e09ac1ee6834382ac5596c25ae715fee7b6787edfe90bf241e4a0f2f8b11eda974aa1dbc14cf77e5268ac002772bc19000deaf0d1323cea3ea498e20c21725c6142653159aadcf6bab00519ce5c806eae384856ae4df6750bd97da095071b76189c24a40aed91bf0dba33f829ccf7117fd00f6e5452d8f76d9af941a3d6eb65658050e3e46ae70fe449c6fcb8bc8889d513f9f0ab184a782407d361302bac8b1fc351f05e907a6c2a4d4be55083a4be14314dd6c89017c4bfb6b08fe00db19b06e61ec411123d95a09a42232ec37226479e9b98ab260f044cbdab2f506eb89fcf3ef5b25bb6100d256917728a9a74ec0a9fd43cff058b78880b867513076e8918f36a8061c6d9e56b8261d39b1d7587206272b4293660abc1a1e6b5d5fcd44722f2134086eca0de0454bbb4aade04d31fa390ceca942cf64c25db103971f3232838d8046700494f90e20275022d1f2eaf93102c86c5afb1df0bd138b586fcc6e5a36fc4eeabc9bc26f255785371ffb961b5adfbfafb89f4cbf2723f674f68ae389355179daac5cae2ed2d4ef95b7d6c3d01e40ca2d75fd43f3b3da96b93f0e9cc1a9535e459d14181d12b9d18e29fed34dd6671cc5053a7fcc460fa5c850588b7de966610f5d190a7d07b4e886d74eaf2e06fbe24f20fc22a52c267787c0c83dfec4f88faca52f2e986ac0b03b3153a84a63c405a2a31f798738495650c382196fe3c3d1af020557269f6f4fcfb334ff656c9e0029016eff4a0965cf0f1fafcc6fb251d733f74b3b6a40b2caf424321c2bc7ce91511ec729730366dd598bc6b23eb424ecc5223237c204542a40c430d9058732024dc5adb9f6950d90d1489b72c3a041aa329aadb20b31d0798502d4bc1d66351a80a306fd4bf674a78c419e257bdaa0135ee15e6c9c085adf518e7a4d2050579ad521fc191ff109663b77b08fe46a172f9e6fcffcec2663b09ec4c35c41fc593f14dd981a35f4ab148ec922bcecbb53d613b0bfda26971b48727678c02c6d8230858607365f1194617705701e324418cd3480ae6b4f5074549f60d7f94818a82c04626ef2cde56251107c07582450d85129dab022f964c85878b865939e01a3626afbef4603092d0b13272d55f7f8e933e274fe4a8e4d060183ef6af951a0b3b465f0559c79b11fdd7c35ccabd13fd19e2f96f1a2db9b411b27473b47e9e6158d0971314862ad2046cb57595ce5f9c0879ace20d1a1da9ffb9fa2b9529e611298821346f1f0b0f82de2634639abd7de26e476725dc5a6e2ea3780abd3ba7f2865e06b889d81118e5e1d86f06bff1f12b1d75008dc2bd847e14503e6c99dd42eb58bc362b9189ab87076f3f9f07812a4bab893b3904e491876074e2c066e13de6afa78fcfa6c837e44c474e6ac653ac3c26f46cb89d21198e2060308526248ccc684e515cae13e4619c57fee9b2409ce64c7105d07430c790fa5a9b54a90ded9a4c1755694aedcadccd2b5921ee2fc42c3364e3ee0c7dd69d979b5008de3ed13236fadf057da6414be85b59a5b7d2b88d752915d212b998c64aa339b8e75aadbbe9dd51b852daa1001820b95e1374a0d5aa4ff6ab10904f0e6dbd188d026dea454ed987310d87c3f22acf751fc9f552f120cff41a959e6cfffa515aaa9f0a1f8c6abdfe00e122952e88bdfc49df63f7616f5e95cb556cd0780e6ef3aab9a5d9ba543d5a8b675db4d6928947da5111e282eb5c5c2c3dbea1bb7adc9e0ae6ddb5f2c8d10eef8167ead0607b836f4eaa7b9aa41206cc063e4b24aa6d5ac4cc60dd68f24bf4dca01718db30eb2b31cbac15917b1bed2f99f26f31561391e6bd4955a1d9b54ebda3c83669bc61f0b94b33da73fa62f856d33b25c3ba88b8ef3fc3f5938f73a4e6143ea66b406a3fa9a7d11394560ce529a1563ab9bd451f4aa357ed2e3a08304deb32806307793372ca67ecb406710271c5254254b48dd344cf7d70455a56a7bca10dc4601c90bf372d83dad409cb0884b7d73d53c83ff43d67c7a7dbfb1d753573f3ad157b6d70f1660beb783fed7d2dce519a5bff4886d02814bae116866df8af337dd09e3a79e48f7c9d506face3c5e7eb2554e35382413470f4bc34856aedc5f1e955d305d208b99a87a21cd5d6e667a9943fe9e9532f13127f5fe444395776c2dcb167e94508cddb1ee1498453c632dd3033b678b58282b8a74bd233f61c35d99575bd46ae2e0f8f1fdeca0e712eac969d6fc99d284e81278267cfbceefc578a5feb109d30441c0c7e2de7d18c0d01896e4c2d9e2f8c02f07f101473e569b1144f022b62f1b776fea3e3ee29552e92da1e778fef230c54237b9046efabe5f309aee3d4a81823956950147c47380c573a4dbfc3be6eb2f38fdb4ed8c954e1cfc43dcc210def5cd762b2e640d8e62cf51fe3400551d1a3daf378a5f5f8f99717f99c5c8e48177ea39b819c9798a845ae5e9f9487e9642e85762e88b56c403071f1c2b19ca47da2e535f1518acb831f42e092596c421b8af09febf75ac263d301005f3dd4cb7cbcebc875e901823c0ab3f36599f621a13b3a25ee4d7ef9fca3b096558a9ed4179469bd22a9e7513c1f58f2e715d42706edbae4f59e12fd70e7054cbdb94f0f1a750643d205d727bc328b8a73df5a9cf4a47457ea9d21f92c9ad12b1c459c180633961890ef85b033713d71b25d5add77aaccdbbd6ea1ecb584e39b2c56a17c8575121463a7956a9aa69e9dbdd04ef581508adc90d26b88320c0dbcfe2bb494372ee66c3b103a12eb9ddc1082e41d282f1895ae16fc639b77f57fd54253ec21c21e51c0fd0e7acfde44b02286a0c1c8091a110734006703123fee5c5c361d76ea8f3f82bd5f8d32bc3fa0272374ce126d208bbfb27a8d8a2eee73775b7e74945d6271d7ea771e14e1e4728d8431ed36d6ccb1b36159b2bc4ac1b2c3134910b8cfc3addda3667b6b9be96fea4e18d32fb55fd7e36f7b31d410f3b057b19e91d2bb13e32faeb079fb8c36df6dbcfd4218284626e4199c6c3a05a47302bca73a0acd6b65c1736ce3fe442e0b63eac60c8151f3d0433dff27d4b825aad7268b02d4b54275f435bb50251530a229fb865240b48af732a305fcc475159cd4219ebfb46b1b063fa2c2fe109b57527804cf606939a092ba32e5069634c06f7816b0b12ad36f9be8834266449878a888ba88a10ba5631f7cb7d5d227b6566d34ee517a8d2c176005c63eec198d6892a834bdfb940400f31203e7af7a15f97285e9a12ed103746a4ce9a9883b00e3d0e568c6038650bb565a16f9a21bf60eab71efb4a91dd09cfe36bdb9b1f7c47ebf9b5c000026e9ba2e9aef4bfe4a66f144dcc3a9741f24c7c2a84c09d2bb8aa85abf6380b82e4ba3c41b3269c3ff35b72a71a115e944a8d1cd31535ec4defa75d6cf14d7357536f7a8155cfaacced9228660b2fb5b222666e04f611dba793da512b592104ace76f6b99a439e42c50651984382380c667b43efbe830f4ad4c2820e90396d7d8c1bd03a7dfe7f37bc34b90c051c2d424c1ee7c533f25558810263ddcb803ff1bb261d5033f57981401ac5933464f51c8d3dfc66b18b1adfa171d4611400f42b6b83c3056d876bcdc0b8df7ecf259d14157e7766185e67339f125c8232330afa66b8bf4b8171d173bdd4a99cc05e095d26164d5e3865e7ca11f8d7b5f80fbf3085c362cb48c427b321bdef9a5e3a40dc241b5b8395d25a1eaabfa6c7ff5a357b116df483c6577903ad5d7f1c948b1e21d15f7fd0868d1d2ad798d505db7848b15ef4e9bfebbfe512aee18431dd12ff46a7702f49c8a547a5569a4d85d0db53a558c869a30762d142b73b526e57c250c1a4d8c93961e1fe0b5d5acac5b481e9ab2ad418081044e865bd639234acf7ba5be722de4172204acb69e505c263699e2daa1a203457c4fb5e1c133f6b03e2d3b8b8ecc286220653f7ec3734813e30813fc58a1b6d5b959b569c988a1a5e0b068d9cece5cf3dad8f6259ae2adc195f17f3faacc8efabe094d9e51e443deffd3f7fe62abab865d08e5c3c8629ddd9e3c4c9b819bbf53c49eca868dfcb36aa2014147b604ae0df8b7324dcad05ebe8fbc67a69e07331d0f96de6dae3988e6dd9e5a505f3e3d2261e9aa0aad82de22dc18aff7226a4f0eacac8187f3aa39f98118c92fe791ed047107916ebfad7d7adfb58753e6ce204e9583890754dc46f67216b7c5d4a5921e34dea4b6b074a73f5f1fbcc4caf738ec0c17a62606730be0cd81b8281a0675cb1540b6fbfb2733550d50bdc553a01571dbcaa787580e68e9479da83803b9a590d83b2d2f5e4705c1557980ca7c2febab11756aec55e3b974f4a5aec29d1ee0d47fde6bdcbfe78f12bd03a2d3bcf6d15658926dc99f2b0416151f51f17c98adfd5d3033b60867f185f041d71759fe61768a8ce925ebd0fb8c847794f3080175d25bcfe95e580619b5a07e62b9a4ea909f1d079f0f061ab12cdeaac1b4679abde4ea12aa04175e73dcf984bd3711abe49d3051ce3bb11867b4561b31be8a71cc2b2dcf912cfe4565da385f437bfc61bade6f5a7e01b650e2dc24934152c502ab19ce460c98c3f0f3cebcfb21acbb3f3c59f010afdad53218ca4fa0a8613a7fa574750c3b9e1e198fe1cb6f2043a3b346fe01c828cedbf0050493ccb507cd58088f50de6c7c4e0049d7bf7e96eeb72b036b7a8d4dcf8eb3db00ac56abdc191d538b50e5a67236e2e458d75f7808c466790775324ac37dfc3850b2d6565af9803af07f3f436ce918cf44f865b279e2fc0b9a878ac8480447b811ee68c1ae74ff5779a28d6483192ef7df2b07e1ff25fc252ee970d5ffd5cda9ac26f0e2d3c5aad79764ed46364c5bdf97417b9b81e715ba8bb968207317cc36eb41a6e06b4e9c20e531931104a17f114a8772e6d5e63562ba3565dc7a39b283604f3944a49124cfa20fe5973502d94e2f30f91820ac8e1be51a54f7687667aef970e67c0806ae399d9e3fe1fc759375c487a07cc6c4fba89169a2961a65669d38b7deafd605400ae2655ccc66ffe3deb32132d83e778258dfd39cda8a0b9239aac191d129f8a88cb38ed4447571ff5c4d5951c126f740f0b41aec7decf91f4b2587fc80b552cd9897c4ce090a0f2bc7a3dd0269fbc32f22eac27bd67f4c0e70874082945bf985c5824d1c726c28a45b6064b2f5e90b91618877b8749d85258c2dbc4e24a5ea23697f965d03ef48007df162cc0d74fd91f2652219b322f42222138523547e370bb12faff843669f0f60f766353b42a078ecbe8b98b22c54fe12430c81ddb7a8c7a714a241ee65d89710ebca7febc909a19311cbc94f863bace29537691727d70171d9203bb4d2bc5743462f813ab384833acb32b5e6aecb284201283870c4fa6681cc3472da5141c5b7ddeaac4f990289c1557c6f0acd9b5f291ef9bc2561a85ed13472cbe0abfc33da6f8fcac27d6873bca8777792f713f1b65ac8891aa372ac80dc0cc5f5227cb08f6aba918da362584fe6c90bce76232ad4f91256f47478a5094f54a79f67f8ea72da2cd311e105ce38dd098cfd5c769a6e6a1d98c9f9167d15e4232bb0b95525ec76e22e088b69f2c4a5de64db7c542de86461f5421132e24088b2fab64b4761b8805b9887a753ebc37adb3078ee2c9246fb0d2cd6470ee1dae9494c9b853fe52b49ec0c184aacbc135667b43aaa82fd9c5ef71057d95c1e9accdc19dcaca45346922368141782cbe0094155ff284d68289a10bb936608e1e41f8d76f90821c8369c4e34cbdfce18ed8d414eaa33f9489e86526c8087851941f2d69ac80ea461f211ea2a60ccbfe4df9a3e55f3051dde4376f2ae1e1db34efb3bce9122aa513779badada933e898d86966114d92f3a0a5569396048301ac5779544fe75cd5e2262b227e2284c5ee8aec18c50c5e65498a68cfc4682c629fac87606767a74e9d8da7f7130816edcba37e4eb6fe88fa8f661ee05187b0180c549291796fbfa45f8cbf159107afb85b7550bf5ec23ef76095ffc5aa4db51adae8489ec420cf358b128743fbb3068c076e3aaac961bae3cdadc0b2da2e2ede7a66157fa71143934a23e9a772697dbc7e5be0b91c4a421304506d199ddf898ac5551e6685a9409005fbcab78722cc12181b25974c799c81800e6a5833a0a6dc41a11297381b40d5e2b56ec03a53ee662ad09d20fc288b8797f8b6fd0f48916b0efde2d3ce074df9d57e52cda0a9022217a95f4c0a14641b709fa97f66927b73818904e89c8e54cdbba674877f32b9e90b2e19222fe1c28ae27884a44513bd6af6d185487e8185ceccd5c55c87c49d3aebfbf23153c29d380bb588483dee58b6a17eb2e69661c6c4b5f67e9e03e9cddaa02b8f309a0d4fb1f945565284088f20d6696aaa1255eae2fcd6b3dc6093478dc4e435439a9e67c7df1a5e67f01bb6fa4d5439226e7fc4beae45c7ffeff3300f0c545c277653b8d172b9352efd040c6ccdd455b2b3407baad15fd649373410955f2a5eea09f7499f17848cc4067235dbddb7f29bbbf6bfcfac1dba962a5f4790a07ef8a259ce390ec9a098dc4127491071e6e0c5dc8f461116e9426092b20d216f7ab7353813c532daa34625a0fa52a5d0333929c8e2ab7f091b55f7708a9470d2c58325818636a1dd69baddc8ffe544b37a50ed735eb05af48f4aa58d53fb74c9431f8d045c129183b929bbefe8536fd919e8bc7e3d89b4796c078e51433341bbceea9937eb648a103b8a0cb6026ceaf115dc6dbca5cf29300406cce20de4cf12a7ff23fd774761f7f9495e714953d88e61f83d8100ff9c07376a5a9d2d2b7409dbf5d1521464e898b3bf739da899f4058ba82d0b7e6a89b5f68d702dd21773e40b61adf001d8cfcefb91a48b70294fb31c8e15b51ea3378a7153d5fd99d0e371652187bfa7da008e3a42f84fc955adf9a33b75d60bb5e18870f9b686f82c8c066d4c2b03d3a98cbf73709389f1bc0718e92779306869b19df4b0fdbc5596a66dc8dec251c02eead751e27433df0cd968bea6982cf1d02be99a8fdd79085eacd0ad5b327281d041c715ce61d744dafbe577a9f2f9044eb159b841bb4ec6d50a680c7c85c9e7e75cff3ac45dab6480098ee4c91c4de76a6670be00b8bc6fc288c63a033cf3a06f210b5d03ff557d47fba95ed1c37f3acf77e7e6ce5482410886f464a703e57c628001c18b542f5a4093ad65873549cec80d4a3b62ea7363dd817e6c9c454b329c2b90dd9f1c3d7c1cbefe441dfe2e354dc257764a70d6337f1ba56ce3f15c67dd348ea3c41c87be8f045c1108bb910e8aa17660614ba13e8c74939280c67256265a158a6d30a559ad30992d2c39b224bcd54f54d6fbb83a6088578a10953a79271422cda4eee5c4f10f6a04312ce6fd44649abafd86998a89df4756bc92e6ac298210b5ef82748c6a7294a199b340df1d695efa137857c1b95fed9ec4423dbeb7ce541d5bdce2fa93d887a46944b12bf90496a97488a74bab6106b5bace22135fcae971fbcc4406f8c376408354422133bb2ff8924ce10980e141bdb22fe2b23cbf0aab6841e8923606421b987c0a4e6a679a64060c1531dbacbda3d5bc9b392d9087521a1efaaf1b4aeae84aee0f53c168d6433d19da79e62a0e9186217dee5baa660ab518fa533c141cae034ec1de52a1f3bb0cc76b8c1d97f3a4daf7b3b594608c3547cb544729f4e165650d317f739c599095e8dd75b459fd1f203b8acff787224c81703a70e7d0de74c8cf3110dcc0bf4da7b4a0daa623c39f0078250f87b923a14271b85f14d6c636ccc741e28b358fcb54ed8de618a437682dd3056330914bf458df50fb34f809e60e887b919c1edf8317ef44821da9722e8ff25b0993247c1ef576720f2cb00a206ab05025af9403a4cc17b0f62dc5d90de06e55ae5301dfd696324fa0f6783d94a1e8cbd6c3c04d174c583aaf92d5bf13b03112f64c54c767516c17312f8f608603e8863e4a729ca5e6cb7726d0717b1bc5546d64e3e6b643c49b218671338a79570cce2f0b62656900247e47af06260cb7d848a3c7c53728174d9141d8df8f51472e1c695c316b63b77d0e71d5a79f6b75c11ad46bdb485ed11f13a0b25d218621f4ebf913fe0538695dd9df8182b078f6dd96a56d81b801a7197c09a87698469f4c36cdd51af1dea5ae495eb92fd38ef5f9cd90908321e2aa90a8c829bbc066764b0261f046c337e32a0a3eb524901b9d6ee1f664b816891b7b8c6b033416621d8472e284619d371004f03240c8e37e31d2043fec99a9532e4f736fd78bb4f4b537f33030206128a3709a32ae90140594d7d370da1404b970b8eb27b25af6ec4e1a4e33e5a48745f9838a858ee6fb9fd582b2e63e0dd7fb6d1a9efef461ead549bf84863bdbd43bb58dafa0a606e57889ac1434766929617a622e58c98b8c70b5737d2f886df8c172a8983bf2eef51ed68e344ccd88cd1507c78998ed8c40b3a4a07fabe5a019793c1afe3d308545cc3f6f68a7f8240c0f2332a52e0071e4249e37ab99e6c0ceba35707067de14fa582eb862f06ff5b5b61d0b97f58cfb98c6f2efdaa4717442c09994c61d3a61fdd086413b0d618a8a3a70f9931a28bfae4d0a4a2b631af800e9b8cb71e9b080ed9b1901f9a35edf9edc9c0cf54596561faec096782cdeff0fec457b1320bd94f1ac7ce2afdc70d965db9c63bc797a10142c003dbffe8e6d8369f35f4c98fc979f87ebc5978301a291d033199d735fd0821f19ab2a1386ba2bd4bf6068aa2ae931c61bc83680fcfc16716082f0410731cd3e314ea6c627760a3b2b04bf2b94d9a185abb778720b31c84266dc0a256441faf49b2b3f0f720b6c68e3a7d62d43b8b43d63dd7604758f9b37eca0b3ca274a87189b6658c89fb36d63994362387c10cadcb13dbb5ce25c70d97098bb3a9961f2a7a3c6a5396c909e2b915a044efce8ed4ed295af51555191ab2aface9313dff0213de05198d78e3ddb43a672047ee08032f09249007ec5b49743f629b31d308082144870b5595be204753115856b22df54f767cc4a721f44d07e2e79df5f9fb4fa6b8f67b5dc9b2aa098cfe54c4049683bb3a18230240c62d36381e25291b2bd8c8dfbefbf84a090a85c5bc39e3ec6bdbc1810fde7be635175ada09dab75aa1afa1b5dd0dd6788281208f8afc1e6978f5df4945b180614dfd9e2731ccfe20a5b144c956a9b56543c42504f6ee8e1109ff66228a3932cfb83191423a78b6b29bd93d1dffa9a453895224c4f0658a3efd2d47ed94575ca7e41b2879ffc8a6ac950674dbfb607317262c20511489ef3d71f27005c0cc887a45edffb2cdb26cafe86edf522bfd2a8577b4f15568cb25e2e68b4d4402901b406ea957fb7ee470464084a9366ca114e44af45f85c91df43d8c3a5b4a8e924823027f4345df9963561706aa871ff68fe30c0f26bcc2215f8e0db213bffef75bcb5f6c52d653bbd17a58e50cfa710fdfa987e4a4fe71988f257dc05844d9510379a20873e2712349c321a0ca8942c497d75c91de3e1996b04499e42af251e2a9cf8dd4608ef9ec1451f022220405e264672e59f09c60895e7d62d86ccd5018898c539ee804559979df9871e0ea10df5a5e4fe2ad9579d9b32c275431bc2a353064b671c9d95061d055f738a7085271203e0ee6cedc77d08382313f52763636c0d98722b6ef1a661109eee79c9e86b93da8ab5aedefa3031de8ac497b1fc6915a39d2d9a44a49f2e41d73b44ce28b13d7f95cb70e9ac1da7af9cbb93394ff059aad918d70995995d46894596341e182c17a9b1c31b0342ef07b417928daef24cf549cd06792c55263cb3abbf746e27e72434643d8546be0c36b97d6e08d669778c7d0c0b6790248a06a8618408a749b7929cd12e3d7816e1affee48564ba4e62e650ddd824518f1d8e8e5e8c2960dfb7813721fbafa80e52ac37ccb66b38f9f6147ee57748dd9fc5aa4c9f30460266fd8fda2eb550b429cb5547e3e381f34b446cdbf71a2284fcaf0d05f236935429c3f67e28135e44d3fe3585647ab3f3f3ae496db280916ed0b33b75e9c9c529a6f8f44c9a84fc64d9c831d70de8b389b73a3bd2d997630e3a7b3c7d916356c9ca0e722e887f241bc702ed75ff66fb3118687493640c46efcbd4e2c79c8331b1a07169ef3ee1c7825b8df821130a58296e54216b8aa4bc21747425aa1812d69a864d2d731ffcc6b03f9ce8f66035b6fe67290e48bf56534145f27ce3de1868b317b3c6b16574b82b8a55ebeb7e4f5b3a5cab5d2a7f7384b30ae726bb17311775d01eb9b5eea59282b3a1936d44dead540109a43d5730202c71945fe7b9b7c6512634989325dc5498d1ad770436781122b6399438c518f5acfe9a2c0837a04ed3d115bde8949a1ed66879ecbf6e6bc74ae40a3069c3a12283d313610216c0efe66a515b7fde69e5e1e89b01fa7cf3b24f4ba21390f62df4bc938743fb51332194baa00efb3dd495b902836457ebc48d3be2b75439cd2007173483816484e13a892edab3ef08d2607427ad5faaf3af914f926d864314b4dec615505128518d8dc1a25ef13a428a6098f196920ca80ee9701cc79ba8150cd74dc22c3e5407abe82e04f69086ee264837e9a23ef6ca7900392b650be64b60bdf4b978eb6cfcf76fdf75956ce13a329d8de7a1833606cb3c8a4b2d6c63f0f3b6e3f1a79c60fc500e656a868004fd9cd90a5d5c011f50178bfc91a1f9e24b489ca350e3f84fb68eb5508d61ac34becaa9c078e8ae8e20e3bbbe116e3c890cdf2e001c808b6dae413ccaf1584320e70bed79a4a414e1896d9983259219005668979233f784d5d66a7eafc9ef588420091623c54e1846a3f22f5d5ac47094d7428396ed55302f42fb2780592dc4c9e65ca41b7e54fd74692be355e304fd97b414dadfa85eeb12f71955d2047e4fcca44133633a7e4e4b9998bc7e336cc8e2ebdc37638b3fc320a9493466ce3a72025b49d6eafc5f915474c39013bb0182d4f369de9bd14b8a7421e10da084cb0776094e306e11f1179e472c51804cc052f11b05925bfc6fcde77b603b12f794683b97c52b99acf7a9035e56b69a1bcd04655e6107ace3449bf603e7ebdc455f02b26b08a0929d6910119a3cf11d3cb804d50aceadf0c11102d498645a0d7038bebcfc82e64dcf2d20f614362bb4856e05032e202062215a702e6b94b1de8a978803a5a186ad1d6f79b83c1308285f4398d52c11da9ea06a2c0c52565de04fdda6f754fa64fde309a4ec304d9ea4f57050d0f21f22bc3c543814b9421a3c7ef07af0ed204210c281301e67fa4a3038e505ab330fa2e253274057f40b8ac911ba75736fc6a952873c18fd1a6d7aa1731be3ec68540f8bad9308fd9bfee41aaaac41046e40737f38993a3cb74aab88521b7667b1f6c15200287f57278a984a1c5046673ccc79becaac718cb45c6158dad1e7c752ca3eb063e5c2303cfadad61c5176e9c743cded475ffd6f394f56472a3f00a49e8b08eeff766c21acabe092d9845a7f1c6122e9d1b44148de5d638051b9090d8794ad85bde936331587588244976b2a4b435a20c62a22f36bc8563ae755fd4df0dd8312696bfe68c9a1742e9fc31e81618f3f1251d0b8c99829f9e02aa3075f0ebee309a15b2fa59be62563f23c24ca3e1ea0c1f3c263ab000789d35e6076c3e228f9e8762afd5552917f847ede56e9acaa4d295784655edac641eb50ef2ad9588b152708115120df845cb2505520951dd8481591c3d1fcdd2c45030aa17b2dadbdef19c206a0f5ae019c76b845b13f9b3caff7448e60d2470ee82a10b4e91dab5c3674f98b3b341635a10f7d0e12928e42f51739e3317767704d125b9c96d4c1706b6e761381ec9257cd0bad4f477d04229fd4f89030bcea65ca964fc7ab018e8a6eaaa6e32eff9caf00ac943a7e01461caf02e8c9c8da01b47a7e4d2ad20b57a8de39b6f72aa1364b2894bfc9eca360d81fd96ed126877ba99bf7d7e9f7183e445bc431f7bd21eb2d272887839e6e0f17f5a1e08dd3f947d3ef69a051a63ed46b2b17294e92616b1281d35ce39c0715b6736388278e1c7bc750d5ef026c49511d759f66782a7b52fa1f2bd30fd581a996106ec5d594bdfdd134dc2c10c3ce6d961929795f50935e366dda83486c3cf806cb15a3c1b51c4d645e153df05d2565b8c9ff74acfdd8763c22ef70c91fd3e734c952e289b57f4275094f287e1186845be60496e00f61543639a830c3a63a8ecef7bccf1267950ce94594a7097a93adbe759db0e50883e6cdd3aea0259815a7bafd0ce23d4e40f3e0249bf2ce0f0f43f6017af0246fb0329ee5cf64c595e67d543174e32cebfb2c256bf44d9c4281c7df81e7c3016598b36bf8fd2b498bcfb93254ed0e5041be9b6f1cfc75cc1acf1085cf5797334fbe401c48d8dcadd02302cd445e267674af7cb9cbfca0f32b67e750387494e25213973083e146c2a01662805360253bf3687bbaf2eb0a9e041f659088e09ccac53abd7622a5fe9cb1d92feb658b4c3d291390c1b15871f4136efbd52238c7c423f8485da4034a3fcf4ce197a371b86056f9f56834c8b7ab2cffb9c16a1581600f298192303221304399b8bf521bb771c15cc8054c73dca58977488c2c84306b9e95848a78c57a410aad667352389c8bebe3fb60e69532895a4b81edf373a2483affe1f6626e3c763e776ef9c6dcb9103fb81d65e2dc9cd0395444ed1b61606245fde000c657d9853065f0c6830973f7507e34aff31e448adf6cabef4d5bfcceb09baa4cbb0a998d980d08ddfd5b221a570bf912093a47fb55e290fd9e835db381db009725d3294647723754afc4a9863b94b50679f7cc0970a575c177099b034a9a341a1b57c794940052a4f5b0ae91fab1c06901a19d225d7db86a9be0e18e68c11dd7d8688450920e8d747308e100948dc88b797a578388827b4d290ac256e323271391f4a9938c2e47fad046ca8588e9eb7cce1f0522c2b15c075a28e7fd57c0015a0cbecc24399dd7ba4c92eea6d71101debea5e042e9734c6ed8269d770ece804948192246c6b4049fb8bc974ced7cc02faa51a5ee16a47a6a59075d84335a6616f14a6f6daf1b605543139878fae17b7d868e4dffc03bf193aee75631f4e54895cbd08e4f9f9cbfa2d43429413f07f63cfaaf2b12be10373baa92cc9d0c3509175d9e2c4d6518101d7710e01d26480c6f2d647ddda0e9bf3fe9f2a7c29c9d4a172baa6bc3c199c543d06eda09b59ee126b2899c4f9194d1b2786c19eac9ec0179ecf68cb1ae9c30b5b6440f47e9a9ce11e2248c158dac3d8386a3b9ab1acfdd548ed82bafca56f415a6adf9df4caac797a51f377370e2d7bbc00d0c7d685a96fa67c09d46263e5bfe4e2496c52bc0427ede8b225f074e86694ffef48e88ab4fc8257003a056e4c26e272ccc17148c9a608bde7551b7238b1de2826a02a4164ac066f34aac37039a7702cc48e2152d969ef2d5c792009cd70c87221f3dbe0ffe8dbb90759af3bc27610dbc5331227d00afb4b2297ab6b887bc6def7a3949d54570292c35416ca46c6dd811316e42a55d94b8d944c2bc487ba97a12b3835105dd5f538bdc011c79aaf5c78168f71de24cb69971cf3a6999c6ba3fb9b78ca7176dc22d45d33259ef78b7efc1ad3eec127eddeef050cdb5cb31ea1218f36769f2c75dd08bcc751046f9f40fe3a9ac116a30b663c0417a59fa66016bf3676ad93e613e66f618679459a0ad50e294438326a228d059a4d1cda1e494f9090883dc102b399b5a51991bf8db7d5187018643b07e3e2f552f73497736fd9e5738e31aa64b37f34a3d077c5a4368b9e310ebbc420251530670dccd86992e30518f3d9159c9f5d96b15fc208b315c8787ff0b9a948a21640c0d6ad6915ea7d9234d9f0021a97656f26a7cfd637311cf40c7c1a90fb8d0ed709e3e56ebfb39a84a6323cebde044f9a84264c8f950ad049a52899b8f3b4f4a241a302ae8c9ea6e37b6c5cae2e0d5e2d2852cfa2dac43f42cf6bfcc0ec3e465a3145c82e9b9f4c5123b20b1e5425ff91c432112affc942ff930a1363ca7351f21905d9f3d1e6f200f124ef9c62bbe141cef02b37d7b6776f5cd606328962d7ba172e00733b6a932d4b7b8fba4fada0e265aef403ce64fcef26c604eea15a2606e8889b553212279660ce9cf1770be6a5b5b0babe76f47af7ee94e2a5aa83dd20d76617e27220a44fc963140b402f3c7a864cb07f4ae26d61b3b65c267ccc6eff21444b4528e556d3365bf80bd4a5f99e9953eea0bd06b5718e3f288255f976d053e8e6bf7eecc0a78b249384a2f33778e2ea6a6ad53d1d9b5aac75a5adedc41665ae8c7f6f25da141f178ad2f6c1fab7ecb8b7b9a0f85d1d91e1ec218fe094fbdfd1d3f74288daaaac5beb651304a0bd462bcf67fe9f1409ed378ad2cd1d6b8dfc39911c8f595dc9c7330d9eb993db39f3fdcf76c66a04fafd5997158f6646e1ced25185dc5661b01e260bae83b36b20fdd9b4815da7e356eef48738ec913fc158f3a88810c4abea0b9291f15064cf18c59cc9238d844c20e271ed91708b084e6c66603b213c750f8a19e3d6839dfe5896d545e90c3d5a1d8e4474d955b7ce009055bd02310192e09d1008b9ee527f8c0b6bfb7b2a98c758e28d34935e55e732d0d6e83c8313bc2fda4a164cc6bc5c8f256e0af865dcf6a87b4a7b57d03a82348e10ae53637c157b89b7fc8b38b9cba93b69823bc4d2b42ca340abb665f2dfdb98a681fe23e2675777f69da707f91ffd2a32a89ac35d640ae9de9c5b03375c79fe0d1940f310229531f0d3db7e64c7f174301855cf5ec149c414da5433553538d28834d77a1df35d23fccf27f6dcc762d2ce1503daf3ab4ce093dc8fb0b504887955b723d49bc9e4446b263db4609a27a014af3b70d264774913fb0359a7589c45314ba3ed7e662bfddf437d4dc703baae513614352eded8b7e1c78009467b13ca66183507239db29f9bf7372b4f98143d5a7b270117c6c17a6ece20775e6a1070346dd786ee6f4b4f4aaaf2272011afedd58335338f9cb37512042d20f064cc3fedf077a4181a1aad973057e341a96f655f00a073e9209284f668457b4903bce2a44c93c6177f8a248ae719760f06778f57e083a6bcd3c8f1b6c66848f3e6bc76740da034e81b58fbbe5d87ea15ee9f6ff96fdda9bd70727aa7a1812cdd610a39fbe3ad9a45ac0d0bba6da16ab51616a073faf4da680557fe82103842f9297faa6245dfe7460166c2debf51df4d03724bd117ed02b3823db978ef7cb60ab35e7de33159705803edd95c5643fb58783da7e656030144ded3d7c4b2841de32ca69539b62b7d9931862fbfa17d19326af9fa83641c7feab2240a9c89cac75b71f21be4dd7612d23986b89063f1851329804f623dc98b2bd16b08a03143be975332be40c33f6e848cbb264fc59395138a6c2a71a20af5770da28b2c73a3910656e53939e78d895e12cae63deeb14225cf2dba70e35b14e173173f92d968e3f59d292fc101498570932e1cf376cd3691e79bba4f203633018c9be3e6fa82eee526ad0205f1f2c1c0b4c0c7bc272544c477b8c03fbd9ba2af61a86a179fbcf883554c472c9d318cd4500039e57449f0de4deb1158036726ac047f9dec5cc7404683ee8489cfa242ae33e23c5fa9fa9e496b7a72bcbaf1d9deb4647588535133b39f4041cb40eb7195827ca16335aa8625b794d07c60b0e2f555ba27c1d24571ad80099e7a79eb9e413944d599bf1818c2767374c1053e6c9ecbc8885758d47487d96930b252b8a8b3306ebbcf405b4111f5052cfe38c9a2db0dadd7cb7da0c677c5d465043472e822cb5488fa1a8c2619da90f0b1299f954fddf1d1a83bee87cf3a0b46bb9b19d0ee005e034d90ac7a9c9a31c3e6c498f1414cb6a9e68c9fcdcd1be69357cf98a87eaaa9e7c3a439b75d1596ad608265eeaf4de2d85451abc224b2fb7a6c1a39aeb53752a7bd14c20292c811bab152d38c1c5a99103e10bc81ce5731d9ab35d5d02ae95f7c36bf86555e29795937ad7d01ec1fef463532ccff15d03caf591ead99aaf2c154d9c74520308ad951c3ccd3dbc46d3f505009d212661b9ab6ab38811f513e4e3bda8b9692529316710eb2471ab286e68bffd384d7e9ce0ee70bf81c4d3c718bfaea44e8de13351b69f1a4e4aa6fcc090719b942c76fcb25ae4ec0c5f2e0095b00ccc810119bc4e0df84c2ec46ccd3c52cd8f6d7364ff5ef2bcc4f0bcb00c5c674e6ff8784ae6cefce4fbbb906b2353a643127ee1db93591a79f36c260e1f60ddc3c6fba28d0865f52d599e155076e1cacb46cb83692c50b1ccc8261dbf77a44be500722f12cc208a519cb8cad8269866c06b0b2c3e1ba489e318bd7dbde59cc1d6908a7aade17efba8d92374b1d462485ec04a15b116ece9dafb108f8b60e133c2624aa2cc28f03e37a647bb10a04351f66e83c21c2d899f0cd89ddec566f8c7c60977359768fa99efaf8ef73aef36f5cf8f60fb1027c9535fbc42dffc7a8cc9e1ee7d94f7819078dac66c6c03640828b247a6a13a7a3ca902b45c1f8d5b4baa275b3faf380131dc3ab12f5bedfdf245bd4d0bd9243dff7a5f4226f750c504e47309c7925fa687a2af4b0518e49e9e5c75759fde5983a97cd37d6d2c2f7d4795a4c8635694a2c79b343a178f371b5ac36a352ca4fb97932cf68e4f1466f3105a249c1b72b8ef53e4fa4f53a6e85b79f26eb471fca84366fac18e58d1984d209570d768e1bc205ddc7ab66a203800f4db0176a9ba79fe1e909d84b10ee3e954594a9cc5414ac9f00107e0c5ad773da28c898d7af9a76218e03bac24fbdd9eec8197c6b9e342a17d2d9ce27548b579641bfaf89ffae7b0495cd19880a1bac4423e5b9a2e7695b7a72ee2dbd919dc3ee4b51d2dcb0c8ae7c266c127d10c05447a9ee7a7ea9ae1c34c392128d621f61d068c45f92ee47cad3bd210e578cf98f96782b962ce28d7bf8e7e9d6223553f1ac570a71bd74b99844a3765096c93f4c2718e7b5c5594fa06797f6067228f3d5ffff441b686176d0682d552fc87c07cd644ee00cc482a9e8bd10a89f6fc5e1289ecbf672c472cf9f5822febf4764258f5d693e0fcdab73ee24a491a8766442100b05d05ab0243b9e5c56330c2acff8dbb290f42f2a843d9fb986a7f817bdc26e940e4647bcc52e7a860d749d0fabf64c41cd35e56ab308ac6e59fd30be1be057e0b07e59b8de08a8087de8de5d122fca9ef16d6647757a8153bc8159689a0d8e3fcfdd3cdf9a5d0e2f2bcfff70f17029499c8053d596feb19237ac131d7e86842102b48a4de2b9dfa3517ac6995f7033446b99a852fb02bdf93c219c17139f2b38219984653b0e06a1846a128d41d71e9db5f980250737e022c904c32a345a03df6afabec2fee255e5243176d65dd83da7b802ffc26ca214a8816a83c43fc8c8f4482663df1912471328fb261a61c5d312affa7025361b3721964ac11168bf3601c18c0db6b337c4e02ab04ef5f07ce661832e25db98d60909ad3bca0a51946330cb4a59a795ac287413e5539eb98aa825237cd56bf53142c788d56c9e4cc986b88a4ca588e2bfd9af2016af0269be61720cabb49c975822006587a2d88d4d5dc4b13b1d3066b38bf11b6ab031718a2d42776a451c4b9318ddb80e094194ddacd9ba4cc7d2d4f69bcb57f4e9a18a5266dfaa91feb71533a91218077f5030e04aaafb1a1b8042aee3bf746f8cdccfbda47b414d456a8ec19b731bfbd6f6fd7bff1244d24248f5e4b9a86516e0421fcfe5220c2f56d00b966fe45a22c550cd890f6bd06ee762cc39561b639fb2b19810865d170054cbb2f184080c41ff5392fda7977e8d623d30f539d16f34c5107fe91baaf51b6da78ecd3a6d3157d3ce2d92cbdc68025437e68b9bd8e9420f173b624c4a888f72ff5888050be42222b912b7baaa82a644dc8b936ad80b4774637155476d8982cdeaad8e0ab384298337760b64b6f85ac3bcf5501ef71a18bc8bd24bf7248e726635badbf3ce2ac9b549644d1f4f36b4adb49270f3ca60fb84f4f61a354be8b68970893240d19a9cefcd3f7e2d05a88d64a79f32493478d40ecf46085e5b9a55993d86b74930a8183a4661e253b3d53b9f0a5642ed3e2c93948a88b73294618c99d3dd3bc2284e6936be615c1885eecc0ab48391a5aaede3b7e42333a540b064765a3e48b2e513578b1f95ac499dbffb45dc6bb6eb2b41ae82e18a4a760fef9ae58093643580a00b381ace0e779b97c101c8bfb43431e48d6e6cee654eec81650acf2d06be14a16ea3e3b1e9c108663f0a54144fcbfd8be02bd89ae6c4c9a011c670755b247bd4a6059cebf5606a556170a61ec5b0729ebee778bf7f5339b574b713807e5707f41760e8bd449f303ef479437d1f6338bbfcc7dab359ea79cf6a678639050191c69cd3e58164881b00901c520561e735e62b381295885cdd73b7bb79a900ec7cec5807b4bad28306d01f9eb364900d7be427cab688db42bf9dca62cf4f343599d2c61585ff79932495a896d3ae1f70e7af6f996a0d6d8040ac8e78cb9228f04cf65b2850d84b164a7b67cf038f7b24ef078efe6ea53416e9cf98a3f8babc1fe3e072899356dc3b6d1b21805383f755e7bb210ea55d901ff670b30db616044c337223b2ce4f12c91b57c2816e98e30ccf1ce7a26d41b962dddf7af43f3ddd4f9f5122fe336fa3cd511205cad3d1b0f2e3932b4f840d335857c7f59f77a094efa05d631570ede3f7e9e023694430e89a9ae6aa3dce0e75331d12c75a21e7c12c25fba64f3878febf964411ba046602c1e51c051957f342d1701e5039cc02e24c180b0d27619c01771f80b75ebbd5b6f12a7d8d01362ff8d99b6b4e5f54f6f5fab0ee2ef91a2a73701e1809b971b5fc6379655bb00ebab624e16a223c9843efec65f3ab345a6a2c5b71c19628c40c89d082b52245fea2bd156bc385a1ffed9abbbed0eec73172c93f397c3889f73050d34b43ae6bec538fef4e831c6f8396469f07ceaaff4ac7236253135d1dbef981de06d91ba2c49d42cdfd2cd3da74eb6f4111b305a0caff08bd1aec74473454f419e1ffb74fdce225dd5b3b2ce5aaeea6fa6d7812ac9897b66aeec1f606b3513edf3345c25740dec43358f13a78e6a355737c076bd248eaef9989c9a17b2f72cdb06195e41a35da29296794f4aefebebed53cce9c9e3cdbc1fdb700539e7df23c8ea8a7e63049a736c174ef7ee74b626402083b0ddeecb4d1d230019ab2a4335e31aef448bcaa0cd853e24b48730c59ef4249240b1f3a477520da1d51868b38f988fe58efa63b0cc2a18d7b50e33290a01a18358798d0467138c9c635f7000dceebbcf78099a91e31e2758bee4767291c51ab4df84a814289c52fd521728c5bea07b9b90a0baa0aef90cf21f7f09bca2891c2b56550a31c47846c05ecbc3770394190f469aa7e2c218a1bbb00fb53df9ac3faf85534362fa4d58f6332ef8a3f40a33ccc09af5ef33584178b9f7e88f96790c4bf15c75357bfcfe0ce8906a5b2569900f6b09d3a55277960991052159751c6e200961e226ce9674194cbe9d42458efecbb2ec7eda7f0b91f4466a7b88aa7183b3b3c1daa85482c50453548745fa1a3d885d52ee3a47e3aa062ca2b1007c75dc0aa5678b8396fee1b0973e1d89e3c9f64a296724e3b46b520c5f10f4b346bc0eeee5c3975b8e3f726195c22ec47a31453b8bd41d09f728a10c53983d60b6f42f9ec0d3f0f162891384526aecd963ff9c1fbd2175c7f78275a8d723b25622fe90981783231a8806b24383bc3158d330fbedc58f385f2a2269c5199b2f305e615ee9acf4b05af86f7d27e07e800dba1ed49173a4060e4154e5b0dfab29f324778fd9074aa3c14e3382e7ba923f0a944f8c6fcc478d5537285b7676a5e29d94d14d013ee4c83de17ed468fea4aeba4cf113961d8d190eb6448323bc2f88eb4df426429ec06a53b7567089736acc638bf11d1dfb8a978ce58fe57b3e5ff98551047acada1deea7053796a9b70599c8f6b47937ec9ce6aa2bd225319b8e5ffca2baaa92b45368baf0836c0d9b34e6bb01ee83a1e6411391e00252455cdbc2d76d7b6520e682835ffb45261fa81d36f29d5832e278a5db1d54bed7779ceab56d19a9a9ebfe308b8e4b99ae18029b04c4e0a0729414a767f0359aa4797e47f4389cc4d4dd6282e35d7305bf640d89bc6dd36f4c3945a64ad8eadc322f463d88973d80aa2a285991e47d074276e8d0420773f853f0830370c547c701425370cc6a821002d3d4a0603f03ef92f87d3c5ac98652d5252c41bec7d71a56936e110391c747b67439775ca870f71d28143fe07940febbec86212c39a43aab108eaea64c780cde4f7663aaef71c5e9611b787cd25369a13d7f1dbe3698249a842c600030b46e9cb244edfc06265588d58dceaeede5b77014f41bf16cc876a73538cb08bafdca1cbf3c87bf570816f145e47f154f2c5865615fbd4555ab29fb018f1b191b8284e381657a1228c106623b2bb9f3a669bc10ddab8bccf78f6b2694badb376a7061b3dc93dad4777183a6201e0f6bbe937a7930dbabd548f8b1202570ddf5cde375f03da7cbe38a997e846d115e50cbde7cdecbdd0b50aea9a04ed8b6eca2f32298fba91cd636970e3841839d158cb72bc5f10e32862d0547acbf6a98b3974062d011eee293954c09787c9d009e1cf89c0150a1298495cd62726c33c04ed0b9630851d17a3496567233318e38133e3c0e3a3329f0ce14c45c64e1caab62d3f12760c41c4e32a72cd71192f61af7d01c1742d737920482103251fa18166ce011dff410176bfbd000b24040932a3bf23b05d132a11025bccd8a9a9d7462e84b9ff5b834e47c5f5c22717b88a6b86f5a3be6e3d13ca759a46f9fa869fa0a4a792d9acbeec353835aba9845f08e0a4310ac42175b7a973ed7741effee88999a24be64ff619ff41474c34da369ab6708531bc9f0f9a9abac3d550382e5902766c3937212a8c488939c98e58afe9d0177b51e855a5376203b70ebd2ea9acb2d37010037574817168d65e43f656e0e20180f85cf2adf66d836f2f8ec0ba0b08ab59dfcd491ecf58143340ecb92f46a0ad50ba74ad8e9f817af5f646f908940c9f9ad1b69c2fc3438d19d159596a320c5e17ea20f0fa0f6f2e53988df8ae4bcd459888c20a41165ae0651da399faf5e30695f50e001f3ae530404630d4686c82a14657d5c7fd3219316f19fb72039c081af7d212437d87f2c963252819837b4777f797a0a265437880c33467b12c7fd2de9493f36b00970a146b6dc1cf543ba40b75a08cdace30833d8792466c288a0dd13cbc3eae2961afe206e46b38cd3d69577b04982bce4351bfbb49c48f378d8c4ed16fd3763b171e1cc1dcdfaa8f4d9e1c83a1db9dbbc82acb705f928073b5bdd582a12c6e4c9dfafa700112d25f3f61d6e37b40dfc37f4955985a3cd45f648703b8d65a26a51b438c46b7849a8d51c8b51b2d563b4a42ceddec89a79251d711c95b763627a4de6bb84a4a44745b03a89f6f3319b46aba51c42b5dbc2419e662869805a1eb47b401eafc27f4288b9c38af9cc1e527c61fafbdd48e1e13cb39f244c6c092621c3a13abe92e09a78b78a38432005217c1b3f90df83691507647e9bf34401e100f2d63ca9a9aa38306e423915e0b47ba8ea867531f05a5776b6a434a6b13b6ab7c7611f0fc63a9a5e704b3807f9f1ea20c97553b933ef9360647131ccc66a80d290840c17f0e8d0093578d7104a81299295465c6165566fa97650c20f42b91e76f99283cf8eedc3bbc3df884ee176e8ed4f34c35c16f89e0b015c9d92402ddd2f9c34b5ad900414f8fbbff58867c3cdfbcdf8c9624fe619129518ff29da7cb0e81d62846f596cea3774b2638e605853ec89e78cdb7cc3acacedd474a11490b08fdabd62daed962c2e82c041a7615cd6290bd1946b867b05b38d2692c686b7514a5de35286ebdadcdb6833be56a1244466de112630b34e78ea9378cc6b9fe83556ae55f151dba489dd5d172b1d988a9f6e6a30c8eca7aeb7cb18595037c1f984bb80212ea6ace1ee68e45eb8a44492a8369dc7f7e4d9735bf8257494fbae5de4b8b5d81ac70af659f7ba347c5bc1a7f49546c4c85ff574f79b531a2747f2f5b89dff6058f1a79d9e237b420aa836165761634caeb9a1f89ddac518b4ced174eb405bcd20179a27e02199d095466bcfadb53fd3a328b84831c299f351c82f97b8fe1e0af354cbce4c1c9dcbdadcaed3aec6edd12126aa1ab632ce6b9205899a24c6daca81fdd4e46320c245218774abcd83dc4ef2f2df9d31d06389b93a0aa4943e34f1ea928e17a5db032a43640cd7e8b417adc207d69a4b93a676ab8bcea39debdad5c480d9727c0c5c98d0c53b722b2e3ecae953b8be8fc74433cb5e3551fd3a32be523b6cf4dbb412ea04c46f4bc58304388028a85ca8c56a9b7d62407fa6da2f720ed090676c37c2d4bd8e4258e9e68893407ced9c4f5ffbaa8ca38ca4b2569379e60c5626c22e1e9780f0ff738e8f6649e17db8a7e8063524c21b973e85897e758cbcbc1d12ee341fac4278d10f7bf24d090682f498b009c01e1d9c1e35f5824a22f6640fe3db2950e19667d429e017dac1261ee7e16f00bddbb3e4bf24065046ed6de4b6398b841ba14bcc688a5e43de4f1fbdc5874487c02d7376a9992cc648edfe836a0237cc1a94cb0a57030985f90cb85fd58508b3f6408c4973f6329a22cb2db98106fab7263fe3055523cbcf3e131fa4db2e33bdd627f71f4dc348a39bc62edc69a159c5e213ba2ccc3686b6860c1caf58b37b14c3b0a6fde0b91c5ec1295b2805e2196672fdbdbbbca60b1d3758ae3eab22e907fc0a38b8c0bb9849188eaa037536f023979d3e32276a5a1df8366f103fcf313515685e14d9e0ec01c9186bd5caa15fca6f6c0829b519f9c2330c9c10b69205d6ddf5b9358eb46cf3e4aec0aa3cb5955d2febea9e192699b48d3e0656a6b3d17b9cbfd36ecf0444e5cb60e2dc56a0df38b881dd3fd79d43c5ceda6464153718946788779d4f00d0c1170e61b2ec89e1a03ef4d731b8455b7d08e83f5c59ce632b3d1a8bc66ed082b4a5c05c222fc9eacb56758a1f9d7b2dfa65643caa22865c41a48d2896b98399b2231e50ae308de6d5793dc01e30867da64a24560604479703443d33821abd8e3cedf60cb8dd044a9d8fde38beea055e200b6a543a591df17f31b0b843ed73d7e27e9c170ccf529a3664b5cd7caae5817b1af040df60bb5b5b62b4edd269b79f22b79d1198cd50932e51a5b95bb8530023682c9efada2285180b1077b38c55070443d741df943b29ff35051d35213459e3496a8c053ff467167f6ad4f09cb8f024dd6569da020c31484aebbc301aac5ef240fac122f744ed532f8be23fa6c7e6679e050b2478a54b4851b2614dfa67e914f5af65aeaced239e72fae84a4c5753977fbf2dcd6b8f1a552c06f18127d0a47faec39728a916be3f6713c19959dc708e3aab2c01421098d99324605e01f3ca0d47eacdf8141aebcf1f2aebed35c5262594f4412651db8f80d0ef69bb1812f2a2a63cb6079f9569ea0679d233f562bc779100fde81d44ce9533a7ec9103550c54d8041aac15b4f28f58ec5fbe1d2a106a6d8312e2657255cdf00626908352f1fc796553dad6bbd2040de7d561b2990a61cfae11e8c87be11914ef37e92f8cb6cd94ad07a9f80b70ae2121007b65ffdf47449b4774328571d73912109728c2d29105399ad7c45fe6229631ad3200165fe719a3691ae1a0abc5819028788a8d650367c7f46c3c8891c9ddf33f5daeb04e726a934ac6a6852171fee7869991d1b8177bde84305dac82ec15b96a1c3f574c44a88fd9a6426ae397182aebf069f2770adf7d474fdb5ccf1541ab0a88fe8c937fccd5a541f4698cfe7f98a79892c7055ce14319b76a05cb6ceffe5f4041aebf94108bf12a9f840b8175534c1a2010939022b098cb8857112c238611d086fa56eb0679db0a703f51faa25c14096a11d23342b9e75b06537128d7f2ca9f945c7e9286f29ca0817afc4ceac1e0da755721d9c4cb2b53a65355742c2be012b04fec49563e4af0842ccaeb4a5497b7727b48d171c2efdc4dfb1bf20025ef960a03e8965508ea690899adcc7cd004df26f809c71099db714352cfefe0655b2eb5737e0625ba23cc15d391c280c088852d7abcf4607adbced86fb91094deac16075565ab6902a8c24bbc3cb78d4967ed4df2e1671d7abb35d445b132da47f1c5955b60de9ed302794aa50809ddf64f4987eaf3b5e8e2edf14f726f6f75ea9b18397aaee000a0e3e22107b9ab4bc85c41c113e90b3ee2a69925a9f3ecf1476f41fba92fee89860bc65c6c02c18007fe35d4f6f16d3516b3792e6a32ae3e4d9686525429a2912e01b7ea05b24f2729f8ce8e2a98d50151cfe1f20c61749804b84b15209ec8cd46c7bf359325f627bd138b12bd7bf7776fb82e76a93658f51ff6b1e5bc17b679c184b2c4a26695a56fdbc054638ab0433029fe9529f4f132dbed59c93047e8af2c580078907dbeaff0cc1388a8e473aad10fb9426111fb7abc08ef6a82ff5352afc5a257d2ba218f518af2cab464d74fc89d60a797a68a28d35c7e63dd91c57a7ded075353d340f424bfcfbcea27dda9f761fbd0c8462af1829136eadacdae587ff401a31d367d3e36e6d7befb13d181bb8f0f2d65b73dba6e319053bd0c9c672839129fc4824c5e55495f145944e402e35f5a064db008685e37dbbb6d5b743290b3fe63e2406119938c84791ba7179563e28feca423281f18d90f1d53b568cc51131e621db66b8236f5158a03c3145232055b5c05852d5ba835c1c98b9dc16245b26a370468cb4953d4b336f721ee7f51e80e26257358c4f9c805e11a6677c1d03976774961faf87d4c89f0265038668ab11ca940fc1fccc1a89520ac68fdb2b0e63c3fa6b1fb1e9ce435f35ad06d52e970d0872b599a6e3f1b95a29ab02660f468df366d3faa3956bd2363ae9ee843942fd8ff856b8a9016e920f1a5dbe74f1f350c5177396cedad919a343465126a7ddd0435190d7da0f32285b9dcf3194663b1a5a0f096974ce1f0cb282248e22f173acd4a3b65b535a18898f4e9d7af614b5f84cad7f0a3bdc82e39bab87b1d58868211c7b3e9882224b2b7a128a81cabd21b281f65c2de53111fa8c45d5b670bca4fbf5d5f82240e4fbfcd72a11a826b3434d938167cf54d15b75b53af7cbd08bda31f9c7e87690c632e81878f4c1d5038f9f706d4e304c7c88af7757901ac111ff0b311f1c69c3576546fffd1a61b97ee2c463f293c43a3ce02e1ed7e922d6091f9ebd8ea1f420625ebac742276ce5d63b3f87271b92710ee1ec66be6098c7319ad483b9cbadec1b4e43c7a4942ab747d0f649fc1822724c00b5197a6ab9d4c6e90a52c9aaf33be0642ea4a10fcf2c0958e06a9be175ef7db77bba795bfbe88b66c2ef174f52dc3d408eb293fe78fe2b215e115ccbff938f97987d480780d095e856302a5e0e56c6dc3617b3caa5ced7878f5daab0a2dd37f5b8067158ed132364fb6619493eac88676e4f5bd1bc0f9841edf7fe7793125dae1ad10051ec966e23302da6da7a144fa37fe4863a7a9b22fd5aeb63bc14635e4892412d045190c88dc6254b4eab6fe20b7075646e70ea7225ec04f1d3c809e49789f5c24fec1cdfaad294d8c02ccb3a31094733dfa521cdb5d17a6796547214296b1df8536da20e27eb41f2a37fda8ff95bd1595f82b2bb7186dd011ed37d7eeabeb950f4880b573a0d22b206526992389671948d2cc13553eb52bfdc12031bfbf55646e40411ea1bbc3588a5de9f11d14fb9cc8d068d4152e3179f3241825c6eb21c3ca394abb50225c668471b564f9cf7bfbc93b1bf0774a894ab82413b5c826bdc9545ffaa0769d285bdf9e2e5264ce0e17756c2c8a38548bb774d4421ae53f62746fa484aedddff4ab3c4ba88d93a0594c0ab8b32f4152d95a8e0ca7b5b53a55cd04e900a9727c72bd13783f8a14f9374afa11bd28cdf018dad62ff79a895824abb4eb728d4555938016b360d1288ae6870883654623ea74a0de8393db44f92e90d4778634976c172699f7d2d368d3d81b7489f7e4f962ada17c785b699e5c4ca7fbbb9d12b004aa5dd92d31ebd47c6d348648df054abe5e11b7e04f0bc0aff5fe064eea3f7e3cc10c1b66fac8c875c009fc360ec60e6df740df1182e331ac7f8d42aaec0453e4b8a4a06643650ad7fc2baf4ca88bc00f9bc5af1164b5429e7797b0e06ea6bb1f40a54704abe7982fce53d4cfa62713a0f0e7c714aa21fbef3c8d1fbfa7d9a2f6b93850807062707ba915ab0a0fe7cf9a109a93f0e1a279226665fba8b6670a3d6b82c6a92a25d3b077ffb69b92f16681330459817db990a0bca08cf57eb0e5612c6968770f5a3ab9055cd9fdce48f28628bbd80565eefbd4457845c40bf4f8e1fa343a883af76c05d72d3dce90c0208fdc5e57818e503ea350470676f26839ef13225b216497596a65ad5d411e58109fce109e10eab2535003332588690e54e330f29374d1db30fc5438d0552361cca3423ab71377662d27fd0998dfc08b708b46a168ee2164d02fe6832f7e07521197a1179803b382edb4be37247423db9ceded3a9e9bc65c3fb85a008299d4e91561bf9dc26bbb7a1e9f114542ca435c00591f9569bcc679364bc2b057d841d97cbd420b8fe0845dce3215679bd1e0005e2680c13835a3ca3da9b6be0e9250ff28f6b8d8ff852a160ba6092f395e8a62fca7026877b0928dfa40e24cbc86358dbd886e8a2fa47ca85e38f8d1655273327aa0a83c6c201a6568a5e376ef04ff47abd39f7e8fb9e08a2b229d3fde4560b3fa2a24cda7881a8738b36c0dccedf4c948d1b9c96fe922b055dd01009cea2f402b671eee27fe080ae7aee3e12b1c8a4f29bddb4e6339dc3fb240afba42671b01d0face744d00b7a2d15b98e3c7b712453f7a28f93d10be3a354e19ae789a86797c20836ddff1abe305caef7bf789cfdd80f4697bee73e34378cb91f847acd257e91d3fc488aa65f651c8af5403babc1dd33bbb8003235e2818bebb755bc21e391abb4a078fc97565238a576117620598278d317ba4e9b52384161c4c854e8c6e6bfb331cece1a151dc005a81e471701b22e371ac15b6061c47b12c1a0bbe075985362658125265e343ae67daf75db9329b5c1dcb0019a359203989a978132ff317492df56503918d27e417b19fa32c144443720962c5ce23c9347d2637c2d527987a49baf3b42545d2a04d5908413fee8905e27425e02b3e01a6b5b83cb79b7c77d6f0c7c5a0fcc6c6f4d60ae599f9188036ecc6b2b33c0059c122be2371b6f161c27da79dcdbada298e5d7fd7da416617ffc188545763642d92805607b35c66db887db24305ca113782253a82ad5dcf2e8a2cc167f65a84769f5aa3876fa0326a44528a8458651f114efac350a536ef6dc2de8857fb27a9ecf50a74b8c23ad288dacbd4c9f5d83d7f7568332b80d8b95aefbdef0163367044ffb427d4c66136bde83e1aa70d6b1b8bd33ccf868c226e97e0bb4c5a2c45ed814cc0276798cc9b607bf39e3e279d5b04fc963d90d5f5a23867ae990973b5ee93b9abf7024d7308d26e0714bfb998e7f5a8b160d30f4ef420113a5aeb4e5acef54612da1d4e47d561610ea67c0de323f869942314adc7476abc474fe447ef4cbb3c8e3d1fb60380ccefd5106e457b7c3ff6f62ad5cc13912e2289ddd2078a7c1fa915b27c19927e35fd2b371a7caaa660c81ddc996ccebaeb40aadf37623a8b6641e0b121ca35d7726e78b43c9531dbc4687decb1b8f7701029387d3928c3cf888e344aa6699e543641e157067b58271acc60f12225452ac30c7e6ab1bd57cc2943351f95dac8d75136dbbc154a10c09e85f6104457586458d880f2e12b0fb6dd4679219e004441fa10c06cfca46c8fc53978fb13c3ddb14c377ec0f79a77e33efc6751f5fcda570a6d6d875c795508a2152373224fdc6c40dbc9de9e3da00a3e94d55bc2d65872fb922f5bafe9dde78436aae4bf120c15d0613a0da4037753a4dab7bf001ff11713ae50f9d0cc2636fd30be972a9db43e358e76950ed49223f81199ced8e640a6e975f69f2e420534efdfb32c8c1549017bad702059f33c652f6bc72aac3c45c9132d77438b858b6927106767ba807a1b8ce3aed398e2e85bf365d4dd980e08235831a3c738f01097e00cfcb5296eecc56f8d32a1b049d41df4cfee9b64e9fea6077a7b623828bc342d7145aef0bec654ce32c82f231a91a1f2782bc5449fb0f3de75878c8e90849eb31039b870699c051ca0079675e0fa563bc4a1391335864211d9f988e8d88c2106b461e727ff93217508743f276acd3cbeee2699dcbfc644e4f49cab4a02d1a057dfc95493a0425e0a03cff10cecdd2b185346ff730623909c953364ea12042206fe2d08b172083f6b2665ce73143ff65383497e86a3d684275f69c00244e8ce38866f2490cd482c78692f429984fd4b0a0367f438bb952878d8c250fedbbf052c73647142bdad73c75fb0726d0bbc757deb830b7c8f2e9e112851dd4f982c0c401f4a1c3b5bac77806cc040fba7376d6a716159903a8ed23e4aa689f927d0324c87d260e6d604dde02f870154f0665e007ae4ce8a466644212e1f6c3460a5fa3afc6bde74e0050eced8869537be2f87202ed5e83f3567edef964b26b9ab85b02a53b190079c3031ab13abbe06c85fe913ef26c7fe024c97a968eed35e5bf597c6a642295ea64db98573668fd714cbcf3873a28eedea35745f520f622357ffc006be2d552b0b7c0c2bf26226f65a2c43c9245a9e88250bcae56c819c13b7921aa8da02dc4a99187e2d2e7ced35b7a258035c92143eb6885689dd0cff054ead598261cbaff8b9e501cb0696bd4a488b23914ea561a8a0c520dab1327597323c182d6cec4d5618a3bb76e4c9ff3abdfe99f6e7f30d0c1510c3fe93ea36f80edc328cce0d24a4863b65a01db8e0fe989b1ebd2fbdb1a7a34afac93ab54ac903f40354c35ee76a59be8294e83b1ba598ef876719e7c7e5962d1244944712da00baad1d3ee373a4ba3cb9a9063d7a890813330c83d99241bd3bcd6939f80f82f0228779c2c1cfbb9fff5b93481dc01f0952e5c47efd9c1785888774a402a55ba22d9fb6ec31ee61b5e91c0fa05724996c3020c0e54553fc29d7575a5456460fa88727d10965d85f51f9b1c9efb11a2d28e9088a0897ec3481ec50867b7164748619c526095ffb2c8b5c9db1cad826ecb153a92f589593818691dffc43235931fe88b7c65a930da26de0089bef9bd5b1798e0bd4ffc2711e989d176a22603372f1a5e99be7739623bb2e9faa1396ddb87df8cb6cf642c6a8f6eedaf523fecf81861f626f73d5d590c83e83300cfb7ae15945e2355d542a23ce4b70c9ee31356e427c799df7b7d7d312f637af54ef5c457e7b2274fe38c80ac9125800d1fe0f39af252ae1205fa620ca85d3520fb9f0d31d39b2e1ac9f8b070245e34c17ca6c1789151f75258313fc5323c60cf96c13e8993b9f4111de8341d2a9316109c958de4ac58d80e680b80a622d917d6be815d22648bef8489430fad4992d7381a5d019a69b6d788bd3c1a10b12784e052a1c260eb94faa7f35451985166450018cb4fc27e78d22b7b59d1589b7b69c2c530fa5d8b8336280de51af3f3ea014724c40684fdf6f70428edb0f0ad9b66b209098390e73ad4cdf6372f1c54ac272a85bedfcd217f1ef34d82b0ea6dfcea1e9020372b13ad03bce4c517cd41a2fbb5bf1cf387870281f8fb458790640820fa76ac325bd6186e44c22a3b24e164d3bf0b54db07b0f05d68dd65ff36096271bee458165b3650e1592938721849f47873ef2526416646f58f2285ff3823f8c4d845334940e60b912b9a3f770b9257662033efed2ee49d8a6b7e13fdff8ce33c64b956a10bb270fe48a018a3ace263357fa4e4ecdfce0edac8d23f43682ea2553e3e61237adbb69e071cda5a2430ae662b3a300c874323beb170103ea2d4d84ba5aa7e92fb3f73f443477d10b2800dab7b0ab8a3ee0acb4a0b454f374dccc96f4b343942eae2720625f7e6a7743f937100b76014d3571e1aa4e1b9164f50705bbaa98a3e4e4d1f8fb860a001d6a202fc348b70afc13a1cf2ef3b3c7166c3427e8decc2dceaaa9a1c1a562315f26ba79ffac35844cb43649218a153b07a1b54d4ad1f2af30f55eb36a908b0797d4b8808e086f7fe3681773ff182a454ba54b3eddfffa10a09786a5043f24228af4577a58c84c63e03baa8ae8200e734dacec70c52435aaa5967866e0a6490087cd781b66c36a3ec9a940544be606965e6b2743efe6354144f385a97a4295febfc7702482ea51f03bbd6647fa0a9ee6e88e70b00520c8f15db7992edf26387f7d58afcd645815097dc8dca12d1b6b89b5d4b31a3508018986315c79f601b8b4b705903f3f000087bf0ba5a04377ec43113ec2dfbb5e48ec83589f7fba8757144956ddae39d1a3b63df145813a4ef4bd51ac3c2ade8bab0d51fdf89d461bb2be615a4e0ce9caafb54ba1687976d9323026e58f05c23b84963ef4cc15968975789b7fd76986ed152bd89c00276070481159e4c1a21a683e23cb6530f2a1349c4ee4be7543874834bab50e9f68cfdf044f5da96dfef17a49a6d242548b3ceb70c3fa4f879fc18668a7922862dea14ec16c30c89a27a3e179f1c1d83553e5bb588877d680aa89228cdeac4341999e90052c5f3600f978c5beb99cf08a09ca9be90122a1ba12756bd641613457a7b58c1dc996ef506dd7e6085c55efc46afdc58743683bcb9365d246b988650167a528faacc7c36c9685560e1ddfb4786d616759fb4b4aa8931f69dae3a7712abd7477149492ac7510e64eea5f06bd3c09074a92e8afa31b474a9f8ac0b0f74472f04a57aba8e9c1ff6b5e28f59020049e512b3078c26672a986ebba0e1ec530b45e435d76df32812f2b49ba5db9199e8b4a66a32edca5caa724408684dd36c0ece34b855bd8441d8ee7a4ddc023c20d5814ac2e4abafc2ed505ffe13c1bc1847f05216fa338f277ad30a69f6f6162d1bcf3a33010f3c628d1949ffde5efd0c704cb715922ca476aceae03820cff7f6eedd8f69b678951dd8ae2aa99b118ee1fb1aeadd468b0686a18ad4904c48665015a188a92a66a084dcf750cc1df7d2821b55af582502740519cc435a695ae452bb64386ebbd00f6a7ced2f6209b3d420559c1fd9bd5755a96af0a16033fde83c2e98dd1542a5ec249bccf312da0b5670dc0c3bbb4ae81fe418f2534e6564a585c1460600da1b983076e62d9e4715eb4988934110867e6080e028ffa7b1d0fbbc56278390b25df2334eee5ff8fa4c30138e51411803c3dc36299357989d673619ddd89d109ac77d64a4f6bfc7dceb10ebbbc7f19b3ec5fe7eae1b00d9b4b26776b95884ebb6a92b5129abb0a7d342a1164b11e898319a8cb0a927ef9c97cdae5b0f6124dbb740713aa3a0775961d1ad2ede46fdad7ef86df46b2d75e83e2f9e69fc407569b05b3e25e75e1d748b492308a1881463d3a104e12b56beabe5ea567cf204ddce26e7c674e482232233c5b801ca7087a8fc311c822249e7b78ffec2572e3261b1f935cf5edd5305368de86856623bd7264dbd535edc9a99713b1eb6bf10d2528683b9a3f5a41170a985ba5af7c9995466862e2813927bf7168a4ec24078028a447fd386362e17c5470981c6014af3c3eb33ecf2b8787065fa8a5007ac7ce7c7b8fc1587ac9fbe849be8c8a6f6288f3cdced292b7c2ab4713b4b46c3e4c7aa788258e3b568a70973c420e335ddf3559cc274c27d66596d108ffa6c2b6c5494286c41e5378d06c515923a4a49067bb1762636f17cb883a934e724710e3033ead2b5367620f3ae1ee23ad55db3d91390c47dc0352a34d95b9bd3b6866074ca6ac01e52c5f61253a1d7f6df5991aabb0f3d5f7613c69c57f306f55b94ed934461dc19a776f3864e7af61ed338fe1852c755e7e2c129584a43c17ead8051742c0355d04bf4d8625f83acf18aaef66d56e3f26cdda40fe5ec42374fc93a9fe180060ea12f8f2cbbddefede57553ed663b07d177d2a8097893e42d7f658ae4f3ed3148bc4af6aeb02adef87fc26a2ae2d4a15a3df32d7d4b601899f8a09db6b06043ef1be8e17a9dba11870f6807a5cf362fc49077bee22ca7b40668a94a57540aa74fc591d834e3ed8e2707e82088a65f3ac9e34a8e88ceb8a46d3206328a826d77ef70c8ae4507f3579898fb80f1a19548cd861bc41dd075ea803460c72a1a25777aa58a44ebf3a000fdca6f3c8fb897d86d4ece55875d8b463180ae9a59b016bfcb1b6dcf87b5312ea548ab7975e1414d2a6925c63e84b1b0f1c9f6a581aa59f685ce5e861bc32b34c51f9f545c6ce9b0a2b2e7d029afcf26d25706367ede1c8cf65e059e89fa0b628568f6010f90bff65a13d600f515fc1de88e6e28faa0b5d8188a4d7d26922442dd594cf08323c8b7828322eb8c39454d7118ef24816fd3c6f332eccdd0afa05abf2bfbac7c456ce1f1998539f8f30af20abb0d6687bbb87c50b3a7cce021fb87a9b7876097006c9300ecf0f2af89a03d9b25463e654800797e9e4aa4c2d031d6ffc2367b25ef44a5ffb427895d690654cde8288fb476d8f4dad72e9e066591dd3e3e20433b5ec664e5809f468a05c22c98b52dfb6edb536cfb6d31a8da3499fa4f7c182fc268e183b1cb2f679cf39f2be8a594acb8027ae8294188c7e9610800ccf47e55c0d5fa83bebebbdebf0b7d7b207be7dff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eecc3d820fe1e3dc55ba705da048702f82f9d4a0312cbdebffdc348ba74710d038ce5bc498dc98612305ba0733ea46dc71df7bbf7e715bec87d2642225b41ac6374175933003a53af75404b2e97cf1bbe9d2b2070c42a3bd4d835b0ce400cf6e0674436e55d7d36b6954457f71674707c1ddb4978c18ea583ba800d8797b4202eac2c28fb7275ce10cdb7c17647c5ad66220addaa92523b1606ac404c43cb30a123859144b136096422dd91e1daf518999dd2a2dc5a2a2e89a7a680ed7447ad2560031888a4cbf34d778bd7a14496543419e9d576107bb8a6d0f38146ba5348d11e5d3487c6b5dc237013a0937ae53f7daad2851a29f2edfd10d58b97d8f645aada494051d05f50847e4c9756bfb58f6113169b937f287affcbdd4528034097b777193cd7b688ab133a8a789f28a192f97737b818c69033d8ee81c3c60888fc6bb1ade74f1a1b496a020d39c111acaae70e1d8ba1da0ef645e281023a0e053658f889dfb840eb0dca11d0d955539ab8f434c20cb9572972d3818a12f3166ba8b2c1298cec5219d8820ba9604346304160c57e7758f99d1ce1e734f1d27e870bf0b3d047f18591f1df09bf2c74d5ce1e450ba2ae906d9f9c08bf085564ece1fea0874b57842a25a69ed44f18b7b475f4afcab8437cd922423b492b5969c6aa67f7f38cc7e6c1eba875acffc0a5a8d484ef96880fa2f118b179b949517a11a2394e4ac226be45ebee8d4175f0ba537ae709c567c259a6907d51c12bb5c45e39f452f5e4d932f6f241005d3f53a0ad837293c92604b60e5ca813d2fe0db4543fd1c69bf32cda8b331fe0b97d00b5730a100fb2f55be793f93bd74e767a7141e87d31f232c64ed3c51a9f6e4ae68f0fccd649c54c0b652bd449817a1dbaf7badb73c2d8907f3b4c66d9dbc75a3fb44c215331be9e6dfa5de2e10430a2758ad4c0632cedb6b5c27b84478557247f818277c61487dd167d7c9eef13091122ebb86eb2b6af86c81792e52d75cc8ccf8abe531221848f25dea133cbe6922bffc541a0e5c783022abfd4fd0b8c129f0521c8ac5c5d8cd1b0e1538e99957f0f1fa73fb361966fbecd9c1eb8276ffe989d26053a83133037b5983ae6273cb08e2aa39724e0af01e14dc3eff5df3c42b060c9b82219979c1134803ccf3bf5a77a324167b7ba92d25cf43ab1a6f813d4db5710a9528a84793f439b72e1f5a1970605ccad8a6812b627fc7213c70163c8f1a5b73ff2fe5512af9d2919f532066284c441873245e86d31479f576a30f7a9e9ff97db3c09fec1689135f76b1fd41c61a9d07690beba01bf335174d830060a9bf9fe51221686a1030255b3b38b07e1b3aa085aca95937e0de5b0adc1f425d1425b2dd1a7901e306cd99fff5fc0624fec681e9c8d71e529109b39cc02fe7507cf3956e9389bfc2ebd7638eb4168f088f5f2293f660751855d1b31cffad5ef5ae537a672c58bb7bebbafdd668cbd08973c4a6bfb09104600a037e20a359c3776fd9ea49758479f9bbb801cf0d6137a96d0c19538e5762c13597364b4f18852f591c2c46a657c3a2fabf70634f99d0946f0b7e815b9f5baf9854c8af223777c2f35244d2fd783ed6a3138d5cb710ea7f25230a6800ba4a7ae8c5af79c6af5a378fd2dc13b4bbef251d5281a1511387a7b360fdd4817aacdc459417b993e5907818d5f6f199d19a309717ed28c84b7b475c08ac58dcae5d11bbcfe8ecb7c416914187d1a592f6aed166cbd42ca3c6fa7b5e4971e2becdc996f72aec54922ad8f3833cc22a1fc2e608e4e99a324d39d61f94d5346b9d546ec55bcc51abd7eee3cf0491bd090164bd4fc51a3ac912393004fbd0c3e200d088be76d45e4137347015fdb544758e5664c92e2b176ce5ef3f946a361a71910bc4de46fa65f412e47f17d316fc2f09b031a3b695ec22f794e99965f23c5105440e7aa53d2cba9003e5843961f96425e499574045069f2f4ebd7f4c94720ca1b75b5e3580abcfa5d3fd50a0e0e89f75adaf8633d5c004432483989c27af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a8fe197bc3cc8dd9c7c3cc0a9ded30acd74d0d3d8d64ad079bfd1492f3a5e3a364e35c2efd1963100fcff813c6a9c106a2a2dde899c92618b1043e941c6c1e6201c01cf31ea39a6100c1071ffb5bf006669c925656e1c65b660cdba055f4965e54cceccb8be0f8b2a2eefb9f3f1656190bafaf125ef7bc61271dcb22a0352edadca5090523cb3469c17750653844eb57e84417e172d8f80ed17740081f6a7041300c32838b5955a3d5f720a979a2df7caf6ee9392807b14db6935b312387d0fdcd4ca21aafd8ceb8ce671b697a08a83bdcf2ff071d1b182aa1f739a2116390b8af87dc489f774747df35b6f5dbbc47ded74d7360f9836563596288432d999504a4195a8108a721e16972fa0dbb6eb39fe7650d3cb29980238eafb7b96ef80ace9a241b899204ec73c81e6b821242b187c0bf380312cd06dbeed3e494e1b1685807ee704cae28e3651198835f369f58252cb3dcfb54531c590717587fdc293165897cb577dc06b922dba7ef352502ee4c22efe634a01e99201b22efd58407da94bba7bb4e898c0a0355fb5fe18555581e50dfb9fc2c0ee2b47a17ed1c63b1f4c4dd77feb36b5356f5519a4d00c5263123df376c762532a6d02502b0891a3116025c77514e3a707e9ec8291cf7f5bfeb840311b963c21a23a7ff31b6b98a729e1004470cf7096e5cf38f2cd3b774800b131d4eac349fcedf2c96ad749bbe8d37597078a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ff49a0ee3627e3c714459814b4249d894b7c54eef40757f8fca94af762eb2a185ee6ddf62233f9de580ba8c6463ccdc2d6aafc925a20cd91ca6bd54f12f3dbc254fae516a426a474701472709a293f572bef40409631c6fac38eded2d4117d88d886ad90347b09fbffc97101e238c3212d7e83d36e9d4aeb0e7ac1e4586f92eb8e0c52ff991512bce3d8d8ba862b5120412dc563cce8dcf3dedb7d7e0a356032a61df3d1ea1c9596bb6e7f68d0b141cdc2e1aca3634de977b9941f49612ff6734119b18dc61d785bfddd21b0405e1830634611be1c1791be66964778ec1bcaf3313da538744387006325041227fd475e75bedd5517fbeacbd954c3f0e8f4f93e6a6acee7b7956fe5945df4e383f2d4bc3a21ce34b20ae95b16d3c255c51682c33083be2f55872592cd7a6ccc250547b3c2a8477ec534d72d26f035489f8a8a86ec2e885185afc89a93fd885811990f2a296df4c4f0ff8c5ddf54026c027974eb5953b8f13c90f68c2f31daa95749d51ab3d96316c1d065574840597f1b52c1def4503aab3c97c298ce974e90139e73cfb2595a1ea8c265356eb2f63c18b727bd0d5562201aecef868ca754ac4fcf56e0f96f7956c525b8d85427e96a86f066e1c76f3fa7c2cfa7a5b8fab0faf892491ff77eaad9c14b231841e69410e19d5a6332ca55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b88fd19f4c6e01b5a47865afcce0d5bf26439e69ffaaac7c29ffa810db8c727d6ac12bf7f25b21f8e2fc95573e20896a183effeaafdfebe22a14c3d77e34ded131ce6e41f97769d284663f4c2c3ef916a9ac99518c7e26a9f642c0f832c1fdb51db5c561ac7705c5a3c195660a7db52a49badbef72e74f8466b18bcbf0ec87646678084854958c9e109dce5b9b73951a14b20bf84d3b062fe7628dd49e06f50b3d9dbbd2b2d95bcac7c52bc86c25e50aaba70da77a9b972c684c1e4855320acab19816a10dcf1983ac56fca8f193f80548814dc3403ef7c8a7f8469e9064e1fa0f39c66c8c390c326d0926717080833737eb044dc2c54087ad1ae14b73c91413b408f1acce5779e88516025841e791ed81d6ef8e3fc5cdbf63f2528e6bb9d7aee673824bece930451636a4ba0139e0432cad3ef5841d838f74baff773821aa306a6deb134491a5b61b8dc908ecab41af3898b5759634d1dc6b6f0406c6f67e8abd5bd25d7d151c276cf25a800a97758524927c83fafdf2f9f069f72f7e2ece87c07dc3e3dd2c5f1d8b2b9eddc3b4ebfa9931aed06cf5f09f734830772ff291d6ae20cfafdc586dc2de9c7cb577f2899b674c9656360bdb591a84d5d70d380364fe8246ce9599d650c876e32e7ff5201040020cad6df129806332d8d00e675bddfb81b68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b8441678214c13b46374df4f59fa662aa0695414d490190229a5f9e0355bdb37050bec8de62cedd629729ae32d21407e7fe111de1bd644e6fdb99db4cb7aaad27d2cf144b78c1bbe1ecbee6b57f6c9c3bfa8c1026194bf9d71f1d51fa9a8ce8411c23451c003e65bf37f1ae07a7cc72f97d76c06789007844d61d9c61a62e6cdbfa24d8ffc84af76d0b8fe2e62bad7dde4c94398ad566694be96980a8b5d1af163b0cf582d76fee897c8b7c48c6f4c1df3db65fc23c1a882352d7febca7735f91db3d274debc2964c400e6cb3d346b63edb8b90ace1848823ebae955c11641903da3647ff3c08e35acea02265eceeec72fa487592f32bc5513a844e6816980c0416e842180413911b86b5df8d838100f08bf47bed5bc62d3ae1097e604051faf42800e9a7a6ede6baf00e7f6d3293f37407d225e7a439636b0e2705b2abf870c637571071d9b81c9fc65ef5439800a459344fe0fa75a38408b8b10c5cdc6fb723125010396428ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b4f995b346edc4d64dd707b1ea89f5af02a5284c1c0938ffa8903ff9757dbc8a0f713ab487e7d449487a21dfcd837f0a0f7192543c4e3cbd810414bd282d891062fbbe7ac5b1c1286ddb864dff520778961dfa56a254e4a21c8391cf8e56a6445107ddd01095c9d01ffc56803d8ddd1131dc17c1f7bfb38c931008367938152f070249a7a0faf98a61c7816253990ba21930a5c48297e099b9ec22738c552040948b69997ef1b0c85f8c9f61be31ee26fb5e417e8422ccf67d89d6f1afaca2359115234bd91376dab039f869f824daed77402f8ef8c3e7e94a0965f425027755683b2d39658efbf99483da9da0fa1dfd6390982ec1795dd6cea69fbc51ac609923e28f3df2d061922a84c6fa58ca7cfe837c83001e154632dc48f980b259bf4dcdc241b2d2ef335d9ac3681856b5453f6e201605fec24e884a3d15a77865a95047ba1b486cf2b9af0206da979cedbb8b6b32889561c2ec9a0fc7f31bd01b51e71c09e3f29d8e7cbdec893a29a5997dc9ac015d01650c1d20f06613f9aba267fa166b127210c7f27210ee35a9cc6008900f0d642593c174f3d47eaa1d3e62519297d214953a1175b7dba32779a8d5c7c7d111445a6010689cb9e9821461f8e7028909e2aea836e2dcef6543948ab59211ba7fbba954d39837b11462c9fcaf512705307c6a002a4624f55df6672dce57968c27c35eb3b6a63d3e6163447acfdb73f4a5550e81a96b11586998c96605102a3cc02f8f9167a64938fd5c63ca6b7f0867bb5ae649873f6030ee5aa399f35e52ab9831e72746549e6cf2c8a92a72092d04019581bf0312973b7d790c8320ae46cd2f778f5026b1b886781abcc15fd15602a397e2cbbd452c329ad79c7a3417ca646a03e0cfb9bf43bc0b305bfec5b245a1eb2f8519dd4cc4a47d74a833f2b662894b6d53151b5b5b13d1bcdda6c5d71e080cb470b5cbb18b46b5eea169757d12c2bc9e44c8ee5ab955b1c539a98dd3782d7afa8e8776609635ee6025bf4009a414020fc332d0c0deb9a9d1ac2695dad66ead482c81b55ef76466d9745911797dea9582128e2972295dfdc4946cfc40a3fde9e9bcc2e2a8f33bea76e28b8edb396d91e8b8ca5d9fc902e9151724cd5fb426639ee7fe3c08621c51dcb4abe187e404b6f879d6b2c68f45e4322541edf703d7425ffc4cc26652bf2142e10faa61111b4056085b87f297203f102f4fb3eb139c9a47e5362a3854a7ec9a30bde69c5ae697160e5e9854a4ff6a0cbb277fbaa996d7c6defb4d9efe18d06b19dcaaeb14e1640cbe76621ce9e4e309dd8e223ea7b21d7a456484d9acd2b3ec3e1ee8e2c46b3208c0bb3dd7dd7c4e0ef41859e77ac24140394cd90ae5c3d639f2739f621d9aaf77b65b1b2636815d93d17c9a4d45a1c7f317eab494e66b4456b6a2566125ce50a7ea9a1d8338cacc230418cfd64c2cce019821db769c5a01cde43500b37260fb6417427fd371f8399226319eca6762e750132bddee2a9e5266dca519425401970ae56e5b4468c0f9403bfe24a0b3d3d80371dde185020d06d1a3922190c7b13edc424ca74bab434655d52cdfa7f273f31fc8b1f5340e6f15e77b20d8c689fd5c65b6045cac5b51b266b98c9b467b6fc918872223eba9790df7434a24a53624e5ad7209c8692078fc7a7cb6035adebabb2f34f027cdc2ef22fcd5109693afa95a2576d2650819dcd62cc9a4ef7fe29f6cd8104b5380892a5e2a966e665f6ba084699b16dd45e911d5effcdcd25c50535824ae1300f6770d574cb63021bf4c474936e9685a0fc5d080fbcf9752612572ce57d8290d560bba6cd95dffaed8033711d7ba722c28ca255648f93b62143e9aef72eb486907e2e117db112c1aeb0e7a9d2c75839a38349cada07574684f534611f498e6d8d82a0253b9f03f8d1b347c8745239526980282a210197daf212b82309139cfcd1ab7889bc94f972f00f2d5e6c953b22f101916299a0d0e9cab85de093c48dd8ae9b6a8bb48c6455acaec9e4b0d849ce0d090393a8fa4aefd3710289ee91c77b622b873d31bbfb77f33f83e259ad51985e5bd10f6e0c87d98c02e73cc01e24b20b1355be826473ba87ebb8e4fd08f4cf897a23fde04c42bdf31a140a35bb84bb05da39f354a9dc24639db81bead7d36fbd0940a6b98ff4ac4aac99b2cbb42e92276b365859c95abaa9010d5528fa9d2b5bbbe5c38110cfe1bd59859e4d42f4cb56775c594c79afdfd1e7754650d62d75c0782c9e4620cbb0331f49a9df59b475646047e49a09e5f77c18a84f24ab00abef9fe5f423e1cb3990f95f32113df09171b32f527d06b179c06d2e564b3b3c09172bded0294cb230224cbe65ef0c5a0eca2ff6ad75887b1d3580f8863d2c7dfb974bc0f1891e774c0fbbf22910ad9ecc40760d90e595eeb12b4b2df7d4566f3b8b91bd38a793425ef1b3a17bd9f7db93598fd03c8e67134e2b12963922d303f26abea122274ad88d1349726ca7e6e1944c4c88e3bc85db1fdfae33e5b64f905ccac6fb30baffb273196bf1c34ef67181f0308a8e913523f127960f0b7453c9f8d5457bf68e8e4f864e06bbc902069d8f677c38fae19d80660b6c93fd464743371e6f478b8286809dc9c8d0098ee1367bf97122a39ee07cd20e397b3d413a1cf60ec9692c5b740225d0fbd26122672a19ac8b4ae65e41356658e2d9b01e0e9ccabc64c1446cc1c9e303b520ca892140331178711b9a05f3537f84085d09d21fcc724c4fad49f0c52787d436cf5e53b270d90e4a6f5ea71cc253a5ca159c6f44da8bf506f743d15efe281f397f1152146c78b14e0c97618254f23d5cd6bb119da7fd9f43ec99e00efaec36195dae61bc8793bca2fd16a25e29b9e02df2dcfdc3a2d444fb2ec2acd78bea4dc633ace3d3639494b79b02063f5596c2ed89485c0cb2351a812a3604c230f4749e2264bf60ad3782218badfbedf9872d8c0a66a398bad7023e0856237a164312d42aca252ec4431fb0ddbbc53da211a0d70f0a12c01c9b09279b40c23f11f3ab4112078e9428760cafbbdb2634b41bcf983e12ba4e65f5f2719b366f9d86f5064f27ed0573bf2dd3bc06f204bb694ccf1f226be074c0b48507ecb2ff96ae5dfbc0039372b312e3e93024c6e7231525ab7bbb66677352d5d0c00d31ca0059cfc43548a7e154e105370cee9030881745341f44714d862f34494951dcb9358f9cfeee0c38e3fa44442cbc566ead7ac07a3eae6a4112e0cdad5530d83d6295b01935a5030c95dedca06522a8ecce907a2ca6ec873c361b1fe99eb13b05ada188bfb6d843d24c8757318cbe4909c0bf60caac079bde7d75e058b728253a4ff6e9058c906248c523638490553f7ec04a1932e596bad5001240fbf5639540cfdae613f7d50533a40bcf3b7b670ad437320cab95b7d2154f4070cb6330fa6963ee2597e376ea8b4fa9e94267daaf665e4461243520154a0876b937ccd10780da2798e77ae1aa3528e2bce6a8861f506ffba40a978678fa18b4cb09327fb4062801b7fef8b00299867953bacf5632bb9015508d050ff316ca4fdbe7de120d80b2b2d1fcc81cc30b28f2fad0a1ddca8c8776a349c50f4bb11d739d3666e151a1a1355cf4c5d275b3fd0679204841e068c5bd3fa7e875e5fc60b5f0c351acfa628a6226dba65a932e00764b38f6b24a9d8294da9f31427c60d47141fd28419c82c36a3018a715a79558855c4caaceac61b087b42de1eccf7c3465838e6d65ff42cd312d87efe51af89b096c6b0c062dbc11fea9cbb05cb496d7a446b941ba947980cbb72550cfe70ad22552f45ec70263c15170c259c53414e0bcfc0b32353217e03211e7b6c472d65537e55cfdc5ef7a799832a0609094d530ac5f4071149fa7a348889d1c827baa5ad16b1acafb65908b0d5635b50c785183faeeaafbd77119f4be86c83538defe407060257eb17c5c3d1ed5cb2a9c4a31763a1878a09d69d356157fe5f2a4572cde75d39bd3bcfab00a9b77f6475816fa853718832f5d3164732060ea69fdebd6d5194fd586fc5a9775bd24e16c1274ce01ff734ba454ea92d3b5e0144fb9aa9b8ff2dac638aa0aa2fb4dcbf77ebab0d1b3a383897f3d371ad0a9670ac85d001e0428e9f65b90169ad4367885cbe8c8a6649e211b9a7cc834cf64c3c8d475134406f5dd2e426b4d9e8e9e0d1c334cf841ecf2b947968b1992bcd01415621b09a4a7dd1d2f661d20952a1a189d0a9c6e312698d964da2d1589febaa689e1f3c4c2edba4f2f1cb620b13d42062d57d61454e66650a77f400cabdc92184b83c7a13fe8591e73005c85b2d790a5bb3b627b189178c5c364fbc3a2798a91fc8de4e77e29b179ff2f0102a6f4b3ac2e9c04aee496da72d53e86d7af12f4dd87478d7762f9369bd2d5990f46763e99b86aeeef7dbbaa6b9aedb5d59351664c9d5ae2e9a21d89a54d65d263f0542bfb2905a70585c191083deb6e9e793d68719d8f5142948afa954ec7db46a2db8fc7a2938920fb78238e6b20fe1ab03240c319266cbe61da6e40123f272309ef69f2e03b95638e37ba9f8698aa80ac194893337ff68d56fa47f62ed1e57af4619b3a1a69019e95fd28459935afc719e12859cb689bf4b462bce5abcb7b6f9e64fa67932b9400c2374fce04710adb1ea2866927ee224b31a33d994a834c951c2c4a75ac6f4c8a7fc788c3092b64b16314a8e6769d72f6344bbc1b556d6a7e53bbf44dbf323566f729e3e93dc58989851b152ae45471ae37ae990b197fbc63fbc681cd30b64de0e2261bde3006c977745bbcbd459be6341b047662037c7dd71938ad4f95a1b7de7294a591a69dfbd94b0c3407efca844f4a86ef491402c0d38402ef4ef5978a3b6af94ef58ddb1fcf92d884dafc08322e61155197c62834b808ef603b63207898131aeab4d555b4d525baf95e824b4d10ac47b2f391ae4df98f8056990048b47c0c7cbfd1827878feabed2fdf8468a09823312bd29cd5e8bf1c3ead2f7a00f0f0b172b2abc3a038ee41526c1a8c84ede11a04d62c4a9c9ed50341d22713eeea59ee4f0402c1cc319c975649e314bf4f3d74c9c11ebf7a471af7c250b429a7d1ac8178c6e9f8cea72c2d827c890b7cd3996486da1137d9f52b5af6899984f9a74fa520273f1bce1eaaeec18ec9cbc3f61eeacdb7b2bf692e0587cf0026e1d18cef2452ae3e74501ba35edffe4cfd318a35d9a3d6fdeb8b197855d49019b521cf436bcc183843bdf6cea11982f246f34b6d6d426023a5dd2231bbc6062c5b95906a13e553e969e4a4e6200d0c3889e9b430932feb9daa840f00b049c75f4794c50f21055edd4a62286cb1cc8748d8eb62cbd2a265635410a84661556eb51d2c389dbc668b47c5f4f64614902c51beaa4403b915bcd1c2df88b52f262630b4da756bfdcac77e8045857e639e37808ed2ab15d000ba0791ca68e45141678bf192eccc0591dbf7f94def0b0917e42486373eba5a27894e49feae932a0044d5fb0b27c4aef177d589172e07f9884c90fc9c778a353a62bad98e6561cb4555a26d56089708b76b78872f7ba8bde8860f6ba26aa63b0e23fa8ffa87d6a480b9ef66f960bf5b7bbced17c71c27dfc14c3899fb8db2903ba213eef1a1025ce2602923076736faac294d0977adc14cb3a602eff1bf8cb46b2ce0a785b1e8b0dd68284cd0ac99c211940daf766cd111da3455f3d5578252671b14790202b01325c48c32af36ea825877b280e97a4c47c9c794e10af4350532ebee73b84b4f5a203e18d4ebf05c0b8910873c292803368bcb0f92480653edc4e66a0a0646dca6b04927cfdf77d0af96b7b08d9ddb1edd3199f86d31afff6ad53ec8ffa7ecec32be4e4b36f9f9811d2cedb792c3048bc8639b10421d52203af214143abd40589a2800f13078b490fe11d7cd0f8d774e3c276776844273725195932c515c4d07a2a8388217e4b97b86b6180178738bb8cbd75e7f8502db94281e770f5bb155f2df47a3b904912ef3b91b6278c3373080b26847a4179ff382c9451cd6f2caf6063e167e93610debe05796481146bf9e24b8a66e6a68f46c0ddc646b9ec15b56a6ca9b203e03b9f2ee84b9aecc526f01fb797df98971c627313176911c038f3a59409944676ac6451d89831ede6b9adce885350abdb04027aceceffcac5cc324d5b623e28b1b616140354b9ac50592fc143a3d0a23364886cc9906ab751ca269f58c397fe4ae084445f450ad379884faab2ba1ea5a00d92c96a932939dc0e70ee6dc6e9f687bceb11753f9cbc742919e98d31489d0eaeaca6160353cd6c539da975e2af6777cedaa5383e8458f24b3919312b63e112d1081f40e4e96f776e9c90be0b0a6f8b1f0ad8039ac0304f629492bfed04d2b4381fee63e75034aae96bd0d5a781f45386778407fe0f1ec8c3bcd2e6b1932d8763bcb8b911563863e40d20f2a1cc0e6be26f69d70dbd412090b8de0b3512c6de347d3ea92db93181c5752940c453316f8d7746868b7658dc34b52a89cbe9ca0790b31099e9876e845999e98797d678f111e28de6f9bcf1062c754f2688d144e0007f58d4408c935593991608aa3314f53138daf80f8db5fdc7991a05b17ee8b41c57b1620aacbd1a2103af474d4c87afcacc5f2cc867cbbf3275a9f09d126c981c3b84493b111e5650818ea020591618574cab23146a30886a533b1895e89d75a745e2ef4427647afbb5150571bb607dfe74ea07f099af8611f878975d10eb7e096adaf8178db1d7edf44dcf798a8777add2d24ed6fd5bf7585247683d139ca843ca76dafd132639989455fc632d458527434140da9a0d56e3bfdae0f95e880e67e97df45c84e3175746342dc6e244827c5ac87cdab628c219cccbe63ebe32e9da973625dcebed895873bd04815255d91a1bbf2c19317812f6e6232cb37d29c2030ab76fad7de57f3fe0169a4a0455ce63fa3c4611ab55f25b92f741fadf5ac58dfb08101782eb54915049b170b894ad1b14ca7289a23de914417ee3af612d07a087186e26de949a2db83ca04c20f82c4200e7ac9572765914bd8fa9ba633d4e8dddc78f27482367345f66f8ad26ce140cd7314b87a57729dd800d79377cbda7604873593010147914dc77cb2d39c60fc15cff19cf72efe9e9ea75fcd0109d059d767a63a1bd484df2100d034477460d66e3381c4a732b376fb8ccc3cc8757c3d7f0079bd64da7304f7f0679d2bd348e6d3106743bcf142ec39e053e7a8b6afc5b6b6ef53a4ec2608e5a32e616e073c9a32019e3d6ee474c5bdd6ee37787d660900cb430f1daffbdd62701002108121b590dc4ef27be85903ef968d376e305c325791ebdbb4f4dbcf27fc2974192e4e597dce8ffeedac0433497c9ba216377ec15156534f56d8d41855ebf91eb783c5bb2cb1e755bff306eaf56dc29edaecb068f98c129cb955b8013e25a4ef97c354a7d0536700d24f6a3affd7361d88ba3d09f7de43635988e7680d7aa48468d669c6e20c648f09f2a7706ca4225e655bde512755ed33a82c40a7b961be601ea43c53fd11e3878a8b20ea166d6bb4b4a1fb124b2453801baff3a9178ba74a34ce6b5e60571bb3387144a74efe1e9bfc4d1fd99249005ae1d2cefbe4c362ce6e5472f0e575a3fb74af29d5e9e56d6e11b3576d244b51d8a3873ce1d37f9e01bd96c0d6724d7ba9a856c41402d7e48dd45c2909e1a65ba6ea13c00fb7c7740073b3b05458a398039e68b30fe97c95696dda4d3cffb80690adf1b716541a4439932c74be21a2913713534dfc7de7ae365827e2e198570a0b98118a2947d8e711eeedfb77149071c3ba8e9b31bc65df8771c616a924ee33ec2cf2878b48e9ad303c6e816124329ae357657baf7b7b79d994b3ee4d33a12a15fed45cb5d87fc4f74692f325af760ce71dd80179d2b3c25500a77d30283922005e7c1a637671e0ed3ca6474307658b40c2d04e930c9e815ddc2ed8d2642f08ac4fc575128c0e1f71ba969aa7f4f28def1f8857d5a87ce285dd78c024c993fc4a0c1842983a26893433a00883b220bdd1e6f8f325fa32c2ec5b21f630cbce6d3173bbda5e916367180e99d337d69b5b3bb2e62e012a5ae3ea023137b11bb230fadd8bf967a248d799c024cc9047a8038bf9322bd6c77c3f15784f667d0dc8de37bf44228451145825e0b0179f1c05417e449b8575d00021fc1a9bb47272a44e2e4ac7e63bb6002bfb8a2dd742a8f036211f0655ad9f2e684b901e66ca773d8189f12f21accd4d2b87e8f485ea824a48255916059b83a6b3424462f181eb6c23b76b759e572da625fff4467abe7fc932cbee4f6449a8790fe5b3acbb9fa4946a916229e60af183f29219afa17dd4a20790378916cf045476f513135d3e382669a3e25e6857f5ef6a9033f338e95f642645645a993824f4e49f1d56c4867cc968e699895cbb6848b534a42a0788652da3d09b7a79b20c5db0739eae2238ebfccc4a2a1cd552e5762d4318204acd26f8a92f2d05443d73ea5d59a31a96eb86a7dacd2a9ef81faafaba8cbdd4ff6eac1d76c61c29b190e9b4e29cf8fe06eacb3d93c7bae44371754c905491f1f9c7df7af3a4ca43b24eccfeb621929b28a92ace07d362a16d67714caa659f4f8ed9a703e48864799fe6bc2350056e7755c882b78bb87c4b550174c7803af42269114646be814d4f32a0cd0259f766afae8e8361ffbb7f98f499a56a60d28c7985991f033e42e0d8be2dc8a39624b54ba12dfc3033ba44a18a676df4dd95aac2d6a223167cc6f108f13dac2de24cd6597df63687ac309e896eb3a93170de6bfdd9a3f72878a24475014c87c8dba70a36e15e839c9da903c23b3aedb42e4f5dba98e86bf5851af22c52d6206fdeb9c5a964c21d20153841f55ca2c4c0d68cdb87ddd4946d97a5a90bd487e07ecaafb6c50c4168a0a6b46577bb97dc151a936765eb64da3c4dc221e86298c7c69424daf951ade68c6d222b67361b858891a745607d658d870525f289daf3e4bcd3b4deb44a2fc741e1f8feea8d1e13d66c97e39e9ca9285426d9b43029f1dc99052708b6b055f247563400d66fb465e072265d981240dce1a218ac755ab14604470ed0390a7cdb980dc3d7680fdf39a4e1eeb8c1f48ebaa72aa8413632332024e72e8dbbbe2dc0c86e5be5cd9c21ce52d1cc26515009ba61ed5fee25caa2e117ef71a9ce336f9a080ec66dd6fdcc9d68196be41ca9b10e95b4a4e4ab5d55353c2cf2a99edb90deb1343d072f38513dc08e4eb06124b1690a092b8921c09001720bf638a1aecdee97f50734d3517067405292a2508bb347d72284d79826298a9e316f8b1c324c64d4978bbe0e1088671381dd36b3b4d7ddbffd8d7d1a3db2b330c9eb851137bd695ecb896a833a38888a8f51bd9a8f46b3ba23379ff2711253ea03e9e0f818059f153e8a45c7a0bbcdb9d019307ec40e24569ed83b29641968b4b3169bc714273a1369dc3afd70288506c619e42f98171d4e19abcf41a6da8fb209a2d0d526dc2a471ec2bad8e7ddb98a2039bfe405c416f2ea3a55ad35f85e075eee470c69e6c81182d0a5eb2e22abb165ae934c9e77c61d619776458661d12320931525012a8a26c67774afea017adf96ebc243594e0321d491ea1723d960d83c747fcc70236874ff270b591370d5804f35bf72ba4597fc6587aa5395cab381d59ca83245ad7ece4c587d156eb9ff65b1e924c95531555d29777037eda5dd914ea7f7a229ed507ef8d666945ad952d9da67889a40a17cdd945c48e85825934fa2e3efce4c8987b8362f3ecd2936cdd1fc80a239b3261aef2fcddace7f5f06f37314ca5b589583f49ff85df0189377eb3da4e632e2bffbd2a59490032d0cb90ca4c687f00c68940241259fa99588ff6be52674f458b907dea2905495949a4209196f3765842134dc91fdea60158a93d374601592435a310ae88546a7066017c093d0b3600e026c276e6d05b3e829d751eabce9df4a7d4d3c0fd9d2c20e0161d229d74d006d38de620a3679a647a8ab8e27796e2c760c750b52b52b733362be91737d78a3392a12faf6390c7be18e07d2dffbb5657bca4b3e26ed303ef26df44641d1413a4280e704a959f4cb58e9f0dbd8de30ada6cdf88286234a4be0e1f28a50c9c8fad1c99d1e60aa50cf5aa5898122e62e801f0fbedabbd460d01c1b84ebe55ca95ea6a871f2e226a33161e920baa1071f853061f18da65622b3b734d4c403d584441ae3f8a7b3df14ff939006ed13231c89156b2290ed69e57431adadf9432c31ca2954b92c53c91a448eac7b368e56bc0e1e58270d4d8f2adb535fda321a49d09bf238dd8c2f8ad7c2ece94e168061ec47bd5829d143e445d9e27a30f8b5df7d0c59a32ac8e6b871894ea21354d86e4f499a2c58c44b0b88e99c6ee48acd215629df260e46de22af07b0ddae8b893b6c09e0a3bed3e7855badf9320b6532b573886545042928a170013100ce8a9940df23d916b5d429b1b567816d063c0d46d3956dbe6a742d4109809a51e4215bc0939dea3010277c3eaac5999c4eb6865b6f9f2feba5be805e19d04bdd7c4f67b2d71c304b16e6242094040c4d940bd6fdb15d10eb9139dbb294368ef50664f7224f294be08a73cf1c40c5d13d9d9c182c7ecba89baaa1c19db994fd48e1c61184ebb91ecb49ba3e83dfd2b0c9b53c20ff980c70794af15ed44c54464ae1a2b56fa0dfd541a8262438779a798afe04a249d0fa7bcdcf1d434ae21abece82f6cd1ee4486854da9064c934a45c0ac9b7200a82486bd6f46ac874afb7d6749c6b94bf09771e9ade7423e521bd5900d0ae7e7591bdff180909ecdcf246e17bc6c488e352e079967ab5f41bf3bd7611f87a8e50c94239b6e6fc4fbbffb412cbfeb9c01a86b8a34bf45fa57e00324d3377c543095d78cfe2c00cf47d4bb0548c20ad4b35486259eaa45eaa7875c95a13bf380b47b7b29bd88d92f7e64027d6ff4ee5f6a99f026aadf39df17134012114dc0e066be9b9a4f3cd92d05c98779a610daaa9043357e4d00838e06303eeea1b0b7e0949dcd1ded7f453fbcc2d60aad9677de28b40ceb4dd83c584122b82551237f052abdeb00b0d4cdbe8df11533d99b107729695e05020d284b086563fbc186b17bb447b71522472e3869271f48966474f12ce0a2ed910fe13e74a22976a217c73ca4d79c2b07ea0e2c7fdd1c4153315d99954d8a9b720e00247cc4c52a57881ea005de8435ceac4f5beb5e8a5245cd5699022189b94c41ba0a6b4f368470f6698492ed62778f05debd4dc0e235e9fc2f728d80032ec6c8af295622d377b775c42428426dafbabeed643ed515632ae2c8d9f7fe837c887e96f936cce92d405d64eca6a881d55ed59c2402a1e732dae9dcabfaf80c3724fa4f6b91044f029f5f4fd3df1da755c171f46e5d81a83aa4200ffd6eaa6e31569f0106413b5e15b53ebd2d39114126d327c3d3197c18435a767b1649c20d2cf01195fc14259bc64049103c997aeceab068c567c804539668b67ba1da66b07abf274c84f97abdcc23449224a534f4a9f88edcefaf86aa837db096f6495e6ee916500f4571c39eaf9d28297fc3ed229d557121a01d9e9ea6d3d45c93871167d103270f6ae850d4229571cd645f180b6936d3e9bff4d991fae24d09c98721b10fef299c30544e7106fbb0fe67828885d84469bc57ff114fdb1431af87ba0e12585dbe9916504acbe580507a3557f11aa5ab60399745a28bdae81feaf40509235e586c5cf2274d22c87993599ce39b085bfe01956ea7d03d5df73b87b1dac2c4474f2857be4bbea8b85e15130029522832824d1ff7d3f9226e785c61027feedfe0e3c57a6ac7fc71f7e29a282ed5fc3b072b1ea8bd8bd1d2e6534b00c81cedaca2bf87fe8028a8cc35bcd4beb6644a098eca6d4bdce500cb665cc2d798d2f0fbe6cd6f5b9c23e20969e3c7da750afd45316a98eee67bb8c8e259877189fec91ecc8332df18c98ef708de8bf4c1491d127dfc6ed2d91d048cbbbdeebdf4b65445823df3acf5a6a5b14d2ec574f4ffc8ad70d2ec8adfdef859adf13b6f4f0319bfde5b8a9b91990d92f86f4db27b71b0ac590276cc9009f512577dab373a7f4f7d92fcc0768e71857686db78433610065637200625c5f8a3457c98f0996091ec078029e57c63470b274203e76bd8ec703237604c94329b3f06a0a503399b68080aa00461613d363a978d550b24f736fbdda5eca9e60a4d859229b7dddc65d310b8e3d028764b8d70a3998235e462e19449dc1a90167427514fd8645325e0213ff3dcea1cd6a1ce3f76356f14e7071cfee717d77bcf38f43137e0c2f1837b7b986dd88b57b59933fc194eb48bc020f19b7d19c60f9cae5c04a51d9759e299cd2b6b3a57a8d94e6d6ac4cb191ced313577f20bb6c8790da9b1f9c54a65cbe21d8d88249b429ef7c395fcdbeaa768c2e2cc19a313e1c2618ca017916c86e989651b081396de5f5ee6c9f67d05a4a444aca29804d4b6c9debdc91e378bfc3446784a1ace360be1cf76019c95ae2515f4e8b73fffbfb69c8a35d40bca7ae86653acb5994b6b8dc98c09fbf62c7091117aea23e13fd04930f3400cd86a8cb11eec5585bcc4c617c688f67a03278ab6d37dbbbd52826a27bd0ddd6ebc172f69dd1fc4bcee556958accd3f47f6f5e972c70da5435d23dfae062ae776c28b404582a1ec2ac417f6ffce52e01222c954802abd7b12a6beecfd005b4119d5532cd5ddc1f7594b74231c72dedbbee88c5b71787b63b71a8bce09591db2ac7afd5e361032aa9963e21620f56b6683374ac18b4d39677d924c0bca39fc964c037d67bf99a9ec01df208299b9eb1d2c34910fe7a4a347783ddd6b61e66f9f06f76b7ab81f02874ba732fb201581ef6fbe2096d803e86d7d8cc96f2b4ee04e00faa7df0bef1ddddd474ba8fcb1da03cc4af1ab58fe85f37983784bae56a5a2f3797e4e08eb192fdb08715f02253db39c63f29a8956ae87693ed2068d7ea43cd2dd5c6a6cc5f221f419d7a0d683fb2f9e8872165639179ecff932c432ade020093e4ad0940b760d75e8d61100673ac4fd2f8d6e4631dfdd703d67c0c3ca020cbff3a1c15c281520ac925561f8ae08804c6fdf4958783febb300580cb81d7c86837097b13f9a3ce3cb2fe1a52dda69e54b1458f686045f2c53981a187c17df76725dbda8372919beedd09cd6b7141a395b65f9810a8da37e4963eabcbf958bde8da2ee15d17a7164106cbb327b4470b6d9242af847dee2a88a6a5b42cc888f0224cd052eebb2adedf8f21fe6d52e000c1c05c90be66b884ec311049aa8f91a8358c60f02d0fd7f380307517ba0634cadc9526a557b1b8848b2a8096f77ffce828d178160e772f71312f9d21796196cb157709a4bbedfb13383075a2a46fbb1e8acc2104c4fbd5b2745e7749c7df4f76b44a51e41a246a7ae7326f4b5910e4f84293087848ec6c21d95eaf1434554b2c43f641273b9808a282f66955a85eca947e5cd1425995598aa654b8bcd9fcc39381b0e80282d36a97a1ccff9018857f3ade90350ebbb507b3461838b143d12095e4356016b56f61a043a28567aedb2e97c4be1b7534684d87c81a384bc3d21e52a36894db28475347233fdc30ce6f3b0eeaeec6057916b15371a8c46f896f03a46b2e036301c5a6ce50a299904e3355f8f0507241b6d8b86ca7fe4a9089a0566f9f687b15ebf7aefc13794cfeab872cd0b9bada157d910754246a54089d065acbb90aa8fc54ce25ed1b255b3645fb399a6b3f6da3049d30116f2d69ee1cf25ff4f5f9e200fd8871b4b9277e98af5beda5a3510a9d28d05fcf5c18a8372a775e5926d49bb063b4da9eb173354fc3256d58f0ede5460b90ffcd9f590325a88c7a793a73fbe42641d251690a8b233cb8c903f14ff4f55423a5369ed7120813407a7e3bad534d7dfca786ae0f5c2da57651fa5151912a24b8b8c477869ad1f26d7d4bd4c8918eb44a20844ceb14922dc0be5af13658bcec4f3b0b30097bcd002bdd2834bcaf912d78606ea093f02072ad1fd9a190e25fe146468d1799c2e7ba3e0094b779dd8043d7ae86753926e8f7d1fb3935f1913eacfce250f79017f37d26ecc8179a31f585dbc07a8918f257f45c3504436b941054ad2ed8cbe251a115684531b0674eeb28dbde9d42c8ee93c577be4969b21eea4932fade91c860160c3ab0f1b79ce03bf27d264908e47194ae78c602943bf55112c9db25c19e4e262aba64e24010c91a604c5754a6d84f0978f1197f67ec6ee0807e440896cea6b3b536fe17afddf2ca19d6a927c5c4d5f6d6828612c4bda2c79da184ed6f53e6f31500e7ed4682f6f8ce97d34292b7fc3aae15e2002615863a0c0479c9627ddb4a22546e83af25b33826e367a53f8b5590fa366e97c7087f68428d479d073e6bee13ddf788526c095cd9eb24c8bf1a1b984589809cf767ac2137edaa251ead92ce07505976ce8be3a483af80292608c2dab9ac7098468cd0472918dbc0a19414c2bdbfe01134558bdb10ec36d15bec397cfadfdfe443e63e0bc14c173d6a8a308143cdb166316b33de0f5cc172f56f714bdc902a6816a7d4e519ee07a44f0cc06b657a12556c390aea16f0a3a3d1c0d47c27a318d459b5f79158a2257951c06e2ac8f944cc70ac78568165e44209b55f0cfa930fb309c36f299c637cad9ce444815522554054f49f5abad422782c17a24f9154aba802b27dbedd527160c3e5295753fcbd04adbd1f3ee72faa68e8dd092a017e5d10e3f385e6d81efb17565f68fdbcaf5ab408d4c219611858e7351380d8586b57989eee26616f548a09175242cb0f62fb676b067f27e96f21bda13ffbc5692d3e57c92f69b62b82738ff6c3941ff68d4acabb78e9dc9f0c305f77c07a325afdd39f2f747474c613d73d0c721e722d1b685f2ddb12d7b7b784a8a6b3f9f87ef8f8813e1cf3c6c74208df4eba05ead608ae4f598e4526bd71668b880ea2d8cc6927463caf86a9497de1e9399c7ebb4e5397ba6e72adcf2f9f74447896e575cb99a294628def2a07d6ccd53c86b48b4905b15f1dd264af4802da8575d74b6d3a4ca2bccc2494a2374106d71da63ce3a4a54f7a04ce1c8795eb76851311bf6e7d17c7714c51e97b65a6a1583c5f240ba586181ed306983e8a0f84592e66cca1230cc5d676e9fc7303cc3acab6d5f1b24ea3ab6b9bbcbfdb9ac28f07045cee1a2f552a32204a47c4146dc9b0cf4695d567ac659349b6daab57001fc2965eea811cd88787563dd14b44816d64f458987aae5bd20317481c49f460bc788c9472b202aff36d226bc95b338f086f8bad24b55bf301dfe81b024aa8556257b5b31bc167ae2ee2a9064e6f5648d9eea0c066f77be01c8dd93bcaac63d18c5589fc992682400592ae80f605133c5fc59c2617f9a78169a72d29db2b00a63733e437642c7eb6a60b202c4396844e788c8a55e5583c417564388e0061fe7b57085dd5c8aee64d2e0c411a0c5b83301cf2726fd55fd9956652e11d9a20ee57e8dede9721da03cb525fdd9b2ea63bab76fe70608b9d101d014daeb52be4127fa4a571463f91e1814744344e41ed4c0705db305cbd19f3da767cfeb22c05163225a2258950d6781b86cfea97d602bf23476ca357cd7b7c540d158526e5cb191acc2cb931ca305ba437d73501656dbf97fe8c7bb5e1c3759a4b6e429b129340c947818af48c03dedaa5e1069d9a4f874c3a710a29b4f814a359c60bf0f59a2a7941bd7838423c14c42f53fda8d01ca44ed6c0a870c79c2dc902011ef13b5e382b8efcd1cb7386b52dbf1a16b98e7686a31cf7e6419de18f30f93673f0c0afa8b261849f7f968151005d93e4a3a7398836fcd58ca5ebfcfc722aa9655bf948bbfd6afff557715604e56d9f4e0d8fd7430641dbc5774da49ac41eeddad8e2c732db6319eab8d4d6009eca83cc2b737d95320e2fcf16cfb18f251499737f2ddcca7d7ef4a4b28bd7be9800f8ef2341d856396e6727f66462e8a37125ba879a5224c695f5d660094e0c4d8f36762fa9d4faed46b60af310ff979e4375719bec68e2f55987368e1df643b62f332a87c1ce98782462e422657d7aa6316c53df65b382ed9dc388a33dda4911da6776141ef2071fc23bd6a8778ef0e86607184a15388a9612272374b677c11f487c678e648cc05ac2218779410066606fd7f56c8ed7fa12c82d38d838c2ec90e34bc8c5fbb57555bb8182bb0ed5214885b1c5baa86ae378c7ba1499e9d348fcdb6ef99c778ce01835874756ee24ddb34e151f80a10ef49c602b43dac8d4041637599203d8ac1484ad7461bcefcc65ec8469fdcfaa4ccdff94c56de8b07e745b8d779cc2c187a48ef6d278b9ae60e64458279828266deb50ff58f925a1e807522f3ce629fea4624155962187a8b27bbceb1fb5b18afb62df29d15bd8186a77f87e417fe5c982a64eb84f135f3fa25dbd82511091568452a0c7a002d58f2df2fb5a61a75c4298adbc72c607fb119ce664f8ee9737e4f488e35a776ccd3f5d570df08829e7f14bde56456f561c1508dac6e49824600847bff3d1a6fd159ca593691f95a8b9d9fcc88bdb64a2f8086f66e409769ea528e7fe6154afa119a2f7deb5ff49fafb81061590dc4aecbbfb6fed1e068adb0f71be325e41742cd3d7e1c7085e8d5de831d9ffbf1258cc6d1ca598b888d8bcb34da5c3170a163de3b19b6028ed1388dceebe7835183cb3f6dea757fd00518de3477a634f389efbad357d156cf8dc4183b15cce8af854e2bf30f13cb9d6a4a1c7caec0651a67e1e725a516a1d454c4e13d4ec811ffa6cde28f948f52eda070e758ff88ef0b07b3aa2eba82f7c5672daa875b2b57f1096398bf73eef11725a25a4544d0851b82800af020dadf847de5070e6f0eacaebe75cbc3a46fd091d3131d0e254c666c75e3c3e86ab748cc026bc2b030d2fa2083ff1ea4d01f323742567a8554cbaf88219f6bd53f2dd97ac12b295dc4cd40d3528deb06f00a13c0574233f8edafef642f009973d431cd1c22852b446846216bd22725caec757737ef3428006e2c61e9f10b4d9761d57b46bcebc0fdb5d66a2bf884b25ec08661ac6e90e8d9d4a0aa7727e8015197afdd5c46dbd83ed7b712c0859e098671414c3e1a7a864fb3825502ea30ffc3d6145b30db09a37185e6c3c941c0d946b62499637aea3551705932cb0cebadd2ccf74a26aa22db04db82bc2bd324ed441ed1215a51b375825f4e2c65266d3570c24de7ebd2d424dfbb1b3144a109ef8f74cec9f43d9b8275b1a888c6381387675f013be08693e4d4b1920c304676e467064c3ed5966d1c35421a32ec6ab1eb4eaf867796be688673c0a78060039adc6b29830226e7999d050e8276c1fd6b91512aec769192b0fc360a1af65b6836318087e9deeff5032f8460fa90df7c8762f592b35fd7b57db661001da696f6352cb31aa0d2c72b414ad612d24558b8809df3bf90779d26a67100cea11842b6777589e2a0a59172c2a639c1a6345696b414205d22eacb374bad2362bf125b94fe7d3088ac0f9e0bd1dc0e3ec5cd495f31965a9a607127432f2c49c8066f00670dde41f59a29e81dfc75861bebed02845d94590fdaa7cdd3be38787649d826ae3adf63160ba5345a75659391ccbb7211b43742d61c492217ab45c97afb6dae78c86da0f13935f34b478d9c470befbce553b118a1eb3e6b355414c54f3ba5d4507aa6f9ecf9868e15da047d4507d43667ac56ebb94cca4ee154314ed72263366f74611de6be7cf8ae83bc2df418d14d6448fed974915bec69e670a6f1b8c3815ebe8f2254bc1389c5d50e41f690ca324665b15491678829d26a612df1c295bc39cc9caeeda882db73e6e1e6944a66b218da14306d1180108aca6136a8229c8dd7b65695c2638f9c0d3436cc456951d92158078d782e13d70a5b3d550e6594331c2de3c2fe1e05a89b7bbc6a81b21b929fc28373851646da7e27ecb9017d2a6633d16333e4f87f33c7af607717f4743dde65c6ff2242109f07ca869ec4f0130abb1463fbafd27c7d3891b22fad6e9094603cb0731d257b65dee4d360ffb547de2548af0e972ae1a9a60ade03cd06ff80cc08a3fe1ed30595a77f90367db2050bd2744e7fd7d6db764f775503b98ebd2648065994612a8cc6555c997653f0c823837ecfe0aef8068f1d82f2273e3263367f962ac9689830332d51907f748e755e91d311b41469abb0e2596a0fd1da7b7553adb257f79e4098e84f19d33cf64eba9d4280cec358dc855b00eeaf7c4dcbb2e9323b92a0a824939ead95e3b4b7d8ff0695db2ff0c47662a1bfb1fad5931704212e8fb185670a92a33eabb43328ef9d450973f597c0ede4b271e842d0d24125efa27c83ef324d7e8823e2fc54ebe57b400591576112439037a83cf84e94f1fa4097e4aa36aaf217ab5e6dc6bcb96667673dd0d6922332dd65ff162af3e0d9d1cde25c9520b2c16597d45f5004dc05217516ca662ebf1e5177398e24b142f37c3bf73daf0121b7643b979cca4f6d41f2e3cf840334c06bb8997f55d683655797ac12924d1a9960b5df47af209ef62af4c74e1a4b41f7222123890df89ca426b4c9ca506dedd91c5ecc49b422dd7956802b663d5021d8c6ce2608da923ce18e8579cd1662262848a9b22b8d0b6c0dd47125368a15ddcb48924b77b96b4e44325f9ca8cd71608ddbac270fb98c1d640fb1922a3286d7ef7c78677ff6d91e8b0d6aa5ef65289d7d1592bd4f92572b4c4e63d65bb1a7788a77a946aab5b05e4d0dea7e025963b592565689b0eac3ffa33479ec601c372557cb1200ab663f4fe8d6ac75000444cb0235eec60a7bd78ce98ec59e6868a72191a84ee7cd440c41e96034e0aebbb9d6f0eb31efc8d625fc93e800cdbb81e795c4565323b13643643437388061a2ca14aeff315e9dc7513dcf6e59ceb8ffeadd51b1d64f54737416d579b1791700bece652590d9d12856aa23306db34fbd2cf5a55b45764ea1df9713c97209f2795a260444577be4734faeed9f374aff17f7d2142ebbca393d671cf6dd4dadf36388443d38aed7baedf006c14116bdf4e5ea93f43b00052374f1cb73cfd3ab117b3a4715e0d401931cdcf2a46faaf76ee75f467fe154b71c946fb80ce10ff73bceace0a49c83cd3fec1c29d4f378fb931ae5eb7df116d750384e551f9c57ecad04de9a2015428abad91aecaccc5895f83b52329d91e336ee8ef7dcf15b81094c41d9fafb27872e48f2a35b6cff3cfeed620e69d3409843b07280a2c85dc24ca44ecf1e38fff5f1ee4e755559aa7b0dd992f065e4f72d0e3f4aa4032ea7b95466ba5fef2b3268f100e16fb60a6b06309ea6d65e2c6d643e1ce956ea86a11a6a09469eed18a8a7f27862af97dd751222dbc74db36e96268659f6df8afb11bc77595057661eb07a0d52f12a0399865c55b40c2886ce2d0b800386119bf274a3769e5e5135e6e99524a62690ffe50381c0db7aae8bb33294d4b031d984f3fc0e5793409f45342d70c352e1401ff0ea7eebef6cf2ee8c5b089a74f96baede0d4526963263816a0d2703576a2e7ec71b619a6f4e6983c713e51af9f8fbbcc7e40c2ee6c97e5813e65f6d22a011ff09723a3ecd1cddf22a20bc2a0c3d72f77d8191a081e6d1582d15d376c809545c38411371421307a90362b165fcce2f77f08e65f3d66fc730c291ec15e8f5f27e5987fd62349679b4b73dba4aaf591d7d85252cab5c91a6db26a02d15a1d0bd59bcafb56d7c2eae07be1fbcf8e1051940819ac1573743a75a408b6f79fe326dc28480c238650dee84b41d332bb7a08745e48eeb6a50857a2859e6e24ae0c18b9f42fceb54972029d8c5898f3c0d5e5f399d7b4f1040cb9bc850ff424ff9172329ee93e28d03964401077fc9a925eae8f95ab053a12cd20566f4b5be1f47ebcfbea1c57c0855ba9aa5fdc8b685ed3609c27e9b574b7e420ce7f2067500b3e72ea5731dc19110c811d12a3ac9b78938db4992525e6f2b6b0bd91da302c99e3de0da7dc9443d36c2fef847106124e428cf6cdba68f8e0ef8b80d863a1fde114766d4de6e98817dd0209ecfe7469f4e92ec6729d815ddf1661c2c7e74f146879f2d2caaf091a8bbbcd673cee92577df389a79a3b212dcb8126b293306ca04ccc14c0a102a9dbb5e64c005dfed2e6f3237b640d20af74780e506fae83048600b915582b00e1172e7e5dc653ad8e20fcd0c1385ba1dbd57dff0daed7bb34a216a9ca03b5e049fa2baeec6fef3e26f4b7b7fea7a74829eff9fa8d9a2866b3114436d27b003174cd8230bed259dc0c08fc141fddb5ea8d44407ba8bfe79cedb10e22df90cef930e828e83c5e05dfc79e15804d4f12c2b45bcf2ef8750fcb7e810269a4dd317bae8ad1d38d41c2b8560bde89a1d1e592e112c8198bbc1470fd5f4407cc12635fe8cacdbca11272509596440cca71b11506e2f69371f2384806662b35a1dc805557f6a6d5cc135656233fe42e062bbe0c7f5b67d151d7b1a01d0d8ccccfe3f051f07bd9870a5bbb326063a36543685de61713dc9696c30be1f41d55b461837919c030a65fde93267a7abc5c08b150a2f59624d4863f1eab63d9fdc30fc91058c42c133dfb2ab521d8f5ac9ee9cc6de39fd996d1668faa689a7f987ae491eecae3eb9852fc1f125c8204cb2a577e19e46c540974786154f1f2f9ee7e04819bdfdae537e4a1f3ee38cbf6d00621e1167751793d73861fff08dda0dad88edfa9f28a90017fdda6d028eb6099b3307f533fdb8707aaa702ee9ad50ebc9164028bad996121c0db8ffd8ad53df91bb4039a0a465582b0f1fef235b52c17dd7b0fa84d41509f7320363608617572c8e3d9226d8069bd642bd2b54175ad442b79fa948bd72c60f6dd9b531aa816e5d89bb4f5f2b38d1aa02e27240d71c6dda7361620d0cd65fb5dde054b36c39e0e8e71de1fcfac1ffac6f72d01a7fe096922e2e43b80e7ec87ed3890adf3b8ec3ce15d223aa24464c166a892b632b9f7e320fe60936c5209afe733a602db5bb5e9b30f09d6c9f6941ff7fce018850c0d53326fec5ce4fc8ea2638302329bc4bfa1c8e2637f6cffbd4f077bc6a9062de4d4152e9f2b56cd893e5c21681218f499d9dcbbc1859a76a28336c27a9f2577a21a7165020d9b8abd59f153286005363798b13a4bc8687dbf6d6d0d1814fc2d3dfd9154745c9c901753bc65551bc42c08d25ed2c63e5e9a2b85251242d3bb387a175b05f2609ad8923de9a3ef8f3524b1b8a4cc544ff3c4dea93b44829a908bb39330e324da0a4768c24507fc58cdf5d8b257191626c3c37b7dbcfcc451b90914b4ada23ce8f9dcb3f2c9010a65fa847fe8485bab1de78192174d97db790736f5d2c50b9849738b9775a42b63da60e787c4b4a3a2d80b0f922bdc8e8cb8496c11b849b249def74670c929bab3ec49d88c7beca1d1839029f7ab1bc8ac2e232bd2651621b7ed89643e4a4e41a12939cb9990144508ccd6fd8ea5bbd3597abbe9eaf35a1f86f84ec5d71fa5ed8a22b39e98e2424db1145f064ac1fe3345bc582bf4cd55901fdeebc4540e7a00c7b199db842422b00af94688768ddc5a76521be7d3dd7878a2ff39f4049533037030fb7132e293ca95914539a29907f105e8d4bbb443b3f88ad0b9cef4daef508957047f6813b4c458d2182d2c836c66b726e22257e23981049fab614b40a8d40535bd706712b62c4c40d8c71c8f916435a420080dd344d126db40e89820cc3c40f920cb7150af5ac92c3814478745038800aa2f481fd22d539406d94b31d529c5a96bc3bead4e982aa54067c9f7bb8fd89f9330007dd45526dec5beeb6c6f7161c788db202d89e1c0ed3761efc64b2f3b6032c8c36a94e14cd5a986f5e2324ac62be12362bdcacaebe2fad80389c3512d6ca70aad34837e6ec6087ca60cc67a9e58daeedd5806661e889fa7b1219901ed0a94da6de331092a0003b35d86e73f38222df38b79f76f46cdd9ce6711612751a3f5b5cf7ff0159d52799f5ebaf60eedf1d683b1bd9367e2d0c2aa9f312f31b7797d4a7411326e1957e2b2e15a0952241f19b35c1a12a3ecae5199075028c196f8e9b25d4dc96e14fd1e42d425192e47ef123d81744178768c1a1d3417488bb567190d7f224419f0379da1d72874089f2068c29e1d7f580520e45f2d73b5629ab484067d703cd9e8c3085cb9df4fabe7d44fbcf3941edb392fb827ecb556723723ec0a6e204ca8f368099230fc49837fbc6daab32c6ca147206dee7471247ed7a938f392e293ab0185467c61307dabca953737604684b8c615ddbfaf5158f067dda557ff770004f3052c494e4328094df472ae176eccb780d99b4bc766f15c6db40217944c0727471e0fc576ec754704280423bdb604c58021543381b149d82776f6b4d62de1369b4a3c5c434608745cdf83fbf7dd227b95190257b676647d964db06741ffbea9c36c8572fc99e2fa90da1aa3cced1589541e13eea3071b68cb0c663e1d624afdbbda220c2147142b389bbffcdf17ab43f9d50fed6467f83726b920fd5795b802aa99acf792d7d8889dabe9ba74015811e026094a7e7916e2a2af7e8249f710cdd78657a00efd05533bbc81c77d7e9b6118860388faf337b7ed40fd4b5d9450bd9e5ad3f41b4a1cb081298dd3e07d5f829fdc3cbd07a83d14e8910b8452185c14f95d576f57b307c93dbafda951ee6cfece12d452d60352391761885b483557bb0de49a1ec2dd74452053abb573e1598b35fb533103564beaf0828cc316ca0dbbd6b6a48729a61cd4c5f4315ea6bdb731652a00c938b0c89c74cf6e76213211c724f066fc6e9dc63572936f6bfa19f6c8ffae84c46a14dda92980a48de8523eb5bb61dcfd72882db6ee82482ae51d2d878d46854d08104a059aa5c868ea3fb72aa858fce07734d467e69b62b384855f38a5ac1fe57a2c120ed9976932255a4c8d0fe6cd87d53a686612417ee62f9bbfb599810918312b564b85c050db4752236912774d77b2370d0c2d9ed873762de43f08c5b185c5ef08622ede4544d34a7a54e710d5131d210d44f5fef98c9547673ac514d74677567afa14b6842d01968b200dd840c571ded35f3d7c72f24fdfce43af89a2079faef5f6d8c3c9ab85905ec739fdfe8d7ba087cc440498df3df9b9d2e9d97a4c8507cfc176c1371804276eacc1949040b5431464d4c57ea16334751a39bbe4d0e4268348045288506cbffddc56a5c20ecd3e02529bc55956fb2fe17d33345aa58f507c4b25f240689f7c86016f73bb1ee614f321a7cebe53c8ac6a28163d4ed4c0921ff952fa519daa656f9baaa04175dc894d04a4162d05fae441f8b27b4104f6d71c0f6afa81795c24ea2d054e4999537c140d405041ce2048690e2185792c78c4b2faaf88983298c8bd0e2637560dc191a3a54e6cd59edcd7b5bbfb31e46fec79d2d05ba76d28cc9d88ced02247dc2d15a94b86a198810bad0f6fe5d9d2cbbf625a6feeb401dcd8c283c8baa0644822fd3070bae602b56552551d4ab5c440c4cca0300ff915e6d62a9b124c73a5a81c0c69c49d027aa8e4ecb66dc5fd1a25688577b20a525c7776d5af351ea23294708118a62bf6b888ede3ebd355462aef85dbec549abff827b264a31710d358c99897e645893ec780399fa30cf2acf9f665600cbb7abe4683e693ad5d823aa2dc0b0a3eade739ef66172e4113b5496d1f640e95d002d9af8d85ab55b24f37c6d269cfc06dff1c837666d0292451607a4031cbd5332ba520ce6a5c739ec7a12f77c04cadb73af3d8d1de9f4d5ec3202ff46f8250803853363025e7382a0623916a243910e509ef26b52960495b781a386aae8ca1ec18dfd57a273031ed657e29db0c638a7e7bc95ba97536968cb686b0a848f0357f4d50f873640f486407bd7f03a7b35d7636d4d3b512612471f8204da9baef3ab1beff471577912db6252ac02fe33dc580480b1dadc22eca0f7dff3698ecf1854cbdf3ac03956aab861a14d364eb1bfbe641f7eacdf97719cb186e6f8d434b4616cf613b6b5224515e4976cc0d39df4b7175876b60cbe323d6a685ee9db5c43d14dd7c734d9508abbbcf1059bc1c3b54d2ddc72f5006c9a51b85cebd2abdee19b589c400860c672e6da6b0574bced31e3bf3ed1723344a21354c781d7972d3897bd80e35d7e61139bb5c40aa98e667cbd2ee6771adc92a0925190149bf694b1a0b154aa5cfcd94a94292925c55960c3359d6ca11d5109fcda03c303370475e1389ff0e5e3458cc948a603c364dbbe37a7dd89500468bd7eea55d0115db20abd130df672a773a460597d8c45569118824b28bdd6174945f25022ed169446f95461736859af57c16052f87c07a9dfc07d6abc35e7fa8e84011f53c503761d70dd1e140a261758db7d2f3b11db6e8954cd7a4474d3e4ca903e0a5695874d92080108fe87c0807f2a22aa3b69b49773598f7706a13337715fa30cdb469998fdbaf7fc7150836b4e35fe30ac64bf5e342dd95666b17fc80a4e5f4dd5e3f3774b455ea9bc3939f8044a0eba6ce6f0d23a402c142cbb48c726c32c7fa2da4aaa1d641c555b3b22ba72ae72a090681f63fa52df406824859fdec2730751957eada20a51ec8d11ed9636a272065f0ddbed32b32a8af52a9931d21fce79f6ffd4d01ace02ff0cd8c48fc505b09bee3409c7e1a0acd9e8043057b8fa05754e74ba4b1159a1ac6d08a6b9074b14b12353b2278d96059347bf5024d98af7691b3287f3ca7a9d7e8e9ce076856c4e4327682014c2b635fbcef983a78f1daf7413a836490c4226173631df10805fd9c686ed85c9b76da3d84a900c4df0043bf87b053f945b35fc4897fb481cf2a460640ca2bcf1ea0017182f90dd4cb6e5ce7be3ca79f181d3d4e3869f9ed78b396e4993f43492c32ac363231b0a94065cc24181705fb86ccf7717e4e015630061dd446ad17202c46f76196e1fa9e8616cd32daa7b5c80eddcf1594c8b31fa7f9f8f04257d20e1d030c3b9371050698fbfd929a5439f0c6ee05d30e1c9174914b120e5c2432b0f6eca62582db6e66baace7186d4d67bc151882676c8a91e80e5184671f6e58f66c46b062b531171d30b68553c227857e55526c4b6c4cb6c7e2c5cbabe461c94f2c374e2d7cf7684af442703e54d7218f0fbca04b1a12d53398643339a69cf86f42a198dfb1aa4dc24c839a32b7f2fe87945872a0d6e7b2fb9f9c15c6e3a0a8b31e9239095f6e087cb791ba3feaf909d13e1e6bb70866985a66c3a0f32f27cf5b84b0ed678971c38cd35072b7f14e2c66e86b1860b06cf905dbb1d746efcd20c82950e6033c75f30bbf3537697a44f684885401bfe79dac2e996a570b75154e56aaa3135e0fa121cecc3b86b34e76f266b54733cf5b13b892e6f1a817053e5ff8af57f0499ae713892565f67903c3204c3b52488fdb2bf475c91735b93e442552d7814a296841eb56f0bf1b779d0c9fd70ae71ddd6990686a540dab0cdb431a2501cad7027bc53e390cbd8b5bee13c72edeb7f734530795569338e6d025e359583db9aa04f508cc71889cc8072b80c407147222bf83e08ac1fe58c01e29969ec29a7e8dfd491368d5042b4fcc8362e5f9b0528d29a789ea9b55e13a8e9791e04523c82f61f763029810ed927469da41dff69d83dd6017fecc539ee8ce56efd8706f4d120d62a1121e151df0d51fd0ce463acb668c432520a9afc250c4368e57f437749c886cbc7e4863ba7657fe579909c8e0ebe82e1bcfdbcab5236113cd69eab13b323a4184beff2400d5e16af498839182c19bc7b47860d94f83c539f811b617954e836c03121ee946bf63852539a0a0944a3cbbcd5e9ea1c28d17645607a59d645eba75f074feb448334771e9da959b4050fad794181c3a111f228aeb56677e1c11b150c0737882cc21810d36ecaca7a8fceeadd9ec5fbce480f8b501c8b30a4a2fe5f947d2410f5153ae8c541c1dc315bb7e93afba40605cd5c03b1685d5017e6af064066d456e1e2752581c331913ea7c075a59afd331a71c25e98b64df61359b55a1e91c07a29665dec8870306d7618181af7a4dca25ba1432085e7b61b4de2a0ab0bae436af952adb527d16674f61b1777ae8bccaa24c4961864df57702f54bc199e954dbc4e26433d362c5e3d1d26efc1469aba6904cb3e3b32a310efa9f4612e45cedaa99b58c4e6e3310049d2cfad1c73db026f756167ae2eda5eefa6beb81c3a9cc49181456a989c4ea088dc2c6adda634fb992614b67b6ee7cfabca9bdd199e146c0f900a45c873c7f8e36dd3e20cbccf5767d0b6d6ce119f52c51366a0dc8bc9623aaaad0d5e4bc70edaf5df2f763ca78ba518beb0450af05a60672a5427813488aa748632107a8257de94c4d4b7d6cb5b0062004c09a58c629b793fc28dd9c7e7da2ba9da10e8d4cfca4049d454535e42ddd0fa0063f76b5a910647add78bf5e0916fb8bbc272eea0e7439f492d9f1fc18c9bd6c9b1b641c794e65a3378f17646d3406ee73323f08946fb50c7c9caec6099f5775b626050ee0cf483e101067d84fe51100cbc91231ccb5508c6e2b72e421fe2c4b953683f17f2ab7c69147a3eedbd76bc1f643b224e6d753eaa5ebcd113db9d4432a49ad900e36acfef097902b252b59f75f19f572f2694cb3f5812e81fe03ff89e3ef16abbd5d9a9ce7eb102349c2a169d7c7d5b96338c74fda756dbabde3bd9bfa0d0f9402820455dec030f5a2ee81bd66e85e97b17087833e68c08423de7d322897e7d0e57e73e1c472789110a8a6cb58805a5f4597d0962af77580cef27bd81514655719ffe4047b3bf7a03921cc1e30ef7cdb725b689dfe330e8f27e167bba86730bf7247be88047677d40924b0fa582462c785ea22fea1dfdf065fa772853846bbbf73530c2da6ee12805c7fa6f1f303021732b74ebdf6921d1022af0ebce3e76730d5cf1a3b7108318e5930d7b20c6aebb74ec378a47c381929348c1940fd6f7e97938ff5939cd63b7a8b9da23a70a34127cd62e6459e38381a3a2d753d6067465ecfcc1905feb44956c792b6dc60eb419bbee7ec2ef74d919359e41bd2e42d6ea150209124b1e389f4d4f60b960ecdbf79441ba82f9a563f3d18e8e9d4e43f31ab2b0037e25b220ddd4db3f44e441ec19e6f130217ffeed869c4d3a3465eb3419497b2b491fb926fc23a57c1182d2757357fbb8861698b94e46e54ec13e000e055f9b1e1062eb2ac6b0e281a702a0cb7fcf6530f6be0f38d730f82bd04e30a9c253e32f16d504d8e887b4df22fd72e16fdb91e75f8239f81fb1ef41fcb4e10a07e7ba02f83171e6ac426eb1049b8ec5d6d8a938c13e01be4cc1deb73eb3a845ac3c2cf39ce7ee8865af8d0d24a9a1dd8e32e8bbf6aaf641a02d59a0e6bb4e3855596414bb959dda2ba53996af2167b3154a9c7dd0c062aab821dfc47c2421791af88afeeef1b035babe74a1fe666520015e81fd1d5deea675e70df0cb2e811cc2aae461e6c1dbe8630e5cde8fd52a9ad18e3c49789b708b129fca69daa48c7759ca1305cbb45d43226524ef93e0f70b6302ce4da86705faf54d56b4baf586aac30fa5c71258b75870114b70ffdfc2304cdaf6b1d3e3729a8265cbda21b5e1bc76a49ec20f7747ac61b36d3aafcb5ad1d298bbc2e3e5e83f86d3c7a75325af5d6f48610e5bf9691bcfb3714bf98c6da081a37ed56075b890e33b187dd7477393981688966ebc6c688cf24c617e76b88c4e7ddfb0363870e75e865ef56a06440a4d3375f847942675fbd68a6b931882d9926739b2843146e407a5846feddc06602604f9eeea794e54549047f8a3f1ad6db34c10e702d504730b1f21c92b29d7133ea69835388b500a9734b98a95f7913e29c24606c8e151600f21af187406978d8e6149fd6724bc967d1a3de20ee6699e1001b82f9d60c65ecb99edecda74de9219e1dea4a73c0fe43cc17a7ba186bce11a47a370d2f7cbb6940f864943788d41c6661a669bd7805eba1059be61afaf1badd2ab5703a8e6d18448d9d2890747b753c2886b6f634f9ef11664a47e6e8d26e43bc6712f91cf606b225df862368dca37e33f113277185d1ce979df3e4d27288c3c823b57bd3b2f7ed90bd93a5fa0d4f44586ff7f82e33c50b3418930bdc56c06ca191bfd2387651a19c1f28126ee44e3ae3c1f23af90f886a548b2484bae1f425fa7aef5c485c05345c57756d1006e065015ea97e0f0c8463805a7c23c6ce6161841b72bdc1a5470c4b2281f791766b0c8e456f58cad9082f972a00d80277b9bec2a9817f5d8bc55365002a0975d9ee775c083e880fb6368b917c45c2c7a11ad83595be391f67687b983b2603f2839fca9a057f92d55d96602c158a0b8760ca6e6edf3dffd257f4b08b7b6da60c731aff2a258610cc80eb61d38a73a0942066f7d1d7d40d62a42c0530f57655c5151588fe5818ff8a4ddb1f9e236e48e319dabc19e970734630bea50000b92c357a4fc1dacd214f5781d97ebe56e8604946fa024910eafe9ad79678e64e35c0fd309b380ddf456793e4d135560b02b51388b73f34b045a53bb2b41bdfc21d25a31a5328e5455a166cc99df8accd41b225fe6fefcd10dd9716f9902eb05907678cfbc97d86f24bb1a0b1205da5e0e3bda468b639574a1d1e7df57897b99cbf094ebc7e9ff47c3d00db9fc876f237aaf28400263595578a8bb5958fa8901d61d6a568fe4880d6d6b273bf44133128e9be4d4821bb0b06540518a4d7e49cbf1b511a8939d827164a4a608beb883f9f79c1abea7a533fd84e485f8fe87c1347d47749fc5a2f037df7fa82703fe4c7a6886c329ebf4b8ccb9a035985438fd5cbea092581efb8ff1ceab177219ba0a9ec20d574d738cb5c13ade5297bb1d9dac6c3e809263116206218c1ae1f7f6af7eb192e864e7270645ba2fa49326748ebf5555f3bdb14c6a6b0ad637791b6902e19fb774cf9696196a81b4bb85b7be7c2d31e85c59a14f0d5623a7cda7b60eba86e4beac0666def8c91235c93669a9e54e3ecce5523e9f256bdca419a048fdec2f8475975c3c3abb73f1c02a9d12e81c7da7ece80bb15c166183013e1c43d246da5f9cf5fce354f120c9beed0adaf5e4af2e9dad23dc66ba78e8989690a796a550e654ad33170c08fdab8b499fb1750b8e7c10c9c82854358a5a807d49cc5dd11f437bafa25aefe3fb53eb47f3b9cdada7e1823f568103d14e7f5136beb2475e1bdd8a440384038730d38846363ce655df99e214c531d5ac663146f8716a4f20568120be22eef6f000ada455259a5a4c47044a29ac714d9a54faebe16324bcc312b47e840206e5b60465cc62ddd59993427cc219a877d2af01dac59cd7fcbc02aac356ba4e942b19f38149786d132d80174520a99f4c14876afc65094fa3a591bea56037be0842a84b487ebc07ae8716848e4ec18f376fd85c77e9767349e4d70db90c3e31389590f5079ee11d82c6ae8dce7e45c17d4a38600834809e338b72ba9828c98e506c75e6f354b6090b6b447a178a5f4e6bef4b5a3ef2f10bfc5a1c08b41ae435b2a867e120c3412e3b65e6c879ca7e233a367f95cae27157380b47b8084b38cb3032868ab8c61444bad3ed7d34f0d43a5b426a28d4240dec85035945c58eb167b1958d606f3f2316affe6b27b27edb4366336c45d17a83101fe9d24a0ab8fda136450f45cefc1a05f50f7b14986675d9fc02b348f4d1f4fa8c1950f87d7d41981191a6256f09aefc65ea83c7c539fbaaad0e70c0630b15f24f201182aff3132935d2256c6a8ec2e1c4244db124082a5cc2a3fb06ea72085646e5d8054c91dfd9a69ebd7333c31199613f9f820025d38d6f9340b2102dcf9501b5de407e53c69a75f20ce7903fd14f889a00bc18effc35b53e867d71e6013aa5ed095408aca9b77e08d2d055607e22493a1db5d23af58962fc805aca81e263c528b4674effd99bf2c6932b497ad56ba68b6416853b412ab51b1147143992b86afbe77617d4c95225de94c79390fc08f157b40608b2eef712be3ee2116df32b690ec7b2954acb3868cf80af755da7e66c91f58b31e38bd3aefc1d833d468205b35386f08f4d59a372bc4e87fe929184409b26360b76c97dc6cbc849a75045dcd8b90e50287c65abb1b9758c3e9b3a409f2d49ac861e599f0146cefb0f418560fc476b15388bd0aa2c12b78601e9b0c852be42d3d854d5e1412dab7840e12a48f633129d562c01ab3891316ea902e5e12aba8811e5dbb4697ecb9ac0ad1d55d0d1e383ecb661506010bcd8c3c98788f1702ee9f9f11c3ee9089b211d79ded9cd89638afa6e80f66325447e083a4d81f6d199079637c5c01ebc194e8807bcd5652350a42ef2dca683af2ae6d442460057ad8efa759a4569d2392ee2e52b9739b02253701d7dfa03da8eeacd758f0761d8b95719d26251a9996cf33486c424f9aca2919243727f77c619de66f724df9febea5e534e24853493a3e12e7f116fe219e251d7ba219ba5f29116f6fed52ac0451324b702bb279e5a04d930f18315412f99f53cb17797ee5e886ee7621101d4df49d5a4cc55828a4d6bc0244f48291bbe02695cdd54e1e33adadb5f6de5da9f1ab826540d2bd2d7b3a37430e513bc0bde64cc6f709695a354f81ed946aa9584b8e74ee6d36b4c12ee80e4125025a6eb0f2bf5a8df5c284678a4dda4b1a151fd926cecfee230b5adf5e688157b7c959298b99abce431e78896ee9e8014a2bfcb45eaeedc30795bfb5611154dc56df9219fa166d7ca5ebfca3e43adc90812ee1706a87734bdaf69f8d1ac8fa18945cf1a02a8927c67c266cc1a3f2fcd49af416ac34cbf08e76209a9fddfdabcc482dba32622c373951e56a6ac26bf6a720da7f844ebb150eea329bc84dbb4f2071e35f64c697c75eb277bf72d34da17cac8df97f7d2073ec612ad36cfd0d37de8049fb38918988ca4671faab62d238a5211fc87f3937aa69d194b648642c0cdaa27e1f6bcdb25d99007c9ff8a7436110b3810b50977a881eccb080035d10b789109f82cb845289b7ea31312fcd2219ed7b01c3fe092ccd0109ee381b77b12305cd7c3f8e805da810b85da6ab4a97f2ad811bb1b3c92fb2281358103d0e94e23105b02c67c4fc40ba23f69bd5d83f4a30bc6de761a9c471cf43b81e54ccb57e7f7fe446b8648687327f17df9d30fd0f7ab4b9ac69825026c7260f078f7ddb134902d34c56a88019d6fc63fe64bd70f8980d5c47e4ffeb3454241753d5f558b5e1daa4c7f670b6d2550b081b5da7f216998228a7bf5582b46d18cdf146e6b79e04972036c33c2ca4c997a25f381082be5ff91c9b64fcd172249aa8e510b2e49980847aba52096a278315dc180f9adbc28e022c6989fd81f1d50f1343ccefc38baf0a13af57293fa5251d35042136104c53f45e8b16b7c69162ab90123b48bf863f5ba201bc105703e066b0bec87d51abe8eb9ea2c884ddd336a72205f81587342a6b6f31044dfd19fff7f67b86e39440e4c048c10246b353dade7210c43e6310d581556d02d493797daf666fba4d5333083eafabf4f55bbffd6fae62a619ffd4dc62f511c126a767f9ce3a07b202834d821813a44e8aab9f0d5d201ef0e70e0f99a7aa3b707ca3ffb059338e1fd49ef732c9d6b84aa823d7829351115e26a0fb647af03de9ccf41a302057569c89e90e8cf734cff133ace4d642be651096688eb9b56d9787005ab506dbfcd4a21dccc7e039690d6ac4fc0a5c2b35b2fa749b73e93b4cfb626ab5d7a0be280e221b3e8c6d428acee4fb9ec9e60980d239708db050980bae302718dbfa45271aed6d5e4dc73c7d2e2d81906b79b8d6d787858908cc78691ad1aecc0cec258a49daeb44316e7cde22895f25bfc7769c695e683de31fbdf6fc381a2271d7b9aa1e6b90dc0e2d2456dbdd50ccdac8b1d80faf005931733609f5ebc0eda7091cf062342a75797e9d8bec687768ff8e32f40dc4a88683ce2a9883e74594631aea3c0581f17b72f4d0ad2ce2d3fc51168d8f8b0635a1bbc1e325311470758cea546fa20326bc7a1b547fe3ae27818a662f4d394156ad0d95f72fbb2c71fdb13f9a27ba84e9e9f51b9e93b54a1361c84278f6fdfad8987b8cfed54eebde4ad7f1a7c36c91f768aead41a6659c0337ea6d1050362932d8a708c3a131a3017e6b5c027b69f9b0f269458ef9598d43296aed5f1f1301f3dbc3d449aa9860cc956571a4f465a1ff887bfa98ef0b86561159e4e329c89e4719161b6d043f2a0b515bb52002c93382ffa79f461cb3be153915482b0e318bba00157403b62bfe117a2a98dd99c84c64da12c17b617ad3596cd451e2b075c7c81f60ba55053f03ec740968951bc47cd36fb5ebbc2405d7a23bbdd44a02e4a0d53ad62f62a9490c9afb25a3087dfb5323972c645c4a339acc2c0c911029bd8dce258bd542cf554965f8f151c6b848c9ca19f543a4fb9bfe28547d111607a320601dd325e21dad195576d04fd1928f7df9fac1223b8f8d84664e7db6d2fd30b135de02e0a196739209b717d0731750148e31e98623f13d646dbe49b53f2c05dfae95c19e47051dd805257af28fd40afeb306e11d135ca94274e15002dfcfbe9ec225a630330c81ecf0c271a0d0f36e4ca400018c2210c421009ec71e3157284ea0c6bf6923a9704f17653cbb5b8082175fb53243d853dd28c9531b5fe53fcac459057037b37f79444d5512d169e234a01b5aa960a7f459e4f9a94b74427236bdc3d542ac077db59194bd3e53cd7215412896edef63ce4c2c55c7bd665401cb3858f05a075150072653e14655d743e7b38359a99733407dc086a66a9346f6b8c6bc5fc67c3132b2f863e17a3f5171211a6ab52bf39ebec88348a8d3344106e9ff9ebc503eaeea7ac9566d80f5e105573c04e7251762b19f48b920002ee8c235482c1a8730b1f6ca1ad309a4173d0097fa9b97a74ee9b1ddd49a40b5acc776f718448f9ab223955e96c06666378b7d75f65b3ae4f275a76f196da9b5f9f07347b3843720e6c5f117db877cb7521e2c86efc8fc1b11041ef07e00bca203cf83224114cb33d2036cd31720cfb470a4e4838f608a00b1f751fcb8027ae8294188c7e9610800ccf47e55c0d5fa83bebebbdebf0b7d7b207be7dff01ef7ae7589a07dfe0d936122845884044dc3ae37d5d10c39c600b4292eecc48e1c8861ae3cb91cb66ef89cc663af4570fb1551b1fee95f88399d1fd0db372f37ff0b87a10a23d417ad530a818a785059f79d2baef6ac0440bf419b4b3173a94b332b8fbbd8c6cbf432ff96d95b6a4584b6a656ceedcaadd13a82bb77add64084650bd61b8e23e2bb9ceec571cd06ad3885af20d345719effc47df20cbfe12f8a866aec14f9cef9dbf716b6a41d93bde757a10b9a87bc3b267fa00b3e76cf095b3ada55064830cda33bab9ccefd6a154dded07ca2e1bf764c4dc36f8a15032b17558bc1cb78f9caa0f1519dd9c0618a7c7a3e970e064f7398ceb413e7d008ca3ef73e628f8d1901e07070b6926f21e045774ca6b427040c27df4a997de59a67e464a6f93875ba587252fc387f6d3536b7379609c41b351d0fbedc930ce2a6df76c1b2454da8253a8a486e0d6db413efd3847ef70a075324d77132e212a0042f094627004c715b7e74421ed58cfe61620d17800a2b31ee364dec4856505a0ae9285a4898ae04451b5822a043b9afefe033aa8be8cd3654dac6439b406f4ea00b3bcf3b29e067957f799ae59e855c3695a3b567e67c76abcff593a824227874b820ea2a1527a3700a36d2a83a45ce9267ed87ec81c80b26baaf4898abbcce637237844db2083eb9b51c3487f8de3ddfdfefe58dc33a45a110db770932c967a10be1b23129c68a5f17b21f0944d91dba11b9f70a52ec17060f0ebd5de2e7edd0a8ae20c2f39047becc9185808f6cd4d40e9f75dcb27e9ffb72d43ac3e56d6b72050d893be68289999df6f694b19abd30719f1ea8781c9afb53bda8f5704a9b83ee44878a19a4cbc4c3a39d358461e97dab59629911d41fc737bf36aad4413ee24e5434e57a8869b3a814094dff0e3a9e18e6ee8bfe115dc1cdf4cc27b9a3e839cd563aa8d8609438ba3f09b94874860eecba18d175b77daf38ee231920d3da0409373b09362a04ee456dbd38a17f95021d34cb72399b0477398c88d00190c8fbb98bcaa3c7c47ed5bfc6e87d56a9b8c50c810ddb627185fc26726f1712a4c61c1feaa82c6cc56b0e9dc4c1988bb117a92a10dca3d6d0208ce56840afaa205182aae2bf09dccec46335a8c1ab8293e5cc1549a7ba4f740b6731933d720daa0b02063723e1082d40ced1bedd9c9bc74879b3b9c815315ec28aa27f991cd842e404ffdf7c48b4ed7816c09f5729a99e62ab57feed668ee2b092b77a4aee3e8e5bcd988878a19da6a4cd4173cc85cf89a3c84229931d917c69d0f966e8674252484349adc4aee691fb731a82ed2e660d45409d81c6d82e4e23972f933ad4717f34ac4bfd271e934f7a96c23252596ac8ab12a4b1477110aea5718f61ba915bbb5772a9d6ee0eab39b79f68e67a12a6ba36baf0be771fa301bc0b7a75b9e01026f8487293726cb660b15bcdcadcca02789dc2d4493fe93992ad4a9663020da888d9cd0b9c3d5f8d929faabef9b0763728d28b2371c091770fd92c1027122cdb6225e4d06185a5afb26aa24f462e201e0e126553dbea25e827a999f0c5824b086ef073b9c3af02e724134c6a2d5df188687e8fb993888a7d944f5f52a900db76a391593907c6a11746b5beb8e859ac760ea085cd6ef60832ee2327f651b54966d54f6529ee7d863851e82aa912a69dcd0f5b2989d906c06a4c7ef982f64cdbc06a02562df380a54cbcba1a5500629c9d159c1d7ad754d01b0de0e3708ade92b4008a388c835b2711fac5ea739e2d678acdd9102921d501adc3f79a4a9c3825f20606ce3abedbfccd275a0e2a172debe2ec47fa2439ac90b7712411ba8af947385c62be6c47bb143bf5535103f04b831ec4a658f3ebae4b816b81d6ac84225e7b36d802a2ce21d41fd09d482838b35a38d112b0df9e1b4111a9ff5053cd4275f5c7196e7e863bf7b80fb1902ea1079bc46481f25d0cff8cf985e45c8219e301a604b07e6b8b462eac38b7462e5197560e036bf92abfa96abd37b98b7f4720ca1b75b5e3580abcfa5d3fd50a0e0e89f75adaf8633d5c004432483989c27af228efaef9809f7c58b49def889615c69359abf8470c0f19ac287a328cdc1a80425a829c62369338d98fbc6a759f1d96bc5701d8807b5072d8b188ff4becee119850df56c375eb46050ca38a90f4291dd639fafa61c98fa12ad845f841845912f3f88c9185dc350d015cafdfb862d6673b924f39aebb0d27c6d16da4d7385c6e5ba0ced06d42e548c9bc2737da5a48112893fdc1256d59c17a2e10d1bd77f51457af4308f888e44a7efa0a24502e9053882d314d1865447729e1943eb61a5c90c95dc4fb5d70879fb7f9531cc1d36432edcf1de2c7460bb3462c52c62f9df401c83cf9a5c50cc0b50952aead541d0c5a69bb792877dd91cca5253af9e1a7f69693317d51658c12584a89224c80d1e39c1d68f1eaecbbda733e6b66902f9db9838f5c159b4d04df8cedef99bb1e28c76d6ffeac1dd17fd15f22fbe71e635df8ebabadd2277505586e4368a30f0b090419c9b67f4d6211f4e11435d9fb064fdb18543c8d4e1ea20c8bcc2a4d30ba609be69d68980fe07b8100a79a95d865c4f4bb123d9d50a7dbe4cdf7eefed69d78982fc8bdfe2c6351094383b14feab2e03aead3aa98032f8785ff7dd07b9ca8144fb3507d7d70e206059057fd5fa59a1b34d5f448b2d4f8e946c4b594de0f7a0711a7881d0ff6c5a2d227a40cf4e477684e4511b62b9f61f1a2810de2124965006321f19de41c1cadc89c9a5937d3faa14a7640cf7096e5cf38f2cd3b774800b131d4eac349fcedf2c96ad749bbe8d37597078a492f6138dcab64987719d67efca98018daddd0cade129b6ed9117489e0c52ffb57e4b0f6906ae1dc0a808dde71d9275a8e02947da0746e0b85fecdd33a2ac072382f6f8f9e9786f6b5c5e896c211bf438e2b654c56a189586c6a8c7708f1c152d49b852362eab5316b523616d6403732ec24a1bca4c83b24c39a47c339dd3e116277439fba3634e73acbdc6559852ec56936a60174c49e06d1f075fc8deedff0ca8489a7ab02e274c564c25f551fe1141ec0dda9ad65e6d9e569d698643a5a1833d352b0ca1c907be524b2660fc3264fd0d584ee85307c3cd4fc9e1f520bb2bfcc254134702927f6c93e91b35826eb0e26ee22035f6e4a80064a310587f2fd13de29783d60adac0b33d936eada97610d175b7edfa1293cc283eb735dbc6f4272dc46a010135e93b44bb3bdd0295b17eb131884108db64734f7e0deb16b1c2d2c6a308a9d14793ae6f8adf702d8326cd51bab83800b5d4e9fdc24f455d4386a028ceab055e6d23ac029b22ca0177056e09c570eaa6a058e6a96c171761309da425cfcfb7874a49bcf5f23a38a57092a9333247a5a0535d651df5ce3ae939683acb6e543c5d690a76c6c1b64850ebaf4deb5fa23c550ecd876032733e8dcdcc4d54ee9eee55d59cbc32c1b87883bc8042f420f7b0870e0ed3c86e3c4f02c95a6cff3fa7c2cfa7a5b8fab0faf892491ff77eaad9c14b231841e69410e19d5a6332ca55d978f10a68a15cc714bdbd0fa2e461a250bbc405331101cf5911067db1b2b8e731cb4dcb8c13aeb6c8824c4b322f100a46c44de7cf20929a4260edd3095e0d1af43025a65598d14f5ef1617047fc67ac854cd4a8528dcabeab3a899fd8978ff224b7bf91b942363a5f0b770d84f4fd0cab479dc0b197e036bb510e3cbd036124472902ed8a3bb291b2766bf43ab4b0861e683bc9a8c513bb8d0d419c8f759e2233fd49022c3d3699c4ce01faa559a198110ba776fe7ddb7f84b35f0dc5cb20c01248833119c82dd3c4c0aee1ea574fe03728793288bf907f21ef6ff818d8a84f8be3e2ef0030a99d9ed244d2ef5f701b4fdf912bad97c5f17b3964ec23493a1df7af180a6043169d125184cdff0e3019ec548f3638f528c941f16187aec67a087956cb16277ecd93fbad4402aacd81f4a3fd4aed7b3fe3578b7f7d45eddecd61dba568b58b46636b4f821cdba816aa9eea0c736492eb25ff64070a8f681da482734118854e8653e509711dfed6da958620086bdc69f2ca9d816ebb4ba88cc99d6a2a4679be68dd09ecb8bae2bdba1f19411f29ff9ffae37bb2e307005f29f50940f941d1945502cc474ba3ad62fb6249b7a669e175f32aa9d0a31c9b0a56b59ffb5d592018d184b2a51d39eea742a0f1caadfa0ff1109b3317847e674c598a1946ce9599d650c876e32e7ff5201040020cad6df129806332d8d00e675bddfb81b68e70af36bc7563d129e1d2fe5952a900ec18e5c33be80543cf857b844167828febcea34367befaa6038053f6b94867cad36e43709a938bf24ed527630f9db1b181a14e2c97499605ef24691af972bec229f536dd6b1825acc8de434a7f0d4d776ec8e1aa565cccedee8d29271a4f7834e90faed64040ff865dab14058a20418c5ced86830e3137ebaf29a3f8518a82a085406a93cd25293b4fc18c63f2214f17e4bdf3a69eec24f7d7d378a14b5e8d66b651ff88844eaf38194cd22cf031aec24a2e4579aff46f55a81259b37515c4d92a1186d61bdf3ca604a806baaa12451b79bdcbd6cee67dc6c2c2e4ad3cf1f632526f74fdfefe5794e86cde68f05e2d3ee303c14c74246eeb63834b4162af62d27bd19cfe3158565006656a6dd5016704f3aed3ecbd380db5472c3da6be6d7c2e6141c44d5ca2b814c480a434336474569b8a2d81e7d3c1082df1874acd4e8d039c5d0c96f5f7bf523caa45d837ef51bab81c9fc65ef5439800a459344fe0fa75a38408b8b10c5cdc6fb723125010396428ce3059395af3f85aa24217a2150b76fc7f5d8e1632eecae6c628534b4077b4303d3b70e75f5c0ed8664e9809f09a0eba4ece6973dbd78c8ab93d9dbf3bc15f976c66cf35b1a743e3fb9184a74661cf7c77d2e1509e20866e21c2afcb0dc724405997b8aadc48dfa3789376e5cc8442d47a90663cf7d7904990da045cee922f07e2811e3a39d0619c107849167bda52dcf342f99c2a711230087869b8daac7612443429a6f64f942ff15816b61d20568e781232d2c90b4012abea3a26ed2420a536bb84bc8079bbea06acbafd62b0b34cc094eef847c42e7cff39a185775c6ae5695daa2659524051cf9e52c5a7268cb9cc9c135c630824aa93edd5cdcb11608fe8a31f6b7c93c983379adf1b01026525bf4ef2a1fcb2dd64447737b60ed8e4852bd534057360b6e7ea00fd96ed5e06cf1a535f5d7accf37771e0d1fec74655fa646080345dae968542049ae966be7a6b9b3bde4528d752f33aa0ca69f51a80771c0d8718b7033545241bab34a03ea5d1c85b22ba4e5bb2cf58bb76af15d08bbaa70d65f88f8848be6666828dc83ae200d3bb96c1e961784653a11e3cdb91dddd824eb8327231a5d3174996ee89fd2f37fbac1a107d4d660a719ba8c251207103bd65598e79964b2c395f618d2d286a3a799eb64074b413b32ff294854a583d78b9a39d8045e428d3ec84c7eb42697aa9655f10e0d44fb2bc8cb6f615a74c4392b8ea9ae2cc020ec7e9ec7e40585fe0922c1c371417564a826981ed3e9a06845500412d15e33b619d795aa45b603e9a93ff4e58457a0ba97de43554b6e3e438326fb0c9abb1349f117a7817f642040aa6bdb5367a23729beceee6e6656938d3fe1e125cf6dc212150b2d78fab8472da7cac055bdf3203c03ef2cc7f552c6cba13a1302dd8ce23159cd4a69814382825d799cfa20b8f17802c22499db21113464f231c88fd1fb9b55da847a74ade7664fa8c5f254541fbdb0c9edf730900788f0c6df6dd9c8322445c9863220da190f0b31530c77099e28559c99b020e1227d12e70e2984b6168357c9701894938f8241b67c2a1f9ad0dde006edd3489de146b32b9f0bd45cd4a8ad452f10c8ac4bc363b02e31846d44ebd5f27aa381ff26fc43bc6fff9427d7b92c0026030cbb1b901145cb64b48a035df9dcc22fb0b5ce346a9f1f9ff77e0645f974911f7fab2be01acbe4da2a07ab8fb32b4c15a77165c83eb05bacd8642034213a928e1e347c97a73d7d726d3de26b21a8e6b973c23f6350a439a1b6860d1d2c2543c2c74ba86bc0ded937727bdef466f057d1f911160f1b84a1da398c2557134570f81a0edb7f782da3b7028c8d83ba2588fe52e3acbc4f57dc3747848f0453ea5ec201c2c559bd35a7aa6fdde896e270405269b675326bf1fff69c353a795fafb6fd3b6c87b146c0cec771638d4c2dba2a2c18e2e0390ea0112f133f98d25bf4ff70466e3a9b663a1214fb0b040b39091df8430e41ae82577956dc9edec6561c2d908ee6913897b9e761a5e1f6d04433dc4847daf64fd5d9a52aadd647026f8cc97aedb970067647fd05b0d519646a97db58309d580f664d9f8da1f71db4f84cceb5d352548deada9c0eec4027d241a34a063ae635084484a57af45b7b9acc9f9ef75afb9734edc2b2b40ba9df4247f0e95f0f5745d69f08c1415da692ae4ce1327fdbbeeda734e4c544a56650e871ef99f96111ae677a4a13c11d6114035efb82d38af097d45edbe8560635cb675781b02b00bc838573d323b44d25f516592bbbc8420177c850235e30eca7511383f18f69621e63f1c4717b46c688b2b44f224880601bd029ed14bf4b229a7e09e6e0483fcf86f5f8548a3ba344886d0ad96970a2486cc94a46f6334c6425104c36c76c2c46ee5cb8ac19e658a8eedfb7929a77cc54a2c2bc9017046e92262e7342674aad05126aded873694e88e1bca7a49fc7b5502cc82a713084507f5b44b61aae9db8e6b1adc8e69eb7865c4fb5ab926b4a60fc4de921b5a1083c52ad2f17e95fb0ac72ebecf730b9aa4b17c9c4d088442d28022c26b3569aafa8a82fdbae19fefa54831c7c5807b9128c556aed1dc3a1b859e231881b0712085807accb05e976b8a663e54f3b31c917ae2b640c3d3ce550285ba388a20f4274c82c15c20a71c91013712f8428720a79c5ff4335376feb458976161ada248dc7a9a043b375e70445f4603ab674e88c5d7d29c922a556c46af8b1449bc931260f2f70fc48f8d709991bd56baa4d7b9b738d72be67bc3bcde98e113fa8a03098cb75ec63beb43006d3cc87360f3a509680a696167596d0af44ae479f5483b6a09fc951b8335f79f4184b52f523bf3c351675c5c608b214b84ecfd0c71be370296d0fc283ea22e01e666a0badc78c0fbe7045449e255b0ea1ea24399177de6678ba92f31553400525b42251e197839e0f092a2f53a8d3147fe3894b45d34ab3e0eea24882f280bd9fea614513dced8a4f2e72d25d8be7e8d855b8314b34e7c79856691db7bc21d752c4e5524ccdbb972fa63c348e8374ab94f2339bc762e215a684e921538b69ee6fb0fd147f7d81325e15c57357a7e63d3e17e8b54b2c62e3c989959799847330433c3249557793e991fcbc8338b670181b233bde819042e7a2e81764e33f023bb8230b2fb9dbd400b6e59ec6316fd8ebb5eafe62be89490b1f3caee12d4942df60c05d50b1316c516179cbbb0940f3f75da7deb797d78a13b1d91a44729139b51de7a0325fc28f4c5325bb926e97369d84df36d580c36ec7f2daaf03c3a29f019122d710a235f5809d977981446bba8e4cde7005734bf947d13714994705f4a3d4d52cf9cd47f2a16ce05d9d0e1142b4889166254c632a0c7d9c61500acb4dabc9851f7b17012b8c1fabdd194e817620fd8978f3c963097e79ebc20c39a0ff65ead75d4c9801ba3b384e80bb5ec0d3d11d571ca9c16ba42d545f431cac38beb4643a3e24644a825aa92a8f4c1e8ae1df144d07ef6f549894429780aa97c46ed189458744e8f1796c749f4cbf4c83be61457ba64ed133b9a0f0198b8f13d74603e0c072c345fa5e81bc31811c0b254e2c61b7d02d8d0b00d9a3f998ca16686e72144a351388a2f242b1c791a44a39b8b72c85f60d02ff6c684ce791bf9a6ca3a9bddfa58807bdf440e64eb9dffbba2ff866c39369c3b3301c34f07d0b8a48a865a16c5578f4dc3bb7974b77b729544d61b0dc6d8a86831a089c874765890a978e2213a85737d1a4841e5c12494420eab8791c7339db8046fc5dde764607aca6322942476c2c83cfab12a96a9cd634f6acd585db77e107020b16f21efae79e59fe8be78ba26b2c81ddea1a3370e01503ccf2641734279f7e083dbcb35d6269a8535ed3c36ef7449036898d69cdfd30dc73033272fed9f5dcf90cec0fa88428acf20c2403dff9cc90b56ea8719f65c28b14f71b3d4316ebe7d04e8c0de82228797d591cac724ab693d2815c562e7e739be9418bab6a8ec57620d8f8ddc7407053949d5b3159e222a63fcbf27da7aa97b9f632e4aa1b3c9404d609bb451d30ba0d5dc6fa7dd4529f3010ca4e1ec5ac86a3c1cb435f03de2fdb98ebcee6109cfe6bc412c4cfd3e0ca5a86b033b15ac0e6336229d0b984548b671238c68c114db949b25181c4bc8bd89360b5687c65de38237399f4d26cad62eea7de4a5f9cc95a38ef2c79386a65f2ab74832a254caeeb36351e8e1011868dbf39e83bb715c4c69f75797f863da9068e29cf6d415842789ed34e0339677ad2b22aa7ac14740879665f30c296d792db6575ed6ad6d5e8665c656bbcea33b299fdcaf4d71f487b530e20f1b0c051ffba7fc5fc3522068daf241f7eda66180bfe0adb2c57244eeda69e55924fcc2258b3c88fe0025f9bef1a8f93a943b5b386a557f0597cbcdc68ab57fbac00bfbb35843285291a5e57d25e53ee807825a6eeb3f2d656f41f343c91d1c92a9b3de375d6d0e716fa2592720b9cae355feb95fa0cf4b3c844aecfd19f21b2dadffeb89395e9775c9d57683104e83f6c15946dddadf0b10360d0ebb6e8f74144927bc9519e563631b07ddd02627fbacbcdbe52ad0227644fad85d257171d25a7df2d939c53d28ef1edbe4030d2abc73380c968344768dfe374f9243b12a299230dfe3e501cceab81a03b9c63e255d73230d8f2e16817492fd5c53635d3a3522974ba0386784ac90c8d244ef6f0435b9e09af596578968466665ee809ab50099c4391b58739c3baf74cea9a0f02297c32c0e657679e1859766c4124c037838b9ff56b4234db9f5ff3f80ec62a67210f94f841114307d9c837a448150c1dd82d76abfb234c0fb588c5742f13da95bb2a1166258a17650262328154516cf9e9b4432e148ff18c26989648df25b01b2b2481c069a5e4ae8c3837293b8e86b29aa58575d31298ad606a3429c92a027db8c3840d12fdaaa14809af472e9101e16e949dc498e2ed225c18db55418081dd746c5a4000f904d875bff9c566a3cf184ccb334cb58b6a8ac98cd4c62676071947d71317b8eec1c9bad860864f5f6876e5a0c74cc0c650031bcdb921af9743bb47dff7c03d94f05f0f6c1205843f59d05562e9650328dbd12e4201bd1a247a3d70aa91a2c064c040621b72c32afbe955bcd46559e70e87b72ab683850ac6cd28c1db78ec8ca35c6a0755d9ad209749d10c33006c4731e958ea99d3da36e7dbc818e5dc3388dc8844dc9ac985ce0e3332a0b241c4680a93636f8854f352f781afe67ea80a57fbd80cb7e1f0710ba72e67f839f6a3c8108a2e6d66d135ae72efae67ee751e1fc5f111e66d206eb7ea4597108613921fa51ad6ab3137914f3dc4dabc0368628f242269531d8b10de87aa90fdf55b7b8b4c755029201147ea1e40ebde81a4718988f845e8a3a8f8daf62c37a63ad20c4a1225cd3410bd26630c838204c4692f0b9aa25638a0ff081393a0e96baa0c1d965bcd227517cf58d317262d4fff88397e4f983ce236264fbf51a63b0567ca3c104d230ba8dd6d59b6b52bdb51fb79252e83c9de0f9e2c6c73409cd7b941acd060f5e189f90e7454def74daa3a35620ab5e207751173426d5a0145d8abd2acd21219e28e2441aac87ae2fce689003d5bbb59576547a05959dc999fd1901d25f02ac29ae44d62e93b41b775bd4b1379ec499dc4c3080b39279bfe0ac45abfb372136fb892e842f880f1745b244e38bf6ddcbb8a8171ffb0e46d8abc44c6e5f172439570b885bf7c6f729e959be115edb33b9e83cc214aaa0d9797004efabb91db6c7ab2c6f6fc9ff32f961d77176815119d54c9aa09cfbcf55ac63c9a10644f6d238cb32377ee114c36afdb9317446687464f8db4d09228684b775478ff9ac9dcda7d52bc97e4845b1248fc110e796fec4482b84c5788d721e364f604783bab3937b8a371c7c84b134f0842fc637d42c83d7739ad4803b3c2bf8726ec9b7e034e1a308e4d11a8513702f2aed80d81575a4c773fb6d0c06499522bc315e66634995408deff957970fcd3f1509cbc57d0fef3d5178cb20e999261d1370695b17f0c32e02e1894f9ec1cfa510721f3cc59882c44da1aad182a8b213d1eb13dd512beca76afebdfa4df29953bf83b64f3441eb115a4c36901982e1dac53599d17a2a2a327e37029d0a312ae1218e67d5ac356b9055632878865f59a29124c54fa3b521bee9078422ecb944ea1cc5c07bd97f9c38bcc6832fec23fba09126ae1be95c0b83c8c99d0f9a4d80f7fd3e0fdeb77720af686ec32258bdb89c1bcdd4f083d61469748280bf61f8df6a0debd35ea38e6c039db3e0848facfa976790bbea1eb4362fc683538a2fadd9b64f18ef831a196302ac44d987ac8b1156b9f71118800ddf0fb1870fc8ddfbafc62c2693a188c2c05a19b9c2b62ca6236deccdff7c9bc4a7e946b34e6819a78de7fb81b1f97d748e9e7fe1237e9b404181e6f069584c57b53de57f95e2f9b6ab734082651f905eb3b3e60fcff2848c0337060fa3d252c02e6d7b48fafffdb1b10277f001c605afa56bc93b5fea9e8e04bd21a839c9c0688822c549d0eb28f967a009f9b6782d9096d29401d82b165ba445a0343dfe46ba20ac48b604215ec0f9c2e358bc94f1ccb0f211e2fed3c115145523adf874f61f27ddcbb9d5ae82098d9f0d4c8debf84fb81e5d8062e5477b95af14bd47871cf573233e045c776b160fc882a23e57e4f4675948be23f300ca3fb8fedb1b6b84abadc650c8979a02c5442e69df5c01c4221234f1a4e75df2c1e465377f61b05dd045d68a591b0458a5ee76a1bb0593df5d9c3eac7a30fd15987ede7af308d0977a3a4e4b159c514b69e6ac7602094cc7283395c303ddb25b08fc492b308badf85a882317a569cc9ebbe79104fefb88bc5205d24007179225f858893d6e9e38a96ffcc3503794894a5505860833e8684805a6b945cfe0397c6194b16866f557e3f210e8de8c4ada96f997d6927689b28e2a7e3fdb03448c7398303e7406df16b685dd9ec1ca4c3a734062486bbba93bf76292937c351216050c7e55dd8aecfab8942de33d88bf90de7f3f193efd4de1b7950fcbb1bb87364378854dd4ed62aacad003b514f45e76128ba3717253707a4967603474956ce406b48e4cd6f7a05c894b4f2d245e54f963d5a1db27b087692c12ba109531ec939cdc23c9b455b356beddf66d4537be6ef9501e802b5e35789766433d229be05135443c4345afd799e321e7e02ecb23117e32622ac79ec32ce4451d078dd96cb1a2081ab5db0e2a8e18a2a9f6c7164f12c8b4e9cd799f92ad4ace8e59db795c9f7426410dd65680a6b6fd269a52adeb04f4e0943cd702b7c23bd52553ee529739dd4160a315eeb4cecac76365b4c08fea90a16f4935d45b3ebd57f19e147dc9c48ff8bb90986abee33b0874247df4b71d1e1e2c81ac1dca0c9efed8081ca657c0f6434a147bfbb0489343f540673aa45e88cbc505414aef18c5b6702c56218628ad84cf3c526ff1982852c174b9595dfb349ddfe1a19c9215f2438e85e134c1de14191c1816134c70fb1f0b3fbd8b09397944710737efe5c1e7b57ffb6f6ab5c34fad1ea19505c5acf17e2062b6966be5e78d6dab2070401010727f0189383a00dff722ad60af94cb6034cb2ac62647394de31f4f92dfcd63b2b25cef85aabfb3243dfadcf7cfcad6bd66b7bb07630c25202481850bf394fb50dcffbff187549f0ac77a64d0b240e204ed381cd6cda9485b4672509a09d6c6ed2862c62b984e4d3ba1c3d52d3fced964ce111e9fc2b93d68962411d20c34d8ca7a0f0b5be043d45f1e8dd8ff2c70dc055aa03c2f890ca5fae12fac44b325f04e3bfb0e0267b93bc8314abe71f8dc5d7e1691340dce942d1360cbee91a087f6d2acd5fa230523ab256ccb626233ef53ce7293625c4c21214c768ac27ed9f5887811f52c31691787f6bb62893198eec9283c2cb02fdc219bd4ca5a7da8c76db2a1e402ad13a85d33604b7ec58f14bbe512ef61be6a43e96a15c53cbd7367deb6bcb88f6b694ed56b0289ae9439aba75c7dd7190a5e9ba165cbf79c6b4df18fc885e5b0bc7a63ca6933abf9a597a0003c61104a7eee191572ebef2a9e99131e9fcb4178f135bb852a677fcdbe11f6dfcb3e746b45ee49e72fae4ca783e135c3241b9f999157e62ef9573f8ab8a3839b1f8775eb6351548e8b3dd11b6a98a049714629c4bb56d7278f9a59d6611fb1eb4cd6d9f84a6923acd70bc2bbb22e5cbd1bc3b76ef5290eb781ec839aa81d171f465f83898c5713f65586a579eadcdd75dc25cfa0988b8ae3d4e7f514e667d213a9b7881f02feda42fa94a5c42ec342f10aa1484d5111493dbba9d728f36ed13697e4c7cc4170c8d326a811a20a189b5aab7357c2d63e41fcd2e82766a1675587525c3c4b1d51c5ddac5e06d701629c24990a3c366adc41d15ca912ebd6b9297191f444e594704b6dd1b17594070a869234475f98bd3f1295c7e7b71e966ffd13b0ff78a5d8c33d4caaee69e0bb5ae603f7765e478e3a66e4f099882033b41362de3362f4e5403dc9469d4b779951a5bf55cb66aea79d4a12c4729a290cc9858010e79f62a3094c0055a579eca03be523d6fd522ba3ecd406f9e447a8b86aa16ae8b5ca4c323bc9a321f5da8ff5041ac45bcd1b0d8bd17b5dee63094e3160a413ac10824ca9fff0a50a082f9a9a7e7243af90c8b08e73eddc372e05d5aef74a6e1df24144057c6982031c9e76aed65892b964ebd3275c0e2ce09f7e180ab4ee2bd4178a2a1428e40b89502f8a78a29e3700e3341fe5b47874eff064d1000e0f254cad08ee26f51b3dae61c62ca819656e4a35167769c4ee5e2bf25a0126c3af4d89904e4887c0d05484cf4c80b8aef42fe8c00d9869b3ded49d99e49aa8ef6d3ccf774a1332fc20cc1c34817ee2e1db8317417dbc62ce17285083208989816ca116cefc4b23d381ea0a6cd2a22a4ef8e4b2845699389607812404306086aa6778e17df26229141c8c40bdade79ec9a358c57c0a98194a9dd14f294f400e962d53dcee677d112552db8569baa453309d1098db8a5869a5f35a608b924df21e2148d726d9d8d92566e064414add4627ac1996f6de87a7bae28c64ae6c24c947d524b0641f37557a228b4a0edb482cb21648d7cd724be5e9db994486fb17f2c3a07ae9d4c428dbe442e7cb8fbaab35b2827b7bdc98e01d1dc460bb85007d1f4b91de418b1012cfeed245cbf6a1fd8046edf6d6db711e790c3b838e0cbbd4f5a3c32e149d8d2dba2673b6612a3ee229f4590c2bcbf7994d6908f385ff46cdf7862fcf8e2b95a3a77eea09a5b524405953d565e395673e17c1766bd0142f3d8c4a56859666653d552450a03afbf993915cc23b7e1de0021e0fbd5dfef3a65e3900a9c4af0eabe82c7265f41e17e0e60ad92b4db6bec2407b53d1f7bb78006b8bf28e19f846616b54648b4a1d8204ae164e695602a1e94f079ac688bca96ff1b857d73498ffd9a019ce06942021b3fd112358c013361f6f5b4cc4ce904b87602eff662a54c3264a6751e1b92a600449ef97756099e5f323d45a97ee3a4cf201019bccc9e28b3e77626e4c4c896bc95c412b424628d7e07c069c9cf05dd32e888b00545287c9b5a159b7f86842b41d217f834c08d28069a7a13191c3d77ab5a5864da1231a46bfbd19f0f4a466a657ed98b3de9ad6e5b8f0960965eaaa5e2e3fed4dc94f47a8b9be491238f7386769e4f788186baf6fd8f992f292cfe35500e8f638b716a53b865286e8ac32c320e3eb400094396dab3593b91527ee0e633e36de2a6c9e30d849a042fdbef5012e39afc523d408f0d24fc17da90b9da606e08387164fdf42307de6ed29415fa08baaa030b4740b424f63f991a251760d8ac986bc1903d60c481935c355cfc2b32776ac04f827d4409732ec620d3508fa03f3381a2695b99ee3d11c1e15e34379141de357ae240521847458f07afad478ba172291f0e9292a1f401e45264ff932a6766e1ec22f4f63ac3b3c10d80a49ce10125fbb05cc8dcd1744478cfd366570d71b8598d000dd8836750059431c837f4b4335bbec938a8c1ef13b8c1203c23c5d06c3eb2591850071bf8ee0d73b198f34ef7743f24001e14e8b4ef200ae10b7dbde2a507b7a37c7759654d2e1d7bbd5563f382d347614582f543c34856ea5cc14c8938d02c09a95763927270e21f72fda6095e2f9453fbad0666494d4123dfe66fb768bbb965e003e050dbc984ce283aa6020e205a649dd3230f2bcb2f0c1d4d65d291302151524ebe000813eba311fc9f3eab4d1bb937ff00bf7f50751ec1fd7be1286f868ff89303b2819647655c6eebd9e60dcd781b09167d74fd4cc1e2f941935b95d1141d7187d31e3cbf79b7ffedd559f1706e16b85a282664a56102e0d0dc3bdc99dce090c7cf74845bf7b2c13a452196f0f58c5c4a562728dbb0ca8cd71a310956bbbaa3d6e853fa92119a0c07de499198295513e4e26f6326f562719b047840f005bb9ac26580109ed642f0c7b5199859106bc0231a10b254ae55b4ab709ab0d190abf80c2992ce6e49482d84656a425b3cd0e2f3d1ad2e36d9f2d68627ffa30b242a3d59371a760a324891acfc7c3d68cdf71f2bb0350abfe92af72737c948ec5f214e34b38a020a7aa1fa12b958dad15514a0ce1aa46b2c8333c7d76165c33553e28f154a0933213b327da89fd03c09f870b9d99134df7839e2cdc04879e73f2551079030450fbb9612dec06de9dd21ea405f9f56f3ba9023d63cb28c46a7b5f1175a82f71d5232671da6b742c0367616450b4af2474daf5ef1fb4dc52c3d0a737d7543f35f4c9d3a904177a0bb3842e24a201c36ab158fdc099a3668ec16841ae846eb48419c859d2383be3179b45f990b3b4faa0d64c949c3dc6d2648e0b2855af197ad3b480be9b403533aa4d0049c753a487a72ff49b7f7bfde95e6fca38a8bdd339c04af643989503cd7c9e09d1d33bc9c4fe01d4edbd521c5059265b32d10c491dd7ab534ce3ba88cc7df0d835f95011e2bc32906ecceb523d94d00cc7ce7c6ee66bfcf5aa8321963bb9baea836bfd860a76b78034cfde35bc6785e36a98af36c83c40cf055b7a80c99e9f2c674b7b5fd531affd898d646434f3d3f3363fb77ed201416cc2599129138a41970829c261a615847b4dfc1a30ae5166a60fdb130bf733928f05949aec80058c7efde50f5bb53be6897760f179fe01eae4329efde4d84ea1c02b24acda47ff035ba6cd9e617b2f1d5b39559cf2b2a3bd537dfb17f4ec6788a2b7c6ad308cfe96a258b8543176666ec0d29674b40ae4b9311e03dfb418b092f9aff8d43c314998d7408747809717fc13cb244727d5215756ab3068aa0b078ee57d719c539f89ae8c870b978e0c42bec6984eb3eb44beab387ef48c0bc47f6f8523dc8e0aa0ffde84e1dc05e3d8efc4d0731650c37d74be38fec9b7f789ffba36dce57dd764ce11660eeeb1287c61bcaed7440472e8976feacdceb74c3e4f5f08ec6f7c1e7159506035e9d96f711b232dbee1424b22671c9a9abcb0f3c953ae634e089466ba97e2880e9160cdb7b92fe3c9416afa9088bbac280f33eb3b42e6d3acebff7783127fa5f17e1dc377c6ae8955aac90e6e98b4f3a066f0de057f38bbc06dfeb173e97a5de474c31f5d5a3dba880acc28bd4a3c55bf6385c5bd999e6026df6f9840b93c632f3df8fc1ed451b26f05504ec9b9a698df6bd2c05c6e463c121fb632b10478b5ea49104dbc804a9dab01a6526622616ed37a5a157ebf7c9a89e0ffe444fd627ed5ce68dda33955db73a0d148e76ec9c5ea3188d505ef2c08651a91b659e6b918c702a5a4764cbcd2e2fd3f456194521252eb980468b6f92b9dc4d0cd6632a18845e511021cfefb25ebb8a142f5150a15eb83c0d20947d3082ce63af487cc907f3cbc68d390fcd461db1bcf2c773f44db2378e00089e8e4bfaa9e3b79027a83868bc770cce7877052aabd5db541cbec856ac75e8a1811979bdfdfef3267c5ee8e19205b530eb1aa7bb8032667b4f0711feedaa1e7c04b25ceeead3e1b48645958fe0edb7a392004093805a3a8fdea4d5964629636576496d482abfa2872f3138df6f16ee110b900af21891df3aa66cca107bdddc05afb97979a5d4c5fb25af7a1429c454d34a0b8b0c12ffbf99ffee1234112cb9a92ed19cde71b7c403e83a827d1726ae1132fc71c9e06154ab68af22e4a7cadee24edee7119e0a7d55c50a648a3d672cb0e509ef26f2e57a2599117fcaf672251690bf3bb6bcc44fceef9af9085186dc79308377243709611386440f32c4f42f962eb5ab346a9924bdc07efd5888a2c283051784441836c32dc7e202c40943cf52296a686e776805a69252c930a6c808edd06e261a13fc9a632e331c34b1dfcbf394b42b518e4c324a14ffe7c5f31a3d23f5d370a582cf35a9c34d80ede55cfc0b989fc28a097c07302a242361a20a08452091243cb4f6ef6c46dd98c90acf3553b581cdf9704f90c039e2f46f7aae8eb53e76391c27d8e430151479b3b6f98fdd442778f7b47e61fdc1b2958a8e46bc70fd853761c9685e20d0b9f04c5cc9b46f37c4f0d7cbe43e1341b0799dda6ad3a50efa7c13801ced54988733c204feea09a952468e2865329f900155adbad412a885e227727fbf08941a31490d545b6fd236a678f7c3c3ce080f91f0810a7bf71c653bdb6468ca961c8707f102e748424eeb353dcac20ca78038463ce7344017775dbfeb60a419152d02be918616449e8fad68ef294a7cc12adc30c802024f87ceb46f0f17fd8bae115abb1e4004d02afd15c1075d620249b38264ef0f772e6994357bd9e373c17f0812ea4a8b1003f625690cff47297a77ddb6976725148ef7b1052105c120dbb09faa795f3f50865bf4170d553955e56804d86c74dc9650b0f928df6eefd208f00a555037079de98341e41b5523086aba4a7fcd752c4497494f3ea562ed2062bee33035649e32b5e252879abce6a1263e21da205003597132d0c6e0499fdb235a7e2066d7534c669e08e477d87dc73f1b37fa87936fb9f9878703389d84fd193eb30041ccb9b63a3aa3f6e51414a6c4b0c17c369a8aa201e8efe489503a51687affbcda564c206124568254f6cbd7a7e9dbcb00f202cbacea7a4f8bfdac458b993da0b7f1aad5bf2df5b5c0ada92d73d9840aa15e659c72b20d540f76e97ba871fc0aa04ff32e0636a3f98c7e153f66e6975b4d3e16f0b242e426ece844cf5e0551b76f835c73c0743abd220f31bfde416b5b1d5e583fa4874ac3ecea472bfff4ac59a62a7a64b4c8b791100c3553002f10ed06a02b7bf5b9a867f9030b75599f2f88e24014ca5f1a1fc1800ca6dfed11d02013d19e26126d05282e5dbd16561e228b9031c5930f1c4c1d773dd99d0415dea84b4bed4d3e5f347ea166cf74d9e283a7fb51e89a21c793df0684316bb5767c0bc1b59e5a2aa18a7aa4d4e335526a6673262d284a49ee6ca3465b5a1ed5d3bbc1d9b0bfe28d7c8c98a31076ea1d73e17a7a5918a940fc588d10ff686857291169a61f065caaab2703cd070888237c1c40a4892d986928c87401f9a7927c881a58f2e40c355035e1d3c60654265618782ffa98ab2c1557bca6d945dcc972199a16329d12041f2bc934324f65e49fc14534ee2929f554632dacc6e26a8cf699aac9f9db4e5e74b1418a0d8bda60b4cdab8155d60c4cf6605c3f9f06f3658a2a67db7b201b5193c2a0b8d3f6495e1ccf1c7058dde8be9ee6835a225c6a6a7bcb37cdf33cd852a3752228c9496dc2f6e892e39156acf93fef630340ec3a8211decfdf66c01262fe43d0cc4d11e0f4a9d9ee5397e8eb5ceb6ff0c29a09444710e96f98085b1e07f48bfeead6290731e6d56efa5d27c7d875722f7c041d5da23d966bfc1502dded9f525a0bfab980400f0ffffff0f10000000000000004790be8a001914f6081e5a6529cdc0636ccd36e3f510355fb8802ecc8adb28eb7c4afad8438669b0e190968a5069368e7fa71ad8575c6a7d4ef9dd8966717f0e00000000000000000000000000000000e8000000000000000010a5d40000000034fe1ab944dd2972000000000000000000000000000000000000000000000000 \ No newline at end of file +5117193972189c617e95763cec1ee92e3ea28883c31f790ee00410d51afa225cdc7943bf24e0d765716cc19b8ac43f35523042691429b50d4a873e78e01cf09485eaa60b50408035d8c9ba730d68992cfc515d4d22f8d816e30be2fde04017333a76a027dadb4cdc6ec3e66fb9cfc6b295da8ed51d037988668c5e104ebffc9346eb24a121a95451319e023fbd8c49f703a47fddc8d9a107086835530724208b57a07cba4bb7e004f43459fc541580c45de172adeafb182bf2078b90e4cee89a4a0650a3a8c06508170fb807112c4a317b52a3ee90d20651d5b24d2355af72020a41d1b22519637d86e58d039863cf15433ac90c2a671710576ab43a4c2012b393ff28769e09df6adc609db9e3cce1e3511120f4af084e41f584b75aa08d60b1ffea90c8be582256d3fd90b8f008abc588bcf7c2d583a90d4a3cf363390fc5ca29b53c05ad32c08d2d74958f8eb30899b7e2c4ec416da4fe2f627b06c5934b3012e071a1ec2abcca04e76b14b1fcd04ef5256a0616dd15d459528e19b73332d3c250e93596a01b637e4eb738f58f37414ae0d1ef7be89167dbc784b3c074f878eff869da223ff34134271fddbf0d41655f144d381239ce95c8347b081690812422850ce5c466963160c801cdee81a8483c8bd39ee91351ac50753306788f24c4934a57b68a0e8cc14853b1fffebb9243b02b8df47cfd2bd65fc361e15559116508321bf154e0038f61377c6c431a341d633cd2ffd11cf73b85d2852742e7a921f5ec13e54a67697f0be4472601af082d7455495c1db6aa5ce3e222e22cfbc4d65eac30f9abc9e1df66ae54d7c1f50c8320ebed4ba4726715302a509b1fe1848275d214438bdf601114e18cee063ece49bd9ce9549b2e393fb70f43ca9e2f052c6e4d6f7a9f0cf4f03f0c3663960df650c13245095c28185b60b816f1cd17254362bd610eae9d55941d286e04dfcebcafe2535421dd8516f26806daeb48749180142e29d7892ba04be0bddf0011629b34f97f747d58b7caa074232a11d7b4f922bbf5d2524be0645bd9be22185ed9ca305b48533b2438f57bc51bb5950420fb102d5d4e7887a4d8ce081f3621906efde0af7b93c18ebd155c9b5717a18ab3c894f84217260a9dc7563939f6ffe7775762eed63ef16e8035ce523d4e43600b49cc2b44e609150af528c957d2a46b02cb7c94af875a02d5ed17c0689da42621ef12c2d26ed796099b4b9a144ecc0805ca24db3dd5af4a279d7891ade2b8fced0e62a67a5202b5dc791ef7a820c9333cc15f40a0c458903665f4ac3f75c45e3e458dbc9f4f27918f2c755fd623fa00963fd8f9261ec115f576766a4bba79e2eb99304646263f372f241ff65518a85bdeb4442660f7d86925133767d5ccc0c3f89c90107499923b95c4f86c650178e99750eee96cedf65241146cdb166a9372888278d0b4ea7dfcab7f51d09da6079de6b1804c2436083f0d56632ec9ce2e95e46caa7a6df271ab3754fed432904311abe59e77ac6ffea647e3e159b1ff6bea64327cdfdf576cb6335c6880e8b12ceed1caee4ff4353c9968bd128f4a721d6741ea1a21aba7776c58272b864fe3f4fb2204481d96b298cde8801f3413b7e98f67f563aed05cd4aae437c3695d97e4c6698efd53b2aed244a05686c4144d728e293277bd33e8f309ee268cb58119c491cf1a7ab9a2c43b2b0f975edce0695de017525dfcadff448c9d977217c762739e74ba2fef7696a41c665f20cec5e15760a35892907fd15bbd2053e513810188f84ed9b01a04b3380684d87b410c3bffd5a741be803816852d6ec4b2856c3a4527ead6784e81583b4418be0d53a144c7c6cb8f2de0bf40862e3e191a76b2ef4fc0384d673e911db1215bc39a62a6f3467ca45fdcc57fd501e9129f7f06a166847a396862a57ff5d95402db23a9c78b79345302b294705523c5fe77c4c148e92e4f78b792b8c842f70fd612d2cdcad9684065c05e1aebd7aa6b5aa2befe3a86fd411f6a094df0dd91d659d49c8254373f1f48dccd851d8c862b9ce63c46a4ca7ac9c546ffb7a134e14331a6925fd6008c151f373382de06c686b870f259a9b0e69dc8e2852e16ec5056a0cddc0f532c5f61f6b09c523ffce98565de6c9ef71f84e703364b5aeefdad2622affcb6c51a6a512b603735c8a364d0875eddcc688a71928cabd8e20b10a2ea51eafdb3b0ff970faab985cce884f863d1bd6a4543a9439ef2e7f510de9f34516e0ce46cb3b58ccb990d46e58b3124566cfea316389cc8198d098f77b5e1dc67c5350289cb8a88accb8cb5120f1e6cf7072cf91cefd374b38fe9d6a1d23c18a78269ee1a3a51bc635d792f4fa79b788d8d372ebd63019fe45a4dd49f31723db3523576c6813c8de3d8a6fd240d1c7fd34b40e7a5e04be40bdb4b66f7e8f264cdfb0cb433c3ef24b7980647f032a5b82dfcfd32f0c6fea985b61858a99f5263899534a83a6952b60801180c3309d2e38bd8f04b5b32d37f244f4c96146fa68d3b4f1d29ea0f8ae9a3335aa19a6b3324f4794d7c6041ebd77205bb9e8a85f0b7b110ce74ef9e7cc5b377fb8bcc1105a371fb3159cc776e2d9e7792a15fd001d358d2829fc72123c320256a8d013c3551eabd0d0fe9a42b134301d5d65b483e71da7cbb033cc7eb18814132bd797cd286936ed56e7160fb8f7c0249ff657702c6b2ea568efd66f7496c4964ae9a57d0b5887f3bf1f47ee958a7e93e26d61a4c056fd57b6180fd2c511ec595ff6915c674bdf129830d7742d68fcac4efe82a5239601d9b173e45e622e06fe5e99e317c73ac46af16d4c415867909a7adac9b3a571dc81c1a6f7851870cfb3bf1c4935b491f113e81ba1eb684efc91de264c28d9583072eb66144a66542ab9d0b93f0f512b3c180825379e6a94e1c82df179bbd41f3e48189633ab4143af01cad3ac536d44e77f0970fc3e6f81b31033d539bbce6e9388618959a0c77f15e7b1e80cdcaf0de9c1ce99e6372a9f6cfd7b15296b267335e6c8cad09406a1955844b87ec3fab4d847aa8c6f0ace2d693ceb1157b5ff5d3b91f9c513feb44815692f7db105cd4e759d8b4def0be265a15729c855639c9182bd1e2fe0888a75c294cde2685e0d11e84dcf8e0a5c1eb20711cb0f114765e72e6017c18b191842234ab81fa74e8373ff48cfcf0b0285ad182be55286aeb4b0a85be33bd50a3fd6717a27f97912a5b425cbf85283a5028725738ca1fea664aacb8cb52c718d9dcfb69a22a2de589952c8cdfbbce7800810997b4075f8ee6ea437417c1fb2ef7c418936db8d07815a6f9349aca132e4f2ff912b4f6ae02d23a7205d6a4e49d82727bf66e6127ffdf9ea60077390eb1216ff8866552252bf3f1695734be878da761384b380adfd56e96fcfdccf8d8a5c0b4f79b03d80e7f15638f8444d7c9fbf2e6f017c63bc5db48f8b7c06382e317112ca095a354cf0df405a58382d07a903d69555ceddfc24aa7fa614373dc216ab67bf533e91b5b85ebcc243ec77d6db74687e6dd330e9052a81dcc4815a59459f504c0d022b209237971e179b9fd0c0e97741fceddb6a4c448de092d193b2fcb59911abbad56b99ee21ed1afc1ddea5221c4651d0ae22e2d641bd5698f7b990f2868fea9faac452ff1e8b7e56d4abb79277eb87d6b3dbb44d019568cdeff1e7ba7223431c2934c5f79e98ced348c9b8f1398b7e0236b2221f2721901a1447df35f06edeafca9d6c61f2d48aec174ffdc6d67ca9a5c0d50e32b86b6f2b37a25b3c85619892356e64308be8fd548eaec87992f87f442fb06098cb68c818e836274e4f12082ed536a82471f396001721562c9fe79a677c91870665a62e6316713db5655f54152f678b48c8149c4b628c02079522bdf1e2325d4fc00bc66824e88be55769415493d68324a6bda5bd661bcd14d047400a78fc8abf770f3ead6c5ac879f8230fad44ba19974e6788a213678b3098e338fc1318f9356e386559d2d84b16dd2f441a6142e8e0fcf7563742fc27b33aac587f2a2ed55d858350c2756d66bab261ea7b13eb42e47b3bc84c640fea46ad1380c5b5946f1deb1078827177c480922058a72eb26f643b2123e74696e5c3dab9915d063a206eb996120379e637c6780ed10be64e4684078f1b469e6de8bf8bb35a244bd07c218ca940922e522af4e4689f116f1968edaa4cb309a63331ab1cbde00bc5f0b1ffd283b54829cd14c4858c908ea200bbf084a023f31ec8410714c6aa5c6fbdd046837286833e72aee7065f458c73db847f0cd4d11ff0821ba2604ba2ba58b4b8860cee4c6cce2d7789e89393f2a10dc545673189284458e3ceaf8b715750a2f3d24a7d110abab24e9cc00729178f4cf0424580dc8f4687fbe0f250b1807e7f44c6e8688e0fb26bbc33a6afe36b9c5259d16cb302d71a731680becbd03736421ed819c6e7386629b2b57ed0546395cc88a35305cbd5f4b276fe1c0be55e378fee4bdbf82ac3fb5593cae213253e7ba735a6b7050f98a5055fa063b87b0d2eb4d4306c9d1d66aae816940e41ac1df069790350aa4a7ff6a398eced2f182c0672d1f9851aa6019b0dc39206e08d3ff6ee94e5a723c43075dc99a97636bc0e4c145250de303b42b1754286bbdc82df8f3c451e58c663226c2e483914c7b08e3df289674c45b664662a37c48a18ff6220ddc187660844cd39ac54f7b454fb405c51962c3043d877fab635919d8fcf28aee40829f68f1ee357eab6e6a57ad2a1dcf09369b9b15feab399185cd8929783f61951fa600f049d0ede314360e07ccf51f66e2073d4e72932fddea62ff93d24ab8260fbc90ee8001bd0fc32c66a8b64515ba314cc362047275684260182fade37dce1502787bb8b80ac3242f996169c14ad6de5538320add7e9349827fedd67044ac0610306474f5ff541095e5f7f07e7c391f83bdeca88bb6911c970f9ccaa01b7282bdc5e9f233b68e2db853ae91f33199dea6a27f8e54743449511edad5f26e0fd64f2abc1842c1c2d693cf9de96674b33f73b89ab95739478d830683631c4fc1ebc16af959d19724c71cc9d0abb7f87c73306ee78bb6fe671c4a045bd724d0443b1fc39f6bcb2d88a8a6f1dfc4d9a4de47ed23d23f5d60730201a4f56d8fcac0cea728f498daf30b16003655782af67cd8d48e5b14a1ebd62a546a5b29a815b210a621f24101cbbda255c47bd9747d5ee9f727fbfd034fbf6f6552d5ffcead646fd98477ca62711718623fb620cbf587d94747291b715bbc460d62da7e649228c0f623a47aff16214005a08bf9812005d92357d208b47a67622054c28f2ce4dd0450d91957d1683da5564c397e9198da3bfc406c5fe260d59ba46d3a282f6d16bad02502b9d0ac403899bb04daf6ed7cf6e4f19591aa65b3d2c44aabbc2b8016c57fee0deaa4d98b0834a46df1c7414c0a7f73e927f2e33866dcc69f202963ebe7870c37f50343eab844600a70b444cb374b83818988f50dda390361d12597c607ddf47ba62cb5da1b5b0147e1aca38bf32422680af2f7e76f122727be10355d1914fa99babc246b9b16ce4a9e6d0e2caa7dc207e50e0d7bfdeb1f3d24eaade5870507f40721c16641aa0ccd49bf0fa5d2c47ecaa65662b7bc47fe3a33122a0ea4ad8409b946cea751909793498ff2b7f51264e21d365b0ab1b653e470e395510c1c4bf5e540cd40406b8910461ec8fef0105d7558bd22a94708e6f6d947ba4a952a322dad4b7865524c863e6202f5c8cfdd9eb41693cabaf7e22b419188a2bc590e5b3bf879b385dfea2059a31bfda28d2cdf77a5f447ee5f49e3f9499198d0993b92a069144013b0cbe82b58511407f6e0f218c6752d3285e313ad447bd116a6534dd39d59d6be3a883d815935e2a01b0a7b7edd700867796bb14e1fbf5c1df0f9ca5acad462fc46e4a61a79ba5e7ebcade39c02db5cf1fb728c454ffe3f54ae783d6d7669f5fcbf45bc7b36a07234c9b7d7b1557aa76c9b05e4c7323cb48488b9ba743906eefc013830f3041db8084ec149d89a50705fba5047f07bb10ba034d6eb721e353faf0e0c66440dad015a6831a827c48c4f523e596553c05c2fa3ec08e4dce7d4f64b63d8105cd6f854758c7908f279fcae0b3261f4bd63323364e59fafe46a4beb1e6ef38aa15932e817044fc7cdd43a7b67f77f7b80b89cbe210b43bf18631a06e08946b65d49ad64e4f3c8f2cba8c83bae623f6cc713b75e09303475d17552eef38d7753067a2f68c202ae75ff9beecaf9b9682029ae05cc90ff48bda7952ccf886e9b56fb6feba1a819423f2c8beb8c9cee039249059137b90c9ddb5864348c34cff60f522257f4be9beffda42962473a5c7b9e84bf6a72ffe0e01b51482b3d55f76235825c7ca345ccf48abcf37367a8b4b5cad1b39ee5ed665c8a6b5ab3138e94da3b8bfaf6d3383002aa14f45c12b0d5cb7e3ba9d41171653ae6f56bd886fbc61cffb3a5e5e1518d63c1d1d16b8e0010011bda3c2821a43dc6a02dd9bbcaa7068d783eb53ee8e7169af44185c4480d14b036eea3c14c8d1706ae8e3b1f1cb259b0f02dfd67e893c44a8d45361e30868aa53e92636fb18aa68317bcee6dc889eb387548849e34b8338b5d76c9f65f7450213c2af90f3657577f5d26af3e1823b77dfd575cd1f4200e4b54119a4c4ed2f24f18f4552f0e337e5b3bb7066cde26c762cd569d56eaf73208fa1124bad0d0810f809012da7578b2de6d12a59233889c3543f724313b50afff4a54086ff2660a0135d034028e16684ce31edac2b862c5cdb0f7fd9b7812c1e39e1740b061ee5a90ae3742c21e4afdfc5827474b6b1979e4c02c9809cfdd11e692ac3d3fd0c99727a1a49bac654feb80bf079da03ac655b4bd39381906f82ee3379cc4f1f52e4103d736eedbf5c1cd5cd4168a2b66c4937423864c5106c06f67b6bab60eb209cdd10f7d0b39ea4076b8805100f468f6fb4f68ddbf51bf9ac64ea8b6ba559439e59dae212ab0026edc81093c21604358d0b96aad249ce5faaca9a743e715ef17f4012d3478bdc43fb45691c40508c8be261fa16957816f9a402dfdbfc5a7da9b1712de3f34e2764630ee160b7bbbf116109b25bd73147d24212fb1a9e1980c06c053c12521595d9ff7138f32309a4dab149be5ed5e87da50cbb9a251374cac70dcce47aced51ea0ee2ac21383648bef54555335bc74cd2fba928d41a2888b8b498c7701270e4f93c42921490d67ef7afbf44892576939e660b626f04f4e4ef209d0362f1ce2063521801c027ffec6746913171985fdc482d50af6d5412ed645a6a31d40ad2c69379d38f41cccfd92056495c922783836065859274ddace6f7b68ab1164f2fdd3692b33abdb135548b36f3713a4a6d8dbdb3bdc7643b700b7da4b2bc8e1086f0ff0783cad66203cbe15d3726776d2751313ee7c1a034a42bea308689c11422f1e40b81657d0809f1d3516485721a50e03d6ae88f48131037625d0145e99cf050f6ebac4c9e116e162c232262a284e816855d4ce4fe0be0c0bda35791c46c8d2ea0d6704a91e2abbc1dbd001eb2332b54710593b5c7a03d8a9b11bc53b9480d8bf5b3ab1e46e59bb8bcf31d8bebc80ea32273126a8f869ed690968c46c04efcda6795ae8bd4ab55f28ce6d5bab47235a8250723627ceb76c13c4a7687145e7ff9e4aee900d34236b78e0d183d65fd13ec40b9057a623e869c0e535a4da3f6bd68bced41dee24995fada3cc580ca2b2cbc09988e3a95f980dc354d9e46387632d1d104a46b9310a7a32bd6a65b695c46ca75e84f26941ab7afc6a3c84df89016f5cc933a128ed6f7377bcc3e8ce25a831e595423ba74719c1fb2d8c48d356be0293e2aa93fc9a299cc29fb4f6cc902550c221139748fc1e3f2bf7f0709e0e111d3b2023236f500eb36fe28fb0f68217957066c486bad204f439f9a417b5dc55ee81e0059d81c5741a8f00e239863491eff0b32c8681643a7bc4ae018f115982d1b240261dce3a3ab6ac32e1b2aad82816bba5a6c6df6fcea3f7ce67d1af6c38eda356e9f2eb84568d41b2c6e162c03522a260b4b9a4f172fec86016b77d925e8f1f0320dfa3e82a2af4a58afcdf222eae19ab60490c2156f347ce88bafd160ae762dbf99e79810d5b0091c86c789f7f8eb782c45d44f8c54357804b4b71479c9114448fbac320300c7c3c6456d9558e1b76f6b2fb62c17bde960f8b3313b24f74e8b02fd421d20d08f3784871d583d11974bb65547ba305820061c9a678858bfbdd4d8a2d2ef41f89a054e4623b5db06d134f0b479d9afcc884b2120cf11ea68916658f83771ff895c0ccf5fbd391f0a6587edae6d1442854c14cbf30ce5e11aef431cf8aa4bce69ced7e06f4913d5e4b660bd64514e66a2eda0295f1757bd6f55dbc83515ccc521bd5ca660bd8c1971e5fae89d5fff599ccb28f0e04747bb1ceca9e252b8c3570441eaad38d8daef3f7fbdb5e3e6211f07f3bf6288d1e859777cf1ab144722e3a1572da8264860258734a3d363b15ef0c7a2350d269db20614b3836d5536cb6a45ac1e436888f7c9ff4e145a85b55161cf1461db2a10386b86fb8965febe9f5dcb0dcc6b99be69aac706521e8ea43b1af3f7d2704de477579cffa3e1dde81b8395f9a869f36fed4eca72a34221a2119b27c7bf7578dc8bf86a43b9de8561155fd878e7713caf6e7ff26db5a61c144323466bf4571adf8f7f1875a6d150102a61663f9214d2da81e4187722cdf57e6ce6e833cb47e5ef8ef3a2a87ba3e8d5c5f6209417923ece71310102654b297bd2a275f0d3a5acab5e39796eb1fe7acb722998281efb215614675ef22ad06e8de33d14d64a722cebebde0d0631ad32e53644a184303e871e83d35b24bc7a65baa05e2aa59c2c1e28f07ab89e1b46ff344c71f80b3d18502a5a00803a657cad9d626aa65b5b98041a4b033ca45d3001e377078f756ac38c229a7e8610776e3890a056071975dd49b6bb959f53242657d743adf6b993f9b8b9d6421b315f384deb7e64ef23f33e351ee66527eb3c7465dd40aa9bc9be8c518d5ad4a01d54b0c2e0f9cd0c75f2923756a220ab90da65d4246b6608901f555bc0081e7545c8aafacd6d6868d7a58e5b09e45c66f5216e7c8a37ccd6a126f371bf3d0e546d57301a930076406d08db85fcc28cf2d874c1a7f84f1c20c38362071f2bb93a4c10ca9d21966ee09dd86af5a449780086ca7fb559dcbcb68dba50af535093f371d83dfd662b5b4498912afa486b9b7aabbad0d9928c711384e576a8f450a89bf31b9edaa764d402aa19ed085962f92d9ffe32d67baf92d23a8d1cbc7d8739a59f2081e301ca596860534c93b409de7ee551f52909acdd7af8e4a6602a170593663468d9287c0adf68d04f8c016114ca8b8dd39227d9cb9b446851afca1477ee1b8cb19beffefd2829891eaf87ced90d26478b624de1862f49f2af73c58e19f7479214ef6a6623145fa93c01010d610308a88005e806a3b944c32910b36cc12fd98dd0354f389d89dd1a712c92edb7f05bf8894e70b5c689bc8d50bff80e5e17a4b8b8bfe92203f9ba426271f7db67bae51d17781d066b4e44368b1ae75b8dd4380f280684b643a68e478d2f97e93ba77ced0c922602ce1191e69e419a18b5383508c4185070b20db4decf6fa91c997a956fce9def673dd31d7c0288e830c4bff0e36ee7a9b98cbb2db66aa0db84817a54bf7fa1cf1ebd1ce1bbe572aa9c926b0692bdae364e376f0534819f8b3719f2be7563fbb5637bc0d159cbf90e4a8ab211fe66e3fd527b17a0f811b8aa788a8081eb82a903b0ca9f9491b236a8a2578eeb62107b860c6aaed04972172344f39ee07a610a89c8c861885b024e433fa5d86d1ccf756219bb13aa4433dad9b451855de204499f7c1f850dd135cf5578964f9f06240789b2850915940625824ba4c304a794545007329797d95293e998efc6755adc66487d67770369a3ca6d657ea341d17c7f1dfb768b60a55c5814d9c3c2a19dd97f0415f08df4ca440563b78b924938c5fa09641eb97877a21db0d624783c0c54ea3ac64c7d2832b2200b327cade12f34b6440c7996a01a2f2baade331da10cec22d8a83958c9adee084273a1d475d20a5d8a7c702378811349148b664fa351c18db84461043358847e4e3acdd1abde638e9a75f0daa8fd16f4dde750be2a83a6511d24c7fc93ad5c92e26be552b1b02137b8fcd6d73e3648968d69b6af3f7dbd44efaeaed0ed5b19ab1507d1aea789829e370345b68561813410227eaf786a398f26e1a311994d268426e5cccad69116ec530ac326c883e72f70d073b69289257e7c86aeead4e43a3865ab97d1e29cdebf9d5bd3c17f9261116a7e947db2e4a7678253d33cf07398cbc0d29af3b90f837a746cd645195513ec82c73133a532c9f5967d5ada47a49967f0d2ac2fd7f40a65b37fdecbcdbae6cda66ab268c7897bb3563f16d7306f28e90f068723c7caaf1e23b56f2d14306f772ccd19532a7dbfbe9885e1d61e265931dabda22719a83416f484224a758b79fc48140cbfa73a523d9598bbba6e26a217d0602e3731ece24fcaaab09b619ab4f55fb8d7409b06ed0e680ec7e77df87ea597d3161c7aa3c7b8df49ed58358b95dedf613968b79c4c5f8cebb3550799b8576243468275d33fd29407de679a5cea870ddcf7cf7e10cd27824b8190e0679120dede13045b61adc522930cfb037f47e510aeeea8d00f25314edd5037cb8b9ed018e4c01c6a282217599dfc72f205a06b7e70ff92e35bccd36011893ab2238ea5bd5e09b0e646acead35e2ba58e49a608ffc039ecb1291f578b0c1106e00a31effe63ee1e3411e6a92aca484d3a8abd51db4154f842390dbea1e64387e615a170d9f9fced7230e935602617317d1aefe4a8821eb0e7b62c676ba2d0618df23a6dce8c41d0ea4c34cf5239121cfe930b09bae44ca3f1d0630f85d7df988698b9a27558dfdd16ac0bfee8b6476ff730a41890d68857c452adfcd660b888e678e408fde7cc6efa0fed19bf244360bb08a52bcd685d9f320d28efb04705a88b4c40b60855c062d46081536996f22519cc8019bc0c05d73312ce95639d64c94b530378995d165faadbd5e0e7d8c4242d9dfd72869ad89cdb2f4aced6511cfeb4575901258c733d013120f9802c2dddb55da0b82a73990a6740e8ad7f8cedfa733d5ecc0c4704679264f68491a3916939a8dd3e4aa92e6b63f8d2b118231118783ed528635ac89fd8464b93ee94f63167ae5b268d1fca7046c7638a845ecc2e5b8f785d6a679baf489d62f043cd46d280d7a00d892e7aa17814b223a340d65472c8e976305c08d6f632e8df75c397bca2b3b152375b887d5c99e06adae39e076ffdbf1218271da8509c85a770d3bcaca103e0bddf61db2c0b8c4e8d79db83e77a2ca46f000093a766e6db575f1578103580362925d361e90223c8e7bcbe8988ac9689a0e2233fb1965b221c81bcb01735864344210de0b1efe8343596cb99360c9b362f682c7c17bc6041d10cdd1bbca5add2f0d40f9d5b4d70e04a51c0eb1b779eeca80ac789b275bc850f48956b8755b219afb54586fe9fb53b786dc1de8b57a4bd21356dfdd91a4dc6554e58207619870f34dcf9a6cb295e49c8a0969107df36bb8565feb44f191eaa642fec2a7c2cd008a6c108e33df0fea909997506413595f48e0a4d933be19898eb172fda8e46176e38da3c6250734f504c58088de7f9611cd9d2fed768e604cd8674dfc97142e8d70c714c614f8c7bdd0d5b7aa0ea71cacbe478bfa699361abb4780fd4fdc6dd53106c639765d948d0f683fad15b6724471bac545387b07feac47847a080470aa3c7c06d2281fe608004ec520f284851010d77c4b0c9ce39555c87f594b29b5b7a57dfd1b1c121fe15e953580824a8f8c78c63354a61b83d19b497aff267003e25d059b3a4e487312e0fe18e02ea2260179773744850329ed8740bc6e14238928277fee0890ff6d9ef25658b81d2c15de5388c279080e4a794b81956f8a3f710bf55ec34d7cb9690d68f7192cf71227db5c49a9e1a7c1ecc48b02d4e8a15708ea73ce485cd4e4de26228cc90b84d4b176bba0f2725146a49377c7ecb86ad0a73ab0c8b294df76cf78d498c61b9f20f91dfb041862fb785c0b1984ad14a27530b9caad3b8133cccb1f269fcd3d20b1842c91bc0a5f6e6347d8fb112a8e9955a854ce64f774696911bad2c3ab4b74c0ad276958461688e87dacc9f69a06c9091a358e5795a8186d65724cc2a580707cdc405ded2b34cbc549e7c2bbbd30f4f5fd84b0763098a098795a5f90dbee591cd9a251aacb10e1a64b019679614f79592273665425f2811a0d80d7429dbe426795b227c247556b0727ba00b56cee94e1ce2f44d9e81703080ae0afb876f485ac4b8c56f5d0042644fd9032468e77c7541d3ab314376a6da76389b9b5a9060db0ee059f195117ff79b74192421439525af0496918fca74797bf00434f88e2ebbb2ed8c18fcbf54f1eb3adac1831ab9d613c1bbd1540f25d07e89ec2cc08775a6fddebcf642f8332c51a8dee90f3cfff83857410c8d6fe6cd808e0b2372667a0b51aee03b86967aae1b8f46d9213afbb4b65291f8cf0721cb305035372073460862695120b5aef0c3298924110526f4e91989253265a6849070883011e32c7b40a2b7f156cda4661fd01beceaf962e96f32056d68b9a581105917e3a6dac00211f9dbfd8a091f7d85417fc09e6ed750cce2675111b65fb072d494380c65edfbb5a0190e703b22015ac18dce163f246fd12f9d9be349b4d34aff1c4ee67d94aef11b0a9289b309d879f10927f846d1afefe2caa587eec9d829898434c20386cc419603dc7635662bad92cead5e057922b197096285062c695ec142d328036b0181a1ebcfe6fc11108e6271613fbe3227265024a79d8cf3fe292a8c35623803c86a58c39aecbbf204c7b02d383fb4272982f75f2fcd64c4ffefbb154d400f4a66c4af94d742735e1b79a82ab0f36af9ba2351bfea9092bc5bf05e57bc7a530175cdb3b1413c5e47ebddec91de0a16ee150da0fd88b15dfd7f84b14ee6dfa5949ca245fe8dfb733693683925f56107f0a37788d9e546d1d98306ef720c4279b8a2a99bf3bc9997f9b8bbd80109ac72ae83d2678b9c671b910918e32156fa6f6bff2902455117cbc5f8f3a1f57b534c908f266862d288cc430446111001beaf32b2fd5487f843e0f0fa55276f7e31a9b8c193292c69a3adfa29a3f323692a15f392a40217240e396bab979d38621780b5dfbece83aa133b536c1ce53b75d82456fc1a3f07094d84937841d6072b48f87424efc4339e95c6a84be751ff945746518862bd9cf0ae0f385230fad7d4c4d28f35875cb98072304c457ffbb85356559f2960c3c5a00f091796316defd13ed001f42ac931676c6b44ca3c0a67626edd8672eff16d205eeae665f54c8967c074dc3ee8ebded60872b0ab4b24efda183e0eafa1c35eb1b7b1b4dc2e340969d91241a662fc1a7679f8d7b46a7a5edc7e12bbe36a1c1b3fce892ce472061a980c57e4fdff3dacae07a04df170529f27d478ab124715e078c337e66071ec0a608c604d7c185c27ec9c1d4a8a9e4017f9c34641beefa66df0f0f8179434a27de6354e66bb77580c451440715c26d61f562c180175b42603d652a6bb165064cf456d3a106ba3b5218b5efc9e0eead3942e7e16ca47b237cef67c7fbc9e18e20ec2993410df7dd6ce9b4c0497e8a08d828af1c9ea6f21029825fa2d3797f1c1896e081d62905700d7a7adf4c60c440253bb6295801aa260cd017f20f65dcd9e747110f03924f6a68735a5f15f990ddb1f0e69da4bd64b62a99bf9349dfcbd4710d467e8aecb35f5b0082b8d46c93e4991da92398f98b3b4ba22f5c0c559e4a6ed82dbda6b424804d0fb57aa28e21d2db097ff84e3320dff03448126af17efd49b7eb1c5e0901dc89ed61a24b7f250a2048fa8d61bb5e14b383b29090b2f0115a4e72ae10a3d22f03364da66af2612bf92cbc53b85ec3efeca5288e3fc2b34352526a94c1cea5f1deacdf4799cb3da5201f84e0edecf8bea3f642bb9afcfa351fa9abf377155655b2533b94f4843d73369f95119dc9381b999b12a6c82e1475b3fedbe24ffde9a0f6e4aae0940c8fe1f82606ca7cbaa87f2045f3672ba3f761d191c53c96d172247789fe23eec75daece136630b74bca0461ff9a120e67885e9d16ebaa6017f1f4fe6e9550ec43119c1bda016955190273f030e887c4ee7d78e66cde53c5599a31fa1ac7b63802447ac123cee4b35f9a571df0d177d6727927da930dbd389ed030824fcaceb266b0db90c103f935db388d41f436cda09a709f4fb858536a0cdb844cd3e7f3c5a1accbe55ff53a1de3163a89d31d1137453962ebf4e4dba790024a2f279d6324ffd2841839d7a2256afff0f1d39395ffbb9109ef6e631adec5e24cd4fb066d061fc47726f9d54861663b5dcbe0b5145bdbcd2d20c1d5af05bf3b4e413b2fddbdee8712c10c9dd5e3b7f821adde1c13602c26a3b158ddca8ec7928d12ecc011d09babb15e011d574e0f2baa335672dd064159563d7f829dbab1cecacfa00c503bba2123cfadd28b9f8857c343bf14a7de7993866cdd3ef5e6dfc415da5785be5876590fb8019903422ae31620d6aa5235094059d286a642999ef3098ed24790e9a8492a7e5f40bc49eb16df0e46b3bdbb224aaa5d9874ea0f98ff8dce60ae4426e57cb61c74d0bf6335b98b548f576a998d42abe49e1be95eb64b48dd6bb8f30abd0d5dca71147d385bf7dcec47c06fe094553e8ef7a1fab60f7b4ce4275474b0c409bd58aeb5ebf9ac2442bb85c75d60149bfd79d4301fd5b5aa91a772c4ca19be37ebae1fc50e2aaa7c41646e0f6b0478fde87ea697c61790d606011782228855c44137fbfd354eb75bc5a24985a6777fadf17a84ce2e8f0e4ccfac15f296e7cecd0af9d91314fbcab5277ff213d2252646c4c85560bc91aa85b0a6d82fe2e9d299d80fb74e60c91c22d75d5a3d230ebc0c6e4072f7d00e55a7862787efd22dcbe53945af07e5a5468511207791b8402b227da6b705145609e920fec8db1c273235e1f693f7c0271772430714e1a340ee7416e2dcab591aaba4ee5cc4857fd163336303a28b8c19f3b841b8603e9b0c068fbd78f53300cf8b1b7db3eab8ce12a91418988095aa90c098aca96f7ca4a04ef8fc4fcf60f59f9cedf3812d6c362acacb33b5a7c87a396c065f7d36c04ee557375fd5e4998794ba2ee2634b72b1507529cc9ea13adb7b603db9ba2c270952e54464e540e52269c9e6789b89c18d6c5cfd23d8945c2fce794534653d81e28b8091536ae1e4c8f9f9bb9c274ab1d34627f7fef546221181c32a6f521a67ca6bd4f1e765afde8a443917e38861a8158ce8c77243d1781e2a663c54bd65cebbe4bd3a60ffe17925ff3274c16b58a1f6a3fdac1d1d9c76400275076d52c03e6bb94bc053a56934d13b0a54fd50101994e6816bb9c553153a30516cd1a58c55f03fa6dd993a76fb26832208ed230593ac1e9585eb30060a3e0e6ffcadffee9190fffda1c66ace26fef80ff107a58d7686774600ada4da22a192368b12d2154cf5166b9cef7720d2af029066d0b7127d9b8fe73cb05ace1b2bdb3c9b1c8599177cc8de07e8912ad2bc6a916f82cc4c6e25b6d962561cc8d7a74c998c23b4909f80af1371b108232fdf6c51ff3173cb591c9ba59fc939818692b636cf335d11982d40420192eaac14bf4fbb56654e29235b6d50b7e481640395402d242ffa60b6ad52a1a07ee8d9fa4f00faaaa3feb18b8fb642dafd679b45772dd6166fbda7c70d42d0aab9b0d87b7e0ed97edea1449170fd0902b9e42cada61fa4747f0c0db5daac3a9e095b266933b3b1213120cd368f7c9ce42460b296b0927ac53b82aa1f3741146ace2820ca9683ee6c4a90ecf1c2d034acb5d855a0221675b50ba01a32a8969f54163640a0548f35067a689dcf737699dc9124b199ed195d9afa87ffc8866f68a6d721617f67709f2a5416c1c5817106f969239770caa367c95269508bb3bbc2a6b7568f38dccb31d5c47daf346ff88637e386f43c1e4fd5dbfd5cb96a39fb5b3d8c5fa914bb1244f92d59b0d1763f4a1113f65c119d013c6f87637b87055417d9a15b9d165d211c1787babf87648f13a106b5021652823c163de9ac425ae2611a07b5258401233c664bed43454efa2f033dba02d83090a48a1bb3039a071508a76c69e5e1f7745fecc2f0455df5636636516cd84192c8398d04f3a63eaa299e918c62b9657bcd4563c74eb761640667ec02b2f598c34956b9a1b0ba826b9d5bde7918e4d8367e2eaec0c987c1ac814269628946d7afee40d6756ff8fae9d0d9e292c9ae06b262d6ba7dc6f1663ced118918aa58ee2e574758fed70dce9833efca0631c34244ec78e66df5ea0d529385913c0427f79aac45f3565afc28a3b50227935582f8c3e92c401848214f16b9839b12ec850cc2ab75fb4c839ca7cce07d9d5534b48077b7fb190324eccceb44d423c7c736bd9430cc6542a335611f36058ca9f5042e529716c2adb330f2a68ad8bf51904ef434f0a90e3699bda75739a41637f6a87454fc690b53b046d0b20e6133162d24db9565b2cfb9b15e5a06d25f1b32c0a56928f13a10fc5952fcee18f777b1bea7e4b1669e3adedd668ccbf52b865663bb09ecda2e99042e7a852e4fa7a68a509222df139aed8aeb82eb5a34f761cd83b85702c7122910d338508abc30890539492015f8a243923b4ec6bec872aa213eec44ffa2c94b839a95947f9abecd4bd1ad184145f7ffd3d5ece4059a4ccd9ae56fc2012de8c68f189073287df717ad81d72a177f23fb289ca9e4bb978bf525d39e376154e922c8aaa7b3d14b1b8251dc623316c61ad6572ddeca857c03320e553e0e35a55bc1ec1ca7e28f70322d000a078ddc8aa5f667e31fab0c2380b28d7fa8a365212458a31e2803b866dfeaf54f3583a57d8583156aff06ef06549a1d83aad4c0332b6d3aa34fc12c4bd81cbc2e179dffbb9f5445a63f079624e7bc1b99526e6d4264963c65754a1e0ee3288dc7a9112e0a4aa85d467f40ef209939865b7dbc692bb317149b172b0779fc9de122419f5049a5955c12e8247588b465bdfd70b3e567a608f586891fccc35baed7521d8b32ba03ef6aacce03b65ae3ef8cb283fd54f4b09534fb9d3c8d36309c3039a5de5f7042d3b8f08b441e70e26bd17cda79717b6416cc2eb7b2500b5259136d9767e773a694b059d34a1045021e0bf13eae79db549ffa00136d9b1934d10471b13be928e16ace21a51a9fce41fcb96f56b4f7566696367251b95050cf83c4f387b292265d0ddfe6037150b63e3c5a43ca4ce251a38b59e2aadf7e7a8287837500a63d724e331e83b1bd15e81fd8a87e0ecbe9ea538e483875b0cd1825fd59cbfbd9027f6204235539f51257e17572a5abbacc908295077cdc6b74cc2d78b46e84bd75baa81732d1cf1924c4fa1a0f5229c3a1325e7e8305b9523b6a00c20c8e9d2bb6042b0b93f46cfcff93ad921c6a476661da3414728af6d3ec57c6818e00aab11a9e808309103bc73aa49a7d6ba95cabea07707cbfb1a1304b5762d428698227b7b022e189fca2fbf969a504f30d1bfb3ad59749d01bcc6b1865f77a915dd64ed26dd5fbb5ac61cec2d8e1107afcec13ddb81d13f2a54a1741d9cfba25fc2d037959b719ba3700c7d22b39044b4a3a747bd70ea2ab878341443516640adaeb03fe7b8bb1b4acb65197ac539bb1236ff64401a84d3bb40303ecf46ad27083325ba06d4ea5c0e466090b6f511a44ec88bf8bbe2c4d3c8e2592aafdbdba8783341dd31558fd308ee6546cfec0dd1f97c31460d04204698f0db86e2475600ad5ba531fdd74fb12d5397fcc0d39c272fed029c317f5ad771eb5fa8487d1f6718f90a53697d42401b9175669912247c3370265ea68eb6c5a2bb401e4d93473f79bd3a6a46f60577286e04cfa9e4f0a83ebfea58f687a614454a28b5ad0a4d4582a4dc27acc5aa739c880261420494f1d436845fb8df51afa96c54be47e95e467cc55db48e6959ed21677fa23c9cdac26e2b159ec388a2bb91d60e02ac036f93d8e6f236db4cc9efff16062b055f3e94a91a638871aa38459033c42cf030692eb17589495e7e71d7a47126297dc6b60c383dd992da048d16de624c1cfa1a19107133441bb60cbfaf71287907584e3ba8e737a2ecfb8d051ca37f6c6f82f9b1da5cddd365353754017af0d1f0dd780448565d56fe553c30fe431da06fe5861a0504704ba7fe0a554a6beffb5d52f69b4dc6525b210796c638f267709513643b5f149c231089e84f8e91a996796d4866c2c5f49866d3110f637f5eb44a3c68e73f01bac937cace463667328f6d68172f074909e1f78f0fb0bf05316c81e7a4adaa255e644b7d761667486221831ac8078978b41a14a6d8e2ffd5e0916d984c0d482169e564865f548e18cab450adf4f68d44929ba6e56066ae154b790cff98b121a993cccab81d0eba4a8514f654ef06eed14d4b7c313829fa8a7c1c56624bb3c1c707d89dbbfa965f2eabc37b1540e407c259afb1759051cb555e3c5d5f19c75510baa6cb391f52ee8dbfd3d3b862290eccb334910c84d28edbdadef64eb908c89a8b8747137b0600b15611e293b2ad815385bd923573271e561a6494d39988e4086efd34e07df8b4eb6e8bd12118ebfa5683343f93deb430bc7a136dd24ff5c46b470c860e3f8353aabeb427ef5084cd2dca548c5c72d88179ee44b7a7989b7fc8cb2ac9cf2ca8631b4440811e523620642a0653b88bac5a29cb17b441ec2938531e1e3de07fbcf3c44cee04582f4fe10a2aa7a0007f3afa2a09f5b14996f1422a5e477da2df8e700677fa2ee1be5d075ee667fd713a55e3c53884dcbfba508f615a67cc84f19dfd12ef35c4cdb66059116a9261eafe6954225096192f1b5c13f66dd3bcd4eaa11f6265fe79941ebbfad5268d02a8fcffb827674f225c86ee6f8673111e68b57220ce91a99e0a71bb6d55eb8fda5688c445e37e4d9c20ad7d56bfd257ce45275bf24454ce440505b909a13604a23834d3dc9a9a8e4cd328f81ae35e4076f011fbcfdde668ae7f2208a3a080a4c8f6e404df43a3e05e8d315fb617dab50ceba48d1226298f2039a671b623a5717bd90484b4e24866412a11378195a0b7ef831b507c2eefefd866bcb5d24f5796de4e07e2235dbbd86817c3f7a43f9f9bd58d63d94f0078227a398182c5739a4c76fbd11a7a4a41ed27fb77ea1a557d424ede6f93867f75ad827af9d1931866ea955cd1ccefbb1b54d76b6a4740e91093da58ad498a66dff815b1ae94386833aff2e023a69f63f3c66e4152843a55efe4fbf6850ad4719ef13aed040c308e00fab7ea09ca34a3dcf8370f3bbd3567e1c9eea6094a6c19423f5da2230e48b4474ad76b4f123eb0d1f5a5d1cdfc938cdc3f663b97589fbfcee54c5166fd7bff5922d6b196a041c1e442998f2d642869f8a299da2ae8ed4dfd22f619dc192fb4d577875201bde7a3ecbe16796ba7712a743084652f078d6042f3ef7f3b5ac5c8abc11094f55f1086e3c45a59dbfc5451a700ac1d070b955305ed4fd233b8fb201a008d441851d4fd40033def95b5c5cf24ed053bdce7fbf61082f614d741c47704009db5c47afa51417136ec79349c559850c435868dcdcb44ed38ee52d5d8b101d4c6099a9ebd25e597f444f85377262167847c66177913ba16e4ea0e19a82264e94eb5c9d97f9939329fb7f51b6713dc3ac21c5731c44f30031324d43ba1cd69d576c02654f950e41c9d12dc32c01b91d7e4da72ba4c185aa70a90481d62ac185cacb26f59798af14fa6faacb9f2c235799adbc3f05f98ddb466a8bd9ff15f680c62418f4c6dee2c99d928817523cc99359ae849d9ef0c3d33bb994bd389bea80062bed97d1265e0689d7acc6f9624a2b81106f36939c01ade663d270a26e3437e89cc9e606be0babcb424edce28886a0bbd6989d6d2209eb2809e8ce7e0b71aeb11eb0c7364a55a28e318fc1b140422fc64ab70e7f5aac5bfd8c27740f11e88318a7eb9a637580ab3681c3b29e963c1df9251d02e7af0fa67f0c4a72ab0a99e27312c195e8782099e259547961ac2eb6c2336950bd1771f2f15601e9ba067e3790cc174f261b79fc68efdb361501a32c95c7aa55e14acc01a4dd14664df9537a05faf0c80f403f402377fc7389ca1740a35b4c951048603ddfe3044f89485eaf07ed738a5d4ceec5108de7c2aad10f0377cc0ef9ba6ee71b762ce4a0a1af9972a8f3b2f0b3d37e37a0b3a1f768caf9e02a1a57cf78111bcddf25d2e8cafeb9955fc8b44fba85526b028cd2c6ac9f4415290ec7fc142875bfdf8e66081ca1ddb3940cdcf941c50daebacfc2cc70c2b0062d880be78cc507f19d20f586310ff52d27ed7b78cac70144efb474e5e0520933a76933ff4789fda25b834178b601fa1953add5876302434dd01bdc87c865ef081c04bc3ef2408866787b4cf5d1b31c7037859cde522d8a65a196b7902045d70875e14f333656421d5ba5ba8e181bca93ddc36eeeaebcde1836776aa2858c50dc214ad9977c3339070b0348f87038b3a853209cbb1f9bcf137210405e81af4934e1e4a81e10fe44b4c43ce99094d5fd71ca79305661c99115f94832676f28140e773b35982463391cfdf03c39244a27f1623c8bbec5d744b15379d9c89a8ee0f567a56675283ba7e4a499abc389a281fb270c0b32ff11cb4cd0a82a8c1b009b35c16aa9062a47cd37be333614691d739be432109fa3bac5081ae67a1a43a4ce798fe21ab81e2e369092a3754ec6f8d87b51bf6d3634471cda671549699e01ad68c7f555c03ad4c9ff5ce0ca81aa4c576f29995ae9d0618591320ae77ee4f15bddd7b51a45770fbe398b8b98bee6447905218dd57d95ce24a0c4b70b95acda8bdf27cb0b51c5a1e8ebcb9279de86a090921b04bef2a3f7e73ab294d5ab72cbdd4d7180d0b77af8dff8ec9a7e347952916dddbd6cdbe980aa4b7efd708e14b1d4e709539c09fd9887d28fad785562e795deeed3631d33cc1bf1b6f46003afb11adbead3aa5f8f5c6495f34b518ed033f7ef2fa0b35ac589b768d08c502618bed2ccd58bf8d084d5ec23b93c9426bd1757692cdbf190e28faa95bad9179f3200b57355f334527dae9b75777ec804d70d7d523e12cb3dd9e3b032255441cb106cdffff05fcc40909ff246f9bd8815bc698549f56486eca87132c05b139b74078c7dd7be9451f7a6c6d36451bad3d4fe54aac92aba92838714691f5962cfc01f51f77dca17c88d21852a8d55bcf36d75c64cc84991aa3dd4294cabec8520b8ffb2a2c11b669dcdb529a499370666e80f0b1d1f349173d6d17e4229a20d514430ce1d3d16ed9e77a5deda1fb9992f5a9e09119bb496a501a810cb8ce6bf12a03795fc4460a0ffeba1f4ca299c6ed3384c5dbd042a7c459adb1db0ed702586d3f175d1015707ec562ef830abfe34e7a991092af5b6f97aa168ed4dd9fab2af5460717af00d23dcab1e5e563881d4f8fa0461d2791c7b95e1c2b66caabc4a0ad8313111b410d8c0162d23ec350fe0bc0e5b17180587033bfe528b7c9075e9748a130e9c7cb939f0aba62d5e95fa19840963737b417b52d66e08a6d9c0f64b348b3f515ecae96cf82a1055f10e9001303a9615a8e32136151c14b20ad79a94e4cb5f5369e38c6b0720235195090eda21924e063a3db3c3a83df08fe04886e7f12c9f0d6e7e2969776ddb08164f021eca2e10c6aeb2eb0ec10bf9a8744298430388cf8025b6d926201fc722f6c1a81591813921bbfa3e76e8ccb29781140cdbad1e49bb67acbed876f136f5e5dfe736b18b4bb3806cbafbdf516913aae66ca47e414d07a28aa40cbb5ae83d4ec2d2de1a763f9235380aec1309d212da13818ecbfcdbdad0c98ec31d2096e9c87a0fb44a617631b3c9372d866d436f2702851d5efd0fe617333da581ef50efbe130c32310121a58b09d30fac3a6d072e1c58e59a0d902f0f9e561d0fe39b48bb91861398b8468024f288f48b70b94715b55b81fbf3442b15cab8fdad2a785df9d5351fae0b51911587b593d2a06fce77ef10f9ab7ca580c92f19edbee66ef8067497c2b8f9c63c2d431e60978e05f33ca555b666b6f47d4559127f7fc902bde82016b01c19cf077f1ae5333c2bc0f1625d6b74aa938cbc8e9d15eeb5baaeddb3b0a5c651f5de1815350ace26386c1fa90969452cc7f084116af4b676ab02e5b29ee5bfb41d26d359045aeabf74b951c0721b9068b6b1077ec203f910bdba55f8bbb5caf245ee474d5387dd33c8f350ee9f1a0dcf8bd67793ef6a3e665b4ca6da60c083b4766bab8c375f69129db7ff6852fd3d0953cbe4046988913fda23626f637b878d037d5740160250346641e95108f2ce6a0b1ec78c82c36f32290ba915f0d0fdbc016cb6a15d95ffa755513a69032e18b5857b98ba62d217cde5f3afe8ddf60fdc4044019daf3880c1d2102ce2a3beb9a7bb3d493787a95d35f8f07ec071264ffe35ec6b4ed9eb9de881651bfc8b96d218b075c058581379d7f96bb860728832735311ea9da20d0e95aa1b0da9339fc02bad4e637934caa62aa83e6a2c28d9866a606146380429d052a9bd001e662944f0dfde46e259451b7a65069694fd54f079e5b3b8f9cee472cc55db73fbcf1d614457192dfaa44acdf42c4c4a0facb371263d2ed3fcc2309ebe613d2ca1fc6e43d36c5036bd6335e505a0ec6291de87699ad21304328bc45b26ff5d430fdf008f80e5cea70c6e3267367c6381fa7e50683d1018ee2e708fb996ea4799f80fc02f20ffb920221d24032e2cc2ede4840c112dd5763bdede61d26758e3c0fbb406b58b8a884f561d04e6969501d4152e4c7050269f2919a08ce4b69fd3c76022abf1ef38d71704f473966719da21d19954f1c0fbee3e11f27eed0f11f93dec3092c7f4ecd5d3f7055807258dde44cf9c236ee37812e5cedfcc8ea496f02b8bcb2ed708abe3c20b8e08054fa0870b54b08e90790b1fdfcb716d6d3a758b281aadf8b593da9041ef8eac6836815c9129a6f03c715c0977f361402b1222f443c8e6e61cae66fd05ae023fd6d3c20893aa2911cba813ce4a6c74d356c731b0b2e60bbd12879b123dc578e72b447aacd8eccd910deb6ebe6772ba7340b3a99653540e1c3023cf312210a7de97250c17b72b584f4dfc829b0c6e5b44b00b96be52ebfebead8d276c29c47f9b66f15ebd8c0b7af1aa6f5673a981135895feb4f4ec51e49d8ff80d3e249518f8f29aa4182e211c295622bc0f1e8240f710de2f4540c6ce3d06ca09338df18e87e70d588c0846f3947d0f5964dd13ca9172d34dea910e337121307f2a22a46026af4faf7afd2f36336db4455b4d0944516ae4ccf754d044adc9c4490a4c49ab3fd6698515c64c48089fbea76d64f79b471aac6a8892872c7448b5f565457f29a8b4f6fca8f9b7da5837e95662455472d11a701956001cce8fb512fe196f1dbaf1cdb6674d97d1e8cd67b3b13bc17ea75f3384a37b923f39e3f77af49b476620eebeb162ca0432bf9d04f71aef2ebaae26ccf5bb2fc295cf0ff9934c8b2cbeda64ea7bf00cde6635537cd04c7214c9f39aabce092391e8b9f4476e97a0b3f6949b99ee3b783aef0549ff4335fef2761baaf24f04996fe3e09f928780b41cd39c0e111316e5fd25c4997a7df225500a1cb85332080da9a48427d4f16ead3e7ec422faae7b323348e781db2e115c4adcfec9e9a3b558bd38db5f70e70cf2075da147cc6e102bf72c93eb88d80bbe960d9f0d66ef5196ffc74c036dfc9b74457d8b89f1c61b303a953ae24cbba05bf57c0e6136b252601990c3307cac4bfb724d581a8595678bcb15e0fb637af7a4a44342d651ad68804526d69c11459fa0388c84d9d0134e8311b7f6eac31b71e3818065ff8194dba847f38e68836578ffa97904aa903d2d44c27a04102a101a8a7c95993a0e17c16c2315f8af9646d61b96d062919d1ca7464d12567d5a499385d9e4804c6020d0930e11681353ef27c47475a1dd4fddab1524b1e48741fc7ae824a0ebd20354630e88125e6e9bd128a60846612eddf143b3024e78d2e4edd3650d1b7de60a67ac39de95f4e5ade77eaa646eb186a19c177ef0c8ab95a63d9754d6a868995deed88512fb6986cacaf70add138421e67f4b3b0e57180ddde53553777f892b5ce405664327be6ed86536897be733497f89f3764e3d3e1b9ae40611545905e71b0559ec7a1ca6c9677f481cfb849a27fe4924f32b791694b5b2a97a2a3d1c6e37d345c30e74ab53833964a7c852f091494e5b4d758a89c08fda9107f7d019a42c697fee9372a120265fddaee70f431ef3f8203b3ace86d064420b5dae0ec03e9b0cd5ebaa1f898d977f109ae8f0f0ee6e7a9f877ce697c90a59999c3be5fb701896914eb3f89fb1f600a302ce78d9cf55228a6f23d5ba5015d444cd1440a8eadf9e58308ee9dfa4144f02cb4794ada3b96b10030aac7d8a2c73b784f73dfc83c1b267a32230567710c5ffe2aed2480595125418e13e027d3f5a9c4ac5dd944c70927774c376dd3e600f346f907f7f4c615c560977836e32e7d8679022234cd7699f366fb8f670accac5ccef907a52f1a95cde21b0fe2825d6aadb17fcb32b423e78d48788996337c06025c947c52ef7983cec321fcd9608fbb608f8cee69ee56a2c161f466e977af0237ffdd501cd4661d21f2697b39ef4ea080c291f7bacd18a52809c63a1127dfe5a13481e13a191f907d2c2f297f21625b990b3b79d9905fc5b853afff0290ec74613bf641f5403d9be3b49f28d59bcc8e07ba1eb122eff0f2c14e1cb967a184a0e64d6718d613328611c977cd2dcc1a7a61fb8b0dd258da6c08a717f3e43dd3172a135878be3499fd2d2b26d48977a5e1eeee1742099dd43433fc0ddcc1b7be877080779b29c6e68fa6ca94e09995f8bae143099c7d68b8c272b8431c85d0776fc1488e7971ee328ff01e58a803dec758782623daabd0c32c0a52f3e7c8d72346ba490efed8b0cbee0e21552da6df4faeae8e7672e1948f818f9ec2af0ee0b0e84ecc3be9cf6572226dbdcad0dca9b015568bae27c91c6c53e0b204f5226cdde130be0d7e7b32282eddd8798c5e0666e9d38627c745fad08694355d05a8402c58df136ea1e8e2a259802f5b83f30b1af3f8ce9b406c375b7ce68415054ea8e7472713684c276203819878dd3d406740f24520702d751cf8a41a491cbc9782cc7be13d2a04c0c4173bf750a8406192846f985f7b137405423286a0acb18375a8802ac00cd274ee7603d2638027156ce1eca30c05208789c2e0ca94232e78e0cea1197fd7d1a65d877bc507dffc76ba7cafee99e466ab04e6dc9630d8cc3b9dbe3ff42bce693d866b93d4ec892462f5c6fe10b7c26afc99fa67dd8b8d96b686e2e525597c9e733fef84443a8ea96ddde8874d4592eb74b9b2fa05322ad8d2e5e0fc145b9d6f2bc013c31bae2807d87b06c90a136e06e30395e35b7ee6d3e3c98a349c306f975038bbd485bd6442ffd19c446480912e212f877dddaad28562859245350b121961499144851e2a0bcfb47e76429dba650c6f98e2f1aaa7f1014f2c88b34fbdd7a6f4478d8eea4936873ad38a525d7f9b0f09cf3cdc0594b60bd0f347de945573500afce2ad42421179f47f9ee12b0cefaf4b5976a9d2658b6dc86a9103f4bcab67abcb2c5691e73f887d297789397f2ff459ca25f2ae857858681177eac3504916cd646683e221dfbd0e743a9a7b9d9584388d770c6528006fc72fdd1f1f2d78cd8d6e2ab6ef991b08e8d55448ebef42dac49d8b79bb9b848136507ef0cab3f858557ed6a3332247705d213e6da618464e63ede11b2321f16459c8ef5009ca075dfa57a31b2db522d58d31b69c3e103561267676a96f1023cbc5988a5ad6727efef63d3ee316b78b9ec7e571c5fb65fbd498505d90a39e1b71ad89d4aba9b4dc02d645bba6f8b7623361064e4ab9d98cd5e90c22b6d4f5c8f96d698cc77ac287063f07e6bed604431dd64afd8aff21f9072411dfeffdad46a45210f56dbe2b9fb1c947b6b7ea9f02b43ef869bd2ab6459fa70d729b3db2657176e7837604d81746b141f95f603cf2b663de03c8b81a9d7c82e0299a321ee407b2183e5a1a69c72b56c27ea73c852d56b69ec5eb636a52bd9dd6d4779574563663317a62fffc5e8a3584bacfc12356b94144b7c32659608502fd8342a2c4ca6ed1a5f039a81403add602447accbf72b2b49e38a06ccb575a0cdbadc1da32d3129f65ebfa516f27c46696cf3bac9cce1ec52c6f5c4d22e3de6b34faaa3a4927bc856567fa03de161a7878b008b27821564aaf30dafb409cd2ace222e778dbe3844210cffcdc1a9b865fe6f93d34ab9a73fe415388054672ced72b55d2f590b96d15f296bebce22f607b67fd1da6ae7921e9c41f05e979bba52d49f8bba7b53c4951c6124e9d92a25b7dab04852ac5e8320b64d5cd5c19ffa20a9e96aeea9643269001dcb5cf37872ada985aba0996215738c164bee847aa9ecbd95e76ff88e107ab37082795f7e9f032a5f1c74ac14ce09fb43be9bc2d4eb7cee4b69f4d4d2b541775f0b33d5e078ed59fc163dea9e430e841bf6a70630add46bcb1e296faa7a2e909b608516e494a5d353c759519c1b0f8be9b4c80b7ab29bfd1a60925262bd7693a61a08e12b89fdf91b98506c1e3fa0779f569ec7d8d051a4c2d188df629a253f33e177d14dc06094a9bdd4fd9605aaf17335a2a575e1e9295f938c9ceb020c8d47a3ebf3144d826c2da82a87c808271716ce31a0d435a9485df63244f4815ec1dd47bb068a7525df3ec0acf14dab33d7e18a45bc886357eefb1109f61819013fc0ef07e1bb511460cc6ef7e726c8fbed932ca41263d069a62081ab79b992204871ae49e608cd25a2750f56df0cefb9862d8bf1aebe4613f4ac77acb6a222dbf34987f46dfbc8d76293041a97f4f6c358e07950123a8b5c04f3a944664440e8c7d48746100e6aa59b7199325dee34d84e5c1ff31a767960c3e62e45488dc746249283e4b0f5da83b294b9bd7297774cb4961d11d35f91f23c5e390964880c9845d3dc9a9b4681547016f194eebc5665c56daf95e725348877bd1a8159ca7d12b629fe4e9f73dc1b9a42b56e67ff0ff7379d78110438398a4460250db74fe6580b925aa0f74deaaf723de375ffff0deeef22445200bb0f0d986ab6850f486ed45a9af9f874087e5ee8508a6e49e955943d80aa60481a0dc1ac3a70c220d2d17680003d7d7531ba92e20e3b3c34509a3c6b8258e87cee3ab76c92f32335e8164d16ea75af2399964592c598ebec3f3d48594d7a0a69c9e5b748f4cb2394e50a4a03685e1284c4d557f2aadc9df08b0622ae52f32a7eb45c14403bd4377da301f1d0d3489b5cdc2f6ae49ce3f108d4372aae4de4922c4597f0724b8d67606edf45192fec56052b051eeb7f627a1f95eeb60861154b8ca8bdd01c89596a3131a7357f92671aafb0af9f60a3f9a573c7b6ffeb523d1dbf4f0515abf6f348774d63f506b19dc5fd0f15067986aab54de98feeeabb6a05ee0946344ff0ac2536f63d828b95be9e1d919ee00824d55a70ecd2f3eed2595e1f9eb8e0f80513e9fd9f8179df1a242678a4ba8b53e9ee21a1b184226ff4b427eb4b83c74c651ee615dee624db3589c7f6df471774a959a82eb9de6b0b1cc040e1db00d0474714824592f6a64f981a3a7134f327ccd733fd0e2a0a9e4abbfaf9f6c3c77e20142eec593506c2f374e5163aea96bd7214c4635f666f4994fd7acb0baa83923cbdd9fd400340bb58f50abdf410407c9cd3eb271162206146279c05a8d79b8ce69d304cb0ecc75ad75b4cd686cd3dbbef2b28d20d23de88a0db4fb3c1a5c9cb2fd82a35f5f34d7c932add8b6248a5fbf645fad02d1cdb8b696271a5223bcca0df5a5bf8d7475f52220a30cbbe0b8991b45cbcdb1aeb2b46aca793235c43587987c000fc5d514963095770c0a7755ab73c94c61a8cc4726b819415b63f90ceb9b4dbd1aaeef3a95bd94cb8325411e65933c849952556e3fc8583c38cdb20457f6ae73dd6717545af239ac2aae6f542a98294c53a265f550d50428a9dc21464114823da62642d279466977eb0b23d3bb6d3bfb6089208ea719f6ab2139d233cacb709220b49331706b6477f8007e09e72c3474ef1554789ccfdbc4aa403cbbb762a022aa7de2a47f39b27164eaa9932c7a704b66e6125d16cef8a5e24c108f74d42076be367b48a001fd5cd38d1d6b44618444af3e43b103549e066b6df2863caa173884cf9cb221f10553a9342ef18ffd994b885a5d02acd1bb4a6ca29622999bb7a822fd37917877978101bd06fff3e4c4b9cfe114a4c46b287e9f7a56d1e01593a782155b374da41e103d04c2ebece1d237ed0920f4acb1d644549dc91d6fd2d47845fa9519933a17df68fafbb4a78515d23a670f3f6e721807801d8469e6cdc97fa95747c0c70265d62dca6efb1038fd9fcca04a421b1732ca30b3089bbfd11f3f8dbfeedc51d6a75d720b72a7400a625d8a252bce04b9851b5a28084006963bb7f854ed90ba3ef3a39954e7f34081d094fcfbd2a38517d4d3a18cd950019e3baf66d714e754096fe18d94d289552b9520bc594d2c40e99beef525e5fcd2086c50add3b03017822394338c6d022d7bd90ef983d438d677a481310f5144f5e2b1e15d2f53d92f961e5773f77b62fee70da51465cc58a18e435da1453d5e864bb426db33f12e4e47eccd04c294799fedc67043a60c94c9fb0256f1507a0b00e8e49ce813e2a562efecdb97ec1823bcaf692ce853a69362f7bf614d39f88bb10a0e3adfc8737ef92e7ea4cff42c7d07558980313be21cd6e17f739eb9041d2d2174cf52134509a99014e27b5116afc070bf92d94334859cdb94924cbc83af621b35e3123e9a6d9e81352f947c275e61b6e22e043d2ec55b7711e4b211e3f83d7b15e8bb592bd6f07f69c45b1c4ef2deaffe1f36f17a870011a4387a8f056b36b00b0dae4d46fcceb8aba8f5ea157e81cc6e9ef1a146e44fd715fc636ca56a8bb73695c3578e7f09c0cebc906cabe33d03b989cc4f87f86dc352de0ff9f1ca5eeb23d5217486d5d3d19da6dd535b79b6bf5a69b43adf8e65b153bcc777f158f64d02a9e26bfc353067c6ec48c6dedac17da3201c36643c80a734d2b2d5528c275796622e68990fe0de71658a4089e1a1d899c858c729e1c41246eb5a2873c1095719a27133cce54a2a8f5b656bc7b973f3e9827e1d437049bdcdf7103fa6bb32f51ff2a46936709586df7e6de7900ee84108469d089361ad72890a4ff14370ad8ed5bd6f3f93a0e13ae92ed35ac31070f7a0989d8bf43b27ec221188b215676d6f6599109e8341485e1964772e4ea28653f13903fd110423848faee024b2df5f6d6dd87479816b19ea6176ba7011729050fba84ab659b8b20e7b4782a651c0fe6b2943946ad0eba5b70fb89159667f12fd46b17c1762ad54d8c548cd1c96baaff75f719ccc53d4f00f1b7eda65386fc2ec69f8a248defd567c702719d5a926c027eeccd85bf01b5ece186865f3241b40904ff75098232624c07cf4d61ee5e19c62e3c4c68237016b1693a875eaf63e2dd22ca44d9a04e17fba9bb17dbc4af0166f0366fdf7cf969b23f153323b2415b9840c1a049e2998d9aebe68496a661310ee0ac62b62ec7ad7d2bea84fea7aaf4aa13b947625f9b96d5fef203b5f230282d0aa44e35330f03ce953663a95ea9430038c5f71da3ab7ed590c20f43025a84bbe7a8f9f7ae1cf9641f41049062f9ce631a974a227c67deb826815350b33d38b8189dbd3e4812fc08b2c58fc6f7455911be757cd34f1c2ae2f7e3503cfd2ec22d03b5eec8f0fa7fcee51c0f02ed12646e72e7e3f09519f0d7ffbec60cd2433ad77f20e1e57f11b65c3c4bf3a3fc72fe428d4de66499708c53ae8c237a10040d6f9c51809db542ae9db35b7254a9336af4d72c3b5a0563f6427d4d844d9e7c3466dde4806877942d125cf887d94c0cdcb44d0289c3920b87e3e3a019d0dedcd6887f88f86277fea67faee056fc631f6216ead568efabc2b736dfb9ca272717f8cb4f80e9215e25160cad957d77fb0424db69088ff58f2843db85c6ca5231aeb504994eff467b22bf07f4c4f38c0f418ec933baa17177563a8926ef8f432cf38ad908407889ce15cf70f73da387df7db2823727d01d3228e233f2da3f8a0df200f07f2eba4cccdb9299d9b6993b3a4df5000f2ca4a9dc7bcad0afd3006baa3e306b26a17376b696eace57ac04a2f6510e8cbc8ffd85a5e0bc4d3b481130e4f608482fb1e82bd262ceaa3d0c9b25f80a2637cc19e8d9b9ff0e0df890799aea94f123a18d6279448253a463c0de120e76a1153261b7c70d4f03cc4c94b86ad57fe326e0359ad20b87b880601c77d7d0ecc03ef72bb8e7e67c833bd27d7774cdfbfecff6769727cb6a5f0230c44a61203099ccf0ac0510f2fac737c1c28c2cb220489580e7948d108fbb949fc30a88d98f029899c30d61d6540987037da77e46ba97fe1542e39db6447d9ce9aed1a68030b483d05716ac68d51df352e099cdc8dda014fb187a4bca243449b0cec04852b06beef019f5c500ce1631f932f8e8af11e8abe97637693cfa4d4b7c7a9d87b5763f4bff421859d4bfca35296d71843a5b643465833a7ed27a904bfef8c7ef491fa74260b42d282205acc2e5f133b012145a87036f4fd66eac87278bc0d78ff2080510f41a202c604ecc9cf8bbef41fc34edee8a8141a9c52c5df08dfb9fcda605254d4710ad5c0308cd772c1822d12194d4d991a3b96fa630e66df8ac00fc6ad149492f034f9f22ada654ab32238a3d5daa111e1d522851424317b8a7b8211ebac906aef38f53cea86a9a2cc7c78262c165b7b6c1a96ae3dcaa797da7aa5a2818c52193c985747603bbcc0385b9741da85fd26f5851120d7f917e073668bd931781081329a2de3d0187a97dba31cadeb3d13dc86959aac97a1738d62ac617a46e25a3fb3cd82d125cf0c87d3d00d562ce1d8d84098ca9c3058caf523b916007802e79279e636bf56d57fbb2727eb4933562416c15d285b210c409e810f915037bf943580fee23d61b984991c273309afbe980a87d3c5d058ae99cdde0fdae004f677290fbe57df42758a26654704fdb372e2145771248e184c9c8727365205806a9048141472a5c34ca87027646e5989a5d190b017c4f884d371dcac6b8383180166cbead46aeac26a58778d0e5181e74a297c7d313bf39f132121a4dad6cc20821e4748467d35c2fb14cf3f9d916503478027278fbb73d1d53f9bec5877af7bf6b505fe56b1a98431e4abbed3ec889887b8482b5286eded4b4f0443b1bce7b79675aef6a1c0cf3df43291f484dce10174da1a28ba3bd4c21c4ba023fb2a5b44542aef4223b2ce89f1f7616316533b5eff35df1df1a80f0aaa2215a33a180ce505e48ae4b8d717b1ca23abe97ae7f22f259c06a3bcbb8ea26921f21218222f75d8a2fb4f145e3344c6b8f62beb1d449794e99730931019748ae2c3a1a04e45eedf938efd1cfe9ed694960c9f7d587171b294064219308dbd8cefb22ee32bf743963ef3c8184976d6f933d874f26f0340a44839ac233316080abd1c0a780c22890bcb3bd554302e51e189c9d4e15dcd14b747ffc8132e7d2c31a50d32a1932450e2825bc4410b6f789db6123288f799cd6fc6c642db060368cf086c6cd04fb6a1efc168bc6d50c3546bbf3c0d9bbb25b1d9cac795a203551b6b67273d9eb5d5c6a3c28ff5f8dc26062002eda63ca30b01f7da357e7aea83e9f01ad2ad05b065595ea276687c8104f7b770849252adf18309af64d0ea33a22a1b411f46c53e8fc28a16481fd9ff94098c782fc4e7f636b7c0d27d4fa799949eb1ff2915adf4c603a072bca8de219d2ed4bd659640049690ffa85165a9ea62b1f3446670de3c2c0940b9fe5d7f056de87065f159bbb375e1f0beba42ec871be8e62e4c7433e0d4a1257704d84111914b6f13447d10a8f781c87bcc53c70ad4b16393162195ba4e67f32b3df95ff5790a8160081668d9ccee5d786c08d413902be526b25f3bc24f6e5b7ab42ca11fa6746697748089f28ab78481066bdd27e9044276299148dd093e8b65893ab0bc9fdf2482c69ae72db2aacd76f5e2301edf8f5dc71a4aa43f79679f2bbc589cd7be9c7a882077b204e3d70bda89c9151ffbdf9c863f24d0581ff5820b75d62c2d4dd532f4308ddc4b95144d2b16f628e7148868d877d0b35d252fe747fcd80661d564037ba86d23099b242776d7e8b22661297da7ce284116bda7c5e5887329b9e0a7d1f53c6e2f32d8285004824f26c4705692aa5f217346f78f9ec5870bb88d0f03caf553320757673a76d640d57801f2ab0009f864d0d84a2cad36a06a0b1f4f70f5ee5677db33f8207f385cc079230cfbf82bee176cc9cee2283bc55f504e723397fb3a69a2e7e278e5292256461aa217afbe7cb668bbaf6e3135517170434fce12564c4d72535adfb995ef9879a3895237cde578c8c3d3a0c658c21c47aab2e6f86ae82d15a5020287650a5641a036b116986251fe6774a38c997907021574c21ab7ebddb019e1d5c9c30213e7599c2949e3ab99828a42022ded599ba8984dbea3f4d6438653d032157f8f76f4d708d02761885ca3968b0b05c4a06e6d2b225e306c4477d817f71588f9ce4b573245ecc19f3c59791ffef82820403cf34605c4b14c1366dfe386e71ed4c62ef5e16b95569a9c91fc51b2c6966c86035db823d5048ffbe90e57f5fac1faf5963d0595e35372d67942c34e43feb4ada9c85d272c0d42bbbbe66ed4b823663a578ce0f2e5c5147817f1aab047b4abd00cc9d01dffecea11f44afd4c66abf46788b2e635fc908899a12d6dfe5b3d97be4540b1311a454d86455aa92652797d2a834d4842da84f91a6b7764961f813f84294e6cac1d02b336f6ca688db3f2ea080dda4cba467750e6d968312e97d925ebcb47007a2f48eda75ceef065c25ce8aa13904e99bb4dc647ecf3a8e4ea5e333d47d805d6d4a96d5d435db9646510f6d92d7bda2b84ce7bc92a8beca714fd32879106856e844b24ec8bb72c949466ac21d101aba62db2c67582f92f4b2cb06bf603d753c1bd7879ab19a4bf25c462f74b4c0a36ea2619ab7507aa619e5e3122c7e9e44aba7c0ac8b47b9b23466c3070b0ada210b01126a4a364bf490df1a2d4a06c747ba21aeb8a5cef400e37e30d4616aa4327fe0e5db7ba282f9b91fe41d577f0c9db891af52b042a7861e82a9bc5153ce52c639280542ebf5cd6be03285594bf322fa55dbef25616d00bbe1c2e9cde31b604a24acf0ab4b629d6b54e965bad59e01f084d066b498b01e6d9b6b81ce4318bad14f0260ef727ca1a1413db47aa3b070edc4b5dc29be99cadf53e7ad4f6775522aba4de90a66b9d5e9b165cc32c13bd9c33ddb310a8db0873917e216a499a026c7e563f8ec43ff224530783d30423f2fe49a693f4cac8d8bc6bbd5fd7df15e6d926ab8b45c6fbc72899f2a867a2a044fe81f806026bb302a03093ed9b8892db9a16cfb91c15085371e520dadfd0753b047d68937fa40377ac5c49f4a55d7fbb615951d63eb0ec4c5c4fce82303458ed1f54cc1105be960753ee48a85f73e51cdd0d6237ce522554c7a4997c80d52f24b23037b6d3578a2698ac9fdc61bee9a8b2ee2c1ff8a6cfb451035f6da46137b8aeb83165171ec9d3ae02a8d49ac4d2d76e4c645e6fa9de562bb2e700356ed66b37f1f4f4e29275e2fdb40b69fda6662d847f865316788e4edc8243128942426a00f194cfa76bb5dcce2bbda9095b1d2b4eddce02229f7aaba53e7808677314716260d2781759c1b24916446ee1f32ec4b6fcbf37b4a98a43ea556b412118556dce0e498fd3c88da7bc3064b4e4f849d4b483d5a757fd71c8d214b2ed55eb5528622a6a39fd6a95fe4d27484fbe699245675f57f7c6f779f76e0d78e6a73cc85d3f6d18fb3513ae7a76e60ab53b2103bbf82ccc5490ef1ce764fd5f8304f5166e9dceb8d4f0f2a5f88169b302225c67e3b7b66649e2497d7fca75c9f6aa1b109af9930bfb60b8a266080a52bd7f866b36e22d1a4fb735224055a7de5f50dd6da4557ef9e284eba9ede58cb953716ca733f31f43ab36b8ab0ea63a9223ce7045a4d0e6e519b7f8f8f0a3222a6b1baaf13d591480f5083cc34d3d300025f2fc595e1162e554e8746093fe57039d58ac1c84d86dc346d89e7ab6135ed4c4042c3c767022473b92233cb99816bd4e4a948ec2f710ea6dfe45800e4eb72baa7e6b6230f70189bcdfd1d50943df67ebadc21cacdab658c6732671ae9a9225b0d733e3590f515afaa1a621ce82b05ed4c51e783c4944d0fb2edcd73be7fff795c03a3550d8d9ce52c574c8846102fe5bdbc5402081b71bae2630e4396c8dcf0d452a43ab6ef03d2dc6368c5f27500814b5a9d8ff70409289e7ade16f3f2aca2e5af4a908d05c21df82c86a466cd2e3349846a3715bc3b9ddfd4a84c89f3e2ecc219710fabdbef5f18826fe69152a09dda1d69a34e92dab58f8a67b9a245603034ea180e202f7db9eb7bbf453cdf28893212bbb13c5e5b6b6b6a35e9faf27ac328aa253a9d5c43125cea17494819404a43792c2d80541a45086cd9f5ea4baf77dbe7ba45791dc55e34cd3f67d903e7aee969372eac2f3390de096464f434dd4845e4fd8336db5075189620e065aa837f6b3b8f99a2a03166dd49c6ce61f8b464c5114bdc77ba9b11e147f4039f4c9ed20b46150a2ac51a9f0922c924c51381dd1a78d67944bb6f76fd5610453455e6a1ac4cf2f12ff8d25e1f2b7eeb79a55b1c4192369a62feee0f97c91d2d336b087d1a9f9d44475f6bc57a0e0f0734f8d7ec590ed50cc824fd3d21d186bc2dcc2a010393e9861cce7b89c48af86838db060f5932e7c9c2fb7965fb2fa1d1dd3768c01065439d8fbdf5aee102ed34c0befcb9d93158df2c30c98c24f6b1c6e233f92793f8598d0bb2d1cd7e7253b5560545a77ead818c20d1d1e6a9d5e84089c752ec8c9ac617ac08d092be159de857fca8897d6a20cb1d08fbb0d9e2b441fc12088b5543b77e4bd2396136ce9d36e85f5493224df753ac0cb16db89f5417073e05078972670a67045406b1597f617c3f3babbd51af7797f6f7a0513f3bad561ca736ded89f1599bd1841e0dd1b605a26a270820d77d1c96d3df3c3ea56ce4e292f9a04b7854c2bb516bcb032d82994a6b113db39ed905e24aabf4ea5b01bc4d2f43342be3baf2d86d88da104ff04c6a8655e42bb7efbe15e4fdf8678ec3a3eb4bbdecb8a577ba4ecb7b39f2356f0d275946b4aa635132834f589871e7292a7e8c77074fed1fd222d2623e6b64a5072f7a6a11a4e57dc3262093aa7960652f5b5c8b46123ab950d8ecc8ba16163adccfe548631af7ac43238821dd6787f45d6ea56918ce3407b585303e983eb7b28aab4080e80015e011e4f656762bce1a19d43e841d3a255fb22a24ad23d9cda6e9f07df337774481c00d39cae48036c661c1023a4c53d0b9821ef9a398dfe7ffa383fd685035eee1915bc884b193407088d60a9d5ac3c6c91a3cf54a6af866307aa0b64dcdb982b46d54eb29d0e2ed100b2ef9415a8cda8ae81a5a82d977e1cf2eb4aa78a724631133e21fbe953962fc31eef968fb62a3ef0760681872f6600848a748d5cfcc3a10c20df327acfda0cdfd5c2a6551203ad01558fdbec6f6751736746ce85f39cd7f1198b1d60b6e057e9a8765f901bc21cc698b03795c6e0c9599a146046d543948f68de8a197203b8749e5036b3f816c4e6a7018960a5a514fc6a5523b38c363e8f9ff0bbccdd0aedc64e52ede6a324c26488c01eab2da260f9d12c5aa5ae29c403fba3de7b3944c28c526741309540c9c9da71a159d8eda6a723ef42562095e23659ceffc2a73c7d780988ac3a5c21510938015c25ba1ba976afe41f10ef97db45249f66bc74bd817f5dc568cbd939b00d5371ca8c2d7a096f6351c06c0be830cd50b52c3b5d3b133367ca7d5eba6fac00a0b8120c9846b838e61b6fd8c00d6d6c653aa7195809e17db6f6c3bd2c7103001fa445a98b058b2d190310da7cb6e46e5a6f9976539551412cd49b4a473752efe81162389693a0f019da55759adc99d6f2d7a797c08fdfae6747388735726f50c052e9263134b62cb79f1a4b9d8de1599b4f84bf0e55c9875c10deddd80bec2295f3db25b8edf61a1e866f6ceee1b84fba69291e82e260109cb1bc10084cf83419594ffcedf4d263ab882763d39d82bd87bf6c76ee8a73c2af1a6d209a72727f99d3111c6b94ade083eec77096ac41279f6d4340dabf637814ba31360c4e1046d8c144b26a16404a9488dd5c6f46027012f9ce52f680096dc91202623b6d1587089ec0374f98cf9f42955c0445bc553af4934df7ab815611ab7599e5e15e2665e9d09d2cd37c88345d6dabddb2b017a7706ca1b1825c3f2b224b7f2f94fc45be993ed777b72695ae7385b7167a572a83b09cc6417cb595ec1f67ddd4a0c5d1172f18efe6abc66011f185a094bcce084e5d34735716d9ed553df445f4c53aec0aa6583d6e16c850212b77e75270e63806c4e788efef5392893fddc9c623046888a2f61768e33515518170c133d6094881e9b56be9c466e2f2708066a990fe0f316cf4ead736da9bae3cdb974395dff8c34071729181ea7396a44c81907980cefb23f31bde9ea249a230ffc9a4f92a6d76cbf7bfb5cede59178f23e4c83ae29306711892d0bdea61afcd4bf7809f3a6fa657b74b05db53d8feffce20fc134e20384a34250b636cec1a490c86c4bc6d2de1d914344f913a0df4fbaa99ed577e07b7cd581b143aefbc8b2c84e6f92bff50929c8efe7d8429ae492d5664f7fe5efed7a0ff1c36d9ffa5ba62611783fed8ba1110e83f63b67665f207130eee310b3d098aca7f35a458adfb158964f21e013925f2dd9ae316174750089d8a730d3a205a5cbb33a56fda415b1536aa953568918df73cc3594830ba642bbb8675e3e765474418963f7b4f315421f83b959b66299923e18ee7a2e82928887daa402bb8a2cde2799f840e7c3936aee946fc0646cd0f60be5e08514ff9636b2c9217acb0ad4b239e61b40faef002072609d26a4545d06bb727ffc5a1f51510457bf6448f947aa2b7fe6c52c279fb8ecc28ef97bbffe7b64b0d9dfecaa2b50dc54d2de5fef24e4b55d6394b54d7324e74ad4aed4fb07735c66106f1c3508ff30a3b1363a41bd65e83b6b909d3a2e2f31837bc9bcced95050fe4543a99b31347c9036bbbc4d6f33c1463b22a612442653b67fd70aba3523ee9307a1a1f00ff4006e9228ea9920accb077034d4eb3f58bd940fd547580817d796df29441af1aef72df4d21b3b935e4644737da0b20c39a6416fe6b6bcd185ee450204c14498dc1d285b9bf85bd42f32e98a226cc544146c5d1f9963e5a406f4c1d202d8e3f52e3ac498ae4a2ebfd930e896568be55c2883d0359766495a84a0e9f8f60e43cfb86021699392d3bdbcd477f050ba4442502966302b1c863a5eda5826dec92bc1d9411ea9e647c0ac7f5ab6a36f2f3ba2fcf2f9003a3c42180d16c707c56807a4a2ed11a903a541ab07161386fbc637fbf50523b3529e995b3e477ddfee98828924371255909d674ab3879b5ac0bd161f01ba4cee34d6168b3121cdc9828fff15c3094e475aa15cb041ec22f0fe3e3e35ec81ee43bf954d7eb6b259bda991d333d0e7a4016d775926069b47df531fe74a01e53974eb70f3a1035da6cef7fc0de4bc936526d58ff3f351210a1522c2c173dc10bd1c458619947b12587f5b9d1f57937a22c7c59feea924378099083d41cbd2cbd323ebb378d273487cb13ae997b93144241fcf3faba57608e143eecc98d8ff0d0595c7d672499f071483ecc40de42d01ce87edc7ea87462f2ee3a2a041870b72dd2dea59d028a5becb031b369146977aeae7624e1b4682f524fd6d03448aec7e5e68652f6a35c047f030ee6b81656a067be6217940556ffcb00ba8f0f2d94bdc78a733e68b6fdf6194ac5423b50c655ae61dc7925291c89e5a569bcc62c744fb798a0b3af2fc0c7be4c691d0d51171053ad443d75fe5842218d6aad7574cff0ee5ae84fff423994c135f2f056777506d5a4f4d8aaa77c4188d9793adb114e256f252c552d8d7b0685de05a163bcabe93677f6f9d4cdf6b91b9c57e04f644ffdaa99e8946e221c7f1a9008d6f19d12ec2dc6ade1a9879caa2eda9b7fb61ebebba03e7ad95c7fb6a153b5febd3be31956b69e2009dcc0c1473773658b158a87fe8ea32ae8022e207eb26715ca97ec14b88baa8351e6eaeba7bf5253389238a242b283ebd4d7feb98b09ccc010e57ed3b5c688015b824bc7e2fa0cc088d466fea338b0a59ff73128b80922e55b5f3bfeee2fefacda2d9c95b20bef31bd35a8fe6890ecaa8241454806b78421a8b1808dadffb291d724ea274144c010629cf5167899c0989a68c7377d754b555dc6dd75d306360869b27a5f391b7a0364eb769b5adf165910e21e3687395efa8e1cc5eb5a3f9bc046f2812ce5c5954dc54a2edf932b24c848cb381dd487e653cd60929c30446c9ee6ec909feca8edc707f92e0123eb5b8ac251d73170a114572a71df6504313491d027f63edf4901a5d505178aaeeb71ec9d85af0ae01518ac21d3b1ba6cd0568557f288ebdba837211748009d3eaf1ea4250b50dba4b041d3a9efbd606fcd4ae0fd13e1bd54c12518270e240c70462e39e2a49db06be712b4562090ca127ab3d649714deaad2fa370314f5d5790aa6d0304214564c104e3eb60b140087d84b4ba7b7b9852dcf5799e73384264a15445afdae2df202b875022c0840f04d90a48dbc2cac28d22e98b4fdfcc0fc3199f3b05ab58dc79f84bbd8a42198a76d1286b697099e934f4f7077482312fca3831de7569f87ac7a6cf820ca48ff2e6ce08b562001ea60129d674372d18d46668a67c54bfe6ea7d194a83af697a43467f67a966dbf6e0a90aba73b2023f2b8df82fd7b4033f848254b0a3ee5496b5b1caf9faaf331e7d1fee5ca8b57b16d349eaa1f339e204f08bbb306a38ca2b8e2bd53442c900a51abc97919832ffd39386fbebdb7e69fcf16e9448838d9755e1865407f8356d5a4b4c84705ab270474983a37ed04666eaca411337de0a1baadd8f1b0c263f1ef729d9f9d32c17cb87086c3b7e460eb5247b2e1bccaeef0798bba8acb0f48010af02f664ef72c131a1e3704e2641766feb9e18a472f2b1824c4930b61a69859ea61945791ed30898832dd7a93d2ce7153751e986cf8481338addecd9d546c6f0df24d317a280ed2cc253a9908e9c244e201be195f4273e87953a1bbae0edaefa678818e880d7333255dfd7c9d78aaa0766aa6611557bf249b4b013acb56ca4f188be123c395d4ce09fdbb32b6ab874af715328d62f08b52282b92872d7edd823a3aefd82f5d746bd4ed3d762090ac1372e11663d601ca5670ee29e2782e60ebc7604921e856f6be2edd27512b0407c735f3635212c2a11af56d7435e2f846a7a18fadf5b7ba39c372fd435fa752c905b839b919df6a6e5bb6fdf83cee95336b26169b61737cb930ca19f0a8fd91d2e0c198c8cf2dcf51db4ad56ead1eef6cf33b6a9f476542d962e0d7821c9b1838e9f092892d6e70c37bb6e0767afed50f07a8c50afd33176b68ebda02c98bb1380281eb611a4489a08c10d08eb6ce810584744751a22ce05cfc1844a2f626a173650c6d0935bb1fc801317741231d3da99cfa6464a82df6ed682af4344fba28da7c8b6c767119c47d53510ea47c90b7d21161e6d12a74d5b99b612ad12f4563b5ea2b8803b53864e494a7a63e54d4eb953e0858bb7d86d05db584f41d8c2e35eb55f042e2ecc10c29091cf8fc015ba35edf63a6a31b44f1f0256c9110b5d1ad170639730dce3cf1b3430cd1162a3433bfc461eebfa216e86f30d78e254b52ccc1ab2b0b6932cd5ec44f2be9bef94f54aea27a55278810b7e130704564d69a2f576184782a9cf370f752bf75895d1c1c1726aa4c9059d8205f91d8cd679cab1a4206d08dd4739063d9c134b5f41825b5b2dba647785027cc6137a8f33d5bea3da77dcc636d033076df773645f2b31e9391b3b21fe47493a9c0012bcec4b0139fed4d362f052127cfffbccd000aac390471b8e4f8674a47503957b7a2f8df9352fed12a88e49598b02f0be1983df61112f84d9b0d607fa659a74485fcd841130de48c9bbde8699f20f670fc0e1599edd136784b7e5094cc319629cf6336fccc11d17cfa63b630c3aadd85f6ad039eab7fa7fc3cbed8147a86aad94cf9c4d5598177a82e7f64de29ac770e315c927217bf4f4a853d602fd975d20ca5173314c56d2912c0f4eedd88bf0cb27b18f436ade3321448399b6cd37aeac4458e807b6f945f2d5f77677810b3d04841cd16ae6c6b9dd78524e8ed56ee860159906754d0cd0e3950d41652a8745295616d6454028aa0bb4e13a3b2c7380cddaf5a8efe85786fe93ec688378f03537a28ac6b55a4992781cdcdc27184cf6c3a0c187548fe78fc20af975a401f2222e3c4a13e2b17926fec02f0ae75963509db67582b8aa1c93e707db6bbe9fab9c83f07e011c2bf39a4d5d5cbf568ce5582e48fc4e5d438bf00463e4f7a81fda1b0bf87b444991d315aa5736999553dca730b081c196231909a40d346f09b26f7863cb111aca9b1109113b1e7e1152523ac9182d150a1cbba1e692c5e5bfb8df4de5f6ece55b5c3a8be66de76744f858c1ea02f9c2f307051442f457c31ad087dd3c9f3fecf874c27cc55df3049ab173762caccaecdb08c720fceb1eb4269aa15daf792b9b6ab740cb3bce8ca5beede6d5907af5f3bf4e2cdaa4e562fbd3a01179ac551013050f1533cc96a87282fc11086bc6bd143d3e4beaa0b8dc1e71417a128f26897d057d3602ba785917a60fd97415c43481ae5bf0c431ef4a33a5516adee9e501ff23182b52756397d97fa1d5a7fbf771c525bf4535a6b618b31ded18a470e388f766e9c85040ad3627b181df091cfafe15604b5a57ce032a3fe626e32d8972741248d013d362b70011810544fcb0133bc5029bbedbf86add809a4361c527538789d471c5b136ee063759814e443f5c5dc2eea7c5b1b3781975c4ee74832db9ad4419675ace9c6edf963b03b67f086881fa1d9232ca306f988bb0f78e27d239cd90356aef74c7b4c4f9555772d57be8d0ee1cc7770b6f571bdf5fdb4fbb913a339887175ab836632eade23282ab64960b2d2b3b56e8e99075aedd3f77c76a5e58c73aabeae4d1fb1ce38ef33bceb9c05ea3983d560395f90d238216f8678cabd7eb95feca2d9fdea28c277515466a154234e27cfeeb2cb8a234b462b62d283619ca6529e20227156047ce9e3db9333ff496bd8da5349df760f6dfcb8633bc354f8b2e18452e1f6f2abc4f4ef156a7a17acfa080039f756acdf5465cee12bb7f0dc4b25cb7f5799eb764f6e160821b029467714562573aa1486185e84705baf7b83dc994467ad8bd6003690a3a20843d893019dda3bed18047e88403ebd5d78159693dad432195b2f346e8c31fd5e5ea352caa505d1ae56d2cd6cd4f7e3eaf8e1bbbe3d8d6abbd3af71f4215752b373475872cec8ac4044490b3415be2ec7fe528c859ad0617f32fba67e63da716753b0f92aedb5d28abff5be3cd77fe15778b30b85e47f476ab3dfd7abda3cc800bdea6dcfef83edb018921b30e0c9fb4f607162b4e37cc4b9bf4c29f9b50b24d820fea0b2ae86f2c23dcbcde8b051edca4d913388d3b64feda01ad49fcf8b98ffd2b0c787e323ed6ec14d3742bfa981da2b89bf4c7534504d3941d653c91a3a9c2fddbf4accb311ac89f9c9acfd6c40b1b057e9b6549d929a6475dec989d58c6a5f30b0e0db6806437476dd78a91681c12387a90a380cde8ced7cb5971ac1bd61af730fff06c8d0614ba99b0e594ad81f7647552f460e321fb577404bc66f7a3af28f8c2401e244bb112746c649022c8ea876b93266812c482c42d2b2e35f8c9c5750758f20f1e3c6b274a11cdbe56d720c409813fdd66145a057e48108112bc672a26d355dd98d659aad8d5cb20f646e0e61ed2a9ed14a21148c3378660d57a000a025d7b9fcfd6142d66693f02817837b0660cea04b46d4a6fcf086b9332136fe9a5f1272e5f160a08afcc5a1099c6d9d77e8235057fe851f4f344f3cdb74748f0d53ff60c4badd0e6f2eb3dd600fbb4117cd7d0eb5f616ac59537b33743ed19a4b6b78667fde3a9e77a768ffafaf1429f0b2b439460528644870394f2decef8ee039b1916a7f34ff36998be97ea69872b049c4b33b11f5fa347f557573809d938f5f058cf153e358e78c069fd6fa63d4e82aee921735d9b01438f4014ac2226ba84248003cb68cce336e8f9525dbc9b84e6f3902d074c6f223b476f7f4e75a84e150446698ce7d0be901b7c11f9b216146ce1cc7af97719125376567559bf4a082ec18ca35367b7a786628dee39558edbbdb30e4dcff55b11461278a43066e80e4c44716349861c109ce5dde48de9ea50ba27094b34c8df2b6a7b5c1f1129d237f52970bab3de175526b920e1d4503c9fc880a1c22bd25de344d452a2aa70416312afff7f5ae5ca4720dabdfda30705f1ad4b0e1873a9e6b134ba6e01ed983215abd46062343a2e1700fb652aa6a5bc3673d074c49438aa8b2513ffa9550fa2e23628f321e7bc519abb664dcc5524116aa1976272604a2931ad9c28bdabdafabee50d63f3028d70ac4149685235d67c54e0db77869f5e4794e55d8df01e58b8f3b14f4b1502b9bbb06235719fa9c6e38118395f63a8fe2a56d701b550f626a8d2483fac923ea3fb6be2631c219d34bea6e357365127ff85747d5cedc3353fee4fabd03a40f6dc0221869f2fc4d349b6ac3d10679a3a3c64c9a3821d0319348bc7b0d05d83fb282a87bb078a012e94dfcd3ebce7eaea9c898fbd8c50706b0a3c3e694ea82aacdaa7bf9b581b26befb958d53ce09d0f421dc2ce794309b6485ad5c60c4ade24848b2a07b46af2808cbc55279fdbf6168a763d7bcacde30559408189b7ad106efd1f9e94f115f8bc1ffcf382d4bb33063503954a723f4801bb03fb755570784e2c3d3cb2fa9b1bc4876abdb8d7e6b340d95ab0bdccdacd30262d25ea4061ea78650340e2882ae2cdb8d061059b51d7c7e126cbfabbf7b069d5073d8188d2674da27464146d8c4040675d8f0212cf383373b3395ac2be4f87f8730efad2786ce2d07ebada63090cc661ec2edbe341819a050ab66f2481e2578b077b713bd9289d92ee1c62e3bb6156e52d902c213498eb564735a7d1e50c34cf84994fd7acb0baa83923cbdd9fd400340bb58f50abdf410407c9cd3eb27116220c377353709636f7bcd6406f59333c04a682d9293bcf8af42b6c242c5f9747372b06f8f10bd0e66c35086638be3afa1267d7739e962b0ebf0a6adbc1ae91d4f665aa7c5b78990ec60898fef81d4e3269b30cc21eba4fa30c325a135c069b4bd3c8f583e1ec4241ed631d224666e96c70a02e47e9b1b0653b0814b2e0e10b109a65cf9bc61fbe7705cbe021acdab29842b67abdf6fac95f4986410386ae76ea29438a667fbffbbfd844592612ae3c3f9aa30d7e3fbc96548b440e5fd4d215a6cea1e7bdd046a3fdf99471170c386dad79f54862979779530c0749d92861cf60660835a4f88a4eccd263a3a3f64125c98f0eb7922a30b59e7f66f80018853df62e0fb1cc60d3ff04e57a1ba4cd9593eece51284fd0d3df26e7a3a3963e274bb9a71e0faf6ab4ee54159d1288c113917f44439fa052efe8bf3631c464aeaf698828fc4c39dcd5d199018652dd936f0d5ee8e6aaa6e93d10944acdbfd209a9469452fe8151b073213a27f0e6aa6bc092ef292c98521351458fd44f19ece0f6e0916fde1a836ec33e6fbc8baa964e56e4f97c7c85b9481f684fed7651cfc32e6c84e4658d2831beef02599d583bd1841e0ddffe769339d9fac051eb9d7db08e5b2e5c1e04e1f78abb8ff5de71e56cf5df6408d38ec71e2a11f77cd2d1cd143cd548a30505f039d6a741a8374c285277029aac6d1d4ba233ebe7d83f1d80e76089a735327236a86ede96896bf63a0345c7213116b8ddd463bb6ee43ab42fe665ca1127f62f45217051901b73fbb66f9e1ad217581beb12903b408f2021ee236ebb7d47a66be92673cd98a560341be7ff9dd4bd6b1d32d9a0ac15ee4dac5b4b9852f81f8658ed2ddcfb9550ec0ce1aa7551b622f5a97d7e6cced111da384446aacce2656fc564d06ccefa3bd87afd4f2636a634696a5c243bf859f3e5927beee63f3a8c8bb3d84f740aa99429794548f4704bc111ad95745dce09b84e26bb00960c684bf72c85a65c5b36e0654c11bed13326c7796fba6a5d06fe41acf8f5e4bc96f4dbf68d6949ae55b8c55c90c3f43cedf1d30001884c8386ff5d71247a3c5edb1b825b0b09532022dd8659c8b3f6a813f2028a5bdd85bf70e9b2dbb281ad92a843db182fb71bdc0bb7dbf11b852cfa2cebe94bc953c00f215622505f3c4918b2526e19516bbf713898e9472d0726f93bbc5c799b9901e372b82a874cc1f1f2764d17ec9d17684f5d2fca1ed1bde95a09b566b3f08d2ae345fca4d198a1b3f0e30cd093593d756dd901a93c36723e04f30b89ceaffce5a44189f958ca8df6f67d06322f20077245b9cefda4864f83cb7f81281f3ba97ff01b1b5396214990a35c4c463e164e74c227c619ba6b84c81fae3621b63c8fc6f18093e83a930bd8cf58b3d1db1e3aa2190f4251356bb63e410a21c42a342275f49408308918020b784e7a634e1f0bc4634448c1029ee29b5e21d170297095e4c0289cc9ff333d3fe15a6f0dbe59ddcfceaac7a57783ec77a2ca082f8ceb3395ce37e52710f7ee98e57a5ad3bfef732ead3475f781d255d2e5f834094ca6deaed42a996f61e25dca6c96d3d9ea5a5426fb8a70d09d33c7e8b1cd9e8d2e9449c621f10bccf7a35e740f742b713ca90cde2fb886ded75e72ecd8d544b91aeb5752bcb019a4ff2f355f35afd1d387f5b98a6d82bab638a63e8970292fae5a1a2cf0e5d789d568f254b2be28233b98f075967d1d50f67dfd36876adcd62cab5bfa41adf59413ed434327e34c5e5a7420b3657698bfbae141c03662d7e2dd82e832d578b5ed3e410499a09b7764f4917b8e645b244358d5de322e6267851c1ed7f297a596593a8aa63da89215bf6d39650ba0c62a9137a363c975060dfc7cff689df666516b6c9fed80ed2a941cad192ae859d12bde8136539ef20d5a2c2a68ae4f1374f855cf39d8eaa0e53ff08fdb39f968b5f9c5a468a406126e8e6cd51f432f4a10519c1be318dc1e197cbf6bea4f349345eb2a9fbb81b2c97e71027cbc42d79ba4bcecd54f495deb100437a198a8b742eee846202e5f76b247f66705362b148d8c396e20b3777ac07e68f1617fc497135fbafea4a032717363fdc192ba911c6a35b1a73333a94b4a46c6cce0f7e9d3fe3ff22065cd14b3d953d413a083025a55108589f5963d72c3b5a0563f6427d4d844d9e7c3466dde4806877942d125cf887d94c0cdcb4debcbf29fca11e36031d06eda89d05db80c1818b57c2acc5dadfb29d7adcaa95290e82ea1540a3e55a656b5e9a3d14bb6de3d5cf471c8f2fc949d4032a4b6e1186cf533bf6191c6b7699af3403caa8ada535f1e384f82b7f8ac32e2b39209e99fa49c97a30117d2ce9c2cd137311921f23319b70485f3f2aafb9d9a4b4cbf6f786034d01f502c03721e90f024804f4467c42b9463cd9d7576979ddd5753307800fe4d5094515bd2dc4e1bedd8cc9e4f2024521bc3ea8e8cd33825f983810235e113ca56816919a25d7a982190b3bc8ebb001e512de74d6854a87f1f0f59c1af47fa7bf91bae69520cf0eab396f2b53d8799c2e37b77556612122bb9d479f707ea3df631b54d4a31e4df84786e67327eae98bf8285e2a637ba755cd5fbcd299d72a0378335babc76744625cd5abff02aa1b11fe776671eaf05da93da3b6d73c46407e9455ed84e25f40ad76d5cfbdff5c5e5303a677486624cfb4afa048c3fd46569ce6175b4bd8310b81558b636ef79a70f6faa65e3a9d815daf324eadf64429858449570cf4e3232e4c03c92c66f70124403b3ced96da58d850adcd14f83f72a3b7e4be89ad20ead9bfcb3e22145d3e26bf7d99c249612114aec031308e4e2aec948d92729fe8029291c654ff62a7dc42e59d1a7d4fc2a85eeaca6bc4c8ef0770deaf421402045e985125393721361afcb35e3ab1daba398362d02d03b46a53d9070f5707f2bbe2a61f36a52f2d1ff580d216ab2758a271c6be9795fd17d08ae244bd4d69a5414ab9c215b97ed409680a30e62fd18e0b4623217b4624f7d30a2eca30b39563914dde5e3fd422d8c7260fe0420c0acbdf8cb8e9456d39a23a0ebff38f53cea86a9a2cc7c78262c165b7b6c1a96ae3dcaa797da7aa5a2818c52193965043cf59c11078bd2d49d2b6bdbc63dc902bddf5270addf18f7963ac85bea4c399361bbb2092dc394fbb585ac191dd0f5ed690659f21350fe7f7eb0a55f0d679548c12759809c1594199013ea5d8740eaa66b10d040b1326021b1c26665851c711433ef2512d7152bd9483247d6d545381b28a65397dbcd17d9060d9ce981c0fbc98c1a8587317a782319a0d53ebd76447a14613028eff6015d29786008fe661b4ae49c692a6f66b85396fed9f6ef4993b96e512a039de643e58f36c5a6ecc70c01f90ee6a93374a8bd17f6f832cadbab22ab7f2af46e7da6fb8a67cd026ce5ce1253d699fa267af09875b5c645db9626f1fae9b83a851b85dfbeff008f62efecf741aa889252692ddc01c6bb40cfde7dd987d1b9f2321e885ce9a2dec723d3bcaaaf33c2938196fc8a88f5bede8ae37740a2820ae64e885eb9addafcfc38f9795cb36c9c5075fa8f7b3571aa7a100621cc81130adc08f7bd0b5d2639c20dca636100855d675d2bc62cafd70f9536baeb8c0ac08f5df3985a3634b196f2e1e874fca3752411dd9d108f9af2c2f49969c7abd75f648a13724841b2b32902f3cf8e2aa9b9fc3d14828087b7b93f07a6a453ea2e98b20f481f78587aaa2adb8a85e2bb1ff8ab30cd8e02a4ccd6000665540df2b585eda746a2052d71140ffbd2fa766ca4e698873000aa78843ec18810e22b42be39a569cde6eb82277866050a754371ecfec3345662022237555ba36b1fad4f904804983508509ea6608a9ef5eeebf2c1d4bd9213b1accd02a4600aaa421bd80b2c419cc4ffc70233a6e14a38068d50c3546bbf3c0d9bbb25b1d9cac795a203551b6b67273d9eb5d5c6a3c28ff5f3716a1c310581020d02c14d485d86c26691cdea5d8c32def15addd6e43708239cbe5a35477a412b19814c4e3d2b9d30010ff8dfb51ef617612f0ccfb8efb5276456b04c2ffe5751f562327333c8a8b3fadbf93402010b74f911fc1c49fd318049420e8424ca1963096b39b5d5e3e05ce360c0fbf35eaebf1f28f3e59ba0e717ed80f5d00a6ddd60555e7e9a1eb89f31a3d8ceeadb847f5121a8056df31755f3f60b504b07b6dc217783bf9f425b9e52cef0584d98ad6dad7df0505dbd6dc50894c2f2d0e7fe97a7f2e067c7e0b6694c036b870280d23863e816a7442f2c52841c1312b58681a596f72b9fa6664c1aa8b12dd6092cfc07b3edf3038427e57799c0beac745bb79ecdba032f56db1c5d31c50b0a803348fa9b7226287f9462ffa9ad785a1156b802cc8ad85f50c8e3694918a7680a4ef7e27e85d8b6b1114fdd1266bafa4200edbe42832705a455eb595d859efbb11dbac5897c74c23ca8949a6595804e4d02399ccbebe197d0636958ff72f212b10e656560dcf5e380e43172bd5c8be9d8e1f2377b2cac5544aa47532ff9f0515d56d9ed7ad23d0f5d563d0f671b098f9f26622afc0d3fbd8f8b4185f5b018f638ecc3847229d411b1f0710ed2fce30254c834cf8f2119fc9c469445d21b862197be8dcdc41a064c78837dd672dcc9d63394c98394a14219c38a78a1ca8903e2b4a30efbad06e25521a02aeb2f6676f6c6c6d270edcd7b7d57b71c65a080dd7304ff7cf0315a744c3c57b300bd7263985d1b668ac4376158c21e38e61d339befaaf5f9bef515b68d9e4ce4ff076c5574c21ab7ebddb019e1d5c9c30213e7599c2949e3ab99828a42022ded599ba89d8b32b5c72356bdc36f2666a70b081ab911c887959652c00c54a4771d7c6b9f50f1cf1c644e86bfe94678983657bb0e9604dd17d68eb110e1ea701ad030405c60d7380dda1f77b3bc41ae4f0ab32aafe6bf5be3bebb9e337870f48281df885783e887feeb9f3765fb3e270c72a968b28b78f12ae93cf6cf15f3216cd317d85d3e7b0e50dfb0297de1f4f353709c9edf209ca8e2575df332b91e00db15604787f0e87999606bcbf267c09967b02dd670fa0b78a4ad226904a2691fc388e896f86bbdb00a4f4eada6de4b7b5fbd3050571ba610d8881d3ab658b7f14b377890d94b652ee5cb56a468c47c8cdaa0084d7410ce95d9ee96464e8b5b92f9c9820b64d07cafc6dec23e985e0f20eccac51048b946c2af099bd0e4b01ce9b982fdda60a1d9a6a13d8bff9457348da7aef96b73358645db94048fe5ab942d0bdcbcf0fba5db4f830c28110980fbe133a2f99600e05a480cb2b0f1c06d7e58f8196b1fc02e1d88cfb68263eb48ad6f4199f9b2acd0d05e643aa18ea04a23403b4f70d57c5261eac7edd283bbb8fedf6c65df5f90d920837728d9ff81565103548d13bc0cbda5ad41dd74aef2ab89a1caf1947ac448b1116417dc08922eda9e9e54a3cd594fc91af52b042a7861e82a9bc5153ce52c639280542ebf5cd6be03285594bf322fab5b08b856985ff7ca280d2c02e511b5f86a876a0fb217c261a61537951e8386a743bdc29ef0c39b12105e186539860128165c29f70b4ea4948e10e36f7a1cd7b0ac28342534f6d728161d728e20df270bba77861d29caa651f4529144364d65a328ef408e57d8f81482204458487ace7932fe7e5e0d82400ce5f60beb0b92bdee72bb7cbded9929d18644cf53cce817b7d05508d4c38e1d4cf0a6eeff9bce28ff3c5d96033c6a97efa3050d10b31bc2acea80b37d3e27f1862f23f13ed94715dfef8077b2b278fbd7d89cf194f0488c957ab7ccaa87a981424bd3cf22be6ef757ecfe0bddb38170e11272c0bf35bac65fe8a545a6d80f1b4efe9bb9f043064db0384eb228740e7b47cd38cba08c2a3c0f3afa1b1eaced587eb9e0c08f8547389d6830ccbfaf4536e4f6eb0d2b096ff4d17ac1a6c331c6005c9044bde9745a3ac1c4f4e29275e2fdb40b69fda6662d847f865316788e4edc8243128942426a00f1938d093e6b43085191da9f5745dd2e7b5411e7bd1a2301d210fab046e95be13e09c7204203c16769e0b17877c4ef8813fd029d24ba53b9ceac4ae01825ad7a44711b408ea1a9bbbace7365768ed27550d7f27c73d53164b6777730b26cea4d92d2110b44e20a32a01337df3cc5849ee661597fb2576faa11aa29da82e90ee65feb726e0be4f548db28fa8d4ec57c7431bfa4b22aff0440bfffb2b6b15c8688053530cb99bb99e0c6d1bd9a60db60d4d34f09eee11ba7d1a245ae03de0f1a82c6c55df75839fff7ceaa6567fa5d9916138811e1a734240043072a73815fbd9474682a4e289e2f84c55844db22f581e62a925df1d1ddb956339e8d734f30853466c301561a09da8dbad02b33aecf31b50314ebb2d58920f42262d349d1090c3510a07c8e5b315669134f1401898d73970de81a528d0d00dae5c6847a6472143b103b97fb481a0f16debfa8e1afa00e2a1cb515dcf0dbcc20d4ec7bd67b80d2f7b192e3fa949427f6b10cbfae6d6d3193d6d6a474e827faa699e1835ef7964bdbdde961924aae4b08577fc91a575823cc884f4188384484d286466fff0b867c9bd1d89982f6f7dd09edf10e694479be6a3d9c70000b1fe2c464f5dadaf847b6737edc45b99aa07b92cb87e108ae0299d644348bfe19f023082d66d4fa2589d34b78b0e55d1e0c96fae1377f0fc0a31a2362daff50792d3085711c4a52e8a564e42ed23171f0a0db1fcd1be60f2a873e2516d1207b7f3fc297239982d5c726d77daa71ed37835b3d62647f957de80cb2b8fd56d82022335e398937e51e994e5ac59d1db5eeadf6a18ee267f6a90c3009625a7f586aff4dfb421f80881680f15261270a77aa7047f3b8698c978938f0a7190804f731f237f0a2523b76c5c849134b7d7d05199602cf74006cd2c86c2d5f96e341f8d7ea09a5ebe7ac6724b4f8843e50c6b740201a78142021689bf6f7b0c4bd069518d74b04172657346214fd86fe6fdbdaa02671ac48445c14830c492834654b9c55c9e41c919ef44f92608f91d066d3fe3d67d0bc5f3b9548e176d25a35159dc5e5e7ed3c941dec704901219c0a2b109d430f4ac4f06f415b908024a620ae5e0ebe2b08a4cc07b0fa9d3d9b5c1bdd615fb9fda473452a66914936004000ffcb022ee655fff7fceb2f8181fd973e8f4763cc7717b010273c9aabecbb707e0b89d5b88c4abf7deec03b1003fe3325d43210e9c09417f35fdb9579033d66c1153039d9983eaa6ba7b24966e6e68f71103e0b00310b70e11c6e0ecfc472bc2c8bb9fa2ecaa11890d8bf41953f0a631e5a70cd167ae88c810beb470e9a7d71713971918249343fbf13af637f27a90af5ed001a97535b51eecf7ad168bac7939b5d4581f4b5d2fb77dc154dab1d0f55a86a01b2ef3a6e3b383de1fa1b56957008e7e7c49a1ce055247d1697b39187096fd5ae8ddb15944ff10029ee48c18d078c8c9ee2a60412d37b5900bf6da2beb94ac27ea95d16b8b035bc9465ff6208c5695d32917cedbf6c7eaa99c63b54e375599784faeef71f0da076032b3160d4ffad7aeb6603eb3770d5981ec13808579e4d39c3ca67d22b91407f9d5dbb15a94e19b236a505e6c10783cb432e0dcc774752ed6b376b3088c9a379af21a47ba59f0a822277e8172ce926e26466d1a77564fb0a958040036e6685105f587103693af5b690bda2c62d594c48441bde98a4bd0077e3f6f638fdb0a42776847bef4fb3d4fdca464436d1f61c752960f85c83eaa3f06ae038d254574bfb42e14f8cb7c53883a72cbbde5d1897b4e95604084166aa76668d0b7e445d9e7f3e70e5e6c6206590d3ba04eabb6429cc697b737f040650768b415513f3ca02d879ce111fcf3d8744137fb37ef9046c012ddff10312f71a641e4c6262a3671b3c057ed8242acb07f72d8cb11282f7bdefa35819f4ea80b3a28785456729bfe177be05034464baff2e127bb2e2b2fd4ab20163087089727ba63740dda111b0ff419e702f301e94553dd7c4d28c0a9d9d24de814c9004c8dcd4090cb4eca53d6ad4e6e65f8e8835eee951a1a7f9ff2d0249c2f21279f877f461c9dd17c9cca40769fdeab3500f78e1e03920f4f7b76aa6b9df11e180f69430b0636344ba5bb272a3001143b9984531803fe0843668c8396a7776208080523ab058d8c2e8eb18e67da1db7d25b524cc16a96dbb316140b1ec29b3a9dd9862ad85acbf21e4330f17308edfd003b50ba5f09e2c20c0a6bdda6944cc211d252e7e841e86b732ec0ebc347bbe8d33bc6ac5bdf36dcb0bde58685b4684d9522abbbe11736768b5ab1ef9208333d30d29481940e7a260dda0f23cd3b54aa1583ebfa9e1418bb80ea2d63bf49d1a0ae72b9e9669b40c05738429875677bd4b63412b77ac5febab0ede8096c0fa8ef0e450459e99c4bb2bb20e4f1eb76ee4a658ea42c12699adc2da9b70f10bfcb31a4e96aa4f016d02ea2ba180b506177ce25d46cdb09f44f19a10220933ba9d6e460bd400e7a12e2dae52ba92b818d60cc2824f61ddcf9697d99acc6e3f3f593e828a15e21eaeb150f1bfdcfff95c70a1742b8439e77c9d78ff5bd15336373a5e5c31e50c27795725cff0a33b004f8bffa05c9bba294eb13b640d92a3d70a35419fe82a316a9748adb1126466f43a0735b7457dfed4fad57dee2415d49708d422fba009e64d952b1c87aa31c44e9ad04f5fde74760a16b9a9f878d9efe74a2c3fee4e37c766486fe50508937c8e43c3133e662545194ae61660a022ef25f7a19f71c827e81efa7e99c277e915fdc61633d58ead5605cc07801a9902a1f80d8404a39e20e93dc877695f042240f218f7b6e6f64be9ceba47d1216e0c6a3288d7c1c1fc448eec8863ac6f1fec0e0b092eb1c9a1140680e3221019984a02bc3a454ff940d02d738c261c207a6ae24c5ae41291c0eaef517c0449635e665ccc3497e47761ce0c72edccd28825d4b5b5f9b1656bb3d66f8ddcd9cc2292ca7fc16b35eebf142b2e1bbdb710e8dc6487cdff885b365d12d403b750164d2f267604cb5f9f29db3e8d2018120677b9fcca2f6708e6d23bbf493a41381118d50cedc1f49af5ea4af1cabe1e3fba714df7a72d5f257254f65005a17531ddd0042bcd39e95a2c7ff2303059a398aeebc9d58962fce345bfdb17df26079ee6ec0e8d2605deee3921d0e67d01ca2a9a371d4cf9fbfdb827a28086260f6c90da3a7214f6a3900e305a96b3fa9e4b62966efda711db02181d0bbcbbe541b6513cb09aaad455502bc3fe8b1b893a92b6778f527d0cd990411463bd53c52258b2ce74684e6c0fb06bd228933c0d166aa0a0f7da63084fd9e6c3f862bdce718ad0028e584a30592ac53ba7471af187d5abb5a6be3ec21bde362fb5a6f7b01768b7edd19623a5abcf6f57ee37e572cf3060160e40923bcb80bdbe66664c3daca1969a1f7891d19a9cc38496475550575da926bc253702c9911bd1a008983180024588927cbeb871ff1b770223d22ff60a1f57f24b45198147a0f3c8178443ec7f66276b637abcfecc3a89e74034f89c14ca81620f9dc3fbdac95c79087cf47a276ac936b1f87eb017276ddded0ddc6fe18580431c25b38ed8a9c94e0026a33f2edafd1d30dd5721dd14ab7ff920168f677f0cca8402a97b909bf3055adae75a180679626aa9fbaf1a910bfb05ed5d29ed8528bf48ff0e773a5c24a816927601a6c7740f4e348092df5ef3c47b1fc54b9463ba0b94e757a6bb0f02ca929a6f7670c75bc384655bb1d0e476093d26a87d63aa3fada74ca75e4a68f79a43c0acdbe25910540f4746cb4eeb652a0e111e8abdb19fff37875e01e9403fadaca2b3399bc01d1a21dd00084f96895ff6fb179e5f7fc713098bcb1f786dc395ec8ac00406226d9a896c475beb8495dd06c53b4ff8b5f20b13cc806fbcac5c692c0def626f14011cc938a3aebe6fb381bd448eb01d2c4ef6756a3d66ceaa81060b32b41bd028be77c250e3fca31c27cc2cddce3e3c7486a272627d4c741ccd1d4bc6d8f3109614c637d4bd09584ebd70df7b096c40fc5363d67366de8fa36d07d805cb1471924d55ca75b5e2c647dfbceb1eb4375343687709e5f59fd2a764807106759e0d63d66dbb23b2467589bc10a4e1fa9423346bf4c5bfafd417ae70fbe7a877460d1d851d41565bf33e2591bafed50ab54c1f5527f5eaf802047c19c1ecf294e60cd55856e8d285948c3c6756046bb4410c633fb0f5e4104c0474f50cd54d2b2b199db689928590069a4164d51a4099e8cd25d3c906edc26f5343f2525b2df6e44366159b7f5d51b6b638be00d933661c70a0f401cee52c57f8aa6ecfc72b9efb392d1930842d30912ee82c8ffa167a195de892a5bea9e2a8a0c1f5d3ecb59f17b797f9086ba59dfe48492de28b4d6537d2e3c2a498f4be00b5aaf1c4f5508f4be50534c6f5f6b49bf93fa81e8d329e9cedc8c806865481a437ee4dda2477e34cdff835835ed291ea3163ddfaa86e1fdb4ca9226c1f0ce2b7302ac63fdebf60a9d5b007227e1fcd21da740008df6d7d7011575c0b3c68a588fcb4d25888904fcc4b95c5caa1e1e6edc70a071f4a2d70d956e878ac673ab57ee1ba3f7f9c7ef3fbce92cff6dd5f928e9aec69798619fef63363d58f4df4a5bd5f5f86576c900b5194fa3ebeb55847f17da1a88e1fd93074dbdc22ebe6ab073059662b7d50dd252280ec5d859c33ed971f61f229d3595cc5819d407d8c5c688e3f7e3546ad9653fd3d1018c012922a95de8c7e63ace716e74c57f48f33b946f528d86d774120a239da046044bced31882e84130ff296181ac1d59425417bc9a807c8e237efd7cf067cad10fcb2d37f1348204c9b910263d3605f1120c62e5d3d69999d327f061c01cf2c8db3233ca737173f7e302c46fcb63a4b697e84247e634fb1ebbdde32919c26ecfe97b261fb2da2aa50eaa200854b48ad679c53001405c98aa71057cea7b0ba5fb2059d74b638f798cebf5163f4a88ca9fe12347e0505e9b1bf8bc030635217a1af3d4fa7a1bc36fe81e5179972e31b7400e418da71a45c8e2f699264026d3b6af130ee58d823255115f98cf0edb3468c12c670f8e80836cdc1977906ce3a41919d6958b16f96053eb9c005540c91d71e61b3366e85fe49244edcf68ffa8c76d0c526320ba264e9453ac632aa3f2f2779704880b7cd71420d5e7deb51d9fecc643df270d5d7cd96587ef38d84e172add55c783ce0121c27eb4d40da543ed210d5cbeaf01590ab55489eef2a1be149c2b402eccb88a71efb677abc0735008bf60a6005c036c6338935a6c2f296d03db7a84b775c4239d6e5d5ca701a66dafadf5e34793d95c52ee380de65d494d5adc924765a9e2dca0ec1d60f9915e04ab3e984c6255a64e34b3142de594993ec053bda616f2075a672070a6f55f55a7f2aeedb9f9abe79222a94221eb6113d639557006993f0abc899222fd69f89287642cfaf00c4eefe60224c082a5f858ec5b3fa9c424866230195384bf9266b1820ac94dde83278e34dd62724b6b9fa5051e3541cde1477923848d4055eb813469bdf2ec0d4712ff4d523d0f4eedc847ef05ce9e15cc68842c31eff658e6437998db2a24de47fe3ac42b64c6d2d0c433a729dc2362393ac832b0e8aa5716d41b3139d7cc391e16b890eea4a3708ec95affb0590189a2efefe0e03efc8bf29ac9f228f520478eb5a079ca6c4b0536d03918ad664014a4d0151b5b1274c942f20bebe60ceb41e7b86621ab62de52abc8d7e9563853632d689fdef52baafbf5f01970167ca1554a37137de9e205c881c23f9ca872130b9ea237189fa59de7953db3aada76231ed5b3746bf38f854b1da67a189c2e4d617db9169617c16c89a7bcdcd8dc5d4bd70f09dc7f139fceb6fc87047f74eebf6b013da6024a349ba19b07bd5ea2611c575880f6252ba3e5154f8f1ba320b1f46cd621dd68f87e43fd693f07ed881ecf41615cc1f44b18a4297e7fac0e5b1511bde884d1d2fd63933703fdf800b361ee58fdf3185c8c98b4bb63996ff4bf8b048db60e85016e3d65e2df1a78379a9651dd9ea95d29bcba16964fb05f30d7207113c64a7ca857f467b5062cc992b4dd942576f23d7d1262b58e2ae47b9619e20abc6412410b0bcbf9bf1b8f9e6a81ca557564fa5169fb3b1b5c02cd1ff0e97b6182a09b1f4fe5ca869e32ee6a93eda7b5c8b9857b8133c9c45a5f624878efc0c3962dba8b2f71304a4bdf17ea3d426949658c9a3e0ee16ea6e10d3ceea14e7d6ea7db9675f4185c8f7ff33861ff21f814ec4d1237d742692c1df86c83f88f287f2cd6df2034326e8db99dcbc93d5a6a37327214ed77a327a998fcf5b8147c966b158a90e27b3a9a74e52dd80ba79a563547cb201230a64b52a154939792dc12020095feac1d490795aba46e7de6a8e6ce4c83e0894c6008ad75eef721a181af8ad64a1c649aa63f3fa9762824881c46c300b961cf183932f0dfcf26416d6d0a6f02f73fc0a417b143245ab2dfb7011263d13106b2b0da6d86c0f9cfb950d9deb98ab5a6af942c6939d52f7fc98ac585efb954ba0a454fedadc994c3ee0e41cdc276d6dafa8138180ac5af75ad3611c1e1a24895b838e8ad002d70fd2da4f9ac90da8201afe281e7a42f3de3ef979784ec6c2c1436b33d761164f2a291a620da3360c9641ba9205d84d4bf207c9f579ae480ef070973782753329dbb2951c802b3d7f465ed099879cf0baf3a40a8608d4b9aa0c4058b1cb44960512bd712b849fc0ec158eb65d8656846683a8a603194f16111fe80a9eb7752a59f85ea81f6a7fa3c21a82d9f13c56c0d02f0728dea7319cbba1410a2159caf2493710edbd427b389edb59594ddf1fac97a0a98eef2e93b7dcfebc798f7415109b695de1c8524b5271f1e83d39e453bc232f38781a4dca7c22bcafba05629c09244b12ba69f115282fc16455d6e7c6cdb9587628e324cb7dd64c5e1df1cde071c8a7c2c7115348b8187289a8207977c25b5e90ea0eebfe8d4f1035d324018ff6a2f0ded34db05b6f07ffe98c59d143fe03bbc09ad70ce6d12dc8523002b48e5cf59c385554778dd1ce00109e77277b0eee3262c68e5c41484d510f24a226da4e3cc7c8919f6e59f914f712e5503f0d08598173c3ff5bac2aacba32ab8b44b5e76b2d6b838f72b85cdf2a452226aa2846adde127e4e1300816e99bc42847ad679ef07c33792b2107f8c0e20b8380cd161568712a17d6d5ecaf60ffe97150ffb9f0f45bc07a996a3d887225d6460c4494bb34077b36822cc25258216f224f057a74cd833f2ae798dc7bb36c8b9409e640002a7c7399a270363cbf6b4b4858aa754f220920aab61fc0d8174ca28a19ace9fd3319f474c12db0094cf44359914d63a18c3c589179e1bdaa595980a859ddf5c964affb38460c258aa748b08c8ee8c101a298ea95e400181547a12282b008f3d108859aabf12693c5482730b778e9f299a433202e6b137778e6ce5a7d3674e51ccb46e3cafe0e60b22e32fa901072d4eb7d979d3a8e2ec943881df3aa2ca6e6d18b31dd825a398bfee06a0f78a834565da4b2a9a6982e0deb2dcc6f824c1bdad81e002becb1b844ea0e79564d331ca0190346ddc821c0d26af114fb036b85e2976dbcaf0aea53e6c98811316142a1c060360c1aeaf918a2364d8b510681039aff79e4120595c8e80883a1b6b756a95aad1c045db3c55a33c19caa65d21e83f4269f28f83749574d07011cb58ab39b7d81376ed6ed287445a109bbbd3ee9261af789adf2086c77f032e1353780a82264376f977031d7e1e0196963db340ce3e6df551f908c460bf683fa88c71c9daa793827d0328786633527fcc2e83fdd38aca16de7817ae2a6ebb8b6513eced2048321f4efc72a5e108ee6ac9cf6f4a7673845438d2ffc22d4f896d8b2df199d4fc5673714c95f91dc009f6e6e47d7ef0fb41acdcb91c6678ceb5df31ac5c3171f5fd74cd4d0ab4020606ae7e76a2134bff96726a9eac863aacf6b930452dfd0a7fde1770e263900442dc962cff3fe4e68af5c8a086cd695fdb57d549e2382b3a4e16a38a3fbafb00d95b91bb6bf3120bdf0c927f345863fe885d51b3f56dc38c88b4ab186598cf69ea76c15a65f2cf76c3da506e9f71b3bb380a7de0b3ce2319cdbc249c4f5f0f0dc0bd0993da626259cd02edeb7e56942b0fe3c25f2d51dd89a92d1a492b2a22a42ab7d02bcaaafdc5d9ff2e0949708622f098a4ccab3bb898e2265973cdbcb8ab2ae994faa1bb7403244973d47ed79b71dd89652ffd36bf412510ba728e919b738e2eb8345e791ae4c0e4dc05749ccda928bde9451ccae20d5a49b878bca167eabd36377db2a50862c854812f07354e99c6c24f9140dc2b7c8e8851cd929988d840907662c16703705cf8c78ec2b5e8f25e198e3b253db48b586eca2a1f02e8dc1c8eb4f8896e48f1bc96e0f27732c4f447d22284e7cb978bf967435b40e40f735ed9d714d9a44be6e697400974bb204ba142648b50387b359ac62f13b1bbf86b99a71789c7debaae0caa2b577d38213c2a1e330b044fe0ccc0c918b7b9d93de29bc050b4e4210fe4986d4b00d55063fa247ba582d69c4351f49b231a3cd1798378ca35f979e6e8a6243173b8ec8d1f653e22f39396a124b1612a4e4e8e404292c1d8d1234f4d9ccf5f55272b4e7b362c129a7a3233e1359c4292384b426f9d767d3145eee29698004833df51f12a021b8b71efb171131e0dabe1b4f7b8b5e7b6b6da6bcbfd97901815883c00da9eb9d7c013cde76faba0c8665b0aac993170b6321585070ba13a565aa1e7f8280b208efe3480615b65f605134a7b800d4557a4f4371871ff540e4b6e3fbd676c20075c31da76dd8c76cd1a09875804bb464d25f0a2812d4966ff256d6c434d437f0ef37837eef776019cfdbcb329e95e0150c460f1ec95819d11282be06d38bb39ceb5e0135be7630374264bd29ccabbb584976c3388dad8e6f93d8178e87c4d843b3aedbf38625411b12c2edc2ae026f9decd8dafc99785a31c8cad41c1f9ce5a7ab884b3067c4453d3cdda08cae7191248ded657686cc4332c3970f12cbdf212a342519b03937fa5707508e304d52ef3974254163ae3ead52eafb406010f3f6e4a853bd38093a83e76c3ec799ec22a29676db0d757ac2e82c2741b9aee3d97ebe242d0fa74ae267f5d8292c9d6620e559beb664f23337bb2d4fa4d287bd82ac10c11e4b61ee33b64439874ff4d065d02a795e7700cdb71696f4ef4a9a973d70764606b595c920a3bfc3b00f146df0610206a123fde3f9132d10fdc746ea58b2199e1e0fdc80760371914405eb3a01d23cbdbc160f0be4ca409e5f1a9e70593987d6542285eaa0a1f47c0d321a0f9eb535bab1be281d1294a928cd129141eea1ec06658bfc1343f9edd7dc1190a5478f7b56acaaded8f78ebffeb7495c3e7291057b18d6785ba1cd812b23a56c448a59449debb2e96bfe89606c4126e27af97aee5807643ba21f8e742c8fe6bcc345dc7920c0a7ba875db8afd1ddfdcde5268fa2ba22288c5e461da71510710240320feeba8942d44b1a9ed4cbe1cfde6dbcc47eaaa672a88c5c499c29fe8485dcfad28ff56d790886cfa691a7bc2500f5c77570ae503303688e7f31149d80a3e26c6b3c6051c704cf69b995097d966a660c5dc1510b9e3dd3f1e425f38e11cb3ddc44c64bacd92e2b44b3976d6d8a389a9f1cd0e4045197efc991c815fa1aba76b0552aab47a054469cabb4f13bd576c59640dbf433ab55d8c6165dd8124644b27f899091005240db1f5efd05ed348c48b7bf7ba9e80849b522eb9ea4eed51b3804511667ac1f78719f0c81277d4e1348de22424291ffdfab3e7566c6acb51d95f9a6060ef96968fafb1fd0a7517bfabfed29ca1255038ff15a98b39e208c9c6ee4fe2963e5e054d16cb15f87fea7a3ee0b068341b77fa01ddc53ccdb32b8efcec0ef228c660fc8fd1d357194b46812933c9b15d65a0f9f63514b982604ec42726db72876658bc3b748c7379f75f76cb7b90b49e28da7dded360993d00d03d82ad3df69201a5037a5f2a6f9e73dcad29df5c92c2fb5f5ec742bcec0cd429155344feaf1bbfa8f72b74c2523319aee60db7fbf6b21255ba33441b0ecd102c7d79316049681c259385bcf77d044c37f7050310b63a319213d011e1ae46c02a8aafc05f2ea17a207aa110bbe790cb756a3a947a5f0068e355638f8f85ddca2b863094b3af8ebdaf39613325134cfee4b15e9733b6893512ea0549c7514a02611a9774a3fc4c09b7c33151424c3a2fdb58b6a9ccbd3760fbff1fa4101dc5157d5f48498abb422312325209f263857f0fa2b735a5705b0130b0a311ae4eb5f16ae1e4b4cd1d99c1fb7fca761727427bf74221594c11ffc655f65fb403a38b07fbe4f54935f79b3e36bc91af7931627e888dc0cedcf5eb2233e46add9c66014abc272461c4abfb4403acb144268936e5822159afe35bc386306e50302b552dd179e114559b89173dc858d94fc42620cc50aa1c3aeb45aa4ecbde685fa46224d5fcfb93e3e8fb4f864e742c894fd7f28e0dda2ad755bc3f4f8c4fccbf9df34e60be6609af3b3057fb5c136ea09edcc6b5e8f75b25edb6f1543e1b6157bde0ab6817c711b7b160dab813a7b5645f834c32da602f08e1008efcb74714fae70c69d514195d4a321dd46325e06d37fbd7bff9dc1b588a56b42539f85376da6dc4155b7cab7a4d1f3eba9ad12540e6f8896d3366b5083fe62c587d506e3109d57a7b7ec06c98e210c28aa7edc187af1343996b5b247957a95055e2f5c9690d9c66c8b4d28015c97d645e0822875f89f77a65bd3847910ef678599222345faacc22eca385fa783c531349ba22f425b3669bab1e6a9546e1e9b492960d81cae5a390de571bc64ac510734cf3f03dafc0ff642c610c47bfd02a6b2d607bd0ce057f276fefb1e6d4192d99b5992d94314ebce605be822861c826bd78dddf267302caf624277bb001d6974f7502449a92dd561e57256e70f3417c972c71c573fe91bc2a3bd9d253b0971880fdfafe64a25509fb740e3cf9d8ca486c8b61f136280248257031e4412adde0c5b0bb354943dfa0d9a406895edad5a7e67aeef1873d003b1e1128cd50f00570aa1579ae5e877664f52e03a3ff342d2dfef6f583134d61b31a967ed1d4dfeda5532a538de347c8387b0182f302c4104120140ba9ece4cf3a63ce3556df951ffbae5723701ca824281eccba2ad1c33d8f4bda540e2ef8ad65f22b2e9652ca45867a980688a5476cfc4a941e87533867fab85f25dcac49523f648d6f455b6303701b10eb4df637e9ba43a25a4f0212ec4e502eb8ed5f57527a6cfaa09797cec01fb97cda87f457e44e5a8e6c5a9ac63845e3bf122e18df74f6070e5f6d0b1105a62b8bc9a8b0e74184b0930b7c1a0a17311f5c1e6927c994822513b3b59553d2cc9498c44c8292a1a339aec0a92bf8766045679c28ad67a939f558fd5dec17ab78e1159f68190298cf586731452b26fdac28658ae8fe0e2e4fa4184222e7a4ea09a4b585dd8e84fdbe6ac3eb16e3053a8499c03cb1531295ff94d420e636d8cc01f81cce69d1756b5aa3256f2e1f55236fc73b6e3fe4ae7ddaf9a9037606943b8bf44d21f65d2b57d6c6a1b7f89f358a332ca9545464955b90b9a6423bd4fe80c4afc6a4869fb871deb66bf0b235c8651b32e60c23c65d0ceb9cb36ca7962e2439ac23026c47a0527a9859f4c72727e12ea3dd76ad41d0ee755ba21076af8958f5f806e456ec501553ff84d0a98bec9f8b2df15bf325b4d8e41e5f6ae411f8fd11c331f11636411a04e63f352b7ce2d4943d6535e40862c1e0d6afee9b104f4f2a1f9a39b591224e00ae48552f3f91bab3ac09355f16a05b8d8bf6e194fefe203be6e2f748196d133630e2b1bfc15997abbf0531eca052e2c9e66157b5c478217e02c5a76a935c00fbfbe6feef7a4cf3e2691896db12e232c493e14a783e57135252d10737411705ff83a5f41cd7df36aa3be3133b90fe8137b7ed1f260540f5d1fec596baa9cb87dda026c271197f12dae4e067a74443e525a78839502af8ccc8ddb93d5d7d7623bad6e3c079a0399023473d21f4703064d2d5fd4b622d30c8bf9aabc9b4a366df812d9c58622c8725d2a778a9611ffbde96362b2eae5c5edc86584123719882105ffd8ff1e5b673f9f9b2533f83070c9c8dff7215b30b6fa83ec62c5ce61b3cc358c3721b2ba0c30af92384e43ce2e2b233c8b0394e250318bbd48351f434d538ffce3299175e8258d0c617d6b1e423dd059daeb0f2b82bef9e92b17598e8691065534accbd208b21c021e8b8fba610fd10b7e5975eae0222661d2c751844c3cfad92ce1e3c310cba18216bb9dedef432066d63aebab3e0a2a5bc84df52c6a1039dd4c8e53f294497ac711925f0830f403dc404c0cf056cf5b3bb1d10ae48e10b770e40efae3807764938019bf2591ce6631010d555c8bf0be6bfd3756f16222162862a690dcd928ebc139c5f18e94d845e199aac4d78fb52b29d2f713d13c2c16d0377f31ac88abc16faa89fbfb87211af73a56dd607ccf52f2533065e1209a618f88d4253f41a72c94b88596ad3902834631e54bd50608e143b844f70b0132c4aca8472e8988b6fabe75d598c27ccf6c39c43686b596a661988ec3d6dca13a37c098ad3489ac971b57a6c4ac454dcf3a5613e4650fadb8f15256cedb479bf5d45e0029598a5380cf686cefd3e753c875a6b0e7a374c77fe27237a5534db8a6159331b3d76adc4e5a3e1e714849deeff049d392c4542cdffd86b3355d18f09ce678825fd78de3b70a30763fa9786e731597b754d57b15033baca0c2eb11cd3dfb8e6f68093f15440f87e6c55f6feb6531707c024e179efb0fba29e30852daa0b3e6943abefd1061b86ed6739b1151f051ec36dbe0050c33036a931f1668bbfd2c519746d1621a750e546262adb529827c62be1060b184d7bf0bf1c3c3b8b34f4100c0450943679dae0b69ff7ab3702a2be895db411284ea3de6760009492fb0aba6c07f7ddee54ea177e6a002fd171ebed025dfaa9d83e34db2f1768d24b3350ba11d5354d1e15c0fe091b23f74a9725354c2739ff4f9735073ecbb1537defcf43494f98c8e1ed35c24fc3cf44dde09ca2ebe54e76b51bfd7e2649445104318eacabfb4940db699ab6da479e3acab73bd0296de038d07597a613157078da0b668f4958ddb3a96511bf178d52d28d66b723bc1b9ecf666462871a41b0479b10659beb7d7a5114f7ce7cbc9d9c4c2d81594114dc08e072e125829e9ab58c84aed1a38ff6476ef84e8dd882146b13db8af1153d7d26a1babaa4b58610f7a230f516834282b16479b39e6ca8b6f3a6ad3c1b31dbbdc41cddf597c77d76279a268761cb688e71c72c56a395b2db3551cd3260815ab076116cf0418f8c8992fcf669669d31a6e5aa21eb11bf390685ac779e7acdeed02ef5857a21648892a3aee5f2655fa19473fcacd2bf0e1e9c44957be1a1bfa7961662ee1dcc59e09275ae845b7a5719ae2d2b71c3fa2a1436e7968b4f1cf280c5417b5d8b4042467382cfa7f030c573990db0cd51fdc34129751fa175da84dde2bb9a9f11324a084f06628c2f42c57f1f5f4a3e95997340412e65c6040a21e6f0a1b9eac267aa942f8d02ea622b4d338555303e57c2423243631022e80f4c991f76e2c089a4b9370bcd1a5a41eaf91c940c9d201a5a24097d8dd3ee1b7c5b0e0ad1193b817fb8a3dd772855e835151ef9173a7cd5fdf3aa018f89081d4c9258ec17b71289fc445782cfcc02d7ab0b19ad67b0043a2c65eadd58d80e51d2162b5ecc52093025df985a1adaf5bce115fd7edf75b79c238f5305d768c7d040c1ed1db09834aca292e70a7f70cf98a5ed3867710776296032d5321d7eb0a8acf3aa5d30c495c988c0b5474fa2687925e236b0968efaaab71120bba11f79230495ad69f2d0fc72d885503a99184abaff594c84bb82bcdd5762f8c281a4cce7006dd761e8827ff36460358a2a8b7df0255000a6b8a2dd8a4bf69eee6df65151c8bd16b6d907e56e7e4fbf21a8f96c10e49ef7cd42a73c8251d3738b9ee5aec2bda0b8719429b5f96b07d26b4971d3b033f4edc769337c3d3d32dffba5193dee94cf1690c43f285072414a30daf44f596c7853be8b4ac507c4422df5113a98fd31e2521a1dbe1a5f7a9b769816d49b995a41dc54c8417bb6a325f669d8dda6a157df1e86ce6ac54fb19c7023938114edad3eb73271f1767d06782e64e79e9a3d703933cd16a7bd00a8227a525c98c28542f0dee53e6659455546da45e677c733f4252dbdc6e9285af89a666819eda9fe33931b78471231d30bb1c26f64490cb2c9436171a50f06ef0e3bbf70bbaecaad53745582a9cbe68a259b0474a8359b257c253f903ccda282cfaf9d410fd19a3d4bdfd7743a214c05b4ec5f198b4b67045e0d6a1dccb5becf3181db5e58f724f47ae40895eb914e21dab294a399f397ed803fcd1ef0ee61bbeca66e675d3db4546e803d9691a931f7af1f3c93f812c90e06694c6a7fb7155ef5ee42029fd670b105aaac96870a6a6cae33f98b205095d7c4cfddfbc823f831de4f9f28d4cf65c20b958a5acc3e24ddba626894606d90d1e62e8a7788354383675041ee7d3a73bd78fe679f14da65e01000f8f0af924fc9a0e42ff49c28e813087ec4ccead382cab1981c3a0e4ed9cbad4cd67e04fc4afe6679f673b095dd6f30978582da220094ce4532423224a0d6a09d1fcc3e5e0bed3cbaa9a5e9488037653cb077cf2ecaa15598a81039a6b0dd366b1b5adca8123513469bfe8e02334b0282c52ccfa09907891fdd116baf3f3cbb62f291b553436074beb313df7c1200bfd5e0a1ea83a5ece6cb18a611f738c6ae844c2b003e0dcf21887b31e3f4246042543a6fbf62fb1daf290f7c3d832632705afdf3192510a22c97e4ee19e69770bd9bf61f441323d3ee7c1f5b76b54202f3f49b35d33a40e8d4b07164cf40e06182f01a2f06f473c00650e8e7cc0dd00228f132424976c8dc660e6aafccd4f39c815bb5162b131d4bfd2907679c3e865b396f4ed403b114a9e1864d9377da5b258788ae478d63067d5cdfa13cd33407e7636631be30a67177de764785ad6ef6763c042ba87431ee50689432e20a7d54916260ed16d13247c8d600bda80e32b50959746c4f49529cda89764901336708fe24f92531695f9f1d521a29d16265e331e872e9da5249f553da3d29f8d36d188eacbe9f256048121af6583e6ec70c722a80159b3a7d57a5f1f820cdf30d5285b6f007d82d9421a3d728828d4f01271b91253e3352230ebb1e98b2013ca2711dc43ee453aaad9f015039c4b4847b500a6249ab008513125b473ca2f83c62be004ed471035b6c867bd5866b7459ea76bfd0d83b20ec7b842f9683ff1196c6e182e6b7007ee8fb57c939e199160370a62cf21394cdc03b6d8354d07f551425fb537a13db97c6b83769d3a2a2fbb812fc7b3247ba2b2b181191880c15be7d9cab1a2ff10a5543ae78081cba69c5731410d5c33abb28d10957c3d15bf2c05f9f6cf212cbfb9e4c387e3f1dcc6af999dacd62d02b9dc059a4f13855941887f4c3353affbac736aa9a019d0b62498479083df0196a702f0e6535a4dcc48039a4b32d1df195491978eccb66a9595e2fe8696ef97144237ff0fad1d1eadecdce476d7022a7fc197a5d974a8831ea75818e80e36e4593d967c30c2839e9ebc940c94e25d29589ae8dc94e173e94368789488b1cc1730a65d1867bfa3174c75b20a000240c9c745a1d0f11b57013faa8a27df88276b6d6e0e7d5fd0b8aaf2dee6447147efa84a9bc9cf0658c5758c729779708e74d38e8cf97e741510439c570a796b4cf9e95823d68f0af12d31eacd5f146f9d0b96ebe5d275539ab95bbd2fb70ab6f8eb60dd2ba662d1e4637e63024f71975a88cd56331f35bf97145f3efcda30e5615c970ca3fe73293cf906748e51c8af9161086d938f7fa54bc6952104bbd70456f13285beeea1019485073656adb35ffa83901f1731ae342772292877844e794c8ed870dbbb1ba0ae26f482df0324d493d545ad409d85afd91b496d5c8d345b6fd6e6e59781160ef8de69635f5cda59722026ca303c6d52eabecafa0665121858021e7911ed35a4fb725590c7d310c3eb4224ffb449655e8f9d3569f135fbf5df631b6bbea3e3b6a3911fd42ac796a15bc1c232b078af91d629016efcc09085b34ba4d6ddb22eb473f1b9a179ec9124ae8a0950b2959c54627d6aba33f3516f151d0b074f2d87585aa9b91f1a60bfb1b790349eb116d57423bbc4943eb10b63c4a606507a16fa526f248318558822c82aa989f4cb153db114a56917e3df9c4b761cd07b3bcd7e2c1075bde55c6f88780babbf5597003bcb4f15562ff06fbd1634ce30ff1529406831147a6d30df828d9ef82278f6fd31b358b9951d7af4f46c809f21ccd8990da6275554de735f9434ac720661c22ee94480a3f7d6b0b153f7c16702b66ee610ac21d2a76fe288664d6fb95963fbae2804e52f87266161a3fc52958c69aaee438684926296dff3cd9f76203002d16840ba63ae65dd0ee5d0c12d46afa063bfb67d80bd8f65836db7f646c9463f2957ddad2728d889f9a6844bd992b3152c00547b2606e5645448b49299613a7ab305ad44b39325fe6676eb5a930560793aeacb4dc5668f1900c9ed420f98ad13aca54fa849e2384b1ae2b3733f5265671776369421305d5181c777e9f64d2c1ec6d9766e1ac8e953b4006d005deeefb00fd393a456a7e9bc8c03608441cc7131af77bb04ca200e07112a86cfa9b405d3486cb6cc6de63bd486a6c400a11c265aefd563f5dc6fd6a6189d02d8d8f36139e4c99df77d7f4de7db932de9eac93aa203aca0aadf1c9a32eca3e7b94fac547bdb8e6a22edaea24af5c67b24ade30c8664811f6424a0cb4947228a0f4366b4096b6ba7a55803abf41b726e9603aa28750d740df65a6278c74ac3170616a43a14ec76526f059e1a865987c87c75347e1637693f89cc436d3103a3449cdba6e16a150eb531004f3de7372c9a0f72289b1fc2e2b2d3d0d5be26be11be166a7a6dd0c452f929f650dad881dad8f6ba055d86a697668705fe05c2680c107fabedd267a2be6a4a37e6584e13053613e1917e0e9d19b5be00795c496a291f44ecaabcda6d1c87bfda3d32e4fa8ea92b4bbb3f35724c13331cff1f404a2a83bfdda084aea6e672c7e247cf4453dd3994f5720830789fe56e74c10f4a4716096b68e326b24e498c1e973f00c8a252ec4e69fe3be31fa10884fd044982e94030d23dac6fc8d8da8f1ac46a3a56057dc7b465b92da3e01509e293a2071e451627d35ca9f18ff6d9846d59feba48eea03b591019c54d2f0d06dece58f4030cad70ab917f9a24e5b3559c11d79ef3485a9a9ff45a32cd6b2b0ca2bf1e6a481ba65aa06748b25e839e64734c34bb5e7d274f0017087cd73dc4c88b31d0353ac227873a84e32f5a6e08200aedf499d1fa3496340eead24c06fc1327e85a2c9d4fabfb4a8db251fb3f5483bbd9213b556b6b2c72b4fcd8301e859a57ca5c48825e63684ebecdbc5cf92f5137a14c45068edf56fb471ec06f8b9023e1cdc72f2870a8fba048993e3c4ca89e13fde0eae7bf8a88386c0c7a5d44da179a6829c22402d9090d92082b1fdd404345d4363518a07f5e7c88bcbc85f35bb2fc5ad55b79c17f9dc6379dc5e70e97ca1e0ef202bbfc372eceaaeb81447d762c0c80cc45160d4db4b5f0560edb48230adac0cb97358928631632ade514a9f9043308a121f9e81e91791e9c1ba59d55885c3e62fc0128bcf480dbd5b33f09b9dece6e0d86ceb7260745ad706091b2e76c874ad2d44c8e7f3c8bbd902fb7ce0a8746f07e3ca11f18368bf57d826296a8b4460169b13962d289443802410f6bed636607e39a972f52131b153ea0073a467c1023abe45e18e8d57dfca98a8c9bea2cfaa6f92f40c1014b3b03158c0e6c5a4ff9a66ab62ff0198f45b644c354c5ddc2848c6d0b90e8a0907c067df23d571a6d924acd3ff7b0e380fc202a1e395840c2f76e1d55d04c13c223de2a934acb786c58822717b78951f292124be7693486f55a91aeb80682da478c2d690a82af3351024769a099b789783f5527ec4bbdf8f604eca7b48d16948a5f51915c030f3e1d03de2441013dbb632c10064e39ac7c833e7abc1516f4fbf62e11e56a7ecaa4126b52f8cc6e305702e70284f4305b3eb96734d5a95ec88a882ee47c2872b7e71bae3f4414b19dd3b1d9e0c10a1443e0d27f26c4718b2d6975e6ae18d97d1c8f3dc8d9500ac74eaf7b70b1a712eaf072729ff385aab5fc4bd8ebc037b3248e9677596dcf5064c7c4d864e4797fab4098a38ecf9b67a3ab25ad00a470f5b2ad21da6b9e67b61a6cac5a2a854c1a2674326f40a4f071ee758cdc9fa76fb146b1678b9cae319e4afd150db220762bacbe97231b446191011f14cef3760da9dedf2764f59357b3892e4321a90ac9f0277ab16f5e47fb98071eb4aada0838c32c8f8ec5b773a24d1ad2966c57330f5ad3698a16789fd9b624720e935fd168babac73c824797c02f0e90e6eec787c3f9da68b080bf5c9e1e622d549faa59cb10a16de7dd1264b6f37ce7d6443d8daab095f70205f8b65420df8299a769537645150a560f94ebde0ae763012648e2fe9106d2ecf7b6728571f6eb002a8e05f5105ad3d73e0ba26750e12f82449ed2fa200ffe659d02efe1d93c86b374c71b6678579685653297ec55fc33f9cf96090c1144cfa7fdbd6a30011d95a4bba153b893455bfa3efae6b3a10ef7ac7412efd6099b41333dd5978698276a2e50cc0d7436d6cbf29d9b03afa8132e5e3831e4480df71b18b1c5ca19f731295031c8676b3d7c2e30ec16670fc40aa576b54ab36459dab225c000302a981ca5de4bf8037abc49a4817086cc0a5b4990139600659b6f5b705dfa17e927813200419ab737616db033e4ebbbcf644994c43069935e55f5fe9bb0f46ca25ce537204779ed28c6ea5818bda17ab1d5e69bdf2164b1f76a0b462fd04f3b3fce1b7137c0df6e2241d12c1d7a13b4fd7933321c4fbf20766fae0dfc23e18d7a6111e901a58009210a2c363a5314040bfa811903d09ec03fd5b225fe802107114cf4325577c5fc6f6fe684df833c3ae83f26f18818bc184e56846fff8738b4f4be48cf281360f0c3f72ab26108acf62aa9624add816a62c00c9c7401f3740a6f604d88831c1dda817b5cb573061f0874e63113f610d7bbce32e83dd7863201f6364b4e9d0c2a1ea0b4ae3369c876fe3235cd3f88386c2cc0d977e187135a453c2e925f46430fb28dbb333c6306eb82aea32614ae4a30926034b0a2bdfededfca5d7ff82bbbb2e5cbe0f1a992807cbaf6ca2360763b4f8f959c87836bf77b165b07838976b04efe7081c1dfd9684e1eccb36f7f28c7f9fcc35812f3ad0506af25938cfc7c553340e68032cbd8781209bfe9392cc13816ecb9ae585a7b8dc08ac611b1ca9083ae51a481620458710451777d46496eece266db9deb41bd885d0f059669fa163b7432a42889d8ba4cb823650d556be1a7d1c6b76bcb58898bbede5667b43f3ed01e44fabe92c1364d15439261c64a1be7649995ff1165e1c5258ecfe2dee49eeace9899dfd77606c4653a5ef3f12362d58b32b675650ddd08e8802c8899225f44e8b5bf34f5f4923362d168c2e9c75fa96c48cc308c4d17fb17933ca4dc1f8abc8b73c7b2cfee3f05c868df1d8579e5e7925b165c261cabb82e1e8da6f7db2ad28348fe149f85036b270b28760f182018b724fc3a5daa7b26db5a0f88a23f6119ec8782c8a5894aa28e04b9d9443958c9d6f5935f812d032a80f48abb07301098bb828171306ae141d6bd3e34596fa7ad170a6d27a7e2a87d83878e214c68ef05d4ca32c91596a13eaa9bc775e54605125542839b09439bf5e77a59e1d42ad2c9b62d31400599d64745abd1f3ef0a597761b609beeea4c8f46a847bf0c88c8973e7f3a94301bd65eec5135ae41b68ebd38fee3f9d0cf55cbabfcd3a362815ba779c1bdb5ee2e380691505f971d1b53ca7855f2b12427cc90eb53e753374e4ac872164a79fdaa7b18125cc82950e59fee4c6508ba7b886e3a02d88adebf5a5c5c460d8dd17450f340fd329718f302b7b6912bebc246e93a3ac4a165517577d90e02567babc5d4b09cf27533a8e0a09ec0617befece6af2bc52a1b06dd1e236c48714ddc5dc85f0ef26c4a32ae4dc251a38aa79fca375f25dbc751a2381950c3dba318c98168b4f5baa0dbe32877a1b645891e4ecd9a6adead696d1d1d857969c27606fd0772a7f11f4e1caeba27cd12c58d44d39357de3fcbab1fa997cb66d5a529a4c3be05cf1359b95242ee4c78e883afd0c7798d127099872ed6ee68737536a78ea852a5cfe647f9fc6af74414d5cc8726834e28ab3f8e11d8cbd9c573394c46414d3ac31cda2c6a3ebe778513d100dce8f0f9c1310dff7042e8022f00ed55a5f748b42da64c74391b6d90230f657395d7a0650611a1783f323876e2a2f52af4e800f31e44f5c20ac90a4478764a2f885b3d41e28a87ae5b914065a4659a523f103df4e2f6dc9fd117b32d98f7ff744b739cd0aa07916229b57dccbaae9d19f3de6c234b49969dad7fb9650bb0f0e3eb11d9288e866e7f80e82d7eb413eedf1e4415d633514dcb2863f532b946e296eb1f7a10cbb22c78585c1ee44d6935977b97480505247489671890d325b5fda22bf9eeef5a7ac553b6bbf49d4e7a2a07557ebc10174aab1da1acd59465ed4c81130cd2fd4ff7582ecbc7f683da5de7636aa2a274ec57f19d51f1b6ea45a86397a25b88629ad6858979ecaa0c5e07daf918865cccc1f5fa003e3e96bf64c673c3e9d116e94932474b91eaaf731fd777017940f8b8e3871a1fe254a2f77f925fbcba9b11d75b5cc8c2566189a24e70c1b69047b9b052bbf285068c196893977f1d3e5ee56fa3b934bf3f980ce42aaee9a2df7b721ca89c82f25977ababccfeae7d7d871c09ef59b813d4ef02775cfc9dc8121dca8964be1176c4bee63c264e531a55c9a121db581c77697ada0d00247e063c52fbc8d297ce37167463dcae5fc77849819e053232d59bfba73002db04b5486f4e2e6a838aa1af994bce34a35e80c0455de521d6fa8724b69c79af96e52cb27ef999e0a88499c13dd13f0a24ad45cf8d1e08bfebc5ab33772a7818899a49e5d1233f2f7fa56888d416c8141836b19c5991514bc07b65fc5951cd90fbc9feb7729f15e23c523792528d745fbf0e911539b2f069f3ca22d2bc24d92054ff86d8c8f05a1ee8b6fd407710d32212a9fc01f3f2458d41595d7093cec24fe73593e853579e0d193cab918cd36b461212268b3867145510609b0e16dd18aee234187a3256bb9b30557b25276dc3e6ac24779c80067314051156c124679687f9a870b320bb81cbbf1bcf72e99c97042534b7bb978f7dc548e1335dbd2d3e56c4a48d940e9fd3d435f6c788c79939ec7442f8e1da77e4a2f095964b8b7f0ec386b65148993597573e8825796875235a4d29539fbdd236a06551ca7c08606a12e54ecc4f0b7c320e6a92a9d03caac967bec952d49e6e351a923724b2fb2f2b89226c0ed1a1ab41a3587db673ce49dc30cfb26c4cac4c8d265158097bd211e996441ca17ec49cfab2ea8eb88470754366118d70b4e99d13337051fbfa3113da4d293604ac1d23a2d4c48e8d46e29838f3726c60fbaceb23e06a7812a1213ff14512061c178432a0668d4fe2cb6c4037a5e359994b783ab310b10fcd467789831957c80e80090f87b2f333d03f851e73712c831ef8e3d3cd0c581db8b50d046d70ba2c42ca40d9b240b050e1ac55467182725e37ad1674b8ec8065727d6973eaf67d4807f561232571cfcc1fa98b192fdace53bd3f4ab3d85179532072151387e6e93f9b6a98fc063df3a47bf2d28dd3ad7e996ba4c42ab7ba76960be2e9b5f13a008cf0cc7189f16d979a7d4c6ecba52c788a51600410838eb686bad537cb92a93a247853f305e09e94530304fdb05cf750ef8991cbdb6eecddec41960fb8dceb76029cdd4884b4500e2874d4b6ddb7b6ec1048ed740990c7a0f97e3ed0d766829f71ea494cab468e8f6be722d3c51e7f6a5cf794ca760c4a45105f873cbdce147125e73e42995710cf16c1b065b2688d952c95eccd83fde3cd10e6c8aa284d7e4f427ddb03019008aa6e596c6af698b9fa039afd3874159f3fd2269d44bd99dd80df9ece81cbc968474e495a037f3e4c04d847b396dfc243f6457d197b89152d18768d798452e1ef7b975192c9e654be64393ff36bc2b614c6fe413674cf97801aa812a51af2b63d1c87046f70f3e2045d27e23ddc2fc40b038c1f33e98794a12c10fe3d8d21a838e423a4dc3151a92b17b29cedfbf84e2a4844c6ee0459ad2f97bca9db4af48c87c74f85866cf51de27f63d846d656393a8dc64a191ef28b94edc92488c1e94480c34d7587e3df0321a81f64392d0c1d8a8b003c3fdb4cf814ed914540f1d870760b7904e0822e5af9141b2e407e365700d269edb84a38f6b771aaf72d20b13d6e6ed43bb60c6c5bffa0d7bcf0e95be293d996d1cd50543c9daa7a97d5217f3e9c5967092c0d83d48d3f181da69fda617588035ef0d270528d0b070591e4eae2b43416968a872a6f8c8c097fff98cf1a1a24ca5c721712fca86456c3b0b996a94b274f5dfc5a91d876eb91853fbc106142c556e9397209006da1fb783ca0552a30ccb62591683557c9bfd29295dbabb53d61ceb56396a6f6878874500af27ab9b921a2cd4d83cb6b6aa69eff79011aa0f30002a5007ded6c6ce7ebb3e81cd606b7a2b5d4dfead7abfbf72f009ef7dde97caa2328aa416c05a53a524176a84f0756deebabc20483f0f403e993a8fdde13915153f528c6fd0a3c333cace31cc46cc456271347fa4a8af16c691d62926a7f7fe5782c8a0ab71817e61740ead2e0f2bdcbfc40bd957da83853b655173320440926426be5c54d08af99e7adc068eb0a09a85db41e98284bd0cd49b0151da5f160bebd570305c92fb9ae3729496b4b35159c29f81fcd29f5fd44918f2a446c1b27fa02992fe45052268138d2ebc8fd49d753ff268ca2cd26024ec40b7c1e199596a8ec1c563fc90af2f4fce4c40205e49ab0924f6a007ec5f35e5b2bac075fef6357537c85d0cd66a84b0a40afcdcae64e7c543a31caaa7241b5f4f545803904ce9d63058a147a58efbd0b0341a3fab4f44768e05a170dfe193696696cc5656138020a089047a20daca0d1617d39d6ceb6a7317135a3b1c2db8200003fd632ecda80f0fa2a206561f7a1b35a710f8fdd497d8b1e53d58878ee77b20dc9e7f14b6b6784fb2cf6bfbbc9bd60da603b20d0ad53c69b4f8c0ef9f7e229a7eaa5536ef8c4d1441feffd1357853f998b595fc8708e89878f612e6fdf5eba5858753774df20531fd9bf5b09d1d3b1d9652c9044178cee023d90ffe793c995f7b7200894027850c3ecb13585b70679d9995fff5570e5d3123c1164a10132c6c23824f236c9be1304537f154bad6b7dbe681dac5aa553bc0cbf18a57386dab48963b950d389ee200af4016978c34a2f08baa7a881d9008ab23f003c5333406f38c7723e475f80ca03c9611407c8439b88213f185245e18fcabde4a4bd1602949f29f97deca0150024e94ac580171e6e4075358d8abde14da54e666b67579d18fc8febe81060a7d14ce34bceb6d5983ed316d68fd85634260370381d89b7c51fb886352da3d8f7ff2a69b7f6441ca3a4d9c19ac48cf20e20c8e2cb090a01e41dd8d4cda67586dc899d06538ad1341b8542803a8dc0c31042505a0cf8c9521a88e4da7e0fd895a097bf9065d55d3456894ace18f9bb5c5b6f309466e4a7532b8800ae2a108f4e978d0e143390b37d003b0ae9bbc28ec574961fee1a532b164cc6c4e010b31d02999edca369e0b1a6cbfd474f8981d12ef24d0817d0a481e31c5b57e59b2e16f64ba066ee23dc483f665bceee79cf733598ddb5f93e8a91c04af5149bd800366bfd188d51561deba276940b99e7118841e4c1495f786322f6a6a0504a25230c1616a2e15424c519e3462965d3b44137e7be62be91f48319c38e4d3be8b1509bed4de4d305e5b128072e009e0b93239dcc60c4f4641f9412eeefba919aa606beda0ebba87ec0f48ba8a2542e07844e5983914aa30eff9fb22db7f179f8d7bd21bb27215ba06b7959fa399e7262dd4b12d199d9b05529e96f9d869b839170448fc610ff101c22435e9166b7052cc9bf71304b941c08c456163343a563cc61a5b29260851eb35cff9b3a553a8148473f41e9ef0aa8d8c8f12ec640ba5226a4fe4af22820f0e73e24d72ce8937e5b5ab84545f27bc6d77b341ee72f2a9e16157d324b325e5bc07c41326c92bfd6ebbc99a39f4fbf183b13e767f323595f3be8f66aa607d054340e976690e595afedc65001aceffa39d2bb70f050cec7d9c69462d20ffb46257ac3c010e14e7f6a4b2165c8e2f7e7e4b400193761befdc23bd590bfb1325bceca0ebcf470d411f35dee1d75b79c389c0fe8825319dd89b7538365d3e69a60455f913c1fa2e36270f95f6b808b4093fa93437b194eb858b1097d58567781db4008b7d338fe74d11bae9e99558df0f66399a7dca05101a4f30513ec4156990b36976c8f81b6666f7d756512dcdba75eb9e8614a7ddcf89cf9d43d259276024000f2e9e6f9bbdaa6fb211d542843e85d11b24fe9f26dfe01c3b2499e93fc989f4d939ff600811ebd649998f8ece55ceff7a4263107d403ff30e712aecde667518eef556d205542adab03d5755afc89ff2f5df9a6561374d8ce24703bbf7d2541b3c22ce486d7e16da1ec04082023c08b87f833fd93cc7ed8692ef762c29f026e5cd14aa2a98c5aad3a3cd249709dc02f70f5d0dab7592fd571886dd06383e789b0e8897cf0c51955c2a14913cf454429b35ba91491d3aad789a436e31229fc3e925746151e61ec304adaf274ca4cce803987043d5feb78cb81c4c86dbb928029de1fcc4f646014f44db744e63bf20d3cf14eef0e30a4c76ff699571335d410ba5ae011fc1caea7bebb8738e9c3cd0ce15f626914596b58d7ce2d4c6c57ef3d4e1229ea2a716096b9f5c0590883d91a813aaf21b6d856607ff0e79a6015a300ad57a6dfd0e3208935def3687536d8834d17ae5d56812ecc406ef0c83601f067b23aec1598af52cb8d040eae7eaa04fefeecbfb35cff6a23b989674b2f33fa5731b39b33408ea68064ef9cd5237a5ebbe660598881ee8c005cb70c7da510a0eb9f2b679289dece54a810c783b62b919351dfc60c4bb9fc19ff9fd1808078cd2e4f4432307db1d0edfc06b3cf98417f31ab6f80abb589afadbe8f875e985bf3d082a8ddaa5a1e51ce502bdc048b736363bc44df86c0459dd61ccf51f9906c4ea58005933f79a312b9ab1e24d5104883718ffab8701d443c97e94a6ad7c0b0f0e2015dde693fea204eb5345b65471e075f4fb49eb79c0ba1e159adaa6a187c59fa238c2937baec3ce4762d475e28737d340b5a9bcf21174979c065859a606e21388787fd07ef88b6d667e0b7b92ba8fb08a5c5d5d01c7b735146e6ab578456ccdce72e25f8d70c002da721045019821a37958f273cae0dc159ad00ce39dbf7b313080b9585e50eca5c472dd30046813d11d8f77edcdce46c4e05cf711258f75fa0f5b84c68c60cf6f0270e5ec9f9e19f37726b2fdc73d3e857fe77a4a012b885f2458d743452897f65b00164db0450ef96d95b9be97548fe8a6f77dc6129dc23f83a425b3fd328beae6b19dc3b5a44fce4529a86bebf4615321d45238db5b4363746223f34e94a43a50190d5ac73a8ecae480bf3df91c6062fb3a709cc494a81cddbd8c0966b2de69822d55b0830293d17dbd0baeda92a7746d1502c9451378fa769765ec59c9968e635488673412600eed784de249efdb39cc9fbefda0f860374aaf1a745662a183d5bf84c865ac9b3f210e8343ef8e82e461104b203dae6e40a096b87190895210299412d4e668787aeb153d266bb31f4a82f82c54d05a2d3814d8ba002a84b238d24ac5c98d7124f9d25c3abd51ecf46b6346bb555812a18a86f4abefe3565ba9de6e7e1e14e24f245041baa744affa37ba6660cf7eb8886dc95b0eb982e0f6513db5988a9c8ae01fc94b94d7031e00be0c438b2b2596f9c535c263bc41a1e34e4914f6e57bfc55ca2a7f7ad397772e57f6462bfc7970f9061dbf4c42fe2b71bbe737e31514e439e6af5f4fc1cff862773cb7de90b830a0821d777c0dc038288b2bd9997979889f04f911cdefb729b6fe3119a4f59c93d7c507480d6b8c84205370fac89a261e83616a933ff0c6897abdb287445560ce49d343a7d21ec9b3b350d6171223f7f032db047ccb343823506c409003b8b1536301221b12e1e71a902b92c24ba03ff65d1bee6c8eadcf72e13791d4ea56914b32afbea036692f284192fffb1a297f85deffb1750464200de0cceb4fa42885953700af169c7b82f911d23b21d8570933ad9235260d59f166d4047ffd0afec326ccb797ee7967b872ea635968815a4e3949a57413ac2d23457a537888a566682fb1592a1ac296f641797d691f93d7cd6d278a629210fc694a3b4c5d7d1404f0fa73666ccc03092d618d0212cc79aae6cb0fb6fd4134b4d5ce56724596fc93f721529f232d1d7da045b7990074cf013ae0236cb5e3180b0494aab581e5e12aa0aa59de81564f59fc00b8128d0104c307478dfb474447f079edceff4ac025471f327e01051ba615dc1f04cb7e670e37d72de81199ea73fe09e7585dc51cfe5fc7f8f75733a9c77e0c5d3ecccbd922e7a834cfc07e8a1f3c1e5b58b9266ff7bf7d7080feda27ddb3a47920d1236e20b6b09325621935081370dc732300f660564a6c314ca4bf207de29bbd49d3630436a06ea01e49d089ccb28fb24bed762df33b7bfb0ffa256257613a41a1668c62d98685fca8f72356af9d75a445bdb329cb6095a94b232aa6d0d602268e49be08a4bbf874fc3925b6ed02f8bf7c2b01f6a9a27461746acffbcff04d44f3e7b0d273ca7796721d9e7a2452df96450ecc632d7798d5b757e50c7c62d699a3489d18c6881650263f689962e65662b558d8b7ebc43be9c29f33b1f92504b511af65dab13f2d26e80113652c57ad28262a5380e9bd3361db5db183bb9f3a2267d08555148309daaf53919fae2f46d92f89df342ac94d05a1989ebe17401f710cfff2d50af247f816f241e7f5a5e65e686950b0b7d1304cb47c2b9d9318cbfcf869ec96398be7bf08400796f3f17d842ad18b8994a2fbf1b4b87ec7471dfb69ab5396d16829f8c32e1dfda7a62a214d57d842a5904e9a15822191404beee0fa98f0845c8c8e1b1c382b85c4f5ff27899ddd29e920b1ec5c9994c3aa985bd2c1ed2722dcc0ee53f8b642de32b5645c5b4dc6ffc1d53e3277affb007814c42ee73c1eafb64b702673fcdcb8a584ff3efd93031dd3a0ed87abb7389740e4808a841580bbd9a923f4db303e87276b5455fc1f8a8a6715591d1aa520ba6c3dc6ca9b95040fef01f250ed0f97a0d7758088b0f7f698912c874220390400cb1cb238788f954b867d8cf7e1f359db100f6f8433506a7ea37d61e92b4f89122152a449d9fbc5f75627ec829d7923b139ef2d0c76f483690dd7cf4dfc35c0ae25efc53317e18eb4d6f3dc32992e88cae672c1c2b1c02b4bd686b47d83c906a7d0bef506d04af3f5a5f9a4b1df5c46afb56772295c657cb29362172ae686aa6c68263ee9c5d0aac2a21efdafa2ca69fb3dde2607a670d7da90b5b1a27e7f02d653e64eb0eb71cd7173cb910d80cb3cc333ccd05248f482b63cb281f6802d8fcb9b9c68c73e03fb85061dcc6f7bc712c2cb9e32cff756312c1f15c290995108de49385d2969d512e24e962456106ea627158e1bc5b8a0f36c07d4f6e2ecff876b5e3b056c2ccb57c8e03be856aaa4e61f7f8bd1bd8a7a073e6742f26f273c5b1861eb3f6788b2598a53fa4c7b1dec61355a1c6c53bf507d25679c9a6face62542f5ebce638b4bcf99c93d187c5448dc8702612741105f434416f25eeaeef4b21d5b79289c404fd0a70669956c082184b3e49068dcf4495f54853a8ab03daad9e1a0602f8523eb5f65020edd9b487891aaaa699447c5460730d31d90fa627b6d27b4060c9fb8d8c18e5d21d4d05c832898459e276b74a66f1ac78bcb044f6d1e19fac84805a61c426a28f53645bb106e5d3ebbc886450fa201e2f3b991961de7aed26f8e4982923fb62496004e29ddbdd4434456517e981d776106f0e60024ab7d15a2c4a9ea9ce26312a53c3d7b0302074aad8c67413f01404d21bc6d7d69167b2908bd5741c5cf8add39eb9b18400f2525eaff4018781e82d11a6bb0387fffcdd73a59b4ef40fe04997b3d4809570f0ebb76423d18eb3ba607fdc094268eb959f47a50f9aabb0e6db197009ce0d7623d62967efaccccbeba71b71fcba60eba34667190c2a18058f50535e8f018a24c70d3fbc7bfef078a74e7f7cc4c2e1161abe5ff09db48adc42c74d70639922d6b5c2e99fb642ccfdf6e58b18dce80758e6f0015968707b5732eb8e7b9316b958019ed825be59753d3897516fcc32f96c0ad1a4048e1ca48be9852eaa617a03eb186d80eae773183c823c76d0ff0833a36fddcf142c58b925448341bc9e2d111dba73522cc2c34c9be6691222fbc7c5fc52491656898218c7d56451837e27a082e4d833e97e9e8082cda470f8f2d2191222ad561845a50d3bc7b1594e972aa7789e0617abb9ad36d1181faeb6366dec5bdbd4038c63960132a4656797721dd43a01d30ff9b9f95645936e86f2853f19397212cef49177d13e9de2c3cced1b21a919b5b78faa8bd66b9b42e4b8d5a8a7847f15f3021397a5a9abcf76e0e873088969b45306caa269948dedfe373adc83239efec23e96ccef05f870447e441e5503cc3b3c6b2bb8c2d84d7f696c4d95beb6662bed6bb4660b8a48ed085e885a5beab271dfa538c202cb007831ee0d5b8e74dca616176d1bf1ae61d1d0732abaf38bc4aea8fe4cbeb471b2f82153b524fadc1647696095abd5f29e40318252e38e40004fde3c8b0fd980b0c5e267b4a6c3b2576376355e37a318676a974166a9324492e0879fc9f91193ebfeecde4038c283eedc18455cd810f0e946b11c9461704c444a919e3dcee5aec18b6ddee62d927276ddda77c67e60fe54467b5710799a37bf1f341ba649b9daf4f41366648e47f7c1778a4e7eb7573f4da555ba69f6c381aca8b43aa18b8a0db05be6cc74908ffd6b788bd3838faeac99f0a75b1d99509c5db593ba4a10fd3a482813d8f5419ee01daebd4933de270488293190276de09c7176e3a192a23ae58813fd515b32c2308858d8a6fac6121f9006d495ab624c846d476738f19e4c865de6fc1ad40a2d2cac0f07d607b630583d182aa3da089cbdc0b204cf6c5ca846caede2ecc86cddb524a7eac1a1be52747a7032555207310f8e3f6f39c8c7dcfb337a3a3d05fe850d856b4c5b7595519bcb84dc77dee926310225c0f737354f702855ef1b1740b6a5f141964994eb790a8c3ab02e365bcc4ac2b27c0f0a48d260ca19c54478f59ad35bc932c11f7120652e4d5bddad7533e4d2a92b0a1847a3318230ff4bbcd40a6b975fd1543c3a597443927ae2f6a5a1b2eee044ca777399f95e7e17e04c6b5bc6eb1a23b82f1efc011aed2ce9da55375121c816537358ea48f74325e975e39d1d5fdd3b767c5d295f17a5e2e294e5d11f303448b2e89048eb1b1e6eb3083dcd4d6c492591aa54f1f626f64a1383a169b7786fa3e6878b31ff25b5ea8cc63703e774ffa00dc3a8d01cc74d88321a5e0e56d6432c636fc0b9792341f37a593dd4406bb11b8a919d3f81a049f38bae9a2ec6b7e6c32a46da0ba8b01bb713ae29bd783d1f4e893d1afe8b8a93abca293662d6f1199c458d0bde72b4ee4aa396ad09abc860235de8e3cdc4e6903a3d5f52def60db8b2fb60a1727410c94707911cff9a4d97c3f1d1a5b13b605c8fc03417a7836f1ddadbd8655dbe109ee5a9de742fb8bbaee9451f9ea9f0736abdb6bd8ea0aae93b8e266f7397fdd7b7c0fe8cc148624f1e3db8b1aaabd34eec1c0ef75027a55a19e91b43335e205e0b1315cc8fc3be8097cea274fa9c10c169dadd527989b3e505b919cbf77a717ce762bcc58ad907a119e01ee721e77c0b7820252f619ed0be6fad4170340ffa65c1196e8b5a035fe9188d026e59310056fcc89fab69ba37f2c54f947911ac3b1ce1528f8dcdf76041e3fba2cf5d8f11c5d37b5b31f8087aa5b07a73b8e27bca40d8076c361e82f18c0dab9cc9382a482a5e344573c3f1f7c95b9184ec7818bd97096550962791d8bc1353f25fafbce07ce45a26877b84872785e5ab746b8755ace8a515ec87010ec67a5396045cac46610c8d2b032d2f9584984e61fdfac8edf33059b8281f1b3258a1fb1bcb2b0c428f8b9a4666e3671ce8e64cf558d67cd2b869c3b4fd5ae604dfa45025e502a50c5cce06d609c822a9238e2de207bf65fb61d36c335911c252ff84468d03541abf6d97a630a9daa4576e71720e9fe6bbe9578c752ed7e919ce7136739ce34b5c1010b9e3b584e632c424fae4bb411484dea7704ad5eab1797c7069ce7b138241ed97f19c645b69f4cbfce5cfef15af5c9b542756d6c72e9b1fb247cb9364e9b4caf6869133421e92ed5b7f7742c3258ca85b2a8e87631ab9acc3edc8870189391f5945812831e164fbbd1d0da0e95c0a14599da7b12c261fd069e3e8e85d22110f7afe02cca0236a00d932adfeb5f1349b97c4298e9c31d8b249e368f1fd89ac2827c014c9697672e4e5614d6b2095258564517d0905f9347c62a54b5379abef863a65860760e8e6a62d4777978cfc2db09f131cf294cace38c777b177d6a201fde7229ab30dbfebcd84c0c1f925cea17319deacc7716a5a7ca780903baa7eec960ea249893ef7fd35ea67403ba6679e5b5119e25cdef1b81c79e82d9a14eb1f66663cd8ede5d536bbaa029687a15c9f7151e50e613739ec0c13ab237bc800c1b8b288105d7482b50b99f692fd35f0f54ca221ad6d82cede5807645a053ac19b0a6846bc3cd17ec7e433fef678ac7bae7b5d19b0aa566b45bbf95bfcd60d67a2215849817c002c850784c016b8bbb675c8e391ef26a9169e7d17fbe8198490a455a6f4d89e3f0a670ff7e5549ea4dd3ebb6e82cfd3a21d36a92bcd7b12a3c90971235f2b5555823bae5a4b4215b68ac784de82989d81bf6b0b011b0a5e61f1837e64abeb70fd1f5453b8a35e631673f71b6404595a983f6a60f0bd1aa947d3d32c262ac2797aefe826bbc37758e1132f342a8985512ae9c527362919515f1d3e1016879fa4446221e614964fc91b89407f78e7c7faeee5f2f6fd32979ce9bb4563dc8b044f2b01650cca65c2d77e215ad10e73a8f12333e0a4fae4f71caed622a94195c8ca252baa16a9301323d4d84a506aec8c959d98cff7094457192fb063b277d1568b721eacd261071fc4aac9c6e20664f7cfa20664db8e1a91c1dcd37f732dde6c113cdb88279ca32b0badd39710f0053881c5981d66aec03aadecfd84d472399fdc4c35f06fd03b84a5daa00b246e3150b6a3142edc9f200e86a9ef60066095a46bbdf761eef3c6cb1bd03420c0890120873134c3cbe850ca3614ca9cf4fa7e0015b77b98b9d73f0db75e372bfc0196e01d060171fcd3a9e7d7b0f7469ff001d0745485d31e5162dc07a249b6e70cb44703ed1b9d95ba4359dba258f5e3d103f599a9018ace16f28eb922fd28e56ed2538701fd2dff2185b7277760b199d10e8b0bfb0d0a2a81f9842f716153ef99ab6606fd487d7bea4ebe9b7827c7e5403d79b400f1214e1f14aad2b730ac6ba8a9e7d14398798bbea87e81dfe58f592cdf0b4127b9a9980173dbd1c178c79c40983b410f44c120a3d510039814335d99df7aa6ea07228b3917e0625c481443bfa57f9e38be2772b707493aad21e0ae062f250f3803dc63bdc0d83c57a6da564e0ee827e8f2ff075a565f136fe377df5bd1ceb313c58938c48dc8299c88810366326d0df08f25688d6d3790ad05a8346742601b5765d9f37f6b1250bd0992dea865e79307f808857b19edadcbcf9beb5d71765b43f0d4ae95461ddcdb2d98a16aab9b5b1f42beb00db8c927213a9707ff42fd94d77be4c53ec8cdb83c18a7cd3f44fa04935b9ccd895d7c549df27655b4c8fed0faffd2462f2fe8f86cbd7dfbfc201820c2906157ec55b8c44157cc92a88e13f82d00a46442cb33d7f78b131f9cc4b4760bb51827ca62a16dc6f3f6dce0b0e7b88079322e955c0c0dd25b4a1c6cc5b2c8dc4d0bd7664ce841db3cefa77682139f4dca0487b085e3bd32c7f2ee419090aef39c78fe7bd77934b59d18c29952d15171fa08b718cb9eca7be7e1c057c9c0b3016e6191f21a04f5bf2b8c0d867d68926fb1f5c6752c7a92eb5a7cb1e772b4d0434758a401ed5e9d24e596fe9a5c74748300ac6c6b8de39576d04b04582c8647c8bfebd230ba71c5c00ccebbda9d68e2af218781750a7c0ce915bc3d6179154c1628bc4033a44cd997e65b046273614f26e1565ceb6b7829e60899dd83bb205e19abee1ade7052602d076b55a53fb4aa85674a40bb06dd492155bfd7f01f0854f3bd6bd592cd448ce718ab760db45ea46745caa23f0f2ef1d7d2afecf2d6fea22e67c23e25f61ca15432201a9a291e85d7dc570aa2e047988d752562df47365a8a4f8d7699967c6e8b307e94029e39d77b8618cd601a1243ad4b897f0c5a12b406228b484c7a7cebf4fb0325b68146d2d8315d72412b658c2a432192e4d33217e8f1bd9402d9d2b5e3dcc4b7433968977d90c0ce752ab7900f70d850d6151f20d9bf3030c68842696bb23c7331cfe6801c571dc59ab2f79dbf2747013431a84afb8ba0ce7a99cfb83e66fadd8d1e07752e740309eb00d44f2c4695f147bd3a0437293a914591be4e8248f73a0af54c4e6a0b48f02bf1cd8921b8e4f400e0f5b489fc37be280853d1598fd44fd362e6ee424d7065706a4d64b1085ff619bf5ab30794b54b65f3dfc668125cd97da99266d8e8621694004b593ced0e87c3d0970d9d974b3687c9862271c5fe092563c907ea065b1f9ea40fa0dc625893c8a47a1f8e45c4c070991dfe2de501247f2aa2a0648f2d6ed9860cb9947248106243ef6c76271d74baa6cd3cc13f56669bb41e10d62b552c61c53ad68baa2ec3cf558fec222d216f0c9b53a9604327e2aa2c2efaae8d856382eb8d616bbfe5c8653aea8d5c5d4c9b3dd4828579745945e4ff33a078b84de939fed76aa9cf3ff0db0c9dd394ca1c57a6a7a71dd3b434cc58d3f55909ec5fee80248f8fc43e882e9c614dfc4c50d4cfd7f2b147ae78594fb010b03beedaa06a154d802cf54e14a6f9a5383dacbb2a60fb0ac19e4036d4dfe38206d19ae7deb224efba08ca2ed6ce7c2f594d86d3ca3feed15d44dd5834cf9deff7be519b9dae58f64f9757b140b83e862fcd6541bbb81ecba5be1268b67512aabc61c6b0221eccb7eb656ac997fb9849404ff2a9daa45c7a5698f1cc63fd2afea6b157ca7b5d59ef7d47d4005e71abdf26c69a599783489cbd50a7b7318fc90a5ec0607de783d0058bbfecab5a6f0e91d1704305f87169ef60689f353a20744ce5528867a10996d34d4875d22b8c8a456895195d6cc509b20cc84be4fc44e603ac8c1dfa46bc3c107250613daabf63bf496989662f3beee74ce0271bff7a3d9948883983e4263926b3126159ae2e2c2658808a07be448cb29a52ee1da2a104e8bcf2f64eaf743ae630f57b4877ef1a87540a84dd887778e59458cfb7b627b59a5442a66733b9c9264e041eca727c195c0fd4d67e1b64cb24929c0d7f78f14b3090d0c17c50876f1e415ae29eaf5d66ce9ba6eae4f80a1b2cf6fc25550ed04cdca712f15d928d8cbc88f14ffb1d7c2b36677be374af22ead5aba65bd68f4847e76d9a949c966dcfbea5dbe214216ffa89a289cbd2812ea70bf005f85faacd9f0ddbd8b89f2c9456952fcc499af410d7e637752cebaec23a8f29bc1f7f7751ee903da50384b68886c4d18041f743eda34bcd4a23d49868f82ccd3ceeec0d7df1c5dacbbe8eed6cdd35b6c4b06f9a81acc1583c72ad14d48b25b36f0aaf4b264dca2274bad9e07c511b329b95f99a72a3b9d9e0bab98df88253eec7222cd3f1567f8471bf21172cfbaf6c11bdd98db8d33f6859b528dfc94a47d7f793379bf85a5d047a9b5f0319845b67786c631ab7851cac2736ba7d2cef35efdde6b638095bc2d535313c78397b7251053b5bb204ade3acb54adce342d6ff13b194022eea8da44546489040d27a4803d994409a6552dcd8ce101c1305b94b0bfd919650d14e2dca68fdafaa5b0767258b5f58305896f94e387a1dba233b7f981830f12477648b3f2a13f3f4073fe6364809b0af09f61ce88248b7c52499ca0fac188463d7c265ea2aa591638d9fecf87a33f49d78bd5dc99114bf7129d448804ddd29be7fe671e4ffd98be0c88a906f937354790901f56409f2ce930d31a044a896b6835bfef278f8ac93e2d00be324ba477fcb65edc9abd031f027b76a44af90188f8255663e59ec774817b28a3bb73789ebe88cc3cd16fe075e5e4ab581b83450763921e0894287134f5ba2ee0fcb40357650893c035e07351714de9166363fb8daaff7ccbc0a4e49f619d6bfd42c7a8722fe2444836e0ebb6591260a85ac8bf7f3e9cfd431fca91c27e36031d6e8d1afca546c11caab1e820f1585d7e3e5b2229c5faffd10cba8ef46c6654265374c1df8169170b227937df29a756c717549175ef09403efe7ae5dc2a4d7894e799ef8ab7dae53a2cb28c0e1dce904cdb22c98b5f14a061be0918d7b4a28c5915d025dbbca6b1d9b427d32157e96764d4e96fc74cebcd758357a2a9fc3500d3482705ade01ce8cd4986219270d720e4182e913c9ae71deb4ee26042a4ea180f01778bc1085139d0241d54497120cfe28a3039baa4766c8adfb5c8d7dba088e2e13b1b525ced7863f5184824b47187e20d676c823f4a9da2b99d8d3e930261817e351c41fd0ca666400c94d9404374b1c1e56999caf69bad9cca8aabfd43b23ec40a1b7997fb513679400bca55d82f782307b9376880896ddd8355d5a698b84bbb048251bae3c849b2a550b36feb14bcbfed926c680ab563e30193632a024ea6cc4acd96d779e830905a659870253498380050f4b0526ad8e38caab90270af4a6b2210bb6d5b08524fc95853824de7e104f6df5d3d47d10120adf12f463e2630abdd9773d2b9c22b5d1dbccf86177c7e1f266f550f6e8fa98f25e1d5307406843181aa844446ba3aac3a11fb06922428b58c7ffff064d01bdbf31e77e9516412223a93f7971f260735b06f4f2a30e4515639131f672e1251f5891680f635fb5ee1e1a2a4709b3d3ecde0d57c613b42d0b536f444ab27c4ef6fe6ca81178ffbcc4233a54b02a7c7e1af1dba3cbc64dedb592ea9c1acd5ee82431b0e4043b6ac769647c2e3a85151933c4a413e1d655e0d85a600136f89308dcf4a6b8918daa29b9ba244a9b277dc69096c22cc9bab529886d3a5cf55e7b0ed6c23342c753b2983e3d43a71c5b04114b9cfb8cc75e997a4fabf457702dde240abb9d4cafb7b7393e9965eb312e1639de637738e53d3e0ade8893d459c487ea744d6928b9e40db4713c448ce3a9fba98d08af31a9962273bf0a1c447f776d14195af4cb3226ec1eb314c246f1d5525e0f101efe18eb4ebfe24fe44ea66e1f9ae2e93234e8fe3cd58dad0105404408034e8733d27ef4b36b8c7de185b50f967347d799b91a84705b9781a925341feb4b12fb1bb6c15cd9a9aaf46df41c9d089bb4b1a4942a7371b7a6aff73065971433000ff74baeea95102a7844c1ceff3d9cfff5d8c371bf19ec4b67e4cf41ae6e8ea38f1164e08d72b3987dabcfc21e9f34c109b7dfc4a5daf6cf43dd98da29767d8d458c5b573a73ad230e1f520b68d2d9dd115387bd7223b2d6b51a1e8333d79d820bbe9a17af02e053c4bc9315e21e0e89fcc385f534344349473b3c5d1f0dfdb62fad1c701fa7871b610bd784f458d086bf5c53f49f54a2ccc5d6af113f53e319cd21d8757d576809b8b7525095b161e84bb241b170c487e64a25b085f30cce7febb15ddc01db71e14bcc2d1840f2e2746c464e6f21bb3ec714273093f3665b9c4345dbf91986e2507b72b2f2df8522096f6c973db8ecb84cd55807ec9429f244aa749664988d1e368a54e704652ccffbe0ae322d4c8ab80461fdafe63d8b8a62a208185ea5eccdbcc99101d73cc3e7e529783004513708220f49c86e08e3bc43a3200a1f49f5fdf604c0a6aabd074e8f3d10d832854c0564f7d298cb8b3565a8ed40e6c39354bc14df124297e907cc5be2902dbebb78dcf19b7779ae9f99a13c0ea84328654421990304a26fcc8bd4349e2a17bf7dc22cd4f77d1f6f3e5bdfc01ee2ef9070cabf1ca182d0f3b7350bc948eeaf1efcdaae0c97c21f96e6c264c595d3d942f913b2da06afcd7bb39dfe168ddda0e439f129cc8adf2a6a7118eb830506dc49dff507d6df64187a5160c7a55aebb44fca57c99ac4b85e078be35c239620bf936db511a9fbed20d47ae1f9769850e450712ba33af275779692c5cef048de85d9fcbfdb9281f27697580b2574c5cad160274f5b98dfc25bbca93d214ffb202013bf657b914fb1840ffec5a6b03136e8a1cd5135a86ed057a163baca185c597fbbf5c1e8f41d564b4b7be82aaa76a484f3a90794f21b63ddfce6878ba5a409570e5f343c73a0994d29bcfef3b3ea2fbd6fc365bef3025d00906ce538d2623b9078f848e60feaf8138af14bda6010b11397333ae27c16d6f1a61e4ecc5c27b9ae5e115c5b803d86fac18f7c699087e64fb08bde4e882d946e4c51492f3bcb7607060f3501182dafd7eea666a2f1fcad61b27b3a958994728bb399590d5694eb6ae6cb8f2e5735a4410209c63b5479afe327f851fe2073a63cd7e7b154d201f17081c806a002b8644dc81b683fbc37c1cba8d77aa3b3c6b30b988060e00030b68a6a0ef9cc58945d863ca8e775045e1050f095f9d5d65483f6b5a55b795cb1475a40cdcfd5e8d17ee9462b89198ca733f0c8cbdca4e825a950da1e25534dfce389e22cca4f5d523d2c99192f072b2a6079df423651b0bf699d36fc8e0633aa31c5be4b786f8f4b50bb0115a0b3113851415727fd4d4b55e70bb0587c99874f8661950368ea43f356a052622e16f9d8059f40dea01d34842f51b1688bcaa37f31c59368e00c481b003ec6ff95874651e5c54b3ceccf268ee699e0140f80ed120108df617d02bbaa1119836389960ecd1abcb1552b5f812ecc1f65dbd7648cd67b86ec382051740dc53d71b3f83494b538e67d75d1a3636a0e20e6ca6dbb7511510f2a612a8e574c0e2970acf1498412993665cd871461b5dfb6f441ad0e8506427fff118edecf7295a483abff197413c6b42eb585730172ea986309db811c25d0267273828def3fafabbca1092a72cd08a2209737a93722e0e47d9a772b6bcfc2b83cb81187fc591d7907c7a6c122b96f0c9c12f943bacc42d9d201ba5eb463466238723f99792324c4a0d0d7ac8a9c6478fe38dac9de8ee057a019144466d736c7b5bcfef47212f945706514f95e5cedfc627fd6ce74c9f6f63f2a381dd7aa9c78a0f7a7ba7777c0f16b54b70d4f2ba724e5b8021ad4afdb120b468dce6482e7c7489dd9200b45b68c357ccc5236a32489f447ebcb0e14057d0f117a2851e9ad4c3119a69ce96319c84497871368748ab124c42e24b0c7daaf6146f7a99870f014a98969c87a95e7d2d026d2da8434d3aef57bb67dd26f365d18922cf514c0951d2cce4338705d9ae988bdefc7c4664718a7e42f7950252ea39dd78486bc1e71d31c3a9abe75e81b6ecb89db4b84e7a6064e78af09c3f5db919d7632bc0b2360d9e071f3da25b3cac53411d5e4c9e63794d7b48e5913afc8c75b44536c3b35285ef163ec18f29f512225bfdbb4ac81e52ce232213be040f4500ff736c26c9c9192f193482fa0ef9b02a6fe60a07eb3b5aea56c1ed238b823c98e1179231c69d4f9573c5c9c3db4f97ea6afdb9dbea65848564abee6b4cf10b7930a20495e599bc64d865731ec3d8f2015d5d86b71b41d4364bd1982a41c2b38d1e59ee0ec243cee10aa5a583b0278afa3307158717b4c4140fc84ad483a0dfa8d91ee7422598e20f7f5bc0cba910f3ea3bde1fca7cd604edcc79656885c2c21fd25e82e48eeeb1e760c3ec92a9d40486c6470ca2162d2931b1187a774bb4d752b3b152aa51d52c132e3ab2fcfe2df3e580a5857f7159e521f315e31ce1103fe2829268b0777f54f62dd8f7284f7307a86393811362caf2b8b24341e909998c4abee5c5cfc52fd99b78a60c1bce2935c1333000485c85365a3a6baca71e72c3d8a9ada79bd18e7f5080e7589ae5f4e5106e584020199d4ae5671fae3777b04504e709f5b598270bd7c1d71440e7472d2e9f550091c7df779a14332c7f098f74d7bc71d1d93d50a6166b40015851467712b3e02bd97ea4ac2f5df08148a75222b3628f6fcfe7c409a1b629cdb04ad8d0ab1fd607ac17c194e0692f59c2af56f620580685c06fd0dd7c72a6d8077e2cb36368a9b6ffdb00df8f3299008470fffe090f1692f78c17d6182a96d966dba41f183ffac092b842a0481d90cec87e9d9c127ba885cf8d4f0c2d6b4107dc2d09f8ebac61afbb03bd4b5f62943646b8b81a02c4f0c4f85a72ed512b373c4304798c7550d4af115c611b55c18cc4e4d3d81d746ba883235ea9c9a57d63fcbab1aee40c114d4cdd2331904e4f81b19c2c8e428ef9582c9257a22f7ec95c61aedad5b15372395d17d9229a82a9c47ce02a89eb2287f633add5249b77db2d7fc490d50c3c9f34be703517b7a7b9d546abc3b5fb92059165fae198183a3b9808a3d61477e4b5155651c6f4de80feb3b9eb742d4e6a39d7fe94b5a60c91fac6f4fd43896140d6be1ce5b53a8f58c58a002667cafdada306d7b2ae5364932dba5b0fe2622090876baae3d27d3a02b21e21caf23b31302db243665f8f42db17316e7531501bbdd2ac19ba0ce2e29dd0402f2bf4045396649e82dcf1ca5dd522af32163dd13a1e88b69433219760b6fe72e682aba4f621ef75d7005bc25a26b78bec610edabda357d0154c1a7e26cbe9dd415e9a7ada471c343cc3825e3cbad0ff2ff99579adc2cbbb4a730a13cd2bae0820b66b6b3c516cf3a30115549d6192c80b5260970adf3e528a1d811c519882ce496fa2cca7ea9d0918f91ab8ffad289561180379b76580ba4ae0d8c253b52113648d82338bcadd7a7cacb9cb48a1b88e026406f1e0d74058864f41d0df23f1168c7d56dc5fed9f5ba72f6aee8a614b9a2341c997269f0f4938c2ba1d06009b5530364a32e06edd7f1313b46a6028f010cf6de88e0ee59f9a89a89535da460164b3ba85d51fa541ba560ff39df1ca5a2ff8a22d2ba49104258a5e3b1d040b5db82924ec5dc3ce7885d38814b3804460382ae4aa3616e459739ec116a24863d4c886efda86da31976bf39ce5d9718d919cf50b228ea0396221f3bb4191f772f19396a3cc55f3186f3d5a52da103bb55892b4e4d411b2a37eabea74ddc87f88545666357b5471010e9382c8d2d3c110bb295630fbcf84efcb89b5db2da79902a8c7402e53738bc277951a23bd94caf69ee4e401f4da8ed6cec476168649c7d453f05d3d3dc7017a3cf50aa5ce2f2f8312f3eafc39901c2062b36b195c9c22879769ba8ceb6c1ea1929f9258584bf6149e25513f2185068ce9fcc1a6cadd44cbb9c66637bb32e5858dc6ea28d45080ec3c78051dc1225b05dd305caab4f815dd763a00ac97ea4d0f26597fac1e6055fb2f219eabfde8150df305b9f039c82b410bd70081c5dbc713e9c5baf0024643bbf68efc13c64e923cf5dedbb37f29780fcacbe52c1d8e6723223bb6e012fe83ac31869a37150f7af4e171cd8e248dbf49546edd42fd8bbea51df681d04a046100f90c9be5cced375bd9853fee09b9b3fe15a8fe5a77c4a2d3774d7ce75a5c315d120f5c3c1ba67a84df647962383979c152361a17a2aac29e6ba6da8f5d966210c34c5d80c083feb60c45a0a55db71d54c187f75fa7d3e5212c41586425fd4278ddd33525d157fc66a0e66a497aa7ed39bc6d633188d7d0086612b00ed8188786602300495d17aee011967d26d248267dd8c6ba941f4eb0316c1ae5ca6a324f00902432e66f97edc6352d2b451be57a82b1629e0599d9b4d64ae7706514c179c8bbc9c8748d2bbd6ea28b8e3a1feefdb22030354f6f1da8919f6969c574f5312e276a2018799d984f1d1b7931a42a1738ebdb6d72e67a30214e708326e9cb2690771ffba6f403287fe39785823c305aeea7ab1932fb6053a86986e5b4a4082d12c1ee01389736d4a7c73c9669a524d642f2484a5639253c8e63c9baa37218ca7608023f8b69484bbb04b2cc83560d895e0ae5a7abead6cd0fb9e60c066410c3d9194a38b840bfd99281fda03a14ecfe117a416c49a4c9fafc5477154c27a98aa78c35b206822ef4c9d351ca67f994b0b7760d5219fd29f3158a91862cf50c88c1a2ddb0b6296bb4b848aff86881430f26c19ffd1a5e05ea611194aaac75df844e4bc3e3831892fa35b621b13fec3dc6e813378bd78ec4c0ba6a1aaa9e809512940675b422744c1eaa506075ae72a36931a97ed95796886fd85d76586d66ef4bdaef0dc44d0ef27802f61502c5e752251fdb8b7456addf769e5f5a829c8c82b35514a9bcb4728d8e773112f8e8e414a13bb237d93c5477ba5e910c672b6c3dd70fc092430f71eb0d38df73bd91272fa9031126f6551406850f7dc85593e5c602ada40511f47fb911d9428ddb545f703cd94ce40bc7fa0114916e4b235dc6c139f051bce7d28ee2fc9d336e220ea49348751a0b7f72db44ac0e8c858f571c94583da57cd4ea3537e7102ca078e951ae3c59d2f5fb25d745fe32abc031496ae70522d861ec5c61000b2cbe54ab2123c65fe9427d0cb8d11cd1fabe3254199b158ec0e3d799d488978894fbeb87f6eb4eadb47f9e72eedc911959f375603cb39cfbbdc34ac55bf964f8dd7bcf0c290a69a28053e35563b6413741e8a2f0729ca0d85dd67e510547603500c3032fcda01b786c6bd368d56936e716109e9820067d3eddfc914110b0b6fdde8275ae81c81a4919184e926df2bdfadf02ff9f8d79ad4f5a7b88d71da92619f11ef3c56f9eb9b076b0784d8d21586a25241164b79a68a518ff91fe8cf4fbedef2ad41bdc6471162ef0cc8b7bcbeb9f73bf1f1c8ce0c802dbb4cccb0623f7b4ca66eefb973c5dbc35e851d10c248f7dba1b80ea3f0178e7d8df1992e90f56a6dac8af7329b8c5219d9d116cdd48f62ac45c2572b22dc40102f7e537fa5af0dbf46e697e72a35f2896c908a5e2a521ffe9f70ce25160ccd4b7ced81823152acd53d06d38f8afd38c4333abc5123d5dbf403d28ffeef9e1d530ccebd0ae4f8302da3280c667386984502323d6c49fd621af4677dca266dd9e24e785801dc40c75a0969fcb7407c4d18fcd0221383ea8ccd755b29f93681454f5eb1081e9b6e5addbd80d5daf2e045e35adc5ab43149e8d109cd80479216bef5efc45811de1c1709fbd1c682c27b73ace10f66d39bf9b8bfa8c9fda834f723ee64874e7ba24634facda0098c1f2637684e2612f0145caf2684bc1b333b71623b6cb36e9c30b3251d945856919054efec86a066f519d5699dee4c14d4c6fe361ad1b64cffa4adcce54256f32aee84ff8f73deb1aeff8482f0fde51ea9e2472e900c970599767b4470c6821d8a5cb61b5955bab07dc9a80a1edf8549fb5a6d78bafda97e0ee2d3981fb3ab9910ab3c9a58fe9ac6bd0715ab352a9f8e149e7954aa301d2df9ebbc39f1e52f574bd35475f58bf47e991793de6cef13b1a7f194d664751def6a0e9e4d6c24803e721050508b586106c55b5a9ad0af3690881a8b2378a22d31573804912917e3733169863ced2a56b58389fb0e184b6bf20ef65080af4a9fde35cfd4aace8c103e14e134cf5d0f05d41814897255ef2e4e644967b19f22ca41f653a394a5e59a7f8926f3a5bac22104c266d2122419cb2cc4ece9cc8323a186671ab39e9dff8cc8ca314c533e9ede6a49b839395a0520e6cc3d2540f89da0a832072d1baf9e126a72dd6a9eb3c8e801ca8c1c9ddfa962d5733257564f8ba322ed91a435a294f24827bcd4b21838db3c3250d6d3479705f422205cacc0f856a389adae9a32d329534369f4e274291f0e8ca1a76351942f67e406e42d7a9b97a4494c6b799ff362465bf72f9d8cdce5d9ef09a60e80b1e8bd5f138fc6d8740be1e5ca82514f7055cb06971c8bc726e4d4e0398cff15aa811947d5cf73dfaab60ad0f6ab06b03116434ce5213c5bb288902ed1d9ca25104480ed4e9993f13c9e797db4a28539ca155e4668b2a8aa0396df3df7f7597f9fb910e647e8e9052a80639a5729e225f22bd1a8e99257a013a7f8923acfd0ef33d509eb6d6394e1a26129f96a1bdb598c5dda35b18f16b8c9816fb9f2b9b8f1294f3da64ae23e415ded33d71b8126309025eeb6955cb62fdb785d1184a466c2ec817be1a15c9d1b03566ea955510b43d9c5eef758ea7a994f51854164f50c18726bea2bf884ee793bec06c4be6479ecb8e3d5952908a8273d1090ab1199ccee3a142170e1c3fb03dd79c8915e6672534afb7e24f614899d35d746bf141dedec06db3abd7160e91c832cffe75f79e2a944dcd22875d1588e4e1efc25159e13d5253cdd0528560e6104009fb3a9e7f23b2643ef1f598042b47fa5644232bda323e4942523987e5ec8942bf9c11e307df73dd41415d07dd56c7af9b5c4dff121b262a907559c448beff7c08ace7707b6f7b3256ce29930f3a8df20ded658e3cd5230479fed7b0849848c3deb497d2fa9cdf7e4bbe4194669f2f51ece0b768f1744f82660956a5abe1b7b6cf3ff390a4e1418091036928c3356fe26d026fc0b7a6959e2cdae45fdcdc989e44df75e1b393897252ccb49345a4a41e46d31260ab38302d30742e5e7a680faf86d069bb12822eb04aed82b0cf50221ea7bb6e340ec802dab83e2f722f10a0a2c3ecd8fefd4c0efd3c1071d7de331ccc46875c685bbfcce267913f4d9b946e0a2c656db92f4f7acf4f2428934b12db1bbd69cda81ccaefd313558417137df0c1cb514d1f5d2e2c19b10c0b6a383302aa708e71c3a8a2f07e82edd560a834b6769672c21ca7990795c4ed42b342eea5820c176c26c6d5404e2e0e599de3c2e34ef942120b51e4ef2b4432533157638a3c6f51b403e4e9121ea624dde6941fed0cc9713096f3690b39375c3703e9af2d3ad971139cb85dd932c3071b73f2ab6c9de7d965e87a6262da9805644fafb9faa07cde7745eb70494c6ba588647e34c1a187752f20f6f2d8fdc05d5ddd1f238faba5071b140fc8e0d1526656de8d13a493927864799df2e5b6862df8d613fa945026d8dde29ed7a932d2545403ecead948b32e99b8d44390e5cd7a71a57b388ba1269b4103c58e3896a02843fe914e0537296def8597f0b16a17064f56b43d34f80d64e20c5017b498839da557ec44e2fa491ce502542c1c69b742cc9efa24b4d1d828bd306b5dda6a20b08b7a1e805c0920a3f0146701983840eb59cf5c5927ef6fd94f9974439ec4c4a38c7be4847bc0ec707942b3ec27614b246e015d1fb81e02734039e6a4df62ba10288dc89181e78effe4b518e50dac5afedaf2e207357d77a5c457bab49c8403111454564949bcb3e25ed91029ad9a11fe8cc5faf11c356a59330b1c87ff0d2e3ddb54cc23b8501bd22da76cf3de9a00e688451cefdcddfd10fd183fa566497d7b5bbd0a268ec91ad89e048077a7975cbc872d50f4eb01525ab9517a7d19b5e278516a1e46e90f712447784e8d9b37e6cc5b2d895ef1c8c01520275c946265acbd018192c5a07017e2d4a8f1353d8f2037af594fb973f9df258e572b1f06fb748efb55acdb3aa5e96244597a3b62b27f81f45c0a317005b90d5ba1031d1842a81bb2928c7d064501306f3337115b5dc9c3d9abae51c2bac9af4448135c85d03908da82982a011f80fa16846dfc443a78e3ba243d396c8d17da2488c093f04cb91bac9519a0a1a48ba02d8b8d7e77226ec3f92cf316139cea776f7c0a566044ad442b1449a77e16f84c16fdfd9b96a740f9797c2ed42f6ab5963138c88e6d41f87441863543f2ec8290a91e77d490ee31f19874fb8b25096011ee240a0d5c86193830a8273cb754806abf1ad1ee6354ac1f65f08289db7daccd43591a6b1503cb122a5281ae21b6e06dbad004e1cfec1390f307e06583ba003180aa52d3c4e75de83d5dd9463cb68d51db5f5221fe856998cf83b3b30415187b58a060edb6abeae0674c8a36ad59551847f3d457f6498f65fbe2c7bb503634f040fa57e786bfbeae3e62f743a0ec12359391a1030988be6732ee1355cf55870887befe11457780818c81fb2f270fd49c954be524a6a78892e2e6d6158763569c6f617bf12f080f2f7cd2620728f04b3110a12ec77c79c78ebda6a342b69e8f4eeda585d7c0e74c39f36513036f2fa36faf8a5754e9b0a6465f00e9bf708185b3283948f8ef855e87530d1c8722728b1983c8eaea51a2d1ad8737403b95880fe3107a6a0097ef374d26d78a82be14bbfb3bd51562817291490575f81d0c0d33391ab103d60e72c4a44b20076f8d4fa9e270e911950d5333a2e5fc4a72bfd1eb7cd910523ee38964b60eea6598e932a641afac3b9e145b54c9c418e912fe430a6f26607e9f3b3f90ba224d8112c8d6b4ed014f6855cbb74920a3480a9fe7934c0ffff0125931ee4575fb4cee061aee1cd83ba40c797d61de97cfdc2431a0f8b27afdc512bfcde3bcc60fca43c4986bcc9a22e76b09e6130f344975b83642824210c97c958e5f70bcc44a3113f32c860b60b37f98e46959911d53147c769f7d9a63e41208463378865197f2b8227cf5229d280aaa950849ccb14ab5af5e7c342cc85e2efb66ab6004c34e4e5bdaae95c792ea05203a429decabddacabe774e1365fe709f470807d99c1972a7b724f3e80e3caaa88e4c9a23c0fd9739377a0d7ae59d7aca136272a56e170e22aa9d0f1a940f9c5b51956d62aadf7bf15d0b8e01006ce184beca749b21bacc972ba334639c0ee6af0bd66711df367a17dc989d77bd1fb11ebb0c9732db40f189110ad9c3bfa5b531740b5910efbbc859bf4299bd05fc6907296d33b0df020f9fe9a4f8c0af82e6c3df7043743cf4df7ba0e498a85976094331dc768c31cdc28832c80188640c94caf78ef1cf405242ba5ccbed8bfdd4a578a357ee0371cf9b3b72391eff996d0c5f86c82fa9b5500610c95439821a3ed0e8caaa3192f84d6ca91c9370c0a9c5113fdc82077ca1d846b809f69152b9a6dd3c2ad9dbf115437c6ebaaad25a449ec165474bed5953679139e3cc0ee21a5225ab148ba642af3db51e6879807149a62293d2e3e9c6881d421fa6070c31d9a1b35edec947974106c7627911191f0dd9465460cee93deed8cbc4a3bfa089f4f50c6a23171193d4b4ec99fd22248e7a17c78113cd5d9b84fdc49921cd32194f21096b3646c104f8a362f08d1dd677ca7a27d969a6630f7e342c712cadc459822095b41daa31126586e4461bd0485f21cd2797d93f936271d26d15c66d38903af83cecb4dafa1ff32136386d2fcc9c6df542506abeec7819de9fcc0c5a1e4ed9e22ce8206706d519d1d47bdc3007f548623d596bf6635052433065d28ef24f6b78dcd30ad6ed1a0e55221a500adf67307fd0da1731896709da2b8eefbc2bde2a39a6ed3f6e7bab742d48bc8c75914e06ddf3bb33e81de5134cace1237772a780a69eb345db1e32787449ed037fcdbb9fd832ac0d76c0786a4530fd293bc9efb63322db24479f5910d6f74458356ba3e755a6581485dc9260dae3c99cc6fe425ba3233afdd89b73241b3ad9533dac7dee0cd77af5f56619a901ae6985787ec07e86810b5b338109b69c3c6f1f32a17d2e0eb8b4ae064867de767c160c81a6ab6d048c408674d4f02ec00bb6732db00f7063cce2eb702af1ef62ea1cf4090379729656fbe103b80adc780b7d5f8cc98098e6a90321020a6976ab49f2eaacd8172afee6d970b817e845af7898ea0cb473339c980d1b3a4afb954ded858811681644cc07554ad9e2ccff57aae5281e471c2107eb400ab16831f200a7ac2443f1e29f4e6de44d1ecfa19420c0f50c96c932af8578b2917180320abea102cc29a6ea4a0dca27ff138a0fff359723e2d58b6359a5679ae5bbe53d6041587cd31591ca3e1566355904a264bfb8d2ea633606085cbb6a9b912bab466491a62d712aac7911a8f85907baa7713baaf7eb61c3e84c64a13eac7024b060683bf00098e186c12451130648b2a9ad66eafba0fefe632e274d98319017664e10ad30634e848fbf139294c5a944b94ad188a0526db3b4e5d8c03a9e3057b8aa6c38ffd23db616ea6e0654322cf02d1b2afaeebfb85dbd7dc13b08833f2d0d4be6dbe837cf393515067a8dd8140a279a1632b9d9c830281f22a03c445695313b3eb6aa0ad580763a8d35a2f7027e797b0b595409c80d7292d18fa44fcaba64862e983ceaa0947cddbf48c0ca6737249f78f2da7084f4bc1317ab8c91430dd7402602b451972ef9d9265d523c07a0ee08b465133c5400866e62c093ff18321fce8e8d58f2048d746c89cb4733dfa2d2239fff61b6067e3e176cd652da0008aa4b5df7038770d3b9ba93e677fd4c01bf5c2b9204073714d04171b6c898693897564945705d685b2658a5d7b62f5d00530b9093622442efa47dd094aa377435a4c233f816b4c38a62aa15188891536f93ff2b28d3472d13fcd49c54239a5f9c09da1f0537b3cc82aac04a6fe7f3348f2dac6d2b14b6912f1fffb65e030bd888362a91a0dfe16956c285f582567ef28cdd2faea0f4dbfa3c601d1222e75ef1e746696324ec58ff4a417b6b60d1f18e96bb81a16105bc3c41af13d7fc3995f243e9c4b78a9647bd23555d0c66cdf97272e509aca1886eab49caac201cb73cdee511a833cc47ade0af3202e5ea962ef56c9709f44fb39dcbf6bb88b96dfd799eb7b094ba1588b7a6e46910582565763a81eb61cbb661a57bdd66554bba4464415176b9eebbd8d6ec3440491d7749e5d3afa11bb6ce6292bf601be2cf171ec07c41f1eff8c80e9378031a5399d7351299b0c89fd99f0bbee6c899f5335e421ea48e903d9c920f218c738ebcc6d429d47f28623b91e999a24639d852fa15bc2689b0a1fdfafbdce0b0758b3360612b51c3b3cc631d27aa8a923a015632cd6cce4366f3ae9fb8722f2b64043516d08e5dd7409bac2cee693f9c8f6436afac866a663f870df5ac9509a74c97cdd55666cd6f578e48bc33cc64c6df4d1f9c8aeb7d8b932079de2ee090a7744d8b038955dd019b240ba04b4b84b1dd16bbc0864cc5e76f5a796e8cd391f3fbe3f4189e54611dd2c5cce3f7e4a9a2358ad268d6d8f9da07e47c6ed13410781c4d302e383595c89eb6bba495bab53a5b9f5a908af0d44c0a5614ef9fe44511c5ecbca46856474d283952eeb22cc9a54f801c2a283ff9ee5b327f5a8336447505017b742dafede6b3c50c0b9e6668e46c66519d422eacbde4c22ff850c61cc73ef2413d4363260dd4a48cb1eafdd882a4c11053d3d1f824f153b26a18b65fa582db847010133410707a39011a80f3be12644f17d54c15f41d7b4c5e3f69c969fa0a0f8f9eabe66205212684bd54ef042f9b030426b2483798516066d503696465f3e87f42e82532338675541885a681209cada50925c90693971443fd427cf574792c4bfdb6a21039049eae2ee7c580bbeeb1e140cd666fc0d2ddb61068969de160bb8be06ac77fce721ccb8b0ed08ea7cad71d998eb24834104371b3038fef5576d2878fe741348118537a617335d77afcfa848fda8f1703152039d3c7a41dfca1347522bb97c1a0ceba95f28ec7c37c6cd6f854958c6206348a708d7cf4b3daf0b9aff0d8423bc4beb08b792f4252c137b671ab797e5224b6c6fb06cf257c1c516d262e34fbe1219243fc60786683def7d18a32752a1ed3b8b545475bec679752d6525b83c508bc84a9502cd724f81d4e7c1c200ea696f317e6dd0c936c564ce4638aa3e870c2c2dda3188185eba68b5f32c527728ae56f81bb040e91bee9bd6ccea3007df85b106d7d1711400971e0fd63c73f3b5a289681f65d1026df8f9ef7004f1f2acea0b3dd6d5088721cee4439584dc2dc74ded0972df69947da1c20e67988b1ee73d7810bfb90817f41fd43c3c0aeee7c2a2c15d305427cdce56a08fd284c30c2a79fd8c01c6730dae7b385f2f14c7a3987c8706e895d70446bdf7627b2e713a205d13a34a727cb12fd3815a4d13c2a95da2b075f2ca13480b6bf57b5197646a8fc95d7a723b054e245cb0b3cdab13cc343d0e8842d0aa584b324bd5f075331d7fb3d2c52e9d898e4b55cd41c552a5f0462df5a361e4a6a9f18784cb6fe8a5f14df96ce8596463322e170a5bea31222d419d641a51c864ec3ea0f0a809018f5b26dcfeee8caab543a462f0a09509da899c33b3a0df2821cade272bd10e6711010f270b2e89c60d078c9c11f77219d75d8e97bd5a361bbc7cae854b9ca9c5fafd4ee558176444fe082fdb8f2c9720d713ef12cbd5c5a08fc68de088102e8b66400731317aecbee809588666b79c6f1fbd8249e5c5a3a340bad88ab1f2e3ec5713780e3d02816829a9556a0036d08c2503e4ea1a3216e1c3b11f600d9aa3e4631a30eb2ee3f5d8fb274373f8ee8d4b0151ea7a6bbfdf7a20913819f27da002f0e7bb5d1f02015c16749cbf6152687cfeb8f6c6be89d4a7de2cdfca1bc6b5ccd336062510b4a8c1ea5401572d760175decd1d491aa9e75adea21c6ec2aae6a7f45c8d513e5284cf669a2578b62f496fbbb6a387841941c9a03e7a2951e30ba5cf132201955902f6c5a5b577ddaaec2cb8ab92d48fe06d423bdf220934674ada88584471148c0dae31bca5c153237948ba5608df60373f55b5b4bc42c4b4cb20c698a0ea4c3da2a0d487f659aa72b63a3df69da5e4af6c113d354fabc4ca9a8d132b8a0b140a2ec33d8f4da784c410045b1fb490048794270e9feffe32f6d3f76e934068a754a46691dda200d65ad727b6ab778333a2e12e0cebd83bfa788992ebe492bb8c300f8c37adb352cf265f4405d1f00346e6ac37d16a6f7f7946c3e3ecc52f2de607d84dc7f181b5c117f1a91dd5f63dd4679f9470a4879f9f46ce62563391dd71070cae4524c3b4e5570b26f4627f01bd75a511b3150a952960d4b1cdc339506bc77945f3b06630aa6f033968e460c47ad8c6b2396231d746ddd06e75f5fd3394d20e875316556ecaf97dd01a8a720249fd08c2f10f7fc27eda229150320922e2399556ca6bea8f2a313a0319704f99c8f5fe0c5403df7e67675699efafe9133bd744b1e0b939156f7ecda0040e177a0a451f90462ed25102a442022afb12768db2cbd08e6314798dd916406a78c4bbcbd8dfe9cc4b5bdc65ac30420d7b423f02364cc9cde004360baaf59bd8bd3a04f16a8ae7f9ba8800941bf2e4153d1094e9ed9381a9ca67c5e04561d9643392f8d675b898219c125302608eb006b9065dee390383f5c2840d9cd0275d1600e3ff1264391ef53cbe816500d5bfccb04034209710863ee2eb97dbb5ab01de3d68b842ec24b9ecd21542fe7ee1f5fa0228d974fc2bbddd8bbd0a340eb9bbb429a2582965ee7feeec9f41d2d4c3c3ba47eecd3253d819348ae49e2a2fa3141255bf874686db627b218d9cc28819a103a4c10a5e27734d67ebd13382874ae7aeb6299ce45f914b0bce6626237197f28608398cfb9087afd5f502fc1bc937d4887aa2aeef25f273b59ba2827e12171ba8fbbd0f6d484190e3530ef580d0e5ff8cdcfce61c057ddbca1aa22c925b50afb3ae99336eef37a052c0e429ee1ba9baa674057b03731b5d743d29aed35a0e25e7be760b8773c5d532793050aa88da2e8d53402df9b21f0b499abb3ec06a962111c80ee94303ed90804ff2ff7313b95ccfbe2838bdd3460a713f59ca5f7f7f4eacdb93ec49f3286adde8a8551559ec99a8134eccc65f1a7b94f3bca371f4373f36b592f48b6d37671ee64e693f34ae454ee124248cff6e7819564094f63d3a206e7c1d8dec8c5d33bc55ff0320a9c4a69d8841377317e91fd75bc6784a4a831a84d355c3d6a398298bf3d7f35a49a6251464ba395d295f0b9d210e49559a2b13b90b0ced0d43f8097480b783111f57f756377a451c713e5d9a31d841c967ee37e217495897b30e3ea5f733d141389d67e14e64807d1482a6aa79a16338a86590efdb301d9afb61118a9032dfa70d42368fc447bddb07c0f943ed389c467daeab77f5a50f0a0d4778ab8e168e577f43a0f9c3b0445d4bb10a37e65a09e529a39adde967cfb8184215e9bb02d823da3da4c9ee836ecdc432f73cd716c3cd486e3a0cfd68fc029562c40c8f34cc3b5c1c3f5de021d8073c70977a5b3273503faf82955840daa56fd75c19a0323d1d3411478a908361a7974d28d4aac641375199e339fabc696965738ca76cdc724061f2af6818d0b6e51ce2a59ab9a727fc937c3558f3823ec6d266d74ff6a41ba356564cc22358ca826de46967940c32935e294176e88bdb954d7a5edf2ef04a09b35d6810478793f3f0f433e4642ea0896cf942de735cced849d915270e405563722718dc460a55b2d21a9da77ff0df2b75f17154a6377cda663b7ac0cf1c4737c24d80d847cdee48bced8f9f12349b9a97029e0f5c48370f14c4facd4feacea859b85fc999128c18e72a33b15c300198cdcdb4f6ebd2231fbb0d113c0078efd0ac8358a8527ffdfb391ccbad28214bc11af2bf0ab159ebc2a9d2123e2b9ff5136d2621569fddd4465a82652d832511c2bfd7575b46ea3938c934c7c05de013346f870f0a172b141f6f8e3dc8b3e72c4cb39abee05fc9a12c52bd247357cb49b410c3b54aab24aa2c9b497bb55f1f941cdf47f94e4d23f14b8bc10ff07c0845967357c7adc61c5b6c7407ef1f2998734404ad9b4a8b5571c1affd53cffa40930ccceefacaf6ff5abff1733158ff9c22189d534ca08e652eab729b7f7b1cfb69eb0e1adb889716cccb520c3b31ac168db98176808b6663a48a8ecae7a95ffe96429bd07ae936f0514d5fbd98d1845b5bd974fc2523e390a62d59b99312ee1e40667547ea4952db47d3d8f4a71ff1a58cf7d20a9ab2a27f0d446039286c880e7aa40644fc79aa1167fdada1b5b0c4a3e05ff61b46d10fc6649e3208a0fcbf23300b69cc2b4cb6218769225a5b1823d5d72c3de39633a24b8a7e207f1b691cb1194c90891d52a14cb6e62becbf6f5d362dce3b566faa09ffd10837b77101ce388becb8617f395aea003106e99728e629ae88b9ea23bdaf746fceb1fcb6452d6267974b0ded40b255eeb62dcf8620b78f4474e5ae3d56d586199276bc3abe5a1f1a6bbb1cf3955cac9912960b5f43542b9b44dee36f39f9e26d6e5e88d152f5a22b8d8d6cc741abb8cee2fc69d784b3c02bcd23fbd7c977b6e15854a3e84bc007a5e1b5cdc15218ed3415a439a843c6f2f032725c56f16d4b9a8f895cab40cf9535981873aea2d789e466a8600037275fcc1aa2a5b042b1faa1ff0f82adfc09429826b1656cefc947235d4c7650655a2ebed333b1bdc9d9fb4672a1f6bd30bf827e64bcc26e3d3d4706c80bc8ad0b2de9441841009229429ad5e9be6e437b17725a7ab74eb454f51f16b453bf86ba9a605f3f970824d774e47c5309645f22f4fbb199eccc4c9ba9da4678aa283a2e32451ad454e7c4e37aecdf7459f64dc0ffd782211e6c05265c886d6d82a52d8ef4be9754923635be4443f98a443204c9baeeb1012ec37d6e5d999564115d5bde87e0a88f2ec1f8dd61003bdfcc1f1dc6d55c13fa23a1ae37612eb5329787109909b80ca2c023e0264c872be076f5c502fe0ab271428e83f36f975662efead163ec0f2db71fc3113522bd39f446857ee0583c103198e8a30bdb1c3105fa334f9a6b60b196b3ff0c5c72fd745149ca0782d45e8e67138a4ac2ff8f790b90073d74f8ccdaa448ad3e7c5abe35e731fa63ac5073666ec3c91d4cd316df82191dd2aff3c36ce49359745efcc8831e6524965f93cfc0f67614d9eefa20709dedc795e2f94fdf80e217b14dc43c7979e838be388c2ebb867686013bf4aa9e93e8dddd0166e82bc198f2d6b48c8750eb8554d073a661d2588055c0aa3ce5a7cf21201f17f675db604e3db0122f4a39eb159e3bab755c905820fc03b88f7b17d7b218c4987eff329480f8ec58361cc1a0a952d8a18494d0579d6b1499201e3c52cff0e719e687fe18276d6988fe02d3b1ad7d3093d08a7f00daea92354bc8800618b1542140b00db6678ae837ea081e39eeb844ee3241fa18bd32dfa5a2e021736ee35d65b446aa193afa55ab384712079584d7ccc86d167fa58e2de4e7bf6828817f042e954e29bc9c7ae53d40750f7a6c0ec68c4a43148c00f9ecb9a99a490a8ea34d05509f8280e3ad2fb41c3572a7f89941ece5e16f3b27244da833217cd86dd85a8b08e00b7694036c5c79ddd2a2af9c7237a1e89b18d8e061b7d36909dc6edadfe765a543320b238c986eb2d733be60544a955bdd0d3233c21e46c9a46af1d007f96e5a6dc9dbb2f7a3aa08b8be280b479a08842f312ddb923222658955a6d3cd210cbc7000c52fa491997c5639152e0d9922e51b0cfe7ab34cddbcac283e05d030d62e2ae127a2e385bd5bd9efb1d2c9ad0da398892993c1b9874ba3b3e42cc5643d917ca24d02956c5c6cd815041c69d998aa11532f0f7926f356e402cf1bfbebac13273e5402f2746d6996c532aa0d9c07e23d87dcfef3934f7c72a197a520c028b298095383878bec6bf6b28002f47449d649ec45a0cc61cf562f04728ea473c97b5b0b892882d1871b5952ee73c785085f2c94186e39a2afcf5801825d86c1ac26ae10c16e64e3e5185dbe181bf4f41ef59a8e5224d87b30b533a9c15230dbeba834f8c8786cfeb80fff2e7acf546d8719e74ad19c2cfa32e27112e587a7d16a05083908c82cfc46cd076027e65193dd5db6a8bb6b4b8a390b9b6c4ebbc3b47209e1b6b43d832853ae1edbc4ec7d14217c81cbb20653422d2c3a7820b616b13ce2bee47de1222491d804d468988319b125dbb067fcd94ce5fdc0a6696ab849e45bc5f0d1ff4fd38d8799896350938a36283664196e1ddb0857b8d4b2cac2ea90a2163653a4b3bacfc0618f1f53edad2478e2f0b9aab64c84ba22d32bb7c6569da0608298f37c56ce3a64b42338fd2174465ca18165d6ed1a37270abf000ea1ba9969288c17b4bf1948b9be845cad30e6323224e3a2d556980e8d2565bfc3343f42d4221b22f7a7e7c2b248414ff982d4164947201204c4733f0f0e5059bf2c622d04cb79b2acc95512568a34146d92fb29762a8b203fe6f8b18a013d9d2f1c98272f13beb8af4493715f0582bd36a2b9dad192bcd8cc886863191ee62f93fffa1f8b444c2d22e09f5b2ca1e81a073631a66cbcaac75a6a43490f076e7b0e0ebf6485f01d448304b5fd6aebe631cd956ce631bdea637287e7a77a7c3bd9ea4dbaf635b19b297db094e010f0346bb674b1d6113d840f9b93d54cb8714126aed49f9f536fb8d06dbedba0decf105d238d2b29d76a0668aea22571bcaa362a13c811f8e6d96222a75630ad166f77ded0c79004b8f83932d8794582e010a66180abb80f5e8d30702457eecfdf17bfcf4600691a411eeee187280fe82f76b3cac48e5d98b9a2ec369befb61011fa6da766f0b8a2b5a5f9f2c7ff4b635cafef9eaabd710202edef340c3fe42c3a0c91a4c2e680ddb191948d0d678fac592b1d0ae73107fd918cd93f3210b326814e45735f90d32d8760bba5b9e76941bb69cae37289224995377379e4487cd628378c338defdacb0245ccc26d5aa0134003122847f4fefd8be19e9c56e3c05487ab5f76c88b41d8e6a5a89b897446011da1afe3cf08c377cab28bc45c261c2ee9021111111c0877c1f6352e7fa4eb4cd074fe621e349d5699902e59171c3a4a104b3be88aa35d88dae47ae7bd7039e92fd2a0d343fbf475f5899aabef32467eb40b212860546f3c423f3bf12f8fec77d321761ee37a009ffa13d2312450864b8f53def36fef4ffb54817a8e72696e4c2ed176f37d24f1b5fecf8955fe02f388e4c5cd8012038ccdad137c4d39de1fc9da72d68b0fc8ffaa77433de4d210ca1ecb4db7dea29601592513bc292bdd92cc0ae1fdab3f25156a798730bdab27a7bfdace0da9c12f969cbb14094488b618c19473dd4fd91bf07978e9664806d3715057275f50f07713e1e78b7844a2c53641bd16027eeb49817b5d707a49cbe337e39aef0a263998f30d47e431961944120d476bf3584e211e944c1e4136a829441c067a4acf948eabb7e9a13e7950b5e71eb6d19fba32426b434d22ff0d178b2e631af49d677a605ccd294d9f3d7ebe4f77a3c1ab2c416575ea7cec538d4168bd4611a51881245ae0f83cfc85bc7406c6addfba29af0fe31f91923bb818c4fcbbb9e28eefebf68b0ec3d8cfb9c096f48e657bf931b7a11f12dd464d027226da2a02b10ea46c333c1f9bcd7bcf39f5246e3c04c3731bb40cc133f96861b764222024dc3a32f5f9502e0eb407fcb95bb5dc33f0c6091ae3a5d1a330ab02cf55a97de79623576eeaf875e1022e7fa4dfc6c100d4718da6b2b17cca76ddbe2ecbebaff7862cb3e71f58b5eacfe1bdf33823ef7b0fcb947cdf66916afe749b8f09e436cb20922f696f18cca9ab1903d150a0b65c6101b700fcb8f04098e89f537f121771720560c357daac0021373a510c8e5e63e66f1204a35bc0aaf39342308b99394ddb17ff67777a374356c1aaa3d729e22d3c1b5b8d44d84df3721224610c649f91dd44d7d2f4d76f3e6f46c3be9334ce4f923814003cad879865c2bdbd4002d112e9cf1b7bf49bc8ce4c97e46e7c457fb40475e6039ba76834c52089ba5252835d4e964d227d6ac9420f0e48e8a849d9b6a0a63a82a3a230c04879e1d9720441199384fb41a1238a4eca9d09da722f917018661ad742eaf1e8646c7433f6236fffdbc70a88cfa0aec45c31780c05e015f061b9be086e3a30cd0696a7d7df2c7c2e1f9468f64da2c67a30a47a3e62ec6bac7a850118c6455af2869811d19e203661fab8beb0f1bad2e26f39828e15bb292e9b52c5c4c9ed2cf08a23d7bc2a8a549144a3d9e7c7a5fc51d53ac6df4f0de99a10c5ec0da9221c7acf7f2a9efc994f2d1b49f710a22566e4d86296baba6af33221bb5a507e79e3128563d40453882f26f9d6ffa9c8f344626775b50ccc30a6095699e6213b3f02fe0632af046cdb9692383e49ba97578438449568d0c095d29469226cac8443044934a3efe7172a0e0ac9324e77486b77c8afa41c16b354ce0036b351699685f9286a101a1589970ff6f4124ef1db54851702a55b8209f2474215a9369feb7ce4778be9ea2dd0631c44aebd88ffce53d3d1fd8c1d48f3e4e7914c1b5605e2eca2df4caf47bcc252d87f6ecc8a4d28bcba73bf53e4b727905f0ac8ea258ea4898a80d927624fd4e18554ad9aaeda7b5545625fd420f8bfce9749df37b0a2bd25ffb64bd61c31d704fae0566e02c14d43e87fe980ab3dd1343f6146b0726df7c2acba968473fb45cacdcefaccc26daea7f11d157b581da40e56351bfaed287b44a6938da4791c9a4f7e30caa78b725e4f36e88431e2d4a391d96994219da8fda8428b8eb5745ea0fb20a1a9d6e47896f24fbc2f4d92950b553897bf2a401d5a967bbbc2b6836c30300a51edb997e3e85d67ffd2a577c5ff118c1d3497cc5d37f545ffa4e0db33fab27d6c0f2f4d109378d7fe6eab64fc60a71518b0a19388dc9345c9e34f94ddfb54bb16405eea29468056c8c1ed334349090a74ae660991d37462519cc7f6c5d4e10f4bbb04b2cc83560d895e0ae5a7abead6cd0fb9e60c066410c3d9194a38b840bfd99281fda03a14ecfe117a416c49a4c9fafc5477154c27a98aa78c35b206822ef4c9d351ca67f994b0b7760d5219fd29f3158a91862cf50c88c1a2ddb0b6296bb4b848aff86881430f26c19ffd1a5e05ea611194aaac75df844e4bc3e3831892c919a55e6ab7bb29ba7fc94d9797b5a29d36d899d2d0d9b276a6bc622a030b4e36107b548b5fff571bfa96fef697597a1458a243555ff614813a51a64799ed5d669310aadec0c205d4d585c4b217c0402a91c4fa9a1643d1f2bd927a1f537e38d1430a13ff453c09444ab7a24a81f87b884c90bd3814b5cace35b9a6cdc927dfa0b7ae0e5d3dddfe08c7baf49b0c886bf3322e4dbeaa7af30a8bfce3364b71ecc135663ffa7b84dbdfc1ce0bae241f33708e98308d6a28b4cfa2d645b5c5defd4fbcbecfc04f0a98b1c003d431018b0745598f65845f555ca1f8482c43da986a525a5a991d70313bc79629857231dd4e01fb82bdbe7e02c05d1cbca7d223999b05d615430343cede77ba1798e9baf902eef731a84fc22e1629e16cb9e005e316afc6301b8689f1399c0321fb99b4287e83595c3e1e88723b876e62d0fee4af73e0303e61a39aa766686b423186cc51ec0183394e7c949812c5d8502267a3ff68ce2a4384a352abfe900f8413fb7f4c72cb98b13b1d3c96d5cfbee48f85954dcde848763576c2506ac3c4fe823585f65f003f03144daf514dbd0175a6471b73652f449bfe72e030da95d5cd31e0816f4e83f7410517a54f1112c2dbe762bef49a96c6f5de7fbb24e5d7446239a9c48910ed3ea3d568fa56a169cf530b52f81fbac738c32109aa6bc7ddcab6dd2d900decd72fb65959ca2f4c2d833b9351d96dae9e6fb4c821750e3f4b36c03e6d17d79819de187a991476f122889f05af6ef9f856887fdedf431752fce47ea6dcecb1b34fafc1ee1879c92b25a64cce22c6f5c8ede80d6af7484269a1c3a3f3d56624db3240f483000520da22a1303a962252980f2d81d36a9d21ce5e20c4279ec8d1f28935c56c688c2855a3aa4ff9cdb1c677b7977b2ca8869ab5841d69123f7c78fa44f9f02d8eb9e94df3497657b658f83ea36926ff44af889109b6bef34cc67fe17db3a9cf2cfcc245b9a9029dbb155a52200c43d0998db44e3187ba67239bb7da6546eb428c938dcdd97bdb78c637ba1bc0b1f8bd1cc567c978f4a4ddc9d2e843f9e6e06509ff51e887bd136e04f8bccf9aab943d2ae50f91ed03c7921c1b95fa0c640de37ca9f8831f42351d9016294df00bc3d615ce843bb613523ba97713ea91cf6c47697dbb3664401c92f4114372c3f83265b971322187202bcf51bb39a8842e84f3c2e8ed074e990cddd1bebb005ec4deeab10f2de011eda62454b3d8c5acc078402a39fc10bc34b6fdbe24e67bdce9db57232e4d8f8185f8dfe064d101458fccd56a986b2d25da9ceefb4456537720530952386579c8ff0bfed8c8d6e5fcc3a7a627fe301fd7a9611b339ec16656943700aa50f13fd41d1022ed6cc0bbd49e81154572d95bf8bdcd29f5b0a66b19c06b6b6b4e1cac70a744957fae3a8f6ac857c0f83061f4a3ef2ae79be4518548a03496f80fcaf0f1173cf438eb10325ce1be33d5b2c948de5b8ef46a82e4bd04d43b20eb6a2d15ffe503e2678c5831999610b63dedef1c0fde049a2735bc473d22992bd3fd024ba802f3052aba82cb00dd63b110342c143ac90444a7d207ba836e079336991790568e73e7e9488b248e6fc5e2e09cec9c8d1bd40aed7764157ab0a7c7cc5e5695f85410f0b7c15246d069b7904aee687722f68fe8b63e1bad8fdb68ef9b1bedcb224332f63edfe801716da60dfda6b3ad57cba7407d4fb614cce70130f1c88de1e20905870e1b674c8ed0d5150095f151a2cf963c0562ab027cdbed41dabf7e29888e0d78a863bc1c085a44d83a8b8b8532384d84aa0d18647cdccf6b92121a3ac4375d799badfeaa7799defbad566d04950f52a2196d85dc1af171a1a1759fa5057febd4256ec5026f677e93bf536c0ad1667f2f3292e2ee320ece280f99d1c5c044c9a6a7505f05405c4a662663aa95e9a2a6cf7564931b5ab972f7fda967c4651744eabbab7e7530411e7320688b686f41a0164191917d3fbc9fa3d9ea89b1a74e5321b75b8ee708da03d1f6fcd3199b93bec06c4be6479ecb8e3d5952908a8273d1090ab1199ccee3a142170e1c3fb03dd79c8915e6672534afb7e24f614899d35d746bf141dedec06db3abd7160e91c832cffe75f79e2a944dcd22875d1588e4e1efc25159e13d5253cdd0528560e6104009fb3a9e7f23b2643ef1f598042b47fa5644232bda323e4942523987e5ec0a2443f197aba1ccedeb9bd0c0852987745379bdbc495814e8375f632d89ba44019d4dfb49a9e2edaf7b3de5b04dd17eea6543b80b4bec9bc336d40ebd78c19a144723d12f6f6ad5c82f29f7c72f80482b2231f604775cc73fff241d756edd43fc531d458eb40d324746ae0937f07f00cb955d98c08b3b794b928057f37a790d3ad39ebca28855d94448e69cbd9772d5216a2a83d5de2271c1e88af0342434380fbd6c8742750fb531eebc9c8a1f574e76920e086506ee16835214852b65f2540973ce3c4036b82a27d21e97348b301e2411a42fb94ef9d6a25fcf1b1bbc40f4addadd655f71445bf3281886a42433c8021ef4f7d82b4ee6af5f4597a7cc119b6fd74ad0c907d1f9b08f27e6aedaed04338d8c1fa033d6a5f665dfdf164ff3622afbf4c68476f98bf3690b6ac80076270dd0808624b9afaababb462de24b59dfaf5f181c9b68fe1d24b248050802ebbbffc0634b9baef0ab3f0141a2f830c670bd562111094621c60100ad7d0086994259f5bf6e716e2b88b429fac22e9aa9a22eef3ae6872a19dae33d324c04f7bb32e1428149afb9571c1ca6dd094f8e4d85ac47ab7a54249ddb9dd578f24f9915993de6b445fce312c5e80e1034031f92b0c0693c3f281095d009e428b84e21c35ff5b806a24bb07854d239ec83357daea3a9e5bbc8d737eef6eb54ffe34d3f251cab2ba58a0a8e68e1de44e93c0cfc5ff93637296def8597f0b16a17064f56b43d34f80d64e20c5017b498839da557ec44e2fa491ce502542c1c69b742cc9efa24b4d1d828bd306b5dda6a20b08b7a1e805c0920a3f0146701983840eb59cf5c5927ef6fd94f9974439ec4c4a38c7be4847bc0ec707942b3ec27614b246e015d1fb81e02734039e6a4df62ba10288dc89181cd19c6ee01bbe1d477679be9ac786284d9014874d32f3412a3d1bcd0d24edf46f4a2080fda909c5150896dddc38d05fd3a8b3ed6f8dd87652c27d192f5c55469d9d23cb9365c293b7434345238af13abfe853c4d54068585cf126b7aae39f93aa5cdc76bf1440bbf36e78882895d42350ce3ba02f24d962ffb70f0ca37664b460f822a68e9a55560d64d29e048b51cb9fff758075850897fec7acd18f2d967ae6fc5ebf39f17fb7315f15e4cc0f4f8606ff88ff4e7c2f4667befbb7a2f2e429b19920f769270cbdc9773315a9f89d535ed0155dc51a1fd7281e66a75dbc1aa0dc485755d52b87f7c962c5aa50297829ad5ca5b842d29f5b9af27d3c5a3f5a00c1b2d323d03ed1fe6506a9c969f0b973cb0c3ef4da7932fdd4cece0404c17ce4eb9f76ac1da36fa8e9245a1b4dbf9ef70e6a01f50b2a14ebf96a5fd05f909c6c595751cc8a4dd6b0677477e6fe702e1a95566eca834d15737795adf88fc00b7d8b7117a48b761193fd4ebad01e3ee6d776f62adfdd813ca9edb5cd25bc792d805126847da439ace99ddb98b8b876a95a72a261d34ae295f32c7fa3bfba336f878a93e457730fe16cc155e8bc0a3ca4c5db4aad0459d1254c0da6fcd20650f6f4021cdceec89c9517e47a0e25daebaf5501fa8b95efa2837ed94f8fae558914a18f0551847f3d457f6498f65fbe2c7bb503634f040fa57e786bfbeae3e62f743a0ec12359391a1030988be6732ee1355cf55870887befe11457780818c81fb2f270fd49c954be524a6a78892e2e6d6158763569c6f617bf12f080f2f7cd2620728f04b3110a12ec77c79c78ebda6a342b69e8f4eeda585d7c0e74c39f36513036f2f5727c17357e56b3601b1bf0ab65abb8ab13781661bc0c2416e5bb5c3820b6b187d18660c9e44647f87dd493fa7b596ea4421cf5eb97114548a7e7526cc49bd4b4a7c341cb811399b63ad132bc5a5abc7171dd99839542fc2867428f8823fbebea25d1b8b4c1d7e48497ea28ab65e3cbbd81d97b7c9e84830803c2427f4a66e18d8b70e78966a11457be8d23faa50ea63bef910d3a090b51ab791e616ca2c787c5944a1c139d35aba74e8574f6dd1094ff8ec9b033a4608234d696b1534706bb73150d09f9a6387aef3accb910395b03bff2728802245d57f6e166b1b4bf23892281d93fe49aaf4f02abdac375c86bcac3c4b7eee3318ad26f8a4fa1f4450e43e0b45bae47cee142415c558f10f9b9fe4fd60a726c6f32a9849f6e80b218acf4ad403a4d52fe6e76be114150aac8236ebe145da581ccbc867c4a6e955a844a69de28ba4f99d95951a4dff4ed850a07de2ea5d90345ed7d95090000875b645ed9a074915675b9da2f4d33e45c3a1f369274b1b574387ddc2ce600dbdd320478340335f00c14af4c4563be55c5ad82a25eb351d9d6105aa695e477ad22ca2cd4ead4bb588f59fb62c8daa61415c40f4d3ed3964705a1b1f7fe250c043a6e8e34d6c292eca7988508d8fe6da7a6f9e1107868c5eef3cae132ddf4ff0aa4271e9464c12f82e6c3df7043743cf4df7ba0e498a85976094331dc768c31cdc28832c80188640c94caf78ef1cf405242ba5ccbed8bfdd4a578a357ee0371cf9b3b72391eff996d0c5f86c82fa9b5500610c95439821a3ed0e8caaa3192f84d6ca91c9370c0a9c5113fdc82077ca1d846b809f69152b9a6dd3c2ad9dbf115437c6ebaaad25a4f69b84371eccb86ff9e35b7917a461c9594aa3c361ac389d28c36723278b7ef6010ce80e5270d69f4e465b6e9eced4ca2be795926caba02eb64a4c72457a93b6b4b5ba373eb1f36e5268350c465028719c28645c7155e7c3f6620332b081dc4cc36cfd546176a90c64214c1dc8b91a14f515cc026099e62814acdeae96689de30a1d1bea40e31c63dec8ecb29f23532956a66b508ff8ccc6c701bc735308c8bde13a6e9027b93237e61d78bac5877c26f309ac6a9470b52b120ef9d65f74e19becc642f04f175af9a17ee42081ed53a1f6f99ff1ac223242be930bbffb75997f77d1ecce520cad0ae65f85b856e07818b119ec825a0e40875e18670178d54f44075b4fd93adb2712bc545c64f32fa13a597a81d4bbd6a6e966585477d0c498ebb9ca92fc1969d9b1e728c357f5dc9284229815e29adad42f00dcc5f393e168ebed5011fedb5a1c7f58aad76435014044c2ba7d2e3e57eedd2a00d2125765ee804a5a6581485dc9260dae3c99cc6fe425ba3233afdd89b73241b3ad9533dac7dee0cd77af5f56619a901ae6985787ec07e86810b5b338109b69c3c6f1f32a17d2e0eb8b4ae064867de767c160c81a6ab6d048c408674d4f02ec00bb6732db00f7063cce2eb702af1ef62ea1cf4090379729656fbe103b80adc780b7d5f8cc98098e6a90321020a6976ab49f2eaacd8172afee6d970b817e845af7898ea0cb473339c980d1b3a4afb954ded858811681644cc07554ad9e2ccff57aae5281e471c2107eb400ab16831f200a7ac2443f1e29f4e6de44d1ecfa19420c0f50c96c932af8578b2917180320abea102cc29a6ea4a0dca27ff138a0fff359723e2d58b6359a5679ae5bbe53d6041587cd31591ca3e1566355904a264bfb8d2ea633606085cbb6a9b912bab466491a62d712aac7911a8f85907baa7713baaf7eb61c3e84c64a13eac7024b060683bf00098e186c12451130648b2a9ad66eafba0fefe632e274d98319017664e10ad30634e848fbf139294c5a944b94ad188a0526db3b4e5d8c03a9e3057b8aa6c38ffd23db616ea6e0654322cf02d1b2afaeebfb85dbd7dc13b08833f2d0d4be6dbe837cf393515067a8dd8140a279a1632b9d9c830281f22a03c445695313b3eb6aa0ad580763a8d35a2f7027e797b0b595409c80d7292d18faa80b71b0a46d982053705a8feb66d5a5d7f4eeb5dfc3a90f78894f4fde6e19dd2308fbfe444e41362072970991e68b33b572cda056f6177ecf47716489810fa504091690df767a30309b2bc305bca5b8ea0ed86152155dc56baf592bb7eba2aea8d84260d226371ff5f13cc274ee9d7e22a54900614d4adbda7a7493c4806a60e58209938a98ced0c7dae1fb1f1c1a51885c8d12bc69bab9c93cbe06498e76a326b2791cad16565b74a9b5d2046c34677d7f1ff5e64d0679fead6efb0e231c04503da8addf2ec2fd34b1371e5c2983226c5253e504594d34edb6039c1a7e79a9656e765ddb4034ed0e3874390a2b6801b4c00c5075ffea1257a67b3aecbf2a45fddae55044d6457d583a4d505d659e5daae74579c97e7cfe39ad1a938a2eaa6cbf4e62db87d8adc8ead7d91f8577c4382b440a9355f98e87f9c320f3d16bf4282b2581cca6e85b885afb5a0ad30b679ec81fcae8ed1d76b08d6b6904f0961fe97d91e8d3881d370a9e1a12f3740de0340c6f25f3f08e42f625fe70a8bad2d47238575d3d7f4d358ae81b65e0d3493f44008ab5217c7c4ec04447c64b3024d45d099ea1b2deaebea35d1f6af43927fe36c3f882fe4ef6056beee91015f615a202aab716009b2cd4cc39f30ec24605a0748e977323a9eabae86630820b7077b7419a7ad1cec795afed9bc2137e82914eaa9935d60549957c3ab266dd66b9fbc08e88cb78ad42dbdda62bf5c9f43b908e288f125f2bb706a0e444c33c546df39379383cfb886c658ca10b97f0dfc4689ce829a14378006a70ad49788973a12db90c0d627b90881d27bd9903a631777e12a8c7cbb9d8d1c4e406769df44504603e9070c1addddfbb6e0e816e7add7aee2016cfd86d1940424988ace925cc122537eea3bb8f4ee3fe1ebfee1096c8c6dff6dee4e2f28600f59109b9944bd6e1989a46b50bd02655152058506f10f8ed45d79cc20a3b411251469a67d1888a245e767c4134f189ffc4697bf4569806cffd614d57e154bb46393dcaaeaa7ad590516c221dd8010e32205337df45156fbab0c4cf277785993dbc7d1a9855b2bcca4a75fd6a9f65e997bf62feeacba582c3eecca6482476215d5be5540f3951119af54017daf14cf30db347369e91d2590ba576c4a1a1eb71c0c88e5b8b9a583cc7b5b2468a53d60773bbebe68edffe009fd48ea360e43f77a89238fc44b7fd87f2d0d4a8d5d91a24a0ef0f2899beee52c520d863082dcaed70e6c9799b9cf146ce68e57bfed58e4c634965f279d16fe6874ca070ca9e17ab5b5b05229f82e6253657abbda6ae2bfbbc6ea7c8a9488d021f96eb25c7c44ddffae44415f536ffcdb3407fbbbecfac7c6b6dfdad376b39898d7d6756304ecc36829ce82ae11da52428f2a8fe0272e548ab1a341d3aa62e8c8589fd4fdcd3495a3e553e3ed0296a426aca860fb9704961d0d9ef12e04180d2e263dd7fc0f8141779cc36fc532540e38b16d8b77ca061d577c6d54f142818dd5654b4a363f548b8bcfc010014602b20a24e2c2a7c73400a52cf4b9bf894b9f1293b7830628c48470c478cff03a94498bd019504fb28784d5e0d5157cd6427d552e2cc8c75b57df3c10cf187ac023b29def7988f5ce2e5129ee91d41af0de5e919eb62f5a3524665002d35ac12eef820064c472ca50e1da641c312b98ef39a6f0e02b4d61072d5110aab27b8c72f35542adbe602d04454e58c8397ddeef8259438fd17af3ba64beacf327c741c3390c16cf054827895a0620446ea23229abf1003bd9019d8d4a06519378ec6c8df5babae67258d7a93b06702afedcf658bd159d567fd4fda4499f2f7598ab82c54c9d7df31e385c12972add1829ff10941034be7557dc312d33d7c47fc3f57cfeb7521f6faae15af6005e01ad8475241c9741d92b77b6306eef2975e61486bf627087fe801a71f2536d12bcc58670b10025d9846ee02fb34a175f63fb3a86702bc7a50b87da0e169df6b1520c8cea799a4733777e0cbc8f9432fa9b5c6763b278edef10d9fab699e2f771cc8f02eb23cb51db6e067553bc336061ca8b8e4aa736bf9bb9d337dfa938c785fde6b40c5dcaebb0cdcdca9da77f21a9f71caf52ea8aa7a72f00d15b6b150ba6b0109591ec7e0afacd76029199fc60db3ea2589a5f9edc4b28811deb8f94e0cb6931dc71d9f36abc0b4a28157596ab111d96ff1676bcd89ea42214a66fcf88219ce59b6b3d7955854c78f93f8bbcf0cbfc5c4beb4c7284e78d6140d7a63798c4f99c39f68777c63912f7fb727d0218fb9a53a8e53ad2761cd2f220c515217cbaa566e2b6f870d4577713404569efefa9baec3f6ce8576f64b139c5c8bdbc8368f0c540d8da06a7add4bf227a2cc4c4b1ace0b0be26a8e9a2f465512c5028e8572e6126dbf11003bf5ab3bc72a10b1bb3fb9c2e9ed3db2025c3ae9b3318ab16caded2c19731f6c5f721e4dd113c48507d2b8ecae88c79bfe0b58156a376bec7c5940838ace2ffb7c760ed995a3924756f0f23c99ac47928544bde707e916e2295aedb6c1ec6d7acc9a623a86c31ae998a31d5f59f37e2d4ed59183909eca836d5c04c14ff1df8952b8d3ed2d09f9de1398b076e1838188fe1960ba897d03801dc9d3a32f5087f6d9942a097560c65d56bde5b5b85ca6854013d5a0c81b1619de726d651e49ade5b662935cedf2fe40695d2fec68b791285ef7d7a77f16664151761491e2ff2e5e7a103c1243af96c0f5dc274d3908936ff9d40dadc45e290f94d4c2ed5e9cd09af24f6f327b8fb9dac9950bcc2502987a7dcf49ebd13468c1aa497ec85d3587949ad61d89fcc76a6f49d707078cf1bd0404f24acaa8dffd6083d4da1d8a0637ef2fd3c1b9d343400a10936c72c197b2df35c05168167a936e1dd5b31b0a0bc6fa7d01fab80973ca3da15afefbb6347f6c0a89d3e77ffc5cde9bcf68b6ace8eded80632b57b5fab8b0cfb724b0012b6d3f9b664adee190578bcdd9a2231406a63c33914cb8ba190c2ac35a17ca6e285c0ebe725f637be23b452755c19a11979478b4ad1a5f5d79682e2e143fe22d56cd6abab83151dcaf9403176c4100edfa6125090042fbe412ca702d9cbf1ba5789456d39cf7b86b568b759f64ff531bb9e8bd8b7eb8478f753745d987b537395e6393f2427c9abface2bf73a721096215b790a824222ee622c06d70f2627bdd2e57d76f44e60ee4b02e3d74156fecc8ac05866f0ef2824df6184859b8e4e5a2575a438a581c1b2275cf08c92971a0a8330a9c38dda1a946523df17d8c8e8bda67dc2f5959f08b08e5de35a0ce6f46b1d58fce4f322f8b24f4e20f3b8443c47030053d1eaa08fa20f064a54b59cffb470be56594e1e6dd5f34e923b3b8ddff1de31693b0f92fc8075c1dd27b800e1800b11bdc7043832cf4c83e28be56650c4348b65c7a262d3a95cede087ae4021bd225a86c0e55914c47f57002a48f7d6908ac3d7f56900cf039d236efbbf774b78f089e34dfc0244020b5a0e089ab3b732344cc2bbb253a13d449cf63e607898747db323eca2e3fa09ef40220775953b1289c10594e0740822775b3dc5c69aa90b92c4f2c8d84eadae756e0c467eed09969d8248f21bbfbcf4a3a360d526fac39fae2e28ecdfc14a5b2fb195eb7c5480777e585d3584ba587d37184c1e3dbfdb1c35e48a5f2743e3528687087b146ab164875b0cd6256842ad413a2f0eb40da940abb3266b4322903737a90c3d2aaf856614fe742d62caaaad0335108da56b8d8c67447160e09f371b61cb45683c91b687bd3ba0034777101db7cf78a9c20a274b2f0e0fe0220e0eb0eb91b15b928172a2da566d050188dec81ff5926445f33029831c259f7e3c52f8dafaea29b7c2bb4fb25a871c9cc45c74dbde4ff32c847f76c098dba940cfb98c74b5e3d8a82e818c8fad13afa75604a4135e2d9086c7bd27c5b67fcb60b3943a2cba251ded09ee195f7aa9c7b21e084658d9a40084602389c14b52dd04bc118049f1e32afde226f2ac4e4472e48a40c9a1e2023fc825f124b764bc4e21942bbb2e75be7b29f055105d691740bd53b821f7cbacc54660228ffd1e23e63d9c1b25607172734506fb98a8f7f3d3c19393edda55050aea0b278de90de1d485e7c7c254e0e98f7abed1eb1c5dd4e9e472a724746c8035f096043d0461cd24cd087bd41055f705e38b6e67454a983705bbe4a9f2cc1acca026dfc75c90e850beea8c5ff6de7090fe86bab9c87bfb16b7330d5fd32ec8f45a8f972b4f19c0c75d5dcbb7caaca2c3a941aa63aca1eec8453cb089fb85bb0ff1340da6becf608534c7260af0b59c87a018c7d59b42f2e1d1fb9ccd6ecd35329c077b1eddeebc2e5135f212bebe616e3dc8f7739a921a9cb92df1af0ca052e6d99c4f8c486df8a83b9041a7c0ac65d1ba94406fb6c5ee5f2b77b7e34c23536aa2ff3c2987c48b7c649c68e0c9e70dfe6e77e5d4c6ec8ed865cf0c2ffd5c93696031b10ab030165642495c2a8e575a88492cc1d42cd50213a2447eaf63d908f3b2e1464b2d322335669a40707caca1c4502d52755672850fd6067a31764cb27850a6a18be482bf12b374768585e9bd9eb4e069dec53a22634d9c2f71b58f272a613e5c08fb561ce240c681ac46008cfc459ba38df8cb1b1b5f0fe2ba351925e3aa0864506cdf0146aeb7ed8b5c99eef26d2e46de8ca8cfe2aad99a8b16b7d31bacac9cbf67e4c0cf2ee4e101ab1503ffdca1f9a48bcf2f7f44eccad7b535387f307bc2c3c39632b6ec193999bd281bd71365892442ead607cc98074888e6852a6e764cc7b40ae8fe9f405109684029d2c4098cf9885c1a14dd9696535005c5db9813d80ad5cad5a07c8157add1b0b9e4ec511c13b0c28ecbbcb1273837ca0b136b338d8407b92c2dada08fc36225e37285eb617471a813815145de431c55329a6aa275d73796b1fdef2ee3548b067ef1aed0ece0ffe2387db0b201288ed678e7d714637a41be97596137d463c853cc9cc99c18a6fcb520eddd8b206e613e366b88d49021edb4b769e6a7178c6c06f07f8805bc6be56b4b43694fda5ff0ee91e5e46fe3060184b49d76e2a510f171e06d70fc9f1bac07bf3e2a131c269dace77acc1f6dd3544e8239eba1e84cd3be18a36c1826ab37e4a528abc53e3690ad441bd3806b9ece0044d3e6436bed00c3724322e741a8870386ec79ae9ea6f5be02ade107e974906910a5d63364ea4706b3e7928f5d0c37a49d47424ddecfab45791eed252a9d18be714b5004022799c04a6f870f063ba08e58360894d5422970b80cc562adf8351c6903a9ca54812a5bfbb589f3bc8e07b4d24c098421f04b7d2f43fda846c7972aa495267f4cbc6d3835c64faaab195019b6ac09522718caf1b2685d8dba698b3a879f75b9199014614d247ce77d68e7ff2c53af7282e3805db827c1bb614ce5fb5cad123c6b2e963ba79e0546e7f6bf3fc00c9a07d693249d549955884d11a8ab65120507103b234f2679cde6e8a73a236a6a231d224a6a7f3eb4cab70e746933abddbde256d672e12c2560101d0ccc677424ed6cfb7527492587e492f9555d922111f5fb59c8956ba42ae02582e0def5219d8768dadd1785afdb64ec05cb2ee68747432bd6d1f60f37e460b0a473c07e618868efbaa4b15f4d2c1d1a6d6b276398e98b22e6a68be14c84dafbcc7dbf350a83eed84e3fc47c0af489605e231020ccc2aa9a198c5e25bdfb412fb9b5382d16d8858899cdbee0a3260a595491101ed3530cfb597ab8344c84a2890867a3a1b840cd43b7588e2e528d974563d957b32460c74e215b255d4e290bd8bc0a577111234042563fa4966b786e992accb324562e58b3de96ceb3cc7ac5d965c279da30986c315c0e41603026da96e198e300d9b59c200f98b363c389d5faf441fdb59de1a83e25f4f4ba090462a187bfd111c069170234a4069f8cf86718a111f2591ef9685bddae9e7e58b48ad4412812797d237239723063f5f11aa2ca66d1ee01de16378812981633edf50169805439501b7776ec8d1e98df180854bab2feb887b7f73bafa4ad73ebb8641934b545033c51ead2d30f41a1abfa3cbcc1f00fd7fdcd056659f9e074ff340357aa8e021807d880eb0e2d1b8b5e27608870bfae1f7852efec010068821b80e4ca4a5620bc5fb6eec1a7c93c125b27ba5723395cc9f5f88fe950f0f7489c50808c2140394f0bb4035617573d1ab365162aa1765a5bc1b4e36e103794c07651cde6ebc3f469fecda85b4f012ab00f3949b13ff92180fa6fa54409ab5b61c753f6f230bf47980b02aeee3cc1fcd14121e1d7be8741fe2fc055ab05538e875beac922c18e338ce672ced4697c2cf0f24650e5717f5c7a2d6625ad02d9091627cbcb39cb28a1d394ce475bb0ca648361f063209ea57da3f52940fdc63e6386d972066b8ba07473d81d535247516e3e0a8c341f2831f306f65ca69e11ac0f23f539c1de2522c0d36de131a93b168c39605f2c14ae40e999efef16f815ab583be7d18c1eb599f66dbb80c362f9b3bc630eba585e14442c04f6c456e66e461e45e40639da266a93f09df4e71c5e51c17942d8930205c4aac6fdd555cad7f4cbb08047a929f7b740ae4b375267bf0a03dd8ef9abc442eff3f9bc9ac3a3ea1ef6bf78dd5b4afa9828afde2269e401dbd2bb42f594a401df3c8d25e211503932c7aa643af7e81064185c973f92a39530b9ad848e1a9ee476f4796da5ca42a5560f5a8396ee71b733565b8b74ec62914b04a30c4b6485ecba363a36333488faf947a5927f4ab20fc9825f6e12354e4d8848755f974fe983807031c9c64293ec7a759ac762c4d610c8ad7d034534922a87ce8929d074383b93e7863559f7f28f5c5fa1f4dbc9ba92952bba108953b9b9a0fd5808051050aaa1555e41298f9b3be376456e5f293a638a31c5de8966bb0cd14147a72e7f13694f40ea7ecd0771b015ea72915c064c0aa71b61864acdef2bd4dac9de830ad56ef07bc4045daad0858b75754059cd32874de05146e53c45b672f05937a5d79a5264d7a600f38a195408fb09a556b49b16bf7c2a98f0f16dbecbbb6ea4ec9b1e5849ae049b4e383a19424bb3bc416164f4e7c7ae31e5ae8e81c7d68639b17fefb6d8dc381a8218d3227d3fdc10b153e0763292e6c18f573d84cc4fde8b91870b86166f988b1216f72b5e6155798dc759129e07b108c3ad392b63dfcb5d7d5b8df7ff154f57e507c7431b51530c24e7c92fc276ab455a9c1042e2103c935219b95bc649905f61f47ca2cda52b330b9f76bc72375e3e246040bf9a2b0cce207f6043febb119c1a9f2ff2d1c830505c191d24b4fabf73756fbe967b0fb61b939419513c438c2aa3d0dd67659e7aa0b63701923e57e5917813af44c08920b3b5d0483bcebc3d7d12ac494c467d3f54edcda7040319cb1be115b12194615c775d3274c4b6971d1641ab6be20ed8cbdf9c84869e8f16bc3687094aa8e506c22ae50aacb1c353cee85f483cdeea823ef35b7d9a698a1e71d65b1c1fd310f4c8b3ff5501acc21e48a3f5595542996dda6d204ef6013cd97f2a1d2afe6e6bdfdcf6292a69b901fd6604893d7838b65a3b2c0b55a0d9a049aa90d137b21a6e66e36e32037c03075c4ad61ed8f705b07ec4a18cb55fb477a490c6b94d0bf20ff48bedf9f762711c503832c28f58db80fed2dee18ea95aded15558d8d6a4edfbb1cae623be949c9f1a0d079cad29ece2a8996f4096ef2492991acdecf88428654eb4602078b791efdad8c49f8aa121abcb7f2b7058f87146533c56de092a1cf7679432ee3001a3ef1fbe9f9e208b6a57c5b5113baa242899451b52736e0abfaefc1bd1fb9423470ffe53bf58d6fbf23675be6bbbe948986c5e3f88c576c09349a0739719d07f4cb58c5b0cc882bb34469d7062b1c4d1a5b4a569d65bbae4eb351ea6bca757aba4d617532030c3322e985ebb344405b524c8d3d3920a0f5f53fa94e606229b4136c65a45b5ebdbd155335c83f6e5fd73961b8469330a1fbe0f23c0481b908449d63a4594b26b5f052bd0801bc9f15d73eccce9d6eb399f2029481382120b2a370e4a19e740d560b90c4487dd52e2a670757c5bada079ee9f6f9f66bbf245ffe350e4bf1fdda2d5fc8b0abee6aee52a350760c7136081b5e8ae249ebfa93a975d69c6158abaaae27b3f67242ca292a8a58ad7379d13c8e19b17bbdad3c65fc6c635d91a6a7d8989e44b701c2b339ad0e9e11b4a0fd3161ca1168494802c18578d9a7769ee06e4452bdf7b5a93011e618b93823fab3dbc474b758bad77e61c84561ab21eae1b0c6e14efbed9e53c9ccfd33f5ada1ad55476997e9043cf704cbff79807de183d8e1d0b6276b1499ec00e2a1a3cdae2be8ec97af714c47c6ef7af815329ca4267541de3719e0224e08a70bccda2e799a7fe34f00bf377427d837dcb151790e219e19b07dcf6b3b4edc277ec9751a923ec4b5d2ce071e05f6c1d15dcfc9070c0df972d45b797a8229a4f653d35c273fcaa820913cc8f9b05c9412cc8a91722bccd3960e66f95c0a1f9b92052412035ba9184bfde5950ec9e60106c4c96457b15f3760e67ec41d48977ba0288f9ef0806a267f9b792632e2ba9e6fbb5dec2b52e7e3fdd395ad94cf6d6a342aa4765c90e8319c97b5d7c4aa3adaf00840a4bfe36a9dd6b594db691d1c571086f9fe3cfcf248ed191d16f9f892c834caec0796e03df85ed16f726393e4da4c4ab6cc172ad067fc41490f8d0456015380f1dc93ae4b1be599c3f2e8738d63110126edcc0d5b7744c33f6ac5bf0f6ad745d3bae94d0c5542840b9d133c8eec95d27ec48eb245312d5d0d5ff91da992d542454be78e71ac6cc03505b58ba8c10540ae37fda4d95ec5dbfe399cd2e96b927c8b21cb5648775a011cd52c2a5ad2b9e298ec551e9f58ac704c0ef8560fe26df5cafadcc67b37e6c3e0b461d00047c96bacb2e8f184206596cf4db7ed4521ed6c01a6a7b210e47743a60a04399fe7f76553f8ee094adda80d41a27eca2d8763404812ca94ecf107db75f2acd0c7b7857784a018b309fde99ff91171442ef2942123c1788e616d174eaa01b9f694d55c5122a71c6fb779a53e479791831c4cc8112b71cde6d2a92346a16f8a2682135b0634a9a3b7b83b5962d18e09d8613f540a90420fcab3f82d8a98974a039b6539228accb2f9194b3f498f5d3ac2eed10b82fac8ac95901fa02100ab4077074045c17534e62f7360053af24d43c4aa2a11b608d25162146ca22111160e3d7e42f82b9126265788993047d85095fddc610512fc89258c8dc53749107ee560d1dbf966de59162efda6304eacad756dbb917b301527f8173f262bef603666a20dd01b8ff7f60d618397e08a169b84a8b3b602c88e5f6ae96022decd0c8e270a4e7f275cc47bc74e36e082f2f935c0716ab724eaf5a64422afeb400df3d5f1a3762397093233c58d1d3aa56f0067f4d275765dbbba0b18ff4e6e4eb21e20f4c8039f8d53033321297aaa862fbd9b2c88e144e9bf1a1f5540fd338ff4386a6c0544e1b9b336249803a85f441efdb195b1b3cafc078de0cded2f6d9f7f4d452aa29b9131cd1f329db80c83965da8abebe60a86c5ddbd9047b45dcc1ac6c8c572a18397cb31ade515c5a64fd892843c7d383aa329bcb1fdcef74c5f1737154c76406bb30a07ebca22397f8f50cd6c617ac086608a0524d64e954988343f4c50b670c52a80e2d8dabf6183587d7cbaeb85e1edec95336ddea574c91403e2a0c777960ca61717e5025794094a608c383f34d28163f2ed0d7068bbba9ff6d75739c365a38721b6885864b857bd909d78aa178ecc63cb0d9d4273db0f4f0f027a1a0c3f65804107ee7f1acb2fb16134638704d0138333c9b2e37e82c4d7058bd544724c92048070cea940de49365c4e2db931934d981ffe0764fa067c826517530a7f4bb1f34193629c09a4dd925d161e0fa057ee35382699d46588ba84a3faaa27d1ea08b08cdba220f0ba9272faf04b9521fcb90be29039b0732d8d08b7dcfb45c366325bd17aa8d3f307089689761393690986ef8e9ef2439d2e9ef087197d3e8ff24cbb113e3e1a800c96d042d793b2f3b81bce19d424c59a4fc553255db7768add1319da175defcfb004be78ef925498b5d42ef246990c578947590668c5007b284f790f48176bb4fdf08bf67673198ca26fe8891cd288f9e7e081194584c965e697c34cea0b254023229aa3e42b86c01b97d015b731c5ca490598a67ff6aa9dac963b332ffbad985d754b74403a3ae5a07f523ac169c64121fe29d04e5be0db4577c482e725745ffdfe4de30ad5f51035e2633b4f0b3b52631bc987cf9e5a9b8565af823f79c2f2a534419252cf6be75a536c29b5b99c5f42e75e0c93003b26f239a3cbadea7b27346e7ad2dddcfc86a7e45411eff8d439fd54257840abaa15d9f52f19f7edad2debd2c4930d8135fb384c5f8870f8315672f2b6b4d753dc8052e55229d706ba8e16f2907f72d92205211130f9312b2b3c240459808f8b28cf1f48e37f2064a1a66b74c6d7e6cf9621b25e18924eb434f3e3f514f4ccbcb4d332e2bcb9aeb09436fd541107514330f2ad0d4a007286ae37dce097c2297174c4134dccfbb5979d8447021750bf3441b47a3f77c0c1501808f2b946f845f7e6a22966824b987ff5a02dfa19270ff8b0f59327cf7da48872ced59cebd67ee61576fbcb92b81afbe00422df5d414ccae8cd8b734f57d0c3a78fc121c91041382b846e78b32efb8998a04a18bfd7c86677a0faf2af265a87194dc7959c3d1b5d4657ecdf85c3a4a7578e7ceb698c5c96e8cd6b6b7d8bde8c06361040889bb9ab8d11a4a9ba3b65ad91cf62233689c8ffda7650de3523ba28d160a9def7cd6785f00bec9f81a54e3de7dba670a4548bb910da1377620a9eff6d030edcf19ca4ebb78d2fdc0ec8406c3db0eba31511a779172322619c86f60e318a54500645c6326767db6c258922048d4c516ebc7aef48e0907ee2ba6f18b60356230c4dc2908b373dad10bb3e7829c97c4bdcb13bdc3587b525400990379616a96e8ac48325e7df0e942ee5188d28c8dc8de541695abc342d4a5ea9fa07645852428c49af33025f7b495ed07b5bcb655f7ef70fe35dd4aec7141866de1ee7cb35d94672640dd48be5be88b7f2d6a5c7cc5c53dece8e5cccc887e09f22b005837d2776a1428bafe89d0bbac5779de825ad4a46b5c52e714b0489d7bd3b94b08d5593c55a0f40174823ab3b14d7a4b9c3f121195729e7ca0dd9cc97e62cfaef4daa0466383400cd15ac8c8db1e14ec372cbe4f7186a8cd0d178b9b0f3305c69981d4c45b57b400c2d992b278eb42e0723f1de5fa1777cf6deb73177a06894bcfcece41e5f1d7aab83a8916cd1d8cc243c8f3005a5789856589c75846ad133eb863fb8581dc916d886b346f7193cbb614c2214ff3b81c24d56be6379c64fb85ed75904e204d5c6bd53cd37def40e4c5e7e3df90349429917f9e19c3fdf7a67656cd5899555c4e8e03ab7718fc2690d88081d4457955527c484767e1ba7648c35f7e0a493060a30e046d5c5a25986b8eaf1807acae91d1bb3aa34e83cdfa0e301312a45f72a1926d89a544751df419dfb35802207db00bc8c41ffd002db917d12474186af9f504ba43627ad664be7a2cc333558923c72354cc2b514bf947f418c97a422041976e2189fb358bb082df68571db110e28dd9b9144c780530db1b6a9e521b5d7dfcc08e9a6bd88b8801dba4568e0264d051a991fd1f7510ccbb861c6d56ef7b38e557abb5b16cd20d910273b9e70dedc34716671e40051d14eb7053bb45af96a84827956cb6fd7f6ec82dd71b1b5a4ec968397647f25ce7987222de1cf1b0f9f3117ed07b80d1f6296ac280dbf8512d0f115875b8d00223cdb470579b956b8bd95ac91f165c98413d37feac82bd152677cb73a846700f099d3f2705354b2efff4f196601e2b9f17bdde91fbd31cc00ed30374bc22ce32f7e9b3afb666ec4086a2fabf0893276c7296c3a270a6f03412af20fe02bec28abf05567878601134a68ec98951423dfcdedd8afcdbb43128009a42529cdc694983661cdd2b347670c2dd1c34a3a101512570e3de0be2bf9abcb7431d3efea93c731225b3d4b144af09fac41e3edf88dcd43a1d1e0b0a53da181bc42fc1b2068baa7d75108aff747fb3aeeed2d038057d1667421a5b362bb9e356af1f66ece3896ffa102a225fa61648baba172816dcf0867fafaab5672482549e162e2d3200ba7eecece6f5892f75dd0c619caef148f3ebb22e62c9deaac00e9ece272dc6b16fce86be6cd5c1c9f2e4051b8dfc49d002f5395c84547738570d7cf23ff99c05f488126e64a532e335303cf3039a208d0fea99014c14dcbb9d34bd419e5df960e72274c01aeab2b077f2caeea21657f127d1aa69b5deade0e1b9f084993329104550178e0f56be96ff35891d4f7b7a479d94e909d350d6cd35f5f7b034a0728a255bca672be65650057bdb8dc4678351f3c668e77d0b74813ad4ce9997e85a4a7101d8e47f02ca8731bd7a4a32cb71c8d51c6d72c843eff7e31501e7c28c8b5b6d938f9b400739d4dde4258ad6d96922b235cf8aadee944722d7e19bcf91e08f27fd5ed8d8fa61b62159937f9d239ff0ce973c7246c8d56e0a2e604100d5bb4c694fbbbaea3dd769de1fa76ffec34520cb3c344c9065cc43fb3898758e468219475da6a9751e5daecab436669178a1a92a2972d8cd5cc4a38655b8d2e1ea771e1f2601ba9d9390aee90a241002034ddf41368cfedf0c6ffc065e82f7ff04b3ed84017f1aadd60228d49547e0104e7e618df900150d134be7133c32dadb7da25add03d508f44a494ce5043da67aa6c50f98bde364f2053086b73cc1ef30f3788e42c00192eef933a1d78bee6116ab2a6d94fb963e15888d95296a581f805ea88e15d1668823467dd9da35b41b6333928764b078a2c0fdd95511ecbb35f570f941cdf80fd6a1f881bed75d8f2e395742246a6c1faee27a72e493f15435fdebc563f76c73319c4d7dfe11d78be580358b1ab24c2dfe30314fc0c41700da56db1bdf82fc80f1430e373664bf3ab0c60b75db4020cc866ae9b5e34628e767c10569e26602e23cc220367ed9bf945f6c108f51ac1adf6ce3b4b8a8e75550877465df6eb1f18ce9d9ea465bac358f99e32bc60bd5ec3ce6865674d99a5e9ca79b5cb289f5896160663d4b0fd1db0acc009f55a564c3f2061563ac4afc60a50aaa8ee1cffb50f78b0d64aa384ab1328ab995021a8232f2c751be92d3bd696ec32c7a0ed3550e6702559bd1b0ff3313ba0479a90b6a3e4cb4fb62ed0c776536db4c6c653ad602b5a1072019377db8d1469e6da554f1dfc84762ae4b3b8b416cad9fae538362fa258a77d277d2a50fc2e50770806b8dbf5f1694e3e13aea43b4c82a5a8d38667b4d190fc8cf2f86aa918d15dae82ad76daabf5ff92e494163e4f9783f2394d794ca930c882b90222568aa45a080ae69b371403e269f043954ffdbd08d01025056849a76e70709ea99a26c43a7baa5b13c687df568538b8aa97aaf0c061a5fa184920177a10eb05b179a240647d65ea70e9ab1b401e25d4c32fad76bb9ac92f75c9a65afabb204ad117c2dcba2750ed009913ccd9ac407c15a1fcc4ec72ab136e95fdb0651ca1bf078ff23113d050f315cc5a9ea14c2ddfb3b0939f65a377c13e0535192a6ad23b395325165aa579e0dc0378930079de5ef44fb6af56cb5dacc232525a2eaed7a351244d82c8b01b1e5dc714bff7a54104c9617d7c87d43a0aa4d592d23d066f46c01b7ea276048eaf7ccedcdc9adca5a3cf9c31eeaf43ac9eb173e8e78e276caad87301a1cb3a8cee085911ac6b80dbba683017c4ab365e62212ca0c966b6d4d65c2891ba95db858988fc3b1b7a9a4d77791c5e66fa0b416bed60c553be262f2911030ebbddee5dc6be0bb76ba13d6ba89ccfe2796364a525d3f0512fa7620cb4da6f5349530e5763a25a93556f6c63eef582618d97ef6f69f20ae4bb6e70ee35126f0bed8d9fc794551a94c18aaa59ac39fe9999feb0ef9427d70e3fe12b1935207f5791d6e68791f5278479775f306d7f714e5d260a816a0fd6bdc4cd2136227cca4d8651ba7f4f21a0b09186131b110d85eb69cf6495865284b86dd99a6ca52772a7304dbb67fa1fc8ac82b9caf2391c5a70b821f4aa3fc91d4145815bfec85db9507f3392ec9f6187ff01b56ba058f855a0b6feb64b8825a438bde2fbed89fffbc93b885a81d21cc544527c639ca241ad9bb6ae4a397683eb087646d79245466ebe3520b669ffb247515714fa5c37e5995ad14f64a0927eaa2a66e0ffe05db094a7c54cafe28e34ffd23c4fa289404acc44b429b03477a47e7f3143498155b59281b855242ce781f344f92d09325ba9a32756e8b3d520e6683fa25e5f5e1a829804ced4ce01f4c9ac5d40f2228ee60fa7dbb18df77671adc7b9b6fbfb9fdcace35314aa75f20afdc64176c3a392c12945c5a98217bfcc595616b32ea2e37c10888b0d0e1c079788d4c50031de136dfecc4621b50ebda8b266855c289f66b72a5033abc0e76b9510f790742b9b0fe88f0d15d18f5aefcf0590eba5eac3853e0116a3a295fe456e2e45424b0c3c1b8a9fc4f6c552803ca68dc8a55e564b82f09ce20cc6be564212307c91b13e3c9b64b792c1a1467fe93e200b8ae0659ac1d1427f5c87b5e83934db4659bcc88c85dc35b3e620b1b82467167cc37ddea241cc389a1153dd3f2b934efcc8e161689eea67bdc7c35e66073ab17a740a5897d3e070a06771029777f98667244467d49621a9ba902022cbe120500de12f2dfc27bf1038176b9e397740b605fc14d759df6ca225666730eb1699a15cb64b331c90c298af9ee4f728147820bd32db4ef8a1be8346a73bd30fe12ba289655b2d85809eb3750cff8813c3b5d413c9c76a64a29e021c9f04cea0d9d68d20310ce843d5f34696deb991d0bc2de423e7d7c76cad8c9e64ea56d63fdf21efa2a2b2fecf50f75a63647b714cb3c10a12d79ff643b347cc3aefa287d0e5801d99e0fa7cdb7dcb61f2810f1ea06223f0482f87a11c9b82e05ab88441706500c56227e19d1094cfb82dd788fd5e8ccf965af43c38340fb48973498a5037d8bc7d15ac6d276c944b7a521e6726bfada1a2ac134ee7cede36bee08506243f5ed2e947ef7ff42e2e3a24a51d3169329a775b0122fea5d15397405a5c6ebc0a5cf1c679fa5d136995b13ee0515da7acd39117e603e59590e9661ea065b2da5d37bd17b71fb97c1f34c45c9e13a9bf7782e3941729663f0e80745d967a422be0277e05e8ebd1fd5b08d0571b8e9608fa81bfe9d2acbb9fd4c53b8b46a12b2672fd51914a520c27befcad1d3185ca74bf2bad6d51f30ac77c0559965b5962db37ac16dc8134dc8a999392f8c00be52436e759a5ec54bdaf79c59321dc422cd42ce203393ad9f74ff8426305ed39c060629460b130543845af163f9f676efac725d447fa31c641464988823b01ab2565530a09932f343e2ef1914a1d6f2ebd3e8f82af623bd90885e13bc4a2d0e607d752378a6f75efbdf869b0c1a9eb836973f5d0b4a20bcdebc24771fd354c58e33a70ff0bf79ed844d918000bc50b87bd7f871a97d3a742a07c168120bfe714790c7247df490cbc9d98b8ee4a0703b791dd24650753d3e5d72c237cd2615cfcb5eda061802941804096906ea7366aaaf562d0a16da32c5e45190da0aab40c9addc36d2278d18c2c2d2c0a45e59a77093dca339414e197c63316f84ac699f37b2270fe44616d353796fb2c4e97c3095621aa276cf667d31eb55b88673a73c7f41a986ef1258e1167f3afcc0f7c21bca8bff9e36eb50b824fe27616c46d3d73808cdb91193298f52a11df191021244941a47c63d65d85993102901ea6b94064ae077b1302728ed22ea035007bec84fdb6ffce1f1fdf488bc28fa4ce99ed6ddcbce2262180c722df1b85490c3d5f40d64027bf1b89240bae9a0943b39dbe6fe3df84be3cce4d6f3a2b34d939e995a33c355f80191c5bd1f02a5656a1cb16afcbf9859b81ef8c5deccabc8202cd4a015b2c1560c0d6da4ddd86e499cec7fd1d8e5b50b9d0bf4ea16bc17e9cc492a70f4d5e243408376b7ff5d5313a7da3384a75c93d3e57e70f9301fa9971d14637408568458e9bcfd4a45bc7af469d1b05e1ceffddba42f0f9925438e8c49e99fd0981aa539a9489458cb216bc581d883a12f7e13980b254ed3aeffc5dec09e54ee0297b20856453d43c516c53d427debeda57271c43bb808b02b1e5e93ef1023a7f590863b972dff650018f28ce4f193d2326b730090135705d96331e981a47152fbc00f3b0ceaa8308cd1b356301cfbeb5d135a957d409434094d6e8967c8792eba23246901ee56111f6ac13df7de36be6689111adefff8fe6a791baaa062b92acce1558c17ae05fe8a36c4cdc8676376ec898628eb1776d1ff6634944e739483a12a68d20b755fd6e640eba2315df6dff909cf0f7e50927deb0426e6988fe0f62ff5ffb06649308a015c6ca842563ea02d6526fb576fa625df9559a8d7c9dd50ea4d37a8cad415aa028e259df748fa53c3ace053aba12652792e069cafa0ff6c66738675a3efb0029073a762c60a2df15ed5c4d939019fcacb7f2a7e416532673b806f299812d5e3e7185fb40ec889ac90203209a1309552a09161219d91e1a0f8c2d172360b9a49335c1ace366b7cf11566db4390f5b5fb07119f628402e617c1bd16cc8b2f8039f1357531f2f61e34fddf197f104bf7fd2a72f683e6b0a2ae2cb950d4b560d34b5986d5a0a9d3be060507d2a9e6d422e520fa420332ee5a796a2e80633065b4c482bf43cbb4ba30cced2befacc77394aa2ec52ea4381c8073c66ad335ce87ea033ab95b2978606f1417959a54591b18c0e50fafeb9bce446649fb0d1e02109206a93a906cc87dc3b63e0366e1146d28b4acdbbe24de33c75a0e5530617b29d904b3f7f3eebd50fc4fd137d362365b9785cedd64c26b95d38b1a0bc16cc7b81acb065a5c7071030806e23217ffffba9807288f32f592b50b1374ee9f65ea8eacff383bc8ea3e7d4a400c524b1bdd5c74e6adb305cc0ba29221b9e81ad2097e4e499180dc4e2d10eb136bc8b471fc2e252f24e10e926cbb4b2033389199386c0c8ff1337f5388a89b513127c3a62938a3f8fd4fe0e06b46d6da74594ba10207639247b371d4fb0a91c66e657f00a79174c2c4d72550e016143cd55ed95e9b2cf7c599fb47278112a762f385eb9120829801b487587624fd7bd13cb075dd9c55af1397ba7d9bffe0c73e98d7222cb86d43811faf2456b6ba8626b4658106eba7aef8922e6978bc60e29d75d13c92ebcc8a43c55fb0c53a7fe170a8aea43b1e6c0ccb6849baa38b832558416647d59c5b78ed4679db732af366a99fc72f31aeae15b2f545ff93fb915069712bb5aae5e43b4dfd1a7fd44094329eb87010c9a0e3817d596980278868cf6cb470212927c7493e5957ea35c6d2ef1569e8a85eb861f919bed3a969e763b85a536416bf90417f200cf2e3dace80775530b5dc07b0a9a9cd37d49ec7f37d2904453b2a869bd8e02e7f5894bc751f8a1d7a439413fa40ebc6f3b9fee420cbfefbc106efaf5186bc82528b60dedffb9e0d3ede522ad50be346849a1c293f93c00dbb4ccf144eb487e40c13d1c3ff03ffe34060266aee5e52190538f26f5efd5d20e0a0831ec01de1edecabd67db2d8d1c4c9c4a3ed36f073abc119cc46878c2a7f5dc8b34e3c8bd8c316f4c71e10a6f6a2c9269f5342d7fb0c2c4bbe1245215667679a0aae65a8bcd93e760ca6bda7be1a820970ab2228e6e2566133edef61d32ab8ff50461365e909eb5048c72195a055c73132516b6c894da601bd014cb8c3ef7bf6c5a069ab53f7661462babd49a91726c856cb92be9602f4d1df8495760672edec1b7d4891ae665d15feb8dfedac7c93c12022017e648fe4bda7e0cf686e8020058350dae20d32831480ddcdc591349ea5618046bfb61ec73a164c209abfb1a1fe4d3c2722fad7e7ac3d86348e7b0623222017362d918aee82984a387285bc211ccbd8e6f5f0518ebf6d5cf48df1b5cae1ca5911f7ba33f6fc416159c2654441c7568e105478f89179ee225ef0202b57ae401c7b622b4d845d987fee2581bf4bd4e2b58684eee03feee1f6eeb115cb86c3dcfddca20eb2ab3dfc1496373631115a7dfde879864356e32e578acd4b11efe699df3fcfdfd2cefc55be009aeb4aa45838a7efb1bb8b94bdf4f74bd462c291b4ac86d16d2470933b2582a881fef6978a2e79c2b40b6ff5caad8deeb00abf404cfb5e8e55d640f3646a3128d1e0e4418e70870cdeac145fb35f8ae09d40772b15428df6dab65ff47e9412cb0c75abffa220e27040dc04c33b27afd91450067da68fcc5a5712ce75dee8074f7fd83bda9810a095540062a8a1eda8e4489ff64510149568a16d7f1464a496a518e34d00953e9c17bc745af7cc7f8a06586453aeb4aef3d435456de93763418795084577869f2bce7da1b28d427d33c8b44832084fc92d516a0490c43f679859ee9fc713c2763f0cb6238a51767abaeaf3ed898e06d7fc6e21206ba77324ea2af20e36fca346b2a1c6c171cc4a03d28846f2cb9dd3920ea7953bd18e867da7bb6e71231fa4d0ef953692427a0b4a492519640f45b478eae806909f965f4a09276b5525d5b3e84caab03f2b1b42be327d48278ee0ba9eb16aed34b5c87abbd489ef5c20ed8025bf1e99cda4436d341aeb12eb9d808703b180590401e96b8751b09b9cd8a887673f4fe15baab9ef87df5ab0b37d87e2da3841dc36b448de096b582a3ce33a36bd11906c0aa1940e1ad45885e94aed76058628996515ce5df0e6c9a09eca0da519a3ffc68fd3e0fb5f15c682fb455691326e867c437476b5f58ad809aade09b6de2162578afc921574139cc1ee73c7e6d4267417c1bd671283b94566a8397136d4c914539eb19be0b47f569087753e9195f65d550fe1e0307f3bd9fa79e2b13ea6042278f7c6a5b155f7394d1d80d1c627724c0ec806261fdbd06b5797b533c80f620914eacbada2ebb716055e7f09f2ab850a8e33096ac724d4be9fc98caed3a03c8e6cc9b9d2ac232f3230448acc040fdcca4e53d2e270cc5eab43b78b4016b6753108b4ee4e754a137868bae7d8fccdc90819d76594c0c8a8ae6ff9da9e7e94b913f46ce218432c4b92b681ee604b3f2e20ae98b8bdd08c389c8c1ae68ab2ed8a3093a9036e815da6517e09d9175d5636845eb5d91555cf223db6cd3ac8f0d67c7ec103cbc5b8382574e1c057f80f3269d80a4ca9c77e772e0746d2b3c9667313673998675ee54b2f8eebc09c21745feebf1c03929b53d75a22ea13365a355cbfa2d170cf9e75302e225481ad18177644aeeab399ed40a97bbf2b5d759d7c0a4355e8c5e03cce77f171fdd16ccd4cb508590719dc083a74a72a1d35c845c593757ebdc16f640587a2357d81d53b1942528695f22648573fc40fd5b4384e5b335288f7462359a8a2fe2748ac66f29aa5d0bbc263e1e9c537b2a69815f49fefa7fadf64f42912cf37937cc1aebd5842d033786c67e99eadd2c452bd3cda173d5be2dd819b2181f81e5fc8a33c361b7ce8a1ac46ad15581a62d9a61217ef90aa7923673fa46bb69d44a6aced445b94c4c6cb7ff2fd567ff551c3041d4690b4a0344e915eb8b30b9b42f0846fa7a4a04cb09f3711287bec7b078eae4988a282dfc5566b45dcedbca2a34c29ed4a8b0f2301e1e3e3493e472983c96405c9cc6dca24ea7f380370f920f91ff2384afbd679908312999b8e37d9259221af3f0a56c9ffadecda65d90f9aba5a75966a5764bbca950303f4b3772922d45f9c0746eeb9714435fd6826d7abc8a622560195dcc6c43df0de2e3eb0d7584da2ec1edf380f46cd9f43ed43162514751f51d3570c1bee0418e5df82bc97a535368495da91d356c8c25af6ea358cc42164bcf51986c9ae376781bee5fba78b0c92866786f08f22eb874619bf74735480e4d7ab194bb11c52f0a3f272938bf09e2770ef860167e62018ac223cf9fa50b608a6ce35060066c84954529d55dfe7f0fc463460c7e78a90077bd7927c08fce9781614819eeb32ae7a806a7b3d4331039e2dc9706da8eb21bb9293bc07962db6782685f7bf56136bdf13eecfd058dab7b5578283bd378294fad1b75c2423cba430385e34368f38f4a03c2877898f98f298fd4d01ff7b4cb8c0f93350db5e9c5e50d5c20b4e1ea9340e1b0a581ad3c78fb2215d191424f8d96c522158f9b392f2a10d95f0ac28b997c534d792f68a9bb74271b3a984e28bcecedf28361f8e00d28ae726247b0b575509293dbd52d074f8152823bccbe8ff3698772d2c1a72393012e26b8da41cf8e70f302d000b53f9855ee813ec78735e4c1f3feca31090daeae6fb377ac4cdc406e0ad320801da79ea23002e8766c8b1281be4ab3a3f6c3c9954e1019aca0c43c0e4dbfeb55083ff44758af36e33761b17957e8235f039f74caeebf4c0e5fe44d4beb41b1af0fcf2fe25d7d2d355d06976c1c7ce098bd7ab88961a3cd9b52ebe4a9f4b637bcabfba7a407b8036ae16ad05bd142b7da56941c4970bf2676ee029f452eeb39ce932401ae4c9eccfb45927e37bf3730b762fe0f327e8cf7655149ea4dbd20504d0ef1a09acd8d00a16c157614e733cc4e71dbe8da9819f40da391fc0c888d56f437ee11e85ce0f94eaeb55b98f5f2c82a3171d146beb5facae5ab4d1aed6f46c6a591f25e713db7ba93a6892e58fa61e1a403762a49b65e8bb0bc05f1abc588acffc81aecdfbf544f561c4b7fe59cc2b741f606ab0e3adde2b4b6224002bbb743b518a8d5b8d13d5350b1474e24f47b21146579116c76991db2ca7bb530dc96dcb446c9e88d702e251dff7b6f3602934364eb633d0bbf51285f4c584747093e33df0f723915da10ec9c4b14a288398ef1a3acc5a0e233a7677373fc244286e24832851fa573d68f2abca99aedf0de4b8b2e6647c1bdaa2bd31a0825446a9d331be14c801aebffce90e9bc37b9824e870c3ba64e6099ff290568dd4f03b58f72d34620788a07d870955097d39f07ec6c7ebd1afc31f885c257a9210fb8d72d65473c424e3ae3c2f34380f8df7fad664d90b68c1bca3b04821bf8be531bace36c5de05d8d089a1010a2ef796113179e8480f7f59f4fb42871e7d81e37f2d3115b33809fa226e1a25daa7ca6a42e3b8bd1019384c14975d2473dd9213ccc8d28936aa4143495eb22d527490ee448652810525f43b439d2488e2ce2faaabd7d886a5cd2c3166ae2244ea8023a8354a245f81cef81bd46322c2f91e80a84a9d2abd4e28c2cb7abe2b67684c43b13a10814cb129f398b747735efa6ef0ee2812c35a4d607e2458071607fb02f7b79f8624c37cddbb8909c2741368f94a6f5f44819d3b900132a238060f9a44a78aa1e62ea03a3be8bebfa0c7e2e20ec7498cdb73277b85e767159bf2ea6f6faaab98befdd10e67316dfa973d7ef6eeefd591a0be9cb10ec320440d1d5fb80855debaa1604a2309885fb850622dbd150a478baf4f6c387e192dea9b44a2c6399aa51823d95e7eadfa5fd9c9b2ccce106f1c97b6689065419cd1fded931b14e21fa16385ced2cf4998b9aa791afc7d2ec141e998d3def5c4cdf19c8eb4f3e140ac99a72b149d05619709c0bff8b019a3a02f9d1ff402590a7e971831240b2dd78b06138e1c04fb7d314345c8cf39face8c8bdcbb60a02131a8c6299e725c26884ff677c816074ca77ddbbca06c45350d222f9a793a226da5dc504c3454461f1ad595a681a6af6f0b1816079597306d7902d306fd985ad8e06063f84a0c2f615b4d8e0732838890eb252c875d21ea920a26d9df3738ea367baca3c63139b9d8a820fc81cd9cf2bce180515bf2135b77f21039a5edaf11bedd847b27bbe3157d49b6550dce6fc6b7e819000470cc531b4d684f269d8ad69d5a540ba370a12095d58ad314d75a12059c2ca2068fac0923759dfb5c6dc1558aa00c81986f77926ca815c835c59435c1ff0aa5a428792b7ea5145b4dd0548dad4141eaeb3f52d247cdf7385eda5f9c3a468c5849077a79880a4eefa48cd71b5d7e8026f9825515e0206854f28913a557d12000fa15a14ef4a24169cafd522d025c279039642c1e3bb62388cf3052ad84a03f0a2b3afc79fb36a2469e5a788f9e20f4a2399fd273200ad264b76024f13350dea17ebc044de33692f5f76dd50eac918a47293f2e618c96bdad917eea849f1ce8e6c730430cc2497561ac916980e13ca6c85d78cd3d14709eaaa95023a592b4ce64adbe2df3f63ed4ece23f808cd3b39097576002e645fcba5082ade3ec43d09457c58e668712429d9d45b883b9a2d35e447823b4396fbeb169ca6b0b622add4c2baa321a14b38de3017c7640f4a033aca4fe793068c8c733d5e59e33998468d895ed16bf0769596073045deae7551119d5d94c9ee61d27f3025fe3b809eca80a0fbd462bc371fbbf4c6e30e2d98dafabe7f2d6df366255100e6aaa41a27b06b2da8c907641ef5dc3c063ffe907d3954915b97f074b00e9273eda441a750ae85ebd891cdf16182e4d46334f135e5f0237aa95f7b1872cfef21ec22f0326d8d5885b1e0a5d2d013733d7f73ca0c50efa46dbd347f052480d932884b68964a4e081528efef6885391cbcc46b355aed3c49d53f072fc750cfa16afb7910facfae423e9040d785b81deba2c786b61bdc301506db43c6eee7e8dcb6d81531480a1cc87cb17dad33c752d2b50276ee2a2085dc676c1ba719e9cff5f2fd6904c2223190207e5590f47ffd01248e2395d43bc8656674413fa492336143d666b0244e9b24368ab7c93393f92d84fbb0847daf8784714db42f581370a5e476f1189de7c6e7ba6a5426fc68e5993c06854d06a15af295f876f5a63874088cfdd5a54981a28449d11420286904e7e5cb579fb3f2df812d3dbd668dc2a8428ab24224c26391b7fedc85440a01e159b657851ac5b74ad067bfab9fd3ebe0795459e4644ab0ddf2035dd3ec4fce765e739a75d301ba052bd181dd1b69d9783c69aab9e1215df8a580f5aa7bd9df4d78040a3be1d8ed4f137d26b7d7626d770774de127877629e7861d0805a654ff119affa6bb2215602aea3dbfa194c3a4cb4ca6b2e842085b1c1cded1cb466e4db174cefac7b7999027ae06a218032933976635a6b27351316013865555803a343c136d2bab2821bfb38232ac931168553a242b0271944977defb5d8332675449073a5baf660699dece3e52c49ef63ae740802501aee0da88077363a2fafd1d41bbd1db885b36853b538e62c524c5b4a9c51c498d0bb00534838d69dff90340af39ba60feee00c9e31a5f05a96e00eea81ecb8e6a4ea89c6f054faa87f0f016d72633d9d5475b6e77c6da669b70d6ab74435bee24a1d8fd91f80586d52cf49c2ffe6e40926be0531cac2ce2e2bb975f9b29e1ab648c3dba5a9f71b99ced272caf5d4cef3debe6d05935ced27abece31edd4d8f3de9bab21a1c9f907f7cb835111e3129838bde3fb2d31f2851c778e20a85067d25ec05694c644a00dd72cd1a3491e7ea71fff4ec627b794b5cce69c9222ebe36ae2a786cbd806ff98376aba1a052b36f90ec577ff44f8774684d5735dc3a79aecba6614834800f66c9b592d71b49427f73100430b591b9ed4928ae1199ec038f9a90fd456b56df9251b0c28a3cee3056da44551e062e1d13b151c656ba36cbb04bb9a3886d9924258afd8de924c7585e08365b1f72faae2a7ffc13576af2f0714b40a68f12fec18ce8f74667df3c84b26803992a9c2074c99fe2ca0cada7724dc38a72c2e0da3615b08776fa0f5b3e062dffdf2b162f81fd4debd89e5cbc81426bad472d5b37715b623f263249b4793de0fbac6e34000705204e3d549ed8a94772f5589e01839016c8b37c76c375efece1dc67bc181942aaa4f57171d8d1775b86e1584a3bb057a6dc3360b811a4ea0ba73f77e02a366fae8dab4f9b88f7bba0e8b15c59ed27b6892849b09de31f9dadc4c32ab8ca2b3839610fd126e9fd5526b5ad4c704e8893558170279066f0a4280822820530ab34b918376bd8abe593f19cb38cdf0cd6d6b92989e3ca463de93bf168ede1f3d6bbff60b12af43becb6e4e6f57fc58f58ec7b5a23cb2c7f81bcafddf61d14d0ac4055ae6d861bc9318cc80488eed59992a6926ad8363eac3748531431a989477f860ddf8dfe80ec48dd365e3a86ca3ceaa95d13aa31900507438902d3b5aec82b7ea33fca823122cf0651f0d2fb39667e38d23663ac00b5ae512ce64bd8051b19862057c18c1538d7ba6189120b7f67a450a5d4d5f068b2bd93ec3f04f6a280f711872d1c9c308d6619392750f047ac56940261c58bad4ff9d9f3e2f1941f595e2dd72fdcf5e2c1cac30472773ae694e5d60ec2af70695f5ed86c12109bb55a038a6f8a9b1a3589a9d93d8acacce80fec8df2a194739f36f74c44ed5983296518c508e264567e5200fb24a0f9a2434721f17b5f4fe0f089886d1af273489b5b66922a64c2e9d9557ebcc83d5a8103a18cb31a3523863f98dd9c39a17769eb23333522e054ad093cfcb09dc7c3191133714ee6a696ad10044677cace227f54b88a6afb7bf0bbff7827986cb8db8b996d2d703cad866aa04892072b8359d44db826046f7f69e2f19aabe7a42950037624b47ceb3321bc4ab259f3838ec3ef1a8a19daaa44e99f6d6cff93b372fa5b79767cbebd834817ba344db53e270b5b3e5842ec664aef0823fa680f7c20d5f5b86c5e2a5afcd6ccdf7cb4e14d4e78766bcb6aa224227f4066a642fc2d41bc6f9baae7097aeaa5ba43e011178c3bf48870f88975baf48105ccb4d60b6795a22abf8d64d1afbb49e92b95ce5cdcc4c79d34fd7e3f93a142eb976f2e2cd78ee7007605b1d64e5fc42602691826d455701606e2f0b4b402db0afa2ada7f45ea19281898b09fc94cfa166c99208766a2b1c39533b1238233cdc92ba5005a05c952b68ee97b795b307078708290eff37b06571dbdef6f479678ea6575464144d1a4739568909ba5d7263a278c31b9b7d6ba365a70a34b3123d20bd176fb4ad1a584f75e21b11eb0c7364a55a28e318fc1b140422fc64ab70e7f5aac5bfd8c27740f11e8839c0f3d00623565e373c6b72cb29e1d7e53f50c0844cc211ef7730e295a1a6461f505ff7c340a8218eb8533f120d63108b807a5f980554dec3ae462d43c6f1ad5af96d8a636193da8b4f18146174d54837ee021d389551f1a2fd6f777e63d5cae90ea46bbc1abc94d14087d745704e91231975d5ca0613e894932a99d3fcdc419d504c1136d80f2f4f936a9822aa1a727cf61dabecd4c3986e450a6d1429979057fb0630d1bc1f562709b8b0ba30696d76c663d7b3169a1fcb64dbbd2cc53986cb0cfe51c17d07d22c6f10c0a3faa21427a6855a3317344767d5c7ee1faba017db0451f9b1b1b922a9a7c85e391c293905d0616529ba91a15c9d7f97764f971db95c96def07fee840e0f632c0be34dfdff5bd3aa84837ba5cca13ba916520837484eaa1b28853816c55e675c9132efc396c77efc472d076b283cb39e77281f3a5663f63d30731f371c8753ec8d7eb225f67294e15dd3c6269d55059711a3f01e704133a97408e2420d19ca9c6c4af7236ced50206326e5b39b1c6782f41cd4d1deac5eb39b3f1b08924ef2a36ea204ad02a4e7a4888f5cc95fa31ca5cb511116e7a5931b0c3dd0c458a64fbf75b2810d3ed1ea1c15ef2df09e62099fa0a725439b8e3f924cabdcb590446abc0e2c3c7db5acd811d66894cbbcad73161e2097cb22ecab2e61e5195a8407601e04b8191f47e4e0450b16678250a520296b446dfdcf9fa32241f935e75cfe2b766377da2200297d9183c9961b8bf707f1ceb06980d7b7e20fb7779b22fe16c1e56d6a3a5d60956b663e68ad61e58c062529bf000bb1a10473ca5a406ad4552ed44407a35ae8e0c0f1f6a068e661fd71997595f5dcec36d86e02c1a669bf3506d029abceeb5f4253ec68bd9baa9617cbdfa4d562ab3a70b0bd2e3c56e95b21a576e254b580077a18b637bd2289f14ccef1d902975033c5cafbd5058001fc96b48fc663356548c9033cce0a7b03db2f2e84c192478fffbbb6a131f0cbd96e697d73d09ff4b4a0efa33ee05af3896c0e992cac7aec01584738970e39f0829dff8aa4d39a390b0d97cc2d4b82f43b2069a37b390cdf0ab30fc9f6a8ce3870b33d65c989aa930b9ccc098fbfa36fd19a3451eff29fb41cc565afd49b52fac91a3c791c1b582222cdbb5182b7586b55528b4aacaee2ed2ca4bd28e7fc85919e4bb7ef93dcc77f79cf3b05f8452d49fef21a827ff960d23e37af7cc0136d7a59dce1c6acde327509ec305474d207421c8ae01eef8460aa58d549dfdae0136dfbe03d84a7751e2e23e38713161a51896124118d9c3e231c672da45a10654419d2c441e88b27fdd37f37739645eec121aa9a1d37a60590485d936bdaaeb2da4de7635152029deb352df52ef5a86fe5ac26e6cb4840ab891f8d342772cf9942a97f377c98093075c3d7341d5c6e9e865bc2745bf980f120caf87ebba1b98b7c444efe7ec62035f4cf385f20c08e61883a6fbd209a38aa3a4f07436d81e88890659e4a97cfcc4b0ba45ee99471d10209471c30f13f48cec5bed7fc4af1aef0fd6463809b3c6c2931d37b0b8963cebffcb476b23d37cf3ed25e943f1c7453f307c7f8e07398c692a4405ba9bdbfd5b9ca61638a9b8d7d688766dddf2723cf4363709a3c128dfd0fce2f775a285c438831d96d9d697e2928066ed9a012c8d047bc7d25dec8bee864b7a1c76d54baa4d0bd62a97a3bfa255de59659c904947298c6f2fe7d129cbc046a13d92f299c7d1e54409216bdaa10edceb0a1e8ebbbea32de6bd2ed44639e7a6e70a72dc6666d27c9fb3b57c484fad0f992bac9289e55d37c0f8228a36f7ffdbd4befa63d44015c1b055e41af6fd152cc0f156dc2100c76db45ccb95bd455b7f1aaf53bcd203b60e97af45c796a88442e4f6419f22160c5f4592225a3186e7f1e331956be5297ae258a2686d8869bb38d3f405fa876838ae06dd83065ca95ccc48302cc77a40237d6eb17114d94287abd8a10a8cefa809c2cfb4c265cdc939f0ee5a6d8899d38f0b1fdd26f3a0c285aa96ecd95aa95586102f2dc7c26a18ade3212268afe7a9944f7b14aa94f378ae9d898cd9c1ee1340077f00229339288cf77f432df676fb9b8adeb410fe8ca72dfb12f9fa56af5b43c97f7464c246827634d76f22c1e9a64a939809a466c651f5de1815350ace26386c1fa90969452cc7f084116af4b676ab02e5b29ee5ad75c72cb5f31ac25b3d95d5de4d401323aefecabb1743e377c5e5633ee52e019ddc13aaa5d181e3bdc1a33716c0e2c91f4869b5c0d131e0107b11d51d77324050ae7b96a864b90a110511a9ab51d38351e76e05b89533a43b256a3f328ac835d511749be9adee399788dc38177ffde931fec0e3d395eaa7bcb91790d72b29a2ca119a60b587c10211cd87aef32b00b92ec7c79f9baa33c6b3e6915572756a270f172ac94cfe437e32caf9368d3176dad7daf32922d0b95bc1b8d4bb408869d81818ba1e4301e5d5c84c6ee1598f89be860d901b190ca18880d895ad8aeb27169202d2f336324f82d666829d03ac63f27a9c87aee4820937fa607e7dac394d6cd2926194f7b05d136c4bdef14deb4dec3a5dad768246612dade0bfc14ebf42ff98dece118ca5798ef8a77fd4a57b11027beaae119f911c477836afb42c4fbb1eb6b0865c91ec11012a589bd00660ff844cf8793f29d1fd02e5e85b62c70ef3c3323236460c1299fe1b2d0dc84ed277c1ce62b02b486cdbb0e5b722379f82a11e2bdd2886b586bf12be924d5ce1a6086dbd54b9891a0fd32d0cfe1e5d6061d0956fbb75ff45080c1e72d396ec00fa5fe18f69df1f9ffe43dfa13347eb4f4be4932e374bb749915b442a805a57b3449c90d58cee02d57fb7be067c4422dfdc4abb647a6ff4a8b0a5f22e6c0b1f2d2f5dedc2a000f841e1adb8cda082ad40254b83a2a6415d35bda51fca71a3a63044cb321ae5be2b0ede8669b4f24690638713bebe151cd4f33d35ffec89251395673ab3ea30bad5c6a842962af0dd3f851cb9c5199fed2648cdeb094e38262169c5cf4f097367e1bbc8085608cb76453b6f6bb562a813ce4a6c74d356c731b0b2e60bbd12879b123dc578e72b447aacd8eccd910d953b76ea69dfdca22b57ef4a9b578e98dd7b4a4692f900cdb2f54978afa663efe3aa46c93428cd9bee55035413b77a8b4ae9da71f5b0cc646e28cc2de7bd219021951666c1a83d3b939fbb359fc25dfd329595e1373a8f5dcde95ba4372022aa6cb9985f37f618529ea4bbd1c9e0d8e58b414d1aac2bfe76ae5f289a6bf036660fb4fc87e9a1c337425d9aaa89e29c9d5121513a85568cc6d331a0f019e6a332258233ab69ff0ce401826f87497aa189f50b40a709805353f2958953f67a3be81123400d0f9b52bcb5cfd0e7ba32e51adee060e06d4d3a531509e37592f00030c70ed1a24ba7e90bc74d9e9cebf8b9c7369d46bb753eccb6a2f3c5ad4238eb2add7b3ce49c9cdb05c5f03e6b392348c64033f6ef4adadd801dff1acab5efd42202a4a89c68d5baef5a65ff1e0ff9a94193551f9f2d10d50501c70fac8452164b11a0d9fc9003714b176a9ea0c9bff7ce37556b6383b0edbe707dde8903da0f8d78e2df49540675bf52893dc38fdbc8608258ac12135047aeea026d27acc724947cddf4ae846fed855d69bf8cb66173d0a2c97e71233dafa2a625f514fb0bc5a2bcc4201dcdbf0e8ff927f2d71e73775001397ffedda62ad2db105ab2e229a93ef60034eaa3dec4debf84639bad228565bf5af0f9f81e2c46c8c7fa42e4943295e5ac3e990ba0fd83d228b30ecdcd5e894e097f4eb6536494d83de751d2eed7d4f9214d15f8523be1f88e8012b2d4460f6729beea663d36080e1a3f8ddff89e718a0984124564eed940c9b08b172fcce4a59cf59b332be3d3ebfd71c65995ef33131a8a7c95993a0e17c16c2315f8af9646d61b96d062919d1ca7464d12567d5a4900ca5de6f76cacc1f74b72ef3cb6d6966eeccc33c6a67a0d4db0647669ed316f08b5f534fe117d01a84f5af8c70662d400bce7fa2e2fe249ba618ff405bc71ad86da2d6ece720d92318f40a0aec4f88984dd363e33324edf871907b39c5e2dfec2ee8b33e78760000c05728fa19d74764fab212ae35c4a27c241b89bfdf419ae7905f309990d55c040411de6f557cf7a9813e413643acbd643e8e57db57d6157072d9f62ab1defd1c00d3a5cc022f8016dd31eb3c58559bbd89d3b532a1f67a8fcaaf60fcdd6200973b183bee5c07200bc921245a11a3160fea2fe9d4555c25f45311966d13be40e433135709d3c502e02964682d2498a2ce932721fda36b5c20bf59c231b8a120ff867fef6c15fec0904d87602d36a9541f971ec40c8386e6acd6d35126d7ca6a90db76dab719380698358c97e19f7e2109d8614517f4579d904e1354b27b59390a6de6e0be7bfa90f73ed59e36d349f39df51caf657f4b0f8318e3f152396662419d88de5a62fef4b979e5ee3191b6098ed59f807f17a9be57b23fc525bf9ca4527fe5f4d82aad8e6d3872d81bb802e55c5d8bd4bf3adfacbf635157c832e7973e0f7f6f97fb6afe1e297f7d7ee54e521eda2cd1a3bd1880fe0629d4027ac65acb60f7ebf8521a103a0719e27b9aeb1b94433a828cc51f3ab438a9e7b81532d9bd56c9d2cdc2d3c47440fbe140082af015562efeea42376354c5cde3b509d3308d315313d726333c8bba68439bbd9bcc0175e55bd50ab217ae802a039dcb326015b1281b33611b402157c5412fd598c3f520ee8aa484ea139d1a1eb122eff0f2c14e1cb967a184a0e64d6718d613328611c977cd2dcc1a7a61f19e8879fe535cee8b3a596151b766a4920ac5943d94e99a5a10a55bcf0562e5ad4f7ec64b9d3148eac1c87196989bc531202ada39f00171d446664b336e2d68618457061e7924e8d48a79178a2f093ad78f63d92682273f6d6010b5587873edff1a5287bf94d31daf0a5fa8ff2558f04b6bf751a483c2a0e88d8f2a6b4ae3268eda66aa92e13f8dac55fe0a1b1cb7a8f50a5c761be1344a6e7081bc7bcbe51492a3c701e6b0a45f5dbff76f3433d5c5247b86b25b5e47fe5d632637e8c5b905b3b4892572c7be22252555c845b25b59b91622e74c3acc68ee158263e66db2ea6441ba8a3dcb1bbca7c66a947b5a4e6faa980e63fb6d5fea2b9f111e14d7a4daf07a12eaa7b9780ba94714f9e07ffc5d8f6ec0422c6f76604f80efd3f2bd49cdd952e8710b26d136f469a09e318b9fdca3319052d13bdca77cd4ad59a65a18c7f81749fd379a752fb23e7eb5eff25e5a7b40ed697123408c355ee661ea5603fabc29fab2fe24b47bfb3876a506140eb3aa43b0abd4f46eefd309e635c0ea2e86326a00072eb84daa69f2d6f3e832d09bd3fb81d3844944037f7138ed2305fee6e7b608b749cb37e35f2a2ec603602b56eab04e110243f5788e5703011e02db08b5ee06e30395e35b7ee6d3e3c98a349c306f975038bbd485bd6442ffd19c4464809ea6da5334448707f1e6561bfd7f2f1b98005f7df50ef641d28d28d00d2ff78f6b99deb36ff8ac793caaf20bfcbdd2debd5cf22c870ac03ecdc62bf55ce1c44013aed49b5306b26a2027b2fa69afe129141f3d738db70b9be22bbb5c324cf7bc1c7171a212474c9be705cc5af2248268321ff0fa43ec0d97f8b7bd19df11df2a6e02bb27ac10482014b3cbb3c6523352affd4ef2c625cc834095418ba9ea9f656d2ca9c86d8dbb852cc098298d10a44e0f8afe41d8d500fdf8ef04ffbc3c0f6b1a703189eada26eceee1aa0160763739cf5c76cd2a913f7bc7c322b6910b78faf2354652581d90e20e7de7a83ebddf910d208dab915b382190cf3a1b53b0172f9035ea972ef915715ffc13a0d7d5d50e34b469213eb6711113260f0915df83d43489aa8348ee1b8e24bff452e3518a78ce8e3557d3d7dd0185e254c65319651175bcd5e90c22b6d4f5c8f96d698cc77ac287063f07e6bed604431dd64afd8aff21f4e6de14a3e4a3e22b15976a447dc5661c1eb4d857def33918fddeb99906b8ac6acd0e6a51228bd72eb883a7334bd87f943ac4a02f9df75fd9051ea508e92b5cee15c14af917a95aea8c16bfbe578ce98af8fec6f840312c29de9d7dce2e73a38c2d4f96203a70db5552312bc9a913396e537573a896b69252bd8e6ef0396ec6a9356b58e4d5034228280cbc7b0c4fb3ecea05a3a5923352a7bad8f6137203a92e4b098d1df4d4339480820885cac54bfacb68819cc2abc4ca58be9ea2aab2af6c1ac94ac5a05c2196c5a94df442944d84863b563c9cd54f8bc87c39a818846262ba674f6e987d771adc09514dd133b049588da467ff3d3f02bd0deeb0b25f4e2bb7c46486fdb98ef485b2660249cbe2820f60fffb54f1242ec156a9d4ed6bf8ebdeeb7bda970e826b06e790d0d9f4d45010617f1251d18c5370e2a8ffa149c64f1d82795f31508924f6698f335290ce87f268a34ac5cc03af29de09b9b2b866d197861fe9ee4388d5fe52f7a9dde2b2af29f4ff23d4b27bf3907f2232392a48708b39e4458d4d84448f2f60286de3c794ac77383971e6472e92f8f9452d67fea25d71bb5c4edf1eb0f52dc332ca7751d5f51afdee49411c8904b5550d4d70b26f68bcd716eae96ddb240dc2b64ccb1cf3734c639e583df87800ceb45aa10cd8a0bb76a43ebed3a28daf873ec9e9d09c3a7b1e2d74bda440b73499ea4ec24548978de86d408f5efa505c8f613646c2c848601458a4d69ca5548075928f3d14b54b6ff9b5e97f81927f95559366b7c8ff74a48be58f291c9c2c85522e05205a1451ea00044e9f4f282a68d8ded7d8f6b49e12f26735426feb1b5156729214d6dc403343c92004e26eb4b10e56df0c94bb835c049403e3fd53282899567b6355088cd3f31bda04bade9de44dc7a05a883559a4cbaa25d4ee490180c85f929f4a10ecd0ebd87831e30d7445e981d7ca911b8f1b1f54e43518813df160c8741cfdaddcb11d6568ec280de550f27bdf0f8afbd063efc63bf90e8e7c053d3a674b045645cf2d471b6934b61ea50ad133ed743ead347899926bd13b5a298cbe478840cb1d20fa4fc7fa6a4775ca6bf80e8192c3469c78e09c9ad1d410fe547a644886331a3cdd7ce3e73d2093ca5b75b9fdc2d39316cf9941fb4105a019a6ed7d7ad15ce7e31a9b4f8b777dcde01cae12470e3927520058bc8b860ef7d835c3d443b6c2b9370ac06f72aea58adaa416674baddd5ff83c6e7c05b0280c1354718a2a41cfbbe3d458769a5fc808f5c5d6d56c4d35e1028bff6fab25ca6eaaf72bc39ce998c754fded30c936a0819a0541b29a1e4991d538575bbc8ab6c9501247afc19b2a4e4858f511dfc899ca19c490e51c4b7d9e5d0117dfba908b60c70473a8c13f810b7a7d326054990ee5ab48a6cc6409cd8e2d9784ac4074ce9b015e8d851ac0092118cf5151469cd56d2edba10951398d75674db52b0e39df60a5e6aa56f2a455221f4eeff8d2b501547954c4224ceffed532c83ac08c953bf805515a9340ade312bdf2f0271dc70719485727aee4f78c8650ab3bf81580aaf2c801a08ae58c29c5939915550600d9ceac46a8e6555d80fe73e9a87cddd9d37d53dd80ef29c709ad1f63254ec08f5181d36b46c94a1ef2a85e2f138bcc450fb5fba798c7160eb031ab795b97648f09d44816cb8ab74c38260bad5e05abf1156f79e66d10280f16acf87e6cc0f5cdda56aede8e13d59d1b69c93081c33f3030ceba5aedd4e94bacd9929247c21ebe629ab4a7b05ff9e5bc1b5988dd03dab43ecad53e392eca1cd4d452bdc7b814d4e77a05a17d62c4c4cba5cb23ae848b98b1d9f911361297cdee077f838c1eaeb08e88ff3136153d8f34dfc43af991a6906b6e9f535c96135a79f9bd90e9077655d006d36fbdb1279e767a3f6e5c00912826a7c4e07157d286ad8c94028b4f6d5f5658ac671583015357cc899d7b4263def1ffcad24e98017457f3aa9d257e92f934dcecc0f6ca91f7aa7257eaba4f7340f265646d2c0235e2c4db49b7ddeecf863b3ecb6611b59b24b91b5b8b504ba930eb1f26f05a122364ae43e6ba230a13a88aab61b77ef81a69aeb371b66e6036a9110e4bd1b54b3823b0ea6a94101f11a220ca8158838e43334b07a79360c05d46aeff10df21dd52b57b5d1e696d8c3e98cac761a4511daac49874c34798158a2a5b895574aae8dc5d0385d27be732c7b30cc208f01301b83b72f59b4535ec898688563fdfbdc53d973f64f84a960e9067e140f5f20da85d6571c199ea96493bde298b660ba83a6162d9c87c7ceef6e5a64f026b8aefe1df4caae7ef8e6beabad99f54444039b8121829e33de8e9b1cc0b0ccf3cc7a469499278786cfb1f5e778cc95fd2d15756e1efccaa7bf072661c95560882d99802e53032029b1387ff4ce6742001f3caa0ff2463bdef5037e4c0673b5f23f9bd68db5d15eba22a3958ea6a302f9dd87e30f89bd63f39d6852b3b002359422354fae7e16afab4ea86908a718443e41915cbc1a3386055fcbb514b1d3287b8ec3a7e5af0943e840c24d2c2f3e2ecef8b902e9d9ae72f73647f34b5506d38d9348c12ce4c7eb8f0869b08b1da071d69303b0bf128f3f6ad604e25e8a1236b93d728752efcf72c6244ce0928568873470563c7554128c3596fb89899d878185abd67676826d5bb337d781a856eb5f7c834b008370158ba3e64520e90fab3f19a5cf5d1e46a0a8d1628bcedee3e42916798f0b9dc146a3eafb2f8908642b222124bfb69dca6fee0b967caa03451a5e8666e760f231c0f4ac498fb9059208b65372195f2fe89e799bb5157246b72bb29add01f1a24370ba398c1323ec61263b5f502dc704657c31b3f5357a2b90db1fe7a5aa455ab7025cdbb18635a80accd5a11e8de2c5248c2c39c2819e55e2b2d29e55b0c6f1d58d664700ee3482d0ed1406de6587fff1de2d02d2d25484e41e4cdc568b947736e90a1d362a6c3739b16c2d5e5320722046d1c99644dde41cb46e4552a830388d60b373160bd452e12571459cd8dd03f4f65b0d3a441446426530b36cf5a60e7441995e7b572adf8de5c846c039a86b6ab63e107e590217a3c0ee49c42de8d4555b8d097aa14db116d9a20d386ca102943278f49ddf7dd7ffe28dfbecaa9ea14425824b1baf69cb5ef098987cea47e4b38b473266b135940dbbed1f49b4938bfbf9cfd72f81bf8121810e230a9e505426a67b40de3fc7b1d13beba2ab6e1fd73b563fccec1e2de99b887780ba6c1cfc47af6717a1c00591fe014fd1b77c03015407eb5dad7564f86e345646c09f4fed4d1197d10fcf6deab831136638496b563a2392db2aeffb8127d3a166a72b50a814b11428d8797d4c28318512607c88d5cb4fd0d5b3e6dc0f9f330786fd557732c3188164254e8e7356c1781ebf94d7215616382af82020aa6d8859619f5463fec56ec7a40c0844316d7cef48ae4eca8f906054d0cef8fa320b3e429f7bcdd071590cd6ea4c2cc15ae3b830299cbddc8296eb74518c62dca6a80c9efa27a470ab3f04e6f525f93e8b16a80d959346492360f5cfc8b894b4bc9063d30a80438129f696dccb9320c272c767675c8001c3ebf9ab185490ed15ca1b1e0a482608581810bd82cdc6109be140d714224597cce1f5362d064dc866c45cee877c33cf98f1ee68321864f9e3455072d0e135d2f94a9ce21fd7aee9a9feb1709293b926be95791e48f929fc86a69bbd24cb66dde97be8a111f1b8833b235baae5cff08039ac6c935287996aedd060eeb936494e79f209c827c2cd122a990779068efcb293053c72b9bc50ebe3157b3507a5bf8afaa01a5a6222104306945145239b886588cd852f972b13d7290ce026b776791f42b7b732049a2f7c3194dd4dc2945d1166d0e1a22b1e377aa75e98fb43b7a60daacd86b3e6a3d9b39e17de9acf957e98136ebfcab9e22634a7d87bf954e655f07844fce3cd25fdfcb1ab2fb05b8cdee07c136966e2eb29c5c7201d8c81c4d76bde385fde5f67ebe6ff012318725ddd0c2f40b157012c82121412a95454170917e383bd080b7a9c4186da4eed1700d877461dc9e03017c521802827b9c600d823e36c5954413fbeecdc8206fccca522a1fc472606b857de294af9c2758888bfb7b0a5367cfc6296340e217b46e72f95d632595688f0bb64a664cb242aa29a3d182f3d0bcc87464c7ac7e0aaf86b8f8b7fe1ca001f6f29a20f484e08b1c2c974390b31dd032856763b8b20d4a3175e21f38f54201683c77d36d599c89901fa3278e35acf06855bcb9cad809555d0fa50edcb4c6e69095685fff2b32e26adf56bee7b51800e8c102f9418b88e0f64fc762e4fc9bae47a3f431fab2b8824b3999bbc8756969fb58cb1bb949eaa61480e1f42b62840ef4568fb5b39f9a89b9aaca7138e082914ff92a11752054d760015a3c54d48320c023c2e50d29f437b270317e37a181866e0491d12e93ccfcda82c9373bd0f6ec0b7781e73157670fa826793442846d26be47187e68ef7ba9e06f77ac2aab995b4ef7d0ffe69bd3064a9f0beef5ed0b76ec1848cf47637ea2c41f44976e12745880bd9fb070da94de4cf37ce221b35d462473df6b1306acdfabd388c93c54543aac08c03905ac82ceebb5ce2107021008fe469cd97510ddace8ea85e3b524d272fafbd10a8eabb677c1d72280de79ff9db620a9af665b561d092aa12f8667e3e0a692ce1ee70f2baf1e0d6c31f9f1d447fa7511a49209978e720910398f623fa4f397ebfe325b1b56d77c9546609405b266cf935ce83ea4814744e11d2bf938b0dfa27866c8db5b3b3774158bad0f9db7de73a6208c3dcd98b349f2ea52fd34bc862c9a7669a10465f7725223cc1af85ccb25ba753c475ccd42e0a57a5863eaf4b533aedaec066d8106a8198f9ab6678ac15250315c310e066a1cf6e86df13d6dd263ac664d90568c1ae06e6caad58369226190412bea321a9342c5b3da1897807cbeac36c103545b97e1a88756d033e3e190f7b5f60fc3143094802339bfdeff22c0b16bea492d2d8a1838b2a27b95a886973a96f44209428b71334128e738574a15c2efe0f420fb0f0eb58e7b3e525bd379a2d41bbdbbc1b97fe394398a536c043182265fa1fbab0886fa95fa2e45fb8f58d3cded73f02b3dfcc7e21c75d9d9fc64ebda62f22a7a2dec00ff48362ece7fa65edbce5b12c6516676f40fbe12fc20f8967737fd7a8f0228597cd03baca5e0231daa3097aee2a05b88f534a914ccea926af780074ca7b71549f8cea483ee931869790c3a1c370e66771458cf6f8684e19a1291cd367c9d897a5ac027f618f9ed64d1c4ce6d1c2e10a94c6ed19b9a8d5786715944b56625a4ced75ece098e5cedcb6fa2769257b8822400b3f1a63585abeb24bbfaa55b1e68195dc14a3b18c2af29c1cef67548a2edad9942b56dae918662bbcf079ddbca16862ec4188eb2ed75c6817670fe40a5b2be094f4501e619138a537cccfff35ac16f44b38e419f791435539b3e9ec0dbb72feacc7d9b869992e296445ed7d336e152b0d619883e56086b7c04542b611527169de659ac088f30a8a952adb74e84adf0f12fc8ca6b3e2771cad7eadaf00babbc2dd8bf7ac3de92aa4d30bdaf5c8145b5701d54ad53982f9cca840b445e773925b7ec07ee788ad6cccf6ddbdcabfb0561b4331232cbd0ddfca3a90a3ea51dbcf2598bdee1ae4b41c2d0d606097ac511d8e95d064af4c0eb6944199dd0e51bf1e5a1b502e728d933ccfc713afd443fdee098856380129ced47995b791358f1f7ad6ca9ab432149809457b3364b81fb5bc11b8a92269d81fe788a29a46f4b8a9bf2858c6c61a75956fb70fdd545a8ff3efbb7f5d35c65cd236d40ada5e8e0a06c6a013b7a5fe41ebc804d0ed728af3e593fdd9de108bda810151bdb52a5a6d605b183944669df1ca1b410c6ef83146534a59894c4323d739969892a207f68ac54caa11e4ec08e2b16da2a77084a8c0ed37dad1bd4e9879e0f811c3092f7ef51b74c6afb4a89d4f7165936fc967a2a49088f5501dfd5de4cccfdcdb101626c34cb6a65741052472b958fe3c29348bfaac08b0bdd8ebe938b31d28ea1ac040797b0b155f3b5899b48f697b53120b63e05f42986dcc37aab22d1ce17621a2f3fd39e34835681678df1d42bf153c37bb3873443edcce9c7fa68a0ca503300c350aa695d38886cc33fc91fc1e0da995e749383134e340320231a5d45bb9c2e08d07d3a1f7686e3589360d3b21a6551bec7db7005660ade0ea3d5d54645b378c98aebbea4d684f9bd0c2cd51b79cd0469bc1e78a8d11c678f660cff0a25e488aba1b1ee49f0b0d0c597f29226e216df6e372dc5da3b66da7446fe1278d44c93e5ed0a7134b9941333f99925af102faa32a2a36b9ced51c0a7ca863a621cad7e716c4d60caf00c939874bceb55a6246297186d66dd44460f61e1bb78f5386bae9bb5ab147b1dad50c1d726c0762ec10fef373a56cddd86e97a3ab87de1f3fa01b871a1c0c983ef2caa22a2d142eaea8a41a70839716add273eae8d1fd8f0358a2ca6272493e599d866b1424ddeba697a7318099f64098b1a5994a112271398bccf9f4482f0cbf5bcecafff9bf25dfe9c8c4b5828aec8b9a1ebab3f82dc9c205b9598996e4dbac7039d6b2cb878659396854b2501fbcab0e250df6ada83d370e88409bb85df37ba0eb299267b5faa7cce2ce43f7cdd348ae111a31f57211b7d7a2a3836f156e6bafec6fe241a7408bd56c2b660d975b5fac9b07001ca279fed118970cd3030463316797258b94e1c160acc15e5040439f2099768df6cc272e6757aa33df57afbed2f1898f382e470eff17863f46e89b8a84b566908dd65e006573e33326a59f0cec0f5cb75c9c3e44facbbf76d86492b865a91b2e789db2ae8e1ee7c410932c552470af315e9a5dda70e6448c749bcc171822ece3965583f8683c5d22f3facb2911dcf5487f47f91677dc07825e64fced078b0b83981d024c0b2a6e50fcefdba65d53ba7fe740fd9b2ee69f3d61e99c33421e7c184333781d3face63834d7116d8fd682732a98b235dbfaa07143169b93be8e28d1b1577cfbdf3a6f66c780f91ae773b048df83ed2a8a3ecaaaf371d741e01b1d190a0ec4f5d524b9b69355634c7608ed466c377f7527627906181fb27c77dc7d235d19bf29432beff021bc3dc0551531efd20e2ffe3fd8b42c4843fd9c4e5224298107895babf3cac6c12b82c5b1effa348e2c12bb62d01aab558321e2050ce51190a6404eba1c7a28eb8c5993fb3feeb6ef5a14d2632ab863768db78fc2c483eb1114aec73997e0252195d9fefb8ac2e8070e21a6e0b2faa006cf2f8b1495307b0284ba4c80e87dfd3857b69d626f3471861133eafb9cad990506d749caa45f4b6a4828c33a5e73ddffbec0f13c8b391adeb4ccc257b5e30dd90457d09100a96ac502e467873c29a49c3c31f6aa3924b427cac07b666f6a522fb5dfba78501353c8cf32f7744d224480f14959464412ca2ca219c901570100f7c1186961cc2356c0605eb88e66e57af0127be6c83268f7f863b3295124f8e2233033dc5198d3de8b830ed12963b69d55c2424cb837fff9a5b850c2943fd887ddfe8375ab1d645bccb34888e478aa71ee619ccb9ae5c68e1fd03487bb275f03ecdef765c6e9f214d78f9c9ae97536b5f9d2287d261f4c009e8c1cd37b37656d4d96476dd3838842b16d82d53a23d7800aceead5e212147234923a80b3164b9ec12ca9f97bc45ce7e2842a32506c18b68212d2be27a2693b2aaf01065015fd54e0f665a6418f7ae1e9fa85844cbbd741d97e90729fa6028c85dd79b40a6c090fa79dc98ee8ac9a8e02a9b322f334cdc04c7eb55d0c01845451ed82f0732a4d8f017abd752c1b32f6977ba5da8a24c7b17a7fa33bfd3ae7d9801c5b294f2c0df3aed6163f1327d296be2c4d5f038d7ab37a67a45d35a8ca1a399eeaac1a576ba9453a243c91c0764c4e23c5304ac851c4d04e32b03bcc6c8eccd82ce089b36a7bf9ae162bf316bbc3842624e71bfe617d90d8b39837b6f33241b489fe3b5d23bda8207f56694cf0038171a01c86e253da5790b0a77eee3eff59d0e60fe996f951e55a460edecef551130fb594662c78e5f25701d915ea0334569981682ea0f48d4176ac57737a5152c468e4a9506deed30bfd8362ca47aa92ceb5c19666dc7bedec98e6a1a811fbbef68f80aa5e346cb034ad25f719fe70a35fe49fe76d488501e252c9e58e1110911bb08b8dc1d6a0cf3e2e0fb267131b7e2dc72cc257b837421e04f8385621c6b3118af41c51c71fcc62cedf9466b105c125822156c78bc9db6802a2174640462c43c4eb4e2260ac003ca053c0b04afc062a087ac3486fa6535a0160c566d082b47effa433764e9a4fe2f36367fc785ca39e22b16cd4d559a35bd3b363f9e96d0dae9369ec05db3c0480b70cdbd66e5118a21e769c6b672c4050536724a80c6e2a40682da87fde0238ca789c8bf84e7149adface68b20031051aa5c2301deb3f2fdb6ebc64e186a3fa7fdb572ec337ab31e2a9c7a55ac18039dca5c0b2e7f4435e9b2e820d3b73b0282292543305186ee3ca49f86678d26a6e0a6c96115709860563ab405bf9ada657821079c4fca4c99b3ed991a3335d289d8f8c8dbf2e30ab4e411b20b1f7ee9ed2c568295ec993ea5eeee9972f30e5026a44403b9d45561518a8a54fe0a7e0b647d2528cb1d1cacc5c8d5d81410b68243b164033dc4c28866b87df88587ab31f5c3773b826c603c648bb771a933b68d20b9f8d6376a361f4130192e3750bf6579ee35653c893d6a30c5e4c15dc7ff8093be3abb1efa72ed2e5992cb50608921c37e737ec7ed2355184d2172b49be2441587a24b0e4ae7b6e25b7c1dfc35d6ccddefa4208a1e08fface0f6c6c9d2eafe6e7a68b70e5d5da4a373e090aa6f879f9224b0b535d293097a90e36382df70b8de0cdc4fad30d19531fc14621c4cb52cd213f8702266ce1390a89923ca5d327c7cfa5e7b97ced1b666d9df38f040d17fc2b8453fb9ec1aac7b647ca7d4addcb067325cdc657e72883579ea4b9c4bc3dd2c6e600efbb0e02f27605ce1c3e0603fdc6750692a4b044ca5422bedf56bfd71c2bd21b5c391b9b5c304c16f99c23394bf82e11f1b1fd0706adbd139a771b65d016c34d9b14973cc9fb984a91e0e32d32572023cd0e2df2969480063b0007bf5042bf6b48552d9184051e6c57f8b04e4bd9393b1536a2030af75abeebf45e97029163284cbe14fab34bb8313539187088164b795b807bed35316b3f939f0f820eb9d2e044c0f047e368edd92fffa5b9df300244bbe9ee146ff557957272e577343faa259eda93a5f8a5258a4971a6c421925dfc89c43e188ca796b2ce5030fa453412b9af14e534522ea3f0ba36cc8d571232a4f95ab1a94a4f5b93b20ae3348a00fe826e39e6f413a75c7187e8fea6617dacf13c8023ed96c77ae3b8b574923e9002813cf58462b4c7258c97a631b5de09b813c000f568d7a643c49799d70e5d020c91f8b205c3f8e8a36e978080e7e51833d652172ba5e631fdb64af209eac828bed80daa93079d73944ae4e97adec399d464d02a9f7712396f7cfd18280c8e2132451e910d34d8c950e344ded74488062fed6735972f21866fd0197a48b3923114d947bcc4f5d5b44558d61194e0044a9f71ab0435221eccd7a642347043ad9154703948dbc0658c7865f4b3b2a353af52c746e569ac4c19ccba832c52ba7201f57f95704b073f1f8213d6136b5d2733712788f498cc4d9e39d1f14bd5ccc87645357a2f9ef8ea10966e132f6e3980a61f5cbb2eadf1995ab83697ad6ab235217850e46bcd41c0099edd56791aa20c841506114c32ce71f4ccbc1ae8b99e150d72bc8707ac6db26469426157714d58d6a8cbc81ec20e72116d8897b4367b665109052f48059bc6e2349c602d5959103c6666164b1f474dc0ba7c8045f8320e191fdad4189814db1cd5e184d5508edb5b3e5b9beda01c37569828abfe53a6de295357da7eba4ffb1af751390556ac90f26d8f6de9367d7774031ed56531afbc00d72b2b922857dede44461b90cbf87019e76a2cc35ff197a2c47e159039a2dfac266d30bf18c0c2a783c3ca752b05031c86869569c712f8693338ffedf7d69459b78879ba5f1e2ebbc5e9a89d151cb1ecebee280c2b5377f87aab8365fbc837ba8e9758ef85dfe809dbb7fbf0b8a4c10eefbf2b807e08c20a23b6aabfdbf81a7e482382bbd506248620e8b3c973afe9a9fe9a3954410dc99fe06c21892fd01e31903826cc1282f39089d4b44e0924569867420d0af9613f4a209ea3c22c237748a3ed0474896e538f891b478dea863fdb479cf8c24809f1758da20cb493256ca6ca26629bf4cf912cbbc10b6d79784c41fba13932fc8de4d0ce55e4a1367de263dd0ebff8c74072c8f5c231abaf71ea8cf2495eb794cdfb568d3b39aa4abe886c761c12fbcdf797b0edc28834630f468b77db4843398d8d955f8bfcac0731f44df18bd59da78e8bb29dee9109940cc77eeb5732e54946285ecf941e7fc5cb4f9bf72d96f8d04a8ed0a895eb5eb83226262fe8c5ed4fc81fe0c120da2b2eb8a46b648cbbc910912fb39602b796071c5faca4ad850e6008571be3e60cc70e1a97678103b709d124d5463dd21ced81c5ea7735a67e099255182849a65d60c1c18be3830bad39bc7aeebd133dcb8aaee281bd8c9c90bef66e515707679a93bae2a8ba35ae2cedcdcacd068835134b0b426d2def378dff338541f816f17d1ef515a4491913c291d9e89f88ba28a2107ea2e25d96f2963648e76c9179155f0e2ee33153ed559b3eaf58dd0abe31bbe28145958d20fff32878ee65bb967908e915cab575f86ab72b467103f5ac0d577b6861cfbe1a2198c0d9ed0b86a190abcf33b09191ca10d34d0be8670d09576662d029a101dde6bb874b60589b7e39647a1dfe597d7446c58f23a0acaccf5112bc753596374d9fa559af468e3a481457b5c83d17a6430cf145070a4a9631bc3cbf5a896c09c1587729f6a422fa323859c91e0173d138eaed8347c5660597714c7c1742ebc82937e200ed1ab263714766b234daa112a80f7b89ab378af1d3dba96d5319af48fe3a117c228107035b692005a566b0b3d098743dca00515e5246e094fc2eaae06017eba7f006d568433ff15a3a9fe16e8d150151187067148ba47eb385de6038e1b783b66385cae71469c4d171dd94e11a5dab162ed03899d971c637d1699a6486c8dc897a6715da52b57bec71e995b074a89b2e94035d272725d88fc6ff445a28b5afb988985f4b2f9e9954bffd75e59ff8d07cc4e5cf10f93192cbfbe1d215b63f5d543d2b487c0685f304e7ff458ea82f1061be066ca628a281af247442508c3609ce94ddcac8faff7282c1d128d280f1794b283c9f618bae7a57e77746d0edad4369207031ee73253e30c68629c2ba699ab05d8d55db98893b1ff6f0207f36e549930609bffd5808ef74bbed7c28c397ca63584aa53f215f2522cc44d5482f78f8440489bc81080cfe75d44a8bf637a2dc7572a5d63166ee071230833067ddd4b49e2405c5dc4eb76ac9cc5db689b5697c08a07ef73635d8d3be54d754941591c3ecb48db2787979f162bb897b5a972706e22394b973fb76b7f538ac228211b1efcebb957eaa0990770585461d05755aceb11bd881ea0aa079f81262410a68035a458f1f0cabd644087173a8b2e03e365078a6a0c3f4eb93037472f5262ba81983f2d696303934af2c4edfe670611b1f9d2f53520045693c5eb3b6feccd545f7bdbcb9b8aeaa930f8a6d43cb3dd28f3d95b7686823ffa27284df883334f0021a3ecd9f6eee817567098ec910d876d4e984b362cfe67ee86ac51da17dd9a0fd760d8cd2eb26b19b78f90e996500a9481d4326f975b3ae4434e8d4849f33d7c556851f640266722a42f12be2f385772f7c71a565c4b8b5e12dcf2aee84d2cd0d78a7d018dcea828ba72f53483b05f485d90f0cb684d2d640d55727b9d53ff3985ee8e65e5c1ec4238f77af836e05546d779f46998ad4fe53c8f8864728431ef8f968caa284ff14c6f87c4ac9374d922a026ab6a8e8e7dbd848182091f30d1b5261abee1357122a003078cd6d6486423705789989820773d75604d08abc66dd4e32e2a7cc457d0ae0eb8cbf39e0eb71ef22eb31a2e2afd0273118691b7e03499529e1a07bbcccd6dc8e0d8d6cf18af3e09c927c562220dfd71705755f6776e598225718fdce1fe143ee4f460beccf1d9818c0244f6e2ce3b79b04b98d657dcc71d9e20e9599cbfd92d88dcf49542aa57ffd92a3c5b727f04717e2f1fc863efc321e149d31db23f626d96a0757f148583c9e6c8160bc806aba964802330b0481dde14a9cf461e85e406ad6860034c530066aff766be5d47f68e357f93b183b89659857eab358291d8c1200a4a5df7a75cca71a1474eebad6a42a541cf2ab0db992bc0c07cd335b9a735e97c597a2a8483a2c83ec11aea24f2129fea8267025b50aa866890cd37650ab11caa4284be367366b2efcae8163e3e0e6e0b158aaf0d52ecd67a35eb6baa1098eaa7abc2557deb46d1f61e5901b3e6a90d33db5b82633b7075d5622deb1a17a561a6a155d503bee484fe1aba17437eee395f8e7c19fde57c5f64ab5cf6551776181284f3365eae2a7ba3f78dc37def098c07a2550e6cc034dde9f6275f48cdae92328cdf77334b402e35b97b1da9e46da99f0eb55121dc01150c270aa904f27791df81f607bd58e38036297bd55be07a076ed76c7d6563c68d87252b6acca50580a42619d6391badf0a5aea43d1a68ba0fc14b153e373f7441fb5be445b661e11774e7acb3da9226c360f94d21fd7fdd6e8a5b4e4786e86b8d4be344ffc39565fff5342da0f39581387a1f9aece185ce12bf43d854cde79a9b2a53ab7f0b888d64df49e0e94a884c19ecdc6459e8f30c83aee08d780b02fd7abd9a1e1c0d1a3bb608e27f6128c3ea3b9b2b83a35de14667be270cf68c3ca46076940383b544037c2f023354cfbe79ad49c7a97b5903c09a62be661db9c615967f58e3dd7a79b7e7b32d6a16278ab09de5fe6ebf1d1fe595b569f3517f9e1f13bee810d2fbb1349c34cd02c0ae0fe5e1b69c359f00dad8191ca90f84bb9cd14a07eed7ddb7f44e4a5dd492ae0cfba7919c22825303842c279c89ac688a8bb242be16fca7e955ba43c6e4f4e8dbd9ebca7a28558cc71150e0f598f25f1ef5028d3f77e24a840985076e5db77c99e5b9b33e950d150a92af2b000233a68e56e82a102d70a9cc2a8d830422f9089426a096cad588e5ce3b37bae0632e1aa0a98d0a4edc1ffb72f5f575d933d19ddcf76fd4341761319fc3e2755f33cd8cb1e1b3092b0689421e6bbd2d4ae6952c0cda551d502957c048de8c0a40699abe9485a2164f4ac28a77cf7ead1e0e5717f0565a577fbd14df387ef32d7dfa7079d3d99d15e74d976f5f25a6554162244fb87e7cc7ba1d442cdaebdb7220c2dba9e24691bc61ec4270f08f5afd1304444a67294bfc806b59c5f658fd010f8e75c59d085bed7a94884320c13bb45ea0a6999289970b3b04e7869d17607133076e1d8e95777e85091c8a745214a9d3148aec34e1a824fd4f158c639b48819518d57977b0831287c73a63b322ba95ed0a98295eabe9d339fe1a202e4c790036e1d892d702bbf79a121d7b0e92ae45b2fa830a9eb14959b6b9b81eae8a8a5223597b39516dacab526776b110179de42ab48aa3db0af4cad97af65c95f56ed5a32851bef8b1421bf8a843dd41a3c603485771ec9d71decf21387956d703cb1456490722bd4aa97f7def7949e869e0642dd3b60734ceeaa45009bbc52f71dea15209a57a9aec7198c2df5d0bc5ef7d2377e032151569e50ecad0bf35bda2644f7824af06c131d2fbb2dd79e4b68b8c914700e5295ade8f4bcc0eba1013f00bc0ccc9c8845d296f6a86eba82db754f555cb23dde2f12a2fbb11a863716804cc52bced37d71aea9075e14865eaac9d5b6415751823775d76e5f2f02727ad2bca5ef4b4c7a9a0cc38fc24c4aa038032942a92e53db866dc9736e3eda24fc012fee078b274a69c30261c651c9aa6532a5a09f5a4e9b389a9bef0d10fadf2ae4b62f4e18e4a987180370c58e4a125da175d859d13174212b4bae34c94a38882d7ea84a73f5204a0935c8c7ea7f916de48662e808c896a07e7f42143ea1e37b799c12f0089d43e9ef4f1407e8aa5c0fdaa3e3ec90be5cb5e53ca17bb642cbce256a5d193b0e99a15e17c3583859f183c5558db48754df6217cd3b6ca17ab18831e28d2f820ccc43a9979f7550c459ad6606114b17c89c76dfde52f2628c9238ab82f04ac3338fe497bdf665b15e121af0dd885219c5ad494eb596d481cbc6d75ff2571df22e499f3f797ea2285765464496d81ca7b10dd029bd744896158c01c4a974d6e65385449c7225192f40e6b11aaa17b072e47079a5b490a864c98bb82ec12935aeca10ab133bab8d78b4ca2e70f766183bae8497717024a85e910b4b74d32c0c2573ad98b34eb2f1fb565f5d05130849ee77fed4f51bfde051d22a792545f584f597737eb1b2cd94a9287975df7c1e48c8e9b59d124147cae64ac829d102c59a05144e63239d370b52b13d4c1e95c01e01f8d6e77a058a1e37f7fc389d22585c791430b4035280286d1dbc6a07d1c9b8a4b4532933b8ba98ff873c238569bd96a079bbde365e3d74c2cc1209b7c005242d6f43a78070e73f30be07ec4c8050454b4c45f654e8fe42edafa3e130c82da5f9078ece5e12f725b6d756747f9d3a9090b993a72d9d8fa08ab88089e1a2209220bb3e5363e14a4ec6b00b3fdd727f2fdba58a4fd476ff2831dc19d9825c7b71046588027f13ec96de3d02a898d3c2b407e336223fdd689c24499dd3611ee785b120cec7ab5ff2fb17b0e6327ae723b9bdffbc5201d969d5cc97c61989f9935e4996341a05e51ca532e39dfd1d0b721efe5a8451de394af7e1c4856e37f5e9d0a2b08af456cd9b4df92baa536d4f94327b56935d42e74341ff560627fb31cf6d0df0a8f0c5662f88b90352c0a8881e6d88e14b8e79ea65132ba36b1f73ca1becf5ebcaba376cd28b00898763872681bec7cda51ecac2ab95787dc60b8287682acddc136dd7438ef5ad8e65efd8f79b0dbd6ad0bf057d9c392f80df42d3578cfa910f6a39a49de12eb55518cac20fe1f0c80eecd57fb71d0910440796c3d395079e0eb7da2f13b098c62af33d53f2868f0a69d8d343b12905a29a3c8fb0c183c4380a3bedf4a29a230cbcc2b5075c9d0173da1309ec2be97bc9183b1393b060f9998822cbe3f8384e5a128d4933bfef4eea72c863053f8da445f4d384810579238349f9e45d9460d4b4f177b1f61f5bcec1f33337c9a0200d3e09fac81056fe7af22c980cf4fe6ca1a09ed603d77ed183e3baf309aa8e4a9d1ffa16ac05a6a1d8149009dc158d90fe81ffcabdb3fcc25038a2d96c7c1cdf94185b05e49382c9b858c63dcd7c6da4580a7ae2fd7063ca99c816a95dcb60674df940e5814ad29512a406484103df1f71cc606b4fe515521bd1a59d5d4db00a353b8a8ded175d122509fb85bc4a8b208232b8a6f4f51990e85beac1b9bdac10771d8955e03626cac54bc5ab01dbe403ba14eabf075f9c7bc560442f9f5f6ff87c71df21f38ff5fcea3ac039d09a9743b82c7f7a71e5a4d120488610f8ac65607384eff04de0bd995d8e5bd4e62ea2de3e6f849a47a64e8d22193cb13198c5a9746acd9bbdcb079773e457033c490bb48563950683fc0765f96783724c1134a0a41d1b39b50ba6e46c106343a481c4ec90d9f5aff304aadda22740a4d0956b4cb33feb8ef4f2f381e6d9dbc31222a2a9820dd9b68121d050806d7207cc5ee322765076e2c031aafc80da2af0bc16a8f12748eb21ca71262f00b5e18cee232d137f517aa0a87cc12e9d3068861090c51e2f06717072a7320fb563e80cf4f3d49241c724f484ec651e0e2730ad5ff47c81e112747af5c9ab1ad2a42a4589fda279b9fb8fe0a722a9fb5716dcf77630cdb769d6cec7c1f14e286cde5d46ac49ec45f1f209c224100e5dfbbaa7fe77c537a6e380aef4dee79236029da084dcfea36fa1db0bcee93d535388dfe424242ef39fb4497725eb84f42dedcc5310830ef0cae2aa219941cfe6a9b138de073d60e376a2f638cd64db678e2a10ca14379e09728f551d60d99efa3a90fe41b7d1b1ebbb935e764d6e2d42f543e90c5abb82498f023edadc7638d41ae4cb8e763aa75eb44300d4d0cb0beb9c7693b2e7c510922846fbafc329da8346976cd96b08150460b34ef9d399c683ce710248fb22699dbf5996ab219a5665263641e9ab099618de3cc75b5cd7ce0dcea9f6d516a73d7def7f2a84fafa35cc1ebcb9c1dde47492c757a99ce966612524721705e258628ee71d4325e6a340b5c77460de35f82faf61cab3ea92f194d6e55b5ebb78a982730698d26d0d3b0b18f001c0df7b87c3fb5599d4c34681b8400fdcf12eedf78010a8c87a3ba16c25e6d68669f21afb296177ac895ff160d3006146fec0ef6b3113d382da9a0cfc4d9b6e0bbeb6140458b757a5426237cdaf286b404e73ba2cc5f5273e840ac6b866742a37256e0d24a215f9092a67cc29c410597c6ab79a5449680bd1b84403f3e775a061eda2edd90feb41be0d829b0c11d9418cf71015e92a40ffe069d5ad983ba77bdf4ae2d3d7d5ec05dc2ee6d23b22d454c33b45b0ddaf4a4e2da5bbd0d6387702f1c976be4f1e135cd3eca5cff5ce83959d1d6e99ae29121f9f1ad2e10f0c011a7fafba2a8200a54ee2dcb79db6138c4c901cb82d226f89173497ae4001e6f0353c9114b761dde4486a777953340282995d9d7ac29d08207921e824efd2531317beefb1b510a7e7a22d1e1caed39e6048f5d425ef4b885ccf2d635f2e10cc5d36f11abd7414b5752277662ea809dfdba2eae476cf7e9498b73a52453c382dad16fca5bba618ca81c1ae1420e2a42e9534c0fd8700c9e9c1192521214dd242c8e59c8900358b2fba654ec3896b0675586dfa3a0a1b9820196715b26d34e879b4c8518d4979bb7ba37624b1db8f2ca5dd8c3239278199353db982416b05be9817e48c774898a3fcbdc52cab303b5c5008778421959bef2e1a3591837f45e27ed121bcd1f7d5878c8713f10a6716af1f556d177b335982d0f70be6849fda3d75e503e90d000cab50feb78cb81c4c86dbb928029de1fcc4f646014f44db744e63bf20d3cf14eef0e30a4c76ff699571335d410ba5ae011fc1caea7bebb8738e9c3cd0ce15f626914596b58d7ce2d4c6c57ef3d4e1229ea2a716096b9f5c0590883d91a813aaf21b6dad11b2013f0e588313cccf2d0a4df556452120681133aa0463425e3b91328bef9a2cdf8460368c38a21beb217a4031f16845a2999355cf0abaf43161bd7feac03a359cc50efb66b8cecd172b5bf4dffdc640232cb37e4d1ac1d32967179bcc61b14fe433a1200f0d7b7dede19c8d37300f08313181ff228872c49ab0778668a370a86eb6de1a55f010e66993d4335c2ab53be75aad73a955f8ae782bc485aae692fa5ec1423db1ad33dedb393316a525d44ff1a58a66e4a19a7bd905657047bc667b8591f28cb71e5a7e52483834969f57b5c9efb9c2bddedf76fb5cfbd1fd8a9297b2ec8f55b45d7b4f2e029f51edd5dc8b63cacf3e7a737535d0ae0936a1ea23054def664d4e410b92448bdc7d373758153f69020cc5c71a17c9badb9c6a715a3df05fd92c1aa82fe25f76dfd17ea92c00249069e71998e9cd6ad9e49658ac7b5a66d659b07f74b51324461623533493d3676102c2250d2fdc08ff1de3f6e8e284564b65e292fa5d1e4781caecc5dbc1efa3d269a5c7ffc643427ea5379d1f191b99bc6039af52b4d1969f43777b7769c712382ddcdb5815db9d9b76240df9b3c4f3450e25be7857e5022da9afd916fde68b6d8af29d1b2b5eb65113c6f2aa8411443ed059f866a4a63daca77b89911368a3f70362a62ee52dfd472893554bb27fc5d7d20dad43bd7822d0a1b81e8e0c37bfe46cb675d4938108efc5859067bdc65aad0be6adf352dbfe79763391bb07a9cc4319bb3428476cc318192036575c8ccf587958807541252b926d53616da3d098d839ca8d512c78e033dffd021b477922abe22d72226707af8632f5a5cb63fd25e679c25103a2c515ddf2428e9dab5a5c6204a140f423896054c4e730c774a2325a6e0b776d6899c0285d381aaa85536b25928e1d9c332034b0595d922074135b618cce4646aa41e031d537a21006ff2a2922f4f8fa87b8368a9b53619f4827c9da345495ca48da877f1eac1472c95ed0004291b62fcb8b0525d1322d90fee959536ff1d05e546c912f3052acdfc84c00f64669a08d8461836a509b76f8e372b186e3d24b9ec2a0990dc2606606b1ab55e7a3b52772ab117912e4e0574050f3235dc44ea156b5b12b0c154ffaaa7986bd386b721c645172b9d92219b28008f6ed293c11f89dc5c5d9c1b3cdcf09c50850148242101fe22b3de659e1886c0c8e90fc0336cc8814978f450ce287706507aae93239bc58136734e9d2954d5a87f60bf9988746d85338e03fd0fd7301ce1919a0c757264501e431b2ff5d245f9b4e860ac28c5f14494312a1a8c4c4ab1b9dab9c9df836b0b5c7f9baecc8dde31d34f4bd22017e028877fa1be3df651a4c6083b121a8675cfb6a5d3af4352731029224e7ab19b0ca008fe7ab2d26745b1d5ccbb9575f558c50d156bbd60d206337f6a9391c8306350524d1237a9fa21377e19ff04f9ded2b9c1f644c21f2209fe245de2ee9bc86bf5aed319404a9830fb2f7fd55a99b92d30fb0c32ecb4d13de33ce8607109366a40fea5fa2c530d4c5b5e7e56da889e93d4731b73eefffb93ee7f8918cd67ecd89c47a475c115c43d6d1e8cd27663b6efab079f1d0abba4e2862c03736baa0777c5665eefee997bdacdfdd45f1fe3bba1328f1f5b6e1124a601a60c92285c65565d1854f89e1a72a2bd6ffa20cb40db753e3b0af139b077c172fdde8171aef55dee909a68458ee8225075b0234a9089bb0f4ebfeb82ab2a04d54083c65e7429cf0507dcfa9d6e496ecd51ad7ca3bdc95c87b21a8d23dff93a0999282e035e800969f306e3c8e6ea822ea892740e6891f2c9d5f5f3f7e5cead036c39890e0f2cc21d9270f714353a1abe8da87fbf53a14f241da4dc1ea7b9bbad9559853e413c8c9a06b4759012cd36eec6538482dd139ce58513980899f125e7dd06c09f47701fde49e3fab3a5900e821f7a3563d43e3d5e98a7ed6a22c9727b23fc1aa132762100acdd4baf63d34ccb231ec43ba5fdd8efd2d5969d6ebb3b3ca0d406870c77e46b7dd4ffdee183f6b9465e46073235d2cd126050d6c9df92fbdd802e42416900bf5af53919fae2f46d92f89df342ac94d05a1989ebe17401f710cfff2d50af247f816f241e7f5a5e65e686950b0b7d1304cb47c2b9d9318cbfcf869ec96398be7bf08400796f3f17d842ad18b8994a2fbf1b4b87ec7471dfb69ab5396d16829f8c3981db043da6b0da4405e89de9400497d038c10c7fea02e73fff88acc03df00388ea11e960598233a1da8738724dcbe87d0f81a18339152d0f2bfad8f9c0c5a97aa556badbb6025e55da70adbd8f6db040835280f24f0927094ac04a5e45eb4204a44681577369fcbf1540b416c75b677db55fc0ce188f40143ce034d7a60801ead6f6ffd52f2c03686e68e322e94808bf7195795b47ddcaf414df3e966d9d6c00f888bb94fcc2f40407fd45a4ad83a80d3677d6ebe30f307f3e6b00cbfe5e1b5a849d030c645b621efe9634025e1c0847a617309ce39a0bfa0d9f79b80acbf5f93f56c8f39970b2a2f6fdc8b3d151e5ae4258cdc128721f9e288f921301e975a720085d51531ffe92af89cf2bec28124dd3fa55ac48152e93e5e3adbd30af89cb945e84d4c4c2866a482a160b6f4c2bf028a485683feda3ac07355ae35d1bc2c4292beb803e9ab104508b8bcd31b5aaff8ff14a02de46d41f4d5f6817bfac038c7dabbdaf6e4fbaf21e26efe0d9d7026b5d5d662835d64b936d438b5e03887c4a8d32431407d5f22b7599413cf4dbe016d39aa35490fdd6ed950004835257235e41132b47e986618b65b4760b27323a2aa8af54d49db0f4ea81f4cb48d8bd494ca6403a6fe3f1f889e65e6dcc9b325e2f19320ce6db77bff5cb613371f694704c7104cc679e165880ea187e6f119f7eb797f984a2e4be4c57e85f8977796c7b23bf70b809c7d91f93ef72438a7ade1793e3bf2d390c21dddf9cfcbe2fb03128b6f84b3e49068dcf4495f54853a8ab03daad9e1a0602f8523eb5f65020edd9b487891aaaa699447c5460730d31d90fa627b6d27b4060c9fb8d8c18e5d21d4d05c832898459e276b74a66f1ac78bcb044f6d1e19fac84805a61c426a28f53645bb10a6023fb6a817f245d494f7a8a27fa601e33e98f9e3054e57dbba2b3ba348e71b4f8ef79c19fe42c3ec1ab2bf896a527d4ebbddf7d19a2a7e4ccfa311ca1571787e1aa6f33c17d4b92a5093fd29d54f6a58f9fa17dc6fc138df5c16ded1c57f0d9d5697add4baa991d37ffe56b87f762006eda099202cfe5ea44a7dbe57b530f40f09e3ceaf0a661a1821bdd739e1e08521143d0bd700575e03206d322a88b0521573d26009af8a9a01534adc600ed5ee516ba97e7245c097510ed2cad35e80fa69ebbb5836c67537e24488f23cf649b8d5a95f1f4ee8744211cd61b9f31e7346bae87e0b6b79a832f4a6ee56419a105b0eecf2980b9b28b6c946a45a6f11787036a191a0bcb2349401106223aaa2289bd20058a0d033ecf267693e4d1a057b60133b5da7d41e8ef92f1b5478df39b4a8632b569af07d635f3a1fc89f801a078e3009633b15b670255133c410e7042972b34963026b199064fa3ae4fdcd6241811a3d546912ecf242e181483dc2440b8a16545ca23cec7929db84e8c132f57009a5736c25f38966404677090f238d9643d9637e55fcdce1b95edfc673c96c977c67b66c7099d461f6378f68683fbd332f6d72d71ebbf2e24a4697e3a7677c5c1edca97446a0e50ec98c22db8447b682f0d8d26d24a8a26ad36c96df065ed554bf14ce7169b41dbf6d72bd279f4cfbe08e52cbb2c57cc0f85ff33129dfb8f75f2139c3b3c6b2bb8c2d84d7f696c4d95beb6662bed6bb4660b8a48ed085e885a5beab271dfa538c202cb007831ee0d5b8e74dca616176d1bf1ae61d1d0732abaf38bc4aea8fe4cbeb471b2f82153b524fadc1647696095abd5f29e40318252e38e400fdfc8ee3531e339a83a39fa3f0ea0836458f3fcb3446a9de1c354fe8ee093b0d3bc87685191c8255d55e2c781882fa36377dbc183e3dfdc815e4861cf5f53a600d7fab181666532edf9c0d6325ae83134ad9c65759808e7eb036e28a2459f6199c82ce717583358a053948a2a1ea02a3fc9b384a0709ec7358298ce2c9da84b1774c0e2d3d647033aa8e426e63d624ac35234379dfacfe091bd08489104b02d3b413b45fe6b1a92ac293cabddb55716e1a68acd847c01a23e37785cf850c3630c3c313ceaed92f3b4c1c2dd2d009a9160e068d0dd17b987697e38f62f1c2d3e0e74526b1d16380a213daaab241dd8c6200376be1de3348f66a89fefca943138d0b2acb1dad567d3112be33ffa385f149fb9a26755212c1e407cdc7377902d803a260be56b66ea2d55111d02bc2dc4c2831a856c603bd4760d8820ad2539f8edcc370f070e980eb542f7d97a3fdae21cfdcb89549de316f99de03a311074f881129c4005b45e2b8e31a8ffeef13102ca28bf6cca36bb6dfbb72d8c6cb5e0420fafd0656de4b165046ae4b02c3798e01a0c4469f06e4b3c4108a98cfaef65de23bda95641bd6446bede7ac813e78a646281889d441fbd9cdd0464bb2af51a1526a59f0e500619a151ce0c1f3a035c362ea4f350771244c67595e07ef56b8207764005866a8d706c8a5381bdb594deb2920f529b3e75069346e0839c23464f0d0bb57fa3e6878b31ff25b5ea8cc63703e774ffa00dc3a8d01cc74d88321a5e0e56d6432c636fc0b9792341f37a593dd4406bb11b8a919d3f81a049f38bae9a2ec6b7e6c32a46da0ba8b01bb713ae29bd783d1f4e893d1afe8b8a93abca293662d6f11442cdb90ff81bd451da53a145858504a329e5bf9caf9430d6af7a8ee20ae2dde58a3185147af51b67ca9391bdc0c9ec33a2401ce5b13a314f461c6ffe0fb89f95269818111c313defd4a16048afb0dee141501214b8fac09e9a0aad70487298a761f20562e6e01bdb29b715998e19ce0faa1fa328d131e912e5b17383bb21a34479906717d56e2609902ac88878188aa3586a78065ceef1404f0ecb9c833e3f35d6bf092c2ab594d7cf1da1d150aa865b292c998dba145acde3581d863ba629c99a9796d6f2afac64c7d16a3df1ccfcd175f25004657ed4166c0a669ecaf85dc50b07115f3e42ee55ab3cb63443ca05fd44e5782fa85cd18fc1eeffad6b2fc8007db62353903fe6e728cfb4e9cb787a97bf119723b3fc60c34dd4b3d15a494ca8262e826778412746acbafb253ee8121cdec195968c1efca88521b53a93d0a2a17d1e3198200b5c58423bb010c8c0fb65f468793024ba22c440e5dfe3c0e54f94eb29a7c5a85cf45b096819a6f8648fa105651dc0b836c5ed40b19b01f1a13fe32cf558d67cd2b869c3b4fd5ae604dfa45025e502a50c5cce06d609c822a9238e2de207bf65fb61d36c335911c252ff84468d03541abf6d97a630a9daa4576e71720e9fe6bbe9578c752ed7e919ce7136739ce34b5c1010b9e3b584e632c424fae4bb411484dea7704ad5eab1797c7069ce7b138241ed97f19c645b69f4cbfce5cfef15af5c9b542756d6c72e9b1fb247cb9364e9b4caf6869133421e92ed5b7f7742c3258ca85b2a8e87631ab9acc3edc8870189391f5945812831e164fbbd1d0da0e95c0a14599da7b12c261fd069e3e8e85d22110f7afe02cca0236a00d932adfeb5f1349b97c4298e9c31d8b249e368f1fd89ac2827c014c9697672e4e5614d6b2095258564517d0905f9347c62a54b5379abef863a65860760e8e6a62d4777978cfc2db09f131cf294cace38c777b177d6a201fde7229ab30dbfebcd84c0c1f925cea17319deacc7716a5a7ca780903baa7eec960ea249893ef7fd35ea67403ba6679e5b5119e25cdef1b81c79e82d9a14eb1f66663cd8ede5d536bbaa029687a15c9f7151e50e613739ec0c13ab237bc800c1b8b288105d7482b50b99f692fd35f0f54ca221ad6d82cede5807645a053ac19b0a6846bc3cd17ec7e433fef67dd1d5d5ab8ab17e176db7f8a62a21e75771c6c6fb1cf9b6056675ce370f7c0683294eb6452abe3d72e873e8c17f4835c92805827d863ed7fdb9c927acc9d398a2f97e021bce766daeb95af44bdd250e1d7664dd59cad14effc853c4b7fd84ea5e8bbeef0bd2fc2bb5b5ae9ce99c55acb4035cfa2018b30202034c9b2c77793f007d47872b0bc533ea72d603cfe488ac741232923ec8aaeee69fe288f5fac5335117a3ea69ee5a3c4e0e55a7d87b97a0fb25597cb1f0c4d66daf56031ba949d6fe9c04fff6d7ae1796270383c3a40bd7e477835ba26fb8ae40ddba21f66bbd74ff00c5ce339d7deefc883b6cc83af5cce1c100bca889844543fe7e3c689593fc7c3aac4e585e0bb90cdb3e3016c80d9335d49c4631772d9342838c56ef8e382ab450b1e535810d96d85b63c8e40b92613017b7138c12ab681f3ab8908a0219fc3ff63198292e15d7afa7a7fd6f88f55680334173ed6ed8540c017a582bb6c567a1d46e9b7cc513b494259b39ee770789097392ffb86cc4f65fa6cd39ada64069bcff20718789991bbc29538d24750015c4225330a5e785b3ed01a6f936af585f555959179272491e29aab6028ed1df4df4b5b5a1ad3ccc839dad3c150b23eee621829071d045454f6303a66ac6da210c58850c8c091812affecc8a61f66d018c25ea5ae916e8209f0f943fdfc67c76c1535e5aff1f52e5c9c0c01f64b4361aa577ad39ad5f00cc127ba3e1bc1422ea3684f4e4da7fd70864c55eb3cd0ec0775336ad4d41c64d6802ea2d4a05f7b6121b033f9cf3e942785038824df06dd3bb33b7be5c31a5d8db220cd120320510bf1dc00fbde62e26987a3acf3496eb85b62e62eb5fe0f65957ca049e5825d99c2136eff9dfc63af917785f679af72575e4d0def5c2a8d914299b9567c325dcec505806c82ab36fab63a290d2b9f7b1f188f71abf9819ac0fe248c0e96d6b99f95f987dce8985e7da4de837168deb49610b989bd55413e16e83b9ee5536865f89156ebd838b311a27a8dff523aae6b5dd1dcd429283a0e73bd241e0fb84f7bfb6081eda795ced77cfc9e8a9ddb9ff7db571141841e05cc3f46e517b329822dc918256a900fd08933c52f4d0f51e352368c4a1175e0fb617317d9569a266846a4985c69ef66d25c64c6403bf29c1b0bf9cf8988ce60782d2fe120acf1ede36059f572a336c942299acb52c3082d905c4d9ec011bad3a74599375bdf9d092bd25db35efae456d18c19309f1c06436119fd785e222262eab1ffd4da00d63ccbe2540560cc4af30620d5f26fcc4dfbf0a5d4bd315a8dc1550bd33f5d164eaa56886c2df4885c9e032affa1668c95cf94133b9bfd1912bc91849e1d926e53247d70158d63e7ab62d3a1400df7a7ce4f210c18aa2d53886740b2f3118fd3ecac63610a676143ebd90d36dcabe307e4fcf8ba4467f76ce6dec996e245225ef7779e02788e72b81d505a55dc753336f5f9928bd8a7d5984a4bb103ce8d46f9ae8586f83a17c531a59f9f59331e9e1bd4cfbff78d55bed26408b0b4f5c8105e4c66a1cee001fafa6d6e7f2f5878c97199d1052491705383cacc2e231f419b4284325b52cafd8947741bcf40d1c91ebe2535a4e51a65be1608b03d6463022a037179b96d460286a3b70652440a2af53b94d48c9fa6a35227ebe2f0a5fa15ceaa75bfc3229d73bdbab0e465f0ad8d8a69b8b6bffdfb16806e7848071c2fc9e12bdaed4a048cbbde5e02c73e76c097a0e4a0cc22aed56a07dbf174e09543ca7c16bc0d6eae57335695397ad7e905cdfba39cd6a0b09259eeb6792d216e06f02422f070ce7cb313e7780a5db594c708fe88b0915c4214779d1bd8c4294603c0891d16fd522b1e0d60b019d3902c4d1ce6c7c7fec75548b02446c534109710e5e7cfe7d65cddf25981fdd62088f3eae8dcde90726963d652f6b8218f7c1058548bdbb7de2f8bad9f04abd886ae634bd92524bd97bac8563d654fcab7d9bd8444e9f9c38be5017b096b54e735e0bb9ccba725415087add01cc687a6253a6c0333f788dd468f619f21bf7aedef83fc915c2247525f87ff9ee8ebf224c85e5cb553ed1ba55548b7379796f066ec7b536285f0c34e0a84d334421cc81b6550eb475dbcd0953778ccfacc42d518429e9a16fa241cca23b768828af1ab2f90c4deaa8102a28406b6822e07f8f297dd029344f70e18cd4c1d2f8177917313c44f084ee3cc6cfd755c9f1192f9d76ca7dc7d8669807f0df2f817f430b007a325c2daa197255c3ad4e7ca20af7b3f7ef34bc41dac1b889413c3c6b134882246eeeca827a41f79fa49ef81f4544b3bfd4f43488e550cf04de795e1c762b54f4f2f7dd23736b549da446275661e651e55ac39a71e8604ba66b62ac9df9b4ac9738cfc4ae5afe6c5d8730145fb62ea542f9b3508b801e28cdf6cd310097fd63e6a1e78c83fe43cd963712d5708bd05b6e1688711c802a884cf6f0adcf566208faa54d81c0d878e0f087fef16f46be718146314a00b8cf07303ba20dafc03073b9e12168fdcedf2b73fc483fc11d6baa4fd8a4af933a9270ed88db30aef271ef50641a8a5f563f62c0c78d762f1fd525356673bf3ba0483539c0f28b50be3138a952081df9596e5073096b46ff71665f175c9baadb491813fa42b1429d528b1a9e675b732927393b2ca170ba68d5a76e568b6c79f43b3ebfbba80675778ee913d56029527d03a915805a4a045148c6d62b53637880a648fe0adce124106480bd213dde0d98d208a3502d52e67ec16a57dd971248d86258591e6a6e50e583bebeaa1c90e23749b89087f4957e95e96c4455e603b48442d32c87a56e776903141887e7551be027cb54d3e78358bf972842707a238606ffaba7666c5c3b954d75893a86afbbdc0f56e7695e87eda154de0549acea95cebd0fc14184f86bfa688bfaea186c04b8287efbdbd5ca3d64aa10d3f8f28c6fc07b8ea25ef67050608dab54b8cefd9111a38a385af63545eba987c69fb124920b0a61b1aea1641140b77f94d6cadb1d84605ae8521411c8980829eaeb9f1bff12444a2156fa82872b7c8532c059950a09e8e3f3f259180ad6603997f6c635046983b092f37294d1f942c4465a215f50fd0dcea3f0db801f2aeea6700e55ff8dea0eecf0a96ac0e5e5782647aaa05ff7f0e3f6a4a6f432b404fef8910f05f511980bcfc9c580786fdba318686b00d72c56b307e622f7e5a3ad59bbd6a0c1ef8f311fbd4231fe9874be1e81dcfa4097600734dddb7de503d84acd8093cb46e85d9e2f451a1330c92642db42788cf115e242a5e6caf1912deebcd9442f1cc0f59878b795d5f9e0f6bfb1a22416e6cea97c72c3c3e1a96431caa3dff02ebeb97deb23b0fb0304690e8d270f716f8b49dc742a8d1ea1be04a1f015afca21e5b3cf6d8ac489fd8c30462c2918001aa8854295a1b411b6952ad453bf527f3186d73a0792cf0f43e619fdeb763243f3ccaa5b8654d04c6dce53816f8fc12a75693f17439dddf04c37691070ffa7a417099dbe58895d3e7cb60191c36dd477eff36ef338de054a89bca2c0efe2d896a9f387c81db3decc27ef8fd1823e2a91e35efdec970ef44dc5b7902046cbe0459a728c8aec887222ab24111571efdb7c5f1ffbe7978ef50935d9f819a064b8bdc203c23c9ac80221e9529b9272f097a2218899e3e5d7acca6f5caab0ee87f0fc1adb4ee51d14bf43f9dd7f2c654fcb67685b1533f73d2ec6cfffb08d88e02980511ce8b78ffc34abb928641e3bf2364dc363ae8afd4287166933e99298f7b91cb93ee8fd1b0aa672fac7f44528f556f715b56c6358f166afe0b56d9d38447cdec49f69c3a449a3511e45b237771d8564d566d91c3acbf89cf987c60a9024267ff21a0beb72ede1055d89791293c0380888196293929798b96e20987bf12c94298c57fde4ad5e5efac66b8a5a3188126c3ac340aff906e31634f2156d09989f1ea01b1cc3a2edc537ff9939796fec8fc4da537b6fda38a79b9bd746cca758cccfbfd7ae043b42236a3872a03b7e404086def9d27c5e8dfbc34f040230520d9ba25f57ce8740cb57afaa67d58f805970b7998be6855bd564cf0f0253db9e6ab9c6c636435806baa14a2c24093fe3057ab24e4d20233aa603a8150cb99e78b3f568f123260d772e17234e1914b0325e81d8a84029a1791ecdbb42237d2adb4c5efd27ebc973df5977248b855250a2ab6e9f78628a50f995d3a778c5e157fb88bdf1604e36ad099b90112f97eeef10578631c9b228a574339bf8941ac930443adf219bcb7b00730bba31f3823e1c85dd7469d9cdf771975e48ec76b0a1bdb25b8a2350fc4f4ae37e0be8b8897d0e89041685227a0b57e3b4df02d0b9b9fd56cc4b1f04eabbba3636ee9e0275fd253e3ad8cf1d7a165ab71900041c600107b72dac51669d9557d04a2a1b6bc1f68ab6397184debe1a3f3addc97a954d60cc9c13626e9c219be462ed88f5a5941ca305ee01dffdc35d50af7ebc1102aeed8c6aefb42841959b3ddf0a7224e7ac76e028e3eef5fea7c2b42a71988573089983874766cb05d702cb5251e9537a25823ca177e7224c2fc001239665cae2cb4c5c750e399b753e23b656ded084c5aafa760f36a8d6be5da5d57eb104001c10a94f5b7e25be8f46820ffd41600d6a793fcbb49ea4593a6703032bbcace6e172cb0c22b18c01716b6c248980a0f5c438ecf4b26577fcc55b5185c56bcd93266899339a8255c9d238be7f86d5b00bd3bdfe1df384697390ff713b63fe4b75d0ab5155b19f0d1017321da92af95986bdc8fdd83bd3a5e7042ecec615a9e2f85446aebbe7bcd532f5e7cf43e4c2d1f226f0f36574ffb7987eabeb9d46d23f7b06a783d79cec944463e64af17fe00ca6763fe88573cfadf2f0c9438a803c6aa7147a54eafcef8bb2c84d4b333b3231b63360a000f3b0cc40e47ff8f4f2c68a9f028f32d21a39ec942b8cafa1384cf9b3f5dba5fa96750a9fc098283ae68a68af8cdb833c45a8ee4281d2ebd0722de9dfe458321eb3744c8d56035afb3520bacc4bbf339df774af0a67994bd5010d04a9db9099a69161ef4ccbdbac74c0f091cb8601c9f71f7462d40d43b59143f7882e1a631e8afb92a41e08e178f8ca9a50e525e53760b4b7954080aea7934b9ed15ff219c703f948f7c5e31216dd8faab4fcf3ba628d3fb1bc34c4bc5896f565a9c0e9ead6db1f4b6f82ceadbd4f73d7f79d111fefd642353134a86b02eebd32c2e1101072e9b2e36346e8169d61c9c1e9c488b18c233283a16d1d41f1daf399b4fd8241b8af820386cef4a7dc84d11e5ff863240ad78ab39974862216b1522b64c33a2276cc5adeef78e42e2453bb6917b5013dbb47caab8e4cbd83d7c7c184b20666778e85fb302d80bbc25ece59ea8a553b9ac5f37c09a6c35d2773e464e7c17765bf9ed9f5d38c4b6eb5f9a480b13cd206ca6cf4e5e594391a1e76b58fef2af778493ef018e078996a3933820ee961f6c476a2c53422ed3d4fbcfb44cb9f3ba52d28e899211e57dcb6dcca1657eac79335187b6c214b6e06bbbe718fd425a708b59ef85eca3b9a60df85f68c3bbfb21cd83ad24b255bb98163f99480a30ff4ee3ad3c2c40274c79a9dc6a343395c9e0dc92737a39d8c8f876e12f9b3db8267dabd05029ba4da6417436e229bc98c12c5529d398b6d985aeb0bc1529c19e63792d508c7fbafd1bddcd09ef3d1027b98c9fc6bdb48b9a7159e296b7a19699a0d4230b5bd25c8ebf25efae2352eef32ca4a9bfeb358e2f4f58b56d918b6ca77d01221d43b94280ee624df542875d9840865e400586e2c8a1b6e555e61c0300c20cc5bedf4a9641fed72e38d9c7adce2c831e34c0ebf3e7158f81d2e8af52497ed9a638f2cf987a10b05f596fdf1daa575b89ae2a2c34530d0f8a1ed6500b096528b1f2c11af0ae12dabe780bd8e242b8e1c5d5a17fb6edf922dccf63e8ab25da1e762bd10cd790d2270df73b7b2c9f089234ab79d246032cb9790d8c94c429b824371d50f93c211c0529c88d047bc77dead3b701f3e75f6eac939d01c3cd7d7c7a06825132bdfc87c110beae1505128aef6ad50af684e4944a4c6cde906f1b77b1989ae4eab0e463c3ee001ef4c35e0b0d94d579219c04f4100683ba677e4173507bb1e6f22f3de4893dcb0dd63c22b88d885447ba19ce370c34d0d1428127b8fcc1c09236fbe37082ad05d25c8c87cfc9133816e635a386981480d6861da4f01484617546fdc3d6bab1cbbecdf3f1a31a50cd52cdfd6b7b98756e84e7764194d434d497ca68d71f3411b608c4c38836d1234696768b21659798cb02a87fb5f111b732feeb3d82b36ed6efff4a5c42d79315707d55cca99e07d5a5c0525cad53872c9bc6e0c528d6e818952b00e02578ab53aa6dbd60328883781fefbd9fa036aa135c0e823072281b1e520dcf8730831471dc56e9b5c4cb904be50757b90f8057206eb8e02ea325257efe24df6a402ba8b7d9dec5df972985fa1d797724a2860d4d8cdc7359b0b5931a699391c4750ff754131572ace3bde02e10765acd8d251365af030e0cfb339fafcdf2c908dbc3af28cf1208a3e735ae487ff1a5b7b708edd01f4494c1ad4cf3d1167b83515ac6db3ee70cb7afb43c10c44a96bae550a928c7bd38d43591466b357f8af164342fb60740380921ac3ebfeb0b7137b101a40a65fead8047476d420ce814fac87e7cf16680e2109e2371f96528f3228d623ed5d458a5ed7a48dbe774ed88b80b2831d0704204a5f9d10fe57c3fe47b5197b406b1f2a8ee7768a103d9db0bfd275d299cb18c3b1add0626400531b88ebe39a6679c031a7432eb603b932311b8829bd7c7ade36c6b882684cd20458fb82a8ce8b53a3b0ae8970c9853a93fc69803cc7317ae12ef9bb29b4f4c7e16fbcca921968e2ff098fb3f17149d5b37b580ce50d98d93e4478ad75770d17146dc1491ba761414cd9af1665e19760a7d0a85c36021075b02adca8f401153cb3150c335364d36cf41dcb170ac461b9436af6039c2ad8d7e155a0045fb63e7818934732d68da317e34be4b8f18330aa60efac01229cb80ec6fe563d71c455dab218c6af16993ad6530c5cca141e4b68b6322493ec5120818cfa67a142b0e677b171a5f928ef346598dbf0e6390d1ff8b04fbf37ad56e61a4f6d4266e5329d8745fb2089da73d586fe9d04142b0ce2ecf772b545f007ce0ca1c294173705ed5706dceaa7777c38e8bbd3875251a37d23507cd513ed0169f63f654ac036dafb318604bd6f380229eabae72beaf0133736a66f01998ea0ec362fb671302a5b3d8fac47a42c8998e2462079550557a752e03d5389e462a5d36d48c0e75e4418981a24ba081cd40f8cfed408821df427c4a390efc3be0ff489e8bfb1eef1b9d25eaa384409c9b844a04731b182811047c443c5853acc5f4b3cad20bd2edd8c80435d3727d454a6d7e72e66fc2f2e1e313ee2e1a503d3e641e62622a595f052b91d543d13903c224cfff452d75172fd4ec0c07b69c62078754192a42963f6b3824d2ef8e097da329f4a5766404dcdfd45efb7244f7c4ba3cabddd99b2f09265628c54b70dc4a304d9f37ba50754a5935e9546cf230103c217342ce733db2b038dbf1e80353d8206ded3d46c8db7acf271ed9d4d13603083b5e6c0370bcb952d341438835b1b88e9cb2f2a36679f9afd32be0d76cdf9c43e898b135e5423b71be7fd655b43ee82bfff00c9969d15a33b3995c6d45327eb231567f18b424fc25fc3b839d6f5681eb21659867f0b2532281700c87b4b6e33cf681a77df9b2a3146e802bc23509e74badb4060fc1b6d9514ab5422206922bdea0d776fa22a4447a5875c65ae0f4cf21e6a03bee19b7a3e4e4f447cfe40609688c16fc3f1483a3f0dfca4e16efa8df50ac4f22c85362d9c2a54f653dac4d5de17fd170226ffb8506d00c8af80b5a6ef8f53c0e9125da092597b74423c231a0e911d9ade28cbd4efba545cc3fed9845e5dafe13b81c48d105f6b72b83cdd5dc8018160d3b32ba8ed9fd6c0c097f02cb0e7d01d3febb7380348ace6fa7b56ac2dbaa64bda15dcec306b1943d0468a6cbbe250fe83a30bac333ec82ce8ad27c95c69ba1968464f29f17b90177d3d0df66d2e98c7c0b686de5e709b88ba762cb87448aa72a899beaac5564c52e4217f653eb897933bf92ddc22afc6eceaa0a798914fc7af49c4fe820b6cda59ebe096b96dfec9bfaf82dda5f269082093fd2bfb20e1e41e140834ef313cb7b83de28f951d3c1873f7c1fd95ba2b214fc46499942e43ebb4b56eab19a59cd82d8251bb7e4fb129213748f040f4b9af708580e80c800851150183398b32256275901f6dcc360e78c2de7893df62bfff8dff46e4b133f007b71b646d7e806659ce79d8e5c506f7bbd6a0dc81bac46ab574bf2c9b720c5abbcaba5272d9e9fd063fde0ac07f669023caae343fc77e2f76f8b4bcb91d27d16024f28f406483e1c8b92abc75e52878bf2cf8de2e6c6b6d89fc74f8d091e459d269fe66d4ea28111aa37aa7c34d2e598e9de04ff5bd95783991617faee4165911ec37cf3c4a3f68006258e33d5a5f2f928d4d21002935ccf45bd3e5ec7e473f2c13b112bd8884a295a3268deee14ccabecbe44abbe37b5acba1fd817af02c86b3d921ae6750ecbc981399f4797032b9e44a71817d1d47fee4fc62d71b62b66d2f928d58920db759af6400180172879ec32dfac0ea61073501d3aaf51265500c21b3d1f800eeb5815cc1c3c83a414d60b5c819b2195ad1f76175919896ed4a328801cecdeeac89e12976b5f1b607c8f4f5f0d450592ab8a147d01292abbd33f5d4ca832bc5f5ade9dd48acf104414db47b156bf1a21d6e6fd3e07dc1a9c412f145574c9e451546284a9464da01e217b35f594338cde8ddadb506a880fbf359ec15c07fa9ae1ee54903697e944d305e3bd266837cce8a192c1af398092abc1da04cd4f4fd50906c48745abbf994543901a0379991ebcb811fd03fad601a8cdfd183faed58dc38856a7bdb46b754a4643d3f10113e7b647ff77715852026056cc6971792035d798f38aff8b3c9b2c896761ecd325cad7ea702f84e7bbddb2e24ed138079418566717831af8649c7c75a2d222ba480f5bc29438bdd3d76c5638d588d681ab0acfbcf1da3e50fb54a065b5ab221778937aa807ac5aa3b076dd08734566493fc13e017c93541adffc2a34fc5c7dd8cff62d6dc43ffb6c54bb2b3cb27240d0d0ff959eaf260f71791f3d635027f8bdb3d1aa98ae31ab1a1d1bfd1e7974eb4f1e443413ead67d38a4c96b49e7814ca97b22fd62ffff5450a2077b25fd92c38ceab64f326b0df984eb7f4451001b9d2ce384f7cbb50fa81ca34554fa52c725441df5d22b18386dbc78816bd3ff4939faafb19a6ff28b16e6b3da64d48504a1bb59c5285274ce4b2f4361ac68da61a47fe4804022eba802b103510cd5b17896fb13d2ad75faf6e5c1a0597158e0dba44393cf0cb47ae90db4c8452862d5ce4e68610fc69fdc9f6c67dfa27f365182aefe845c2392baaa2fd5bd5ea89c597f8038e7656572a11fc3a03b6045319fcee340ab0cd75ec5f1c49f7c903dd3663165df5f6d1b6380051f11ea946f963513468c1bd9bddc4e71086369800c83312481fe0709d8394d11f9c7435cf398a3f2d35e8ae69be73efb6e170ae8cd8f9dbd07d5d541007e21734fa3f593030d83cd8fb7efbf786924743aa3991a5117e95ef315a6dc41dffdd7625c53527ffb9ba52adbed88aed01ccceeb17d8228917d4c659c106763b7eab95182963af998fcbea22c775a3e81c0dd20ce0b5d9f6052dc949b789f5ab05fc998c897738d36f714947902284961ad709fe67955d55375bd2a10ebbe603265104555766985a5610991717a49f9230e6fa9927c7fd919dbcfc5230861e9c6b0116d0a57f3b321fa5a90887df36829e19206337ac16ab23265a932f25ff1a93d6476a1bb8697fea9997350bded76b5237248d421bb630bebb9a4cbeef017c15e0e6712394812dea51c9ee544674850274186f17b80076591c35218f1e08d79728b410f4f7038a2d4f5e515d7db23ae6972f0c67d82c057c4654828ffac467a9ba25745cb6ed61fd14006bcc1d76222a0b519b8b3286a08d47145b32f40c0185b1ec0e3cfae2a78a9c887ae01b3b4aca745c2bcb6260b254e27922a2f526b7bcf39ad9d20d2b8500d9340a0d0fed30291e0c60422eec19ade0b0bc64ca12b5dab95cf1595e52f3681c9369db9239d859db2d35609542e104f2ba4da92bdd60b89e1fdec31fb35d2a25d787eee122bf7e33283735b6a6a4a77b4090cdfc0b59cd1b5152506c28502e33846ab56a2c08d6401677b7563a0cc98dedb79171e35a67bb00a7908c15104d39696dd51d8f00a75412951a95adb832799a88487d6e2277484b43ec42196a6c108e5e82239585bd93f2b64b862ebb8bfca37e729732e555d61cbbcb70b8742d4ecaf6a07a23fb7756650e7c4b3c9ca313de9f0f9adf3a30d0f2e70ecec18eaea36c838b44dbaa0c855ed4eaeda9780db3509d52164c9fc81652e6af7beeff247773dd4bbb6432048f0f7ee79c9d67f7328584fbabec5027ca247185bd7cd1f9e7cc54531d2315d2c573a8203a053cbaf61fc31d1b82fa651e139a91ffd555cd08d90150f98828a558b85a85924993ceae9a038e50344e1211f78b2a926481dd4810797cd763ad81a3868f8db4b501c33b2f9fa7837b375bd43971c37b6e86b5b14b1caa7987b80af9645c45ee7619cc22e0d167b38d60ca4b9c95bff1446bf1fddeee81a3d58e5cd34acafd9ecc0b14afab88e1f5335cad1fb8eb675af6047752b0f75eb085eb3111fc6eb293e2945df5972eef6d5c031cf764ba4c9a6e954fac8d70a1b68992e94cd96a5b9dafe3057c7030d7fb6eb158a0363017505b22855baece4e7ef7b63ae1d82be3f378efa7daaa7ade985c5b5d494ceb7bed8ed293e3beeeba27507c503987417221d29df50c58916eb3f5e980ed25bd8130fadd56e9e7e6f4ecd320bd294f942792276cdc24711ba3452a9c1c959454dbc99abdcd64d42a8a10aa18858243142b065b1bb843661e4bf91b68d70ba0ff8cbb1f6e9b98d70c93d5b0f5c4fa694fe1f4668ed994c9cda29937360b4e380011976c209d9e3753d4c81ca65a0c11b5de9b102071cb5ac033374af2a14f029853d154ff1849d31ba3569ca5d916051218c8bd886df8fbd9fcd75cada1faea37de8924742508a2294dd89f959ac9077eef66359d44df28fc3a61a1d10642222a0a5ba7f0e972055536b02044d3e54959f3b5a72073b6bb4ce2070ad3069fe78ac92407f2e6eca69ddf538258613aa5bd4ca238a70c426d940613a2181cceef9932f346464ba2a490d1c15913cebc5f38e982cca4fe4ca20439877c98b3d38b0d1117398a85e95ee53902b73387dbe8c03bad4fbd139f9da3be41da0761e9b05f1d0da4859625a5dd2e576677ccdf2556665147598594ece08b0fca2b4bf30ad8f20f96cf99aaaae3a45ecdc56b3c91b566044175565764c7bfff9698038a26e8923a8e6d5bb5cbe2e0232fb93951bb08724034f6caee0393e52779938be2f301807b309a04f445016557566d3ab944f8ac6bb5c6f99f8f495bdbede80d31e04443db75d67fba46f85fcf8229ba876b94299795be97d0bde505e6b6deadc8d1fe4431040acc33cce75b9e5510eaf3afb56b5494f028d56c8d7ea0deed4295379a05243979ffb250d768ae9aea2e3d172bb1a0f68749ced907239900b6b1088daf6a618086ee71db4ad0512d3efab9125551002bfc41c7c29813e2c47be5ddbf8f1585fe5fbe4a80fdee88b93f0f7cc9892e77cfc9fb3fdbd8662dba9738a96d09406e9add6097ec51caf9fac90d6b60a11982a0daaa33f6713127468018f522deb84b5a62361ca4bddb918b68335dd1d9e4d3a83bc4dd42cdd998f1a0bf83057f55dfc8eb3733bbe449d2d0eaf5c5031e52cbc6b90b180f2b8a388abc9f4544519d8947d75cc2fd4b9a1a1c50f8c1d24f87aecc3a0254e26ab4833f1855e8e38535557fcc4cbd2b06e69a3cb836c70d9bb1aaf2b5167b294a5da958d4f2fe19df149cae49c6c9d06a42e311eae59ff016eac266109b70ac9578e8164b5f95c3ba68b2eb172cdf620480c401cb96f3c8b6d8960ba7c0eab9efa93e2594cb371057e98cb1ecd2bea77c56cee0b9053fe3f0e0634de07a711d76988c8bd66d6dfca3580015d1a0aed96c7a8bd390cef373698462d09491ae93790a4d18c0e2bfb1aaf9155f28ba52e4fcedba2d0ff676b7ef24b38b7fcec4d2753959763c50ac2c71d1e6357099b5e3ab98fa7ccc775de24453a0d10415c5b13c087cbcc3a61d359a18ff73c7dc2db6d826d4c4009e8522682b40cce826480feba5b1ead0f91f98102ea17edc7e0374dd29033e139a0d7ba7b422889ab91ca15aa6b6e7e43e915871f2169cc842cc49b97e974bb06f9f12c593b6c806a85b5ad8263b6cb1b831950b9ef7939cf1d5fd23bcda789b72f101d3091949f1b005b1921785c312cbe8ced7fb0655b0f6d889182041a523581b2443716f07e8d3e8f2fe6fdef67eedc6622c5bf9f1972fa1b36276d98261749da481c290f13ae9077c5fa365ba5286118c2eb63cc40892d13216fa0285ac5c84e7e2f2ca76dbd37d58d23de0de4eca79eb6da7a53fe0d4ce0fc5326e3d7b0b9f88ff2e3a854ab8f6b128f88d26039148ce01df33a7c90db59ca3054a8bedba3148e7c041176d15e7182749f4af8fdc34209ee607832b4126886b90773a643d40e325b5df6e898e76970c8f6abc241a7ab84a9c8a0f15893d534a6bfb9c288750deb669a56a27386a4c52f82bd48f32703401813d4438bf18cbb91ef5627fb0ff21f90aed4ee764580e8bac0673d24953392198dd253e37d9c2092f13e87ee7cdfb8421d834a23b678474217e129dd1bf7caf337f259d5f20cf41446138a538e15cdab12d7fb2e7ebf3c05ea9ec00f825a52e717589465abf9b9f28c7aa4f1a41713e5f01e58ee45e1a04c7a5527f5ad604d2924df0b5049477fb482ea5807386a9efdb942134486d8b16c698970c7aef82fb2b21c19740c5971992e06ad41373881726794a7de89dde8be64fbff7c9474f6e7e6af6ab7d4ed6ceb9b2bcad23b39df7bdd89c971960f79a619c60a87d85d89c95870ba8bdd7418b826d656e6bf912ffa0b77d0e89fc58b033b79122389bb71cb7eaf644918cc45d1d3d393721844e8fa380e69430d22a39deb408e17d0e302be4fb0ef040f326ce5767b144c940bcd91d7fb67170af89e286f4bdc9de3b05cfbb81257509cc359498f83f69d9ce4bc6cccb203f54b4e7b63ac342804400a8cd186e220b268330f46e51f96c829efead661033ab68e13c64160e533538711244d78326f7f124d3ad84f35a2c34e85a77f9858dd834f95646e671374c204cb82b0020b65fc67cb0669e41220e60fbb46b9d662390b40e4db90904d36b8b7aab567bd0c588baea565d1e107d56b44778065a362945185ba8613d51118f762d784afc4afb6b58822f375c7fb738ce1708d17a6a8927a816b61d6ec1fca25344d11c0e1479e440dd4b24ec10913ff63a4998f7c63831b9e4cfbfe8b5692089dbb38bbc1a05dbbcd87a595a4ef56ef490419556d1e4be8f36a5b64edef19b70b1a1b036207dd8396545ec079c8f02bb0df6cab5d0b09a94a971be5a1ee99afef86c077012cd9e34cffa88c824e865eac16c63b1e33b8c69949903aec078e0f3da94ad888528a27257a984039ed9c2b7f753f35cd3ba4efe0e7d4b1a2a55ce0adf75d0324f91f9d1acd9e8190eef8d54af3d1fa013dd5aa6b1c434949f7b6d7347f16f16305903e277ec405f9ffd770f1dc92e676e5f43a949ab7806ead2ee57cfae56c9870fdd198dc644048122e69e70bde3365d87d0e6eba9e704dde94bd37c4f7efa246e9c02286cb99c8a02e2c34c82548d6d776f28eaa3cffea7475f9e8b5da8b77c0395f77979101ef592b300f4af6fe51fbc358252f222ac9e271e0414118f97fbc7a0bf55061f664f34d0353f21d42c1b98b45eb2fb7b96a4bc91faa6bc085c9bc50651496b379c57f611c2abaa3359ea69bb0400faa37c0e3ab6373af952b3dafa0ce8bcccd975e46fc6d6474297eb17d7140a7ed8784192a7a96e19f25b0e3d29e491019217a90e1ad91452b43dd3728566ccb2e4bbb7c985c43769d7d437e8323c8042e2fced65e7d6de318e4d8a18dca809f875347d62c3e8bff9b061f55e40bb3f31104683fef68c8dbf4dcef57c623f7badbfa9bca4155b096f6960a9a11af941bc55c113d6321989b7dac4c7a4503ca26653ad780cced1f23f5f71cef39e864544836123f04bc2df144ee474a8a4c6f280ec0f383a3ea6e15923822ba470646fa95f47e713a87978e57f6255faa531323977007137e4ef1dbab8f10a04e674a8f438eb8de7803ab10cb48c5c69c37b89c8f0c1b5f5cbbbcdfbed31e44a0c0bab84e68ef1ddc7fa992250c6343790149da0f31d439a1d0264c147b9aa2c12a9d0459cd88080058fc959f09a8a3c2da4eb9b8a9449332c62d163213bcfff3ca9f419234b783578b4117686b9e3db6dc9144dbf4c5b002bd5dccb4d0c25017ed2d4c430dddc8d0bcc286846e39ece3d7a1790c4d2a2037ede0380c198e12ba4f3d1d32a80bcc1ed18e7e2ef41c6765a501eeff4977e1be7de07cba1c7e03b600623ba9e64c1f31e29aeaf53cbb5b85ae9aa01c32683e147c40bcc489faa32377bfeca8ec504e2ab412a155074401b0d03f6f12265f1d35bfdd88cfb023f7a01c1ac3f778237b5b56f7b57c5756b0c6755f831e4ca8b14af1bcddce12496d51f7b9ddc677b20c57424d19e0c7edcc3f8f816a5162fa7d44c6ce31bfe7cbc197af6f549e8a262cd6b0dc4defb236d9c4521834122b7352e7cd67a18d520fa168fd4069fbe0de77de840b35bdc9b77f37351e0b67d7e64a6b0f0e27515b86b2211a1c8c57384722e5e1e714e023519c6ba9a959f69eb85677c3774207f9c2dacaf430e5266aa91979964c144a4e5b4acf1d5d6963bfcb428e3b18863c1108a2ac5ee2656a68b5e76279ed6ed4278cb54a1bcf89ad510e3940c8b469863516b5e8c0eb7eaf67422205f5bba7e9f73c5a201d42c9786db5b20f9f5fa38712065253e7046c5534d8d87f8b88180062caa169c162b801b6aba9e0c89ac68c21021355dbdadb7e17af212d99f89a74a0515ed5b43b0602879e9da261e62aec494bb906bbc146eeaa2a08e1545892e9cf9ba742d82c56d7a772d5cf1f07fb55cbf2aa1945165b664535b9ad55ba67c8c547e674ec7e1668f4777d0eaec99a0cf49fa4ea8744757e3ad843d688640ac30fb5adaf771fbd5dab7890699019d485cd19a64a42b9f444952bdddc16b7414bcc5cdd2fbfb6342c04ea46312b24c25a96640d647aa5aa3493a3d602c22d7b27dd03e79ac9cd2e0a74fea618984efcc81983cb074e46168d9669abcede5dd64832fb6f975c7ebcbe7afbbb50e4d9dbabd86d09a6623dc351ea9a70a0d84815387d4192c50583df61dc1349bd8bec7cb4f38184af30abc0109425bade2df78895d0b586dcc3241b086565837ee684e4044c10a6618654e0db7eaf02e097b10364f6e691e6dc391d3862eb28d0fdf9aad2a267c07356f0dfba829bfbef8837e4fce896d1148d7197b35dbf130dbdd917ad63a44b0b9b1de95c81426a6d00a09acbbce43a2b914729336ecf130603be54f7f6c63e95e8af925d00e07880192dcf11afe4680c55ed3a6ea3c90daac6c34c5263daf0b5cecda91f501d013450b73062d5a5eed19c3f6cd12b22a19a7df8c5518fd59201103abaa148628d5461a716b34138749ca1a52a297bf9db8747964d6040db88c37f21d3747f20b8cdb2b4c4629392600a61e4e6af426d2ddf1ff598d0f4554513330f4d3f4c59dcb5116de11704552c1d79bc9df0ab0e288acb7ac85932d9b5f6e5c6f375cd1c787867e9e605acf3038154fbcc5f688ef5e8237e8c406725b6161ede0e696dae12d5dc133854521eb9e85efed46134568f359f59bb8ebc4a7635801cf1d3413685ca6edec29c64cd6e57faf2647603e4bf57bfef3b0d1c574302daf42818e39d7a148301c79ee78fd6d9c9d620469986c39bf1f06e79948a57cbb44bde2b33716ea3b32b156c3fe51e553396e5daf3f9bd07394712b97bd5a5068dfa3a0cbf26930a0d71c1d9b1fa5d4fc02a53645bce97a39c17a817649962a0a967e9c8a683686fe08594613260bab208c8c74299d063abf54dddd8b3d40c513112f0fa517505904318d9dce9be872b58aa9a2e04afee5978257e308c0dc76e61a67365e9676102f6a4d863c8be6a02bf302cfbc832ee3b5106deb60dfed6c935aefbd4a642f0f4abc9403de1d2c271c870e95904d8fe5a16c9a4072b16316cb4d3790fc1bfb435b8bc6ea4cf693bff25194cc053fa48f4ea457ea1bd24b5435a2b7ab008cfb8f208cb8619a4b01747f83f3da16f8fbadd1c09bf0ee5312c8e8c4c5c8c3e734d2aac6f451018f2988288683930bcac2ac366119a87119a5b3a2928a106d14f7d9cdd04c2ca4ef57f669ab1a13b63ad19d2be1677f31ae5aedbbb5ffa35e9580548d8650699662f2c9d543f036ff4f7663d3968f3a5f951ea0afc63c2b5a322afcfa126dc43d3de442353e381238effdfd12ed426ba5a26a59a24e722e114940f359550478060f349fdb84a82d155ec4be2b0889c0e33227ba134c5503c042b779400dbe7845ad2458a40ef188c043eda702b596edcd00455bb1e22855f88a52ac26090abbd0eb708e74b4618f8697d55103e54976c4dc99bd9451f44c576c2533ba92b0a06664dcdea678d5fbffaa7dc38075d5a3d40b21027819c5bde134ed3b765591c1fc86a5299dd6e05639135caf74b2f8bea0a5ff14a7a9dba653b3e747d9de9698059f7204ad47922cc6f5c06171290f7a5f50a691f48c71a8dcc2875091dc7fccef7ffaca470982352d9152bc0142ddab5759af7c9606b84bbfe1c62b31bf80aef17fb62c6c1b522849914a165c7b7c500ca21d13beb1cbdac04b0bb59acb1b9c98fab1c2c37e3e5880d91c264f41e60cb8f29cc13b765457990aac96e5986283fa27b4b1dbf7afc50a0fb128dde66f847c788baaba0a1cd12529cbd19c9eed20be499c7dd85c14077c50898d4e7b65a6a0e484f2a0ff6d2b514a64d6ccea9aa73b5805f28c3ec1ac258d2acb875d6aee8d576fc168cbe745ce436c81a0fe051b56715450aae72625b6545810542bfc344b77974514ad46b4d8c223626f388e7927828e8277c374bd84d823e932c4816507e1f4aa6ae4571e48d219f94e5dd7ac86f964f05bbb16d155bd97f4cd04b919edd193fca94bc6d22274383006f534e7f33060b07010b6191807b76f2e75de9e6cdc973f51f00926c87f577e36cc0b268b6ff243e06ed4fa4cadec6390e9e6a42ee10cda057010d7fdbfc52d5b5f743262c541effe4fa11992df3d6e648500733710d339e4ab23e61f713ab54e651ce47f2cf002c0c9b7153b93362870acdf4ab367a2cd5802c3aeec4d53bd06c281d06a6c81ebdf5c6ccd5355d9f75e0c65baa3a4d05a153004a7b389a707941180539e94704aa07c485601928cf31d3a448ff9724f352a660f2917bbc1ddf5e724dfc808f057a893e596536c56375cb7acc11d7ea76eb19939e3320796bc046ca3305592dfcb89c0907ab887142ecfbb23f853cc5e4be9ee424b05f0b16e6bef227eb84ab3270969fd10149f2753ce293aa4cfe4f3f8e1252dd26af5a56dc33d40472801a3e14c2d8ed750f16d1c2f3eda3455634d55fa8ae8674dbfc2e6e69966f9e924056e1b0aace489787dafe3df8f0d324016b4b1bdcfc8cfe927b8edb5a339db548ac8bfa209d6fc571c85d24a4d514ccad05d400cb3d206bc42b783885479c89b9a117dac1fe42bc971e00a3a9004e0c30c252800fa31ab5f7894a1344388dfc1eefa85bf93a166b850cd477c46e00c03f907b266bdb389a368f96c722a9d8086651fd0b70acde4f41bf6f4ac4920690410994c6bbe9fc499cdb2df90165db23d919b18c3ae3713b452bcad7658b15ff697cbdcdfe1595289aef29df0a3d57f54fed3a5f5c5acf6e75d81eb866d98a9c2a578d18220bf9a22f6b3976f9aad9eab0c124674757804bcf37b230c7d788832a0158bbcab07ff8525e6429be20340cd844f2f2a5ff944617a96ef0aa1095c512353ffc2429112a5f56227acb9f2b78bbc993250011a7727a4bd403b09f362936c09dd82a9286527d8c30a1dfa485152549c422e3296f5aa731300ba477ed5037382946c6a0c968f96ebdf2d3e2243502b4a006640e1af184aeebf1f0703bc19fb6ba5ddb818ef615fe4eaad3aefdd79754193d97651226caca7a342da1d1379be8a808e1eec895b39dd99ae73970c88f21778d9d9ca993536c7e9957fd0d1400f9b292c4807961293652e7affb36af93d6119327b29a43679b0c39a9f5e6bcbc36527b152900d9d6ac4a4f04d4ae5064b676f6cfc2c11043d44ae403a6a2e83f7d83c9e269c5ac8ed617ce0d758f3a82707950fa52be457dfee959d1ddf9c5cdc0a248d4641e75fcd7b61bde8fb1e601b8faf11486a7f620727b3cb9a115e0bc00b984871b1ac7cc0c97614e696d508fd5496e50a33e62994d0c0b534596b5e908285e4ac19217d56b68dc7cddd02f6922992d151a139170db74764f9035d907c984428243272af9d6c344b4724a8037b6b7a15057d0e0347a9be44441e43f10b55f6fc07d4c6b163744a4b537802de12dc8f606b772c86421ca17c74a629447ee59ddc4b021e67aceddaff62e7beab606ae8c212313b275c1e922f59cd2a4723fa7c71f1d45a9d0775fb1b79eea1613866e825f1bed4678dfccffb3fc1ed347e00f1eb2fd0989d6401d187f9ed807796411f18b550aeca7737652e34b134a9aa8a652d362aeabdabdf468258074e9a55907580f2f5b8d9c113612aa9134dc4e8767ad8f3042b66c412ed935868ed1632fe644b7b3e0cb60da1b901253b91aadec2bab60bbfa9a1fb4c8461d067e14ae2af074d3d475a3d104911f40e7f4489b94b2551e895700f8813a417bffc742bcf5fcb0df694dc629e67b64df5417c66b566060b242545bc765efba93b408531dc9175f83b5a7f4d76910827a5816bc07984a49c71695b4379d304a8d291082322c28ec8667b63a0674ad56f52b308800aef72339cce757205989cdf4f6f99c38f4aa41d5b3c25aea19d5547a9236e3c39517bda0631012d4eed969f267d1edda622b5135e54f3dc5b60c51fc2d2efd9a0f4342740ddb69c4b952cbf9b540ccb2f274b4014e929414a3f531f238cf8c664172adf303ec5bd84a6a09a85f97f0e84caae7b78fae3882c0e09855196a14e5726bc1550f4cd9a7304e7fc95630b400ee731a7763c3a995b4fafcb5876d7dc14eedfbc703bf55b968b1906b8611d9cab546c61c31561ea46ec73cbfc69acbc48e03c6373edcf4be22c2ded37f8328e1654a67fd98185a1331a972b3ac7d35fb75ea4c00a371b21c1fc41946bbd964a898bd16b4f523019b3fcb0592aeea4f49a33935c06cd766fd95891094a157ec55d213b2d6d65655e14a64fd9d8471597c7b154089e84de0fea87b02409f61f216d20dd6bcacc13110d8b92370636e55fc5213bd64f9598262435cd28070d51dd26ffce12dfc6dae34cc961cb327a75df8143b05a7cfa824cfcc7db5bbe9464136ffbfff8598b686bcb48523a8c88aaf47017e701da4c9ad08fe18ef17d14a6771e60c23851e2271679fa8d4390af4c21e4c1c0feb09e543f21e9c5e35b4c2e4aa5e8b5802605ce8f48791fd567913b89e9c3a037d3c7b6145f4ca56db852e4304c74f48f60eafb4ccbae66d2dd03d49aaed8c2ef07c7f2211a857399f3c2689db10c313c7f97cfd16b3007e09f094cce77d29d503c7979f5e0acd44cf893c982ce0940f657a038a1f219fd3e744c66d66372a846d55e98c9cc71fc08e199d834170a912cd251e900513532d90e6cc570ab514d321037389fafece9e1032dbb2da60f6e81612a558133403b52eec1ae46cd3db7248f7c9bbfd9e7b6fea9d6744df2492c06d7e2706db5475d937c76763281082abd6bcf9faa721c59932a5501fd441826830b5258faee019cb644652e26cae86092eea40de814ca9af5c627b67c2e3efeece97e91188d7e5a194e69eb9ad7ce2ccb56ddab27a7c9e19350d4bdda1479c2ae6cf2d39441aab9488d9942f462b643e49cf77d43cc11c8f095ac94a295621bb08a37a43b2faa59356c5d7d83056618e818f677ebdb3f4612ec1d95c916cbfd3263d64eec64738a7876b06ba65970341785cde78a85e85b0d5a906cd1f592ec4fad69736612439a1e4a743cccc2e8957924c6d7e5c1216bb99b6c654b5f5b9ef23092d5932a578dd21ab983d033550c931b518b8107b8b54ca383c2732b84df1da39809bd45eaca8338584727810adab4be4b55c9b8d1a4f2917af012ab9af0c423143c9fe5bf44b01493a7f0dcddb48848342562a2654fc543c43037dbd7238d05b287e55055b780b2cf821787a2b6bd6a76eb0f2973725e34ef9e2bf56f764419725b838319a55b2a6c92fc90789b76817a2e229dcd476b56e170dc916cb65d841123f050ad22b27ae0c213da7cfc00fa244c01a382d7a9f02387d389a73c69a497c047b4666cb46037402f5fd1451b8502860fa90e1eb3031e7621bd62a325830c12e9b0c28285ea2360e1e622a0b920ed0a7fce5e77e4aa1f2d8732bb67bf557ae6448555ba2495371b39c84a42abd70539465f5feb02662742890d44450050f60708d74e99e8f7743c23df4959cbac2c0c6062774bd9976c6f9b1140de735e88ca9551f5e961c499bf4e9a33209e8e4143794830b82d1fc33394dd02e64bdbd83a5296863397d6b9279404733b0d46ab7add71176d5989002e52a0da17f6de2496b4fa86fbc91aa2c8a4f7bd4e62c42e5a1e9aaddac629a133a6e3d206a46ba986d5cc637710d47d5cf5021e8adac7a6f61fb070acdc0a0f5fa70b0fd55da8f1889b5a8fbef280fe0f644d30706b7d652a05e15aefde17ccf152cf0c2af1cd612232ee62c9397c0492a25a91b8793e48f7253e6203f43d6c442e33d231b32266f6880b9d05be1811e3ace962ff77a563ffee83343478f8dda97c7da524f9c038274f451f23bacc77e882500e3a0fd47ed529747eb0824c61447cc3ab31784dd5324944a0bc5a2e7683555ce9a80257aa4973a9e237587fb2baf69f278f11dbd2ad64dd4a0677906545abf251682d1e4b725da0c78d5eb89007179a9816e5a6d8aa0423bdd7a70c31ed92e833f3001ee0ab9ba0532d6fc734235bf5f5295a9c4df1661c9cfc6edbe1771c9037ef271a0d5a1aa4d3ba5b59cd18d381ea2d73d63e449c0a79edc57ef62cd17d9512cec88a510fec3d83bc6cc59af8a9295c485341258d9c10baa4e9c0324cb25742e16265996e12ab76eab70a97f549504c80c88b4b04847e2df61d9c9161d958b908338de158352268fae36b2fbcdd5b47e892e570001db3261a3669fb7070fc229d304803b22a20f828ebd09071578312c0e026485aacfb63fad644a1896ce5cfed0ab8fc48e423f770a299ce0917d2c6ca442eabd3da086ef63f8df10f00066fae0dc008a0e332aa01a510398e90dd391d7ddfef9e26b3060b4a04cf96c8a69edb10bdc4a5c74bfcff12d7045313f358bdb149f5c78710db01a96e0dfcc6f8e14398f06abcd8b33946b6b5afefa081c7fda5e94396922067d804772517284cdb5acd42aa8e6d4a95e51d8e0d408cc05a51ee0d27087cb4af0caa40b30735dc027ead78507b027b3cfce31c2c9d008b7f787e77e4f7ebf8520ff4aae27856d670e92c0af79d3b36c887ac818e387a8df3da617f5370446893a390e40aa5d1dd807889c8c02f840b6914b1b33f17d5ddb26b0a36c1dbec2cad78f58fef5d83af5a4d05f4a983d499f7cf27a42264a0b4922a299c5d774daaff2c10c6bb2aaef63b173756163115954070368f25306929948ad9f94f472ec5852ae2ee6a86dc6345aea85cd6dbcc8675cc5ba496a512ba8bacb4c8978676ec8014ee4227466cdfe2f0797d2dc2626d6e738b43ea45bbc0b305092241bd74c0e1543e64221ff0e0633c9f5c2f914b6b79bd5b38ce58dfa9f8216fffef59d5c2684dc034ba25ac23a885a9ab967200f63634afb3d1906e309f03f5c3c48f5a1bd1ae88e23f239e4222e4211de7a739bffce9f9e65a20598044784da727540303e97dee76a0c383e41abb1286ce854a6b33ad2d565f0d0b2323d04d872ec9d77113a1e5d06bb1ed1eb05f5cfc556681e8871f2a1b6c47f684c64807902174c5b6f54ca82a15dea2d9d972a846eb540a73471de0614659ab01549d60e8bc19b6110c3c1b426f48706778b041daa0451d6876df073eebe39302fae04bab9485e581c88fd241e24e060c28fac771ff6a44ed5e986066835142ad63f74de2027ebab9c878605b5bc51a1ec2071406eac1c12ece68bb299f61cb3ec65c980e48549de2b194abaff59c6f7c991f90907cd5be8e1b88b6fc2030118383813f06fa368435574218543d0cd4331b9edb37361063307e6a97b6204b223d4605db7cdee5f6663323a97bea075328a917186c884a40c699d6cf4c16d79fcd87ebac673d63b42f87a84e771e4ac6903c7a9777308fa7d906a449b95d7232ebc4c2c9e92b4dfe94178ba9e69ff0ac53ecbf932d13fce1356ee8433efd2a4a23130908b4d07d16ff2d049f385a51acf3c0d9212d0c1d6d374cf3cd2d83885bb92acdcdb9efc91d2cf01558a143b25797bb681691b576990adb97c13f2a4d0983f4fc3e955572518f680ebf3ec64c578de5b0615be076d89c328a7af4fb5765189d650be46a26915d8a02b09c430079a8ea3b682ee499c166546eebe99fd729998088abb85102aed300fcc20a6422d32ab52988215477b627198e63e7e3207517c3f119e0acef1328559cd02e0369b3fe5e4d1719611b063efbc54a577fc8627b12235ff1b5667cce53c4bd3b0a6737166df484b4d17d9b79b84c7d5ab2e50b5ecad91ec05dba8c01e5e6081fbe3444be3707e25ca2b98dd75c417d37b8558735af0699dcf1beafbf81d5141faa217bf0709c193954e8838297ca662afc17968035370a654ccf164292fedef4d9d464a3f16da23f2470ecb1e49807722cff97258ebc1cc948293f02b6097a3b598b19c640c3f34fa03ec00aa7ec4c9c45c2ecd84c021b0d59589830a21b888d8560d411fa3fa4d5409fce36ddbb14eb1f23772f88ba7cb0da88543c464c0302a4e40d6797f334d257c97aa41c75563159479f2cb143d8ce82e06bbaf49da8245db6d9ec2363863bc5477d1ce0c41a70d6aef9c7e1d389833755863ecd849480b4f730facf99f2378c99a1b4eadc88b4a5d46b6ee120a3c6f41198f109b96f2f6266b7b044b2b6c48f4a4283df6d1cae91f70bd2581be5e9ea68c7c116f373aa4f828bd3f6629eda57b8715a3b93f6ee2b987fe3c1446ee5d5869f07eadb9ce235932e355f4a4381cef7703341619c78fe895c4873c0a8f8d39d3a88035a3afb21deeafd3674939a8e2ae033a6f1b9799b2c398b990827e90ce1cebabe1f644bbb6b2f97f3c60408b790ef99fb2b53acf091c152bea921bbfd7d299860832c44381de6eba9806231b35bfb712aff00c2d7df266632b97591ce0e0117cb0aea45dbf28916378dcb802681f3c8725a167ea64eb740d6ce9e75329bbead0dc035cab3b1ea1d2e1b92e7c1cf906fab33354d302a245e0f6af73bc132542350c222cbf232eedfc1d6f53eb60b2657436edd357f1f1643dbe438d5cdbc47bc56bb285eea7aed331d107e834c4c7c93b4fd2895932db1b5c173f056d8ba2f97007b735dfe71785ca28b7237c6f198f260c2f5e8676d8ab75f821b00a95eb444db55c5a53b9642388ac66a1a1389d9c05b35c07c583b4a83d03314e2d0fd583aed67364754da053007d82f4906ed775f3f6041b5410f9eda5f7a68e94464d29d646d12a3a77b932cbea7336b949c45d71544cb2645299c3fe8032bcb12005b0c136c8f2a690503bd39ea9fa5d0aafe09016858d36fa77ae92532bab28b59e16ccc39b1a8fbd8ff023f4ed38cabecbfa14d8ba2988b59c61d129a60af28694876cd014fc9288cdbbd0b927eb59024970758f458b02f821b42ec9ac754ab6a1f43121ddb4fe75a3f9ac35d9eb62eb0f8833baf696a9260325237c09b527fdfdbb6f1fa931a14323c77fefaba821eb872ae1be1607be45e453ae1f7e5e9f861fe83e95faa5800fdb5b5cff39e207f2ecb5ba24297e5a12ac9992cc9b75fd7f4038daec406fbc3d94333b81977153d04c0fb34eba753503e0667bed89662001e66ff0595260304d16ec966609e83d844bd2787eeff6eb9544b480f39b7315b562698795a9d618d55f0310389a362aa5e68ec58905d54f7ba0c5210af561f2c159a426b2182e9db50fac57a2558613618334e8ec721e1e020dad78dc92d09dfad9dcf213c84684d1a904d2992a1858f4df744cfb1e83c24271ee10c0c2389006b386cea0d4e275c1de11b08adb84d013c6f94f48ad7e5b6aa324674f487e6b8b705f5f5da00df9205408a39fc748d4d9fc042ca2789698a08d00a9e0740912e3b8e3572c98b0d246f2f045edbca28ac6f30a834d8b72d2f7eea93b1d43869fb794cd5c275ae14ecf506c55b81a4aefbe732af4e6c70411fa418c6e5025033997d804325f8aeeb4fe5de8e195e350a448cb60a5eea049fa60f661eb2f28e03e84e68ef36d8a19d8146b4c1a55ee197b2138d3c4c59de5d999c7784d62fc395fa7c8ff21cebeb5997ab26c0b81fd6eae43a27dd4c3f8937d4fd1aa7244eb10ddab93ae2f1d38ab9c8e08473e60e530630837f51d3132fe856f732c2f2f27dd8c2b345dd5e1265056e2814247d508d8f7b530ee006a47f4fb2d39456056656ace483be285192442de10149a8d09dc0fc3ea96e0160b9cf00379738b329e27c965a160bf9a4e79c70f574c292b739e60f7dc1af94544ad800aa51c08ca9756198c21ab45926f16f0f73dcb1fd43c3c0aeee7c2a2c15d305427cdce56a08fd284c30c2a79fd8c01c6730dae7b385f2f14c7a3987c8706e895d70446bdf7627b2e713a205d13a34a727cb12fdb7981e96afcb74fd52e8f34e412e033265342d99be599485119a945f59a34b37a4deed56bd60c604c5ff28343f32b834ca2b50f8bab30c98736369e2faddee333425eb5a76a688a1d8d49e2683c18ec77b01c6712006dadb4d6c5287277d78ad4f82f677aa9a695b430b1ab21f2ea27d6f718c5131917cea208698fd48502ca88aa25910f5fd16b5105d2883d31b95fee54f4448064629d6bb3da8aaeaacdeac5bd283ab92d313d9ecbed2d8017498768143a93bfcb5c2935b381febaf928e54d58ca9c8fe7600d3e0089560ebf24c237efe92def1d4944e2f5199c0ec2dd7cae5ba53bad40b3230122a0c588545e119cdb058845b43c28629270381c1f902bbab81ac8c7fc4b70f90f342874bedc3c526683acd15939c2987ef86f43e90ea80e0647f934088c4c91c00f9cbf388a4b6fc8f7005070cb5bf98086f239338c1fd979b9acfb6f7cbe4f6f58d1dd856ec909b9269b5fd10ddb503bb1e447268f37afc16658005bbb5ebae5deaec8f3be028743d86e450eeaf6731ada224a3e8ef851fbab2b83caa43af4c2227054cbb3a3de751df0f817dce0d05940e531e0192772ec33c8a533084bdca0f10f3a1d57d15416a4ef35543d1c4cb54c66da46abcead4a66fb0464f6fcc3241b51418d73203bad650e529565ba1e038b7aa5d5f0346a7dfbbafb4c0c9d4d196a8a5fe669b5a480326cdaafbbb70da2387095711c269f73da2b2f1d6fdb5484b272ecbd9c8f721ba6446b59b16efef04095a79eb489854a04bac2c36d9846fc0ba260e706c4881486e21c7833774d0de5d1e87037232d1fdae6421facce36c347d71f42fbed040526f07513f969b7e097b87311ddb467e61a2a5ccbf77b677a96641c688aa6bae7270a3e7637884838fb993ea2af180511210941bd81fec8107a72ff29bdb05f27b1143db030dd911d148a11f00f9537d58efb23b4384d976d2f4fb159ff65fcd5d5a6a88541e2199922c426a79b7fd37169eb6d1877d2401e07da8ab0089f7e618f2df68a376e52054ce188787909385c6eb576ecd5c7d66c74400640e53fe97332682e35a571dddff3a663b7ebb2480c7878ad05008eae73f2bad9201348a1ee69ab7b7ba5c124d889df69bc99ca672672d6859729a190e8a130654f5e94ffe6360d065aac362ad57438020ed4e53592b3e1ee0c74bdcb8a8ae4eeaf0ea919e9a4e00c163c627f9f66c935c1171ca1daef4e835ad21f7a87aba001452aa0afb6627d90258108b95dd95a5e6def312d3565332ab3c930078d400cc6b469ce5e4b98d4573175b3b66cfed5434000b966a1f6e99ed260545e844fe4f7857131cc6179f0a7c91e7c2cfe52a0ed6364a593ca30d788033f2b9bed2b3d6b277604ad01d096326c61f68742b9225e6f0a49ceed7d98b64110a5566c1f60c78ea628812270bbc2655a962b8511ff97ced3f826147ec9cad8eef663e0e7b0d47499c37fb47727c7c81226470645fd6d1c41fc9ab3b76e36a8dd2cb9c3b4ceb1db8325bf26da9a0cbd0e5b0fa8fd2d4b458ab04f08955546e9d678f291526327ea0aea21eadb6da266ac2dcac4e32c357322eb5e4219b18727d64964fa6eea835104f1ab1be6433a6293a05db072b37e573587b404e509a2da2bd5fcc123656297a5231c57728353d4a36513cae0482a48aa5ae35e65b9bf03b84065976d8db6894044a03870b058e1ee06b841ac6ff7bdf4bf6f4ef3afa47f2af596a15b5d8f9d9bddbe3a30875df5fd1a570310d813b90eca1aa18904b6645502cf3445eb3b293a65a031ed3da701e879852ba5eb75ec8d321fcd29aeff957da46c97e68d0129219065104dd2ae81429daff289206ddc0e43b4defac053b8089c056f140992a82924af12192ad16442b5abfc110d614b1b4c23704e445d214a83ec7fba0bd15ed078ee9c0995022358ed36386dac4f33d9e8933326f06fa086d9b7f48e2bfb169dfb514395ff484c0067ef9cacd5c7eafad9c40e9eece97020f8f86581c4033bdb18b5540c28198f3ba6c7f568b7d0d168378c7af985e10aa7ac864c8efb5dbc2dee5bc06b9813d40022c0aa5bf5430a61c13a0af9546549d9c83459a2bd20a85d21e358c06b9cd14291bb029562c40c8f34cc3b5c1c3f5de021d8073c70977a5b3273503faf82955840daa56fd75c19a0323d1d3411478a908361a7974d28d4aac641375199e339fabc698926e86593abff4cf0bdf01a063f0fd0a6c2f53d63b4f759bba1922cabc0cbb910c7d03a2596c0e48c98c39107c4745f1a5b420793b4ad3b90245914d62043a1754e4b2d6ab8bddce38eba99157cc2c255c86cb88816a6d956449f801abd74d03b9c45de292c4247f3fde8fa96719db307f38eb0bef5cb0d44d947522b6814e11a8d543da9eea96e840116beef4d6da92b2417cd3f46bd2d3b541a132d4c9d9f0fcb51cf20d6bc4f18ab0dc22f6a7159b5f904384df644108b94d18f7d8cabecf86fca6c225e8e7609f9d0cc8f9e5baa1a02e2346e0ef260913edd32601edcae2668ae51a7692fbb619b932f5b9963d4989c89a93b94eb5b1bdd55f94a854e89215f639c8aff9766e77e79f963832e3e365c64e1c25198a5311f4b3c0193f729cb83ffe97b1e657253bb416af2866d5a0093526796da7e394e7515d374eea34d1828bb540688a8aba19f3aae6dbeade73670d10bfde2a6b0570c8d2f90aeea832c5bec4c8e2647cc326994b725b0a50ee35460dada3ed97f9d90dff88cf246417cfe5dcc29e6e888a17e7a5ae1bda57e4db08ec74c629e88e14336144a602808e06c48cade67e0cfa4d4999ef412d6d4a107a2f30ea11edddd6421e4b67893fa4f08504c979b53c1018b19f9c2b70c051afe70dc6d727b5ef60d9dec98fd50020d8ae599513898691a86978ab816d332d79f8b865530cd9d21dac2010a0936691444dbc1747040780d4fe5868103453997ba40ca69f83ccb38effb1f25adc8217bd1e140ac2c65491f2efe58897149b1e483fea71f5a0bc10504982fc6910bce7337b77101ce388becb8617f395aea003106e99728e629ae88b9ea23bdaf746fceb1fcb6452d6267974b0ded40b255eeb62dcf8620b78f4474e5ae3d56d5861992bfeda97c38836a160b81a1cf6a3cf3e2043788db636c689befa590c5f23c83bc210b6fc2e5082510c7eb550044813c6673a93e699b4cda06f72a28eb2a55afcea808be8b64dc7c8eb1c3ea3befa0fe94d51e712562fcfd9d136db3ff984e91c35a3c663f2e6d52a5a832290c38a25140c11c5e2a4f4b0d71128333bde45e85290f7cf40120845a493d690852226a047dd0ae833cda4ee119aa5ffc3edca83d02c8a9849e6166e096ac9c260241afebb771f612287bab4dbec0d4b0aa7b1c9a7880b863f620b861cf2d42cadfebcaa73d105b59c5c9e882094598c2848311df501890805f1a95ee36ef1b1927f049c0ce65298505293b0dd48787af0866e996a2ce5c680e230e7fbeafdb261a4eb80132832737eead4cae60bf2f2e782d5450fbc8ac5c391dafb12dd26b1fd23daf5327e7fe478460b8cf80f230f21c86b7820b8436bcaf9c2edf150e1a15d467927ae188382ecd927d576df7fceb48556737f3adf71fdfd070b24184f37b889656645a00ec9fe089b1721dc355f65fb0573532a3d84de60bbd3273bbcbaad65dc98d1252ed21d66c4fda72a3397d05a9756a394af7162146e400590a31fdea629dcf9c5d6428a980cffd79644b1415f255dc14fa6ebf7777094cd39a0427dcc15f5283e594019949fac5590c0f9ddcac45ee6136570c6696f5bf37fd1e5c94f34a5e52c11e273ebdbb9263bb59801f28ec1d3bfea538ddc0b6db77db47edaaa3cb31da04cdeed8a9dbad9bab77a299768e86c15754d073a661d2588055c0aa3ce5a7cf21201f17f675db604e3db0122f4a39eb159e3bab755c905820fc03b88f7b17d7b218c4987eff329480f8ec58361cc1a0a95ddb8f0f3b5855303de0181e777f6171c2d1310face52712ae12158e5f6a834364e19f6bcc189bd9561187bcf444d81bba557b6db8507fb79ede8820610d05cabcb247ce3bb347a928db0e69e23f549746b7dfb413503b1e33266dc80d38aa4038a06cbf8b48e60987b06e0b95a82d87f7af5ed2236bea533592b8ae4f4502ff1d95693c531bae98920b40132448ecc849518663e7ce64e0d045d2651f83538ab8d3ad46aa915b0125f4e4ce1dd7c22534b5ffa93583d81a56d887a8a06ac8861b803819fe6906da80540bc07dc631167bad243153ed37567516a077af7fb9512188c01b747a22af3ebab4a946008e5d14ffc6f94ede406562d9e76bf4e148630b3ac7e612e197a628b87a43f669430d6adf29f66071cbbeb896f7b67cad3709236a31b81390860021e5e0b3528acc25fd9dbcc2097c44a1ef503b2705c786c758ce6e917580cb37d0e145eb2843357530b4400a0d97ee653d396cc10a43494ce3be5ef48d799b901d16a51d28c4f5bd8fe4b805e269746fd25e922d50d92d48f30f445736cb6f1cdfad9392cf554515dd7a7e60745143ffd226cc24f1737d77ce22f9671fd2d33d7651c4858d6f386fd066ab43a6c65d2cd8da1390153afd29aa3524108fabbf83f4a25da532480dd1a21d53671b2b005106e18b34dda986ace9fcb1ea2fc53e5f3e1150e67c011e34bce3dc91a50afdf01ea6cde9cfb7937ec7a428232e9dff2138594c4ee03b970dafa172fd6539eebce44e8fe892fadb042d5193dd5db6a8bb6b4b8a390b9b6c4ebbc3b47209e1b6b43d832853ae1edbc4ec7d14217c81cbb20653422d2c3a7820b616b13ce2bee47de1222491d804d4689849ad536119f2ee917c52391c3f2597eb84b658945e9dbb02d6dbbc6277bd8bd12a79e890dddc719121b65cfbadadf3d1a375c2cff1b592ab025fa9bfacf9fb04631fb38e8225a07633d08d29c12aebe6c45ef3c1d7f92cada683adebb0ec2716ed430269934e115145d9a8f903905cd74c3d359285e05bb3688fb5c297a57b613d14f7d266deb5d3f9ffdfccd852deab9f4e313e043caff95489306588e0fafc08e6635c3507af74a7d3b73ab7a3c61b0981b2206bb809beb1a6fa06bc0433c3666c1b0403200911d799bc4feec28dacfb276079c55bc41d294ffcff520e0cac38ba78ae08850168a6a15278c9982c53d39e2e88c83b0fa0c6f51fe759daccf007ec787ba0a394671d323e7cae5d13ed773e9862ee6d7aa5e7fe4a5bf0bdae47da7c582b1645f31c28c1c62c2ad6dd49408edfc38e3d30612ce32d609c1d830d1b0b136666673915496686847614e9d626dde9bfa000c54cb98ff4d0e70984fbdfd652fd1ceb1a5a4c1c0c2b53e96e53c31e057ae27a70beb4bb0958482642fab3e10951bb5c2281aeae1729d8a9b1c0828e9c8b5b51cec639734de8d09a9d62f300691a411eeee187280fe82f76b3cac48e5d98b9a2ec369befb61011fa6da766f0b8a2b5a5f9f2c7ff4b635cafef9eaabd710202edef340c3fe42c3a0c91a4c2c2b7b90ea3a0e930beb0bdb20d1450ed568f6250a654f38f26276518d3f9e63c0cc88e9d3306c766af638ba5fa812430e9ec444bac4e883e4df9c639c33afb9a8c6d414333e8182ccea75b2eea7bff1c3d1ba2945403f826ebc7e8b0caca0ec764ae97c4abfd4825db4245ad6317df3f785127a26f1f15ddf58d00e1b7f5d1aaac3f312bd0358f4b83d4c938ec0cc24d3d38f85f6a6321de53fbe2eb9937c497ca6dfbba4d47b87f9834aa8b606f4594be435c5d85e3bc6428b3d06f0b895b6aee840f839d7c2495e9f7c1c95191bfeab0c2c3344c7d457f6620c15c873c84fed076ca5dbd88e04ee5534a9db6ff0b4a0fbdcccfe1a1ed9fc4099a4414ee916003ce754c0dd4c65a1e799cce587392c7a06c4a90103c777bd10dd6abbce245a38201592513bc292bdd92cc0ae1fdab3f25156a798730bdab27a7bfdace0da9c12f969cbb14094488b618c19473dd4fd91bf07978e9664806d3715057275f50f077e39794afac69510673a0b3c66d9b4ad99266e492ce2703ca9ad9bf7b818c651c0663d1a2a2d681bfccb4292a660acea50714019ee4ec2658d7c07dc267d1a1034382ee28e1bd8e7fcb7c2b576d0a32dbaca293bc3404c1587e5564dcffad2c606681ce1af52c89ba288f9114f8c87c122840741e0438794177c7ecfcd8fe6d5207cfd3b55fe3dace68afe965ef5134952a7ccc8af9883780e93e97f19d9ffb6f8b9c07477ba66aff38d53f149defd78dc25b807a3fa2f1af34ac560a81452f53cab917e2c5d14fd0538ff5fe288863ea53bc5c6cb49c5cc30e103418f927be34484b95bbd060e606f629962a2bf7b3182d5176b2a103493bf448f53916d87e42651b6333aecf6108d1da638bdac5b142f40751c493aaaedbedda93ef7111ecdcf36b33ab80a7a25999b1a2b05207d8e125fcb215ab9a621a2802869bc122e03d30d4f803a3d9bd2485e7728791bcfbc7203b15b29571cf7415d254fa8d4c4273d4c2f17062273a5fccac65b9be56befea9ada158618886d90de1e0fed32cf08e6601a535f8efe7e717c8d39c64c002e0c4fb2e92de916e23ece72b0f9dc7767566eac108db9c13c7a3e601d0b24b2ceb4214c715fe771ef712af9e1ba14eb1851772e6241b7d7a630c3bd981b4a558ad020366aeb4a271efae43f276701249b802f123108b717ae34d996ffa4ccf25c81a053b1221282d7b9e75bdc32c0b431ffec05125db254304d5ae99a8e83945eff7a870720a2cfcd5bf1e045ac06182eef7d4c234f45d6a6547f6fb8302ed2adecedff1dc3487824b9c28e807edfbe332468d5897e2ba2427aa37d1f980da23c1c45124b7a52c8223245f1153c4ec40a5937cb411c12703c1dac4a967f8d0c9c1c6b9c1f62eccb0555dac79669027646c2f304e166b9c101984a3909172e69d51540910282864d3c2befc5e22b357eac906a12ca7f261199e6b456a7881247d806c33ca2822690f5e377dbedfe3ec3f37ce001c60bb87cae0b7cdc27fdd1ac5682f566cffcc0d73e243a12a6f4c7e7a6ed50667710271c502e3256ea3a9044b40cd1eb9a5c4bf8f1db78853fab33f1a7b9c89a7b6d69a28fe27f504015aec6f9a0ddb60c18b714a4f0f985c766ffbf2a88ece2d9ff7766027d09f56dfa43aca9740786c7d160c1f440e15e63d775830d09f0abc756de4cf8fe329f6fc23def01dce409ddbea620881dc7edc4435fef211468635f616927c0180fa4ca0862b6c67519e4c3528d9f0d4b76cbfecbea5e2529f08ada50f320d112ed510e2d787937237eb19b44402a9e2ddad92cf8ccef56200b115266b6df704730e06ea7416bb68d5ff6afadf7523e904c2f647d5c0249059e45265630b2f7b93f02db2f895c619eaee7f1c8cddd05315bda64b049e5d7e3042d96aa3397e13792a9ade57dd05d09e4433ecbd666007aea88f88cf0f7121304eea7e767edcdbcc805db894bd28a2fe7208fb8e54e36b0e23f1324581b10980287dc71070c1c663451ba15d0a13b46318b695e32ab23a333c61fd2d3a56c8467345258cf0f43c27ac49bae77c815cae8266945eeee9e02870018ae32c9f3d2033d9286549abf565dc2b640de29b2b4c6743b0c8b8e689af7e9fb8afc199cb4b6ca5a682e687042d97d2345faacae6a4c296ed718721b5bd3a1d3edae14f8c01d50008335aa1d41e252183081de07f7798361528f07a1154ce34bccffd41de5b8218464fdaf0914b8a0928371fa48f4035a9781508ff8ff0c7f4c2b850cbe9d8a5a4f87d85961fb2c5b50705fa17854a2d22c97f86e3421e70283e6e764ee5ce8ba025e07e7d44110201050764f9d7a6b13d44ccf79e6066b0c1d20aa2771087fecd91fcc618b7ec4782a463171e84dd10acd73b8424ae2fd3ed653379716a605f920bdb01c7eb719d41dc5bab3feb66c58abe4366a415c36955726c7661e9ac99481e76704569d3fc669cb71e9b662ba72934530035b20ec1af723e203af13de5e615e247df47e5a68a727cf81d9f8c2306b9e58bfa978174ac1f4d2ec58623c1ce0a7a8cb0d5167cf65e8a3c436dfc3d23d0a773378b501f179fac18c1b41f81c066d9984472b96895085c87ebd284d162ec03e4e1d8c08d3580d4b58db7060b07f6001f6fcb781f104beccd3b8d22e4ef77645448d5c526496d6500058cd4050f784b1c9a5ee7124d0bee41ddbf20335e148e4f99d64059eb3697805654f40253c14417929bd55c3d55377bb5ee3a28948326e03b79b79fa33f3c45454dc74d8e8c6b632b039c5dbdede1093cf45a8a1cb2fa71ec55f8166ee8a0a9a289dea9259b4fb0aa2828d7be12f6e10a70e8b2de3ec59569fd0ffa66a5f415f0b1afdcc105384f00d8130e109518d857b80d978b32d2363280d2bf84f62f70692a27de1e1ed895312c38e637f74bfa3395a8db6ed033939047044459c9b230f8a05ff64b6155751deef4c912b1f6db62af3d7ba91e8595a844a3b2cbcad51f35aa064880595337f85d980d5be8df070bfb114e3f6c7f9b805320fc15b03cd7383496a23592036545fae6c3bf295cbbe694cfc52771631dd68e605ede0b2b27c1bcfeb5198fc426b354f33ef4924edd044ba97175ca2043e897e3a07ba1002100e4fa19b502df92139d6199a6967deb6bb789c7705560e226eafe2eae5a1b89c23d1d2235b5b3550025c85970678b6e8c07ab98efcf71fd77a33ab12b9f7e8a2eb6becf60ad69d455643d474655b304c9184c07f4552e234d238577ec8c455c12147d9c7b408a86e7da439a2be1ea99e87d92654367dd07f7bb1151a3ccb2a3df63bfc72c6012b39f35eec5ee6b757bdf85b8cfb1e7dc6b0607422e259c5f4fbf09c5547314cea0bdf04413987e340411180788983659fe99a6e5b8e4f65efe4037def42912a1f01a0f8e72f445d95f9e195f67b47ab3682de77b6b79e6904dd4c4a728ff0301ac89ce955e82b624dfea7aabb26343737be395fb5548189016e3cf225f338b4b583084dc16143fa84abdfa99a4472339bea2ec31265e7913ca06a9f1f8f0ce4fca28101be5a2dd806a7d288591132b3f313979b21b78e7616bed88fe00a5d4fee097467498bb9be457cdb074c005a327399285b5b121db267c94c3b6b423d1c58c7b5de22c3863f5d4b4fe4264f2c17910787cfc33e2fe995218897dce9270afee72c819e5184ae1a4f82ed6656b2b2d4e8c4b808a19b4a1fadee00003a22931d72ddaf10165a361ce9cdde8e398c7adbe506ed2d24fcf015ee40d765249f0e572c4b5f82784e0be74a1a56318ced9676197e42f200a76961928f75c7336a70d39cf96a44a1681ba9578231f0f4cf7048fd0bf868b11171f28bedfb3cc398df3ebe5d9b2e0ea9febc4288ad732ba380cfcc7e5ab50adeeacd4614b80fd287c2242cb20558e0672dda8291806e1f0a541ef813d0c4fa79f7d37f3861138bdfc90fee89a237b0d97856f44a5990d575d2fbbe8512fdfa114dc896b42936880ec77bc0574ac92378605b041efb2b5cbcb135eb34587d001bbad464b19ab31890c007ce54310c7f101a9aba334d7e02b93a2835f99393819681dc0dabacf253e9e5f4ab7491ac12dc3377551865e8600bfd7d873df453d707d49623400d2e2f6b14dc3fc0debee22adf0740e6990e3a84388b581241014d022f4820782c016e287ec44d7b7cf2b466f96674245591eec017535fd5bd6bb558557bf68f8070c26cf389b9560fc753c2d970693dbb649342d6ab22c06dcc5d60c17335a3b4e7fa193771ec16c1c1ce7f06c9122215fb2e096d93e533079be88d08d97678217a5c7bf81b4916a584a26e6af0527d2b3078631d334f1f3c5bf155ae4a1b60632d23c127391e4bb4802ea504a1cfae7cf74ff14c2bb6a33cb58f8ddc91705a270e0b841a1bf564e30af73a0efb3177ca329e0de6611c254c1448100d831a5aff8a1f2262bd33ad811522bc8c45470a7225f1c9eb6377e0e487936c6884cc19ba604b8b60cf600ec9fdd90048ff5676811f652654314c0831cec343aaef1040eabf85e17cc0c16eef9a9590f0d0b9a4464bff78143155add96c05b34f7df89eaa4df11fc721e00bc435f86f6cc38ab2ab196c8cfefd13e0b49bf4641846e26dec831d7bd2512648d2ca94c4c0febdda920f68a69fc6c7716c275d48a381f5e1c87ffbf99c7af11364cc7cd61f9b8f94e97e4e1119d13e1222b8d0e296f81818c5e439b4be1a592995ee61ba52dd4a6a225580ffa2592791c884cd1be7e6e4224cd12eb706aefd614e243f75e9c00c849854248793d07b7bff38317357aa8ff1626f1df740446c37f611d1d5d4826244bf444fe7e104144f6e21f7c5c2c241b5e1750a8851fe095f4b2cb87427d6148fce317a34133333de99ba6d170a999c9289f18168bce3dade784719ce98d40674fbe8a9d9038041e1569357fe583a66acbd2611db5accbc66c230d0b777c1bee2f74907e01129ef175c88a417a1e64c34312f932b486321e9efafff5163ff72fa7023de9b4a74f345bc563f267ecf4418ddb0e030b0affacdcedf5f46bf71464e05f3c2c6f4a202ca6563f9c074f992f77802415b94253000ba4292c6b4935ba48af20d088f0c6946d72060b924c2e7f4970b9b6cc7a7e1918a3fa16323369ab8f039d10727b60c357c755cde5ade024c08fcc4fa7f243c57989709b2975e7e6022ea5b2714be9b87bfa941badd81660dc5a5afc093b3329e68fabe871dfd3afffd2ff91d5048eb6eb780324de9864bd8fec05dffc31e94ecfc2dc589939d2ed183dd6caccef5a2443813d60f9823e04a582a21c64371e568f808aeedade1ae4015f3de30e8e9132fc7e9791a858aacc5b401ae7bb0d66e0098b82644b7e1c8c58c7654683a707d20500954a43951d5721d1a1e0b01c313d251b29dfe260ed7e97920325342eac06e4e9572859b82b23103519b4c1a4ddbc332057da8d715d1720aa57b55fe86b95f77bcd755e5240fbb08535941e3bff1d861e02480c671a090ed4c70bc7f90720560399ba46b112271be1792573906802854a52f12bbc18f4e725b15aafd827a894ae3afd71cccf971a0004cfad41e41d8aa6839c9fd3369a07a09ebd0f3ef7fe6e815581454475a184ad52fdf1722f8d40f5bd207cb37e83675738436468a0c0153677456932af964a84780e43da8e2840e769f4ff7b8179308e835f6a0f6dc91baecc42d0545701de0f987ead257afac793855375f115a7eb3087e88c179c183b394621763f1ffe355b8f2cce91ea4429859d00300f71a4e9173667d39b8b1035146f795cbf3b6a134ea45ff6e6c5c8c6ad0f4bef51a5b96cc5d6be3fbcd35b1c3515315b131beda4c15bdf8df9a979dd1826d4f20bd50be59495afac8679a8c0bb5b9c2254fca1f384b4912f407e8bd624f2e7ab3671ba4cf8251d34bc64beb950934147ad0899e798008baeb24a4a3c337c6ad84daea8ebe88e62f25d8eca3bde73bbfd05620161f00dc048b120ff9cf974aa640a519be8cbe65b91e1fcb2e4080826548653e38dc1aaf1ccd356c0d99b154ced5381bc498cb83b4488e897fff8a702251077cf18e0b3b6bc22a399566ab699cf0f702058279fff3158a5bf9d4190557aa35fd86903befe7844973c73d7f25be97c744e814c6b6d6dcb0271bc7251b20d4a115fe050c769c4b4bece3e20200ff330666ed9e648785f4e9884190b6c747967f883a9bf29e33265cb4fefe822e524b48b3c3a228fce3010dc27381a6b66f4d42dc8c72ee589f292fa7f4447d4b12fd10663c66dab94c71952bbe96b0c971026e2c845da3df5a11c5aba608e82482c21b1e18a899c12d116cbf348bf3caee2cb9c441a62df2d6c0c16692de5bcfbe76a23b0495824b1c5c55a813c9788939cadd58b9a4867f94e709a9aa09c67587ea15053bb1de0c5e5a5d7af367bb095ff177d61da47edf0d6762a43bec304186b7d7749aa706c578465cf5d12f3fa5ffce427906908530ddca1b7fa7d8433ca37745d967eafc75e58bd2bc415ae1dcec6dbdcc223f37949f73bbba0cf67e6e9575670cf4f74737286d295d63025231ede0b5c08a0ee63166606ca6579fb5bcb93d8a71fcd6849492ea2c3b280e14a886b0136d4f55b9de3c39c24c8c35500d4c97d5f37319f685ab40b5b7299f5ae031b460dd0747c0dff5d7d314f2041986f243f4bdb18624049497d9b45baf95c0dac7d40e53e2c3d8208ae7f921b2a0ec385c987696a70a156f4a6766c755760420c20727034197da2508414fddc7bf8c6ff7100b0b5bf4c1f586378f78bab30ef8041db9caf34a349b487021447215bb3ee45f24037bd590827bb1867da9a8bb179d157c36376fb196c0cd61fe6f78365e974fd1e264d3cf67aa5ea772bf048e678147339522dad44e4094b26978b919f11093123354fec2fc0587484adb83a291735a38d333acfcdd5fea4c464627aaaaaa5bc35ad9ccf9b83ecf26bae6697d3a1cd953454402a7a871970a2a94a5b3e65d54205749e231217e98b292ac02ba579d7c91d552b5fd72cb59947f9d74c0725e4c0d3da273c97f2064af6289335ee437aeb8de221074d83ec1ff380e9e8d3bfcf1b127141b347dc5290e0c6614e4f93c7a4e99d864b48153dc9b3b124d4d1642d53adc26688e2460f210d59fd3c0483f900e785f5c97084f371b4c8271466e631671b409f1e77dccb61f5e9d37a2152be6430d983cc9370a193b4e095d1f41678eb5dcf2063baf6c470dd4e2c03e8999e3d0cc1ea8cb3b14a29bfdd5077ccb4207d355c3d60ba796161e34d2b928efd1223dcee1987395c69f850b3709ac71753419f6621324fb93e76436cf398127d6758385a1d3671ccf14fd60e1d281a9ae13305f1711804e293e51154c5eff4ca99c0f7e04223586ebc8f8270dc2f1040ddaa3f9ed29d2b3603f0feacd6f000b42709b8885dd98e21e080258f945e195e946d0f653686ac433e93ecb66d70f27173607128dcc2a5c84ddc60d52407f1809a1da5d73afcb738c80759d954db23bb64f64d19bf13e06c602d6e93953b1886bfa2eac6f2a4640baca7493f3910ed20408369c65e90d29273fcb7cf12e022f3f018fb8d73b65a876d518690f5d87bda11c06f3f995c9538a17519b6a4ca59fa241f560be28db03b8bdbaf137f91f19a15c6d5cd580162d3dd77e40d1e3f5fc55288e417a9f5c50c06c1dd6f3503371af0123e28244693d30556d7c2646216f5c629927a3f2fcc664025da5270b638a04bd74f74ed2032f491171421447ea84ed1fbcaab327a5f12edd023ea020e2401f43e5ebca0579525f2515fdc40a49bf9fba897ef8ce7b1e9284948fbebb8c4da4321dc131e296749dd7952e9466378f918d02ddc711b70787466241ccb3dc2fda14997202a89c9419386ae6e0b907d662f626033b751b0fa52fb0afb59fdfdf1c0f9e4d4c5777e2a6a71d707ef3c8ba9f6066adba8644388581b7073c3a3dcf9bc49efacf8916d3470f92c3b7049170a44f266032d42797a2151d8d9cbf7633b27873e2b55f0341fc78596c1b276eb212c4198ac7595743cdaa7c61c92a6713c82bf70e6de4fad013cc8333d967d90f03e9826ec7bf1c4adc900b9ebc494454867673846eaea3e80a8710154ed8f6a32f3c2f7673fe3ff63c7d276017b752741530df0523e95999ca32b4295f8e1577edce342169e3223387905039871ff723fbd7da090791e6cf468bc38f127e8fefd905de383fea6ee4cabfee61ade5aafb0a4fb58ce277093d882f05f1b3fa37974ea2919dbe539f29cbff674cc227366a935ade92782f60bebdbad1c1c520986068498093b859741d0f0dc83e3326c844c409e570961c49cd4653d6a4e2e22c0c9afd20ffb7f86f345a5b4cc20966269eccc0a1810c12ffea2f594b5a3959db98ffff9ea5e527a22d4b5fc9f33729783e0cb15d7ebe1864828af477e67bbaa025ed02b147c2bde3184159c3356554b3afeff716379ae5f50dafc5761a65305e70cee9a7944eae80a54d4b76aa2201d755cca3228624893f80b8b55a24674a66f496cdb0ed6a4d59e5af531c59163cad1d11133a2930b4bfb00197c30d3b2d8109bd19d61b7d651059ac22d778097d0a900dd4fd6aeca227f3df0a55dc52a85239edef46f52af140a8a2da61cc0729e913213d65af6d816f9f407b6519df1c6ef52bf02b36236eb9aa6384dc4621d9319f870810d0ac6512b12548b411b43a4a0360a08eb9384f55ce1ae48b0362ae126cd349dd0a5cfc3d1593721c878de133137332cc703a2fe90ccd28b91ed71bbf27c93bff71a57c038bf6b7f52778a3272b1d18fdc121bcb07edd84db3bf3effdf734840a9ef8db48fe62810b697e2e6884d6ce27db2c97a9e0e5cffc8e5628021520d9e7744c123bf4004fe48b9c4c5dc10c8013a441e07a37bf905fe840b826d6dd36ff96a1f234b9c1544bddda6c9ccfcaabfef68ccc7c499ab66c5eaae7667db41eadf8e1428b8cb42047f0ac6ae6d9babdc3d0a339a0bc04964d14552e21f4967191ac58d8c39a7ad0462f7fa21a76b349fb432cf6ddd869a2d522cbfafd0634cc69d443b22ecdb9297521056ce42dcc9618f26ae0bd0f8e3d765b0c320bf2e18c9e3f56a803016c1a9e4b4b5e1ccefb57a3bebbe02a9c7a96e5b456118a6e80d8cdf7b4b3648a618b8cc69b13dee51990719ff327933b1c07de7d30e9fe056bad79afd00efba0c529aca2a26000c19586bf709f100af949620c04044975719e5e557cff74fe88efc8e6d625db25b6435830b79ead6e1e9c62fbb9e9da67b8a315a5af7e5cea4921e3691a4d4b96fa1c222d14dfa76c7d37588f83f0a30dc90000f528cf5624e7cfe64b49f23f63ff44f42b2f8a3ac9314f7f1f92e9b2ac43092fc98e040d40b29bf53d4fe9f038c12ce2d9c70add0f6b3372d6922858614493ec88cc9ca1a5921ab8e16a30039b8478034bf0fe22286800983401a6b44877d3965dfcd7cd2dbfc662576278fe6dc133efce3e7249d047637670346073df3259d440e26a0dac774cc3e1937a0c73412686212c437058e9549b51bf9db9d35a27e9255205d9b3b0595489e0986832e9e563f9b23f116474a5c0598ad9f3571aae8925bacdbb364a2c9f995a6f6f2a2951d0c45bd67153d16860b2d05e654b8767786161ff481696d9e65ec662f5bc4cb6eecc36457c1ad091c542a6cabb9a4d50a700705bfd48ba2e05597dde166c820d6d413128ed963c9191dd0d90850cb05b7931f0809e005bca25edda67ac24bacdf8a0aa93df9312624cd93815f95ab0957be172daaad3a8d3a2fd4f01b0c958e66ec0c446c1acd22153b8d6dfe49060c0820a33a6a2802dd82e00d354074b27f15a3b7f0fafbf065eddd5dbf4ff8fdeeb1e658290fb8d4c5165ce7fe0ca968af6789d9136e64d442973fc3717c9b46a7f71e7cb8ad8c64802a943890530cc742df0a55f30102886ccdfdf0a1f70b5bc582bcddaca750c62f91c98418e84fe29d5477518fb846da212f02e9e4b9bbf4ca28090ba9385ce58ec5e128c68c81b2a49371ac7cfab7d669987031fd83d51c4836a6e087960683fe3886bc5f3515c41d45cfc5735795a8186d65724cc2a580707cdc405ded2b34cbc549e7c2bbbd30f4f5fd84b000c2920bd660bcb94bb88a5a72a38208e500133bf278d4da46c43f49207849680fe0c6f077c174c6dce94541807edb1f60e635d5cfdf48da2516d924aa881bdaead6046d9c05c2e0087bd731f14c4453dc411105c814ee7fe1943529c23ddd4847543749efccb63cbcf6622e09eb30d1a49b1da4c1c2425f4c4c4424f42eaf95667e04b7bb2cb5c7403242f36044879f2768a738549ff425edac0344cbe53bcdeb9f8b80d5027457a504db9a785a64f25c5c9a62021104693989d894d2410d725d2b5d6822287bef0a55ceefbcbc6e62a0076f37111e7f49980c5019801c846516b8b56cfafd2abaf690927550e2bb775ffddcebba319b0175bff48955b041878ee3d8291af2da62444cc628dc14843e71b1319b1e2feb12ebccd09063d264ede88c9684bf5091a69aba324fa7cb9463afa81b083fb2e1dd96f4503c9ab484ab17131852687d05859e778cfaacef5abda361ef733ce4c0add2f05776e44e901ded747e0b6dccef79e59e4d8ff40a20210c9cc82c98b829aec953fb033f4a56a0ac57345d21562d03cc364d0c3fe5ddbacf4ead850a64e15bb43a9ea59cceab086adadb0ec72c7951f884d3a1b2351aa176058b931a2f5608e756af5229fc333748a804751eee8445848f8dd3d9b56de224e011b1f662c93d72609e1d06db16ad4ceee6ebb1de20ece41835942b04f42cf2115ad7f86628f7374286f001d45b001fed89658d5257fb1e2f498995b09c3ae0f33506cc753953697047018e3510c600b55e0601cd3a64fd8a6935c93f5596beaf6be97bf4eec9c0d4a024f03981034c725917c395872a263a6d4f2905253e5f5764e3328a37cd4b9611b1017b19c841a9a3f9dd735488e86b84633c4294bb9b92fbee4fb55de72a022aa230b2f78a9602d73ec246480c8244a860d5cfc1da2a319d7ae8c446bcf6ecf1299a33bbecd29c662c7a86f33fd65c25aa5647c4b769cb577a4808ad5c246f63839e31b904ed3042dabcef705d94f3c9e2a9af732398622fa3ce1c32aebed0dea51228d18a0b97057b01dc16878a789c051d8b2de259147f2befcee5aa757ea4e7a39248f63a086bdfd49da427aecb7578c9a971c28e62b5ffcd70bd24a0129d1f1cd6acc8bb132f9416a72b4fda95cdad9356961f85aabd51de0b0a98ca8c557c2f17bf411299d4aaef4934bb11b35bc74ddd99bdeca534924efd14da413a75dfd0d5968dfd376f31c8c33f681bf556ff95fc2684612e637317b4e83ba72ed8af117aa2e3ac4514eebc586c79d345d27f75fc6eab2a43d05d49d68d41308fc592ed309ca265ecaa79fc357a87f7bb1e4142ba91eb7d57d1a76d76770ba60c3998f85140fc6b72c931da4a2d742a454a9ebca8d54ac22406ea6bc2ab38c5f39affa99c3553f8e89f7c1c6bdb1d3c079cef1126d8b0f2467a1a80e3c47d91ce6e3e6d38fc496618d424487e12ee84d01ae089c5c3f203ffabaf6475d95a207a98a548e06bfc17d0d740be22f28d2961472069a173d65626f56ec953ab120f0ec785eb441c9aa17d64d94a73d1421aba5f33ce2627d71241c0859be5d40a694f0b93f16447c7d85169fcbf1603db1b819dea1a9a761f9285411c04233e8ec50fde469c821498f2aa4e7d93e63ec8b45911f1361308f7efa0c9c7abe342e01b4ed0b05d2e121dc95bf4ca1bf14ddd823e6492eab0db09bc22a25b0f3d754a89f9394f0f8603d5279fb57ff168c5c91a08f504cd3a9a87d089a8a38309e505200c763d644f8859032a590613c86efeaa31a8a95ab4d16f4bb1b80b84a1b706017ce2e17bde15ba604d78f6b1f256ba7f81840d8a5e06bb88562030264f61a43cf327c71c56f39728a2e807229b51bf7c16a0b474f3b67b4dcae5caad36d88ca3e3670e6239426730aec0b8eb5a9272bba80563063ee393742a8ab1b2cab9e1500169102102ba288f430e9a6222090a1fa500b0b13f4d15349ca564aef2c5134dfedb957f6adefaad8dcb43e4a6a1659bf1ab4f0f84de39bc52e8e92822a25050b7d65bcb8b40df47781ee7eb473e3444ca88e6f0bdddd8894f211c4b992f16c3f978ea5e52969041d863616594eaa120a07254495b1705b176af462dcab1343b9ff62d1062dbb19c0835474aa158fad0b12e8bf105c858b88af6b675e059dac6dec5e24cd4fb066d061fc47726f9d54861663b5dcbe0b5145bdbcd2d20c1d5aff00e5840132483afd607fa5ab2a3e578ceda19a2862cc761857e0c18f626127d98f29bc1b1edb7540bff5c423e5797d4fdcc7bccd956a27358e7169f0ef035774c820b05e480e669bc852eb8d27f56d05a86df645df5a54690c476281adff2aa32a7acd73d61fc5bf66fcbb2686e3f269f3696cdf8852d8ca2b8606bce91cb326f6a213c5a9c8f6ade380fb29e5d09e9fe625b9b0a766caa7b9b969c2e6cfd1e0ff29d5de8671904fbffac3b016ac935134577ef5fba2c592a7037f64aeac61eca63eb41035f5a53a3489b2d7137cf5c5b9a08e36569f47babfd54f4c7b1163a77daf93e65c6209c29d014ccc395dc020cadc5fd57c10fc25d8865e43068d201fd5337960425b30b1bca7d91821614f42bb65bcabc5207a1660a7b63f5fca396a416da1757483317bed668bb03a3b0563e98f7e8e8e8745fea8714c99d97373cf3c13a6095af4e8bb00441488c821dea9f860a8a0d421d88720b510e9e897e8b48956af0dbe463d455556a456267a16c591a4a2f357e746340a70ac9db992a879d6de9e29b89a94aa52fb6a42485dcca1a3698246cde44db4e407fcc72007978d32930ff7815aa8ebb9539bc33c2ba907b0ac08e1358e9ddd58c433eca2c301f37f8d30016dfa4b35736873b0277f5da2615892ab5fad8c079b46e99ba1067edd47d6fc72d1b4751d1c83fe82da61801754b5b46378dc9dcf8a425973ea592a6f545ec16d6470398842eed3158e9161194fb2a2d604c2c12b8c42916ea8f4c7da569b0312a8a0e72de623fe67e0e8e9dae1c82cf97aff03d6b21e5b81ac42ce4f869f228dba8f73177f8c0efb5dedd870f9547b438cf1f7a0ae3ee6b6254215e44f9bb9c274ab1d34627f7fef546221181c32a6f521a67ca6bd4f1e765afde8a4487b02832559823005f00fd0b9d195f98b71a7f58cf5e7d4399592b0b6c1653a57eec2074a315d25823e24ae469618299c906e50de009dc4456f05d2d1f25a1bedaa494b6c05dfaeb8b607120d3b7a2456284cf68323ede3e5141b1ad7de6da972ba2a36c19e0464ecae41cef3cd80eb681d8f8424c0132270b81dc0e694631eb0f72f1786d2af59139b69e4d7d38b2e3068d37c13f6752c959167621e0501c7de4e71602dcb9293497c81a97353f4711d398d4564e9f3d409adc67c3bf3aeb407ad6c113065a0bd1d730871cecac49013b7e67d4df1f3ca8c2f4d594708a01bc577be6f92dde9900fddc29c69959faacf92f66fb7791e7e9b35efc903f61562b379ef1e5905d135a27676ba6295773afde05c594d0cc7bd0c4e89f28f8553886230a9a0b89cb400aa0b9b14368751fb8f5fa31e64df7d9493be4c5b0ab170e78056350b9eee1ad1fec93a06b6e70277987e5608d0b177293918bbdd2214d6df4aabc90173e3fe4400cff5022851b91499d0c8e08d16cc405cff7a89c3cb5c587e5baab92bdefb5067529ed4fc57f8509107a4a16147c0188365680c2051d96ac578c4ee078fc370e92ceee30ebfe69de42fb02f891517d258b225d85d39137b49a4bf3e7e288b074e3be9cf9141a3097b23ab78871fbd3c377581dc485499b87e621ec447083b4e904f1957905948bb365bb897e80941c5d4bdf01e5ab0e23e5fba0ec73fd3367a8125fd32cd3f3d73bb393b4f56baf2b5d21f5f49ea481f1c9ea9b46b606c125a197890d1eb22d2daca2ad1e208b101c1a926c89b0ec9a82e1b2664bed43454efa2f033dba02d83090a48a1bb3039a071508a76c69e5e1f7745f5848e079fa0208a6f6e0a0fae650e87b31fa5fecb5fc0429cbf5749e04aaf07342b2b0dfd4e1fbbbaef978ca9d0a90f2dca28207f9ae82cee02800c84f2839e9674bc0ccc06f6c181369a880d29993ea97e0de217850de5c87e6dc1b5b26ca34f8ac6502a46e0f2e579341068c1b61982bd6abedde6eff249ca145bdb3e89a2bcf50de6f3b0fcec8769584d92dbdb6d7ea29a6d4426697a94f2b6d986da402102a7feda4833cc9b867e1e442c32bee2daa7921769004dc5bf6e8889b0eda429aacc7168489de7fb73cb1c27db160303afc5752d6bdc0c6b75e434029d0e090f8471e0acd832fd3241e2f5232c38f77cdecdf0bbd9df13eeebe7bf98e7f61ee980b0fc0112372ac89173b855e74f7a412ba34fef55b66bea7fd3e6a832568895069d0f61ef8e3296015ed037861ab2fad6dc208e24df78ba266ed3cf81baf184df1a84195682398193c28a2bc2bce27b7499a40ceb54e43feb5816a89694b2cbce27146d15431ab801a4a773e3ef28730eea062acbde2c58a12697c1acd515471b5d27700db1d8502e0a221ed9e6131f7665e19bced99043e1167882f316598e2513d6880b8817894cc1e909d754db895e0982ee01eedab46a7c49dd1c1c63164065b546aaf239f08134b019317f4a6b633c2f7f192157c1da546ac6ba4fdfe6296d7f10efb26d6d4287f5622079f18ca494ad62bd174622c5fa5c3cb0635fbdf2964863f87df252d1f46a7ce7d23d0e021d403238a89b1728c9bb6186ccabb67fb5345004ef27b2080a062a02df88eae5cd53d73e860e6e847723819017bfda0643c65754a1e0ee3288dc7a9112e0a4aa85d467f40ef209939865b7dbc692bb31754bf4fe081a6bd6758d507b595ce270930718f41228edb9ef0fc51b2cc086f050ac5410a80493ffbf1807feebdd704c37467afae69f86c9f9898e8e8d19692fa5557e2cc79c3c06f18f8b293d41c65a05f615445cd5f67e9db815219488cd688ebf4c5a07dc378e0b8c6612ff0ea2d4c41735b544ee95898d9182ecb9f0cce94301924e3afb40133094120153166855098ffcf68023dce495d4633fb539dd288f2c7daf935fc269e4bf7b150945677da335dcbdde47f29faba3c28eddde5a2d11ef0f6515bc1640de3909674e564ccbac17ff7dbbe13b2525d8f8108f4913cf4a47a5b7691ce25c358a52294a50a0d4b57d92a930f839074c61046f0bd9cf89b0727e94c70eacc73bf5cf7572c18108e6a898ea7e12d07455609670132d54d5adb42f9f3b3f308c9d8253b7f67380f6adf4cab7c4f13280bae0fa42e95302ff9ad8c8df2901ae8d26110c6433de19e7cfa81ce717be22e7b58fda4fef3a1eeeb53fcee41fb0c1dfac03a382d3085f15113f40c0b71bb72ad4e41fd8b2f85dd94faef115b075fdd86c7a721743b59ddb5017dfbb02103da20df2a7ab8357efca067194a5366c713de4dfcefd7a9dd19c0766996c004f7b987406724e7beaba0466c878341443516640adaeb03fe7b8bb1b4acb65197ac539bb1236ff64401a84d3ba281c904bfbf3ab545b3ed5005de68f9a962c2b99662328fb34c7259f4670784343740f6c2a2351f04a9f5c2d2710c6a79a4e509dae2428e51f8311bec210bb1a13264add692bb1d12caea7dd8adb168353edf3e88b69dfc3172947644ea91d3be5520fb1f2e57f93261ead1472ff0a15d63bfb930541e177be2ad0fbd9847b63c633c36d3fcb21e96d8ad383c43065ac6ec05589ed741546cd17c5a20d999b0bb60cac655133bcb76e9a1e93f03b2a60b73f4a99768ad3b83a33d520dc25a78cf76d994c9ac9d7e0b220e352b73a874f7cf9ff2ce11d822da0237e493f27f462d54b7f71cd3c6923912cdccb6862324d37bce408937b44712777a0c6220d7c40366955f23226e21d037124fd1a32739616558c7844903e43570f217ab598fc268a582893a2786448d3109ed9b0a3f775c1520a0eda8c005463a084a765b00043af6c6f82f9b1da5cddd365353754017af0d1f0dd780448565d56fe553c30fe43126ac6d61c1d3f91da0c9f2634f183d931ba5b499bd8307209f44da1349b30c9294f2b01763a05f0281235e86250db9d9d2afbaf0872f339cefbc890cc08a2f8ab656e017e05ac9e99ac4b08b691441a4fbcdb702793a374292159f47e7abb8243cfc841f85c76890c58ea6f6209b07091812ab50b0b01707ee7cc7fafeb50ec569b159df7163b13e94d0512115ad6c3b175fdd6d3f9f971ab8461fb98e5132033a6eb88b48d257445a7b1f49eb2552f819ba4acc2118606500510f3f15044cef36dc494e4a9ec9010792aee04a1bc3c788b1c0c9770a506e9d228a85fd9e1fd70d85e1f4fc44f1e2434600bb076ec9f24aad16d796e5086c454bb86291c1aa44911c6513d92f14900908402947eba4c9a959cd2e45afeff6af49898bce1c61fe4cec130b13162f445eb5957786460e184edfb29785ee8be2d9ff83dfec5224db5de29b155a9f1d1cd43c350a61f169f81f98d3c88ed89b30477c5fed54b66e2d40d9c272554bafe022c1ff771add5ae26bff061ad1b6c598f8d8d73454804506c49b332ad0ef45f5a480470da4aff01f0fc20016ac18214306d8ddc474920e620a3d2286ea52f24bfead6d7c196104e4a69d92c24366eefa51a7a37c0aeb742df3cac99d875327165194f4441d47baf3d8c97a74bc231d7f86782251bd634bb53441290459b39c9de21d56c26312b0c53cd49d207aa158efce9c652223d0339d53651bc19d50a8e67dbbebb4cbbe6c5f46f9a3105fa3f1b75f208837a4b06f27865f93f78caf8d8d2eb69b635efc4def58e64eca4b98687a14f9e20f2574c9d6fbb747556a724c6931b244d32300a649e172e8733c3e52c427ceaf611fbe05bd34c997da3315fd0689cb72a024f6ab798cb295e613954fc5017439d2108beb984a1f14ab782cd59c8b5066ce84fdf3ef0dc2bc298ce55a75d446994acbfb95df6324f5adfc26ad304e7b0d13406301e5e9c653ae8b1ab08f76edf70e503527c3a04381f8e5f35409ba16f253a8e20412d6688384beaa982152cee07e46169fdfe3f153777caa7f6d3477cfb45121f396388aa863379158b9b9d5ce5ef0524b4573f622a92a3d92bfa0270e2df3eaf6296488e1470916ca660fac1dd4958c4131807bd4174c1ff626486e58475ff7a5401eb8bae4626a6f1db8dc18d0752c02b3918256df914c2e80766dca8ebe9a36fff7ba8ba8e6e7afe5228ca41701c46bc34a29b9933e08bf708e127ce0c7acf3226db553d12b0258f171ed087e00ed751396db51cc08acfc3cd8d75425630cfe0f5b924841983a28f4313e9a4b0731d98be0221b10acd2b2dfae26e1a164f6ed5f091aef4f98acb95481e840ebc396480cd3deca070b9f5859070746e90dba328ba9fcd7380f76e305a2b1b012c9bfee2c913cc582f959c83e1de2f98febfafa5395e4add9e5daf0f07e67190d4b34904baa1f3879d7fed244f623f4c64a4593bbc789f01f82a975b6a8f5dba7bdca655fd57b44a5261fdf3f1d4dc9bd0555f6eefae01b325df7141a45cb0aa9775c687c844a483a299cefe195a93379fcc9b72566123afa2416a5d8c23e30a763c10b141b8e336b31245589068b56cdc72e3e383c3a5b8cba0905fd4c605aa52e90ffe18265d5d4b846ddb7f78d924e49f8b7b489779b168469b0dff470c1d5e95ae8055f4b19ee4f6052bcd2c2fa365d88802af319b8025065eaa2100a4c76ff699571335d410ba5ae011fc1caea7bebb8738e9c3cd0ce15f626914596b58d7ce2d4c6c57ef3d4e1229ea2a716096b9f5c0590883d91a813aaf21b6d829accdc4a322cba7ef971f34608225fac069d53ff2d7f8111ace75f2c0ffe44f4eac2fa3c9e7063238940350379bb9c0a39c5ce1dc1399697914eb7feff4a87566bdaaa1599932d66611ef374defa79fe8af49a40eecc173f7656da143fa9691b3c8d1d1fc0362dc9342217598684e59d97a1ebc0f84355ff305ca0fb9f692d5dcd2534dd45b423ba7c7be1c97fc3b86ada8eb2d1dc59f21de0fc4cfb62df4df6cc9d4048f2580335395465d69276cfafd38538b8d88e88a4676f7918cf87f82bead9ea443f5d06a93af58936c9f86bdb86c1c9ae95c55c2222acfd4411433636745a3e26d947a0ba86ceee450b0da81f433c5207b7f41a2076a249b64e2990082ef8eda8f6323fa4dd3896cc81d8a8fd4ae37168a9cea7655c47ebe501d669815f521f9a8e7524e99fce0b4212801d81ab3927c84f6acd8b3200160236c395678a5fca4557c8bfa7fa353478ca18f479beb608f4b32654f0dbd2e45caef71031c3e8bcf13604a4914119f590fcc2026bfc5a112936e32a20ba845d3b394f9e54a7ac502a180a9e48036ad453e7c7b37a3130d672a1bb50e652d9ec90d26505638aa6b3d36a5a81e5189c05b2a19e0bca6a945693d12a73ad9bd196dda988a89c383543ea6d0c392c5fec6f5f0205eaec5afd5b7b483319a574048bd16e4068f8a03f60cdba3aa015a9d57fbd278335adfb8b445c75c08ef6281c7962dbe4202ad0dd5499d3e80bf239fff5068c092d2ecb3b63a2c9ea981a3b1b7cf35d1d89762cb54f22525bdca5a91cac05e51b15b0281c9cae543906b3c87cd5c2ac580e9d872c29c19ff3153749ddabd3967089e552d55b1a907741ac44788a37e66e8d16b20eba5350ca5ee2f7e124810210eb3b84793a1229b93dfe0c807c4851122cda33b58c725481c152cdb65afaea693b75d0dffc1fb2b525088b4eac6340ac7a566b4d9aeed930aaa7ca436107784aa28c4c78a87a86ec535a2ee9306e01f5f41f665f6be400b0b0bec8333aa901c4f9e67400e5796a4dc9a83f393c27597353cbcbe77a7556599064fc6616a45f6bfd4e44a4fff04bdc75778a28f64def8d81058ae3ef69a9f0a52a6d7da4f45a8b45f2eb870edfa355f89febf7e2b0a153402fb12a830278e532dfa90b9eb040a4f7eed97da8a7a7384255ab3ab34dab9a84f4277ecd0c43866a86b0124645100211781dbc50bb291b619bb802c772922ea2e33fd1579947de7e43ed94495f56ab7cc2eb28cd5ae63ca5202bce6935ec4fc754554af9dce416a79d53dcb4037dd01d19b17515236f63e78170a4281df0855cc84fe1e0274de093142e86957c188e67b8edc05dbb6ba8fd1c297bece020c23532c17d7907b51980f3a031593a8af787a9c1e237e7ffc221a584d43b8df1d4a783afa5a8bce8fa4a3804d66deff93664fee33dd27da39140525ff241b27410886aa83bd65994d6344ed4920932f9bc51d42fd1943b82a424aeacee2eedda8610120b61daa5fc5d545c469a71ebbe8e3d91644808883d43560f52fbef3da7c4ac0890b58a318e2745700c571fed553db9fea5b5f5433018aa4daf29484c72343fc8b2ccb517cb3a4c355edbaca37460e9b0ad8283044152c97d2553e185889be2bbaa35b41dc4ad71f9fe78d6704062813321b609fb4186ca769066271395742e5a16f92aaeecf7c8e354676736caba03cb91f3c9bbd905266a58498eabf474346bfbaa1c391b6bfe0f0fdb69ebb5a99d695daf1b5b0cf1f958c1af9d486e1dddbd54a876ed7630481e79cd9f7d9d557e245c24ea33549bf689dc19a4ef0c9254147e0eabca136ef850c56712fe601a5a2b44303b312961f62b93be830349c08268c775c62f543cbc4f9ec7126c3d3e975f8edd4bd44713c44fe07f13ddd274bf054b19c646f5f1e2fc7267f62b0f61d04c100b89eb4f5b1010fdfeaf1677aab2838675162166a06eca58eee6627f2d0c093e63e2ac2fa45694878dfec08c0c821e4d0c7050c93ff14317b20241a34c2d563408a8f0538a6335dfc4491ad40564d653805e2e167177227a2952bf99c6c053c4d638e786904fc72c1f8b093fd0bd9a28aa473396cc897c331981732406e72ceda5da672368190616f241e7f5a5e65e686950b0b7d1304cb47c2b9d9318cbfcf869ec96398be7bf08400796f3f17d842ad18b8994a2fbf1b4b87ec7471dfb69ab5396d16829f8c3825d4d4971b5c8e4db15163ff733dd3b120a1dbc51f02f7ae3dcf8363d2dd4795347a374f6e140dd187d651de85b396acd552aa999ecbdd206828fca90001257cc8348348cb8b3ab171c78333790019cafbcebacbbd73da3a276198011dbff546674abd956ecba069f076808b2f1be1c19208cc2fc91b5d505c07489370037e5b501f17e5a38c4dd53048a129d44cfddce8aecc7b6904fb3c440fbe80277a6f80f0684f388c68c411dfe844f54a905aa974d9c31e172600858877bffcdce1091ef064c83f00573701e9e2695dd821269d40720d8431484bd70756c2d0ddfd1fd2425dcec0e9c31730c83c3c8f59a94ba627de4f8c5f31235809f43caf95823563316833bf464965e2959d34f116ae374f6e3d8d47164fa6a3e8e023e0247e713a258e6c7a86b2f990bdc073a17c2707574e44989d7879b502aca482f1322678fb47d65afaa0bafaf19875fa458628a54647959cc9d54cf42c9157a4d738d74adb2ca17bc8811efce3ac94b5e69df82dc5168251051ddf3e4d44259fa6eac3f12ee4370f4113c1b3ba5c9625e430c0f442857bb165c0c6760ef2bccdc28857c78bc81f8fd342ad4ccc3b72cc60b9737b9d59a6b017da5509b61a19f454ed4c7cf7744f68135246bd2ba87f0c42b8a1ec76d28fd131b0afe98b03c289e1dbcb0f63615c63ef82f508a74998bd335719fe819a33047ec7e802111fadac2314c7ad494d3b801a6d24f44c057a0e2528c6d73ea1bcc78b1283a6bf7ad25d5b464d6280a3dce99648e3c72269d6f1988ae4368118d88631c056937d40edda8848ee0522591aaaa699447c5460730d31d90fa627b6d27b4060c9fb8d8c18e5d21d4d05c832898459e276b74a66f1ac78bcb044f6d1e19fac84805a61c426a28f53645bb10e0675425681e167095ebd2dd9d3c6d7ab9fd7fc58d1e85fab4900e68acafce76243f30dc173fb5ac8307a086322c5d00afc24fc4abaa52704ff0fb464fbc734175e828435f4c85fefd2083f5e14ed77d23c172e364c3efefff0291af8526841c0de946be820564d53668249e8f16ccacb79092d2d2b65c5c2d7d8f047b8495770fbb476299543af91af40fe0ef94805fe95cfd634327d10f7f6f4a80cd4a85660dade983deaa5d59c4542eb4ba56c1c165c307cb6dcd912de8bfa48944ba262f480573451bb72a855bb3497afc75bbd5ea0200bc0849d738572fb25f992401d1725d4906313492e50420ac8bc8f4df8a7966f421d6ae78a345e43c3d1aa1ca3eaa658540d3cf2b6a74d0c5c5ae9cce7a2bc632a0a5331d0c35bf2fdd4a0f31d6dcd6f74ff2700a7bd5fd3cfa373476a1bfc3de1e55ac7d8c3efd316a6156190d2c650b12739f9107233f6aefac5418ae9af0b37ce5505853d4bf8ed2e797c74f8154ef06103989948aa86da25f7b0b6b9c43d0b2eb9b3b07a3d3e9d714f660c29b9d53588611d32e63b289e37e5a58ea0275d156a4260cdfff1a28de93c2f12453dd8490b20ad88851d09905b799b159e4a47397627bf4cb6a98eeb173e5c27e6a0f39d878ad03092aef8c889495768524f8f24edf780e09a3e4422c2684113efd7f407498736ced34df9c30faf010d7fceb2b9d8eb2b9a89fd8b30251fab8a7d3ea86e30d474fed46abbf2dc97c6fb35861ad4ba35d9970225861670a0d5d5d45271dfa538c202cb007831ee0d5b8e74dca616176d1bf1ae61d1d0732abaf38bc4aea8fe4cbeb471b2f82153b524fadc1647696095abd5f29e40318252e38e4008608bf227393afa643e971e4b8742e8787e86e92750cb2734990713ee492ecd976e01fa6165a368fea822c8631b8d0307124c38b42ea4660a1a8468918d6141671625a4561f9105a1b5bc7e09aa747e29888b22095e243f380aa1d3eb27e3e9090ba26a78669827ca839784c16e5d18c305626f96a8df42bf782c24c30ddd167c1e69f3f4bdd73c7872ca05970234151774a88eea8fc6bf6289a2d4286620f4a285fb364a4ae6697ba5272672aa3731ab74776fba4739cfabb62b156504980e92660cb2994920c584caf2e68c5903735117e7e8de717c10fbaecc6fcdb206a60ee1afe0a6e29733321ca7b45127b0c631d5456a3d116da6c161aea8b080f36da0b39af7400fbc2ef27bb39ad01bcfca2f1fa879ba7a8d2dd164a1444579507c308be21a360ea0bac833af5c1d7f5c38f206bbfaafbf02c3cced079175fd9a0bb0c8b2cb34d9a0b2982c7ca1bfcf6d334ff249fb469b20edd535f4da2800aa833550f03f1d237085793d4f8846f9bc2c744465c608cf79a0d01e98d044f44af866de0531eb4fdcfe9931516f7dfbaa15081bf1dded46b7c08620e74b7b58b179844a6e10d414e317dfbae98efcb381f2f07d6ea0884a8cee60d3be03e695218b71474cfb962495fc0461f1783ea1b252a05046c06c97d6c0e8c0983def442e1514c33bba4780b040e7ad63de876fbb049e85c49aa1dce8645f3ab168713b3bddcbd4a4286efb89d9323f8110ff9a30aa972d2663c65dbb03ab473bf8e59c5cf350a32c636fc0b9792341f37a593dd4406bb11b8a919d3f81a049f38bae9a2ec6b7e6c32a46da0ba8b01bb713ae29bd783d1f4e893d1afe8b8a93abca293662d6f116e09644c602798b9593cab3f534925537e65e92f3380106c89887d7bd64743d045b8523505bd06450c89e2d3040eaf55ec94b75d34aa433997edb3433d557204ecf5dcf881f1d25e41b87e9fad201eed2a2af83adcf435c3614c5bb2bad48b9b59bc91096618a497985696cb777f2b3949dfdce819245d969b58addb2a024bc68b9a414dffef3e340bc5d6928114d08ee5c3fd8a7408b5bf1af0672618a13b51a302bb804aaa4b2934cb3d83e1ba5dc867eeb8205cbc7e5a67d823cd48e5a4a30c0366d94529ea1d4f49522f32fe2e60347ad5c2fc1e7faa8026cbfebe9b34be54d495efe436cbe8f55746459898e911e93cd750e6cb652a61a99d3568e253cb077659be1f5768731513dfab0dffc4f6b6a223e81b5769927c960bd43b87748958b849fbb71292b64fa5039fd8d4b16a61d1233730c8fb3879946712db99bb028a35cc3133b829c1326fb260f0253dfdf7e4735162e3aa44b3acf299ea0298dc355d69c3c8898858e5797e5aadca0c7e3f871c95e5c5ef6307f18527dd80cf7c6d541a6d85925c19644db44a08edc43e1dcfd13916ed115f6bacffd6e7792ab142de207bf65fb61d36c335911c252ff84468d03541abf6d97a630a9daa4576e71720e9fe6bbe9578c752ed7e919ce7136739ce34b5c1010b9e3b584e632c424faec765a1b39182fdfc4d449e46eca408ec3476102e9426582afed019783addf6d1eb302cbfef2919975c7b47b87dafe2ea50e4d3a9fce85bc1e1cbecd4dcad3fa2a91b50c473a1244fe991ff0dba24adafd561754e1a202625b6452123f49284090bc62952c0331096f8055a17de627b6d436f93e57f3a46f9c3ff411d0692a99407898085c623cf693a8cb4069e325785514dae0ecc6527184471e2248cee90f9914621d1db12324bca65fdcabe3642407feeee34fc00f7f75006f7a558e4edadeebe5f25f95e1730485c07219ff8cacc013e5f5c610aca65945c1dbf4c01ee453d844225dee37e3748d8c05c78065763ffc4c2c2e9f9babec5d6d8dab2d34e1c03da6914f22d21f57308e1d5f581effee6f73e87588058ffe288e7e6effcfb89217a15c9f7151e50e613739ec0c13ab237bc800c1b8b288105d7482b50b99f692fd35f0f54ca221ad6d82cede5807645a053ac19b0a6846bc3cd17ec7e433fef678cb94c1ed7ea3abd8b764f2ab6c1418d20ee72e4f0ea863416cd114dbf7f774c9d49362720215bdebd430e6660f53c55a6bdb2e202f28bee7038542ee506abd40e83577beadc95adab53245120b5cb3515e399b867e78cbbe4c25f8c2c53dc64709437f11bde8b053ee00a403cb36bc62743e933bec2e426d51a66687263c86620484c56500096106941eb9ec427094c14056f4039cc54ae64ac67c7e740d64d76b778d49bf117a777f49d99693f2259b661b0cc840483745fc9940b48cef032f7fed4f0c812a0d90d7126dc8c8596ae5690ecea53c5d132ee12b06608897e245c2c5e1b89e51ef003c537949857d1783c4765615ad6dbb98e9272d15451582040abb117e7046cfc198f6f6886c583e96d2e97b6bb9be5630ba7eb3f4fef100b163f01bf96ca7124e5c5fb3e9a29d241e34cb47a64f7ad78123edefcf5a297e2d5c45acd5add97c97332c3d62e0029b01627d6e4f37db1a8a961bf06de155129059e4dcc0568360d39558b5d7cdc12b6d71715c752ca7a2c55bca91e1588b2df61acfeb844a439af2a5e172b04a76903c512652f5208acc00dbcfe6bdf1420d352a6aa229c60c9819920bb9de360517d815fef964f09b54e1fe89ec28214db42bf25547ae2ff9833fd98f43845f8f999403143097386343b2e63d79fabc802bd838d305e5a2885c0caee88fc5f75b7d56d65ffbb1d037c67103d100f2f17fbc1b41615d24b733b1322ea25b4323490f8c06ae7b8308f5f978331e376eb4a9f08b30f1077895738f9c241012f3cfda1c3f788745203fc633efcf3ed9c605c2739ef50bdef39e0627da10c59b3f35d416710be5e99b15ef48d4f375fa2b538ba2bf07bbd607b56343597831156b7fac42ded4ed26a342245639b2f46a36b73565559763f6d7b0f6069a7daa790fb06e5c8f3a4bf12aa4d48c5a470430801db71540c964d3f75b691642d62190c3dd0bdf470c7f0dd8e88c00a43d938bdcbb640a62464956a9e66a711dc62c3abdf654b26201006ea1f8f864b2a3a3e8c0962c63ee28a4eaffbd1f020011c709b23e978247299764b75d6e7b55396211b1d43abac5723c990f7bbb49aebe73732951efb3f43dc7513b192a00e0f8c031587301c7a42d2e028c9d51f4ae272afb50cb1491427a8b85a3ba4ceca5fd374f58f25a7ab4efcc72ea73c0461cc93050db447deb740aa15dc4ca7f4a67fd7289c2ea7c3d90ae770f78ddc2c63fb192d86a327f3da42eb0a084c3dadca75ba5c4b9c1513561ec91c824c5797eed6b2ba0d36a06b89244b41be2da8348f7a6ef2ef3e38706372b5e5b61ab1c27cdbd59d764956eb53e0c6f4e75b082686df62c1718ed535ee61e4ab06abc8077a2aadc4f201e705a32a3c9b1857a0a6193a7b66c338b719d7a0c86d6774940a20b61b65d5d98e568b230373a1cf286082744c89b36e24fc994ddfc8dcfadb650b1f809856790b1edf4cec5491e8e6ac235fb82a2fb82791860b8582508820897e1e371058195e1408db92f4724b630e625ea370231e88f1bcc3368e0a7147e152837d07cdd2169e90243cb1adf199a76edd360dac633bb10096966f9c79debf6b489378444484ad535d4c428d8bd625ab5cb4f3139b3a91e1b2ec86516905414cef41d257dc2728e567ac6974c4dc183b6a6049a3f7cf3da9c37cf4f4186006eadbf73aaab30664c2f85c84827149175cc79b656989165a096a1c8663e4490f1aa611f1bbd49eb13053909fd9e1f797a086a5aedd4e94bacd9929247c21ebe629ab4a7b05ff9e5bc1b5988dd03dab43ecad702ea11a4aab40bea81fdafb0e0310718b8f02f3b5b94679b5fa247eb76a6cf2e26d732f76ac70a1a0d8c42e22e665d89806faab7750282ca099bba57f8ac0d3535c2c6a33d34331b7d89074d3e38b498da632acfd79482e2662e646212182d22f3af5bf0dc7899e2a9e4f7f003ddd7f000e9eaf52d94206905b9ade205b2faff9d57b632b117c7dc284d9b7841158a1dc3d67cf3bc1e683eab65c0b826831b4db7a42a1c528dcc83f5d3673f95354bcb8d2ecb5c35a3bf7fc4e155d71e6c86678ca73329b52c7c59b8ef753cf87aa864268b9836e62235517a8ec2b9c94503eb1fb44d7862da66c87c490250806d3d8ac31360a6f18d64f7f91180699f83c98e9a7f3cbed0071458058ca0fed73f30ec290f0be4127ff12af6a892467a199ba077769862da34e80e09f43097f4b895aa37f8ae4646402acd603ee9c3deb77ddb4e7ef05277a66594e11b44320b5411343762274accf853c5c86bca4753dbdaeb224017c1662350d2a6949037251e4c21162208e8ddc39977339dbed9598e04db1673ecbf9a3be1de6dc2d6667ffad6d8d063321568b96509f1ceb59e810f41f15ad1bd4fe0b5f27a4ac80313a0836d884eb923516f6e70902111f5e4800192ac5ecf03ad3c53104f606e6f61597452cf7b1dfc877fe478fa9f6603e2d82990ed9efac7c4a3123ac3983e9da828bc2721348975b07a4929409b8b4307a6985bbaea74aff2a98c0045776c5607278dc1d5947ad789ce5a93276a6f0328cafee3305c08fa20334225086fc2a58b61fb675caade6156226eff1684495f693e91a4cc74afb340e7498017e0df273118702de754bdeeac84283436e440a583f9f04ef3ac5c579f8b546844fc25a5b416866f61635e703c621d3869f2bc25f4b6f5fdf400b7622bf33da63887721d141803142554a687b7cd9f000db4e42fcd2c54a06d70ae4a2b09a788fbb666d3bf912f89b435b35c88ac23a07abb287b8bf51bc220d7940aeec8d23c0d6a8b33bdac6ebb2b64ad2b6d98e9cdcc9689ddad4f6a34d9bb0ef85630c43f4eec4589c3e84fdcc729ae824ec9cfc3c1fe9d7c646661a5cba684dffdf2e952a9cd7518d7383a64cbb91f3b1f8a6b1b7aa555b0aa5decb6f769fd92db3ce7e18eeb0b78adb3b466a0dabe83518cf3624cf2e925c5ec386b73f1a42f19fbc8490480835fcd25868f139cc10f50628016a3eafe753edc181c4cc92d8a6ade86a41f151265912da9aa2dc8d8ad3e6ee6f9331315b612f4edb5e63ea3a2b02c576ab9dd1694a3341da6ddd17b1220d90e14f2b7148e99f349b17b6e62d1bbce85a81db71db3979c2eb467472a011ba6ef2db85ad0835731adfd0ac986ad055d979ead295c1d2bb064bac818da151d8006a081a77a589c8fb8c87f60f423a01d9170ce11f3800a523ce8bcb48a484bea822845e334d54848b604386ba1ac447952d541ef0d8e53f7e7d726308fcbb4ff3a222efb2df2f5a73feadefe0f89627fa61fe7a70ba379befcca43e6fa3768b92d6af0f292b6946f93201efb4ec4462fe9e3684e5b7eb816d079e8429d64e15dc73e7bcd3fe9c495f3e40d8b56c99d8061faf20b9dc5255910f635735b2a7546aea73f7d1b5840a1d8a8b5027260e00c605bd7ee41127d889d04d4c6a872b8671b96d30947827923b8346855f5f64ec8f134e906c974a31bccc8d319120a7cb3f2c114073a6062f5d37086ebd183bfafe968416039c2548c6e7921a25ae51233c03f4cb7221c105f7f1bc81a9f891e7941697a4a959cf3dab6959fbec288efbcd55d9bd152e66c0b5c6ccc4c630ed558419d3818c33e20646046800455f6f26d160ff37d2dfc1b57b8365cf2684d061b5ddd47916e631ea63f77c1444a4d6ec3286ad2809184e8bf88f47bdb3b1e9803d194152eca210fab3f3e5ad3e4099c879085573280e26a55b974176e1d575ab69a813f3798cbc005e0fcf46eacafc36b7b9c892b541259f47875b28f9932b250aed71f18712ba3eeb63b2080f2786994a46314f9c398390d480c8fcae494324b9825e3648a70e6d7280ba76394c0db22d8d6e0b20ebb7a4dc0e87c763b7ba2451ae6f592f2f5fb6dfa0206df2c8a22a86bcd56d49393eca0bcd8b5dc1f88ca813e85da30574cf5ddddf7d35e6326e0254aa08c034a7d87bf954e655f07844fce3cd25fdfcb1ab2fb05b8cdee07c136966e2eb29ff2dabe71833ad45c0a65e07592008c8164b089f897e73dfaa167f50806a43b1a338979c87c2796fc9dcb624ba6b4b7849425c6f732d03b39123d17701b956bf936c41007a3c9539c4592c51ff6fc6fe4063c5e356d584a4fd063b787f89a750075edb08a14e1992e2995b7e8bb038ad2a5fc07d57ce556697425a58062685d2a60a6d2b28314903c0e681cd160a3d78b83161e4b8f491771141d3fb61f8f44f0fa63e769d44b9e1b496b00b4bdf8d877e6d6f5eb14890da06a23360fb8c782d158587ce8a212fba267560d47f7ea7bf654a259900f594b9df88c227894413ab0704361e64e46087bd4b1caaca8c26222583d249c9db16a83af96d020176ae3404e18102867d8f200088cbbfe8b9bb3ad7b8bd07b9e2cfa7835f1282a6a811e3a4b101fa19795da2d6aa90ea26176dfbc058972a2caf01e85aa899aeaf75330688810758cff3955de5702ffe06db36afb6e783cf001ae3f4cc4e989eb6c140b04993890a1bcf673456dfbc20484052c599c01643f4b45c2011bec90ac914596b3bb30401e7011713fcf06f304370c695ce40b3bd708ff070140d996bc8e49573dcaaa830421099abec4f68a60d804ef225ee11d2aa763314c9c8ad2bd4770227d4432f270f27ffb4c754364dad229fed3ff73dd8edef7d3bbfe99fb7ef9300b1056359fd0a53901ac441e700a2eeb20d2b8d533c69a3a20900d774c351b7722a864adaf08dc34ef55b31c65d5000488c9d63d61a25e3307eef6a910cef0897a72b86fb32b7e48de5e2433cf34148f2539dfc28fbfa4f2d24bfc95f1a4e2d8b5973525fb6dceb5be75fda1308af4bc80637e6fad7aad2f9dacef6a9e6f06dec6e1b9a10465f7725223cc1af85ccb25ba753c475ccd42e0a57a5863eaf4b533aedae71b5a88492e23b0f133cf1e336aa434ad18ef81a798abc9729cb6ec83870ae5ce73b59d5af2b17122d72a4810887786f64d653cbf83e142fb158e3a8ba8e7094b9a2ab08c45415f133ca4f6c2b9d48b35b4c9998e9c78dadc5427b816dac32f6b4534d68bb97ecfad17359bb800e8dd75028a82469b3a795dd6546f546961dc30f6c43ad793f5e0acac049c38a17247b8aeaa9637728214fc802db346191b3f9959cc6f2158043cbed86bbd67661daadefb6bc73e9e0ad184e9a58688f7e3b4d3af6a327fab967e2adba8f2135a8c98fa21b3481b9af20ebc9056bb6ea9fdf0d1f66ccb973699821248041968bc4f84fe036fc45e871ecc56bd2abe3fca1fb169c3580f05017d26502cf1ee30935842759889ad626c8f6405abdd835cf3c9bb920f778abea3e9a9e9495a458b8e0a0f17c5b0cb13e9b86530db9f62e3edcd707728654c5b8c81f86f842d0c8f9beba17e451192c5e82aa09cef4f29ab30a43946fae10ba5f1ce59589a731e841017fb48e19bfd700e6d96bf9d6c8e1d8e590a8f1dbe6a71454bcbf67757e3f15a556ca0ba7ad86a91f893087607934da4d3341975e6f86a289753a35940c5e7bac552cae4e46c82f9134ab7d00dd24b2aabd7150a1379424f72809f3d08edd1a559e84420e0b893f9dd61055233ee1de88352cda60879b04c3e9c9e10cf8008b3312314c6b562b5e88de42e71b6a27c7acebec7c91a9f261a2b53093fd1dbff21d5aa17bdb3469d1eefcb5806449e78fc388d4fe8794372852cf2e6cb35f42f2ec228ca42813f1ed95ebe8c0a0e600c7d2eaea596097ac511d8e95d064af4c0eb6944199dd0e51bf1e5a1b502e728d933ccfc71301f379f3243096a1a412698453b5e56f416bae06ab20bde807f40fb5a46c61ddd7710651954e80e9800e6cbd69129cbbe3601f18e688a0ee39a5d19d476678519ca94b608977e52e2fcb7d43c6a07150dc4de63e452f13877d6b6f6648aee9142a1fd5d281c905514ff527b5875bf5bca7cc3381d60b919750cec0656fe2cd7a27c817754ed618472be491b12ca967b74dacda6f3861ffb8fd0b880014b1f10df12afcc6373a893a921d27065ffdf5b758872ed087bc86ecca963f70c79f70e7f99fe281894e5b01c3aa15dc4a1c239337cd1b3c877fdf4dfd340b878c428dfc2083be5c4bda0637b77cb893315d8e7f73ee5a8c7013a9a4a9e0c9a7707072570bb5be1c17eedc7f2d8627d41e5ac82c6df36ce74376ebf1ac55800b9f8688a7c026faa6e57c76eae5856ca194f277c343b8c4b570b492f8bd4356a7110204502c4c79f853e0177d7c6e6dd5e8e2d66727b02309fdc46343c266881efcf995aa3c645f0ea44af121f44dabe18c92d13487f90f1b7b4e05f0709f21bac26592e63255fb7850309f17842c12fa7d558a122bfe33dffda5a7a5b8b0140ae47de7da84d0e745f9d3067c317d935b593f6203df754947e8fbef0950a90edf761e25756eeebdfe9b99174ad6a87621b34198b87e09f8dd939aedd551aa6951e4de5b54bd5d0ae330fba60991f1bd48061cfae47eca4e6980d81df66d7f85f3ae2eb80be0379d2a3aadb374fb19f427d031e1edb02603ce9f4f061b416c1a96df615639b344c8c844727fb0d5c24cdac6abbcf0e060fea30728aa357655e98253d413413e318099f64098b1a5994a112271398bccf9f4482f0cbf5bcecafff9bf25dfe9c8fbb3d17ab6b2d8554081921e994f14daf0f0321595bab9f2373055ab53ac76d9ed2c4fc1088787b4da0fc2d0719268ac54cedf91988d9102368aa2e2f5978d0c42b24e01c3cb1965edaae4b0ee4a3535a99961728cf2afa06e0f226f4ddd7e3a42ac275aad10099a446aafa2f08e3c3fa58a6a8ca44787e26f74f27cabe6e082ef7bb45451f5d144bdd955e6a17da1ca02ecc6a293e91a30ba6227196e38f41b358f3a878715c0e589199c5be7fd60fb3e9d98b277e3fd66734d175e8ed1840956732d56a149cc3145cc3e028a54f6e50e2e2284faf3f0b529245aff117d367b15a933dc46779e55946782701352c19e23174035d9ec841a456c1e0d787293a30707e7c5c2b5933f36179f396b575054e65243ae3f7b93441596d3e4ec2b31a0bfbde47c853837a8e92a09aac11f3eb57f12be732de18fb81d4169080313005c63dff74a7e6da8c4cfddf92222ee1c304e6d6034eefa06d264c6c6d4ecff799cdaffe46afce6e18471bc425257ab516312eca12ad4f200abe775fd46a0de2ff5396837554a3297e1859dfdc52595507da6a3847cb6bf7034d4a4b2446ee5a4a230d00d3426ff9e5250eb74548eacc5400d7f72ff42c06028d675422da2047fbce5ab558321e2050ce51190a6404eba1c7a28eb8c5993fb3feeb6ef5a14d2632ab86ccdca8ee63f014b39c014213cf1d6a4ab38f9fa39d6ea0ac9f9179799efe02eeed1cc5e2e965cc47b3cfc1b2c6866f0b44f462e54745e94194b84f2ccc76739b1697fe013425b2b83504d3c7711fb1c7aa689078dd88602ef571cab76f72c6889351fb816713ec0822717a5fe4c2c496c569e6faee4c2b1c58eb46bdae092d052fb39843038bb6cd7466aced35ff5c56ebc95234806accf8f4a794b2c773a09fe1f0a6cd3e3255736317407f3312cf837df82911138d58470467e907ba1e4aed728d9b476d19e8c8d8bf8f639062e65695bea31a3da45e91a40ad0f4f779771a1b683271f7a7229209af23dea421e0a9d5994d98b4e8e96aafaedb3e9e20bd203671a388579e3df965bb6063286d64b6821bd580fb18f3f322aa5ecdfb8807a6493f2d1ada5eb70af9e8c8a25f01fadbbc3a03d368837777dc162ae6d69df2017c12ca9f97bc45ce7e2842a32506c18b68212d2be27a2693b2aaf01065015fd54ddf5c3160b46a9a7d7cfd28d6ef98083cd7e40d24e3dee12794c08b8e561ce8696e5f5ab8c554483ff82e74acc7ff7f5552d83da4de6146b8247f410eb95a0a063389bf6b0e36a388b86affed535d3a374010341f0431a27e385389d722198db0f54e0f13ddeae794d05ac4cd347567b06a352a53e79d38fad3e03e41cbd16222f2583762363ccd9ec6f95449d1b4c19675154adf69f0bda4a54a62d5df414dd92d0eedc4c05ba4144ed203dee841b3ec19448db2d922ea153ba802fdb9b284c98a0fb38a9444b12f5bb017596333e5fcc61276924f86104dabdb35fa3b513e7aa5653c3608d6ce9394e6304f170486cc567f9af24495f8d89f6814bd5bd184c460800e0ffffff1f1500000000000000149a71d46b065cbe8197e21b99a0f46fde03c84421e51f173f7f24990972a0e300000000000000000000000000000000e8000000000000000010a5d400000000fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210b2a76fba82f19d6c5d5cf5d6fb1f0f5f865bdfc040d54e0c1c7b5398a67b5794e75f3927ecedf7da07900de28974877c41062590f85b7138f947c2afb0bbcfa50200000000000000 diff --git a/pallets/wormhole/src/benchmarking.rs b/pallets/wormhole/src/benchmarking.rs index 2564fcb5..2285a190 100644 --- a/pallets/wormhole/src/benchmarking.rs +++ b/pallets/wormhole/src/benchmarking.rs @@ -1,10 +1,14 @@ //! Benchmarking setup for pallet-wormhole +//! +//! Uses a pre-generated proof from proof_from_bins.hex and sets up the storage +//! to match the proof's public inputs. use super::*; use alloc::vec::Vec; +use codec::Decode; use frame_benchmarking::v2::*; -use frame_support::{ensure, traits::fungible::Inspect}; -use frame_system::RawOrigin; +use frame_support::ensure; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use qp_wormhole_circuit::inputs::PublicCircuitInputs; use qp_wormhole_verifier::ProofWithPublicInputs; use qp_zk_circuits_common::circuit::{C, D, F}; @@ -14,12 +18,7 @@ fn get_benchmark_proof() -> Vec { hex::decode(hex_proof.trim()).expect("Failed to decode hex proof") } -#[benchmarks( - where - T: Send + Sync, - T: Config, - BalanceOf: Into<<::Currency as Inspect>::Balance>, -)] +#[benchmarks] mod benchmarks { use super::*; @@ -27,6 +26,7 @@ mod benchmarks { fn verify_wormhole_proof() -> Result<(), BenchmarkError> { let proof_bytes = get_benchmark_proof(); + // Parse the proof to get public inputs let verifier = crate::get_wormhole_verifier() .map_err(|_| BenchmarkError::Stop("Verifier not available"))?; @@ -39,28 +39,43 @@ mod benchmarks { let public_inputs = PublicCircuitInputs::try_from(&proof) .map_err(|_| BenchmarkError::Stop("Invalid public inputs"))?; + // Extract values from public inputs let nullifier_bytes = *public_inputs.nullifier; + let block_number_u32 = public_inputs.block_number; + let block_hash_bytes: [u8; 32] = (*public_inputs.block_hash) + .try_into() + .map_err(|_| BenchmarkError::Stop("Invalid block hash length"))?; + // Ensure nullifier hasn't been used ensure!( !UsedNullifiers::::contains_key(nullifier_bytes), BenchmarkError::Stop("Nullifier already used") ); + // Verify the proof is valid (sanity check) verifier .verify(proof) .map_err(|_| BenchmarkError::Stop("Proof verification failed"))?; - let block_number = frame_system::Pallet::::block_number(); + // Set up storage to match the proof's public inputs: + // Set current block number to be >= proof's block_number + let block_number: BlockNumberFor = block_number_u32.into(); + frame_system::Pallet::::set_block_number(block_number + 1u32.into()); + + // Override block hash to match proof's block_hash + let block_hash = T::Hash::decode(&mut &block_hash_bytes[..]) + .map_err(|_| BenchmarkError::Stop("Failed to decode block hash"))?; + frame_system::BlockHash::::insert(block_number, block_hash); #[extrinsic_call] - verify_wormhole_proof(RawOrigin::None, proof_bytes, block_number); + verify_wormhole_proof(RawOrigin::None, proof_bytes); - Ok(()) - } + // Verify nullifier was marked as used + ensure!( + UsedNullifiers::::contains_key(nullifier_bytes), + BenchmarkError::Stop("Nullifier should be marked as used after verification") + ); - impl_benchmark_test_suite! { - Pallet, - crate::mock::new_test_ext(), - crate::mock::Test, + Ok(()) } } diff --git a/pallets/wormhole/src/lib.rs b/pallets/wormhole/src/lib.rs index 36457741..2abd1cb2 100644 --- a/pallets/wormhole/src/lib.rs +++ b/pallets/wormhole/src/lib.rs @@ -2,8 +2,13 @@ extern crate alloc; +use core::marker::PhantomData; + +use codec::{Decode, MaxEncodedLen}; +use frame_support::StorageHasher; use lazy_static::lazy_static; pub use pallet::*; +pub use qp_poseidon::{PoseidonHasher as PoseidonCore, ToFelts}; use qp_wormhole_verifier::WormholeVerifier; #[cfg(test)] @@ -11,6 +16,7 @@ mod mock; #[cfg(test)] mod tests; pub mod weights; +use sp_metadata_ir::StorageHasherIR; pub use weights::*; #[cfg(feature = "runtime-benchmarks")] @@ -29,51 +35,115 @@ pub fn get_wormhole_verifier() -> Result<&'static WormholeVerifier, &'static str WORMHOLE_VERIFIER.as_ref().ok_or("Wormhole verifier not available") } +// We use a generic struct so we can pass the specific Key type to the hasher +pub struct PoseidonStorageHasher(PhantomData); + +impl StorageHasher for PoseidonStorageHasher { + // We are lying here, but maybe it's ok because it's just metadata + const METADATA: StorageHasherIR = StorageHasherIR::Identity; + type Output = [u8; 32]; + + fn hash(x: &[u8]) -> Self::Output { + PoseidonCore::hash_storage::(x) + } + + fn max_len() -> usize { + 32 + } +} + #[frame_support::pallet] pub mod pallet { - use crate::WeightInfo; + use crate::{PoseidonStorageHasher, ToFelts, WeightInfo}; use alloc::vec::Vec; use codec::Decode; use frame_support::{ + dispatch::DispatchResult, pallet_prelude::*, traits::{ fungible::{Mutate, Unbalanced}, + fungibles::{self, Inspect as FungiblesInspect, Mutate as FungiblesMutate}, + tokens::Preservation, Currency, ExistenceRequirement, WithdrawReasons, }, weights::WeightToFee, }; use frame_system::pallet_prelude::*; - use qp_wormhole::TransferProofs; use qp_wormhole_circuit::inputs::PublicCircuitInputs; use qp_wormhole_verifier::ProofWithPublicInputs; use qp_zk_circuits_common::circuit::{C, D, F}; use sp_runtime::{ - traits::{Saturating, Zero}, + traits::{MaybeDisplay, Saturating, StaticLookup, Zero}, Perbill, }; pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + pub type AssetIdOf = <::Assets as fungibles::Inspect< + ::AccountId, + >>::AssetId; + pub type AssetBalanceOf = <::Assets as fungibles::Inspect< + ::AccountId, + >>::Balance; + pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + + pub type TransferProofKey = ( + AssetIdOf, + ::TransferCount, + ::WormholeAccountId, + ::WormholeAccountId, + BalanceOf, + ); #[pallet::pallet] pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config { - /// Currency type used for minting tokens and handling wormhole transfers - type Currency: Mutate> - + TransferProofs, Self::AccountId> - + Unbalanced - + Currency; + pub trait Config: frame_system::Config + where + AssetIdOf: Default + From + Clone + ToFelts, + BalanceOf: Default + ToFelts, + AssetBalanceOf: Into> + From>, + { + /// Currency type used for native token transfers and minting + type Currency: Mutate<::AccountId, Balance = BalanceOf> + + Unbalanced<::AccountId> + + Currency<::AccountId>; + + /// Assets type used for managing fungible assets + type Assets: fungibles::Inspect<::AccountId> + + fungibles::Mutate<::AccountId> + + fungibles::Create<::AccountId>; + + /// Transfer count type used in storage + type TransferCount: Parameter + + MaxEncodedLen + + Default + + Saturating + + Copy + + sp_runtime::traits::One + + ToFelts; /// Account ID used as the "from" account when creating transfer proofs for minted tokens #[pallet::constant] - type MintingAccount: Get; + type MintingAccount: Get<::AccountId>; /// Weight information for pallet operations. type WeightInfo: WeightInfo; type WeightToFee: WeightToFee>; + + /// Override system AccountId to make it felts encodable + type WormholeAccountId: Parameter + + Member + + MaybeSerializeDeserialize + + core::fmt::Debug + + MaybeDisplay + + Ord + + MaxEncodedLen + + ToFelts + + Into<::AccountId> + + From<::AccountId>; } #[pallet::storage] @@ -81,10 +151,41 @@ pub mod pallet { pub(super) type UsedNullifiers = StorageMap<_, Blake2_128Concat, [u8; 32], bool, ValueQuery>; + /// Transfer proofs for wormhole transfers (both native and assets) + #[pallet::storage] + #[pallet::getter(fn transfer_proof)] + pub type TransferProof = StorageMap< + _, + PoseidonStorageHasher>, + TransferProofKey, + (), + OptionQuery, + >; + + /// Transfer count for all wormhole transfers + #[pallet::storage] + #[pallet::getter(fn transfer_count)] + pub type TransferCount = StorageValue<_, T::TransferCount, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - ProofVerified { exit_amount: BalanceOf }, + NativeTransferred { + from: ::AccountId, + to: ::AccountId, + amount: BalanceOf, + transfer_count: T::TransferCount, + }, + AssetTransferred { + asset_id: AssetIdOf, + from: ::AccountId, + to: ::AccountId, + amount: AssetBalanceOf, + transfer_count: T::TransferCount, + }, + ProofVerified { + exit_amount: BalanceOf, + }, } #[pallet::error] @@ -99,17 +200,15 @@ pub mod pallet { StorageRootMismatch, BlockNotFound, InvalidBlockNumber, + AssetNotFound, + SelfTransfer, } #[pallet::call] impl Pallet { #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::verify_wormhole_proof())] - pub fn verify_wormhole_proof( - origin: OriginFor, - proof_bytes: Vec, - block_number: BlockNumberFor, - ) -> DispatchResult { + pub fn verify_wormhole_proof(origin: OriginFor, proof_bytes: Vec) -> DispatchResult { ensure_none(origin)?; let verifier = @@ -133,6 +232,10 @@ pub mod pallet { Error::::NullifierAlreadyUsed ); + // Extract the block number from public inputs + let block_number = BlockNumberFor::::try_from(public_inputs.block_number) + .map_err(|_| Error::::InvalidPublicInputs)?; + // Get the block hash for the specified block number let block_hash = frame_system::Pallet::::block_hash(block_number); @@ -145,36 +248,11 @@ pub mod pallet { let default_hash = T::Hash::default(); ensure!(block_hash != default_hash, Error::::BlockNotFound); - // Get the storage root for the specified block - let storage_root = sp_io::storage::root(sp_runtime::StateVersion::V1); - - let root_hash = public_inputs.root_hash; - let storage_root_bytes = storage_root.as_slice(); - - // Compare the root_hash from the proof with the actual storage root - // Skip storage root validation in test and benchmark environments since proofs - // may have been generated with different state - #[cfg(not(any(test, feature = "runtime-benchmarks")))] - if root_hash.as_ref() != storage_root_bytes { - log::warn!( - target: "wormhole", - "Storage root mismatch for block {:?}: expected {:?}, got {:?}", - block_number, - root_hash.as_ref(), - storage_root_bytes - ); - return Err(Error::::StorageRootMismatch.into()); - } - - #[cfg(any(test, feature = "runtime-benchmarks"))] - { - let _root_hash = root_hash; - let _storage_root_bytes = storage_root_bytes; - log::debug!( - target: "wormhole", - "Skipping storage root validation in test/benchmark environment" - ); - } + // Ensure that the block hash from storage matches the one in public inputs + ensure!( + block_hash.as_ref() == public_inputs.block_hash.as_ref(), + Error::::InvalidPublicInputs + ); verifier.verify(proof.clone()).map_err(|_| Error::::VerificationFailed)?; @@ -189,8 +267,13 @@ pub mod pallet { // Decode exit account from public inputs let exit_account_bytes = *public_inputs.exit_account; - let exit_account = T::AccountId::decode(&mut &exit_account_bytes[..]) - .map_err(|_| Error::::InvalidPublicInputs)?; + let exit_account = + ::AccountId::decode(&mut &exit_account_bytes[..]) + .map_err(|_| Error::::InvalidPublicInputs)?; + + // Extract asset_id from public inputs + let asset_id_u32 = public_inputs.asset_id; + let asset_id: AssetIdOf = asset_id_u32.into(); // Calculate fees first let weight = ::WeightInfo::verify_wormhole_proof(); @@ -199,34 +282,180 @@ pub mod pallet { let volume_fee = volume_fee_perbill * exit_balance; let total_fee = weight_fee.saturating_add(volume_fee); - // Mint tokens to the exit account - // This does not affect total issuance and does not create an imbalance - >::increase_balance( - &exit_account, - exit_balance, - frame_support::traits::tokens::Precision::Exact, - )?; + // Handle native (asset_id = 0) or asset transfers + if asset_id == AssetIdOf::::default() { + // Native token transfer + // Mint tokens to the exit account + // This does not affect total issuance and does not create an imbalance + >::increase_balance( + &exit_account, + exit_balance, + frame_support::traits::tokens::Precision::Exact, + )?; - // Withdraw fee from exit account if fees are non-zero - // This creates a negative imbalance that will be handled by the transaction payment - // pallet - if !total_fee.is_zero() { - let _fee_imbalance = T::Currency::withdraw( + // Withdraw fee from exit account if fees are non-zero + // This creates a negative imbalance that will be handled by the transaction payment + // pallet + if !total_fee.is_zero() { + let _fee_imbalance = T::Currency::withdraw( + &exit_account, + total_fee, + WithdrawReasons::TRANSACTION_PAYMENT, + ExistenceRequirement::KeepAlive, + )?; + } + } else { + // Asset transfer + let asset_balance: AssetBalanceOf = exit_balance.into(); + >::mint_into( + asset_id.clone(), &exit_account, - total_fee, - WithdrawReasons::TRANSACTION_PAYMENT, - ExistenceRequirement::KeepAlive, + asset_balance, )?; + + // For assets, we still need to charge fees in native currency + // The exit account must have enough native balance to pay fees + if !total_fee.is_zero() { + let _fee_imbalance = T::Currency::withdraw( + &exit_account, + total_fee, + WithdrawReasons::TRANSACTION_PAYMENT, + ExistenceRequirement::AllowDeath, + )?; + } } // Create a transfer proof for the minted tokens let mint_account = T::MintingAccount::get(); - T::Currency::store_transfer_proof(&mint_account, &exit_account, exit_balance); + Self::record_transfer( + asset_id, + mint_account.into(), + exit_account.into(), + exit_balance, + )?; // Emit event Self::deposit_event(Event::ProofVerified { exit_amount: exit_balance }); Ok(()) } + + /// Transfer native tokens and store proof for wormhole + #[pallet::call_index(1)] + #[pallet::weight(T::DbWeight::get().reads_writes(1, 2))] + pub fn transfer_native( + origin: OriginFor, + dest: AccountIdLookupOf, + #[pallet::compact] amount: BalanceOf, + ) -> DispatchResult { + let source = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + + // Prevent self-transfers + ensure!(source != dest, Error::::SelfTransfer); + + // Perform the transfer + >::transfer(&source, &dest, amount, Preservation::Expendable)?; + + // Store proof with asset_id = Default (0 for native) + Self::record_transfer(AssetIdOf::::default(), source.into(), dest.into(), amount)?; + + Ok(()) + } + + /// Transfer asset tokens and store proof for wormhole + #[pallet::call_index(2)] + #[pallet::weight(T::DbWeight::get().reads_writes(2, 2))] + pub fn transfer_asset( + origin: OriginFor, + asset_id: AssetIdOf, + dest: AccountIdLookupOf, + #[pallet::compact] amount: AssetBalanceOf, + ) -> DispatchResult { + let source = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + + // Prevent self-transfers + ensure!(source != dest, Error::::SelfTransfer); + + // Check if asset exists + ensure!( + >::asset_exists(asset_id.clone()), + Error::::AssetNotFound + ); + + // Perform the transfer + >::transfer( + asset_id.clone(), + &source, + &dest, + amount, + Preservation::Expendable, + )?; + + // Store proof + Self::record_transfer(asset_id, source.into(), dest.into(), amount.into())?; + + Ok(()) + } + } + + // Helper functions for recording transfer proofs + impl Pallet { + /// Record a transfer proof + /// This should be called by transaction extensions or other runtime components + pub fn record_transfer( + asset_id: AssetIdOf, + from: ::WormholeAccountId, + to: ::WormholeAccountId, + amount: BalanceOf, + ) -> DispatchResult { + let current_count = TransferCount::::get(); + TransferProof::::insert( + (asset_id.clone(), current_count, from.clone(), to.clone(), amount), + (), + ); + TransferCount::::put(current_count.saturating_add(T::TransferCount::one())); + + if asset_id == AssetIdOf::::default() { + Self::deposit_event(Event::::NativeTransferred { + from: from.into(), + to: to.into(), + amount, + transfer_count: current_count, + }); + } else { + Self::deposit_event(Event::::AssetTransferred { + from: from.into(), + to: to.into(), + asset_id, + amount: amount.into(), + transfer_count: current_count, + }); + } + + Ok(()) + } + } + + // Implement the TransferProofRecorder trait for other pallets to use + impl + qp_wormhole::TransferProofRecorder< + ::WormholeAccountId, + AssetIdOf, + BalanceOf, + > for Pallet + { + type Error = DispatchError; + + fn record_transfer_proof( + asset_id: Option>, + from: ::WormholeAccountId, + to: ::WormholeAccountId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + let asset_id_value = asset_id.unwrap_or_default(); + Self::record_transfer(asset_id_value, from, to, amount) + } } } diff --git a/pallets/wormhole/src/mock.rs b/pallets/wormhole/src/mock.rs index 4c764f26..8527f4ff 100644 --- a/pallets/wormhole/src/mock.rs +++ b/pallets/wormhole/src/mock.rs @@ -1,27 +1,29 @@ use crate as pallet_wormhole; use frame_support::{ construct_runtime, parameter_types, - traits::{ConstU32, Everything}, + traits::{ConstU128, ConstU32, Everything}, weights::IdentityFee, }; +use frame_system::mocking::MockUncheckedExtrinsic; +use qp_poseidon::PoseidonHasher; use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; -// --- MOCK RUNTIME --- +use sp_runtime::{traits::IdentityLookup, BuildStorage}; construct_runtime!( pub enum Test { System: frame_system, Balances: pallet_balances, + Assets: pallet_assets, Wormhole: pallet_wormhole, } ); pub type Balance = u128; pub type AccountId = sp_core::crypto::AccountId32; -pub type Block = frame_system::mocking::MockBlock; +pub type Block = sp_runtime::generic::Block< + qp_header::Header, + MockUncheckedExtrinsic, +>; /// Helper function to convert a u64 to an AccountId32 pub fn account_id(id: u64) -> AccountId { @@ -30,8 +32,6 @@ pub fn account_id(id: u64) -> AccountId { AccountId::new(bytes) } -// --- FRAME SYSTEM --- - parameter_types! { pub const BlockHashCount: u64 = 250; } @@ -46,10 +46,10 @@ impl frame_system::Config for Test { type RuntimeTask = (); type Nonce = u64; type Hash = H256; - type Hashing = BlakeTwo256; + type Hashing = PoseidonHasher; type AccountId = AccountId; type Lookup = IdentityLookup; - type Block = Block; + type Block = Block; type BlockHashCount = BlockHashCount; type DbWeight = (); type Version = (); @@ -69,8 +69,6 @@ impl frame_system::Config for Test { type PostTransactions = (); } -// --- PALLET BALANCES --- - parameter_types! { pub const ExistentialDeposit: Balance = 1; } @@ -89,9 +87,31 @@ impl pallet_balances::Config for Test { type MaxReserves = (); type MaxFreezes = (); type DoneSlashHandler = (); + type RuntimeEvent = RuntimeEvent; } -// --- PALLET WORMHOLE --- +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = + frame_support::traits::AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<1>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<1000>; + type CallbackHandle = (); + type Holder = (); +} parameter_types! { pub const MintingAccount: AccountId = AccountId::new([ @@ -104,16 +124,14 @@ impl pallet_wormhole::Config for Test { type WeightInfo = crate::weights::SubstrateWeight; type WeightToFee = IdentityFee; type Currency = Balances; + type Assets = Assets; + type TransferCount = u64; type MintingAccount = MintingAccount; + type WormholeAccountId = AccountId; } // Helper function to build a genesis configuration -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - pallet_balances::GenesisConfig:: { balances: vec![] } - .assimilate_storage(&mut t) - .unwrap(); - +pub fn new_test_ext() -> sp_state_machine::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); t.into() } diff --git a/pallets/wormhole/src/tests.rs b/pallets/wormhole/src/tests.rs index a96fca74..2a706be8 100644 --- a/pallets/wormhole/src/tests.rs +++ b/pallets/wormhole/src/tests.rs @@ -1,211 +1,366 @@ #[cfg(test)] mod wormhole_tests { - use crate::{get_wormhole_verifier, mock::*, weights, Config, Error, WeightInfo}; - use frame_support::{assert_noop, assert_ok, weights::WeightToFee}; - use qp_wormhole_circuit::inputs::PublicCircuitInputs; + use crate::{get_wormhole_verifier, mock::*}; + use codec::Encode; + use frame_support::{ + assert_ok, + traits::fungible::{Inspect, Mutate}, + }; + use plonky2::plonk::circuit_data::CircuitConfig; + use qp_poseidon::PoseidonHasher; + use qp_wormhole_circuit::{ + inputs::{CircuitInputs, PrivateCircuitInputs, PublicCircuitInputs}, + nullifier::Nullifier, + }; + use qp_wormhole_prover::WormholeProver; use qp_wormhole_verifier::ProofWithPublicInputs; - use sp_runtime::Perbill; + use qp_zk_circuits_common::{ + circuit::{C, F}, + storage_proof::prepare_proof_for_circuit, + utils::{digest_felts_to_bytes, BytesDigest, Digest}, + }; + use sp_runtime::{traits::Header, DigestItem}; - // Helper function to generate proof and inputs for - fn get_test_proof() -> Vec { - let hex_proof = include_str!("../proof_from_bins.hex"); - hex::decode(hex_proof.trim()).expect("Failed to decode hex proof") + fn generate_proof(inputs: CircuitInputs) -> ProofWithPublicInputs { + let config = CircuitConfig::standard_recursion_config(); + let prover = WormholeProver::new(config); + let prover_next = prover.commit(&inputs).expect("proof failed"); + let proof = prover_next.prove().expect("valid proof"); + proof } #[test] - fn test_verifier_availability() { - new_test_ext().execute_with(|| { - let verifier = get_wormhole_verifier(); - assert!(verifier.is_ok(), "Verifier should be available in tests"); + fn test_wormhole_transfer_proof_generation() { + let alice = account_id(1); + let secret: BytesDigest = [1u8; 32].try_into().expect("valid secret"); + let unspendable_account = + qp_wormhole_circuit::unspendable_account::UnspendableAccount::from_secret(secret) + .account_id; + let unspendable_account_bytes_digest = digest_felts_to_bytes(unspendable_account); + let unspendable_account_bytes: [u8; 32] = unspendable_account_bytes_digest + .as_ref() + .try_into() + .expect("BytesDigest is always 32 bytes"); + let unspendable_account_id = AccountId::new(unspendable_account_bytes); + let exit_account_id = AccountId::new([42u8; 32]); + let funding_amount = 1_000_000_000_001u128; - // Verify the verifier can be used - let verifier = verifier.unwrap(); - // Check that the circuit data is valid by checking gates - assert!(!verifier.circuit_data.common.gates.is_empty(), "Circuit should have gates"); - }); - } + let mut ext = new_test_ext(); + + let (storage_key, state_root, leaf_hash, event_transfer_count, header) = + ext.execute_with(|| { + System::set_block_number(1); + + let pre_runtime_data = vec![ + 233, 182, 183, 107, 158, 1, 115, 19, 219, 126, 253, 86, 30, 208, 176, 70, 21, + 45, 180, 229, 9, 62, 91, 4, 6, 53, 245, 52, 48, 38, 123, 225, + ]; + let seal_data = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 77, 142, + ]; + + System::deposit_log(DigestItem::PreRuntime(*b"pow_", pre_runtime_data)); + System::deposit_log(DigestItem::Seal(*b"pow_", seal_data)); + + assert_ok!(Balances::mint_into(&alice, funding_amount)); + assert_ok!(Wormhole::transfer_native( + frame_system::RawOrigin::Signed(alice.clone()).into(), + unspendable_account_id.clone(), + funding_amount, + )); + + let event_transfer_count = 0u64; + + let leaf_hash = PoseidonHasher::hash_storage::>( + &( + 0u32, + event_transfer_count, + alice.clone(), + unspendable_account_id.clone(), + funding_amount, + ) + .encode(), + ); + + let proof_address = crate::pallet::TransferProof::::hashed_key_for(&( + 0u32, + event_transfer_count, + alice.clone(), + unspendable_account_id.clone(), + funding_amount, + )); + let mut storage_key = proof_address; + storage_key.extend_from_slice(&leaf_hash); + + let header = System::finalize(); + let state_root = *header.state_root(); + + (storage_key, state_root, leaf_hash, event_transfer_count, header) + }); + + use sp_state_machine::prove_read; + let proof = prove_read(ext.as_backend(), &[&storage_key]) + .expect("failed to generate storage proof"); + + let proof_nodes_vec: Vec> = proof.iter_nodes().map(|n| n.to_vec()).collect(); + + let processed_storage_proof = + prepare_proof_for_circuit(proof_nodes_vec, hex::encode(&state_root), leaf_hash) + .expect("failed to prepare proof for circuit"); + + let parent_hash = *header.parent_hash(); + let extrinsics_root = *header.extrinsics_root(); + let digest = header.digest().encode(); + let digest_array: [u8; 110] = digest.try_into().expect("digest should be 110 bytes"); + let block_number: u32 = (*header.number()).try_into().expect("block number fits in u32"); + + let block_hash = header.hash(); + + let circuit_inputs = CircuitInputs { + private: PrivateCircuitInputs { + secret, + storage_proof: processed_storage_proof, + transfer_count: event_transfer_count, + funding_account: BytesDigest::try_from(alice.as_ref() as &[u8]) + .expect("account is 32 bytes"), + unspendable_account: Digest::from(unspendable_account).into(), + state_root: BytesDigest::try_from(state_root.as_ref()) + .expect("state root is 32 bytes"), + extrinsics_root: BytesDigest::try_from(extrinsics_root.as_ref()) + .expect("extrinsics root is 32 bytes"), + digest: digest_array, + }, + public: PublicCircuitInputs { + asset_id: 0u32, + funding_amount, + nullifier: Nullifier::from_preimage(secret, event_transfer_count).hash.into(), + exit_account: BytesDigest::try_from(exit_account_id.as_ref() as &[u8]) + .expect("account is 32 bytes"), + block_hash: BytesDigest::try_from(block_hash.as_ref()) + .expect("block hash is 32 bytes"), + parent_hash: BytesDigest::try_from(parent_hash.as_ref()) + .expect("parent hash is 32 bytes"), + block_number, + }, + }; + + let proof = generate_proof(circuit_inputs); + + let public_inputs = + PublicCircuitInputs::try_from(&proof).expect("failed to parse public inputs"); + + assert_eq!(public_inputs.funding_amount, funding_amount); + assert_eq!( + public_inputs.exit_account, + BytesDigest::try_from(exit_account_id.as_ref() as &[u8]).unwrap() + ); + + let verifier = get_wormhole_verifier().expect("verifier should be available"); + verifier.verify(proof.clone()).expect("proof should verify"); + + let proof_bytes = proof.to_bytes(); - #[test] - fn test_verify_empty_proof_fails() { new_test_ext().execute_with(|| { - let empty_proof = vec![]; - let block_number = frame_system::Pallet::::block_number(); - assert_noop!( - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), empty_proof, block_number), - Error::::ProofDeserializationFailed + System::set_block_number(1); + + let pre_runtime_data = vec![ + 233, 182, 183, 107, 158, 1, 115, 19, 219, 126, 253, 86, 30, 208, 176, 70, 21, 45, + 180, 229, 9, 62, 91, 4, 6, 53, 245, 52, 48, 38, 123, 225, + ]; + let seal_data = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 30, 77, 142, + ]; + + System::deposit_log(DigestItem::PreRuntime(*b"pow_", pre_runtime_data)); + System::deposit_log(DigestItem::Seal(*b"pow_", seal_data)); + + assert_ok!(Balances::mint_into(&alice, funding_amount)); + assert_ok!(Wormhole::transfer_native( + frame_system::RawOrigin::Signed(alice.clone()).into(), + unspendable_account_id.clone(), + funding_amount, + )); + + let block_1_header = System::finalize(); + + System::reset_events(); + System::initialize(&2, &block_1_header.hash(), block_1_header.digest()); + + let balance_before = Balances::balance(&exit_account_id); + assert_eq!(balance_before, 0); + + assert_ok!(Wormhole::verify_wormhole_proof( + frame_system::RawOrigin::None.into(), + proof_bytes.clone() + )); + + let balance_after = Balances::balance(&exit_account_id); + + assert!(balance_after > 0, "Exit account should have received funds"); + assert!( + balance_after < funding_amount, + "Exit account balance should be less than funding amount due to fees" ); }); - } - #[test] - fn test_verify_invalid_proof_data_fails() { new_test_ext().execute_with(|| { - // Create some random bytes that will fail deserialization - let invalid_proof = vec![1u8; 100]; - let block_number = frame_system::Pallet::::block_number(); - assert_noop!( - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), invalid_proof, block_number), - Error::::ProofDeserializationFailed + System::set_block_number(1); + + let pre_runtime_data = vec![1u8; 32]; + let seal_data = vec![2u8; 64]; + + System::deposit_log(DigestItem::PreRuntime(*b"pow_", pre_runtime_data)); + System::deposit_log(DigestItem::Seal(*b"pow_", seal_data)); + + let different_header = System::finalize(); + + System::reset_events(); + System::initialize(&2, &different_header.hash(), different_header.digest()); + + let result = Wormhole::verify_wormhole_proof( + frame_system::RawOrigin::None.into(), + proof_bytes.clone(), ); + + assert!(result.is_err(), "Proof verification should fail with mismatched state"); }); } #[test] - fn test_verify_valid_proof() { + fn transfer_native_works() { new_test_ext().execute_with(|| { - let proof = get_test_proof(); - let block_number = frame_system::Pallet::::block_number(); - assert_ok!(Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, block_number)); + let alice = account_id(1); + let bob = account_id(2); + let amount = 1000u128; + + assert_ok!(Balances::mint_into(&alice, amount * 2)); + + let count_before = Wormhole::transfer_count(); + assert_ok!(Wormhole::transfer_native( + frame_system::RawOrigin::Signed(alice.clone()).into(), + bob.clone(), + amount, + )); + + assert_eq!(Balances::balance(&alice), amount); + assert_eq!(Balances::balance(&bob), amount); + assert_eq!(Wormhole::transfer_count(), count_before + 1); + assert!(Wormhole::transfer_proof((0u32, count_before, alice, bob, amount)).is_some()); }); } #[test] - fn test_verify_invalid_inputs() { + fn transfer_native_fails_on_self_transfer() { new_test_ext().execute_with(|| { - let mut proof = get_test_proof(); - let block_number = frame_system::Pallet::::block_number(); + let alice = account_id(1); + let amount = 1000u128; - if let Some(byte) = proof.get_mut(0) { - *byte = !*byte; // Flip bits to make proof invalid - } + assert_ok!(Balances::mint_into(&alice, amount)); - assert_noop!( - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, block_number), - Error::::VerificationFailed + let result = Wormhole::transfer_native( + frame_system::RawOrigin::Signed(alice.clone()).into(), + alice.clone(), + amount, ); + + assert!(result.is_err()); }); } #[test] - fn test_wormhole_exit_balance_and_fees() { + fn transfer_asset_works() { new_test_ext().execute_with(|| { - let proof = get_test_proof(); - let expected_exit_account = account_id(8226349481601990196u64); - - // Parse the proof to get expected funding amount - let verifier = get_wormhole_verifier().expect("Verifier should be available"); - let proof_with_inputs = ProofWithPublicInputs::from_bytes(proof.clone(), &verifier.circuit_data.common) - .expect("Should be able to parse test proof"); - - let public_inputs = PublicCircuitInputs::try_from(&proof_with_inputs) - .expect("Should be able to parse public inputs"); - - let expected_funding_amount = public_inputs.funding_amount; - - // Calculate expected fees (matching lib.rs logic exactly) - let weight = as WeightInfo>::verify_wormhole_proof(); - let weight_fee: u128 = ::WeightToFee::weight_to_fee(&weight); - let volume_fee = Perbill::from_rational(1u32, 1000u32) * expected_funding_amount; - let expected_total_fee = weight_fee.saturating_add(volume_fee); - let expected_net_balance_increase = expected_funding_amount.saturating_sub(expected_total_fee); - - let initial_exit_balance = - pallet_balances::Pallet::::free_balance(&expected_exit_account); - - let block_number = frame_system::Pallet::::block_number(); - let result = - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, block_number); - assert_ok!(result); - - let final_exit_balance = - pallet_balances::Pallet::::free_balance(&expected_exit_account); - - let balance_increase = final_exit_balance - initial_exit_balance; - - // Assert the exact expected balance increase - assert_eq!( - balance_increase - , expected_net_balance_increase, - "Balance increase should equal funding amount minus fees. Funding: {}, Fees: {}, Expected net: {}, Actual: {}" - , expected_funding_amount - , expected_total_fee - , expected_net_balance_increase - , balance_increase - ); - - // NOTE: In this mock/test context, the OnUnbalanced handler is not triggered for this withdrawal. - // In production, the fee will be routed to the handler as expected. - }); - } + let alice = account_id(1); + let bob = account_id(2); + let asset_id = 1u32; + let amount = 1000u128; - #[test] - fn test_nullifier_already_used() { - new_test_ext().execute_with(|| { - let proof = get_test_proof(); - let block_number = frame_system::Pallet::::block_number(); + assert_ok!(Balances::mint_into(&alice, 1000)); + assert_ok!(Balances::mint_into(&bob, 1000)); - // First verification should succeed - assert_ok!(Wormhole::verify_wormhole_proof( - RuntimeOrigin::none(), - proof.clone(), - block_number + assert_ok!(Assets::create( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id.into(), + alice.clone(), + 1, + )); + assert_ok!(Assets::mint( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id.into(), + alice.clone(), + amount * 2, )); - // Second verification with same proof should fail due to nullifier reuse - assert_noop!( - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, block_number), - Error::::NullifierAlreadyUsed + let count_before = Wormhole::transfer_count(); + assert_ok!(Wormhole::transfer_asset( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id, + bob.clone(), + amount, + )); + + assert_eq!(Assets::balance(asset_id, &alice), amount); + assert_eq!(Assets::balance(asset_id, &bob), amount); + assert_eq!(Wormhole::transfer_count(), count_before + 1); + assert!( + Wormhole::transfer_proof((asset_id, count_before, alice, bob, amount)).is_some() ); }); } #[test] - fn test_verify_future_block_number_fails() { + fn transfer_asset_fails_on_nonexistent_asset() { new_test_ext().execute_with(|| { - let proof = get_test_proof(); - let current_block = frame_system::Pallet::::block_number(); - let future_block = current_block + 1; + let alice = account_id(1); + let bob = account_id(2); + let asset_id = 999u32; + let amount = 1000u128; - assert_noop!( - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, future_block), - Error::::InvalidBlockNumber + let result = Wormhole::transfer_asset( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id, + bob.clone(), + amount, ); - }); - } - #[test] - fn test_verify_storage_root_mismatch_fails() { - new_test_ext().execute_with(|| { - // This test would require a proof with a different root_hash than the current storage - // root - let proof = get_test_proof(); - let block_number = frame_system::Pallet::::block_number(); - - let result = - Wormhole::verify_wormhole_proof(RuntimeOrigin::none(), proof, block_number); - - // This should either succeed (if root_hash matches) or fail with StorageRootMismatch - // We can't easily create a proof with wrong root_hash in tests, so we just verify - // that the validation logic is executed - assert!(result.is_ok() || result.is_err()); + assert!(result.is_err()); }); } #[test] - fn test_verify_with_different_block_numbers() { + fn transfer_asset_fails_on_self_transfer() { new_test_ext().execute_with(|| { - let proof = get_test_proof(); - let current_block = frame_system::Pallet::::block_number(); + let alice = account_id(1); + let asset_id = 1u32; + let amount = 1000u128; - // Test with current block (should succeed) - assert_ok!(Wormhole::verify_wormhole_proof( - RuntimeOrigin::none(), - proof.clone(), - current_block + assert_ok!(Balances::mint_into(&alice, 1000)); + + assert_ok!(Assets::create( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id.into(), + alice.clone(), + 1, + )); + assert_ok!(Assets::mint( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id.into(), + alice.clone(), + amount, )); - // Test with a recent block (should succeed if it exists) - if current_block > 1 { - let recent_block = current_block - 1; - let result = Wormhole::verify_wormhole_proof( - RuntimeOrigin::none(), - proof.clone(), - recent_block, - ); - // This might succeed or fail depending on whether the block exists - // and whether the storage root matches - assert!(result.is_ok() || result.is_err()); - } + let result = Wormhole::transfer_asset( + frame_system::RawOrigin::Signed(alice.clone()).into(), + asset_id, + alice.clone(), + amount, + ); + + assert!(result.is_err()); }); } } diff --git a/pallets/wormhole/src/weights.rs b/pallets/wormhole/src/weights.rs index b1516fa8..8d06df5b 100644 --- a/pallets/wormhole/src/weights.rs +++ b/pallets/wormhole/src/weights.rs @@ -18,10 +18,10 @@ //! Autogenerated weights for `pallet_wormhole` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 46.2.0 -//! DATE: 2025-07-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2025-11-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `Veronikas-MacBook-Air.local`, CPU: `` +//! HOSTNAME: `Dastans-MacBook-Pro.local`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -70,10 +70,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::TransferProof` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `MaxEncodedLen`) fn verify_wormhole_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `424` + // Measured: `256` // Estimated: `3593` - // Minimum execution time: 17_162_000_000 picoseconds. - Weight::from_parts(17_576_000_000, 3593) + // Minimum execution time: 14_318_000_000 picoseconds. + Weight::from_parts(14_468_000_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -93,10 +93,10 @@ impl WeightInfo for () { /// Proof: `Balances::TransferProof` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `MaxEncodedLen`) fn verify_wormhole_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `424` + // Measured: `256` // Estimated: `3593` - // Minimum execution time: 17_162_000_000 picoseconds. - Weight::from_parts(17_576_000_000, 3593) + // Minimum execution time: 14_318_000_000 picoseconds. + Weight::from_parts(14_468_000_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/pallets/wormhole/verifier.bin b/pallets/wormhole/verifier.bin index 6f7404b6..229b59d9 100644 Binary files a/pallets/wormhole/verifier.bin and b/pallets/wormhole/verifier.bin differ diff --git a/primitives/header/Cargo.toml b/primitives/header/Cargo.toml new file mode 100644 index 00000000..a164947d --- /dev/null +++ b/primitives/header/Cargo.toml @@ -0,0 +1,40 @@ +[package] +authors.workspace = true +description = "Fork of sp-runtime's Header type with a custom hash function that's felt aligned for our wormhole circuits" +edition.workspace = true +homepage.workspace = true +license = "Apache-2.0" +name = "qp-header" +publish = false +repository.workspace = true +version = "0.1.0" + +[dependencies] +codec = { features = ["derive"], workspace = true } +log.workspace = true +p3-field = { workspace = true } +p3-goldilocks = { workspace = true } +qp-poseidon = { workspace = true, features = ["serde"] } +qp-poseidon-core = { workspace = true } +scale-info = { features = ["derive", "serde"], workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } +sp-core = { features = ["serde"], workspace = true } +sp-runtime = { features = ["serde"], workspace = true } + +[dev-dependencies] +hex = { workspace = true } +serde_json = { workspace = true, default-features = false, features = [ + "alloc", + "std", +] } + + +[features] +default = ["serde", "std"] +std = [ + "codec/std", + "qp-poseidon/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", +] diff --git a/primitives/header/src/lib.rs b/primitives/header/src/lib.rs new file mode 100644 index 00000000..bbe897fe --- /dev/null +++ b/primitives/header/src/lib.rs @@ -0,0 +1,346 @@ +//! Fork of sp-runtime's generic implementation of a block header. +//! We override the hashing function to ensure a felt aligned pre-image for the block hash. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Codec, Decode, DecodeWithMemTracking, Encode}; +use p3_field::integers::QuotientMap; +use p3_goldilocks::Goldilocks; +use qp_poseidon_core::{ + hash_variable_length, + serialization::{injective_bytes_to_felts, unsafe_digest_bytes_to_felts}, +}; +use scale_info::TypeInfo; +use sp_core::U256; +use sp_runtime::{ + generic::Digest, + traits::{AtLeast32BitUnsigned, BlockNumber, Hash as HashT, MaybeDisplay, Member}, + RuntimeDebug, +}; +extern crate alloc; + +use alloc::vec::Vec; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Custom block header that hashes itself with Poseidon over Goldilocks field elements. +#[derive(Encode, Decode, PartialEq, Eq, Clone, RuntimeDebug, TypeInfo, DecodeWithMemTracking)] +#[scale_info(skip_type_params(Hash))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +pub struct Header + TryFrom, Hash: HashT> { + pub parent_hash: Hash::Output, + #[cfg_attr( + feature = "serde", + serde(serialize_with = "serialize_number", deserialize_with = "deserialize_number") + )] + pub number: Number, + pub state_root: Hash::Output, + pub extrinsics_root: Hash::Output, + pub digest: Digest, +} + +#[cfg(feature = "serde")] +pub fn serialize_number + TryFrom>( + val: &T, + s: S, +) -> Result +where + S: serde::Serializer, +{ + let u256: U256 = (*val).into(); + serde::Serialize::serialize(&u256, s) +} + +#[cfg(feature = "serde")] +pub fn deserialize_number<'a, D, T: Copy + Into + TryFrom>(d: D) -> Result +where + D: serde::Deserializer<'a>, +{ + let u256: U256 = serde::Deserialize::deserialize(d)?; + TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed")) +} + +impl sp_runtime::traits::Header for Header +where + Number: BlockNumber, + Hash: HashT, + Hash::Output: From<[u8; 32]>, +{ + type Number = Number; + type Hash = ::Output; + type Hashing = Hash; + + fn new( + number: Self::Number, + extrinsics_root: Self::Hash, + state_root: Self::Hash, + parent_hash: Self::Hash, + digest: Digest, + ) -> Self { + Self { number, extrinsics_root, state_root, parent_hash, digest } + } + fn number(&self) -> &Self::Number { + &self.number + } + + fn set_number(&mut self, num: Self::Number) { + self.number = num + } + fn extrinsics_root(&self) -> &Self::Hash { + &self.extrinsics_root + } + + fn set_extrinsics_root(&mut self, root: Self::Hash) { + self.extrinsics_root = root + } + fn state_root(&self) -> &Self::Hash { + &self.state_root + } + + fn set_state_root(&mut self, root: Self::Hash) { + self.state_root = root + } + fn parent_hash(&self) -> &Self::Hash { + &self.parent_hash + } + + fn set_parent_hash(&mut self, hash: Self::Hash) { + self.parent_hash = hash + } + + fn digest(&self) -> &Digest { + &self.digest + } + + fn digest_mut(&mut self) -> &mut Digest { + #[cfg(feature = "std")] + log::debug!(target: "header", "Retrieving mutable reference to digest"); + &mut self.digest + } + // We override the default hashing function to use + // a felt aligned pre-image for poseidon hashing. + fn hash(&self) -> Self::Hash { + Header::hash(&self) + } +} + +impl Header +where + Number: Member + + core::hash::Hash + + Copy + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec + + Into + + TryFrom, + Hash: HashT, + Hash::Output: From<[u8; 32]>, +{ + /// Convenience helper for computing the hash of the header without having + /// to import the trait. + pub fn hash(&self) -> Hash::Output { + let max_encoded_felts = 4 * 3 + 1 + 28; // 3 hashout fields + 1 u32 + 28 felts for injective digest encoding + let mut felts = Vec::with_capacity(max_encoded_felts); + + // parent_hash : 32 bytes → 4 felts + felts.extend(unsafe_digest_bytes_to_felts::( + self.parent_hash.as_ref().try_into().expect("hash is 32 bytes"), + )); + + // block number as u64 (compact encoded, but we only need the value) + // constrain the block number to be with u32 range for simplicity + let number = self.number.into(); + felts.push(Goldilocks::from_int(number.as_u32() as u64)); + + // state_root : 32 bytes → 4 felts + felts.extend(unsafe_digest_bytes_to_felts::( + self.state_root.as_ref().try_into().expect("hash is 32 bytes"), + )); + + // extrinsics_root : 32 bytes → 4 felts + felts.extend(unsafe_digest_bytes_to_felts::( + self.extrinsics_root.as_ref().try_into().expect("hash is 32 bytes"), + )); + + // digest – injective encoding + felts.extend(injective_bytes_to_felts::(&self.digest.encode())); + + let poseidon_hash: [u8; 32] = hash_variable_length(felts); + poseidon_hash.into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use qp_poseidon::PoseidonHasher; + use sp_core::H256; + use sp_runtime::{traits::BlakeTwo256, DigestItem}; + + #[test] + fn should_serialize_numbers() { + fn serialize(num: u128) -> String { + let mut v = vec![]; + { + let mut ser = serde_json::Serializer::new(std::io::Cursor::new(&mut v)); + serialize_number(&num, &mut ser).unwrap(); + } + String::from_utf8(v).unwrap() + } + + assert_eq!(serialize(0), "\"0x0\"".to_owned()); + assert_eq!(serialize(1), "\"0x1\"".to_owned()); + assert_eq!(serialize(u64::MAX as u128), "\"0xffffffffffffffff\"".to_owned()); + assert_eq!(serialize(u64::MAX as u128 + 1), "\"0x10000000000000000\"".to_owned()); + } + + #[test] + fn should_deserialize_number() { + fn deserialize(num: &str) -> u128 { + let mut der = serde_json::Deserializer::from_str(num); + deserialize_number(&mut der).unwrap() + } + + assert_eq!(deserialize("\"0x0\""), 0); + assert_eq!(deserialize("\"0x1\""), 1); + assert_eq!(deserialize("\"0xffffffffffffffff\""), u64::MAX as u128); + assert_eq!(deserialize("\"0x10000000000000000\""), u64::MAX as u128 + 1); + } + + #[test] + fn ensure_format_is_unchanged() { + let header = Header:: { + parent_hash: BlakeTwo256::hash(b"1"), + number: 2, + state_root: BlakeTwo256::hash(b"3"), + extrinsics_root: BlakeTwo256::hash(b"4"), + digest: Digest { logs: vec![sp_runtime::generic::DigestItem::Other(b"6".to_vec())] }, + }; + + let header_encoded = header.encode(); + assert_eq!( + header_encoded, + vec![ + 146, 205, 245, 120, 196, 112, 133, 165, 153, 34, 86, 240, 220, 249, 125, 11, 25, + 241, 241, 201, 222, 77, 95, 227, 12, 58, 206, 97, 145, 182, 229, 219, 2, 0, 0, 0, + 88, 19, 72, 51, 123, 15, 62, 20, 134, 32, 23, 61, 170, 165, 249, 77, 0, 216, 129, + 112, 93, 203, 240, 170, 131, 239, 218, 186, 97, 210, 237, 225, 235, 134, 73, 33, + 73, 151, 87, 78, 32, 196, 100, 56, 138, 23, 36, 32, 210, 84, 3, 104, 43, 187, 184, + 12, 73, 104, 49, 200, 204, 31, 143, 13, 4, 0, 4, 54 + ], + ); + assert_eq!(Header::::decode(&mut &header_encoded[..]).unwrap(), header); + + let header = Header:: { + parent_hash: BlakeTwo256::hash(b"1000"), + number: 2000, + state_root: BlakeTwo256::hash(b"3000"), + extrinsics_root: BlakeTwo256::hash(b"4000"), + digest: Digest { logs: vec![sp_runtime::generic::DigestItem::Other(b"5000".to_vec())] }, + }; + + let header_encoded = header.encode(); + assert_eq!( + header_encoded, + vec![ + 197, 243, 254, 225, 31, 117, 21, 218, 179, 213, 92, 6, 247, 164, 230, 25, 47, 166, + 140, 117, 142, 159, 195, 202, 67, 196, 238, 26, 44, 18, 33, 92, 208, 7, 0, 0, 219, + 225, 47, 12, 107, 88, 153, 146, 55, 21, 226, 186, 110, 48, 167, 187, 67, 183, 228, + 232, 118, 136, 30, 254, 11, 87, 48, 112, 7, 97, 31, 82, 146, 110, 96, 87, 152, 68, + 98, 162, 227, 222, 78, 14, 244, 194, 120, 154, 112, 97, 222, 144, 174, 101, 220, + 44, 111, 126, 54, 34, 155, 220, 253, 124, 4, 0, 16, 53, 48, 48, 48 + ], + ); + assert_eq!(Header::::decode(&mut &header_encoded[..]).unwrap(), header); + } + + fn hash_header(x: &[u8]) -> [u8; 32] { + let mut y = x; + if let Ok(header) = Header::::decode(&mut y) { + // Only treat this as a header if we consumed the entire input. + if y.is_empty() { + let max_encoded_felts = 4 * 3 + 1 + 28; // 3 hashout fields + 1 u32 + 28 felts + let mut felts = Vec::with_capacity(max_encoded_felts); + + let parent_hash = header.parent_hash.as_bytes(); + let number = header.number; + let state_root = header.state_root.as_bytes(); + let extrinsics_root = header.extrinsics_root.as_bytes(); + let digest = header.digest.encode(); + + felts.extend(unsafe_digest_bytes_to_felts::( + parent_hash.try_into().expect("Parent hash expected to equal 32 bytes"), + )); + felts.push(Goldilocks::from_int(number as u64)); + felts.extend(unsafe_digest_bytes_to_felts::( + state_root.try_into().expect("State root expected to equal 32 bytes"), + )); + felts.extend(unsafe_digest_bytes_to_felts::( + extrinsics_root.try_into().expect("Extrinsics root expected to equal 32 bytes"), + )); + felts.extend(injective_bytes_to_felts::(&digest)); + + return hash_variable_length(felts); + } + } + // Fallback: canonical bytes hashing for non-header data + PoseidonHasher::hash_padded(x) + } + + #[test] + fn poseidon_header_hash_matches_old_path() { + use codec::Encode; + + // Example header from a real block on devnet + let parent_hash = "839b2d2ac0bf4aa71b18ad1ba5e2880b4ef06452cefacd255cfd76f6ad2c7966"; + let number = 4; + let state_root = "1688817041c572d6c971681465f401f06d0fdcfaed61d28c06d42dc2d07816d5"; + let extrinsics_root = "7c6cace2e91b6314e05410b91224c11f5dd4a4a2dbf0e39081fddbe4ac9ad252"; + let digest = Digest { + logs: vec![ + DigestItem::PreRuntime( + [112, 111, 119, 95], + [ + 233, 182, 183, 107, 158, 1, 115, 19, 219, 126, 253, 86, 30, 208, 176, 70, + 21, 45, 180, 229, 9, 62, 91, 4, 6, 53, 245, 52, 48, 38, 123, 225, + ] + .to_vec(), + ), + DigestItem::Seal( + [112, 111, 119, 95], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 77, 142, + ] + .to_vec(), + ), + ], + }; + let header = Header:: { + parent_hash: H256::from_slice( + hex::decode(parent_hash).expect("valid hex parent hash").as_slice(), + ), + number, + state_root: H256::from_slice( + hex::decode(state_root).expect("valid hex state root").as_slice(), + ), + extrinsics_root: H256::from_slice( + hex::decode(extrinsics_root).expect("valid hex extrinsics root").as_slice(), + ), + digest, + }; + + let encoded = header.encode(); + + let old = hash_header(&encoded); // old path + let new: [u8; 32] = header.hash().into(); + println!("Old hash: 0x{}", hex::encode(old)); + + assert_eq!(old, new); + } +} diff --git a/primitives/wormhole/src/lib.rs b/primitives/wormhole/src/lib.rs index ac25ce16..8bc383eb 100644 --- a/primitives/wormhole/src/lib.rs +++ b/primitives/wormhole/src/lib.rs @@ -3,26 +3,19 @@ extern crate alloc; -use alloc::vec::Vec; +/// Trait for recording transfer proofs in the wormhole pallet. +/// Other pallets can use this to record proofs when they mint/transfer tokens. +pub trait TransferProofRecorder { + /// Error type for proof recording failures + type Error; -/// Trait for managing wormhole transfer proofs. -pub trait TransferProofs { - /// Get transfer proof, if any - fn transfer_proof_exists( - count: TxCount, - from: &AccountId, - to: &AccountId, - value: Balance, - ) -> bool; - - /// Get transfer proof key - fn transfer_proof_key( - count: TxCount, + /// Record a transfer proof for native or asset tokens + /// - `None` for native tokens (asset_id = 0) + /// - `Some(asset_id)` for specific assets + fn record_transfer_proof( + asset_id: Option, from: AccountId, to: AccountId, - value: Balance, - ) -> Vec; - - /// Store transfer proofs for a given wormhole transfer. - fn store_transfer_proof(from: &AccountId, to: &AccountId, value: Balance); + amount: Balance, + ) -> Result<(), Self::Error>; } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 2c1ad93c..9bb0e64b 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -47,10 +47,16 @@ pallet-transaction-payment-rpc-runtime-api.workspace = true pallet-treasury.workspace = true pallet-utility.workspace = true pallet-vesting.workspace = true +pallet-wormhole.workspace = true primitive-types.workspace = true qp-dilithium-crypto.workspace = true +qp-header = { workspace = true, features = ["serde"] } qp-poseidon = { workspace = true, features = ["serde"] } qp-scheduler.workspace = true +qp-wormhole.workspace = true +qp-wormhole-circuit = { workspace = true, default-features = false } +qp-wormhole-verifier = { workspace = true, default-features = false } +qp-zk-circuits-common = { workspace = true, default-features = false } scale-info = { features = ["derive", "serde"], workspace = true } serde_json = { workspace = true, default-features = false, features = [ "alloc", @@ -111,13 +117,14 @@ std = [ "pallet-treasury/std", "pallet-utility/std", "pallet-vesting/std", + "pallet-wormhole/std", "primitive-types/std", "qp-dilithium-crypto/full_crypto", "qp-dilithium-crypto/std", + "qp-header/std", "qp-poseidon/std", "qp-scheduler/std", "scale-info/std", - "scale-info/std", "serde_json/std", "sp-api/std", "sp-block-builder/std", @@ -158,6 +165,7 @@ runtime-benchmarks = [ "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", + "pallet-wormhole/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] @@ -177,6 +185,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-treasury/try-runtime", "pallet-vesting/try-runtime", + "pallet-wormhole/try-runtime", "sp-runtime/try-runtime", ] diff --git a/runtime/src/benchmarks.rs b/runtime/src/benchmarks.rs index c670981c..fe021308 100644 --- a/runtime/src/benchmarks.rs +++ b/runtime/src/benchmarks.rs @@ -34,4 +34,5 @@ frame_benchmarking::define_benchmarks!( [pallet_mining_rewards, MiningRewards] [pallet_scheduler, Scheduler] [pallet_qpow, QPoW] + [pallet_wormhole, Wormhole] ); diff --git a/runtime/src/configs/mod.rs b/runtime/src/configs/mod.rs index e76063a3..5b2e7ec8 100644 --- a/runtime/src/configs/mod.rs +++ b/runtime/src/configs/mod.rs @@ -57,16 +57,16 @@ use qp_poseidon::PoseidonHasher; use qp_scheduler::BlockNumberOrTimestamp; use sp_runtime::{ traits::{AccountIdConversion, ConvertInto, One}, - FixedU128, Perbill, Permill, + AccountId32, FixedU128, Perbill, Permill, }; use sp_version::RuntimeVersion; // Local module imports use super::{ - AccountId, Balance, Balances, Block, BlockNumber, Hash, Nonce, OriginCaller, PalletInfo, - Preimage, Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, - RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Scheduler, System, Timestamp, Vesting, DAYS, - EXISTENTIAL_DEPOSIT, MICRO_UNIT, TARGET_BLOCK_TIME_MS, UNIT, VERSION, + AccountId, Assets, Balance, Balances, Block, BlockNumber, Hash, Nonce, OriginCaller, + PalletInfo, Preimage, Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, + RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Scheduler, System, Timestamp, Vesting, Wormhole, + DAYS, EXISTENTIAL_DEPOSIT, MICRO_UNIT, TARGET_BLOCK_TIME_MS, UNIT, VERSION, }; use sp_core::U512; @@ -130,6 +130,8 @@ parameter_types! { impl pallet_mining_rewards::Config for Runtime { type Currency = Balances; + type AssetId = AssetId; + type ProofRecorder = Wormhole; type WeightInfo = pallet_mining_rewards::weights::SubstrateWeight; type MinerBlockReward = ConstU128<{ 10 * UNIT }>; // 10 tokens type TreasuryBlockReward = ConstU128<0>; // 0 tokens @@ -181,6 +183,7 @@ parameter_types! { } impl pallet_balances::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type WeightInfo = pallet_balances::weights::SubstrateWeight; @@ -621,3 +624,17 @@ impl TryFrom for pallet_assets::Call { } } } + +parameter_types! { + pub WormholeMintingAccount: AccountId = PalletId(*b"wormhole").into_account_truncating(); +} + +impl pallet_wormhole::Config for Runtime { + type MintingAccount = WormholeMintingAccount; + type WeightInfo = (); + type Currency = Balances; + type Assets = Assets; + type TransferCount = u64; + type WormholeAccountId = AccountId32; + type WeightToFee = IdentityFee; +} diff --git a/runtime/src/genesis_config_presets.rs b/runtime/src/genesis_config_presets.rs index d7e835b4..c86a093c 100644 --- a/runtime/src/genesis_config_presets.rs +++ b/runtime/src/genesis_config_presets.rs @@ -19,14 +19,15 @@ #![allow(clippy::expect_used)] use crate::{ - configs::TreasuryPalletId, AccountId, BalancesConfig, RuntimeGenesisConfig, SudoConfig, UNIT, + configs::TreasuryPalletId, AccountId, AssetsConfig, BalancesConfig, RuntimeGenesisConfig, + SudoConfig, EXISTENTIAL_DEPOSIT, UNIT, }; use alloc::{vec, vec::Vec}; use qp_dilithium_crypto::pair::{crystal_alice, crystal_charlie, dilithium_bob}; use serde_json::Value; use sp_core::crypto::Ss58Codec; use sp_genesis_builder::{self, PresetId}; -use sp_runtime::traits::{AccountIdConversion, IdentifyAccount}; +use sp_runtime::traits::{AccountIdConversion, IdentifyAccount, Zero}; /// Identifier for the heisenberg runtime preset. pub const HEISENBERG_RUNTIME_PRESET: &str = "heisenberg"; @@ -62,8 +63,16 @@ fn genesis_template(endowed_accounts: Vec, root: AccountId) -> Value balances.push((treasury_account, ONE_BILLION * UNIT)); let config = RuntimeGenesisConfig { - balances: BalancesConfig { balances }, + balances: BalancesConfig { balances, dev_accounts: None }, sudo: SudoConfig { key: Some(root.clone()) }, + assets: AssetsConfig { + // We need to initialize and reserve the first asset id for the native token transfers + // with wormhole. + assets: vec![(Zero::zero(), root.clone(), false, EXISTENTIAL_DEPOSIT)], /* (asset_id, + * owner, is_sufficient, + * min_balance) */ + ..Default::default() + }, ..Default::default() }; @@ -77,6 +86,7 @@ pub fn development_config_genesis() -> Value { let ss58_version = sp_core::crypto::Ss58AddressFormat::custom(189); for account in endowed_accounts.iter() { log::info!("🍆 Endowed account: {:?}", account.to_ss58check_with_version(ss58_version)); + log::info!("🍆 Endowed account raw: {:?}", account); } genesis_template(endowed_accounts, crystal_alice().into_account()) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 28df014f..876fb79c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -35,7 +35,6 @@ pub mod transaction_extensions; use crate::governance::pallet_custom_origins; use qp_poseidon::PoseidonHasher; - /// 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 @@ -52,7 +51,7 @@ pub mod opaque { // However, some internal checks in dev build expect extrinsics_root to be computed with same // Hash function, so we change the configs/mod.rs Hashing type as well // Opaque block header type. - pub type Header = generic::Header; + pub type Header = qp_header::Header; // Opaque block type. pub type Block = generic::Block; @@ -134,7 +133,7 @@ pub type BlockNumber = u32; pub type Address = MultiAddress; /// Block header type as expected by this runtime. -pub type Header = generic::Header; +pub type Header = qp_header::Header; /// Block type as expected by this runtime. pub type Block = generic::Block; @@ -160,6 +159,7 @@ pub type TxExtension = ( pallet_transaction_payment::ChargeTransactionPayment, frame_metadata_hash_extension::CheckMetadataHash, transaction_extensions::ReversibleTransactionExtension, + transaction_extensions::WormholeProofRecorderExtension, ); /// Unchecked extrinsic type as expected by this runtime. @@ -261,4 +261,7 @@ mod runtime { #[runtime::pallet_index(22)] pub type AssetsHolder = pallet_assets_holder; + + #[runtime::pallet_index(23)] + pub type Wormhole = pallet_wormhole; } diff --git a/runtime/src/transaction_extensions.rs b/runtime/src/transaction_extensions.rs index 5afed1e6..6216b96d 100644 --- a/runtime/src/transaction_extensions.rs +++ b/runtime/src/transaction_extensions.rs @@ -2,12 +2,17 @@ use crate::*; use codec::{Decode, DecodeWithMemTracking, Encode}; use core::marker::PhantomData; -use frame_support::pallet_prelude::{InvalidTransaction, ValidTransaction}; - +use frame_support::pallet_prelude::{ + InvalidTransaction, TransactionValidityError, ValidTransaction, +}; use frame_system::ensure_signed; +use qp_wormhole::TransferProofRecorder; use scale_info::TypeInfo; use sp_core::Get; -use sp_runtime::{traits::TransactionExtension, Weight}; +use sp_runtime::{ + traits::{DispatchInfoOf, PostDispatchInfoOf, StaticLookup, TransactionExtension}, + DispatchResult, Weight, +}; /// Transaction extension for reversible accounts /// @@ -45,7 +50,7 @@ impl _call: &RuntimeCall, _info: &sp_runtime::traits::DispatchInfoOf, _len: usize, - ) -> Result { + ) -> Result { Ok(()) } @@ -59,11 +64,8 @@ impl _inherited_implication: &impl sp_runtime::traits::Implication, _source: frame_support::pallet_prelude::TransactionSource, ) -> sp_runtime::traits::ValidateResult { - let who = ensure_signed(origin.clone()).map_err(|_| { - frame_support::pallet_prelude::TransactionValidityError::Invalid( - InvalidTransaction::BadSigner, - ) - })?; + let who = ensure_signed(origin.clone()) + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::BadSigner))?; if ReversibleTransfers::is_high_security(&who).is_some() { // High-security accounts can only call schedule_transfer and cancel @@ -80,9 +82,7 @@ impl return Ok((ValidTransaction::default(), (), origin)); }, _ => { - return Err(frame_support::pallet_prelude::TransactionValidityError::Invalid( - InvalidTransaction::Custom(1), - )); + return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1))); }, } } @@ -91,6 +91,165 @@ impl } } +/// Details of a transfer to be recorded +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransferDetails { + from: AccountId, + to: AccountId, + amount: Balance, + asset_id: AssetId, +} + +/// Transaction extension that records transfer proofs in the wormhole pallet +/// +/// This extension: +/// - Extracts transfer details from balance/asset transfer calls +/// - Records proofs in wormhole storage after successful execution +/// - Increments transfer count +/// - Emits events +/// - Fails the transaction if proof recording fails +#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo, Debug, DecodeWithMemTracking)] +#[scale_info(skip_type_params(T))] +pub struct WormholeProofRecorderExtension(PhantomData); + +impl WormholeProofRecorderExtension { + /// Creates new extension + pub fn new() -> Self { + Self(PhantomData) + } + + /// Helper to convert lookup errors to transaction validity errors + fn lookup(address: &Address) -> Result { + ::Lookup::lookup(address.clone()) + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::BadSigner)) + } + + /// Extract transfer details from a runtime call + fn extract_transfer_details( + origin: &RuntimeOrigin, + call: &RuntimeCall, + ) -> Result, TransactionValidityError> { + // Only process signed transactions + let who = match ensure_signed(origin.clone()) { + Ok(signer) => signer, + Err(_) => return Ok(None), + }; + + let details = match call { + // Native balance transfers + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest, value }) => { + let to = Self::lookup(dest)?; + Some(TransferDetails { from: who, to, amount: *value, asset_id: 0 }) + }, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest, value }) => { + let to = Self::lookup(dest)?; + Some(TransferDetails { from: who, to, amount: *value, asset_id: 0 }) + }, + RuntimeCall::Balances(pallet_balances::Call::transfer_all { .. }) => None, + + // Asset transfers + RuntimeCall::Assets(pallet_assets::Call::transfer { id, target, amount }) => { + let to = Self::lookup(target)?; + Some(TransferDetails { asset_id: id.0, from: who, to, amount: *amount }) + }, + RuntimeCall::Assets(pallet_assets::Call::transfer_keep_alive { + id, + target, + amount, + }) => { + let to = Self::lookup(target)?; + Some(TransferDetails { asset_id: id.0, from: who, to, amount: *amount }) + }, + + _ => None, + }; + + Ok(details) + } + + /// Record the transfer proof using the TransferProofRecorder trait + fn record_proof(details: TransferDetails) -> Result<(), TransactionValidityError> { + let asset_id = if details.asset_id == 0 { None } else { Some(details.asset_id) }; + + >::record_transfer_proof( + asset_id, + details.from, + details.to, + details.amount, + ) + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Custom(100))) + } +} + +impl TransactionExtension + for WormholeProofRecorderExtension +{ + type Pre = Option; + type Val = (); + type Implicit = (); + + const IDENTIFIER: &'static str = "WormholeProofRecorderExtension"; + + fn weight(&self, call: &RuntimeCall) -> Weight { + // Account for proof recording in post_dispatch + match call { + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { .. }) | + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) | + RuntimeCall::Assets(pallet_assets::Call::transfer { .. }) | + RuntimeCall::Assets(pallet_assets::Call::transfer_keep_alive { .. }) => { + // 2 writes: TransferProof insert + TransferCount update + // 1 read: TransferCount get + T::DbWeight::get().reads_writes(1, 2) + }, + _ => Weight::zero(), + } + } + + fn prepare( + self, + _val: Self::Val, + origin: &sp_runtime::traits::DispatchOriginOf, + call: &RuntimeCall, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> Result { + // Extract transfer details to pass to post_dispatch + Self::extract_transfer_details(origin, call) + } + + fn validate( + &self, + _origin: sp_runtime::traits::DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl sp_runtime::traits::Implication, + _source: frame_support::pallet_prelude::TransactionSource, + ) -> sp_runtime::traits::ValidateResult { + // No validation needed - just return Ok + Ok((ValidTransaction::default(), (), _origin)) + } + + fn post_dispatch( + pre: Self::Pre, + _info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + // Only record proof if the transaction succeeded (no error in post_info) + if post_info.actual_weight.is_some() || _result.is_ok() { + if let Some(details) = pre { + // Record the proof - if this fails, fail the whole transaction + Self::record_proof(details)?; + } + } + + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -117,6 +276,7 @@ mod tests { (bob(), EXISTENTIAL_DEPOSIT * 2), (charlie(), EXISTENTIAL_DEPOSIT * 100), ], + dev_accounts: None, } .assimilate_storage(&mut t) .unwrap(); @@ -326,8 +486,73 @@ mod tests { RuntimeCall::ReversibleTransfers(pallet_reversible_transfers::Call::cancel { tx_id: sp_core::H256::default(), }); - // High-security accounts can call cancel assert_ok!(check_call(call)); }); } + + #[test] + fn wormhole_proof_recorder_native_transfer() { + new_test_ext().execute_with(|| { + let alice_origin = RuntimeOrigin::signed(alice()); + let call = RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { + dest: MultiAddress::Id(bob()), + value: 100 * UNIT, + }); + + let details = WormholeProofRecorderExtension::::extract_transfer_details( + &alice_origin, + &call, + ) + .unwrap(); + + assert!(details.is_some()); + let details = details.unwrap(); + assert_eq!(details.from, alice()); + assert_eq!(details.to, bob()); + assert_eq!(details.amount, 100 * UNIT); + assert_eq!(details.asset_id, 0); + }); + } + + #[test] + fn wormhole_proof_recorder_asset_transfer() { + new_test_ext().execute_with(|| { + let alice_origin = RuntimeOrigin::signed(alice()); + let asset_id = 42u32; + let call = RuntimeCall::Assets(pallet_assets::Call::transfer { + id: codec::Compact(asset_id), + target: MultiAddress::Id(bob()), + amount: 500, + }); + + let details = WormholeProofRecorderExtension::::extract_transfer_details( + &alice_origin, + &call, + ) + .unwrap(); + + assert!(details.is_some()); + let details = details.unwrap(); + assert_eq!(details.from, alice()); + assert_eq!(details.to, bob()); + assert_eq!(details.amount, 500); + assert_eq!(details.asset_id, asset_id); + }); + } + + #[test] + fn wormhole_proof_recorder_ignores_non_transfer() { + new_test_ext().execute_with(|| { + let alice_origin = RuntimeOrigin::signed(alice()); + let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![1, 2, 3] }); + + let details = WormholeProofRecorderExtension::::extract_transfer_details( + &alice_origin, + &call, + ) + .unwrap(); + + assert!(details.is_none()); + }); + } } diff --git a/runtime/tests/governance/treasury.rs b/runtime/tests/governance/treasury.rs index 3b4fe933..3218957a 100644 --- a/runtime/tests/governance/treasury.rs +++ b/runtime/tests/governance/treasury.rs @@ -88,9 +88,12 @@ mod tests { pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: self.balances } - .assimilate_storage(&mut t) - .unwrap(); + pallet_balances::GenesisConfig:: { + balances: self.balances, + dev_accounts: None, + } + .assimilate_storage(&mut t) + .unwrap(); // Pallet Treasury genesis (optional, as we fund it manually) // If your pallet_treasury::GenesisConfig needs setup, do it here.