diff --git a/.github/workflows/ci-basic.yml b/.github/workflows/ci-basic.yml index 1d273173929..31000553d65 100644 --- a/.github/workflows/ci-basic.yml +++ b/.github/workflows/ci-basic.yml @@ -42,7 +42,23 @@ jobs: - name: Run format check run: cargo fmt -- --check - name: Run clippy - run: cargo clippy --workspace --no-deps --all-features --all-targets --locked -- -D warnings + run: | + # After upgrading from Rust 1.82 to 1.85.1, Clippy started failing on new lints in upstream + # code we don’t want to patch here (and may already be fixed upstream). To avoid carrying + # fixes and merge conflicts, we allow these lints only for the affected crates for now. + # TODO: remove this comment and these Clippy exceptions after syncing with updated upstream. + cargo clippy --workspace --no-deps --all-features --all-targets --locked -- \ + -D warnings \ + -A elided_named_lifetimes \ + -A missing_docs \ + -A non_local_definitions \ + -A clippy::needless_lifetimes \ + -A clippy::needless_return \ + -A clippy::unnecessary_lazy_evaluations \ + -A clippy::unnecessary_map_or \ + -A clippy::needless_as_bytes \ + -A clippy::useless_conversion + - name: Show system resource summary run: | df -h diff --git a/Cargo.lock b/Cargo.lock index 29530543715..f71b844e33e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,12 +339,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.11.0" @@ -501,13 +495,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.2.6", ] [[package]] @@ -518,7 +512,7 @@ checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.3.0", ] [[package]] @@ -954,6 +948,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1079,18 +1079,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -1245,7 +1233,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", "crypto-common 0.1.6", ] @@ -1296,18 +1283,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "signature", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -1361,24 +1336,6 @@ dependencies = [ "void", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -1413,7 +1370,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.2.2" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "blake2b_simd", "core2", @@ -1449,7 +1406,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "blake2b_simd", ] @@ -1665,7 +1622,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1803,8 +1759,9 @@ dependencies = [ [[package]] name = "halo2_gadgets" -version = "0.3.1" -source = "git+https://github.com/zcash/halo2?rev=2308caf68c48c02468b66cfc452dad54e355e32f#2308caf68c48c02468b66cfc452dad54e355e32f" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45824ce0dd12e91ec0c68ebae2a7ed8ae19b70946624c849add59f1d1a62a143" dependencies = [ "arrayvec", "bitvec", @@ -2486,19 +2443,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", - "sha2 0.10.8", - "signature", -] - [[package]] name = "known-folders" version = "1.1.0" @@ -2822,12 +2766,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - [[package]] name = "nonempty" version = "0.11.0" @@ -2950,7 +2888,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchard" version = "0.11.0" -source = "git+https://github.com/QED-it/orchard?rev=2083efe8d57e6073914ae296db2d41f8bfe1de50#2083efe8d57e6073914ae296db2d41f8bfe1de50" +source = "git+https://github.com/QED-it/orchard?rev=7ec34c9be7d36ecdce7c3658efe9a78c2863ed97#7ec34c9be7d36ecdce7c3658efe9a78c2863ed97" dependencies = [ "aes", "bitvec", @@ -2965,15 +2903,15 @@ dependencies = [ "halo2_proofs", "hex", "incrementalmerkletree", - "k256", "lazy_static", "memuse", - "nonempty 0.11.0", + "nonempty", "pasta_curves", "proptest", "rand 0.8.5", "rand_core 0.6.4", "reddsa", + "secp256k1 0.29.1", "serde", "sinsemilla", "subtle", @@ -4050,19 +3988,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "subtle", - "zeroize", -] - [[package]] name = "secp256k1" version = "0.27.0" @@ -4079,6 +4004,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ + "rand 0.8.5", "secp256k1-sys 0.10.1", ] @@ -4425,7 +4351,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", "rand_core 0.6.4", ] @@ -5928,7 +5853,7 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "zcash_address" version = "0.9.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "bech32", "bs58", @@ -5941,7 +5866,7 @@ dependencies = [ [[package]] name = "zcash_client_backend" version = "0.20.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "base64 0.22.1", "bech32", @@ -5954,7 +5879,7 @@ dependencies = [ "hex", "incrementalmerkletree", "memuse", - "nonempty 0.11.0", + "nonempty", "percent-encoding", "prost", "rand_core 0.6.4", @@ -5983,16 +5908,16 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.3.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "core2", - "nonempty 0.11.0", + "nonempty", ] [[package]] name = "zcash_history" version = "0.4.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "blake2b_simd", "byteorder", @@ -6002,7 +5927,7 @@ dependencies = [ [[package]] name = "zcash_keys" version = "0.11.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "bech32", "blake2b_simd", @@ -6012,7 +5937,7 @@ dependencies = [ "document-features", "group", "memuse", - "nonempty 0.11.0", + "nonempty", "rand_core 0.6.4", "sapling-crypto", "secrecy", @@ -6040,7 +5965,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.25.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "bip32", "blake2b_simd", @@ -6059,7 +5984,7 @@ dependencies = [ "jubjub", "lazy_static", "memuse", - "nonempty 0.11.0", + "nonempty", "orchard", "rand 0.8.5", "rand_core 0.6.4", @@ -6082,7 +6007,7 @@ dependencies = [ [[package]] name = "zcash_proofs" version = "0.25.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "bellman", "blake2b_simd", @@ -6104,7 +6029,7 @@ dependencies = [ [[package]] name = "zcash_protocol" version = "0.6.2" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "core2", "document-features", @@ -6139,8 +6064,7 @@ dependencies = [ [[package]] name = "zcash_spec" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded3f58b93486aa79b85acba1001f5298f27a46489859934954d262533ee2915" +source = "git+https://github.com/QED-it/zcash_spec?rev=d5e84264d2ad0646b587a837f4e2424ca64d3a05#d5e84264d2ad0646b587a837f4e2424ca64d3a05" dependencies = [ "blake2b_simd", ] @@ -6148,7 +6072,7 @@ dependencies = [ [[package]] name = "zcash_transparent" version = "0.5.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "bip32", "blake2b_simd", @@ -6196,7 +6120,6 @@ dependencies = [ "itertools 0.13.0", "jubjub", "lazy_static", - "nonempty 0.7.0", "num-integer", "orchard", "primitive-types", @@ -6690,7 +6613,7 @@ dependencies = [ [[package]] name = "zip321" version = "0.5.0" -source = "git+https://github.com/QED-it/librustzcash?rev=e27124a49a0e549227e12adf13e42f88593f1dee#e27124a49a0e549227e12adf13e42f88593f1dee" +source = "git+https://github.com/QED-it/librustzcash?rev=acd68d123997ea9788c14244bc3805c6d2e8d68c#acd68d123997ea9788c14244bc3805c6d2e8d68c" dependencies = [ "base64 0.22.1", "nom", diff --git a/Cargo.toml b/Cargo.toml index c6d6f3e6d6a..db6e5405298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ resolver = "2" [workspace.dependencies] incrementalmerkletree = "0.8.2" -orchard = "0.11.0" +orchard = { version = "0.11.0", features = ["zsa-issuance", "temporary-zebra"] } sapling-crypto = "0.5" # The dependency versions below are in accordance with the currently used orchard version. zcash_history = "0.4.0" @@ -31,7 +31,7 @@ zcash_address = "0.9.0" zcash_client_backend = "0.20.0" zcash_encoding = "0.3.0" zcash_keys = "0.11.0" -zcash_primitives = "0.25.0" +zcash_primitives = { version = "0.25.0", features = ["zsa-issuance", "zip-233"] } zcash_proofs = "0.25.0" zcash_protocol = "0.6.2" zcash_transparent = "0.5.0" @@ -108,19 +108,19 @@ lto = "thin" [patch.crates-io] halo2_proofs = { version = "0.3.0", git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } -halo2_gadgets = { version = "0.3.0", git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } halo2_poseidon = { version = "0.1.0", git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } sinsemilla = { git = "https://github.com/zcash/sinsemilla", rev = "aabb707e862bc3d7b803c77d14e5a771bcee3e8c" } zcash_note_encryption = { version = "0.4.1", git = "https://github.com/zcash/zcash_note_encryption", rev = "9f7e93d42cef839d02b9d75918117941d453f8cb" } sapling-crypto = { package = "sapling-crypto", version = "0.5", git = "https://github.com/QED-it/sapling-crypto", rev = "9393f93fe547d1b3738c9f4618c0f8a2fffed29f" } -orchard = { version = "0.11.0", git = "https://github.com/QED-it/orchard", rev = "2083efe8d57e6073914ae296db2d41f8bfe1de50" } -zcash_primitives = { version = "0.25.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_protocol = { version = "0.6.2", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_address = { version = "0.9.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_encoding = { version = "0.3.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_history = { version = "0.4.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_client_backend = { version = "0.20.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_keys = { version = "0.11.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_transparent = { version = "0.5.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -zcash_proofs = { version = "0.25.0", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } -equihash = { version = "0.2.2", git = "https://github.com/QED-it/librustzcash", rev = "e27124a49a0e549227e12adf13e42f88593f1dee" } +orchard = { version = "0.11.0", git = "https://github.com/QED-it/orchard", rev = "7ec34c9be7d36ecdce7c3658efe9a78c2863ed97" } +zcash_primitives = { version = "0.25.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_protocol = { version = "0.6.2", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_address = { version = "0.9.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_encoding = { version = "0.3.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_history = { version = "0.4.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_client_backend = { version = "0.20.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_keys = { version = "0.11.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_transparent = { version = "0.5.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_proofs = { version = "0.25.0", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +equihash = { version = "0.2.2", git = "https://github.com/QED-it/librustzcash", rev = "acd68d123997ea9788c14244bc3805c6d2e8d68c" } +zcash_spec = { git = "https://github.com/QED-it/zcash_spec", rev = "d5e84264d2ad0646b587a837f4e2424ca64d3a05" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dd1c8aa4359..8a487d845cb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ # TODO: Upstream specifies `channel = "stable"` — consider restoring it before final merge. [toolchain] -channel = "1.82.0" +channel = "1.85.1" diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index f202955e0b4..c5b9ceeaa16 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -62,9 +62,7 @@ proptest-impl = [ bench = ["zebra-test"] # Support for transaction version 6 -tx_v6 = [ - "nonempty" -] +tx_v6 = [] [dependencies] @@ -72,7 +70,7 @@ tx_v6 = [ bitvec = "1.0.1" bitflags = "2.5.0" bitflags-serde-legacy = "0.1.1" -blake2b_simd = "1.0.2" +blake2b_simd = "=1.0.1" blake2s_simd = "1.0.2" bridgetree = "0.7.0" bs58 = { version = "0.5.1", features = ["check"] } @@ -112,9 +110,6 @@ zcash_address.workspace = true zcash_transparent.workspace = true zcash_script.workspace = true -# Used for orchard serialization -nonempty = { version = "0.7", optional = true } - # Time chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "serde"] } humantime = "2.1.0" diff --git a/zebra-chain/src/orchard/arbitrary.rs b/zebra-chain/src/orchard/arbitrary.rs index dab7cbb2840..a77ed1d1ae0 100644 --- a/zebra-chain/src/orchard/arbitrary.rs +++ b/zebra-chain/src/orchard/arbitrary.rs @@ -126,7 +126,15 @@ impl Arbitrary for Flags { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::()).prop_map(Self::from_bits_truncate).boxed() + (any::()) + .prop_map(|byte| { + // Clear ENABLE_ZSA: it is only allowed in V6, and this generator is + // also used for V5 cases where the flag would make deserialization fail. + #[cfg(feature = "tx_v6")] + let byte = byte & !(Flags::ENABLE_ZSA.bits()); + Self::from_bits_truncate(byte) + }) + .boxed() } type Strategy = BoxedStrategy; diff --git a/zebra-chain/src/orchard/commitment.rs b/zebra-chain/src/orchard/commitment.rs index c97e5053f20..d483f7a769d 100644 --- a/zebra-chain/src/orchard/commitment.rs +++ b/zebra-chain/src/orchard/commitment.rs @@ -236,7 +236,6 @@ impl ValueCommitment { /// Generate a new _ValueCommitment_. /// /// - #[allow(clippy::unwrap_in_result)] pub fn randomized(csprng: &mut T, value: Amount) -> Result where T: RngCore + CryptoRng, @@ -244,14 +243,7 @@ impl ValueCommitment { let rcv = generate_trapdoor(csprng)?; #[cfg(feature = "tx_v6")] - let vc = Self::new( - rcv, - // TODO: Make the `ValueSum::from_raw` function public in the `orchard` crate - // and use `ValueSum::from_raw(value.into())` instead of the next line. - // Remove `#[allow(clippy::unwrap_in_result)]` after doing so. - (ValueSum::default() + i64::from(value)).unwrap(), - AssetBase::native(), - ); + let vc = Self::new(rcv, ValueSum::from_raw(value.into()), AssetBase::zatoshi()); #[cfg(not(feature = "tx_v6"))] let vc = Self::new(rcv, value); diff --git a/zebra-chain/src/orchard/shielded_data.rs b/zebra-chain/src/orchard/shielded_data.rs index 2acf2c20519..c89671983f2 100644 --- a/zebra-chain/src/orchard/shielded_data.rs +++ b/zebra-chain/src/orchard/shielded_data.rs @@ -133,11 +133,8 @@ impl ShieldedData { let key = { let cv_balance = ValueCommitment::new( pallas::Scalar::zero(), - // TODO: Make the `ValueSum::from_raw` function public in the `orchard` crate - // and use `ValueSum::from_raw(self.value_balance.into())` instead of the - // next line - (ValueSum::default() + i64::from(self.value_balance)).unwrap(), - AssetBase::native(), + ValueSum::from_raw(self.value_balance.into()), + AssetBase::zatoshi(), ); let burn_value_commitment = compute_burn_value_commitment(self.burn.as_ref()); cv - cv_balance - burn_value_commitment @@ -283,7 +280,7 @@ bitflags! { /// # Consensus /// /// > [NU5 onward] In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard - /// > field MUST be zero. + /// > field MUST be zero. Bit 2 (ENABLE_ZSA) is introduced in V6 (NU7, ZIP 230). /// /// /// @@ -298,7 +295,7 @@ bitflags! { /// Enable creating new non-zero valued Orchard notes. const ENABLE_OUTPUTS = 0b00000010; /// Enable ZSA transaction (otherwise all notes within actions must use native asset). - // FIXME: Should we use this flag explicitly anywhere in Zebra? + #[cfg(feature = "tx_v6")] const ENABLE_ZSA = 0b00000100; } } diff --git a/zebra-chain/src/orchard/shielded_data_flavor.rs b/zebra-chain/src/orchard/shielded_data_flavor.rs index 11ef89df399..392971749cf 100644 --- a/zebra-chain/src/orchard/shielded_data_flavor.rs +++ b/zebra-chain/src/orchard/shielded_data_flavor.rs @@ -4,12 +4,12 @@ use std::fmt::Debug; use serde::{de::DeserializeOwned, Serialize}; -use orchard::{orchard_flavor::OrchardFlavor, primitives::OrchardPrimitives}; +use orchard::{flavor::OrchardFlavor, primitives::OrchardPrimitives}; -pub use orchard::orchard_flavor::OrchardVanilla; +pub use orchard::flavor::OrchardVanilla; #[cfg(feature = "tx_v6")] -pub use orchard::{note::AssetBase, orchard_flavor::OrchardZSA, value::NoteValue}; +pub use orchard::{flavor::OrchardZSA, note::AssetBase, value::NoteValue}; use crate::serialization::{ZcashDeserialize, ZcashSerialize}; @@ -52,6 +52,7 @@ pub trait ShieldedDataFlavor: OrchardFlavor { /// A type representing a burn field for this protocol version. #[cfg(feature = "tx_v6")] type BurnType: Clone + + Default + Debug + ZcashDeserialize + ZcashSerialize diff --git a/zebra-chain/src/orchard_zsa.rs b/zebra-chain/src/orchard_zsa.rs index a90c115a9b3..a8ea0f5924f 100644 --- a/zebra-chain/src/orchard_zsa.rs +++ b/zebra-chain/src/orchard_zsa.rs @@ -3,8 +3,16 @@ #[cfg(any(test, feature = "proptest-impl"))] mod arbitrary; +mod asset_state; mod burn; mod issuance; -pub(crate) use burn::{compute_burn_value_commitment, Burn, BurnItem, NoBurn}; +pub(crate) use burn::{compute_burn_value_commitment, Burn, NoBurn}; pub(crate) use issuance::IssueData; + +pub use burn::BurnItem; + +pub use asset_state::{AssetBase, AssetState, AssetStateError, IssuedAssetChanges}; + +#[cfg(any(test, feature = "proptest-impl"))] +pub use asset_state::testing::{mock_asset_base, mock_asset_state}; diff --git a/zebra-chain/src/orchard_zsa/arbitrary.rs b/zebra-chain/src/orchard_zsa/arbitrary.rs index 95091114260..7e68d40248b 100644 --- a/zebra-chain/src/orchard_zsa/arbitrary.rs +++ b/zebra-chain/src/orchard_zsa/arbitrary.rs @@ -15,7 +15,7 @@ impl Arbitrary for BurnItem { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - BundleArb::::arb_asset_to_burn() + BundleArb::::arb_asset_to_burn() .prop_map(|(asset_base, value)| BurnItem::from((asset_base, value))) .boxed() } diff --git a/zebra-chain/src/orchard_zsa/asset_state.rs b/zebra-chain/src/orchard_zsa/asset_state.rs new file mode 100644 index 00000000000..785ff74845c --- /dev/null +++ b/zebra-chain/src/orchard_zsa/asset_state.rs @@ -0,0 +1,426 @@ +//! Defines and implements the issued asset state types + +use byteorder::{ReadBytesExt, WriteBytesExt}; +use std::{ + collections::{BTreeMap, HashMap}, + io, + sync::Arc, +}; +use thiserror::Error; + +pub use orchard::note::AssetBase; +use orchard::{ + bundle::burn_validation::{validate_bundle_burn, BurnError}, + issuance::{ + check_issue_bundle_without_sighash, verify_issue_bundle, AssetRecord, Error as IssueError, + }, + note::Nullifier, + value::NoteValue, + Note, +}; + +use zcash_primitives::transaction::components::issuance::{read_note, write_note}; + +use crate::transaction::{SigHash, Transaction}; + +/// Wraps orchard's AssetRecord for use in zebra state management. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AssetState(AssetRecord); + +impl AssetState { + /// Creates a new [`AssetRecord`] instance. + pub fn new(amount: NoteValue, is_finalized: bool, reference_note: Note) -> Self { + Self(AssetRecord::new(amount, is_finalized, reference_note)) + } + + /// Deserializes a new [`AssetState`] from its canonical byte encoding. + pub fn from_bytes(bytes: &[u8]) -> Result { + use std::io::{Cursor, Read}; + + let mut reader = Cursor::new(bytes); + let mut amount_bytes = [0; 8]; + reader.read_exact(&mut amount_bytes)?; + + let is_finalized = match reader.read_u8()? { + 0 => false, + 1 => true, + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid is_finalized", + )) + } + }; + + let mut asset_bytes = [0u8; 32]; + reader.read_exact(&mut asset_bytes)?; + let asset = Option::from(AssetBase::from_bytes(&asset_bytes)) + .ok_or(io::Error::new(io::ErrorKind::InvalidData, "Invalid asset"))?; + + let reference_note = read_note(reader, asset)?; + + Ok(AssetState(AssetRecord::new( + NoteValue::from_bytes(amount_bytes), + is_finalized, + reference_note, + ))) + } + + /// Serializes [`AssetState`] to its canonical byte encoding. + pub fn to_bytes(&self) -> Result, io::Error> { + use std::io::Write; + + let mut bytes = Vec::new(); + bytes.write_all(&self.0.amount.to_bytes())?; + bytes.write_u8(self.0.is_finalized as u8)?; + bytes.write_all(&self.0.reference_note.asset().to_bytes())?; + write_note(&mut bytes, &self.0.reference_note)?; + Ok(bytes) + } + + /// Returns whether the asset is finalized. + #[cfg(any(test, feature = "proptest-impl"))] + pub fn is_finalized(&self) -> bool { + self.0.is_finalized + } + + /// Returns the total supply. + #[cfg(any(test, feature = "proptest-impl"))] + pub fn total_supply(&self) -> u64 { + self.0.amount.inner() + } +} + +impl From for AssetState { + fn from(record: AssetRecord) -> Self { + Self(record) + } +} + +// Needed for the new `getassetstate` RPC endpoint in `zebra-rpc`. +// Can't derive `Serialize` here as `orchard::AssetRecord` doesn't implement it. +impl serde::Serialize for AssetState { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::{Error as _, SerializeStruct}; + + // "3" is the expected number of struct fields (a hint for pre-allocation). + let mut st = serializer.serialize_struct("AssetState", 3)?; + + let inner = &self.0; + st.serialize_field("amount", &inner.amount.inner())?; + st.serialize_field("is_finalized", &inner.is_finalized)?; + + let mut note_bytes = Vec::::new(); + write_note(&mut note_bytes, &inner.reference_note).map_err(S::Error::custom)?; + st.serialize_field("reference_note", &hex::encode(note_bytes))?; + + st.end() + } +} + +/// Errors returned when validating asset state updates. +#[derive(Debug, Error, Clone, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum AssetStateError { + #[error("issuance validation failed: {0}")] + Issue(IssueError), + + #[error("burn validation failed: {0}")] + Burn(BurnError), + + #[error("invalid input: {0}")] + InvalidInput(String), +} + +/// A map of asset state changes for assets modified in a block or transaction set. +/// Contains `(old_state, new_state)` pairs for each modified asset. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct IssuedAssetChanges(HashMap, AssetState)>); + +/// Apply validator output to the mutable state map. +fn apply_updates( + states: &mut HashMap, AssetState)>, + updates: BTreeMap, +) { + use std::collections::hash_map::Entry; + + for (asset, record) in updates { + match states.entry(asset) { + Entry::Occupied(mut entry) => entry.get_mut().1 = AssetState::from(record), + Entry::Vacant(entry) => { + entry.insert((None, AssetState::from(record))); + } + } + } +} + +impl IssuedAssetChanges { + /// Validates burns and issuances across transactions, returning the map of changes. + /// + /// # Signature Verification Modes + /// + /// - **With `transaction_sighashes` (Some)**: Full validation for Contextually Verified Blocks + /// from the consensus workflow. Performs signature verification using `verify_issue_bundle`. + /// + /// - **Without `transaction_sighashes` (None)**: Trusted validation for Checkpoint Verified Blocks + /// loaded during bootstrap/startup from disk. These blocks are within checkpoint ranges and + /// are considered trusted, so signature verification is skipped using `check_issue_bundle_without_sighash`. + #[allow(clippy::unwrap_in_result)] + pub fn validate_and_get_changes( + transactions: &[Arc], + transaction_sighashes: Option<&[SigHash]>, + get_state: impl Fn(&AssetBase) -> Option, + ) -> Result { + if let Some(sighashes) = transaction_sighashes { + if transactions.len() != sighashes.len() { + return Err(AssetStateError::InvalidInput(format!( + "transaction count ({}) does not match sighash count ({})", + transactions.len(), + sighashes.len(), + ))); + } + } + + // Track old and current states - old_state is None for newly created assets + let mut states = HashMap::, AssetState)>::new(); + + for (i, tx) in transactions.iter().enumerate() { + // Validate and apply burns + if let Some(burn) = tx.orchard_burns() { + let burn_records = validate_bundle_burn( + burn.iter() + .map(|burn_item| <(AssetBase, NoteValue)>::from(*burn_item)), + |asset| Self::get_or_cache_record(&mut states, asset, &get_state), + ) + .map_err(AssetStateError::Burn)?; + apply_updates(&mut states, burn_records); + } + + // Validate and apply issuances + if let Some(issue_data) = tx.orchard_issue_data() { + // ZIP-0227 defines issued-note rho as DeriveIssuedRho(nf_{0,0}, i_action, i_note), + // so we must pass the first Action nullifier (nf_{0,0}). We rely on + // `orchard_nullifiers()` preserving Action order, so `.next()` returns nf_{0,0}. + // Nullifier type conversion via bytes: both types wrap pallas::Point + // but lack a direct conversion path in the current orchard API. + // TODO: Consider adding a test for the case where a V6 transaction has issuance data + // but has no nullifiers (the test may require constructing a proper mock V6 transaction). + let raw_nullifier = tx.orchard_nullifiers().next().ok_or_else(|| { + AssetStateError::InvalidInput( + "issuance bundle has no orchard actions".to_string(), + ) + })?; + let first_nullifier = &Nullifier::from_bytes(&<[u8; 32]>::from(*raw_nullifier)) + .expect("valid zebra nullifier bytes convert to orchard nullifier"); + + let issue_records = match transaction_sighashes { + Some(sighashes) => { + // Full verification with signature check (Contextually Verified Block) + verify_issue_bundle( + issue_data.inner(), + *sighashes[i].as_ref(), + |asset| Self::get_or_cache_record(&mut states, asset, &get_state), + first_nullifier, + ) + .map_err(AssetStateError::Issue)? + } + None => { + // Trusted verification without signature check (Checkpoint Verified Block) + check_issue_bundle_without_sighash( + issue_data.inner(), + |asset| Self::get_or_cache_record(&mut states, asset, &get_state), + first_nullifier, + ) + .map_err(AssetStateError::Issue)? + } + }; + + apply_updates(&mut states, issue_records); + } + } + + Ok(IssuedAssetChanges(states)) + } + + /// Gets current record from cache or fetches and caches it. + fn get_or_cache_record( + states: &mut HashMap, AssetState)>, + asset: &AssetBase, + get_state: &impl Fn(&AssetBase) -> Option, + ) -> Option { + use std::collections::hash_map::Entry; + + match states.entry(*asset) { + Entry::Occupied(entry) => Some(entry.get().1 .0), + Entry::Vacant(entry) => { + let state = get_state(asset)?; + entry.insert((Some(state), state)); + Some(state.0) + } + } + } + + /// Gets an iterator over `IssuedAssetChanges` inner `HashMap` elements. + pub fn iter(&self) -> impl Iterator, AssetState))> { + self.0.iter() + } +} + +impl From> for IssuedAssetChanges { + fn from(issued: HashMap) -> Self { + IssuedAssetChanges( + issued + .into_iter() + .map(|(base, state)| (base, (None, state))) + .collect(), + ) + } +} + +#[cfg(any(test, feature = "proptest-impl"))] +/// Test utilities for creating mock asset states and bases, used in zebra-rpc tests. +pub mod testing { + use super::AssetState; + + use orchard::{ + issuance::{ + auth::{IssueAuthKey, IssueValidatingKey, ZSASchnorr}, + compute_asset_desc_hash, IssueBundle, + }, + note::{AssetBase, AssetId, Nullifier}, + value::NoteValue, + }; + + use group::{ff::PrimeField, Curve, Group}; + use halo2::{arithmetic::CurveAffine, pasta::pallas}; + use rand::{RngCore, SeedableRng}; + use rand_chacha::ChaChaRng; + + const TEST_RNG_SEED: u64 = 0; + + fn hash_asset_desc(desc: &[u8]) -> [u8; 32] { + let (first, rest) = desc + .split_first() + .expect("asset description must be non-empty"); + compute_asset_desc_hash(&(*first, rest.to_vec()).into()) + } + + fn random_bytes(rng: &mut impl RngCore) -> [u8; N] { + let mut bytes = [0u8; N]; + rng.fill_bytes(&mut bytes); + bytes + } + + // Coordinate extractor for Pallas (nu5.pdf, § 5.4.9.7), used to create a nullifier. + fn extract_p(point: &pallas::Point) -> pallas::Base { + point + .to_affine() + .coordinates() + .map(|c| *c.x()) + .unwrap_or_else(pallas::Base::zero) + } + + fn dummy_nullifier(rng: impl RngCore) -> Nullifier { + Nullifier::from_bytes(&extract_p(&pallas::Point::random(rng)).to_repr()) + .expect("pallas x-coordinate is a valid nullifier") + } + + fn create_issue_keys( + rng: &mut (impl RngCore + rand::CryptoRng), + ) -> (IssueAuthKey, IssueValidatingKey) { + let isk = IssueAuthKey::::random(rng); + let ik = IssueValidatingKey::::from(&isk); + (isk, ik) + } + + // Creates a reference note whose `rho` is set, making it serializable via `AssetState::to_bytes`. + fn create_reference_note_with_rho( + asset_desc: &[u8], + rng: &mut (impl RngCore + rand::CryptoRng), + ) -> orchard::Note { + let (isk, ik) = create_issue_keys(&mut *rng); + let desc_hash = hash_asset_desc(asset_desc); + + let sighash = random_bytes::<32>(rng); + let first_nullifier = dummy_nullifier(&mut *rng); + let (bundle, _) = IssueBundle::new(ik, desc_hash, None, true, &mut *rng); + + let signed_bundle = bundle + .update_rho(&first_nullifier, rng) + .prepare(sighash) + .sign(&isk) + .expect("signing a freshly-created bundle must succeed"); + + *signed_bundle + .actions() + .first() + .get_reference_note() + .expect("first action of IssueBundle always has a reference note") + } + + /// Returns a deterministic [`AssetBase`] for the given description. + pub fn mock_asset_base(desc: &[u8]) -> AssetBase { + let mut rng = ChaChaRng::seed_from_u64(TEST_RNG_SEED); + let (_, ik) = create_issue_keys(&mut rng); + AssetBase::custom(&AssetId::new_v0(&ik, &hash_asset_desc(desc))) + } + + /// Returns a deterministic [`AssetState`] for use in tests. + pub fn mock_asset_state( + asset_desc: &[u8], + total_supply: u64, + is_finalized: bool, + ) -> AssetState { + let mut rng = ChaChaRng::seed_from_u64(TEST_RNG_SEED); + let reference_note = create_reference_note_with_rho(asset_desc, &mut rng); + AssetState::new( + NoteValue::from_bytes(total_supply.to_le_bytes()), + is_finalized, + reference_note, + ) + } +} + +#[cfg(test)] +mod tests { + use super::{testing::mock_asset_state, *}; + + #[test] + fn asset_state_roundtrip_serialization() { + let state = mock_asset_state(b"test_asset", 1000, false); + + let bytes = state.to_bytes().unwrap(); + let decoded = AssetState::from_bytes(&bytes).unwrap(); + + assert_eq!(state, decoded); + } + + #[test] + fn asset_state_finalized_roundtrip() { + let state = mock_asset_state(b"finalized", 5000, true); + + let bytes = state.to_bytes().unwrap(); + let decoded = AssetState::from_bytes(&bytes).unwrap(); + + assert!(decoded.is_finalized()); + assert_eq!(decoded.total_supply(), 5000); + } + + #[test] + fn read_asset_state_invalid_finalized_byte() { + let mut bytes = vec![0u8; 8]; // amount + bytes.push(2); // invalid is_finalized (not 0 or 1) + + let result = AssetState::from_bytes(&bytes); + assert!(result.is_err()); + } + + #[test] + fn issued_asset_changes_empty() { + let changes = IssuedAssetChanges::default(); + assert_eq!(changes.iter().count(), 0); + } +} diff --git a/zebra-chain/src/orchard_zsa/burn.rs b/zebra-chain/src/orchard_zsa/burn.rs index bc738c2a7ef..85250124de9 100644 --- a/zebra-chain/src/orchard_zsa/burn.rs +++ b/zebra-chain/src/orchard_zsa/burn.rs @@ -10,22 +10,9 @@ use zcash_primitives::transaction::components::orchard::{read_burn, write_burn}; use crate::{ orchard::ValueCommitment, - serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, + serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}, }; -impl ZcashSerialize for AssetBase { - fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { - writer.write_all(&self.to_bytes()) - } -} - -impl ZcashDeserialize for AssetBase { - fn zcash_deserialize(mut reader: R) -> Result { - Option::from(AssetBase::from_bytes(&reader.read_32_bytes()?)) - .ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa AssetBase!")) - } -} - /// OrchardZSA burn item. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct BurnItem(AssetBase, NoteValue); @@ -171,7 +158,11 @@ impl ZcashDeserialize for Burn { pub(crate) fn compute_burn_value_commitment(burn: &[BurnItem]) -> ValueCommitment { burn.iter() .map(|&BurnItem(asset, amount)| { - ValueCommitment::new(pallas::Scalar::zero(), amount.into(), asset) + ValueCommitment::new( + pallas::Scalar::zero(), + amount - NoteValue::from_raw(0), + asset, + ) }) .sum() } diff --git a/zebra-chain/src/orchard_zsa/issuance.rs b/zebra-chain/src/orchard_zsa/issuance.rs index 856b64cccde..718ed394757 100644 --- a/zebra-chain/src/orchard_zsa/issuance.rs +++ b/zebra-chain/src/orchard_zsa/issuance.rs @@ -8,7 +8,7 @@ use group::ff::PrimeField; use halo2::pasta::pallas; use orchard::{ - issuance::{IssueBundle, Signed}, + issuance::{IssueAction, IssueBundle, Signed}, note::ExtractedNoteCommitment, }; @@ -38,15 +38,18 @@ impl IssueData { pub(crate) fn note_commitments(&self) -> impl Iterator + '_ { self.0.actions().iter().flat_map(|action| { action.notes().iter().map(|note| { - // TODO: FIXME: Make `ExtractedNoteCommitment::inner` public in `orchard` (this would - // eliminate the need for the workaround of converting `pallas::Base` from bytes - // here), or introduce a new public method in `orchard::issuance::IssueBundle` to - // retrieve note commitments directly from `orchard`. + // TODO: Replace this workaround with orchard `ExtractedNoteCommitment` if its inner + // field is made public, or if a note_commitments() method is added to IssueBundle. pallas::Base::from_repr(ExtractedNoteCommitment::from(note.commitment()).to_bytes()) .unwrap() }) }) } + + /// Returns issuance actions + pub fn actions(&self) -> impl Iterator { + self.0.actions().iter() + } } impl ZcashSerialize for Option { diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index 4f8762baeb3..7d4d0d34393 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -160,6 +160,8 @@ pub enum Transaction { lock_time: LockTime, /// The latest block height that this transaction can be added to the chain. expiry_height: block::Height, + /// The burn amount for this transaction, if any. + zip233_amount: Amount, /// The transparent inputs to the transaction. inputs: Vec, /// The transparent outputs from the transaction. @@ -190,6 +192,8 @@ impl fmt::Display for Transaction { if let Some(expiry_height) = self.expiry_height() { fmter.field("expiry_height", &expiry_height); } + #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))] + fmter.field("zip233_amount", &self.zip233_amount()); fmter.field("transparent_inputs", &self.inputs().len()); fmter.field("transparent_outputs", &self.outputs().len()); @@ -1107,6 +1111,44 @@ impl Transaction { } } + /// Access the Orchard issue data in this transaction, if any, + /// regardless of version. + #[cfg(feature = "tx_v6")] + pub fn orchard_issue_data(&self) -> &Option { + match self { + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } + | Transaction::V5 { .. } => &None, + + Transaction::V6 { + orchard_zsa_issue_data, + .. + } => orchard_zsa_issue_data, + } + } + + /// Access the Orchard asset burns in this transaction, if there are any, + /// regardless of version. + #[cfg(feature = "tx_v6")] + pub fn orchard_burns(&self) -> Option<&'_ [orchard_zsa::BurnItem]> { + match self { + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } + | Transaction::V5 { .. } => None, + + Transaction::V6 { + orchard_shielded_data, + .. + } => orchard_shielded_data + .as_ref() + .map(|data| data.burn.as_ref()), + } + } + /// Access the [`orchard::Flags`] in this transaction, if there is any, /// regardless of version. pub fn orchard_flags(&self) -> Option { @@ -1388,6 +1430,7 @@ impl Transaction { &self, outputs: &HashMap, ) -> Result, ValueBalanceError> { + // TODO: subtract zip233_amount if zip233 is approved. self.transparent_value_balance_from_outputs(outputs)? + self.sprout_value_balance()? + self.sapling_value_balance() @@ -1420,6 +1463,19 @@ impl Transaction { ) -> Result, ValueBalanceError> { self.value_balance_from_outputs(&outputs_from_utxos(utxos.clone())) } + + /// Access the zip233 amount field of this transaction, regardless of version. + pub fn zip233_amount(&self) -> Amount { + match self { + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } + | Transaction::V5 { .. } => Amount::zero(), + #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))] + Transaction::V6 { zip233_amount, .. } => *zip233_amount, + } + } } #[cfg(any(test, feature = "proptest-impl"))] diff --git a/zebra-chain/src/transaction/arbitrary.rs b/zebra-chain/src/transaction/arbitrary.rs index 96166e4c957..e3e2527b498 100644 --- a/zebra-chain/src/transaction/arbitrary.rs +++ b/zebra-chain/src/transaction/arbitrary.rs @@ -254,6 +254,8 @@ impl Transaction { network_upgrade, lock_time, expiry_height, + // TODO: Consider generating a real arbitrary zip233_amount + zip233_amount: Default::default(), inputs, outputs, sapling_shielded_data, @@ -1138,22 +1140,13 @@ pub fn transactions_from_blocks<'a>( }) } -// FIXME: make it a generic to support V6? -/// Modify a V5 transaction to insert fake Orchard shielded data. -/// /// Creates a fake instance of [`orchard::ShieldedData`] with one fake action. Note that both the /// action and the shielded data are invalid and shouldn't be used in tests that require them to be /// valid. -/// -/// A mutable reference to the inserted shielded data is returned, so that the caller can further -/// customize it if required. -/// -/// # Panics -/// -/// Panics if the transaction to be modified is not V5. -pub fn insert_fake_orchard_shielded_data( - transaction: &mut Transaction, -) -> &mut orchard::ShieldedData { +pub fn create_fake_orchard_shielded_data( +) -> orchard::ShieldedData +//where <::EncryptedNote as Arbitrary>::Strategy: 'static +{ // Create a dummy action let mut runner = TestRunner::default(); let dummy_action = orchard::Action::arbitrary() @@ -1168,7 +1161,7 @@ pub fn insert_fake_orchard_shielded_data( }; // Place the dummy action inside the Orchard shielded data - let dummy_shielded_data = orchard::ShieldedData:: { + orchard::ShieldedData:: { flags: orchard::Flags::empty(), value_balance: Amount::try_from(0).expect("invalid transaction amount"), shared_anchor: orchard::tree::Root::default(), @@ -1177,15 +1170,27 @@ pub fn insert_fake_orchard_shielded_data( binding_sig: Signature::from([0u8; 64]), #[cfg(feature = "tx_v6")] burn: Default::default(), - }; + } +} +/// Modify a V5 transaction to insert fake Orchard shielded data. +/// +/// A mutable reference to the inserted shielded data is returned, so that the caller can further +/// customize it if required. +/// +/// # Panics +/// +/// Panics if the transaction to be modified is not V5. +pub fn insert_fake_v5_orchard_shielded_data( + transaction: &mut Transaction, +) -> &mut orchard::ShieldedData { // Replace the shielded data in the transaction match transaction { Transaction::V5 { orchard_shielded_data, .. } => { - *orchard_shielded_data = Some(dummy_shielded_data); + *orchard_shielded_data = Some(create_fake_orchard_shielded_data()); orchard_shielded_data .as_mut() @@ -1194,3 +1199,31 @@ pub fn insert_fake_orchard_shielded_data( _ => panic!("Fake V5 transaction is not V5"), } } + +/// Modify a V6 transaction to insert fake Orchard shielded data. +/// +/// A mutable reference to the inserted shielded data is returned, so that the caller can further +/// customize it if required. +/// +/// # Panics +/// +/// Panics if the transaction to be modified is not V6. +#[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))] +pub fn insert_fake_v6_orchard_shielded_data( + transaction: &mut Transaction, +) -> &mut orchard::ShieldedData { + // Replace the shielded data in the transaction + match transaction { + Transaction::V6 { + orchard_shielded_data, + .. + } => { + *orchard_shielded_data = Some(create_fake_orchard_shielded_data()); + + orchard_shielded_data + .as_mut() + .expect("shielded data was just inserted") + } + _ => panic!("Fake V6 transaction is not V6"), + } +} diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 2804b099b32..870fe911344 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -475,6 +475,14 @@ impl ZcashDeserialize for Option> { // in [`Flags::zcash_deserialized`]. let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?; + // `ENABLE_ZSA` is introduced in V6 (ZIP 230) and must be zero in V5. + #[cfg(feature = "tx_v6")] + if flags.contains(orchard::Flags::ENABLE_ZSA) { + return Err(SerializationError::Parse( + "ENABLE_ZSA is not allowed in V5 transactions", + )); + } + // Denoted as `valueBalanceOrchard` in the spec. let value_balance: Amount = (&mut reader).zcash_deserialize_into()?; @@ -833,6 +841,7 @@ impl ZcashSerialize for Transaction { network_upgrade, lock_time, expiry_height, + zip233_amount, inputs, outputs, sapling_shielded_data, @@ -855,6 +864,9 @@ impl ZcashSerialize for Transaction { // Denoted as `nExpiryHeight` in the spec. writer.write_u32::(expiry_height.0)?; + // Denoted as `zip233_amount` in the spec. + zip233_amount.zcash_serialize(&mut writer)?; + // Denoted as `tx_in_count` and `tx_in` in the spec. inputs.zcash_serialize(&mut writer)?; @@ -1164,6 +1176,9 @@ impl ZcashDeserialize for Transaction { // Denoted as `nExpiryHeight` in the spec. let expiry_height = block::Height(limited_reader.read_u32::()?); + // Denoted as `zip233_amount` in the spec. + let zip233_amount = (&mut limited_reader).zcash_deserialize_into()?; + // Denoted as `tx_in_count` and `tx_in` in the spec. let inputs = Vec::zcash_deserialize(&mut limited_reader)?; @@ -1195,6 +1210,7 @@ impl ZcashDeserialize for Transaction { network_upgrade, lock_time, expiry_height, + zip233_amount, inputs, outputs, sapling_shielded_data, diff --git a/zebra-chain/src/transaction/tests/vectors.rs b/zebra-chain/src/transaction/tests/vectors.rs index 636813236c6..08cc8fced70 100644 --- a/zebra-chain/src/transaction/tests/vectors.rs +++ b/zebra-chain/src/transaction/tests/vectors.rs @@ -30,12 +30,15 @@ lazy_static! { sapling_shielded_data: None, orchard_shielded_data: None, }; +} - #[cfg(feature = "tx_v6")] +#[cfg(feature = "tx_v6")] +lazy_static! { pub static ref EMPTY_V6_TX: Transaction = Transaction::V6 { network_upgrade: NetworkUpgrade::Nu7, lock_time: LockTime::min_lock_time_timestamp(), expiry_height: block::Height(0), + zip233_amount: Default::default(), inputs: Vec::new(), outputs: Vec::new(), sapling_shielded_data: None, @@ -491,8 +494,9 @@ fn v6_round_trip() { let _init_guard = zebra_test::init(); - for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { - let block = block_bytes + for workflow_block in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = workflow_block + .bytes .zcash_deserialize_into::() .expect("block is structurally valid"); @@ -502,7 +506,7 @@ fn v6_round_trip() { .expect("vec serialization is infallible"); assert_eq!( - block_bytes, &block_bytes2, + workflow_block.bytes, block_bytes2, "data must be equal if structs are equal" ); @@ -638,8 +642,9 @@ fn v6_librustzcash_tx_conversion() { let _init_guard = zebra_test::init(); - for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { - let block = block_bytes + for workflow_block in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = workflow_block + .bytes .zcash_deserialize_into::() .expect("block is structurally valid"); diff --git a/zebra-consensus/Cargo.toml b/zebra-consensus/Cargo.toml index 56495ba0526..bfb2d11c03e 100644 --- a/zebra-consensus/Cargo.toml +++ b/zebra-consensus/Cargo.toml @@ -41,7 +41,7 @@ tx_v6 = [ ] [dependencies] -blake2b_simd = "1.0.2" +blake2b_simd = "=1.0.1" bellman = "0.14.0" bls12_381 = "0.8.0" halo2 = { package = "halo2_proofs", version = "0.3.0" } @@ -103,3 +103,6 @@ tracing-subscriber = "0.3.18" zebra-state = { path = "../zebra-state", version = "1.0.0-beta.41", features = ["proptest-impl"] } zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.41", features = ["proptest-impl"] } zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41" } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("nu7"))'] } diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index 611aea2ceba..e683e3191c0 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -8,6 +8,7 @@ //! verification, where it may be accepted or rejected. use std::{ + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -25,6 +26,7 @@ use zebra_chain::{ amount::Amount, block, parameters::{subsidy::FundingStreamReceiver, Network}, + transaction::{SigHash, UnminedTxId}, transparent, work::equihash, }; @@ -253,25 +255,34 @@ where let mut legacy_sigop_count = 0; let mut block_miner_fees = Ok(Amount::zero()); + // Collect tx sighashes during verification and later emit them in block order (for ZSA issuance auth). + let mut tx_sighash_by_tx_id: HashMap = + HashMap::with_capacity(block.transactions.len()); + use futures::StreamExt; while let Some(result) = async_checks.next().await { tracing::trace!(?result, remaining = async_checks.len()); - let response = result + let crate::transaction::Response::Block { + tx_id, + miner_fee, + legacy_sigop_count: tx_legacy_sigop_count, + tx_sighash, + } = result .map_err(Into::into) - .map_err(VerifyBlockError::Transaction)?; - - assert!( - matches!(response, tx::Response::Block { .. }), - "unexpected response from transaction verifier: {response:?}" - ); + .map_err(VerifyBlockError::Transaction)? + else { + panic!("unexpected response from transaction verifier"); + }; - legacy_sigop_count += response.legacy_sigop_count(); + legacy_sigop_count += tx_legacy_sigop_count; // Coinbase transactions consume the miner fee, // so they don't add any value to the block's total miner fee. - if let Some(miner_fee) = response.miner_fee() { + if let Some(miner_fee) = miner_fee { block_miner_fees += miner_fee; } + + tx_sighash_by_tx_id.insert(tx_id, tx_sighash); } // Check the summed block totals @@ -314,12 +325,24 @@ where let new_outputs = Arc::into_inner(known_utxos) .expect("all verification tasks using known_utxos are complete"); + // Rebuild sighashes in block order to align with `block.transactions` indexing. + let transaction_sighashes: Arc<[SigHash]> = block + .transactions + .iter() + .map(|tx| { + *tx_sighash_by_tx_id + .get(&tx.unmined_id()) + .expect("every verified tx must return a sighash") + }) + .collect(); + let prepared_block = zs::SemanticallyVerifiedBlock { block, hash, height, new_outputs, transaction_hashes, + transaction_sighashes: Some(transaction_sighashes), deferred_balance: Some(expected_deferred_amount), }; diff --git a/zebra-consensus/src/error.rs b/zebra-consensus/src/error.rs index 8fe14c62d52..706f9d25921 100644 --- a/zebra-consensus/src/error.rs +++ b/zebra-consensus/src/error.rs @@ -74,6 +74,10 @@ pub enum TransactionError { #[error("coinbase transaction MUST NOT have the EnableSpendsOrchard flag set")] CoinbaseHasEnableSpendsOrchard, + #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))] + #[error("coinbase transaction MUST NOT have the EnableZSA flag set")] + CoinbaseHasEnableZSA, + #[error("coinbase transaction Sapling or Orchard outputs MUST be decryptable with an all-zero outgoing viewing key")] CoinbaseOutputsNotDecryptable, diff --git a/zebra-consensus/src/orchard_zsa/tests.rs b/zebra-consensus/src/orchard_zsa/tests.rs index 4100c3be9c1..2b45dfb818c 100644 --- a/zebra-consensus/src/orchard_zsa/tests.rs +++ b/zebra-consensus/src/orchard_zsa/tests.rs @@ -12,50 +12,262 @@ //! In short, it demonstrates end-to-end handling of Orchard asset burns and ZSA issuance through //! consensus (with state verification to follow in the next PR). -use std::sync::Arc; +use std::{ + collections::{hash_map, HashMap}, + sync::Arc, +}; use color_eyre::eyre::{eyre, Report}; use tokio::time::{timeout, Duration}; +use tower::ServiceExt; + +use orchard::{ + issuance::{ + auth::{IssueValidatingKey, ZSASchnorr}, + {AssetRecord, IssueAction}, + }, + note::{AssetBase, AssetId}, + value::NoteValue, +}; use zebra_chain::{ - block::{genesis::regtest_genesis_block, Block, Hash}, + block::{Block, Hash}, + orchard_zsa::{AssetState, BurnItem}, parameters::Network, serialization::ZcashDeserialize, }; +use zebra_state::{ReadRequest, ReadResponse, ReadStateService}; + use zebra_test::{ transcript::{ExpectedTranscriptError, Transcript}, - vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS, + vectors::{OrchardWorkflowBlock, ORCHARD_ZSA_WORKFLOW_BLOCKS}, }; use crate::{block::Request, Config}; -fn create_transcript_data() -> impl Iterator)> -{ - let workflow_blocks = ORCHARD_ZSA_WORKFLOW_BLOCKS.iter().map(|block_bytes| { - Arc::new(Block::zcash_deserialize(&block_bytes[..]).expect("block should deserialize")) - }); +type AssetRecords = HashMap; + +type TranscriptItem = (Request, Result); + +#[derive(Debug)] +enum AssetRecordsError { + BurnAssetMissing, + EmptyActionNotFinalized, + AmountOverflow, + MissingRefNote, + ModifyFinalized, +} + +/// Processes orchard burns, decreasing asset supply. +fn process_burns<'a, I: IntoIterator>( + asset_records: &mut AssetRecords, + burns: I, +) -> Result<(), AssetRecordsError> { + for burn in burns { + // FIXME: check for burn specific errors? + let asset_record = asset_records + .get_mut(&burn.asset()) + .ok_or(AssetRecordsError::BurnAssetMissing)?; + + asset_record.amount = NoteValue::from_raw( + asset_record + .amount + .inner() + .checked_sub(burn.amount().inner()) + .ok_or(AssetRecordsError::AmountOverflow)?, + ); + } + + Ok(()) +} + +/// Processes orchard issue actions, increasing asset supply. +fn process_issue_actions<'a, I: Iterator>( + asset_records: &mut AssetRecords, + ik: &IssueValidatingKey, + actions: I, +) -> Result<(), AssetRecordsError> { + for action in actions { + let action_asset = AssetBase::custom(&AssetId::new_v0(ik, action.asset_desc_hash())); + let reference_note = action.get_reference_note(); + let is_finalized = action.is_finalized(); + + let mut note_amounts = action.notes().iter().map(|note| { + if note.asset() == action_asset { + Ok(note.value()) + } else { + Err(AssetRecordsError::BurnAssetMissing) + } + }); + + let first_note_amount = match note_amounts.next() { + Some(note_amount) => note_amount, + None => { + if is_finalized { + Ok(NoteValue::from_raw(0)) + } else { + Err(AssetRecordsError::EmptyActionNotFinalized) + } + } + }; + + for amount_result in std::iter::once(first_note_amount).chain(note_amounts) { + let amount = amount_result?; + + // FIXME: check for issuance specific errors? + match asset_records.entry(action_asset) { + hash_map::Entry::Occupied(mut entry) => { + let asset_record = entry.get_mut(); + asset_record.amount = asset_record + .amount + .inner() + .checked_add(amount.inner()) + .map(NoteValue::from_raw) + .ok_or(AssetRecordsError::AmountOverflow)?; + if asset_record.is_finalized { + return Err(AssetRecordsError::ModifyFinalized); + } + asset_record.is_finalized = is_finalized; + } + + hash_map::Entry::Vacant(entry) => { + entry.insert(AssetRecord { + amount, + is_finalized, + reference_note: *reference_note.ok_or(AssetRecordsError::MissingRefNote)?, + }); + } + } + } + } - std::iter::once(regtest_genesis_block()) - .chain(workflow_blocks) - .map(|block| (Request::Commit(block.clone()), Ok(block.hash()))) + Ok(()) +} + +/// Builds assets records for the given blocks. +fn build_asset_records<'a, I: IntoIterator>( + blocks: I, +) -> Result { + blocks + .into_iter() + .filter_map(|(request, result)| match (request, result) { + (Request::Commit(block), Ok(_)) => Some(&block.transactions), + _ => None, + }) + .flatten() + .try_fold(HashMap::new(), |mut asset_records, tx| { + if let Some(burns) = tx.orchard_burns() { + process_burns(&mut asset_records, burns.iter())?; + } + + if let Some(issue_data) = tx.orchard_issue_data() { + process_issue_actions( + &mut asset_records, + issue_data.inner().ik(), + issue_data.actions(), + )?; + } + + Ok(asset_records) + }) +} + +/// Creates transcript data from predefined workflow blocks. +fn create_transcript_data<'a, I: IntoIterator>( + serialized_blocks: I, +) -> impl Iterator + use<'a, I> { + serialized_blocks.into_iter().map( + |OrchardWorkflowBlock { + height: _, + bytes, + is_valid, + }| { + let block = + Arc::new(Block::zcash_deserialize(&bytes[..]).expect("block should deserialize")); + ( + Request::Commit(block.clone()), + if *is_valid { + Ok(block.hash()) + } else { + Err(ExpectedTranscriptError::Any) + }, + ) + }, + ) +} + +/// Queries the state service for the asset state of the given asset. +async fn request_asset_state( + read_state_service: &ReadStateService, + asset_base: AssetBase, +) -> Option { + let request = ReadRequest::AssetState { + asset_base, + include_non_finalized: true, + }; + + match read_state_service.clone().oneshot(request).await { + Ok(ReadResponse::AssetState(asset_state)) => asset_state, + _ => unreachable!("The state service returned an unexpected response."), + } } #[tokio::test(flavor = "multi_thread")] -async fn check_zsa_workflow() -> Result<(), Report> { +async fn check_orchard_zsa_workflow() -> Result<(), Report> { let _init_guard = zebra_test::init(); let network = Network::new_regtest(Some(1), Some(1), Some(1)); - let state_service = zebra_state::init_test(&network); + let (state_service, read_state_service, _, _) = zebra_state::init_test_services(&network); let (block_verifier_router, ..) = crate::router::init(Config::default(), &network, state_service).await; + let transcript_data = + create_transcript_data(ORCHARD_ZSA_WORKFLOW_BLOCKS.iter()).collect::>(); + + let asset_records = + build_asset_records(&transcript_data).expect("should calculate asset_records"); + + // Before applying the blocks, ensure that none of the assets exist in the state. + for &asset_base in asset_records.keys() { + assert!( + request_asset_state(&read_state_service, asset_base) + .await + .is_none(), + "State should initially have no info about this asset." + ); + } + + // Verify all blocks in the transcript against the consensus and the state. timeout( Duration::from_secs(15), - Transcript::from(create_transcript_data()).check(block_verifier_router), + Transcript::from(transcript_data).check(block_verifier_router), ) .await - .map_err(|_| eyre!("Task timed out"))? + .map_err(|_| eyre!("Task timed out"))??; + + // After processing the transcript blocks, verify that the state matches the expected supply info. + for (&asset_base, asset_record) in &asset_records { + let asset_state = request_asset_state(&read_state_service, asset_base) + .await + .expect("State should contain this asset now."); + + assert_eq!( + asset_state.is_finalized(), + asset_record.is_finalized, + "Finalized state does not match for asset {:?}.", + asset_base + ); + + assert_eq!( + asset_state.total_supply(), + asset_record.amount.inner(), + "Total supply mismatch for asset {:?}.", + asset_base + ); + } + + Ok(()) } diff --git a/zebra-consensus/src/primitives/halo2/tests.rs b/zebra-consensus/src/primitives/halo2/tests.rs index 1b2da85af00..f3ec0763c8a 100644 --- a/zebra-consensus/src/primitives/halo2/tests.rs +++ b/zebra-consensus/src/primitives/halo2/tests.rs @@ -62,13 +62,17 @@ where recipient, NoteValue::from_raw(note_value), // FIXME: Use another AssetBase for OrchardZSA? - AssetBase::native(), + AssetBase::zatoshi(), memo, ) .unwrap(); } - let bundle: Bundle<_, i64, Flavor> = builder.build(rng).unwrap().0; + let bundle: Bundle<_, i64, Flavor> = builder + .build(rng) + .unwrap() + .expect("Bundle should not be None") + .0; let bundle = bundle .create_proof(&proving_key, rng) diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 4e134f7f59e..ec629ce7537 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -143,6 +143,9 @@ pub enum Response { /// The number of legacy signature operations in this transaction's /// transparent inputs and outputs. legacy_sigop_count: u64, + + /// Shielded sighash for this transaction. + tx_sighash: SigHash, }, /// A response to a mempool transaction verification request. @@ -384,7 +387,7 @@ where tracing::trace!(?tx_id, "got state UTXOs"); - let mut async_checks = match tx.as_ref() { + let (mut async_checks, tx_sighash) = match tx.as_ref() { Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => { tracing::debug!(?tx, "got transaction with wrong version"); return Err(TransactionError::WrongVersion); @@ -478,6 +481,7 @@ where tx_id, miner_fee, legacy_sigop_count, + tx_sighash }, Request::Mempool { transaction, .. } => { let transaction = VerifiedUnminedTx::new( @@ -655,7 +659,7 @@ where cached_ffi_transaction: Arc, joinsplit_data: &Option>, sapling_shielded_data: &Option>, - ) -> Result { + ) -> Result<(AsyncChecks, SigHash), TransactionError> { let tx = request.transaction(); let upgrade = request.upgrade(network); @@ -670,7 +674,7 @@ where None, ); - Ok(Self::verify_transparent_inputs_and_outputs( + let async_check = Self::verify_transparent_inputs_and_outputs( request, network, script_verifier, @@ -683,7 +687,9 @@ where .and(Self::verify_sapling_shielded_data( sapling_shielded_data, &shielded_sighash, - )?)) + )?); + + Ok((async_check, shielded_sighash)) } /// Verifies if a V4 `transaction` is supported by `network_upgrade`. @@ -753,7 +759,7 @@ where cached_ffi_transaction: Arc, sapling_shielded_data: &Option>, orchard_shielded_data: &Option>, - ) -> Result { + ) -> Result<(AsyncChecks, SigHash), TransactionError> { let transaction = request.transaction(); let upgrade = request.upgrade(network); @@ -768,7 +774,7 @@ where None, ); - Ok(Self::verify_transparent_inputs_and_outputs( + let async_check = Self::verify_transparent_inputs_and_outputs( request, network, script_verifier, @@ -781,7 +787,9 @@ where .and(Self::verify_orchard_shielded_data( orchard_shielded_data, &shielded_sighash, - )?)) + )?); + + Ok((async_check, shielded_sighash)) } /// Verifies if a V5/V6 `transaction` is supported by `network_upgrade`. diff --git a/zebra-consensus/src/transaction/check.rs b/zebra-consensus/src/transaction/check.rs index 133c7e80470..e806951f73b 100644 --- a/zebra-consensus/src/transaction/check.rs +++ b/zebra-consensus/src/transaction/check.rs @@ -176,6 +176,13 @@ pub fn coinbase_tx_no_prevout_joinsplit_spend(tx: &Transaction) -> Result<(), Tr if orchard_flags.contains(Flags::ENABLE_SPENDS) { return Err(TransactionError::CoinbaseHasEnableSpendsOrchard); } + // ZIP-230: coinbase must not set enableZSA. + // TODO: Add V6 coinbase ENABLE_ZSA tests (fails when set, passes when unset), + // like v5_coinbase_transaction_without_enable_spends_flag_passes_validation. + #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))] + if orchard_flags.contains(Flags::ENABLE_ZSA) { + return Err(TransactionError::CoinbaseHasEnableZSA); + } } } diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index 06aa7040b1c..509b4579d79 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -20,8 +20,8 @@ use zebra_chain::{ sprout, transaction::{ arbitrary::{ - fake_v5_transactions_for_network, insert_fake_orchard_shielded_data, test_transactions, - transactions_from_blocks, + fake_v5_transactions_for_network, insert_fake_v5_orchard_shielded_data, + test_transactions, transactions_from_blocks, }, zip317, Hash, HashType, JoinSplitData, LockTime, Transaction, }, @@ -77,7 +77,7 @@ fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() { // Insert fake Orchard shielded data to the transaction, which has at least one action (this is // guaranteed structurally by `orchard::ShieldedData`) - insert_fake_orchard_shielded_data(&mut transaction); + insert_fake_v5_orchard_shielded_data(&mut transaction); // The check will fail if the transaction has no flags assert_eq!( @@ -86,8 +86,8 @@ fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() { ); // If we add ENABLE_SPENDS flag it will pass the inputs check but fails with the outputs - // TODO: Avoid new calls to `insert_fake_orchard_shielded_data` for each check #2409. - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + // TODO: Avoid new calls to `insert_fake_v5_orchard_shielded_data` for each check #2409. + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS; assert_eq!( @@ -96,7 +96,7 @@ fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() { ); // If we add ENABLE_OUTPUTS flag it will pass the outputs check but fails with the inputs - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_OUTPUTS; assert_eq!( @@ -105,7 +105,7 @@ fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() { ); // Finally make it valid by adding both required flags - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS; @@ -131,7 +131,7 @@ fn fake_v5_transaction_with_orchard_actions_has_flags() { // Insert fake Orchard shielded data to the transaction, which has at least one action (this is // guaranteed structurally by `orchard::ShieldedData`) - insert_fake_orchard_shielded_data(&mut transaction); + insert_fake_v5_orchard_shielded_data(&mut transaction); // The check will fail if the transaction has no flags assert_eq!( @@ -140,17 +140,17 @@ fn fake_v5_transaction_with_orchard_actions_has_flags() { ); // If we add ENABLE_SPENDS flag it will pass. - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS; assert!(check::has_enough_orchard_flags(&transaction).is_ok()); // If we add ENABLE_OUTPUTS flag instead, it will pass. - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_OUTPUTS; assert!(check::has_enough_orchard_flags(&transaction).is_ok()); // If we add BOTH ENABLE_SPENDS and ENABLE_OUTPUTS flags it will pass. - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS; assert!(check::has_enough_orchard_flags(&transaction).is_ok()); @@ -823,7 +823,7 @@ fn v5_coinbase_transaction_without_enable_spends_flag_passes_validation() { .find(|transaction| transaction.is_coinbase()) .expect("At least one fake V5 coinbase transaction in the test vectors"); - insert_fake_orchard_shielded_data(&mut transaction); + insert_fake_v5_orchard_shielded_data(&mut transaction); assert!(check::coinbase_tx_no_prevout_joinsplit_spend(&transaction).is_ok()); } @@ -838,7 +838,7 @@ fn v5_coinbase_transaction_with_enable_spends_flag_fails_validation() { .find(|transaction| transaction.is_coinbase()) .expect("At least one fake V5 coinbase transaction in the test vectors"); - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS; @@ -2415,7 +2415,7 @@ fn v5_with_duplicate_orchard_action() { // Insert fake Orchard shielded data to the transaction, which has at least one action (this is // guaranteed structurally by `orchard::ShieldedData`) - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); // Enable spends shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS @@ -2865,7 +2865,7 @@ fn coinbase_outputs_are_decryptable_for_fake_v5_blocks() { }) .expect("At least one fake V5 transaction with no inputs and no outputs"); - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS; @@ -2907,7 +2907,7 @@ fn shielded_outputs_are_not_decryptable_for_fake_v5_blocks() { }) .expect("At least one fake V5 transaction with no inputs and no outputs"); - let shielded_data = insert_fake_orchard_shielded_data(&mut transaction); + let shielded_data = insert_fake_v5_orchard_shielded_data(&mut transaction); shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS; diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index bebd8e3ed60..e555d6bbb55 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -20,6 +20,7 @@ categories = [ ] [features] +tx_v6 = ["zebra-chain/tx_v6", "zebra-state/tx_v6", "zebra-consensus/tx_v6"] indexer-rpcs = [ "tonic-build", diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 8becc5bb79c..8eda5993943 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -302,6 +302,18 @@ pub trait Rpc { address_strings: AddressStrings, ) -> BoxFuture>>; + /// Returns the asset state of the provided asset base at the best chain tip or finalized chain tip. + /// + /// method: post + /// tags: blockchain + #[cfg(feature = "tx_v6")] + #[rpc(name = "getassetstate")] + fn get_asset_state( + &self, + asset_base: String, + include_non_finalized: Option, + ) -> BoxFuture>; + /// Stop the running zebrad process. /// /// # Notes @@ -1358,6 +1370,57 @@ where .boxed() } + #[cfg(feature = "tx_v6")] + fn get_asset_state( + &self, + asset_base: String, + include_non_finalized: Option, + ) -> BoxFuture> { + let state = self.state.clone(); + let include_non_finalized = include_non_finalized.unwrap_or(true); + + async move { + if asset_base.len() != 64 { + return Err(Error { + code: INVALID_PARAMETERS_ERROR_CODE, + message: "expected 32 bytes (64 hex chars)".to_string(), + data: None, + }); + } + + let asset_base_bytes: [u8; 32] = hex::decode(&asset_base) + .map_err(|_| Error { + code: INVALID_PARAMETERS_ERROR_CODE, + message: "invalid hex encoding".to_string(), + data: None, + })? + .try_into() + .expect("length already checked above"); + + let asset_base = zebra_chain::orchard_zsa::AssetBase::from_bytes(&asset_base_bytes) + .into_option() + .ok_or_else(|| Error { + code: INVALID_PARAMETERS_ERROR_CODE, + message: "invalid asset base".to_string(), + data: None, + })?; + + let request = zebra_state::ReadRequest::AssetState { + asset_base, + include_non_finalized, + }; + + let zebra_state::ReadResponse::AssetState(asset_state) = + state.oneshot(request).await.map_server_error()? + else { + unreachable!("unexpected response from state service"); + }; + + asset_state.ok_or_server_error("asset base not found") + } + .boxed() + } + fn stop(&self) -> Result { #[cfg(not(target_os = "windows"))] if self.network.is_regtest() { diff --git a/zebra-rpc/src/methods/tests/snapshot.rs b/zebra-rpc/src/methods/tests/snapshot.rs index f4d7804088e..982325e4f67 100644 --- a/zebra-rpc/src/methods/tests/snapshot.rs +++ b/zebra-rpc/src/methods/tests/snapshot.rs @@ -14,6 +14,7 @@ use zebra_chain::{ block::Block, chain_tip::mock::MockChainTip, orchard, + orchard_zsa::{mock_asset_base, mock_asset_state}, parameters::{ subsidy::POST_NU6_FUNDING_STREAMS_TESTNET, testnet::{self, ConfiguredActivationHeights, Parameters}, @@ -536,6 +537,39 @@ async fn test_mocked_rpc_response_data_for_network(network: &Network) { settings.bind(|| { insta::assert_json_snapshot!(format!("z_get_subtrees_by_index_for_orchard"), subtrees) }); + + // Test the response format from `getassetstate`. + + // Prepare the state response and make the RPC request. + let asset_base = mock_asset_base(b"Asset1"); + let rsp = state + .expect_request_that(|req| matches!(req, ReadRequest::AssetState { .. })) + .map(|responder| responder.respond(ReadResponse::AssetState(None))); + let req = rpc.get_asset_state(hex::encode(asset_base.to_bytes()), None); + + // Get the RPC error response. + let (asset_state_rsp, ..) = tokio::join!(req, rsp); + let asset_state = asset_state_rsp.expect_err("The RPC response should be an error"); + + // Check the error response. + settings + .bind(|| insta::assert_json_snapshot!(format!("get_asset_state_not_found"), asset_state)); + + // Prepare the state response and make the RPC request. + let asset_base = mock_asset_base(b"Asset2"); + let asset_state = mock_asset_state(b"Asset2", 1000, true); + let rsp = state + .expect_request_that(|req| matches!(req, ReadRequest::AssetState { .. })) + .map(|responder| responder.respond(ReadResponse::AssetState(Some(asset_state)))); + let req = rpc.get_asset_state(hex::encode(asset_base.to_bytes()), None); + + // Get the RPC response. + let (asset_state_rsp, ..) = tokio::join!(req, rsp); + let asset_state = + asset_state_rsp.expect("The RPC response should contain a `AssetState` struct."); + + // Check the response. + settings.bind(|| insta::assert_json_snapshot!(format!("get_asset_state"), asset_state)); } /// Snapshot `getinfo` response, using `cargo insta` and JSON serialization. diff --git a/zebra-rpc/src/methods/tests/snapshots/get_asset_state@mainnet.snap b/zebra-rpc/src/methods/tests/snapshots/get_asset_state@mainnet.snap new file mode 100644 index 00000000000..d253ced0039 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_asset_state@mainnet.snap @@ -0,0 +1,9 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: asset_state +--- +{ + "amount": 1000, + "is_finalized": true, + "reference_note": "cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b000000000000000007dd2affe87ca0930d034f1434442d34621b68c579d60cfb7b87988329c9e262d447ea48052db6f5fcd7668cee86b48ed21b4e5d03a01f7aba7a9dcd95803491d" +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_asset_state@testnet.snap b/zebra-rpc/src/methods/tests/snapshots/get_asset_state@testnet.snap new file mode 100644 index 00000000000..d253ced0039 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_asset_state@testnet.snap @@ -0,0 +1,9 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: asset_state +--- +{ + "amount": 1000, + "is_finalized": true, + "reference_note": "cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b000000000000000007dd2affe87ca0930d034f1434442d34621b68c579d60cfb7b87988329c9e262d447ea48052db6f5fcd7668cee86b48ed21b4e5d03a01f7aba7a9dcd95803491d" +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@mainnet.snap b/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@mainnet.snap new file mode 100644 index 00000000000..9efcfd5868f --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@mainnet.snap @@ -0,0 +1,8 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: asset_state +--- +{ + "code": 0, + "message": "asset base not found" +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@testnet.snap b/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@testnet.snap new file mode 100644 index 00000000000..9efcfd5868f --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_asset_state_not_found@testnet.snap @@ -0,0 +1,8 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: asset_state +--- +{ + "code": 0, + "message": "asset base not found" +} diff --git a/zebra-state/src/arbitrary.rs b/zebra-state/src/arbitrary.rs index 5c0b837566a..43d4309a5d4 100644 --- a/zebra-state/src/arbitrary.rs +++ b/zebra-state/src/arbitrary.rs @@ -37,6 +37,8 @@ impl Prepare for Arc { height, new_outputs, transaction_hashes, + // FIXME: Do we need to (and can we) generate real arbitrary transaction_sighashes? + transaction_sighashes: None, deferred_balance: None, } } @@ -96,8 +98,13 @@ impl ContextuallyVerifiedBlock { .map(|outpoint| (outpoint, zero_utxo.clone())) .collect(); - ContextuallyVerifiedBlock::with_block_and_spent_utxos(block, zero_spent_utxos) - .expect("all UTXOs are provided with zero values") + ContextuallyVerifiedBlock::with_block_and_spent_utxos( + block, + zero_spent_utxos, + #[cfg(feature = "tx_v6")] + Default::default(), + ) + .expect("all UTXOs are provided with zero values") } /// Create a [`ContextuallyVerifiedBlock`] from a [`Block`] or [`SemanticallyVerifiedBlock`], @@ -111,6 +118,7 @@ impl ContextuallyVerifiedBlock { height, new_outputs, transaction_hashes, + transaction_sighashes, deferred_balance: _, } = block.into(); @@ -124,7 +132,10 @@ impl ContextuallyVerifiedBlock { // TODO: fix the tests, and stop adding unrelated inputs and outputs. spent_outputs: new_outputs, transaction_hashes, + transaction_sighashes, chain_value_pool_change: ValueBalance::zero(), + #[cfg(feature = "tx_v6")] + issued_asset_changes: Default::default(), } } } diff --git a/zebra-state/src/error.rs b/zebra-state/src/error.rs index cf495311efb..1149716177d 100644 --- a/zebra-state/src/error.rs +++ b/zebra-state/src/error.rs @@ -9,7 +9,7 @@ use zebra_chain::{ amount::{self, NegativeAllowed, NonNegative}, block, history_tree::HistoryTreeError, - orchard, sapling, sprout, transaction, transparent, + orchard, orchard_zsa, sapling, sprout, transaction, transparent, value_balance::{ValueBalance, ValueBalanceError}, work::difficulty::CompactDifficulty, }; @@ -264,6 +264,9 @@ pub enum ValidateContextError { tx_index_in_block: Option, transaction_hash: transaction::Hash, }, + + #[error("error updating issued asset state")] + InvalidIssuedAsset(#[from] orchard_zsa::AssetStateError), } /// Trait for creating the corresponding duplicate nullifier error from a nullifier. diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 56be011d48e..2cb66142467 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -21,6 +21,9 @@ use zebra_chain::{ value_balance::{ValueBalance, ValueBalanceError}, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::{AssetBase, IssuedAssetChanges}; + /// Allow *only* these unused imports, so that rustdoc link resolution /// will work with inline links. #[allow(unused_imports)] @@ -161,6 +164,9 @@ pub struct SemanticallyVerifiedBlock { /// A precomputed list of the hashes of the transactions in this block, /// in the same order as `block.transactions`. pub transaction_hashes: Arc<[transaction::Hash]>, + /// A precomputed list of the sighashes of the transactions in this block, + /// in the same order as `block.transactions`. + pub transaction_sighashes: Option>, /// This block's contribution to the deferred pool. pub deferred_balance: Option>, } @@ -221,8 +227,19 @@ pub struct ContextuallyVerifiedBlock { /// in the same order as `block.transactions`. pub(crate) transaction_hashes: Arc<[transaction::Hash]>, + /// A precomputed list of the sighashes of the transactions in this block, + /// in the same order as `block.transactions`. + pub transaction_sighashes: Option>, + /// The sum of the chain value pool changes of all transactions in this block. pub(crate) chain_value_pool_change: ValueBalance, + + #[cfg(feature = "tx_v6")] + /// Asset state changes for assets modified in this block. + /// Maps asset_base -> (old_state, new_state) where: + /// - old_state: the state before this block was applied + /// - new_state: the state after this block was applied + pub(crate) issued_asset_changes: IssuedAssetChanges, } /// Wraps note commitment trees and the history tree together. @@ -293,12 +310,22 @@ pub struct FinalizedBlock { pub(super) treestate: Treestate, /// This block's contribution to the deferred pool. pub(super) deferred_balance: Option>, + #[cfg(feature = "tx_v6")] + /// Asset state changes to be applied to the finalized state. + /// Contains (old_state, new_state) pairs for assets modified in this block. + /// If `None`, the changes will be recalculated from the block's transactions. + pub issued_asset_changes: Option, } impl FinalizedBlock { /// Constructs [`FinalizedBlock`] from [`CheckpointVerifiedBlock`] and its [`Treestate`]. pub fn from_checkpoint_verified(block: CheckpointVerifiedBlock, treestate: Treestate) -> Self { - Self::from_semantically_verified(SemanticallyVerifiedBlock::from(block), treestate) + Self::from_semantically_verified( + SemanticallyVerifiedBlock::from(block), + treestate, + #[cfg(feature = "tx_v6")] + None, + ) } /// Constructs [`FinalizedBlock`] from [`ContextuallyVerifiedBlock`] and its [`Treestate`]. @@ -306,11 +333,22 @@ impl FinalizedBlock { block: ContextuallyVerifiedBlock, treestate: Treestate, ) -> Self { - Self::from_semantically_verified(SemanticallyVerifiedBlock::from(block), treestate) + #[cfg(feature = "tx_v6")] + let issued_asset_changes = Some(block.issued_asset_changes.clone()); + Self::from_semantically_verified( + SemanticallyVerifiedBlock::from(block), + treestate, + #[cfg(feature = "tx_v6")] + issued_asset_changes, + ) } /// Constructs [`FinalizedBlock`] from [`SemanticallyVerifiedBlock`] and its [`Treestate`]. - fn from_semantically_verified(block: SemanticallyVerifiedBlock, treestate: Treestate) -> Self { + fn from_semantically_verified( + block: SemanticallyVerifiedBlock, + treestate: Treestate, + #[cfg(feature = "tx_v6")] issued_asset_changes: Option, + ) -> Self { Self { block: block.block, hash: block.hash, @@ -319,6 +357,8 @@ impl FinalizedBlock { transaction_hashes: block.transaction_hashes, treestate, deferred_balance: block.deferred_balance, + #[cfg(feature = "tx_v6")] + issued_asset_changes, } } } @@ -384,6 +424,7 @@ impl ContextuallyVerifiedBlock { pub fn with_block_and_spent_utxos( semantically_verified: SemanticallyVerifiedBlock, mut spent_outputs: HashMap, + #[cfg(feature = "tx_v6")] issued_asset_changes: IssuedAssetChanges, ) -> Result { let SemanticallyVerifiedBlock { block, @@ -391,6 +432,7 @@ impl ContextuallyVerifiedBlock { height, new_outputs, transaction_hashes, + transaction_sighashes, deferred_balance, } = semantically_verified; @@ -407,10 +449,13 @@ impl ContextuallyVerifiedBlock { new_outputs, spent_outputs: spent_outputs.clone(), transaction_hashes, + transaction_sighashes, chain_value_pool_change: block.chain_value_pool_change( &utxos_from_ordered_utxos(spent_outputs), deferred_balance, )?, + #[cfg(feature = "tx_v6")] + issued_asset_changes, }) } } @@ -427,6 +472,7 @@ impl CheckpointVerifiedBlock { block.deferred_balance = deferred_balance; block } + /// Creates a block that's ready to be committed to the finalized state, /// using a precalculated [`block::Hash`]. /// @@ -452,6 +498,8 @@ impl SemanticallyVerifiedBlock { height, new_outputs, transaction_hashes, + // Not used in checkpoint paths. + transaction_sighashes: None, deferred_balance: None, } } @@ -465,7 +513,7 @@ impl SemanticallyVerifiedBlock { impl From> for CheckpointVerifiedBlock { fn from(block: Arc) -> Self { - CheckpointVerifiedBlock(SemanticallyVerifiedBlock::from(block)) + Self(SemanticallyVerifiedBlock::from(block)) } } @@ -484,6 +532,9 @@ impl From> for SemanticallyVerifiedBlock { height, new_outputs, transaction_hashes, + // Not used in checkpoint paths. + // FIXME: Is this correct? + transaction_sighashes: None, deferred_balance: None, } } @@ -497,6 +548,7 @@ impl From for SemanticallyVerifiedBlock { height: valid.height, new_outputs: valid.new_outputs, transaction_hashes: valid.transaction_hashes, + transaction_sighashes: valid.transaction_sighashes, deferred_balance: Some( valid .chain_value_pool_change @@ -508,19 +560,6 @@ impl From for SemanticallyVerifiedBlock { } } -impl From for SemanticallyVerifiedBlock { - fn from(finalized: FinalizedBlock) -> Self { - Self { - block: finalized.block, - hash: finalized.hash, - height: finalized.height, - new_outputs: finalized.new_outputs, - transaction_hashes: finalized.transaction_hashes, - deferred_balance: finalized.deferred_balance, - } - } -} - impl From for SemanticallyVerifiedBlock { fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self { checkpoint_verified.0 @@ -1068,6 +1107,17 @@ pub enum ReadRequest { /// Returns [`ReadResponse::TipBlockSize(usize)`](ReadResponse::TipBlockSize) /// with the current best chain tip block size in bytes. TipBlockSize, + + #[cfg(feature = "tx_v6")] + /// Returns [`ReadResponse::AssetState`] with an [`AssetState`](zebra_chain::orchard_zsa::AssetState) + /// of the provided [`AssetBase`] if it exists for the best chain tip or finalized chain tip (depending + /// on the `include_non_finalized` flag). + AssetState { + /// The [`AssetBase`] to return the asset state for. + asset_base: AssetBase, + /// Whether to include the issued asset state changes in the non-finalized state. + include_non_finalized: bool, + }, } impl ReadRequest { @@ -1105,6 +1155,8 @@ impl ReadRequest { ReadRequest::CheckBlockProposalValidity(_) => "check_block_proposal_validity", #[cfg(feature = "getblocktemplate-rpcs")] ReadRequest::TipBlockSize => "tip_block_size", + #[cfg(feature = "tx_v6")] + ReadRequest::AssetState { .. } => "asset_state", } } diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index 77c252b0c75..4db9f3e2e84 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -13,6 +13,9 @@ use zebra_chain::{ value_balance::ValueBalance, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::AssetState; + #[cfg(feature = "getblocktemplate-rpcs")] use zebra_chain::work::difficulty::CompactDifficulty; @@ -125,6 +128,7 @@ impl MinedTx { #[derive(Clone, Debug, PartialEq, Eq)] /// A response to a read-only /// [`ReadStateService`](crate::service::ReadStateService)'s [`ReadRequest`]. +#[allow(clippy::large_enum_variant)] pub enum ReadResponse { /// Response to [`ReadRequest::Tip`] with the current best chain tip. Tip(Option<(block::Height, block::Hash)>), @@ -233,6 +237,10 @@ pub enum ReadResponse { #[cfg(feature = "getblocktemplate-rpcs")] /// Response to [`ReadRequest::TipBlockSize`] TipBlockSize(Option), + + #[cfg(feature = "tx_v6")] + /// Response to [`ReadRequest::AssetState`] + AssetState(Option), } /// A structure with the information needed from the state to build a `getblocktemplate` RPC response. @@ -322,6 +330,9 @@ impl TryFrom for Response { ReadResponse::ChainInfo(_) | ReadResponse::SolutionRate(_) | ReadResponse::TipBlockSize(_) => { Err("there is no corresponding Response for this ReadResponse") } + + #[cfg(feature = "tx_v6")] + ReadResponse::AssetState(_) => Err("there is no corresponding Response for this ReadResponse"), } } } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index adc61f887ae..c4a593a160c 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -1947,6 +1947,30 @@ impl Service for ReadStateService { }) .wait_for_panics() } + + #[cfg(feature = "tx_v6")] + ReadRequest::AssetState { + asset_base, + include_non_finalized, + } => { + let state = self.clone(); + + tokio::task::spawn_blocking(move || { + span.in_scope(move || { + let best_chain = include_non_finalized + .then(|| state.latest_best_chain()) + .flatten(); + + let response = read::asset_state(best_chain, &state.db, &asset_base); + + // The work is done in the future. + timer.finish(module_path!(), line!(), "ReadRequest::AssetState"); + + Ok(ReadResponse::AssetState(response)) + }) + }) + .wait_for_panics() + } } } } diff --git a/zebra-state/src/service/chain_tip.rs b/zebra-state/src/service/chain_tip.rs index 04ea61d6982..c79788fbc36 100644 --- a/zebra-state/src/service/chain_tip.rs +++ b/zebra-state/src/service/chain_tip.rs @@ -115,6 +115,8 @@ impl From for ChainTipBlock { height, new_outputs: _, transaction_hashes, + // FIXME: Is it correct not to use sighashes here? Should we add transaction_sighashes to ChainTipBlock? + transaction_sighashes: _, deferred_balance: _, } = prepared; diff --git a/zebra-state/src/service/check/tests.rs b/zebra-state/src/service/check/tests.rs index 9608105766d..e82e9be681e 100644 --- a/zebra-state/src/service/check/tests.rs +++ b/zebra-state/src/service/check/tests.rs @@ -1,6 +1,7 @@ //! Tests for state contextual validation checks. mod anchors; +mod issuance; mod nullifier; mod utxo; mod vectors; diff --git a/zebra-state/src/service/check/tests/issuance.rs b/zebra-state/src/service/check/tests/issuance.rs new file mode 100644 index 00000000000..db9ba0b6f31 --- /dev/null +++ b/zebra-state/src/service/check/tests/issuance.rs @@ -0,0 +1,94 @@ +use std::sync::Arc; + +use zebra_chain::{ + block::{self, Block}, + orchard_zsa::{AssetBase, IssuedAssetChanges}, + parameters::Network, + serialization::ZcashDeserialize, +}; + +use zebra_test::vectors::{OrchardWorkflowBlock, ORCHARD_ZSA_WORKFLOW_BLOCKS}; + +use crate::{ + check::Chain, + service::{finalized_state::FinalizedState, read, write::validate_and_commit_non_finalized}, + CheckpointVerifiedBlock, Config, NonFinalizedState, +}; + +#[test] +fn check_burns_and_issuance() { + let _init_guard = zebra_test::init(); + + let network = Network::new_regtest(Some(1), None, Some(1)); + + let mut finalized_state = FinalizedState::new_with_debug( + &Config::ephemeral(), + &network, + true, + #[cfg(feature = "elasticsearch")] + false, + false, + ); + + let mut non_finalized_state = NonFinalizedState::new(&network); + + let mut block_iter = + ORCHARD_ZSA_WORKFLOW_BLOCKS + .iter() + .map(|OrchardWorkflowBlock { bytes, .. }| { + Arc::new(Block::zcash_deserialize(&bytes[..]).expect("block should deserialize")) + }); + + let genesis_block = block_iter.next().expect("genesis block must exist").clone(); + + finalized_state + .commit_finalized_direct(genesis_block.into(), None, "test") + .expect("unexpected invalid genesis block test vector"); + + let block_1 = { + let mut block = (*block_iter.next().expect("block 1 must exist")).clone(); + Arc::make_mut(&mut block.header).commitment_bytes = [0; 32].into(); + Arc::new(block) + }; + + let CheckpointVerifiedBlock(block_1) = CheckpointVerifiedBlock::new(block_1, None, None); + + let empty_chain = Chain::new( + &network, + finalized_state + .db + .finalized_tip_height() + .unwrap_or(block::Height::MIN), + finalized_state.db.sprout_tree_for_tip(), + finalized_state.db.sapling_tree_for_tip(), + finalized_state.db.orchard_tree_for_tip(), + finalized_state.db.history_tree(), + finalized_state.db.finalized_value_pool(), + ); + + let block_1_issued_assets = IssuedAssetChanges::validate_and_get_changes( + &block_1.block.transactions, + None, + |asset_base: &AssetBase| { + read::asset_state( + Some(&Arc::new(empty_chain.clone())), + &finalized_state.db, + asset_base, + ) + }, + ) + .expect("test transactions should be valid"); + + validate_and_commit_non_finalized(&finalized_state.db, &mut non_finalized_state, block_1) + .expect("validation should succeed"); + + let best_chain = non_finalized_state + .best_chain() + .expect("should have a non-finalized chain"); + + assert_eq!( + IssuedAssetChanges::from(best_chain.issued_assets.clone()), + block_1_issued_assets, + "issued assets for chain should match those of block 1" + ); +} diff --git a/zebra-state/src/service/finalized_state.rs b/zebra-state/src/service/finalized_state.rs index f8c9bade5c1..94328d9e51f 100644 --- a/zebra-state/src/service/finalized_state.rs +++ b/zebra-state/src/service/finalized_state.rs @@ -91,6 +91,7 @@ pub const STATE_COLUMN_FAMILIES_IN_CODE: &[&str] = &[ "orchard_anchors", "orchard_note_commitment_tree", "orchard_note_commitment_subtree", + "orchard_issued_assets", // Chain "history_tree", "tip_chain_value_pool", diff --git a/zebra-state/src/service/finalized_state/disk_format/shielded.rs b/zebra-state/src/service/finalized_state/disk_format/shielded.rs index bcd24d5c604..9cd23047d0c 100644 --- a/zebra-state/src/service/finalized_state/disk_format/shielded.rs +++ b/zebra-state/src/service/finalized_state/disk_format/shielded.rs @@ -13,6 +13,9 @@ use zebra_chain::{ subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::{AssetBase, AssetState}; + use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk}; use super::block::HEIGHT_DISK_BYTES; @@ -207,3 +210,43 @@ impl FromDisk for NoteCommitmentSubtreeData { ) } } + +// TODO: Replace `.unwrap()`s with `.expect()`s + +#[cfg(feature = "tx_v6")] +impl IntoDisk for AssetState { + type Bytes = Vec; + + fn as_bytes(&self) -> Self::Bytes { + self.to_bytes() + .expect("asset state should serialize successfully") + } +} + +#[cfg(feature = "tx_v6")] +impl FromDisk for AssetState { + fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + Self::from_bytes(bytes.as_ref()).expect("asset state should deserialize successfully") + } +} + +#[cfg(feature = "tx_v6")] +impl IntoDisk for AssetBase { + type Bytes = [u8; 32]; + + fn as_bytes(&self) -> Self::Bytes { + self.to_bytes() + } +} + +#[cfg(feature = "tx_v6")] +impl FromDisk for AssetBase { + fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + bytes + .as_ref() + .try_into() + .ok() + .and_then(|asset_bytes| Option::from(Self::from_bytes(asset_bytes))) + .expect("asset base should deserialize successfully") + } +} diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap index d37e037cac7..33f1c76717b 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap @@ -12,6 +12,7 @@ expression: cf_names "height_by_hash", "history_tree", "orchard_anchors", + "orchard_issued_assets", "orchard_note_commitment_subtree", "orchard_note_commitment_tree", "orchard_nullifiers", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap index 3c333a9fc43..abd4ae001ec 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap @@ -5,6 +5,7 @@ expression: empty_column_families [ "balance_by_transparent_addr: no entries", "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap index cb8ac5f6aed..8b114ddce4d 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap @@ -4,6 +4,7 @@ expression: empty_column_families --- [ "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap index cb8ac5f6aed..8b114ddce4d 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap @@ -4,6 +4,7 @@ expression: empty_column_families --- [ "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap index a2abce2083b..2d119139d26 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap @@ -11,6 +11,7 @@ expression: empty_column_families "height_by_hash: no entries", "history_tree: no entries", "orchard_anchors: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_note_commitment_tree: no entries", "orchard_nullifiers: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap index 3c333a9fc43..abd4ae001ec 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap @@ -5,6 +5,7 @@ expression: empty_column_families [ "balance_by_transparent_addr: no entries", "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap index cb8ac5f6aed..8b114ddce4d 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap @@ -4,6 +4,7 @@ expression: empty_column_families --- [ "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap index cb8ac5f6aed..8b114ddce4d 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap @@ -4,6 +4,7 @@ expression: empty_column_families --- [ "history_tree: no entries", + "orchard_issued_assets: no entries", "orchard_note_commitment_subtree: no entries", "orchard_nullifiers: no entries", "sapling_note_commitment_subtree: no entries", diff --git a/zebra-state/src/service/finalized_state/zebra_db/block.rs b/zebra-state/src/service/finalized_state/zebra_db/block.rs index 4dc3a801ef3..6f0d2340b91 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block.rs @@ -463,7 +463,7 @@ impl DiskWriteBatch { // which is already present from height 1 to the first shielded transaction. // // In Zebra we include the nullifiers and note commitments in the genesis block because it simplifies our code. - self.prepare_shielded_transaction_batch(db, finalized)?; + self.prepare_shielded_transaction_batch(zebra_db, finalized)?; self.prepare_trees_batch(zebra_db, finalized, prev_note_commitment_trees)?; // # Consensus diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs index 194f2202a87..4f58b505cc0 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs @@ -136,6 +136,8 @@ fn test_block_db_round_trip_with( height: Height(0), new_outputs, transaction_hashes, + // FIXME: Do we need to (and can we) genereate real arbitrary transaction_sighashes? + transaction_sighashes: None, deferred_balance: None, }) }; diff --git a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs index 4bba75b1891..8c980ba0111 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs @@ -19,13 +19,16 @@ use std::{ use zebra_chain::{ block::Height, - orchard, + orchard::{self}, parallel::tree::NoteCommitmentTrees, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, transaction::Transaction, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::{AssetBase, AssetState, IssuedAssetChanges}; + use crate::{ request::{FinalizedBlock, Treestate}, service::finalized_state::{ @@ -36,11 +39,34 @@ use crate::{ BoxError, }; +#[cfg(feature = "tx_v6")] +use crate::service::finalized_state::TypedColumnFamily; + // Doc-only items #[allow(unused_imports)] use zebra_chain::subtree::NoteCommitmentSubtree; +#[cfg(feature = "tx_v6")] +/// The name of the chain value pools column family. +/// +/// This constant should be used so the compiler can detect typos. +pub const ISSUED_ASSETS: &str = "orchard_issued_assets"; + +#[cfg(feature = "tx_v6")] +/// The type for reading value pools from the database. +/// +/// This constant should be used so the compiler can detect incorrectly typed accesses to the +/// column family. +pub type IssuedAssetsCf<'cf> = TypedColumnFamily<'cf, AssetBase, AssetState>; + impl ZebraDb { + #[cfg(feature = "tx_v6")] + /// Returns a typed handle to the `history_tree` column family. + pub(crate) fn issued_assets_cf(&self) -> IssuedAssetsCf { + IssuedAssetsCf::new(&self.db, ISSUED_ASSETS) + .expect("column family was created when database was created") + } + // Read shielded methods /// Returns `true` if the finalized state contains `sprout_nullifier`. @@ -410,6 +436,12 @@ impl ZebraDb { Some(subtree_data.with_index(index)) } + #[cfg(feature = "tx_v6")] + /// Get the orchard issued asset state for the finalized tip. + pub fn issued_asset(&self, asset_base: &AssetBase) -> Option { + self.issued_assets_cf().zs_get(asset_base) + } + /// Returns the shielded note commitment trees of the finalized tip /// or the empty trees if the state is empty. /// Additionally, returns the sapling and orchard subtrees for the finalized tip if @@ -437,16 +469,19 @@ impl DiskWriteBatch { /// - Propagates any errors from updating note commitment trees pub fn prepare_shielded_transaction_batch( &mut self, - db: &DiskDb, + zebra_db: &ZebraDb, finalized: &FinalizedBlock, ) -> Result<(), BoxError> { let FinalizedBlock { block, .. } = finalized; // Index each transaction's shielded data for transaction in &block.transactions { - self.prepare_nullifier_batch(db, transaction)?; + self.prepare_nullifier_batch(&zebra_db.db, transaction)?; } + #[cfg(feature = "tx_v6")] + self.prepare_issued_assets_batch(zebra_db, finalized)?; + Ok(()) } @@ -480,6 +515,41 @@ impl DiskWriteBatch { Ok(()) } + #[cfg(feature = "tx_v6")] + /// Prepare a database batch containing `finalized.block`'s asset issuance + /// and return it (without actually writing anything). + /// + /// # Errors + /// + /// - Returns an error if asset state changes cannot be calculated from the block's transactions + #[allow(clippy::unwrap_in_result)] + pub fn prepare_issued_assets_batch( + &mut self, + zebra_db: &ZebraDb, + finalized: &FinalizedBlock, + ) -> Result<(), BoxError> { + let mut batch = zebra_db.issued_assets_cf().with_batch_for_writing(self); + let asset_changes = if let Some(asset_changes) = finalized.issued_asset_changes.as_ref() { + asset_changes.clone() + } else { + // Recalculate changes from transactions if not provided. + // This happens for Checkpoint Verified Blocks loaded during startup. + // We use trusted validation (no signature verification) since these blocks + // are within checkpoint ranges and already considered valid. + IssuedAssetChanges::validate_and_get_changes( + &finalized.block.transactions, + None, // No sighashes - uses trusted validation without signature checks + |asset_base| zebra_db.issued_asset(asset_base), + ) + .map_err(|e| BoxError::from(format!("invalid issued assets changes: {e:?}")))? + }; + // Write only the new states to the database + for (asset_base, (_old_state, new_state)) in asset_changes.iter() { + batch = batch.zs_insert(asset_base, new_state); + } + Ok(()) + } + /// Prepare a database batch containing the note commitment and history tree updates /// from `finalized.block`, and return it (without actually writing anything). /// diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 08d64455024..60d2b6e44ad 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -10,6 +10,7 @@ use std::{ use zebra_chain::{ block::{self, Block}, + orchard_zsa::{AssetBase, IssuedAssetChanges}, parameters::Network, sprout, transparent, }; @@ -17,7 +18,7 @@ use zebra_chain::{ use crate::{ constants::MAX_NON_FINALIZED_CHAIN_FORKS, request::{ContextuallyVerifiedBlock, FinalizableBlock}, - service::{check, finalized_state::ZebraDb}, + service::{check, finalized_state::ZebraDb, read}, SemanticallyVerifiedBlock, ValidateContextError, }; @@ -325,6 +326,15 @@ impl NonFinalizedState { finalized_state, )?; + #[cfg(feature = "tx_v6")] + let issued_assets = IssuedAssetChanges::validate_and_get_changes( + &prepared.block.transactions, + prepared.transaction_sighashes.as_deref(), + |asset_base: &AssetBase| { + read::asset_state(Some(&new_chain), finalized_state, asset_base) + }, + )?; + // Reads from disk check::anchors::block_sapling_orchard_anchors_refer_to_final_treestates( finalized_state, @@ -343,6 +353,9 @@ impl NonFinalizedState { let contextual = ContextuallyVerifiedBlock::with_block_and_spent_utxos( prepared.clone(), spent_utxos.clone(), + // TODO: Refactor this into repeated `With::with()` calls, see http_request_compatibility module. + #[cfg(feature = "tx_v6")] + issued_assets, ) .map_err(|value_balance_error| { ValidateContextError::CalculateBlockChainValueChange { diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index a002c301766..bfa2963da15 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -21,13 +21,18 @@ use zebra_chain::{ primitives::Groth16Proof, sapling, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, - transaction::Transaction::*, - transaction::{self, Transaction}, + transaction::{ + self, + Transaction::{self, *}, + }, transparent, value_balance::ValueBalance, work::difficulty::PartialCumulativeWork, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::{AssetBase, AssetState, IssuedAssetChanges}; + use crate::{ request::Treestate, service::check, ContextuallyVerifiedBlock, HashOrHeight, OutputLocation, TransactionLocation, ValidateContextError, @@ -174,6 +179,12 @@ pub struct ChainInner { pub(crate) orchard_subtrees: BTreeMap>, + #[cfg(feature = "tx_v6")] + /// A partial map of `issued_assets` with entries for asset states that were updated in + /// this chain. + // TODO: Add reference to ZIP + pub(crate) issued_assets: HashMap, + // Nullifiers // /// The Sprout nullifiers revealed by `blocks`. @@ -237,6 +248,8 @@ impl Chain { orchard_anchors_by_height: Default::default(), orchard_trees_by_height: Default::default(), orchard_subtrees: Default::default(), + #[cfg(feature = "tx_v6")] + issued_assets: Default::default(), sprout_nullifiers: Default::default(), sapling_nullifiers: Default::default(), orchard_nullifiers: Default::default(), @@ -937,6 +950,45 @@ impl Chain { } } + #[cfg(feature = "tx_v6")] + /// Returns the Orchard issued asset state if one is present in + /// the chain for the provided asset base. + pub fn issued_asset(&self, asset_base: &AssetBase) -> Option { + self.issued_assets.get(asset_base).cloned() + } + + #[cfg(feature = "tx_v6")] + /// Remove the History tree index at `height`. + fn revert_issued_assets( + &mut self, + position: RevertPosition, + issued_asset_changes: &IssuedAssetChanges, + ) { + if position == RevertPosition::Root { + // `issued_assets` grows monotonically (no eviction on finalization). + // At ~112 bytes per asset this is negligible for realistic asset counts. + // Revisit if ZSA adoption reaches hundreds of thousands of unique assets. + } else { + trace!( + ?position, + "restoring previous issued asset states for tip block" + ); + // Simply restore the old states + for (asset_base, (old_state, new_state)) in issued_asset_changes.iter() { + assert_eq!( + self.issued_assets.get(asset_base), + Some(new_state), + "tip revert: current state differs from recorded new_state for {:?}", + asset_base + ); + match old_state { + Some(state) => self.issued_assets.insert(*asset_base, *state), + None => self.issued_assets.remove(asset_base), + }; + } + } + } + /// Adds the Orchard `tree` to the tree and anchor indexes at `height`. /// /// `height` can be either: @@ -1439,6 +1491,31 @@ impl Chain { self.add_history_tree(height, history_tree); + #[cfg(feature = "tx_v6")] + for (asset_base, (old_state_from_block, new_state)) in + contextually_valid.issued_asset_changes.iter() + { + self.issued_assets + .entry(*asset_base) + .and_modify(|current_state| { + assert_eq!( + old_state_from_block.as_ref(), + Some(&*current_state), + "issued asset state mismatch for {:?}", + asset_base + ); + *current_state = *new_state; + }) + .or_insert_with(|| { + // When `old_state_from_block` is `Some` but the asset is missing from + // the in-memory map, it means the entry was evicted during finalization + // and lives in the finalized DB. Inserting `new_state` is correct, the + // old state is preserved on disk for rollback via the `IssuedAssetChanges` + // pairs. + *new_state + }); + } + Ok(()) } @@ -1677,6 +1754,9 @@ impl UpdateWith for Chain { &contextually_valid.chain_value_pool_change, ); + #[cfg(feature = "tx_v6")] + let issued_asset_changes = &contextually_valid.issued_asset_changes; + // remove the blocks hash from `height_by_hash` assert!( self.height_by_hash.remove(&hash).is_some(), @@ -1696,21 +1776,22 @@ impl UpdateWith for Chain { for (transaction, transaction_hash) in block.transactions.iter().zip(transaction_hashes.iter()) { - let ( - inputs, - outputs, - joinsplit_data, - sapling_shielded_data_per_spend_anchor, - sapling_shielded_data_shared_anchor, - orchard_shielded_data, - ) = match transaction.deref() { + let transaction_data = match transaction.deref() { V4 { inputs, outputs, joinsplit_data, sapling_shielded_data, .. - } => (inputs, outputs, joinsplit_data, sapling_shielded_data, &None, &None), + } => ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data, + &None, + &None, + #[cfg(feature = "tx_v6")] + &None), V5 { inputs, outputs, @@ -1724,13 +1805,15 @@ impl UpdateWith for Chain { &None, sapling_shielded_data, orchard_shielded_data, + #[cfg(feature = "tx_v6")] + &None, ), #[cfg(feature = "tx_v6")] V6 { inputs, outputs, sapling_shielded_data, - orchard_shielded_data: _, + orchard_shielded_data, .. } => ( inputs, @@ -1738,14 +1821,35 @@ impl UpdateWith for Chain { &None, &None, sapling_shielded_data, - // FIXME: support V6 shielded data? - &None, //orchard_shielded_data, + &None, + orchard_shielded_data, ), V1 { .. } | V2 { .. } | V3 { .. } => unreachable!( "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint", ), }; + #[cfg(not(feature = "tx_v6"))] + let ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data_per_spend_anchor, + sapling_shielded_data_shared_anchor, + orchard_shielded_data_vanilla, + ) = transaction_data; + + #[cfg(feature = "tx_v6")] + let ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data_per_spend_anchor, + sapling_shielded_data_shared_anchor, + orchard_shielded_data_vanilla, + orchard_shielded_data_zsa, + ) = transaction_data; + // remove the utxos this produced self.revert_chain_with(&(outputs, transaction_hash, new_outputs), position); // reset the utxos this consumed @@ -1762,7 +1866,9 @@ impl UpdateWith for Chain { self.revert_chain_with(joinsplit_data, position); self.revert_chain_with(sapling_shielded_data_per_spend_anchor, position); self.revert_chain_with(sapling_shielded_data_shared_anchor, position); - self.revert_chain_with(orchard_shielded_data, position); + self.revert_chain_with(orchard_shielded_data_vanilla, position); + #[cfg(feature = "tx_v6")] + self.revert_chain_with(orchard_shielded_data_zsa, position); } // TODO: move these to the shielded UpdateWith.revert...()? @@ -1773,6 +1879,10 @@ impl UpdateWith for Chain { // TODO: move this to the history tree UpdateWith.revert...()? self.remove_history_tree(position, height); + #[cfg(feature = "tx_v6")] + // In revert_chain_with for ContextuallyVerifiedBlock: + self.revert_issued_assets(position, issued_asset_changes); + // revert the chain value pool balances, if needed self.revert_chain_with(chain_value_pool_change, position); } diff --git a/zebra-state/src/service/non_finalized_state/tests/prop.rs b/zebra-state/src/service/non_finalized_state/tests/prop.rs index 2a1adf65c20..f28689c9722 100644 --- a/zebra-state/src/service/non_finalized_state/tests/prop.rs +++ b/zebra-state/src/service/non_finalized_state/tests/prop.rs @@ -52,6 +52,8 @@ fn push_genesis_chain() -> Result<()> { ContextuallyVerifiedBlock::with_block_and_spent_utxos( block, only_chain.unspent_utxos(), + #[cfg(feature = "tx_v6")] + Default::default(), ) .map_err(|e| (e, chain_values.clone())) .expect("invalid block value pool change"); @@ -148,6 +150,8 @@ fn forked_equals_pushed_genesis() -> Result<()> { let block = ContextuallyVerifiedBlock::with_block_and_spent_utxos( block, partial_chain.unspent_utxos(), + #[cfg(feature = "tx_v6")] + Default::default() )?; partial_chain = partial_chain .push(block) @@ -166,8 +170,12 @@ fn forked_equals_pushed_genesis() -> Result<()> { ); for block in chain.iter().cloned() { - let block = - ContextuallyVerifiedBlock::with_block_and_spent_utxos(block, full_chain.unspent_utxos())?; + let block = ContextuallyVerifiedBlock::with_block_and_spent_utxos( + block, + full_chain.unspent_utxos(), + #[cfg(feature = "tx_v6")] + Default::default() + )?; // Check some properties of the genesis block and don't push it to the chain. if block.height == block::Height(0) { @@ -210,7 +218,9 @@ fn forked_equals_pushed_genesis() -> Result<()> { // same original full chain. for block in chain.iter().skip(fork_at_count).cloned() { let block = - ContextuallyVerifiedBlock::with_block_and_spent_utxos(block, forked.unspent_utxos())?; + ContextuallyVerifiedBlock::with_block_and_spent_utxos(block, forked.unspent_utxos(), + #[cfg(feature = "tx_v6")] + Default::default())?; forked = forked.push(block).expect("forked chain push is valid"); } diff --git a/zebra-state/src/service/read.rs b/zebra-state/src/service/read.rs index 0188ca1bf5e..1450a390348 100644 --- a/zebra-state/src/service/read.rs +++ b/zebra-state/src/service/read.rs @@ -38,6 +38,10 @@ pub use find::{ find_chain_headers, hash_by_height, height_by_hash, next_median_time_past, non_finalized_state_contains_block_hash, tip, tip_height, tip_with_value_balance, }; + +#[cfg(feature = "tx_v6")] +pub use find::asset_state; + pub use tree::{orchard_subtrees, orchard_tree, sapling_subtrees, sapling_tree}; #[cfg(any(test, feature = "proptest-impl"))] diff --git a/zebra-state/src/service/read/find.rs b/zebra-state/src/service/read/find.rs index e9d557dbfb2..e87c396199f 100644 --- a/zebra-state/src/service/read/find.rs +++ b/zebra-state/src/service/read/find.rs @@ -25,6 +25,9 @@ use zebra_chain::{ value_balance::ValueBalance, }; +#[cfg(feature = "tx_v6")] +use zebra_chain::orchard_zsa::{AssetBase, AssetState}; + use crate::{ constants, service::{ @@ -679,3 +682,14 @@ pub(crate) fn calculate_median_time_past(relevant_chain: Vec>) -> Dat DateTime32::try_from(median_time_past).expect("valid blocks have in-range times") } + +#[cfg(feature = "tx_v6")] +/// Return the [`AssetState`] for the provided [`AssetBase`], if it exists in the provided chain. +pub fn asset_state(chain: Option, db: &ZebraDb, asset_base: &AssetBase) -> Option +where + C: AsRef, +{ + chain + .and_then(|chain| chain.as_ref().issued_asset(asset_base)) + .or_else(|| db.issued_asset(asset_base)) +} diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-0-genesis.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-0-genesis.txt new file mode 100644 index 00000000000..83287eb444a --- /dev/null +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-0-genesis.txt @@ -0,0 +1 @@ +040000000000000000000000000000000000000000000000000000000000000000000000db4d7a85b768123f1dff1d4c4cece70083b2d27e117b4ac2e31d087988a5eac40000000000000000000000000000000000000000000000000000000000000000dae5494d0f0f0f2009000000000000000000000000000000000000000000000000000000000000002401936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b30101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334ffffffff010000000000000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000 \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-1.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-1.txt index 8bc02287f09..51dc5091d69 100644 --- a/zebra-test/src/vectors/orchard-zsa-workflow-block-1.txt +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-1.txt @@ -1 +1 @@ -0400000027e30134d620e9fe61f719938320bab63e7e72c91b5e23025676f90ed8119f02d7bf0f7d0d11636183d03c1dfb8992871ca2d609d1b3b7f9026d326f727d15c10000000000000000000000000000000000000000000000000000000000000000f2fa494d3fa60c200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000100000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000010232de274d7acc75ac97149e063106e7b2c306e156fb53ec5683c0372d382b861e5c6bd944d71a75fe8ab41560cf577122400411d316b553461a28bd01a52042320b2176905f04e3722c1ad9fd61b5836ed7c730164e6c7ad338b62e268133969cd9e88d91459a64ab94d35237209892ecb01560342c4d66731c92a26739cf312b9bc671f36ba61f0ccaf721965a1c544cf52cec716f43f5ce7594fd7a38c5ae25cf0d5bba65c1ea9d30ab10d3babaa27b49398a4940073ee28c309ba1a78c3f103f57cfc000a1062bc874bf21b2aca3e321ffdbb6a0d5c91f607e4fc1ecddbfc31902f6c18d1a33ab0f2ff852d7d3e667df7cbfb834578536f77bf13e6103aa806acf3f1142dd58fab8c1ecdcf1853e4633636080ba8ec1f20fc8ec32610c89a951127994624845aa17c68c59e1ff7c33902cbd135f0e238e484e10825a95bbe4c04de01077aafff36868aa134986cdf19bc06b08cec2af46665b46ed87ae7d1682a82a689629e60834d9d78f03328a37b1be8a76aa32870b88994a37393a7ceb6fc9652af879933d662dc3e82e152b5b84ad85959544270a1c4ecbba82dbe40398bddb449596d8175412b5528b7fa08534a603cea9b402734316722a6c0675e7e5155e50014c021baed8620720e57d2d0c79e0db3953f5fd6729a44c1fd380f9581e8b7ca47efc54881f54cca709378000ee4feeb2b327156e8d15b99ae7dd4b9d2b29f9adf8a0de4e6edb1ea2d6e21b93d6f075e9a4514327a53c03618961cbe84760aa3f9376ba94bffae846e7a592283b990f0121d19921d2829bbf6e8de3595f28e6f03547dc773a0a40d36ea2310f97b289b8e51d4d2c5570134f3a6e5f615cf0da21c850fae083d539bf83807da707a1c1c8017e1365d9eeb60f3830e2b90ec793f1c7e1157602c871e73efabe209f2fd1742b3e92b69ae291ed232934c3e70fea0d6f0d3ed162282b6484be8e08b3021cb152e6a49f73c906d8dc4804900b7b96d34ba082e5ca5390b9e405160c3e1373efe270d7bff197cd1179969604091ca3e5375cc31cc41999896cd33ec4e5fb950ae4454f570a2edbdd6d89f39a41f3e15ced3efeb77bf9f3028d33776ad63835ee6c542c9467e6951275c099574f78a3a336d77631e55a98bb160b507f18c73b0be251dfc15085e0e215d34bcc01095444829fb794c0a97617b0928f8dc4a3903cdaeb92c7f3a8feb7f012b7258b82573539183953a67d80535f296bd1365f1bce53f39e14099fac7a673f0c09797b18525498b770fdb7a9d4b4b72e8d142435717426e4f8c5a3bdbce2b5ff21d429b3abf310f2aa41ca3294e10507c52c7320bc9d5faafe6e5fd7106d72f61c9dc5341fc9de535f6d2511f737bfd47c8b7413c4d2b41dfd9b9d8c99cf74d60a664052813ea9bc2404047fc31d5600dc6a46b9b7ff03d99d73daec27485cb19a2e31e059bf8ae9679f62bb86f0269e171d46883f4140545dde2eba26c496458b470cfcec2f966f658b790a39997ecd6cf2790d6f5da81847c0027be1dee8b480338664e1c61fcf0311802fda81b1da6b375a7efb02b1bc5518536bd1534a3b4994b980dc2313bc29607fb277d3e5104721aaf2a9b85bdc86b9cb4bfaf1ecbf16632e2d2d3c27d86c8cf5037d814e74c70a13f07fe8cb2ba7b7eb3737b2085f3c07893dead5dcba68fe41c74dd2ca102456f68248e21f674c06a87ccd997410b0ffd0e9d8d638144a56b5128d4fb597e88d71348727d5f42ac222dba55f4d6d15b4394f42f7926f8f416c6bdb5059585f63a4722306f71b9939234e9b813fbfd669f2f1367684fbbeeb404113f2067c548d02d5278e4ae43123069aa794db95d64582ef5efc6e85c604b2c8cfe7b674b61ddd6cbb8f5b134d40dea6e5cdb90dfb172ab0c8c830ee7f66843bea19b80c5fd0221c266ad77f2b31d25e71c4014e01253205998c1ed6d308ac2dbf2ec22d04232cf6523b39eafaa0830c98bd52d65f60cb88db981e9de1d6e239f30af6331c58d0ca45e8056fdf3d2f50d349afadef99add2b4771facd4623dc34997536b15aa7a62bcbc7fcb7690bb9435f9d0efcd60674d682d83b7cdea9040cf2d02428f81aecf9a47aa208f5f6af3b8ab70bd0b455fedd5f79a41cfd801a8c7776820e2d1de7a4767a65a81a2373e2b10c3fe018e03a8107d72117a6f22ce0e17c4b36987d7344bf3d458adc4f04b1e00b01c2a37115772f82c90c9c3607efdf671effc3feeed437cda9292a26bab6dfc16d5bd04576e5fa858fb1ed18cb174539669d98eb7ec200e65f6553d34a4659c9d2d03608ed41a5b099ba86184f3bbf3fa23461b4bf142f727938ca8dea636f31bba3e73f6a2ff0acce72ae4f604ae7c6121a1dd219ab7424619fb7e81fcec114a2c36d8f45ca424e9fb07ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f0000000000fde01c3f6dcb809e2f2053c78bff8d879d80e4b0bc8b16f9b90d2d44cce577d27de624651e8b14c508bb1241dd373417196462dbe9f7c59e938e22c5b6337b636c6ba25fc82b90bdd9b6bfe0c13d753f0ca1d8717e56ae6a0ac398f62788d9dab69ca67d5c5b98b9a86ba271997e5871b79e8277a4b11ad09bae366129dc04c211318756b7421e22cb0f0de5144114e3a7bbb797a144c876d5d77e3ac4d6d91af824bf3ef9edf2e798619acae2589a001e3a1d1aa2b47bed1c09d2aee2c264f677079202410375d2f51b748c36c480e86e122c790ab5a8effa17cbe336dcaff84e2925a9e8a3130f30cec1dd260ef6949cdb86a943cc248b7cdeeee5a14326872c1f044d708dcbde6c2c9a0780285025eb5ea4ea189ca779b07b20619f221b17cfab80c580ab06c9d27a649891cbf9d1442569bf7bd189805015d2ad25503499a9c79679b21d9b2d835dca3ec8f2ee14915ab9f9e3b84e72539e443142b4f21e496f3fc1c669af146cfce7b43ca7998cf33a503d4d584fb7cf5379d07d604c0d7c713079347c79c9cc063293d35739fb42e8e2a42d80329818fee704d805ffb7f5cfa837cfeef10563be627f4759b9ae82a6ed9f1144880db656139fdfcf2ad03e71249fe83101021989aeebe0c70d3428bb0dac4017b8b3f755a23da64be6a09fc11f6b15368ff996650c8ea850b2476ba9c0ef2da18b82da996075d6476c58ea438d63f43b8aa0abd77a416f4173cd57c86a70e8456577e1e0364720b89a606077b79da39275839509ffdaf8a25500ba11d3e56fd5fbfc620d7b9b1e2cc4d61bf6945e284a27b4c7e0879d634bf1914a4d9b9bd72f9566f3935bc09f3aac3ac522b17b7cacdf9e49ca2b00ed3234df426be7388b0e5279b006b963351e7287912f149f5eea0f7965f59fdc5f64b6208a6064430105773084a1e1107047267b07f3300bce119f7011373d991b89e2674de9d8b655052ddb27c9704d49e52d921596a2fd2c5f8307683f533a497609df7723c10d0b32e7eedfd8f17a1fc9e72545c5b27acfd37bd66c01768743d87b982e3b33588d96d39ee5d7f46f371a59b5be3a8dd06b8917e262c1cf55339d85b9ea18dab68f29ec5456328a6c4fbe5688f2b6bc00ce1bcddeeab526625a382ce070d4a2872ac0e69aab93e101c7d8e47e18828fd9e5a2f0ac8ce4c080890932fe5f1a49139574e94f5ff2ea528ccdac7df6f32bfffede5c68068c21e8b58ec45597fbe417cef41ea977ce58bffabb4a63233b972b028f09696be67c4a1cc399999a09445888ee85d883d1a5762c930d28212e32839518354ff925eb3ea0f40dabdc30ca0a48a65607b0d088c95b453034769db536dc8a3c61d1f728a76ec9c868aa49148ca9376714fc1110c335a4b494d59409f55d5f8546a9d682d430806ed274209d7d995c205e47fb53bf75ed5c6533113799756c4e648571ad7b41a7f238143d93b1e93d5c8fc1729682735f67d71b541232b0acf347ce4fe494204cf005597faef6a5c824f753beb4fa7b3827e27a301367a0b720d04a959f7ee96dfe586b55a71bdf2c3cdb0764f3b96c814b9922e6b015cabe4e384085c310449ec5313f8fcc4af2be1d812ad19a4fa5bc94c72bff98e7f87847c22229f82872da0e7e1d328600be8226b5880a90fb135d223f454e068c2f8f76360f730ceb42e606bee3f145c67eb4a8aa617677f5376af014865f3f923611afb152ee773c9a71d9e3fbe6152a874e7c5062772f6cca0d3c8547c89ae62cc6e583620a19da1c7f7b7370b07335f7314d305321e8edbdf95ba8a93818e0a2172f95d36bbc064c15c3a29d303c842ce87217dfd3e018dda643e4f5562cdd0fc547730915aa9649bffbe57e69aa0da551d77f59947105ddbf99d73c7c1565537401cfd9efb36716c43dac66422bb9273e6e297582f6bc1170c247d8e9b4dba419f86c8053d0034c1154827a17b4b2eaa1d94c5169bd940d678f2059c28342d7bbbdb115bfea240d27f35e0077f0a784cffea43e59595bdcb8ffc294a31b5a60bc92798ff651ab4ad2a85169952f3f08575a45d578b0375c47b31a9143bb203daf115a37b100f810e86b0116678c0737b7024a4ea6b9a2641d4daf6ff42d4862130f4de3b3627aec8ee904a50373219b1de0fbb237b1a52459c2a08be1a1f1c82c1852634a9361af230d3168788a63523b871062318c2a8a84df84c48ab47aea9c9ab0e2d72fdcfe396ddb9f369ce14d43a077e3809a15a433056ec97e059fba63b26c5d87de7bfb67b0d99a2af8f5e11bf42fe224f232c81b42cf460b020f6dc48bc0bcccd446bbbbf1f9ec6dc89a06252e0f38abd23ba8b8fd7dc70aad77ee48880bbf114b788999fb144b0b307ec70337658ee2888232a393da42e0b77ab526eeae28bc871f787fe83b8a35c342e768ad040c84a9403d695824254e218739509c4f2e6ab23afe52d2fe23df4622ad9c8a85a8a5e1f9e27279ffad590e7f67ead3649fc99c06d03fc76557e8aaf999e84f30b9d116ade05d2dd8feca3f9e020e9d590cb1e984a3e1acd20d79207a59802a05cb9f77ff74c7eb124fd41f18973f902576f2cc25de04fdf07de81ec1ce46cc4aebc8725632b848ae29cf12dfb8860ad9d30f53f2b69ec4ec3fdfcc8ad0d86da08a946a7421797bb51f8b0d9c98c2f2468645d0c84823604af825b22a060dc14fff401fe0aabbe81310f5359ded87b83251aba5c14a7a62ea02a1c68c7ae4514a545c988ad8acd7aafc850521fae846d7c059f1c6008c82ef1cdab9b26e457a7a19158bb9ee7dd6e319f92a07e24300c421a36242f9347d601161f40006ec3d69b21d3ca4311faf62f62b05f6a1fdcb863188dbb4246f3edefe1a65ab4b619a1a1f4f51429e843aad12250285783fdd418ebbdfc6469d40584667762735eb8fc89320fd48287bf8e30b851982345d52910c2d60ab9bc29080f49b55b0f70236828946c41efd80efb9fac51f62f0e4e889156d4f8a7cef16d447b330b1950e2a4f54431aa65f026a318eb00ad0fe50cc947e0275c82353e0bdf629f0aeda5f081d802ed511d60583af0f200e835f20edd96a3d69b364d6aa686c2c08bcfd21979c9d995813a0382ad3e26921dccc1df46df37a412c20259159763f683eefe19b8b720df3de5fd57b00cf4609df3cdb66726c60897dd8e082b74bc555453b52a4c6b2af3b062fbbf455c6161f6816a74df11566ce6ff2b58369fd385412feb6d06fed9e29958fc6b64b99c13827e582aec4373b96b39b25714a2e3c19816022c9b112d65379caf1e4f087ec1970dab4d2fe2f03db8960992a0236c42f79f797b0665808506a05f88b5be9071eb3bc3ffc002e33f083da4a31c6455a74f063297f36e4291ca86359c7f52b372f4745ff4ab710d6ab6d722424ee556afaca36bf194df3d55fa0761b842685f91c62e9221c49bc5421e620f2428cb0f985a810a605165d45c7435f0dffc9aef608fd8c6bffa3657e4a0db47a975d2335644877d7ba80921a8de81ef712f40023139571e4d6fb9a9b2f22a50fd7bffe2f7d9a0099c39fcbb5b97f7253b7d9395a3e6a6d3b0a688ffe7ab27c6e21159e2c8d3fc30448246d21c973d4c8c2b8f47603c0fe3e75d2ca6428b257233006316996d1aeb47d6c749a3fc3e6625e496b410ba622f23ea0e7d55ca312ed728d4af760e35c216ecd704c077fe5b184cd3d5b34779473ec4f5fc649207a4f72734b058746473d34865fda4b14c58343dc56772635bea90659cb2be46160f9f4dc47b3a5790a442856aacb85010ddc9ed8ebe81754c812f531d3cb6a5590e87218593c5fbc6e43b4fab9acc532425182a7f06e2edf994fc49605c21959a644014fe5d2df091ee0ff7eb8c6cd39b4ac5d413ab9238d6ad949080c4dd7c582064b24329a71c2949f69af70d43c5be889ae3d5a592962c9e80ea033d21184529425f840e29b9f80924bae76f35f6e7016f2dc334801dc702c674728c0b9621c225d3f7865b7dcf034049c1b0e740c867a7e99668224fac5ff5bfb63850ba7ef83d8f2b3203e1da17808e3471900f8407eebe10dd52ec35bd70eef8c71f3e0935792c4d2dd13c75fd57f79bfce2755aded7e324e410921c5aefb823bebfefa15cc27582b95567b610c23d79f42fc9ee8e2b84331fd27b276943e0d2da4a1dd5e74bb9f209f5cb325016268039d08908237a28a43f93e21c5bf34407629f49807706bad517948171bfbc8275dd92ac925a722ffb3722913ba54a1bd6d7f73106be90e105ecc75151c85c026e6bdc51d6a0905f75525197192d73fb153ed441fa8af73600cfb80d916cd55d10e92dd2005ab0b3683d60ead4b96c5605b79a2720f491b8a64b55d6005117b09c6d95051a967595d1c52044a80ef59f98a5e2258ca974b3b1543481d20365cce10fe7cbe2ca17dd6beca0d432de5adf402b30d2a3eaca2e142972b4bf0d459cbe3faf940b612e8ae0ed62b96d2986ebe976d6995a197aaf1953bcc977070f326fc5a12c6bd5d2eaf60c1118740d83c1426a5252a90e94bb4f1c4cba0031555cc6f9a79fa447435ef1526356bf370babb5e3142aa86b877812a3530ca47fcb0b474ee638564d59acdfa1c042753d30983fd9d978902c48b2e6263ca8efd3a9f639812f3b55448c0641ac02c1e46668648fd6e60cd25dd59a0da09c1ac46a0ef88a99f644739349e2f97e017aa791721464f30fda92016d15099d1f05bdf9bdaa0ee91a749b8da690cdad21b1ca7969895cca541da84354142e53932df6821ad6c20ec57bc552b6df155581468b54b2304f46909c409e20078d0470c9f5f119820839e523b0afc0677f5a515f1cc26fa4a06675359bf6469925750ffa7f6139c2040d4fbc1e38bcdbfeee71bf76206211cc65b0dd51c83a1e42c52db621c36bbd0867f444f0d5726555c3e1c88093587330a829cb02656582105e7d79255536e27d8ce5c2d62657150e4cf1a82dd07a333c67f1057d68996f739ed110656e70cbc1d6dc165e58000a633f61d960d88a6b36112bbfdd1f263e601da817c76faee168900d4e89b65a7c01bbb3bc52a8b8de90e8318d8cd5cd5b03d7c3332dc3e910a94c589774d802ad53114275029551ce436c69b8d3bf619b781754e3722791e6e32eb328d99ba535c8d4039418e957edd937a2bd246e93d2a2b7c7e9b8523fc1909cd789017783d7fbb953d3e7c11cf1a173df9cb03dbfc3f385c8a5803570793db7c290c3525dd93a9e93a725928a7774db58c73692c49ea7bdeaefeb339bcea818a0ce3fc555aebda2400ee0150480a4bcd5f694da47a5636167d8b281ec519d05675b1fee9c14281751f04dbe37e1a9603f86cf82803cec34eb5d47ad42874ab21edede9d999797d3b3ea75eb1e2ae4d78ee1843f13aeb9d30df8103682710e2ffdee90e9cb90b71f10a9d250af007f605091096e52bfebc74297d732e9c2de64761dae8516c308f622aa2f216acbc1eeac257b0235ef9865fc01283df7833254d392b5c51667108152ed3d66165e17977ee45da87fcf7a4d385bf4dceea87b57256706a7a082d480827a6050ebe4d1cd2f61bc4f44cb14d1e1e23535c956657d00f7bdba2f503207d0ce6e98381396f7fc4ad13ef86fcae4314c64e895a5070f35ebdd89d8676a82332f9ed90c2d27ad6b579829ff6afa70ea805ced1f506ef1ec747c80d0a8c460339650f5e1bfd601f2a3828c543e62392df77acef0908b37b90d93a5a00cad27f09cae95b065e85e0350ce4223405195adeeeba4f78ec411a47d89fbdda9f753a3297625c5e8876e4140662923f83f0c7642abd42850f7b0f54b8c7bce054a03112f7d2adadbaa3afb6f9b15144de8954ead7278febf47af6053b203be1f11d2d0c41442eb9c35cdb49a38dad2a4184a5ef1050084c07b071859d82c4bd28f70a1f1c5c789cf30d31eccb2650d92edc14faaa2f6a954c6a2bf29ec024d560f1ca1d48870d8a67b8fdb7388f6629d43df42f6e91f47dcf344ba55115db1c14935b267ab2766bb514928777fcae6f364a513fa4bca15a37c6b8863779203b573ff03993c3d82cb8bf30075f167afcff1046058d1adef5cb057138e9976ec40ac4b430bf8b8369a4a2cb4d7f41e02b05a1180df8606dc8e5846d731d5caaf995ac752d06cc33ccce1e404a877d9ae9465c4b00ee114471b7f8240cb45d2291e968c536a42e8567913b32014d08176503e05d2833ffecba3165a47db46eade9f00ccb39f87c1098e28e04589d9fa8d2312da49684bd33c2b174527039260f67fdeab726ba17440238f2c8ff04949c7e4e73c340f9307aac7112a37722371966e5ee9e190fdbd48de38801d66e80bc79c36aa761012f716cc5537e20e6c397c07f41052db306ca68c50a9d329bc767c69b8e62a1a8e4b8be7244aa358742ad6a31167c0859e0c4c26becaa531e5ffb9e488cabe5822abba4ea560522a83e0a70471cc9325c1bb7e6b9ac7b0fdb4d1a216468a17bb1dd87b20baf362b4dc03fa268bf163617d13654463b593938538299bcda6e88e34c5d70de6c9058924359655bb59a15e54f01ad6a9ac90d7f4ae32fb86889e837adf48c1083f9138d696ffda9a2b00cbfcff2787d46bc293dc657069cb80e40d05f2e7571ecaa6b0bf2319ad9030708ddfec3d5c42bc91b268455022ddd0e9bab65abe56d11ca68062bd71b132b24168fbe06076f08e21e8fca681c25ef8c6249ab82715d8d409b0f0b40aaef64e00e084e296454baf2a213f837e4565e7843ff0acefbef7efb206ba42d43f622a10e34c17f59fc5462073e53015afe85f9febc3487030a7a65f2957b6c6ff2b2a71a92c4a855335eee07a7da84bcecd43a65b7341855751b8f8d55a1cc35cfb72c00ad627798f61484817fa6cda601a43b0d496bb16df14940e87b31f4323d8d0e3a4caa5e995e9ba83bcc5fadfcad47bd32b981d1ea3304975403b58d79c02f952ea35b81d728d23e83bd409d59d0f7adc44807b3eb9aeaf6a5fb48a86f9cbfa33246a79c147d8924ba65a6682f9bdd8154f92a371c04cd26359c46d45d2974200bc72d5542955d92237010f74bfb5172e138e8c8752e0561cb3916950eaa186f0ef9b0910280949f952b38dd0173541c503cc2a16dfde8e5cc127eb848ca239400d0b0b1c863f92f3b2ade1d88a5858caaee7cc165acb4284171842570b21f512f8da482b054fffea8c08fbbe31f0df84259986d7e4a94a40584df09d5b1f0103a4609477aaf794496c2c25d0cc5f8f6f23ee7b0bbc2606d84aabdc4b51b4d9a260af77bcc7eddcac082d4c43e3084029a08c50a4742a4680e9e1334b9a954bf09ca81ef708ae36bd252e68bb0db6f0df6d5fb78b171aa4735996cd38d8b24243523d5ccf5bf7ae4687250cd40a6a13e53b1e96a09e4d7d986bf5f6ab40dc4ba03b790c62fc3d0c97571376ae44ae6d692d83f4ab5e76125543c212c939609b930782694f150fda65a47268c8ec96e7496c94c251f90315ea24a9fff409cc9d90c62357aa63f1debace0030969cbcb66fc63b0f4d7321d030808fcd8fb68831c3864a282f42e227a5761017f31ef46d0a9a1b36a93b87be974c13a5fa0728b393eca4960ff83257676801180e679a67a9f23de91597c4cd3be17a70adf5f6a8c0c8c7fe28d5369f2a30d82a567c005c21b06de6c8549f6b6cee38ff517982bbb0f7064e32e111e6517e394896d2fdb416148b097770cacbe9060f1d88492486e253d87add10662d6af8c7484b0773b2fa5b2b8cc82e2c075ed8b948f063b639e03b33c24cbaa5ae64dfc87e08fc7c431eb5a83c20b3d5b81294b56c18797e340175f70b57f914ee38f8769c32a40b996c870354a25baaa79e0b12155df90d60a1fd6e756d6f31a36432f6db8ef209f239a5038fd605df54e38d151e475d8ba3a11a742189462eb3015871c410c252e6845bfcf024d258023cdc6dcea2afa0fd92c16c45bcf539dc1b7ef8d324a6b73c5b8da3ec57889a1068ef87c99ce710eea1d39147a41382bb0e8b41d956bd2f30512e91d59c53606481ea7d04009b4a83329d77b8b851ac012261491e8bbbd5cb751b2b2e42d00daccf436495ff8fc00bb2705f0aca87e8b291a113fc3f73a2e0874a7cc661320304846d33151dd8a99d61c878edf710f2ad0ea4ba3738c5553ec6cac9d1a400c41b2927a43b669fa134936efe46d8c159b3d13f0aa1d3e9f688898025b1b9990e05e9e44758813f4c25d310c7f822e6bf251f5616aba59381b44a6a32560d4df14fadf3ac945c27e63e13276fed8fadcd23708b374df0d9e45392256eb486c348cffd74361d3af5f277f343b146e5c611470e2303d607c8c38086de2c9b03ec4a3dc0ad275dfc2156f9d1a4aa291881bdbce5e5dd17dbf07771952701156c9d5b523fba81c96146fca8811b855653d04d8bf7a6dc36bff79dfadbf43c6202f875c7a6f2d979170915ee52294f07b4e99b54c8ef35e53fb1b7645080738699aa49e570059235520fa5a8f244d712c9576b337c3a52c6273e5a19a5e5840ea079c872ebd485ae6b8cd0d59226f26d7bf34ae9001d6abeaa79a9b3c6f232679a675281bb2fd3219f979c5580dd9cfe34b5535b1e1e4aafda8e780fccdd169a0cb43b1841df3614be7035b0b11e9a347a27905fba72e9c9567c57fdd38d386a4be7b3790592187d5b71a944331e234bad2eda5f70542db56417752d4ab76480f9d7fa30b1ee98e3af21c7432358274dd3099ef7a52a06072137258292fd33b164b7dc3f3ab5b4b3329237ca3264fdee7f89dcb74afe205dd5313486a2d06f3f75a31f9970467f16860921b1e2250aff0ea7feb175ce8419496f17228095dbbf3a27ca6a03d04518a0cdd1a2f16ca475bb56c9923b4537681e4da4874a312b8d489ef1b0c6af6fcdffceb5efd1d0f8804f36cb514192c9d355c88ea05cc6c2ca4daa58d66edc5bebb6df26d3d3df285c245f3253bd0fe66212fd355a0d0e33b4beb9adb68cc83eee633c0378d26c37d9a0c5e561e543f839dd1f255c0d75d9187a1865eaae5f941631688de48291a402b8c9462f21ad31dc1b8153a82d53402f332683a5fdfe3b608f1781439846af78cd8d02fe4ef2bf61efa3799cfa901aef2ebf993fa8a2c9acaff60d4630214be87bb55c8e3d04717599649df8dfd9f022a80fa14ad47b2c4991f26103809109998849134533e085067526bea97174caea89667bbb35a63a9efe95e03463f58b8f1990d6ff743ce4bf0bbe6744665ba86fe141513e73d88bdd3a5c1b87d0e82409bd88c1542a6f0c7880881ab8f6d8671da01abc1929c56615991c29c720798f178b84e1410d912eefa2607b15a19755be279b4a5abb2d1a964b476bdabaca51d3e7e54a3384aa3e30d10b0a084c778370f2be30d5af28e62e9637424be02694de7ce7cd6a959e56a771752ba5ea1008d14bfedd24752e87480867f2a0f092b63923137b20195d2c5ec15d7cf77641bf51609f1a184211daf602e5e0bf8178259cb7089caaf4a70b3795f301f26668cf502c0808ee1a8f0c6cb94deddcc8976c1b215db44a0b8b5e3db0260227d7327ed729195647c03186b6029790bbd207813ba0988467513332dec8b770961645c6035527a7226bdf2314bc7b68999a9d7cb0493c32003a49ac877b36d4b77fa33bfd49af531b04450950492cd7be82e108aaa8c6441ca3ac8d0f1f998fe5a4387150be2aa909d8c31cd279e6222b5b5564b7199d552b4ffa309c9ab8a404a284c857f07eab73ff13f92394e48966b078a040090fde758052a88f71bc8cbd62d4bfce666bd2b59f0b306f0d864dd9eb93b37045506513aa7fa623a7fabab030c7fc36f34a8809d859992f1edace4bdb6d536207c64a347f54121a0a1c59649ebc5f5c42edda097fae7d384e9df57189263d7f4c62116fee3ca0ba125eaf6f54a7c7f1df139e2ab5638305a541409b4875df364bb613047009aade81d1fa1afea8f6eb80cec3b1113f10db3693841ce1f131eaf1fef5c973eddd31f4237ce001b95d60578b88a3e0c8dae96dcb35b5f2469e4a8bbb54aaea242a46b609f37a120fa29e97a667f2b26a97894b136ca6aad51d65b1f9dede1a85e31b42e9f1c9c6497e39c4cf26edd3bddf3c0817b6fe4200fa076429ed8d772a87913f72b7c01f5954f25497a263d75db10c26e23609b2aae3e95fb881ac2b34bfdfba3dbebdfe35b908e299af1182c58e5cd22664fd58c59b9785ae7c8f2c3dc9ec8fab37b7165d229c620873ff294768748702c09d1092b9911cde96e4b2ccf98655f355ae957f63bcbcc5bb939a2aaf1d249e14d833baaf558ee287ce00f3a7495818c388d8e4922c6ccf16a55524711df691a197f2ff02c3de4209e5100e204a580ccfa7bab1d124b042beeb3c93f1057a34e4f770dbfa73013cdab39e31ab2afc94c5d00e6101aa49016b8334ece04b16da2fbe4380100609b30963d52bec43e4c49046f5583d8d3e5ed43fb86693ff2ff1b124006ada1652aff2bfa7b68b0a909953e22b15129c2749017ed1b93b27bc41cba5ef0e02801000e2f9b5222c1d8d732b0dabb9892710d0f56b3f9f64334d6e541fa347b31f987257030fcf9c79f2994f0bad1064622dc87a83e884b6f124622bb53e90879750b00000000000000000100867da11116ff3ea4c7c92a1be22f406a73a0af422f465273a4bbf24c1b570e8eed992d65fada6ec857a3aa66d4ef38cc700ba8cd29bd57b07c2964b2bd699a00210080a0835b85a29f68b95d0e0339bd20ca08b2280240bc0741c33918a6a25c138601ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b0000000000000000024f27d1100632833749168d206bce845932c580e6a9a0e3b3248b90ded27a920b4913046e7dc70aa533de6852e052d17b5b4ae15e0dd912cce7e52f2fb30f16970bb16e6a1b5b714290ec87832f086e3a375125a34d32814fb4a0063e5242fe684ff8b144c6e6aa918eb92e803000000000000c574610f80dcaa42e990330a168ee5c69ee41d2dbcbda07dadb5a35b86149e2a6e9dbd721d5a8899afc380924d7c3f3d2f2e8c6e8a9a2cede927cd43124ec40e000100410022c840b145fdc7ea3b54070c5fa0b0a0544f49a6d67d24e66b61725f579cbcf29f20af47284c1efe9501c2c0f0e9fd3d396061d3c653451cff52a16d9e7eae58 \ No newline at end of file +0400000027e30134d620e9fe61f719938320bab63e7e72c91b5e23025676f90ed8119f020a70bb32bfd219e26833d128fb172e93760aa1e93a4c5bb73b897214109cf1ce0000000000000000000000000000000000000000000000000000000000000000f2fa494d3fa60c200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000100000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000000000000000000001022a46968d3e18126e043a75a29ee62b013a509f75e15848f5b7076f8a8515232a8f1936228aa3f6dbb6b3c0b4d064928da244d786cd8e0c36c99d71d9a69ac530f35cc933e1d989161f712fc7e03144eb3953f031e75ff6298eb6146209bfbab31d705a6a2e0e281dd63ea34025999c441b020d0fd1488f4fc409d44cd4048d35d9b408f975fee94626bbdb03d1ce88e7111649fbc25d653e642c5a6cf00ea12daffd4754cc102f274cb5cfe8597bd7733e3253123c58b3646b7dab1df3ff17c038a2d7eff8d099beb8d8749a5f8046afece919f950a276c9f31290f2d3e5f7d1ec67eb3c1ba82b91bc987e71d5b561b6ee060bdd5b709fad62256e464c0e85ea34271d40b52ac47b51189672e59b52f5209ecd82ac738d13ea812aa3d7b6f2037448ab3813ea9627dd2114ace4808cd8019d8dfaf41d6c09cc3628088ac842d812559364f9ff08540283366aa4a2ecd8ebf86db6cabb000d882276fdac5205f29e89574f35c2d7a14eb38974a85588d8d6f99f88d7df2fd5d034f2fe9e5e8b6325ab445c8b257968e570caabfbe85a056d3cce83b465f976149045bf96d649d75f82d1aeddde3824d3ea9cdb84cc13521f25e82e6f2454647f4cdb20a6d6bc34a075f9f582376f751ab380860d5cdd16527234a2337aceacc9cba19ec781ae908416e329d611f4741df7415d2a0b4a9b231c8c902c154d459c89840fda5db847771c873503d61d742f75ee036893da820f7c44169e1f820d34db133018ffc537803e192f399adb97b7cb1d7bebaad3c413b226218077003a61cfbccce8f64600b352ede772e4795e7c809229aaf28c936edf328f78cb6c242a385e6d86cbad4765954e67ad5cbbd933bba82efc5296a142af9d8fcc05912d8b2f8ff12b65c5b34c55970d558aa72aa7cc01a104ba0a91cf2dd44c54f9d4a53c9fa0c5dab0ac5cc0ceba2d97c75d0e66e64b1a6de8ec186a620135cde4a5c5449ecfdbe7832e811f3c2b0aa730b7bf24d356a000c722e6a84821aa079a15d6f7cffbb49e1852ec3e005a17393c0f118e7713128d21cebf23d47627ec32006c828cc18652a0595622d18ab6980742df9049f0803b762d1fc409e5825c6c6ef082f176a475e16d8d32819cbb608bdbe2fb64bb3eb5e8de977efafef1a63cd0006ccff6f0761560aff5255fc62dfd8fc29bf6b6fdecac10592dfd035495f59b531f8bb221bf4e963d3387704f61c309ae782cad00c1539783580ae89e5f1791ab4384341f198b6c901a3bedcc04c22c865f5100aa5d41e1c3a28e333f75601b5079988d004ce889854b975e6fec8b2cfc5f9ab82eec606c152c89f1aea80db68e87736d28dbde05f7f3fd427c3cb00d5616029da2c5bbb5f0a4b334151d449b882a74674f3ccd6214c77e082333220719d20beea6670026a7d53c65025c661be152f63a1eea74128da1942585e1d12ae4a2de89e670480872ca54190f188fad7dbb6ed0efbbc5e948917ffdb5ecec9994d2fe370d2c62fcc6399c75b06494c4476f5c284073dc0f447fb5ed5e4e5c91e71c73118cfd75d5f38bcd537214f10bde70b7b69f09655007b9b1e89413fb24ce6018008292c0789d63d2a1ba9090db6b2e1340b9ba40838f38e90e20c2300dfe0224ba34a75c397b52ed6295c2f99eb672e7e80ce4c395364a5b2ccbe073eb9f571765bddb49c947fa64a2c314514e9782dfc67faff3be254c3cd11842e9d6f4889d5c9cc5e9401024b6499d2c8bbbe70738e44b2dcd0318aeb6ea3dd6fcb5901eb2b85225297fd38f4a1ef0cbe9069b757bf9bba06914340beb3725d21230a1eca312ab7e3f7e504baac85b31260223363835b032f422d35bea9bc21fc6a4f0f1f1af1c3a0d4a1b7f90c13384e790f7853a9d224e2b9ae612ea45f32d15db80a554fa12f33fa94bbb03c8c1ef4408cf9c3a84089d1683037796d547acea1984952e24f6958fc03c61e829665b270bb7e1d8f96e8e3114f861c518677ab733dd6e24e5944026b2e25c56baaf48b4b4f2bd0e78816381ea43cd87611ecc1ee3d132ec5bf420bcac7c008b57362fd77da833e56bfae146b27f5b1bcf9a1c18b243fce9c8e86033f0c91cbdaf26abae936510baac066677933cb979f1436218e51a22a01312a83ef305054eaf4c59a70f1528f22e614e25f9330b03621580af3858c67dd7856b07585c314babb54b681efa732d9509808a743e5a632f828daccaa5e2e96fbc93d44f4d7bf50b606247d1eaf64ffb084bf2b3dff7c730b9d12ba591bb058c51df5ebabb9b1a13932329d135d04b0a6087bfbaa57610ab647f5a2fdf214028370e3b305a622ce2e29631bd8b464bcad80053a922bdd8e36bb1e404fe412481e400c38d606887f4e09ce06b57a5210a25c5ab3c336e4849f76d70e49707ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f0000000000fde01c32e80a1d7be5cd020f55d70237eda1c9ce87ffeb801ac6ba06fc87f14af7ee001f71367e7c399bd5af9c4321eb8c9b9b40f0b864113fac97dec99817e0863eae150b7473246a404791e8df7dd87b891f4898f4b62a4b4e8a082df0308bc9ed246779248ebfe250c90ae494ce65bd4301aad926c3381b6502a271051bc924d883b45946fdb0d0aecc8016cbddf8416479646852b5dc7bd4171f2be349cef6d631d593db44de0110a7285436ee91b7f614793048f59f7643005f80c98418e7503c5d3d2dd66877e556618303b288e4ed6173be39045e72520881b6c8a03ecd4c0a338fc252f38f0155cc614e42e69f6623d9148764c1650cfedd235648677be68f1a0c3ad6dca507bce497191f654e0a8d122823fcc44b875941639faacccd1e36f881520f9e73baa1a785b410a30695e20e2921560b1dde27097f64dfe29f80a36ea47392454dc56c11eaf29c69e2f14aed2ddaa15356e072979ff22f7793380f0ea8e6c69ca4bee3556fd7a8177a991b3acaf3ee6343b544902ef0bfa7fc8224c3c2f72857cc1d0385def61edf058cb83f5d5f4b8551a827e740855672918ba5e89c33c49a25bc7fec3d8555bf75934dcf455a114fcf9aad62ef9d630c8e971c8dc03573d32f177ac1a88ccdc32cdac46bc0da79afbc03e46b90ffa0a4485b8ae89197e28b4305780218e3ad88e2aff514d9831e5ecd3b8fa4f63b1f5934531e4e4a61d39fbea70d790b2553cebc7787b1b725efd6a7a5997dfeb8cd1cd64b9592e17f8da680967f83200ffdd1f9a6761a401a68ad4a8333a89826f7eaf8ac86a1d10a90ccfd1f6e39dbaca8d3f36e6bd26cd19f39c3de7e12339097a36d31a95c513f3b429f47b2cfa51b092d6e656e89cd4ab951621e0a464be157cb2b32863697620a018835dd4d26617d14615b2f8dede57f3c1ef76db65ae99f0e634e8c92c9c2d23fe33ef41326c80de7ba895358d9c905bb902a33c2cc9ab83a8114253c0b71f2db8f6686f57a8eeb1c8d931649767a92946043da9b56d0bd108202ac4618680ea828f731fdffa842f0da822f7b3d938cefda7236f87244e7d6e45330af9e7fc6969efa1a48d8d4e2fabc4b048b996dd483a0b095fa1dfde3df2e4e9ca643528292ccef9e18e393abce26ce80dafd197f71acb86ae668d60a8f248990a73a034051f83d98b52aac97c280810a497b2e369f9a98e66e43f3a420b6c5bcf64fd1df00310d0f2332b8c1b48b0f1a7bf905388663ea87a17bc351a25e1f823cf252c4ed6bac6a0befba7299229720f55dea4cfed0878ee992f14070b80eaf0950673174e680a8ea2725cc6844c2cbaa5340aeac5861491c962791b29cea2c10ef3c4fbea57002ec0b724701098808798f015ecdcbee8f557796d0f948f7b134f613537e27d5189a233dd93845f57e7ee32b2fc58f00829cf943222628d28ad37d7e8698c0b43683dfcc79a0fe795aea76363056c4a2df20cbb6ea1a94ca98429906c517b4499ada8ca7c33f92099c24706569ccea7d80176015020e8875ac5e804d158f0e6cc77390960ac846eba694fbcb154353ce31ac0f39e7c140421f32eb5113a4a8a82ae5714d7eeb6dfe172a114e82520a3324e2e0eb57170e4d1ca0f71e17bb5039379ab74c82bf1e59d4e18d6f8219a019e1830f403db9203611a7425c19eca17335f8b2402fe724178eae4873bc0d670c86c35a678a94028e8fe15a81800975fc633d86311b84bd817dc4823e555da13d8324b688522a33ba071add3b52029e71f53ee24560005461cae8646032d5a7647a051ca9173e79ff0684b179ddaaa5315c63c809777cff4b9087da9d13fd70229060cacbc4f6a5173d9e5bd2236239a8f3f1b35731b0a8eee4e1dc351c45633b381319ace4f8e67480439f8ba094e4dd6f5f2302701cceea42fb092459a110115dbc4b4a23848aaa865d256dd49c65e2f3e78598c0a676eb92689ff84ba3c62658f17be076913bac3ca6730d7b8fddc085f0f293598e6eecf7777d476c50f2732df4abc81e448b68bf6a89b8550ca99fef0733a99d6aa6819eebdc3d0be749f3902b7a9468fc1def14bdcc78195cf37a56aa47fa97017e6051c221875ab74e5af730ed6c781d09a090efc0fb39013e193c1cd6d511010ef87ea4d415cf27e519f1910b2ec08e0f96271919991d8f49cfa308c2ab1256517186628f2b68fc08fbd40c083d0e3e76c3998f7ba7565b4d821d43e9f6a6e131ba11c988581af34fa1a5c2f38e560828362ffbc98b532ab54436634d51442f91b3a5c270ecd05d6fe23ccbc6b8e6d162b0b052f2e8c5d757854e82b930aae39b08ea2933ea1c24eca311a27d1423a6991fb632e82556a01141078a6e0d926540a82d038b91fc3292d655713739a103ccbc2d00c2cfd3db76e059e9681f522d20b53aa08075ad64770577c8f62f1fd5fbc035e8ab1adbd422691bf8fca969b5602ff113fc584cabcdd9b09abc611c5d018e1e01b6e9bc81d05dcb2b925446a3c99eb7d6f33906b0bbaf6d54df9544d3d45008aec204af4737705d53c00e28289e9f5039b7c10bb124486c78026f45d6f86232ad8758658501756732f7eb25b7942ef8e1ff8001aa3fb223282292e2a232701ce204a40ef5d33d56ea348788dc75d2b189f0e654662f3fac48248728a99e3c383bcb90a155c975853e60142694ff51a99c69e58e17fe128400196c2491493f132cd894b4713aafdd51ba62cf9b9c0a5780b907df4e8c3651f61d332b27dab4101e686cf000975f51ad9714f34a55fd8559de60146baa51bdbac23d7f52d0572de1f2606d2740cbaefe4bf7c481f01ed4241b0fb11e876878b2146d35936d373e600fbc01af91081622126f8bea375d7d4d4bb0798c6351fc76740122cfbd2b37a2deb1fc8c3d1418c5b372474579e74579bf586c3576507ac5a2403f35365b1138b23a91ae42b452c695154bb0e9f207f8167a55055dfe777f2d2f02ca6e7c1d058f30ebbb3478a6214ff5d4408e5bd7c68fa2e2e72b34031026283537f5553ed0ec745934ad9d80494d45f70445265e0c7a44b8f623cf0f1e3829c430f14c189d21529d08de9c2edf644f1c73f31288ba01b473a0b60f5438610e9fbc0588344da1adf7bdf8de2b7f673b1092293a57863e1e57c2de648661ecb464d3f5ad14b16de9a8d7670910645d6774365543808a1120f6b8853b2744d5813e1638f5009eb80b4f80ec542dd2d26f38be0bfa39004e571d0d9fa317e82fc8bcfcfcc22c290ea08a04c7b23f374e2618282618b563ad6823d04d782db515eaf6c7ccff0925a31841087d6564b94ad4881b27e1bf5130e8b125783e416501aace48fcef33bfa0a021f45133af6f587d4f9db7e1124238934cab6c201aa9b3a2509f47b912ee8c08b6e26360e1881a191674f1fd4562b083e046b309e0188d46ec0ba1eb3ad663e8da09606816e71ebedf98474f332d0db48feb611d9bb8c9c9278c805127e5afeab725ec1e277bf0f6c39579a62c4fc5af28a08fb689f532f70a97379c1d01b5773e66a8ede53515681fa99e705875ee753e3fe1a5d1dd4fabcacda6e6240028869bec9a2f0c318973a47efb55dfddefc344b52a352bab66d16be34bca355d93c8f796ee45ed489c202806e57ab7504752c77f22f371026a1c659c9b8a049ee00a52cb513ca264b58bee06ef3760d30a03035768ac5060e0f10e83f1b323f9e52d5ed7ef4f520b04c8131824d98edc957f955f4f56c97e27bfc6ebcac914bd2df66f625e6cf8180f71b6950b831bc2144a7eed754373ccba95c11904e90c769b891a8104cc43e675266c2ab34e5341869340bcad8f0e03274a33069de50a132f6996401d5054107d036b8954fee7ad59c8f066d9a28b8e3e38081c46cb2cdd03462ca6bc3ffb68e694269e6664a7e78eebbd079db252de1a5e4d163f651e95ae1a364c95a713910bb10f93e48c39288aa7d96a962a16624de9a6a6d17c0b4aa76db1c1d384ef9584685f92f6d89d9d7c76a90f5f2bc5c5ad8f6e23a57330c99bf1a28b08bc6865d5bd7fa5df1fd97b016c52a82153eb51e18adba7306a38cd57ce9d086a85ebd8451207f0adbfe103f78e62652bc415ac7ff100ca3b6a2a54c24e1a1e2ce09d1da16064a791b50fe4e7fcadfec2fa4126b36f18ca19da22734de293bd5a46594de528a2b1729b28db97f8cd9253c643819e8cdaa7a9490412f97d31fdbe291dab8235fbadb8e6d572d01730ab6e8a1a4d85c4abcc45952b66556b70e25b761d13b41d1fb89dfe0f4a4258c471401be464aac77e77a05e0003c9b29891b5320d4b4c10605d76bdde0b68f3bf7dcac410275289d8efa4813c0e0771438e98e5033a14d9c024db5871a365ddc1b574efe2498fa5ded028bb1a7298cc34e9cbb97e367d1e3e43f0f12e18108989bf6780f17410b6d18cd04506ac1ab3b9c5723b9f43523254ff36c27410d75fe0bfbee904079125c56627db186aacbb5983a9684bd59788563370724326940b5c8e235bc3dcf39095f6c73333591c92353c1c1eff96c7cf8d63c14e8599af2d3ec4e802a2da1a912097cab62d280c4fe934a87819f5ddef95e3b1419e8aa3064b1da8c071684ecdc61666523cd8152192821a3e11b0a561a0fd6b8941b61697c986b0a39c67c917d2dfc3c017a7f65977039c3ffa967583285201a75d291db2d2e05636d143977efd8bc62d304acc37c182a22429db7df3ad5099a3ae55d34d52b0a49e86737228504f2f31302604e981484aee7f2c96730e0477c909d2783b944c694811f16d18acd2bf88048fbe1424ba57bb1cf99d9b3a4c0160a69f2f62e1ba92616e68a9579ebfef073a5754dcd79fd7d639ccdcb579091442ee3d41366f6bca7c3ee3b55e3bef69082a8a2f88355cd4ea9f563d934247c09b06e626e7c879c241c1b894636a7d7baa0765596bac635c2a4c26bc6be3ef0d7473cb93a627a9fd9133d1910d72214cac07375b4233987bc113e40545991ea6bcbe77e9332d567da5a9b4078ba90a4f4708203c2ddb2e7fa846014f7f147dd27b1dca90339781e866e3b2afb433f45b303f3b1d8f5e0770f42b7feb90accba31ddb21b1fb6f5cc9f3290633faacd008a50ac589e35caa71f221836352f7d249e125e0cb624f8da953900f26fc129024d81b5e0a7e9b9b3805f8d1b1ed0aaeeca905cd8951cefed311718e3963932cbfdf1a7e521b7ae0066361edea58d435431d51df47d9415b73f5d4d5cfed2ec0da9033182a30ae6e3cec2373000070804f5a09d208e2b640f6b69609a888915230232714a622de83c22ef36376a3f6bfaf3514f58363010001145a576b275e87377018d2897a5a51bf8acfd93e3ffaa670424e1b727531b24511399c8bc193f3f0a331e9f7c231aeab65a960ea013e33360e8fe8a72a326e5396d56307677e497b5630b30cdd259337b86d69ced041e50b2b1e3bac8ea04a790d2e4222ee8270492219f821925b9e53532e4a8febef12c979c06056adb024437fedb6258f657a1191352aeb18ee8285a78088f48d70872b06f1af2a82a1b6da0794603a192c3f641826d979a9129294d86c1a5a1750e0794eb4e55cdf04abfc69bacf314289c31a1e0cc6579513f387a15b7cbe01ba090e58b83a0128efebeaca87d7e26690ccd24436df55f91df89db8802cc6f5bd9a83e0f16c12db9ef714157e6cb64bd30b6e0f37451b7be69029c41b504316fb773d0303d3f7abee79db307ccca10ddebe27582604b49b6501a353cf4f46b3c41125d3edf1b75a7fe2b5b9e4bce8c16b607c932cc274c9b88e6fa57df70967b6c585e12371c0da219948f5e8698e8a12f6498128bc037e588aa13a238c349923b39ec867f33447ee6aef1aaea4a3a59531d3351c9d0024d4e343890b3c81b4082f898c192ff9d24aa2d67ffd398627deb446930862596b07eb0e053bf8343d2b473b02f30d58d97f3731bbfd642fd7238e78e73061e7551a95ed8dc9ed4dde9b2e4820524c9a2acf06c7c8c3d9dfb6011a9b553db9752d19f935a3b54169170651ea381e7b581c4777ce9065a351a05f02899512e5abff3c1e19829e38a9589138121d91c3c8d264ce45291bb81d4cce6f773232b475fa37a5239028762bdd940b7c57ca64ce6a80eabf4a3d4f8825162ad71e0fc5ecfaf1b4e477fdcfea2560a5d261e17ac6bbaf1bad39863d047c7a2565b02c3448a5d98c8381439a3c266dea59bdb7344c0d6d5b6efe69816eb78a53ec182a1527164c44799981b22b19f33e9815a57b9a8c5246f706a95aff9ef46d315e09077814e1b4cae5a95a6b91d10afa3c00d82e92a2b06ba032f41b82bb536ff806436b55bcf6f294e9e483bdf89cf3d3800e2dd5241f38894581c48bde1c03762bf356f91fc1709c4cb256b44e9d3dc797944448e8ff26ff5fa4a143db0b8fbc046667cf116446e9e96c33c920511d50cffa998efef3c31f60c54642d4e74eba2835fa316bc0b744fc0c6a1c1f417ebe94e8efee9a9a81faa92398ed8d4e391c2de56b5becf7f88f9ff92c397fda0cffb200224c5989e24e6b5b8586750c94cc3816380bf3bd78b0819b6a061029d04daaf4e7209b01809a96d9faf171f2f0051f66c9f209957727299378077bb7993d5cd8498e26482f728805422b7ee3663a2e83ac02c139e71c1db030d34c8b47c5749b2f024933529a900d3f3479525cbf042e9c56ab27a5337f01990cc9fc857f0109d1066a538d11c798b4d1620963561063f5d5550b5e8e0273ef4c75f449d16e880184afed92d85692cf41027d426e2263442c109b9a57700dcd13ed2bfc300e11ae68ecf15a63b93027e4ad349afa243010282c3500844e11c7e70ef7ca543d0866c42a2bca6d32f16c0dd2c7d387304b9f2348e28f4a1d7cf8c2f916baca3c56f3679737aeea1a186fdf1149c512366973b68cdf025525fc138fe27545cb61afd1403cb0d86d0027e3bb4f9f37d534f0594fa68409cd566d869e47a86338ea1b9583dfd09a5fbb9f14409e8c44cb1c2cbe14c2fe1496515612ac80bd613e5fec353f1d54be561ab3ec48901dc054162c0ca4b2260efa5f4d40fdc1437afdf12711d7e47e1f2ecdf1d2db1acb489c1ad72a643561cc67990069406259da6b8a1be0b0dcedba67fe860b162c2578a01c7bdb6079151e050ff8c408f03f01f2e539e937c559c68826cbd9e0f812c51524c977f470513bb7c5e6a6ab4ab2f0f3fdef09aa68bf646ac41b5ff24efdc802237d869f8c61cca9b5ae1a6e714682d0413c013321513f5185d1270f1be317ef28ff49b7d957c9f57d97b073cad269d868e5220cb17d498dd87b08e04e1ecf08114ce16af5fa9a5c6ed64c4a1ed089874efd697b43874b315ba68bbd569f4ab30bf3bcfaf0cf4d7a1d6bfc7ad8e745113145ffee4ff65c2f45e3a33d6ce6836504e46b2504793544bdb13c7554ca60d1ed538f406c1736728191e1a0c7fb493c2cc9f50bc43cd6f1ec2ef121ceca053ee304ced8e06670738fe8558942de4a432f489b5500672ecf401405d3d4a8d1dc87afbae76dcd5019c17b130166778fc9262b440178d153d1db762c915a044531cef94b4463e3f37f0895eb640649ceff3d6064e129c4fb4fee145e3db488163588a447da804b5fcd24853479cb66654e3296e3fb14162f81b96305a258aeb3e41e0f95580a7c3b550490bc92fcbc885c1c08d4aa33fadc78b31cb1dc6b6de873912be2d4a975e75cbf6e268478166b3f3aff82ca941f72008e5d0825b4aacf97d2c8e8e91803e80d707d3f2c247f9c222e75b625276777df11966cb6306e764d2ad9918a09746d78246318ee2ba0ce753eeebfc81f0537b9fbf54a33bf44af3d33ba0ecff50a2e6b5015903b796a3873245c684ac869cb0b496d3a1fde2822ae3399b5a462d19bfc437864cb37dba8503671defaea24361c253916f686903d82ec0114457501831abc5b843ca8b905a318ee0a19d261d91104abd055ba419f6392f8ce47b7319b47846a1e9484767d722d246f38f578c77047c07d0dcbb0d174655500eaf0ded2696c4e0e286268b70d355cdd61c75c937fc1a5c1a49cb178cf0b79443afb3e6d2fe89d33e8cecc4836068ce0337d7c40f1669f797482a05adf4a66f7d6d0f5736b250db0771cb53e232d457ee0a38d78339dbc13603ad3cce223afa6ff228ed4ca72512a8c87b6b5de090b84ab3162a2e0ddf2d0bf9594c0d7c7ae1964a7ca457ce1775d3d8f73bdfb2c11b91392feed8e546cd4ee795b27456f0e83bb69d58c327325302b3b377200272b0a1e6c59f83bbf3c9b7f51d6910c1aeb6a0b9730618950bc233eb1cd0e9c3c3f56385dd45bf162595620738d40659c2332e0b2a5493bd9cacfd89354a2d21a82537412c4ff77cccdd07fe64dd81c292110ec3b549cf598003a2f5620b62e05f1fb78099f0d71907ff2cb57499a8d43e9a3a5259cfe868ce6a13617b465671101b4ca9f8fac4e96444b699c62e4d719fd9b9b984d635854fb4ee6ec4fa05d2e6439b83b99b60baa348ade0d8c6cc61856453294a4113d53048bafbaa960e51385fc86d0103bfa92e18786638b6086dac70e4c4910071fa2e8dd516701cd1e1e9f1a505f71ca754e4148ce1ab3f1703de0927c3b0ed27680eef3559bc487b93cdf87bc797b10d40f00ed621e51c5f0bf0c0c37e4ecd2a5200ddecc6b3711c7307e0f92a267cf786d86dd0d9862019a06a3e46f4a830f376ec9feb7ccea52e907fb9e07bc5bb6d0b65f37a457b61099ef719c930a8668e4f8d6532849c629bb081cfa34c225e04a81300fa9ff89d1adcf8cda6baac27498949d120925775e3a2555db999c6193623eaa31484cbd03fa2c95b0e243d90c6230b5bdc03f96cbea3aef61b96c73a070cd55bc23d5ea9330ce30b18a92a26060b058be34ce5e9a52379851e978d11688cee2af6afc85354cfd7372a5de96734ada61af62f3e1ff96009ea7080e6d507c228fa0e14630e5dc86160ab0e7a7f53a172fd405750a6f5d285fcd58f1ae55178128aceeccce7e54214d420f3554f6eb50ac449d227d590a264b435ed77d38a8553dc11960db070ac6dddf3eb70b568746186e181b4075dc0725d74d812aaaaf1a49162e4922664e6ffc13da33b96080f36fe2af3e67e66a382caf2fb726e0d9ea24a4cf7dd19b13e54cd4e15a6f15d0a275367dace48cee1f783e16b434be9ba34ba96bf57faf099ea46c2b1a869c747e4d4f173a9051ef0791b3769ec6006fb05ebd151fd7f0c02103fcf82f0b36aeea1e2ffd8413ae651f1d39e8a007e0740d3b7f3f3179e7c00d787633bf94947c4c8ef1159e800ba43fccd3e2b51fe7658545e99f8a2279ebe5a9775441efac8c34dafeaa6248678a81f2bdb6aac2836395d2ae7cfef990f1f93c8fe77990e19dd09ffc9c694c00a93f9c888ce384d013c3eec0f2de539e32e24405f0a85d9e942dfcf08b1a9b9d418922dce3fac4ac146d15e267a9bed5407239058dd4bb0f26936930fad9e95f189d59551e5b5436dbd000d6d51f666674ffdd56a8d7d773acb79889796a67c79401521e4fef3e1c13f336aa3730f8176d5595a212c340520f67c60d581964354e08570ab76eb1b4ccb239a065af838e7e12cfe38b1eeb576a08ca8e80870a28423d9ba0eb266fa1452246873308b04991fe3d0a018638a7ea504d91904a9c1ab40d91a6252d0362619fce83eb13c2c381d0f0051e9c8fe5b864c00ed5c559beaebfdf408d60c7431d88ad093c17f95ab80aadfad695d4a5bf7e8d5de9ce28ad161fe61a78967ebbf60299703b64302e40ef312013d8ae59437d6d1e50a69791c70e149c098db71154e841fe7d42afd894595bc1a7bf6c5291fe8539d8f9e61b7aa7eb89e397985470d467a0956a841b46a7afdc32f8098013608a49a8cf3aad71121e7e75a0fc3877f058214534d4884e1cc290afac643541386d1775c40d02d5a24cb415b13e687373b0f9279b2a71bdb5e935e12d5ec025287dbb0ae92f0eea99712d7d961bd8650386f078e0402cdfc184731b8f69ae58bf41660bb3577ba8ba0f39c0dc5d37c16743a1d1d3048afdf79bed75d144fcbf84bf31d31a49bdbb09adc9d47ec4af826fb59cce63e573ceceb7cb65986d6705096563ae56f4d888b8b35e9a417f07e134223ba9920a6da3c3338846d83d680ade617cd73d01d1e7b02bc1f38e2f9aa3d72ca38ca6b8b2b047a4ab4179f9a2486a98d2c47f8489e19660d967e832667326c5ed19e0b6c371185049e236809746519b3fd0b2da81bb047581751e5f000c27f90af1e9c6f94122a52882b25124c774dd2bb9dcf0cd1119b849e40e62f4fac94e984caee31dece427ff8e82d60f1662906336a28904968bae02dbd55144148f7fc0fdee5de5f28641e6927ff9fc05a6bab50f7ce11d0130ba201dd1507c8dab4430f37b0259fed76e7d7c2eea8e711f4c9f7e8e9e22540201003c3bcdcba1cc88821416ab7f84f8ec7d39de552f12dda67ee591a5e7428e72a041fc192d0763ef7250c1b2cb7b28691dfabbb1107d1428a48eb70b8c4786821f01008efa775c2dc56e24c14b0e3c699b3f87750f15898eb03ecb05c1cf418cec83b445f823ca7d9b5c6d2f59d2babc66fa45ef25699c6334df7096d63dc863dc5c0c00000000000000000100a66e571f0aab1e56f1ba167ae5c70372d798117b27a1fb90ec0e0c420a39be295775ab7d796f04c1eb5bfd26dbea6c289dd71d3a9eb5ecba47ec7beb2f0ac82d2100333473f16d482c8725d51b4cef4e26ec24b4681f9ef8523b160fc0880c6bc1cd01ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b0000000000000000014bdddfd920a81d4344b290ccf81563cd88d228bf514ff6c10a14146853746190d63dce655fed7940453fcf78f23bb58b1a7bb3a389bac35d05cf6becb7fa512db82ac459e02256acaf1a8ccac2c4b5c581c3f178dcc4b047e32fa1a7e751bdcfc6f42d46381f89dedaa04e8030000000000000afa43a02ed891bab3de4fb88ac8edcc540d7c23085c43c821e1dbf7e42d710e15b2810a40cce7baffe1fd1a1c9334b8dc2e54fbeac600ff17a771ab8d7a4c850001004100829788fbd48367fbf6850ccd87dbca6e0e9eae376e4c320704bf2b392916ce5bba296d83ca3e3f8d5dbc9c34b273750ef1fda0b50495393b096a9206ae3c4ab5 \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-2.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-2.txt index 6279ba68862..f23785896c2 100644 --- a/zebra-test/src/vectors/orchard-zsa-workflow-block-2.txt +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-2.txt @@ -1 +1 @@ -040000004857519825ace8df0c136ea906c84186a08289804ca92136a2accb5b2a0d73237fc1caea96f9f155f5ca179a4cea64227ccec90601e7b1bd343951afaaf69d53263828365fdbcc29987bc48ff88a38370d836e14881e067ae2593d084780b4d70a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025200ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000200000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000102f241118b2ce309887cb54e04f285ef3e7044d4d89a65b48115bf4cfd6f9896182d2bbc0a5a3bef45b0f3dff77322b3b58dcc50bf06f702a02703126476579d16a215e195852ef780b03873cd2de6a98a7d1a62dc87af7680e86772446b5c94b1eb788ea1ae1376e3da5c2e63b768b89dd18781480027047f8318034f87777521faa5bede05e4c073aedfad76dac1541ac0ba3339c4416f25f3470e208ba1a2bb13d422ed8864ef01094df0cb59d6dd618cafb298dfe2dfb72e8b89db270b160cb6a35f47c6fb91b34fcbe3f79166cfd591a3b6f93aa5e259b54bcd06dfb3559ac58858241ce27b057232c47a42b1a2fce5350890e37eac2af7aa5b7f7670e4438f2b4153ba7df5e1efc39d02d611e97ac5a27678d677ded19581bfa267ef635bc53d7577437e538488062a5742e995d45bb27225569f898c716f6e56874073c2fcafde094807f54b8d4086f30f8120308314683b40216a8037f67a42a336cb0bd03715af7032b3acb4cd623ea1c5e0f13535ff4d43c604305927881727d3aec74c0cf25ff3409283591aecfa6bb353cb6fdb93d22184ee775e60165674c70ffcf8c5e960dec18392f0e7708a365b8e3bc4540cbd271f1778e3e49e3278f846d8bcdfb07420131a205f6df385978c9d419b7067e5703b824ed396a2939eb5271bf8d97db5c13f095305494b27d979065bc53ee874a8f4eedd9fb2f5fd910b1d013bfd601ed98a0e8b5e8b4079b0c070b992db0bb1a3d460d6512c41b585aacd4dd31531dd993ddc4d07f1fa254343dc7f6b0e9430e091778373c9bb5f97b7cf41f109d0a466431785ea10c01d1bc1bced022b65de89d7e68e506d18bf3a48737497fcf0864a8957c0d8d364c60f5332982478be920db2debb59f764140ff19baaf754fb92c15d04b6402ecccde5c9edc02b676a6c4e421ece044e07c2d65ccb9db0c7a7b4a4473febe4574c6e5fcc13bb98e650c99f0d22d156bfb997880070884828a4ac8013e795157555f0873c09f0c74a618fbf3f7601896b08f9b47cce7b1e744cf56a809d8c3f21509aaa03e4eea4b05bc6e6c38e5dcea51db462cb39a278cfa1c6ea88e1a0aa968baa499324e5b53989b0e701ae2947cb5fa6d1805038d764b52dcdb815fb279d1ca08a73f32861a3c575d69b3aa04bca7d6d2443782293764eb8266088d1ef484d60e946170f847b956bcfdc58f29a9da594bb48f13eced3be11c8bd65b825736ff8d7c84579d84a2d3ec3ddfedba5a9d49a6d5e26d5171a662a0379a3a9ac2a54069a7929ae68c2052864064632419dcc5712a4dffc999dcd0b93ae02764c1bc44aa6f48559361834afdfea92853e65a581722eb45b9bd427e06048767893be9f8e159a09f296c801296937e3e49b6dbd603f2001e0b0cb64e101c75880fa119c02379c5bbd992c41b42c7541529ffd99e7bf280dd61700802815b1c78f60b1b6b47ee263cda1e7385c31fedf3c161b794a1fed77d7983779ad18ccb55e861c4fd6c8563a89eef199d3127e7f9d7c4a8652a7953641567d5eb1392b5bb9258f4dbfa39ad638b1decd0680b5f040de55002301fc32a168bc7571e5744f4baf8f298069797ac9219e93591800e284d423bcd89111daa473af8f7135dff72c5d44b49c848ccdeb5bc2ad9cbd447fa6e7b5a7fc3d1b71fea307f8a036d90d0bd14cfe84b141134ed138e17a1dea008e7e8369f0d0b80535974201e2891708bf22d0f979362d7d5475dd1f048c0462e17e35567b18a42d1c62c1a40c83d9a8a2d82b56926cd6944d4b54e2d19bfd90f27ab41d163ee9d4fe7fcf952026fe69b8ec19c23740f8de6da64c23adb4b26b8c0d99799e144980aa8b00a2bf62d4d2d34b16110d730f0e7d74ef2780596b58312026a2ec5c014415b19b912097e783dca10ae8b7ace8c14540a1c27d291210197e31576dcfa0dc8b8e8efc628dd0e1266a1a7d8a307a63e936dc50b1b795bdb2d9439f9922133bdd8d5cac7b647a641ed08993a233c8408688349648a13a3c323c16408ac20ac180e54d624fa2cba1d62d2d0da978e7ae3ce90a901194a8bb06c45a505d54f0e754e1e851fcab54118c38b0248a9c82df53c0dd66285410032f528e1f27cc61336927804680f6d26eee5e9670d094c0a0477213dd5dc23cc6e8a7915c13ddb8c810750309588821d32f7cf6024b3893d869d950b07a5e005b376faec992ed2615d540f4b330f5fd99461cdc382fb52ec354455da172df92335c3c92ed685bf27cad6019e468fd753180c5d3db1473f481f47f2f9617c65028225ebb977d3085723c1cecd31b07887e6a16ca101acab9d6a95785fdabe33c661202fa7a9c0f5fe283180f001aad1569acb68e0b8c787d40186a45ebae456357f620f2b02de4d879a013b0d6ecb91ea075071f970640f0488ccec7918221afe0c4157f56b5061cf333ed5cdf458deedb92360000000000fde01ca7ac2b5c6827393ebbe834f9a037a3c968d2d5b5ceb34a449526335d16f2e8b299750391903fb2955904d7121ed341ebc1c2dbaa1b2dc56e45b38bc006e4f13e1497b5197713290903db72384e2c4a55f92ef841bd8d682d00aa8f8dbab67782754262afad5ad6935805579ee8b44b6c1e944e93eac64a0eb6efb1297bcb4694382d4757ccae338c9b6ae5f5cafc5aa6a6faf0c2c61040f54bca79ea17318a05be90b29bd1323b88528750ee701f2ba804fe8c575470fbab71d8ca7b6109a0bc86c79107931c4a03b11b2b320c2ff2eecaf0d6a80ba97997b210d54e90b9b690fb0583d2d9ff175d12519e1e2f2a8d5608ebb5c05d593dd4f7619a3243ec280a81313de562fec5466955d6c4c4bae349fdb1cddae13166ac4427d69d0cb86e2fbd8de1608b2eccffae90045a9df955beaf4ad5372701c8cc7845d47d445f201c48d6cc66ff3fcbb33a5c0469e9f87a2171fa59adfbe264e0392fc607b5470d8fd4bb68f755bc1ad1490e264e6482f2f0dfa1d63a44b573abea01aac25bd32505e697a8b407cbf2e65902f8cdbd5638bbda7b6f1ec4b6a8f91a8d9b30cbbf72b71223b888f7fb4468aaf2384cc1fe314cb09a7f07e8fcf28554b5310041efed2ee1ab321c9109b1cd8d83ec1b9608872a22b4fb52ec9f8ab6ce0792d07d79e616db0dd26d6742540700308b23b89926586f723d413a7672415816d673d03de50d5e6b1003e3be5eeb3e028d8b9c5cba0d39c65bdb8bc1bec6415332d077bfce869df76d118aaed686eaca41aad835231f92a19525ae34f84a9b89b98342c03b0b50387e64f64d1cae3d3baf9d9982a9fee6cbb4902a6da89dabeb855ea89bcb2be427b8fc408fa90e7ad20647a457f03c45ee276f17633540accd994929a4f419c4a038664cc7ca2c9dbf585bd0bf9c9386500c81ae3a3ccd24e3ab73af7af7b61d71744e61f9a678734e11aea29e7dc46b02bf4e563474c9b62b0920f151a29a65483682ae968195da27a14cd3821535b9f768978eb78e052909fadfa5358f224c2c552f967ddf5e4122da5f5c5c6e5693c1417d32f78a0ab1a18624a84ee530f11eb7343c8694e2ea3602763725425263c7385ef31d4ebfd6c1a5ddf2140f098f8b12bfc0068ee65dfcd3de1e64647ed8abcb55ef3033a4e14334b8f95acc3bd23e703deac44c88c08dc284c44ac027cb918a8c0a4b398b1fd3c0a0bf3226005eba5e842b7ba4c5ed24be05f59c1a5e20326fc9348bcbe1fca374420843aab753342d931c79f34c78f82a5a0d611816dd71dfa011ab389b80bfa51615cd0e364a48001925f833a3c5e9cf27ea5368c4a27c45910ef2c9b51f0b9a67990d380f567df675eba3cb6a253788eac8d694dc0b2dc298cae8aeb95a153caf37af972f61d3972846889fd702a5e6c5d96b29decf8e94e0194e353264f1e67ec895e7945e8a36fc23eba473192d0e32f94fc4c7e3435cf1463cce675c6d23ff97ddb8b54b49ef25d1f1403c77f5a90c1b674be717d1c05ac04d90a842f04a9786744f8e3541ac3868634c7fd7ddc1b914e4b291d00771913699c3014196b4d51b72628975a48e29d3baf9859c00af414896ff933a02942d433725a24743b4a162006e0db794e6fe18520bf62375ef2482f833834cee472d35f3de2c877a6de75e8b051f20a90cbd3c2c76757846f3ef83e3298fa1a65f905d0d7b632708615744f87e188baebe71d1d5b7523c37d55edaff4ac1ddeeff8ee7917cf17d932076d9776c17b758a21d7b43d841f8a8bff61834697ee5ee161f62c972032a6189ac44797d079224d7a719035ab2a7e4d309cbb4357af52bc2b75c2ac29cad1ac9cf323eed360e96844fc2af8837dd302a06bb7570402170dbe7de0eef4716ae58cb8066f1b5a7b1a4e9ddf74138914d6580cf26b01f9ae53b28df2e041ea1de6835c24af1830f0497949dec2890e9a15c7d27a83f998a60a3b9a3770046d8db7dd2e791b79b985c5a8a324743b5f8969bcb200d4d0b9e7c11255a3e0215a3d61d6b7902e39bf1f01edf1f11b38df185e843458ec511062b8dcf8e46a85f0c984343e0a1781d2b3b163c1ecfe797d0340c745fd878044ca28aa91d63fad063e40855300bf519b3545fdc195cab8a0526e715a17423cc6c1a5e554e178f76c629534e32a67e368600bc6102a7604f5e157433f35795eb916b529179895488f91ace815b6b10b2daed42f4fa7b6176ba26e440c105afd406f0bbf586c199dc6ad0e079e546d503bbe725b3c7ee6435e6cbc824ed874335356d6723e1ffb7e9e7005ec8ee53a134a0739ac6cf653eacfe4139bea4fd8646b6ded484ada12c8f1cbe71624b3945b45dbc4bcfd48f9163131586136cd181afaf8ac987a0e5791a1c29b7e597cb7b2e739f8f7bff2eed4195efaae644ae070aab3a08e50b32e700ac8d3bddd7aa88117d6aed4ee899b7148340cda14aa5bc87f13dda39c0ddd1fc04e29b05f033321b84ddee93ec0e57b749d3eef4f10bb76ae074814e9274599919c2fa2810013e0fe5ee439f7dd671eff3e80116b7e7869e8bb359df539f1ea6d64f2dbfe00aca1bc557915d151df4a5acd4025f77758362e85050285d3833f0ffbf12eeae55322d5f88447eae08feff98efd541dd36b1a7f66e5a305e2d5e0c514bca14bd48040183bcaad5576ea4ea16838beabdfda29feb22c664deafe60497055b75fde378010ee7066a7c055f60f792e44646712aa677f80fb5adab140ecccd706074025805699a80aa728acbcd605aa61aeefb8351d2fe4af264a482791e1a9c030896d120243a5c35fa1576ad9e94977f7d24c63771355705008fde0f72edcf982221e2177999759240eee0878bc5491de953e9dc6fd2aac32d6e9b275f244ef4d1f82530b503ab6bdb74d8d4e47bedd03b06413ffd814818de99a6b8cbd7405a77e4e20d83f3b146e51fc9330feefb055862bdfcfcaf9085e376c7fef753f1ea15764218b650b60004d720d01cbcb0154f5fd8f8d9a6f88755c35538995d02d1cef2d439e1e34b1eabdefb022f5b2e6dec94ce9a4d3bfe412cb92909fb74975a97654f047082ea424d30da3d62fa80ee1252322b66d0c1446816f87fe68e67811312b41275d247060dc8adf93da19c4608ee2a4b78cedd752b2eda7269a02cc407985d27a33653b8dd7e000fbe66cc98ad5aaa8d1a154cac00a35ccbb04329cfdebe3b38c67fe612ef013e48385d936fca6a2a377db479a5a695e645d94b9c8ef8253226d5fc685bc9a861a9dd2c9f1ddcb35de7948f6171f7f6f8b364f4ae3ae46f1c2f27a1ec42eb947c6034d8f0e3c606fcae66da1412def172e13a25b3f13abf1004af2a508e00c35c8db31c028899a4b95fd52c8c047afb97bb45a3290a358bf21a8d150db24c0cd4ef66292477d2361c1507179bfb2ca3e90b88d1390e3f8bcf15d964005c45323fdefae0a3314a889935a9619fa17cdc9fb20d8b4040ce4cb72a4bdfda8cf3bd91c269599c2404ac9ec5ec131405397eb69cc393502a6902252fbe1beb4670751a63228336ab5ddf738f86f8d9c1a20bfe95f7b93971b2c4d308b6c30cd8ff18b0c31e04773d0206d8a34bbd02422d133c7a3bbd62d923375b172ad9deafd6a18d874ede3724df19d7135584105675436378fa441956bee2de18c692172539dcf5a06c8b9a250e8d68ccf6339dae539966809f5235b5b2fb55060b2b851f48434129a4e7f543b33a19dea8ea348c9c7e3c6397e0a235cb7482255b7f980548945233a494c1189b3a72d5008b6da396902e6cf9141f01fd40ef00f7cc734915fc7fcf74d88cd6c052a752f33b8c9cafb806e13e1d1c4f46cc8c3ce802c13fa82d5b044052404443bb65d60d0a542424204787062092155b625b0c6e8f9b8fef65098d2d1e4167bd5f58c1bbbf8e3fa75af1ff48b7920392f2222294835af103f615c6d75ea63bed664d24a5dd8cdcd1265e0577960ff9dfa3f10b59b11e2f906e09b748bc8383a51ce8575a5d4eff7325ada9ecdc08d9eecd032735e3a9aa1b8a42a230284122d182a00c40517402988ccc28b18e82050187172e6cf1038d0b2088257090c76c850f4f237a8bda23d00d132b9d11aaa6b8de8b07d450dc239d3a896ff88ac07fe7b0861d842131354d7ce0e71c96df7c89f0221fc2721958a29975407de245b576442bebf0bc7a51963d7f9378f4e6b24f152a05680fd0264c29dc10a3d4e316b964258e304e518bab0c89854e287cf11802fb3a70318e52e8745a545b6e4afb0a339b4fe9ae6e8fe3350a900f81dc5c80b57d0dc34e3e52a9831dcf33d2fa47fd490f50483b7eaa573c277e3a23a61a34e53435650351f053c4d69dba8ac18eab53c774697ffa25524304f55943541b8ce2832ab73965ba95199d0c1aebaa55a179b351e9b79e284dce434a1d30f9493ebcdd036b2c3072b27f77ebdd27b33d2c4443b270c67f29f4f99cffc888825177ac9c306ab3c441f9470ca3c310dc7a1d912bbe35724fd7bd83b60f3c2c9e6f27e158306dd8b047b0dcb56a58b09d74807097e3ea6c48ed18b6afde4ef10048269749121b8595b6ab2e7ba989eb9119da48f2b8628c65bd30ecd8e712dc373a7599e813dea0908b3cf4a3e944a1a13312add7daf4a6e7004e47c267fbdc381785b0cc0d283c858e61a0a5f91b883bb3d021b11d5f570e1f3e8c1b321dbbe2f1f460491ce276363db26d67f4a5c4b52e90d60cb18f3d8e54f05ab790975a7b4fe24313008c8ffdc5d9a0a6f49d2f6f5e0de636f4c659114fe6b93b95beab18c2e1e9f4178394374e929f79964e4be30fc6832b849dfd49712b6dd9125b0cad541c0d2313680bfe5e4ad570a974da190fc7ed020c65c912c30e4e688ab03bdf0a4239e82bd132576cf5ded576c37725e8e7281c7ac1f89c6315d43403bd4cdb21f819582f9908ed6278290a030b03588b24362eaa55dd7af62706a6824531aaee48ff5a3349b50bba17457dfe5e3d14c0597392d5be2eb195a4d68602b340e9a2bb53c23ce1bd4e52b5413bf784add458238c55472d3081a246560e049e340e94e983050d18d6758d68d30af7cbe184987e6faa981fbd91f14931987e78fe982e5d20460fdc95097345158687ffa97e4b0a2cb7cd529a407344a0cc6cc00c7d8cfbc1b40ed1785ecb679adac85afd12e64eadf8ed5befe9b7fa275acf8d24dd451ebae5024afac634f6a57c3c7353f36e692f8402749162ff743b718454a35c465bdc6f0c3906d2d1b965597af4d80b2b199f929d39281658f18ca235f25e4c5fd202983d1044e86ad8d4e4087e343dcf43b50505593f786745b547605b18714e31f084069acf7a49593716e71f0305df1fcb2793e4f116605c7edc8ca82a17afecfed822209c5283705c2a6d2551239f64ee12dc9f39613fe01e4a7d68efec4c99d4c30d573465ded99ff280d205cedfd957b920e454dbed872913c05168695e9e3e3302e6af149dfa9cfb62a10db495c3b1890e057f69bb6f6f8473f108438908f3a73a00dbe6673dee6fd622c005a410a83c8110a9b22804474bfe43cc1bbbaa205f3a035c574370a76bae3d08c0550edbbbe16c344b706c9516650ab7b6ec2b2da40d5fa1ae8acc2c459a497e995eb5f7d93e7e5a336517c3b5e94df6ab2f8975361711601d32c7db2d04c334e5f25b6d6519fce5630ed5a47528809fdf68c3f6c722ce8244accad68c6fa96fc65e56fe3c8ecc3fc858afd19d0ca8c5bf6ecdb472326c5e153eba59a4ff48746166d97b5f02614a4f18642b1c0fdec9109c90601c3f3c1fc762bb2bb9f72fb635f7783d64199cba2be725ef4d9e2d8124fe4a50a60d848b21643e648ceafd8897c50f57d436bc426a115603a263116bf32bc357622c5016c11e6e4d1fc45d8df2b25da996ec20c37536858f6f3f8b82ad999512d428fff007e7eaa5fe917bcc16952b75f8b61a276ea34b16fad82e9b4d959d2a0f27f5dc05fcbfeb18f527433753acf22f9860f525a8914bcdc1699b9af28014f736872d7596e48646199ff957ac0bbaa7039ec9cd9c0752106d0f3db24ec3f84323554d6f50c2237160f78b67cc43297fc3088cb3718b96f477c44f9c6e7e539905211fdd6e04c17bb9faefae45562725851eee2ac5483d224f43567153f7cb91324488affe1e50a1212af1c52ee7cec44d4cd883c4f86dd6c95b75ca97c41da41d2166dade2c5e5335091363d8cc8d4a462f156edcf0237801fa231e61d6b8263751faa7b9f6ee4baca3982de862118f822d66bbc9dfd9633cee0726f8bd6b15281723e74a44a225cfae2cae3d118a036e205ba8a232dd50feb5050d45bd588b3ce086b0966ef78d06e1ee1999e3abcd5d80fe12c38f2ba3ef2a3ba2bdeae2c036982472abd72c2ec4d8bdc15de7ea865f9d2b0971bcd47d39d3df4e88246480118b651ea269583d7345e902071e2eca93f9c4f65cf561e5163c12757e72e93b024e7b1b60425af4709ccd1091bbd6b2311f88a5d1d9a55d22815cc77caa85a504dbc5522b211afa469aceb65e611f921800969e6e8b817637f75443075b2ddd2af74b7cbe5763f6729d6e513204da49652f14a5bb9000bfddb7c8090b63b88c2ff7b9f84733b51f7f7a90e9cca73ffc44eb87013bf77dafd015c62c8d6ba7271eb6b60ac3542ef92f3d224063b949aac134eb7353e5762709dc782134e744d42e8bdb81fa5019ed109bebc599c41320ebc33c340583fb063ddc7031b695126e19d015eda79ae501437d8f8a3ec5e764f0247ec60f7e525cc9c6f2aff5f2dfae3d9d85e8bc975338ae7fd193bd6e67de07d96788013d0deafe6b76495854f9493e3bdb2410e688c32fef4898e1dd3eed09cefa3a5d4fd81829717f4666eb296d02e8d2984de329130761ada14d2249fbd18a1dc4e0daba6da6c803817649743e1eb3798b75013394850761617b5564e85eb82e5a24407e8a50d6d3704eb14a54030d874c21b6831850da00b80693d85cb6b4c08f8b957448e035e0f9077aa7e200ffd7bf5f62da988c91d2bb6e1db2709ce70ec443b360f88e0828fe57fa57180b820838990beea1eeda793d2ee8193f3e28243950ec3aaabcb0ffc069c26f263c5a15bd5dfa62e843ce4286a5864cd6d6a797c605be15aa530da558189a96962ddbf054d517123fc1f95aa5fafe57a674622b9dafb43817e7d253f4acde57322f5028c95ec9774d5bd4e7bbcfd9186ccde50f2ff78739ea4c35b8b0eb00a02f08a9af872a2a599a59704b5392a198215f80417a8989b8aa0d0e1e264de0044618e8ce4a1d27459048c4a4eaf43816ccbb918a0632c8683b342f193c9570ed66086329ec25b2dc1947e526ee151a5eb9c67edcaf1e007983bb220a78b5a9a14d1e26ff78cd479a1da60304a501390d54102aeb7c7752cf5a0daccff4e4d0afe134e4869587b497fb2ffed338e9c8bd8fe1e27503e2af401a093dcae57cd943d81886c2f08200f0c2974613dae4b358259a346a98a4317ed90bc3050286ccf97f0dd29ea2d79a10406da58363f67770883ae4bd69ad69351b8321ec64e4fd1fff3a394243f18b201236cfd93405d648ee5bd96d58c29f9af9c2f64acfdce489652c42d3553559bed749e390d8445a04d47d09ed7a2de1c726bc73f881ad6630db065fc8c69a39fc798fc400a7fd7b1506b2f0f79b247fe2c42290047969c004a10ead36a2a60538e9b42957730f2cdb08612c0facd7c01754f714b18cd45f75a226ea1de2b9764d0413fc887f71b595ea91dd2f7c3e996084136617b8e5c611882f829c7cd03623b13e6290f96afc51ac5795f9e32946efb63a0ba7fb3a6ed2de1cdf2c38cee8f6b36f3a7f923f5320c04dbce729595edeaaf4f2f6d6b8c89e852d90b13e1e5091634384754e69627725f8c1c351301823e023b75c450538b7a63d26aae2e60140269e8683e0ea53a22fb06167e3b58ce81725bdb37e73b1331206a1791b9ff8f36bb27079fefe73b8608f0ab41a54aea08345c36a22480a575f3fa60bed30b26e4eb2c4f0d661d0378b3718df0b9eb61ea57fd60cc74de3dbee1e9d29d1b236bd22a24e9beef88410f62156c62652ba09377f698b05b0df0c361b69304b322bb6abb5fb8ef0c20e02c4c52fc5ad6bfa7a4688f976518b0efee423a971b145809107b132e14f992dcecb426f5698d7610fc8098b432242e23c142cea8d84fbda7e2195c6e38ef04ec6f36c50a99b92723032c2ffae20939a76163e2cfa75fe273c419829d751da757079bdd5579edee20782c666be38069f3586066ce36c150781cce80c3beab4681b2fb2b228e5f9662beb3579b92648e2147810fd38fd6f391134619007272796ffa240244dc7d687ac2576ea04d5567ae5520f4f5690eb77417de753f70b12eead15bc867622aaf86ca3efb8ebdf1c69ea580d3a83db55c0398d831de99ee22f612c1a3162397d6de412820e18404e2bee7227d1e9664e5cf773de37f65910bcc268b3e422fc62e4ff47204fb7adabc574fd106b14d7c3444d652b91dd01a8f4b33550ebf9914ff557dce4a070aa8cc397452a99e82b336e8fb7a1cb9e31ede9d52385867cee162052144830a3bc648f32710d937c3e3db620b6ab7ae1076760d5c83fdce3deeb2d08b4d1ae26802e68c845231cf581e5723c208338c667866db9f13f03185c219821ca12cca1a2c9059618370f6680914fbb11909b7f74b4866026e7f4157c8c059830763f08e04f7822e01f13cf81b3b378a4d9728fb637a7c0c01c68f7f266d15c01ee24a86b0545bdcd08e84ec27b4f8fe0051b34be761a7d0e2fdf0f9592fe7822663e7bd81a87d41f35ac24e9c4eb28f740a3ab020732f4c0904e3245dfacfca22f2260fcd5355c1f2efb41404c29055323f2fad42a29c8c88a1d420c781ba53aae19f3e237410a263fb264489cb51fabc8d26e8d2f12196cef83ec1c491faf03ae493a92cb8cf8ce017d002e0718382fab8078583353083265b24ce3488a89de647b7b09ca1e9b14200987af22e6e1b8902bbfa1ec11b5b803d0e34dc51575dec1b39286a164eb2f3a1b844f3757dec28523233b6959f37c496d4a4b4e6d5eb3aa7b4db3352f23a23506edbd3c043a41110afdb6616b5eb7ced0258d0164bee24f7280612871d7551c3283b5c405f1b32598ce9dc3b9169a7f6a68fe20a7a16408059c418de3ca21233cf71269c4b552cae579740cd66dfc300c2f121a2122d8b3d968c843a950dd22d112deb6790082e4255a7349b2df6de8c33d816358fd95d3ad29dc9a0b84241742869eb79ab3ee20d62d3577fd66d2ea36962ed455fc0a65a8290c00b0043e30023544f672a78bf19540ba62fe460d27ba73514bd946e7cc69bae6c46208f01e5506c53dbebea118d9fe4ee71094d55a099ab675ace21e31bfdce965b8b3620e317a9f22bae6edb7b170070eac6e53207ed08b2459c799e2b01e9f5d2a9ce1132df13e376ed870d8dec1a1a21bfa5835a160cae78e1b6296676eee3996b3d22d1e4ad9335edf785a72b80f392ffa47d9e8d97ffe9b6c4dc529b65865d2fa87b6dbb0ea30191fb4ce68585799fd5fbdcf7f25017d946ff4d632e81d161806868564aacb7207b4f26e9fa118eebbaf83797a2ecdff6d465fa391812f6bbb93b9b9aff3599408f448ec2d273ebfe272cb232f27e0e8a83b6ab0d7561b39e1b092ba0db98050c2398d264b1162a0d4ff6f8afcadd6e863addf9dbf33806548c7cd9d77f48269963eeb1ce02effa30492ae9bb7bec03da1380c17989b221ce2c0fc1df05f1434efbb4251d17b630f44352fbaabe40cb2ab3ed6c1349c9f9801128503e709034a03dd70fe61c1c3389791095a7928d184f12ee6449ee6398e8c08b0847ab8dc4875a513264f9fd368643273d479b2f411938fe015ca3c3dca5a140f2cce6190105ea369a279dcbe57f3ff0bf41437a981022d63fe31726c46478181af3ca1479a62ad0d569d570752d0ffe53fcd875e8b8f260e895f1b3b74cbc72c01e2b7944ae202e6320b610a36f521d928b1842f216c20f30b305461844d91f8281d07b74b528f4c8e117dfa6d0a87b6bb72d9e595c514f8834b44e66c78f4d4270ff5c19e994239faf0d6b2c2fa2455d392f284d13f92cd20604a64e4bd90e81f33c2f2e284e2857cc43c63fa5d5a6ed93a5e22cebe87ecadafab90c5b90882a5d8813f4dca00b0564722b8f2c5240836d67e2e453c1adc134dc05d203ecaf5ba6ca5bec78a7b8e2d6f74d3def21ac522f3891df75e92c67d8966ea7eeb823580e8604b71a4e2c40ba4962e13ba3a9487c91d83857bf0a5df923a0d76ad0fde0adcc6c9477cb6bf9cf3e92ef9b8195834c87fe868c421f15444b1a5b7cd412ea0ca676c0484e9eca9dea62651ccb1cad08648fb223b9d44ddcd885ecda7585e01cc542d066c76a3042b8cf86c029252c768c2e46da39ec8cf2b599a824f0f88210100fdbb1cb6b8ce1f4e46046e75d68d9fc04e1439215cb88d5b7adeb7a9afd91204b569c4f32f0602ed3f370180265602121134e6a1c01f492399fb96d631264b220100bb236c67d944ca6223f786cb88e96cdef7218b18e59ad87f2b1e213d0dca561de102fbb5d5f698aad4f2a96e6a0076b8cc5c05ba4c5085634d22b6311f8fe83e000000000000000001009457c1b8017a5cfd38a1908d4390017400ec6963808d7fdf45ff08dd081c59a435344556915fa1310401a2af69d3c6251115ffaab6099452f28c0e6b3210c72d0000 \ No newline at end of file +0400000027c2873ad7397d26de7781a8f4c069d774a60bbd26b9b3c239e56c0265f3ee2c34e8f49e6438bc002559469cd420ccd702a954e2c475eea628d80d247ac4d358ba1160597725662d87c631a0aaf3b1810cae60865f86e38e6673bcb3a3231bf70a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025200ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000200000000000000000000000000000600008077777777d80a1977000000001c1d1c000000000000000000000000000102aea196ea3bce51dcdbd01bdd60b7e83a294cdebe9f92eff7bc393b13d4946712e31abf3f6f4fefb3419f01a558351692e812eef2e3fbc7d9d8b6e3f8d42d701447436a4a4e8bd2db1ff5ec328bc6801e25f273260e8053031f5a4f94c960b21e94204708321590dcbebf2cffa69b1668a5b78c961eb4ab7632a77b429b7db53a430f838a7439f44f7cb585d4d97e39f44eb87e1009b9eea0407db8cc328e282119031d11888d38ac2c26b50cae1c7e9f8b354cf63648ca3bf4cf532a4a519273bdb96c8ef842611cdc1e99cb7efef97597de545a910952aa9ab4ae8ecd7698fbdc8bbe853a8feff9c963f7f39d26b8a20db3740dc5394a23b1297457d344faa3aeb20bf1ecc59d646e29d417644bcd8f342775d7fd0c50ef957cb381e823c353139cb2fa037c08ab15202707a273baf8c17b203e255ea89ce435b3a7a8ae3af04a854e4b6cb10f3d3080d389e73eef9c27c06f26d66063d719e8e7c8af8f7df014ca9fb93b31557c3859550bbf8906665a95254a2461cc5dd802ab015505c4e42bf25f0053d34328723564f4eb70f1de4c1c2e1efdfb3855279302e8bbb5d6461fab8062e5ca07da30d264ada980ac3bc071eb6d38aebd33ba08e6fae8b96e86066dcfd0e8367dd3ca297d5899090955b03a10222f8bdfdd40a312ab7018526b2f305d2c66d0f5d8a917e1f64cb81ddc19a9ee9ff045ad11f586a553237a901e52647d7c9dae9371ae4b873c449f3bae6878e7e0abd1de435148d7228c191400e26715ab45a05c6552397c894a4adfde6971b2b1608d77cc7f2033aea85a9ca823f1118ea389b98715153530ce0174bb306d6781488ab4c3b3cbdec949ec5da16cf4ed4bf24b29ebf2c07e47ea1696daab14153a296ac3edca0107cb6c6d0411e22fe9ce68ecd81d04d0618690a4838f755f7ff6d7738d2dce4d18f56180eebe3e34d4735051fe99fd251e3b50eb44201a89e329b5d4b92db0c1b05592fe78d32b7621b6996587200a6429969e5b6713ba1d5e75ad93e6971b81a5d7dad55fbe5bbfbffa450110d02010f43464d01cd8627e2367b54c9f415de80531d73622dd7fe1944bc0ce0c52439c5a6ec568015d3438e5684c2789895c7ccd339f52960e5b8fc5cce08a6511fe128328e711798c06429a78eff80fb88e818196da49b3ce1ef0c2d2d4b2dfb7e5e8642cc65706af6014c4723cf2acb0497cda82086d9c259419a060043443f2e7086649d059f573fdb911934b443675f5534ec98a7ec473926c645baaeb2de5cb35b0bf2ab95f18a6872437f456223d010960757a608e27d267905bc8733efeb3f3798f27322c127fac739bd591a4f59d12844768564518e154e38b165a749feca251d2e93afbb9b89cd33bcb8a8a88727cb88a2ba44c7f41ba8fcca793bd983ba05c9f154567c289e946ba8740e8aa3f8371076c0ef8ee811b5b707e822b8346014056114c636588fba327be66e50517400a2c56219369059e5bc1474e296323e66fc93456c1a213780d6d6b35d8f0de89196de8e3472bdb03d00999d78bb81d2239c9f0e9f72a4eada7f2c7b6cbb6f04c9601350c76b65d93882eda8aac4ea0ae1e9a66298171749071c2668fbd57584c769446e37ada616718ea737051d9c8fdc43ceba9e6c61c93c24a47cbc0e427ea6136a9a181556a0d65cfc247c5f45950dcb88ad3d482c62acb38c22c858d226fba53de2b43778864ed38683c4eb1dec2c628c4716be2c52f84c1f74b6622213afe3a371bd7a365c7a279828ee9fcc9df447d34b9f4e1ae8b025fcdc77e8c860f654bde77a0439bb365f29294cc17cbf40118f7801061c94d38b74049e0f41431216c9f8ffa67ee940d5433bd61887b21cd8a7736d20c22bbf63192c65e76c7bb67472c68d810e4adb590ec44a1277aa2062cc4d11e4bb168e7bb12d549b6bcecb93e10dd90a85812b2948e6d300badee2a5eb116e62715b57b2a8b695698bf9c26b5c378c9509f2cfb2cd1ad6ac0f1affe78822f7a44f9dc5c371875cc485592a30c4b640253a4e2ba18c616a19b4837655aead9aef26cc6fe5e7a5091699a837ccdb2d17404f28bc6a3044f5c5cef2ab5deb089194b4501a8ed869850e307f9721dbcffdb871c20cfa56ae350c431c6f9aac6dba65efb48d3177ddc5cdb5c77a6084a91e839a7a3a723954dbe8fd0151949da50a025a718bf6c3eae12818c40c64a3598335b82bc13e33ccae355b8c371934e04530a74b149855c8377c9d2111c36df6a25caebf2ee988e6799298f1c49e025c09a82c5b01be12b232623333a518665fc7a3bcd3e24b5cb492b4d764fc49c22e941db838318a64447dccf501cd22c3d4bb9ca569ceeeb65be2aa6814d8741713566e738c1bc3007a734d3bf2f8eb411175df180a1d8b51d2496be07f43cd4fffb9a37c4a8778594669315770899bfd43a76556223562127e33a450a0000000000fde01c754b7fc76e1b32f20d876d4c0aa771ce2c30f20d99a6aaebda6efaf1b52017a22336afee15806267d128443d6174621bb0886342f804f661ce1ecb329ce9f3067bf1c0a33e62d2b539f4e93529b7021e04317a7b55eec94ee9bb5aa0761bce27774af8e9b388a13f2b5632832c309537698d7627e638ec3e0d6216c6f6ebef9a19ad53ba916a40bf1f9f584f78e1dabcab46a3d4074cebc0aefc626bc9327abe4f7769fe35a43ae90d38ffb88d4beb973cc2170e6c6c92ef9282222ef47e749c5b0a4325be1046ab492450ce15e742e2ddb543592829dbaae427e56d1e281dbe6d4c5a25c92e87c915c32474b947060f59ad463790ce2ab5e53c6e6e5c674a19e31e2a46e63d61355036befa328c02031d8a1c4b5d89fc0377695668b9dae329f316c39505276faaa02d2aeb2bfcbc51b627103a23d479c8da69cff875e71fbcde0a61fd75aa9f42eda310b868ac90555c51f98785cc42bf6e521f7d4f1f74870227c09b4980a12910fc91c76c3586f23ed083a3f3d906a92f1e9aeb52820b8789c88639f758ed23a4a9621fa4c76a0b34b48b73411b8d1ab3f39550d05dcb0e3cbdb55d34bb3c34a716f7f1a38479cf84694f0572874fb101999e7216df278bbafcee6958d69c064e5fc9b03a9e67030bffb59421f7f400f9f07aa03231bd1754b7cdb9835f2f0c2e63c63185772614106defde3ee216ad5947e20d9aa8c08b614026e5dfa9d2cdcbd1199974ba4b2227d846f1397d3b84b9e7d51bdce73f185b9b0e1e14d3681d57eb6d67c62677ceb5a3cc1da5f830077a9dcbeae3b17a96afe725ff0b356872b4429c0bdfa8589f7dfb37287c5a473c16c73b08bf7954b6ddbe88861c729a6e4b5f320cc10ab8ab9182ee267b7135d78497a1806e657c0faacb86874fcd4b23cf71b597d7440725bc77a118f995bf0773d4fd4f3e4de31fd5d521f1076ab72aae628c7b58af32756291fd7e3b272b0ed6860bd7eca6ae2aed965edf044aa5784735b781ea69f9a19133e09c7b8f7a6e21ac4658d2ecd89aa720af5730e9eaa5cee76883272ed485276a5eb16f3446833406c691113595ab200a70af285b3b1d28c057b3554edad652fbd5d60a271f6fbad47b5231079e05e26e2bc990f573403a4223207a993f2c2d80bb21bdfb03ef279cfe68ef5eb1bedeb71a6f8c268e8dd45344e0b2b2d37aa6041b99250795529d01a3547b8a373bff62665bd1f3cbe70ca3b2797a688d400ca5f1734b938dee2a128b9b6c081b0aebf1b1c3104a0260642c75a6a507d2ba82a1fb4e285c450c360b31170545b211715ea68556c9603fff8467277e1fe3ddc86d2b832647f66c8fa99d0ee49e3b900d003ce041ec6ddbcacb4655ec71ae6566d0e057e80d3af00ac928144bbd48bca2089e76923b354c3873a96a73c5ce0169d67198e6754aa9d810c2d52c62af0bfc25e6a7752466671eb1c60f472ab871a2807159abb991aef22b4e23e066ec83b1ee8b1fd3b8b49e4d1234922c984e4150642b3bab2037ca1a8ea8e6d983d2a864132cce10c23ca0b20f94e4e9de7784dfedff7cc51021b22b1e385f4126699df2fa3ead6bd1c086d3c1b74642ecf7632af636da3780a91357c8a29cac20bb2fdbd9d9477ae1b2dcca03a61491245fe7f06dab703ac7301f37e8cbaa8ab5b6ba1f88987a9acf187be3c6a74ffa433c7bd961e29946b75868b763df383c4061383053177ed0ef19847c1a27737350a25dda055e0b14d4f17aef727f1e610ec28c2e918dd70cb00b21bef00dcc1045dd7f25379f8ebbedf61147ef9fbc496beb9c68af96dd9f2a2a7a3a59e4c5370a6398aa2647835cba429c72feb7cf2df0a0b6c7fb37d230d7d5c09a442e9d6b49de4f2197d989f31c543fd355a33c88d67103202c27cd4eb69211747aaef2b764146d3ca1664bfbac8c6f784cae3e93fe52816c8fade38210e843a0da7c4cd9f8bad8dbdbce59e3e3754d29478d656d6bca0bccab20db2b300d537ceeb312b2f52c972f87af638bd0e1918447be85dab6279cdd5fcedb419c4e1ea1a799e16021dad3f20c707e686be3819bd02c40822cf7a3b8d1412d7d8a1b9c449d35d8d294993dccfbacc319f42c30ae1dc239223c15a225cff8b05bc098d5dd94a8d10991983801bf293644fe51e321fe958f2bd71ab1ef70d339530b065e4af70fa705615c816db729ea78601bc8c8e86df422ae89bf3ff9edf8f3c81e8eb632f94b82e43580dc7a4e569da11a16cfec70c6a22dcf9c73624ed6f47bd6799a933d35fa152ff61985aadf47be0826690cbef56029860b013f8c1d46e6ffc02ff95d9bdbf738ea5a6999b6aa118fea7cf0ce9af89b4082524d20bdeab5dbd1704983af56f8b27a2b00f2e3adb77a982e041f1d90bb238936c9921f8c8d0a48250fb605de51a110378890d0f7bc7375dc892ec84cb0942184e916d13b6fee5c36b35f22c9bb157a201cdccdf3b2f4ad84b5b9b39b3f7837ba9b052695e9c4f1697fdf647407d84620a5268bab7704920200f136ad1e1b16f3540230d95643b731a1b0bb28df019529cef1c9a2b234523260c86b34e4cd053f7a18f9edfa39b551a03e0c8481cce4c2482f95dd5937d6048b90dfd10cc3359825fa7dbc05adece12ed493d6979327f11fb23676a73abe02094cdd2941f62fe3476554fec806e682aba1e9d85d0fbb749baffaaf2918a849afa55a2078143de02e786f19165a451ace3611e23387194846155abada9cbb845fdcd6ffcd1108554b3ab3a0972a878f1bf6ecab1548175907548ee1341c9efc2f396b10e34f2387c05c7570e883951d89ea65ae53185b585fe2a377f87e2f06be9f860ec0bd1489057fa0adc562d3c1da073d21e285cc50a3355b638308202fbf641149bc7e38647714603dd3894d423c1db16fe190aeaddabec9858d0a84b31518d62e97ac0ec579ef0d93e487abb5b300b411cbae81027a1da3fb507108fd3adf00a7feee2cb0fd4f44bbdc67e5c7f18cbfb72d7ca39e6462b5be148f56e7c3c8c30c2be72161df2b51b9d65177e4d467733ba0ed385b04a36f89edc2c91c44980053ceb60694025f50bd5fad161857f2ed942bf1643874e999aaf1f6fb152ab51e461e06208df5ec065ace1fe18968eb2d96c1a8f880b3a5d41379e96604e421d64e388a35119fc646950ac88a083b797a83cacf80484d465e0d42806633cb0ffc1e3d6c119e4dcf3d5d7f928da116920dee25ec1b3cb0e764af9b403518d3e74f79974e2e91b3c7375f054726320bfc42ef968d5978ecd9274d7c7e28fdf4e98969a56031318cfcb20bf2077bbaa85fbabe36e1f998eb233260b94c7f9735498efb153e2a2980e9827f8966eba09388ce0fa7b933a704c7ee1057686b74cd03259e03dc32d165be77b256c4ed4bde320375631e35607a65c0d8a25b520509badb2b14b434116118e545ceaf1b9e84440fa7014a3fe66c9ace40898e372060a037c1e858306f62ac1b9168f23224bac1dfd0cf6a1eb182914bc127c10d4358bd6ad37b5c274309c856169b9fe2f18cb1b5efc570b1817bb6ed904c5957260b4f234b400410e16d9673bfa70c5507ab36e6c1d12603a11a9b615a657f4a0f098c86604b91204c688e7eb67d8973fbe7c449d7923538f707aca7c5bcbf2f12ad6ffbf073150b71748f176fcada737687b17d6e99f4788ff3ae6cb90d1904fb50bb5dc5618d123c7b8862640c2639194531acd3e5a3775e2ebdb5445ce19d7bbcddeb97f8b3281f7eead74663e512435f855d38ccde468ff6dec4845de26ef9f89ed8fceb02307d2338e7ba1a91d86ce287849ab64ebd95268e44ef092dedb0cb9c43c992921ddd13365b2fc6ddaa69fb01f11567724b92603b7391c0d1019737e2f5ddae752cc87b1fde6f03efd0d1301607d5f912a5ed7eb085316bdd99c0eb6c072845e429572b827c365af84ddd29c1d21daa5dd238fdd3589049dce3f68e414a5c954d288c27706275f0c1ad5a1f7aa28d4330e75a0d3c700fd8a7ee7073e0b11cb4aa15d3a67bda1ec50b0e2fb79c7578970fc3433fee13a25b7c4c7f23993ae5a85e06272677f3cd6bfe702b816eab4f920431129fe60a8f43ca0ccde1ce14615c6a13659286c1af1217c9bcac0ddf59fb8dfd259a014fda40acd6da51c8ce9b9473048fb14724012a34d1e55af44c568072c722dfe47b63321b5704f51b711d1b471c7f1d42c7c4c1d62986da17cfaf6c2e0e8261b5ea1264101292c9d74ad5e1ce210c24027cb96827523c1f05b1e1f4f2964769f305ef4dfdd292ce9b56cf29671bdb590f1f146c199f42c16e6eca232688947be34ef0960503e6990c2bd6562a027c518650472f2d7ccd0b4c2c0e48782aa898c867b0ed35a5ee2b9a49969cc60a6b0f7eb6d62f3784a0f786aa31b98421c2e53619b072f40f640dc4eac9348f297d45fef993d8efac40d54313bdb6b62b4a21cddef3f6d2de94baaa5a1a01dc1930d5c730838cbb3114699eb00e5e05513779cdc8a72d0adad0555286073c4f3df4bb49c69c863ec93975936f7db753311a2136d21b12f20b979d065fc203343316db6d4af99b8e03a4fb392b542a61ac2a8feb029f791dac934e3dc19a974202d4cb9aca89d552ba451bb1ece399032fe3aa471fa38f642039fc7460a95179179abc3ebcacd6e439e5167466cc8642d3ee2434b6212cee8be60673309e7ece17ddadfc190c2c76424883d8d163d8fbaa746581e3e20657fe191c2d8e029c830e96d77d2bd3c905ed5e7dc539861ad5834903715ea09cdf45f16a1b548cb24a3ff3732bfc33cd730235bcb5a12b9b44e3b899ff4b7a7fd898ff31b1ed4c53cd012ad221d6c31572df1aa12f1e5738a0a85b3b4e2bba85659c9bf18d9d4025fd31ffb2978c453388f437d81d3a852f5b4fca2d6de35d289ef231c15bd93cbfb306e665caa31aece93c2c17924bcd599f6971b03c938cb80f182085686d3b3c52250c28212a73375966dba08720397498b86913738e849fa5a7d4eb836e7526393c4c8f67d5c11d9a636ea6c0a80dd5f375ba2394a3d67fbb7bb99d16d5e7a01b21a17a8ca1ef752461e87ec68e84d2b96e39af5894d217f93c77a90e53b962691b1e4d666d2069377b067262c7fc9a89a722943ac1ea8bf474afa52d05f924fb089caf9d8c0f8b62ec664a2a9623f5636a45f70f7ee13d0f1a438c527b948c830cbfab2b5e8d7790e1d05fefd844975aeb1518025ada3e48fe06b5a50262bad72453b679fbff44014248839e528f94737b0a3509ddcb6af0ba6054779526951c2f17b56a2fb5fb762886a536f16d35ad4470eaec941467f62bb3c65d902158b538a02b8c5f7739255598b9f7193a15fc62b66da64ad54f7cecdc38c03bbe0b0138ab76a11c375c1b41b513d090ed060c8b49380e39da7160ddcc546bea7b32971ca686de44aae91d223e9ef643e97d6838a7b784175f23826ef53d7c854038a204f996e274f6861dad6946add3f9b28749792a745647a602f95b73e29f18a2f12aa3bb957ac65a39f262491f64415857073c2e609b1972d4fd98bef1d81702ae000f4c04df495fda7167efb1961da2e73e14d26ea129245e7cc28b40554c72193aa07dbaf7796a5ab5a9e923ef6108d80644646404b39775c54eb9b69f8d5ad332c10573e209f6f1e4724466446069cf9de84fb1982535ae987d79eee3948c462395f2fe5dfdab85f34289af9154ea22007b0ffa4c80de37561ce5a43718062739c92da7f14126c6bb30ca37ca4e75b24d1af5138c12e51396f27beee5cfbdfd33f4603903540e6bd85f7ad422fea491dce38a67ca731c796667a17b92f2f4990cb677f902dc737ff44912783497b29d142b0f310e042a447b14d1fb357e405b07bade56dbce01240cfa877199feaf15854cfd2647ab17a4522eedd764b9299607ded8993386667c391b630603646978e103592d542167adacd3c2f79a51d32918f72d9a79eecae2228bbc4aac88e5000d4a042e2e8e478e58f68c34ccb0ba7920da12baef1144212bc291555b2ba0a54be304026f884f58ff0f151f73c2b5ca2624923891d841a2ba35a54527adbb605a9c7c34225316df163c39756ef54519331c114d65a8b693f3565b54d82a7b2d6c6ebbe81e31a161b6aa0ae9823cf1f0075c315f7cffc524f26deda1bd90b436b7651a94ada84d8744b53ebce1130cac1dc86e04c266059f8d130fb8b72670ae4217cb64be4247eaa54b865bed9c45790270572393d70f869e8885743c69a6852474e1c8780de41318e2124771a5e76b2fdd72e499e3ba1ae34639918c9adf64484da208a85f0c9e5f2fe061cab4a24716c62b269005a7a973680a6b096a0da98695a7c68879eabdfb02a6efb67c07c119b3964bd1311a48ba62563fbfaaceb60b5709b7121a3c7937bf6f412e847be027d241f8ac4870ce6dc15da3b0bfd1b092ad9a9230129face2bc15d62ae532920098c8b6cb50f2bdf46ec9ae7530ed4b2695fbeae019011f6493ba847d0883e918fdb5793762073be7971f6fd6e04386ac973b7bd6f272d03cd5ce4113833a1e2b8967756af923c1f04bbfdd91761f8118ddd37b116322bddc5c373c46ceffb930b4f879115bbb49884a68b8972f92c109a4181eaee0cbe993143d5e410abeed3cd0a186112030dba501e5bd077e2c760894416c3818997d9fd580301362ab6521460ae67f5d6abfe50d8ba881ca7d9c6a3f0fca20161faa7a10286f9805ea972c4358f4462c91bc20c1ccf0d2f8578602f8b2fda488e93793c064b28b660ce91155e6eb388bc74d35a0fcabbfc26cb2f62a9ad552a5a6e6b071e503fd84237114cd1aba12994167c25b70f27efc6f094d796318ae364ff82e34c2ddd58b5f4d2d3e7f274d047a34eb6611405bfc4d0c72aa0d925cf774a493ba25f6d7e746df2a64e99ca228312af74e2ea16a9fb21a7ccdfd9e126282cc4a5f73cecf80e63400981eaec1c2a506a6278f938cff8529f432b11f7b80ec8532fbc4efe3123da93fa1e3dcb0a491edae4562a6ff0ed10ac5622b8b761a816c47e3c457e35feda228666447f12d11a188a0af3cc0568b5175d56be7d2dbd9ffa4ff5e756fe06d6913ce886616588ef8a3a7474d8599e6bf807d7e2c16fd2fca3aefb7f7e3c6d7370e5a5f06ff4d651f8bfc84c8904b24b942b8fd7f1ad2bf43be2a15a6c3130f103aa743311fd9ef27b1c5e3c94c5e0ee4d7da246836d2bd9d2fc94fbbbf6f39ab23567670d6024bd92c45f8217bc72b968fb645fb7bf0dad94daa205c2b32707114f0d3a2b048e77122570344de0cc5341bd2e9dcf056c8af924fcfe927e00fe90a86db2d1c76507bc7d41049f956288499b93ce3101da31c501b640d85f26b432782d166aabcd52b2c1259f1be64c2fa580755344274060495389e57180cb8303ea85c1be637fec123014099cf8d73778449a8f2f7cbeaf2b9a2276d6c9419632e5e1cc704e04c8f4e931d894e8e72cb9c097ca547ab3e9d65c549e50b3b979f162b52618a8c1ab558a08cf4ffb03ad6fa5be884cd91f36ce7edb3f39d0f153102dcaac8d3eadba968537042ce1d838c4de19b920322d2e3bbbe7e3c32246f9c138be98cee9624bb978244ea5e0770f841908783d1ddde305415279a2a774f3e0867257bbeff179737a121308b7e43f01172c172f458ad05786c2791f21d9ddd241df3e8a61d38874d2c19bb4a1875b536045b991efc0e0f50f4333787c57ba80d393613cf7ff307a002778cb0ef16e137f51df6cff4dbd86a663e1c8bf10ad0134c54f828e936fe07af62aed9ac1d061c4a1be5a4bb3ade5db7f8eb73ce04b621bcd3e37cc67c88fa920b33cabdd4744f760102a0dd5e9a3c483d54d3e98fd70ddf2ab70d0a67fa727fc90fbbf9767a908c1876ff968434e99df92b4a163272228a4bac0bbb522526352ba35355c39256dcd2591c3f121e1eb3a72e1f2146d4231b22fd62a90ab5897d648ba487268290cc514b26e6d1e8efc8f864a33116f7257a918ef693d2b7b42f01f97c97ec9421e6f45744c2a3c498e355efe70e19dd2f236dd11d081f911c11f9e953fc3920d657ecf71bf16bb1c59dcf8fb813143f1c209674b40719ee3b4b340e7d722190d9cdf719855e8b0df6219ff1d14ebafd12e78fa701123ac389379281164b4e910d31715070ab76b188d69fef0c76d6b92c8a7057906306131ee222c074423d653442b0b807aeacf10eae54b643c0c2380103a03ceb139e202a546107916120156c2f5f558840b9ccd30f0c049e5a260326920d2c2a71f439aa33abc373e05f094db3f22e4f345aac051a49f3c25f4257148688e88ac05f9929c160a3c0151b12849962a86dc5f52eb3dde805334d3b5a3a41ce506b927cc046fdb32ee9747429c6cd232294a2494572be5890744ca45836450b16c9cf38d392d66623f4bbf7983083ecf4eddc26bd0a981029378a80b8346a65d97b21346fb695db709cabe0aed5317250fb8c1b84a7f0f25a83edbce11db67be1875b1256022d9ff144930e007c1fb625b28f49dfc726c62838bb54962e18b5cfc57908c1e96f17da2200170be357a5f0de62f3066449f0657183235822a3df69c44efd10d554e5ec6e3d6b50503c59525158a002f64e5bfd186bb5c00953063471ed1619994f436765d3323b1bd823453c22250207679df5c833667f37ea203c4015ab161bd1d818fdc724863eadeab76438e5dd8c17ff12289d15bf2ababd5b1f11d99956068ff66a3c7db86de45408f533e710369617c7939e2fa30f774a885e7dd4dc9a9821d6b8e361f2ecdfa26658b380eb9a5a5c6761aa8ec009e7e640c0b2e11b239ffbf6254e4c85310d42635d6fc4bad0b816d1da7ff59712835a33328a2085d63020f5d7a3bb79e100f65802d1e0a220db9f9012d4865c3df6c077c6349970fbfb7af8c02f7bef83605e44a39ba2987b27f1c5e833ebf530e4a44bd368b8400d9c15a296ee0237e80bbc63e4307d6898f88b0237bea353021d1dfe372ec7551b39dc3516221b5de4b6dcf73061f6e330963d3a333952641e203f5cdf835b491eb7b4d88590546e19ad7b825572259eca0b2eb694537b010eb049befa627f2e04b27252a2e3a470ecaf839c75823cc17fe178170bd09f620b6dce0abfe69ca0a1a72b8b481700dbef6cc54fe6f9bbb510608eb6e2b11fe52239f9ccbba8c4e8c173703789275ef3c7d46d1d74efdd56b8271b589f7946121db433f7c65f46c698a4088741bb94930eede7ca711c5ad1cec93142d644922d118492a2c87ef47515b178fff9f4c4b0dd17566c6c7c0a8b896dc37af0941921211e0479234d0cd880650b37458d1c182923da9757b6a3281375a8a1967a37ac8493f3edc69f9f6cd92733b4c650e16cfc84f11e7a370eb029311c00eb6f41390cbbfe4499cfb519ca0b3f3fc03fd12fcd933686f54e206cd211755ab51af642b3d638cc76c30c4852c57810de8a88af7971bd800056419daa30ff4ee9606987b72cf401a63c73d4e0266ee0059abf8d8040cf9b1cc0e03f20c6ab4f81b0656c0f70122c0a1e1817361d4f7871bb8b49803dd66afb9bbbef42a02b60e97b05f917c9f0cc44dd609e2c47f8cb40b25546f9b40a6cc85626714419e5d99c2a19f6aea34ff1aa68150dd843cf09d281a536b110174b72d46492a4cecb645639418b0b83a2181e1e3d3cac90608049c0315bdb22f427d5147cd500b9513c0b11946bbf7ffdeba33ad07c7cca8e12a70dad173e95a3235fc19290d5df255e56eb421faf99218f8d5933093a76ef50badcafe4aad4c8dbb46d6f41349fc644de5a67932e138e785a4dd6c41356c33b5d62c8d20997c021f5edbd62b2d7dd64866c8029abcfb433d9e86ed96bfe23854dc8f1ca9798a03263fc7908d993160e4ee5380dae6d928aae08f4f0385e1ee2144a1d72de4f3adcd65bd46839567b5044f1df941a79e74bc380719f94a5a62fc25636d0df6bb744810afeb61bcbfb7db683b86d305ea6ee173a7f3c60c836a455d46abaf5c4afda70e98dcf7c769b8b0e591f7c0002ce392a0ae74217f17ded37207d523019b9cefddb63d0cf48208ad358982c37e54b8e43a86f09d1ece53ece8b13f07fbf03d8952339dcfa593abc3eb8e04106c4d29b4387edd6987f5597bc8934fd4be0bed1017baa2dc5c6c3d9078de5812c058b068ce350ec2d47796e43efb6e633c58d1876e81cd3341045c883fc990ca83a68ae2b4739cce3e8f2bb4e7e9fe843a00211124a4b36808bf3d2763f0f61109e11074da88823ecc7ec4ac91ad7b702aa17e60eeeb14f1824ea79c143043a3762472da7e1085476d98d713a8add40eaa5aa01d3e391cb0ad27b9db1953faa08335fa95d2ae399545218bcd2d79ccf0f5d2c41932f95164594584ce83db0e838115c71c536f4a653b33ade923ad419a04051d90e4aba9a15ab553447422b5515010037641587161fc476726c0770b756dc632ca272ef713724876c7ad3763c2e7d9457ebe7fbf6099c04e05990a8455386561d5fb81861fcec46ed8db4704f74202001000aa0458a92597a590b936e887db45d498b249a3736018ebcf12271839aa2842cefeff60b4733779d839b207efda828cc5b55076647084c640bcf43b568e9312100000000000000000100386ab6ce82fe7a9ef925dd5d1837967771ba1cf00e39a5b291e6553a3d322e01c3ce6039ee91d184cc93325ccca36e09d1e6d3bb2bcc006cba274b26c8bc031d0000 \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-3.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-3.txt index 56602252f7a..dafae23bc51 100644 --- a/zebra-test/src/vectors/orchard-zsa-workflow-block-3.txt +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-3.txt @@ -1 +1 @@ -040000005ba77f13c960d742c71d10ca37417f5d946dd8856f74682b4489efae9aa5aa866f2a89c783665bc6555a2be4cc2d25d3775b579fe56c5a4a77b0ec921ecb21aad5a6fb36cb9b85ad878b5240cb4934cdf395ea443b161a3fdd514214f155079e0a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025300ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000300000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000102f004291b7fae5dc4abca6bbd50eac8d8034922c33e6ccb312949f9069753183402a9c0e8b38abe29dbe03ea0de7e988fed2ccb33c7b84d453185c932903a451b49b0e9956369376e152de7fdc80a51eadc8ee75f7541cd5e5d64d5fb6904c530ab56469dabe049383d83b82aefe9c9ec6099514a7d57f8c2da9cd49b7ce8f90a6ee3e8572b9f6eaab0e45324b471a21ebfc5e0f744faa27d3bc14ed2a5c662a12cd8e9077f821455ac8e84752b739c1f4d6bad5e4edbd2209f86ad9b733744222ea528cfbebcb1e022e063c6f82548b1759988bf2a92032b69c229625d9b41efde74bfc06407503ace4b0ce436ee50f17aca51a6fd4c23817d94475e5ed96e9e6bc287d4e0707911a8cea62d38dbda9fb76736a381d9860d543f2124a97ed6c410e0e2534b7ad3b184d2f5d0f2b25e57252ed42eb8e85e7b36878d38561b93ef972295cbb06219ec40e0cb964a38c58d77b7fca3501e092e4a70cb9c71fff7a9070e0fbb6e48c8f8d5bcc10ca834dea1ba7b8f95b583a3b8ceffcb0f0799454d7b9b9f949d6e778c6b5b4064fb8d83027ffa0f47f91967f11502a03406088be3485615aefe1554e7cef599fadaaca7f4f9c8da80660c535096e5fd083a29baaafecc337634c2a66f2271914b2b504c56bf1bc1488c5ef89876b97daa371cfe9c4918b16ddd08ccc0f38e3d6e768c506083887af8564492fd3abda6e2163e8fb117c1f0604f9228f18d228bea801b2ddf53aeeaea8f945cbf9ac95a06ac85d3bc2d433c9d058a96fe1fa49f560209b770e75e60fa4909b68164fe75d7b092ab862740d9fa9b98291ab9972c799f355ba1c20737c2dc80bd27b5bf1069014ce08d0d1af7222ec3d69e475f4e380bf106b4aba0ffa6c06d299be9ce8996afa3fb966289adbf33321fd742294249074e641ed1654ee077825a807d340935e63b0f790421d2c1489af7cb00fc645fb2ab94d4e232b6feeb03074b1bc514516f32a35418e0964320912e65ba2a434b04f130fb0c718072de346f6a19f5654c6758df9718ba54621a1aefc374856f605ddde4b38b7c86df3b5eb8bf904e74c456fdd752d957e25f454634545a78a708ca4a62b2bc31c51f9d07c2b220f1204ae1e83583ee1ba93d4bac64bfeb299a075863f1c8574930cc4257615c03942ecb5237a92c6cfa6ff1482229fdba748b1369b98ffd3360a195336bcc275faf1d6093d17d37d356bc0ab9d3419972b2715f78f788c1e255fda7dc99bd64bf9b2b43ccbebe7b61c58320b5a5207754087f6489c50b1fb87111248e8f3c2ab6e24e3527cd5e2231378d0143647ae5cf31e122bbbd19971bfd8a0eb84d143c347af0cffcfed62e855552198eb7907f965d819a065dd7da36e2930059bb692f7ca0f802ff63b3fd1eeb4d2ee5fe3fb51555867767ff01b29942861bb1d5e1be0a70a6a626c5105790684de09c8d6333434581d6b7070e8c40119dcaa29e4b0f03793b33d3b85c6af3c8f77ed5a1dd978870d6a760b5f49b67c3844a7695b7008319840f283a04423ab75c3eac8e5f7f9ddb4b4483823e53b1691eb4364f63c11997167d4925d4439fd0877b03ccc4fda7a65a129645656f899efec62f1025e14a83bbaeb71ffb8267f86e6b666a03386024204a4766f1c1fe30dcc47ad9ce54e8b9bea5178aba47fb609aff2c1647b303361cb1a47a57fc4fe13a1a42787530c7f18dfd00e48ed9c82597fc4832d6710826b785f616b5e0bd8b4aa94494f91a1c9a18f699f7b8be1873b19a8d1d562b94df7d85f359a4bca6bf0f9578358ff96fc67fc1c80f076de39805da6553ac3ca8f91e6df0f0640d3b8ae32af43f0be00854e623553c7d74acf8d686d9eb2724db54beeb932f13cd4911c0bf3d0f74f346abe9ac5d61fbcdc6d4cc11d2f3d9a2b00e458f20252bfd7172599c3042007a7621edfe0e332ab9d974e8afe49915b5fca4f82980d254561ea786bc8a34bdfcf7f4737a82b4937ea8e4ec38188f649e3c24e611f982832e649391e1a12b1fa5dfd151d9c6cce7817f8ce06b119229791a870de57d1f62bcc89d27cc4a6444a20ed0e42f5103ab6e099b934380d251232a1ddecc1a9a4501883dd63e50d00c83b6a1565d57225b6d0809b67b538bb2be4368a01738e7ca404b6e4cf965c27841124ab42cea0357f89a74ebb16f903789f8b4e0f8e852ef64e872ce4ea40dd3882bb0423e24304a610d1dac5051918c7646b704c65dc6571899d5fcecf31adb7f0d0e11d0f88c578ffdbe393e21cd697c4c5ec70df1c50c6cc5100f85866de476fafafeddf5b41b2a663eaaa5dc17337e1d515eeadd82dfc7a98cbba00a0f7c582b05f85b9a79640d6ee831d3e3537f547051939324e11f112ff25a9e15c15605eaa484b1d1fb52daa47683107804083d076244412fe3a29d63420289e26e5600631ec2c335138bab56b0d186fc6df0b01f0000000001917cb982d92f5e3b74e5c2f3451a5b027dc882a75b0ce76a3f22f83507085fa20700000000000000fde01cf147a972acb95e978c3d9f55ad6075f928eeb62d9bf2d9d33e346d98beb2a08fd5bd276b63f3e158848aec0a0cfad43495e2565fc550b0ea123a9db658db6e066b1e0b343c34703cb6a8b38a0db8d82b9c6bcc5872fe4b383d669f00f8e00f9df31667eccd6cb074a46110ced3f68fdd59487ed69be97f03e4f458129cc705379add415e5f7f9137321689a0f64db744a929f8089b72409da48aa2c2f43fc60b885b6de074e0e947061e6d259ccbddf8a4f3f571512e1ee05567164307f84a089036b91a036d5cfe02ea2507a1e4f0f1c7a742efe91d17500e67c5ba0ed08a1598472d389d88bcfd41953f3427be6b3db084ff0c89ee1b317b59d38f8b2db417beedb560d5c119330a75e327680005508edaafedc48f2ec9deba43e876c484962bc8d54929c180aabc445c464acfcb8c3eb85d8ae7c79b8202b1d1ee44939bb9ab5bb339c343389c2b37adfd8990205ffaa05b99d582f392d228251c29bab2349c788128abfdd6e6fe5ce87bb5e2009bf7b901f6de498418ecc25d705a88e53bcfcf7698c8ada0e58b224bd28f36d76578a5449b57fcd60f0604de0df7b7cca16f82d87f56011e28b8adabb22cfa085dfac360f9f3965a1a2385adbe49fb21964d5c5f854f305caf2b352de416f9d655ff5af16be17ab0dfd74e4ae4deffb024c86580c15d9cd2d6adb836d17f978ba30548fa4f627113e7fd57243ff3e930b9f5111a9a0934e8e584ba451464c07cf9f2d64021ce8dfcacf946acacf2350b1fa9273cff0b98e4e4917e1cb54b9c6ef9ae89ac38f9ddcb9ee4b48bc6b3649a9485951617a325bad531b85c42ee119bd0947b2456023e568dd37fa8f0126ebf8ed2a4345d0b1f6abca9428a6e252925dbaf4174f220a110743693bc65a2af9e228b8f1ce77bb1a5dafc05d636f6d0382778abf5e4fd9a29dd3ef97f287a1ebd29ba21b8773c28d0b2e620dda8d0eedd13ab6d09e67cd02e6d8628dde1d6cd740e9dd04d7d208d214b1b42f2deebbcdf01137865bf5e4f92966a2360e660e46f9d85692e81b1463662fff3175ff5dd30c35f7cad9983639a1657322b0ca5d7a6a3c3e5d78b13801bb272be1fc364520821a36a47285ace2251465358b22b8ecca595b4f7feffd8f37ad2310aab411257b440d5d99460e4a381ac41713d610b061741d2ea15e5a91ffd234fc884188af174ec4895f67bab4a85b10a496147386b88fdee438fd11d4e9a317b9fe5ee1b2fc31b2e4fe976b91a0b222c5f244a811a21ef1bf1dc4dbe8542eb880a889b3caf600ff0d6f5c92752573f67b09e48933e18ec5c2e7dc0f1c4b3c655e4dcbf72be35c78faef41765cb0f51b496745058cab298093f85c2dbc544def635377668ae59b43dd53aaeef07bd3988ef5664acfd3126fe25abc0f0174b8c31166b3d22abc443c5d1c73a98a5aedc7f1eec58a7f237a46c09b5c0aa148438a5453cb42b4a98297b94ecf910c99a40c29f5af5184e159e546a1dab0ed332bfde7c75d2d3bf97973fb06ae7cf28cf80b2223be0bb0099b2a7429e8d8c6c4905d69cd6e70fbacadd9270cf20a5b5e4504a260d3ada8084d6541f48d90ae70d164c1261592a2f4294b51a1612cc520b0fbfa2835c3d34a673878601ee72a17367764bc71d69ba0d9d3849e3f8eb6228adfbeccd454957063dfa9c403f923f72fd51960efd0f780787e4cf55b2a68db0259cd154e36d7a2a8ff9c82dac7fc69f3696b5016d41075c251e0137cb675af0fa76044d80955a084bb72a854dad0f99266ad3fce937ca66a8e693e7a674ede3ea1ca14151036fba7ceb669ea1803cb0f9f4cef763b291ab99b3913e7a0fa337cf7534f47124902b917bf6fe4c0b21c1ebff18709812a40f6743f439365ebe76f3295aa43b5757b6d8aa534068194f6190e19768262300eae53331f46ac12330ea14b08b3e979732cdd5dfa3b202a57b96678ebd4407f881db5f2adb448907d93bc92c824d97111720886ac8975e942ac7044a915b6f0065a6184e8fbb0f01a3e0f345f76a4b252c205e42d742f4447170e5415d98a914f484241bd7fb074f3df82485bc2afc3c8d7006aaaea3d4aa7af261fdc8dfd4a74ce80606179070265e6902f4bd6df8950ca2632045473cca607ec05b4cdbf8c595b8e6c7311429da02d64c0572112270284fd36f7e389c465f1406d40c8be768d6fa5ca8f0afc13d4237f0a96e44f200a1c68e37e78bd55f5e167d7f466c69710693e5194209599cdcc51e204b7e554a27cb60d750435f0a06568d3abae757bd7f52263539eb9105217b3cf497240502b3cd4eec18ca0701e070797e1a74004894d66799790fad811ca3b90b41c2c8089d500166ca1ccf7210ca52436a9068978acc4ecc94af614ad7ad22acf9f9104fb93f60c544a3955b92d0ac7a6ca75f7fec80cbed79c4474488f0eeff68865d7b1486ea01d2f978f4f267f85b6a5f0d3d4dc287d0191bc531b76a8093bb45a27b1f3efacf7d5d74f051ced5bf294312ba50574b7067beaacee81a673cc03f25930b37745b91eb787c7c34d8c88ccfc60bdb00264670b3790affef4030292f14e62fb960fabc6e127d42ec5170ffee22835482b207c9936e06aaa333f577ff3f721bc0d7a65673dbc4d8bbcea8ddaa0545f9af1a4e1b0b1e9a192b594a7dd09f6f28654af3c647f4c2362b4ffaa8dee0125f14b0b232b4c86480b6e029b8b52a1c0f14e40530c9a2eaa46ed8c5dc5ba897c383cdcea42a26fcec13f8954ae54f31001ef39ae542dfe99367b70d409a163e07ee9582f584b4c99d40786accaf2cfc0bf1027826ed6406a562b4ace917605a4bfe8aff0cbb4f6078fca401f7adb63c360e193bdf6656d97fb23a8b630f1bfb5669697bd719479887e2cc125e5d545431faf277657c6101e6b0de165f9a2a55e12e43038aa402e245f23d691d3682412bf72eec2dd856a19d1ac92fe789f3764caf0f4fae2eb8dd09967cad91eb39ff3fb23ec4938e9c235d6b693e265624d5fb3bd4cd29d80b6742249377442f77ca2c64ec59153c31c1f0b09c6f8fd7b65ab3d37ad13e70a4fa59d0036b729d06052f6f33ef9b009f7004ae331415f68d58529f030139fedd803ccf695026b41f1f26b97085a6b7e81fae97a60e6bf4c31fdbc54b9a72075ea7873cc0d903c5ce5012081319ffd7f0b2c4eb61f452a64e5bba22329dce8e41c76bb2603d557b581737f84e6bf1d2db403803def3e19bd4114fb020d35a962ad11b1c857fe84452a733743eac0c4a48f9f790f7528adff182b343419e297517145f258917f1e4743d0bfd8395a2983b99e22b319bce55bdfd686cf894693c89d160bcbb37262df08a3ea5f5efffa565e39ab7cc1da6514bfa246bbf27f8de8cb6bd794368b36edd0e21b80655508eaed5aacd9f6e0a38c801b3589e0bc01eb582f716fa47904c44e736202ebb67473da8cfe24b08139cad38be74aaa24a986aa420ad8254ba5d52072f2ad6b7bdcf6489deb974a12a70e5c6b32fdcd3d264eda855240aa00b5c14e10b7b5b0dd5bfaeb90d8631f91a34db8d4635cd25b4569d6d23782243512873542d46eec4245407f3a988dfb5e0163da2177c0b224d7115c34d446f8848649353376d4f74c405a523a0724d9c2cbcc9cf34937305f934b2abae7a186594c2461710c71d750fb49b19d28daba94d0fa40c756f6251f2da3518cfad25f15016012704e5165732193c4f13cbccee7978affb823a400f91ee5054b009d50daecfad68144874d3958c089f5ac0d03bba0de445441d5f261bb9a57d81f934657d685bb6314bcd09e8f4f8dcd5071ae65786a4a5e03a070414ff437de3939fca6782faee0736a3bb2a92df7f25e9f4bbb7d4e2bdc7a14b16db88f8d532233881ab437ac910e026deff4d3c4d98da4cb5a3e8b2b975a6e911b60e5eb04b156334e03c29b90821dfa54461c6099ac5efd5739eff53f877c7cc54def43f015f5223930f12e80c7110cfdd6fdc038955298a194b79f479a84e43886a39b3561c86863f4572c70ccf29239a93ef7c49e13fd6360472ce04b8d9d7576421aee76e57f93a214d0d2be2bdb114964eec450a742e624668a3a6b35a9e261a872f5b1e11eb25b20cdd171f56eee7deb2bd4cbcab25d46cda0cd59f3bd3ddc24078e31387a96788d83b2575ef1132bb708d830b59f938247cc647400fb505f2e386f8b10d3aaa7fa46c2445f2f4e11f8d8d1bb5ab4fc64cf7b069a1627b541bea9361a1ce7f498625bb1e5a15a12ea96def3b5656f053451834104677821a38d6f008deb95f852f67f30b66bb4835d5e9d772ba77deb98f208f702bbf91d59e786e0a88315815456d5617b7d55c99b5cef6dc74f11f070c3676d89bf72fe7564eac120e2123af1f53e230ed3cc08d597ee756d18b8991183ca0d67794e9be597794efc3412b0cf0418f3d130bffea423e9a67087c8342bfeb32e4ea0814a777fb461c04d085e55962ff1db7c25291df04d7165ce4cf8bfcd9eb99cb0f9b1ffad98859e7951cc5fa7e9f1f5e338d1595639d1c294869110c623c089868984191cfe306f53e6e675686622323d579abd0592b32859f50780920334bc2f0b6ef827e746f09e3b04164e2c33101bc7d35be404a6ed4a0281ec543eded11143f1b66d1cdcae1e8a73b47eab205da1ef3388931d586b13f7816f2fd35e457611a79ca62984e4b53b4a1fcb41236be8b6973746e23ef6cfe262cf57c3810416cad02be9a8ed6e9533ca20abbe836aac25fb67c9b6fe29d6b059c0922e6a2e20542bc9949d325adf2aeae89e90a1b805b3789811b56f79b4ce266a4a7fe228705eeca27f7c0c30b212d66dee3630dcfed56b0da0232cbe8814a866b791085530d81d8cd27782c9136931b28837138a79886bd84ee6b79222152c21d89fc5eb506ee08e83c1cd8a3ee782ec3b9a90ba320719a2042e3a376a1552081717d48d11b2a4944ba6479a765936c626de634cc3eec0fe21256beeb8b64550b9863108df491024bca785a50b18ca803b15203d22f5fb3d14a2469cc5f01bd439f8020fec0512445c5335bb2b9705a03a3640b797533ae85ee47c4d83c7d8ed852d145bced9b2275567f60178391d03624450acdd1fce14231bfceba2e54a1bf9f15f398f55f13874cb0363adbac86b9b3cc3e65acdc2df5c4423a59b8c830ca8633f10758cbaec645b706258c9c88633b59303fb4c05e859894705f644f3e46e06982c6144edbfec2da7ffb582ecd0788cd12c86e421abf82e2a4e3f368957ccab8b2e9cbf3361c28bf76746157cc8241840583688f1829b74902d35f80c683a49f42f911f11fef98d5962fd1dd43999195293cfdcf7b47347c92687e6717b9e1393f0ea5788af7d1f8cf30a8d5242d05f228b65bb2355467cd29f6dc0a1c75a99e60d67ad2075e105327017b00821e7ce823c515c9b6228c4f5132222d466abead48f4a008029e14f6e09623944dfe92781167ce6c6c0d1643a7964075e300f14af0033e2c2c22e4720e199652e34a452a32f1d548cb602a6be1592d87c7c699c6e0d614a1d766c6988123815474b823352c13cb9381e5bf82a9dfd413a3f88f68a5b46fbe978d77ba8de8d07073dda9b20c8d80b120339aa7e2b7f4acc5cebf0ad93e96bca67bdde01da95e6785e43368176d8c37f291a0542bbfac2c52945becb52ca6661c4aa68733433f5bc271eb3925d08a6f81a18a0bbd5aa608f1029318c56ef9ac2ddfd42a623794b7f511fcbb29637fa72bb780d2705f799644845a4dbf79c8429a7e864ea5c5e08f66aacbb33b5e77829241de88a5fd893e6d3113b17e77a325d9094b4a1680277730452f9806a32533efa6cd319f0a83cec908a8f0b97ab06175b8a1d59ab49ae21b22b7a438945ba4ca4526634353cd4e2b97e302bfb9a69419df011edcdd8b3cc2bfb0da32679ecba47cbb17792f07482a630848a13958666d7f444d73d492b03c8480b910ab49c7bb19c9299faee0e12bdb32f985ac9ae911da51e8430c12199503d21f2d1b34411d7ee831cb8959dd4ac688b14121eab2215439b9272b4b3eb9a5a4be11cd7f178f365025210cfa56c9265eee9d0e1f6d50158ad5087ba94e5a62b30512834a4ace8f4ebf3a5535766a4bbcf7fc4d6158899f71e5129cb35737e3148c1cdc226d612d0676f02cafc9273d0d6712c2b337b2692757eb81099e2d784656277d54250721298a25e62602afb6b82603635ab6ce28542ce65ce904420aa76930d15d4974e49e9ec71e45ca07040927fef5f4c8d5d78ace828c8c753d066aba3b015530c2c5eaf1763cf0d9d3f273ba695a6bc0bfb8f4264850fd9eacc28d4b08817a21e87d7fd07e9e18ac7d9a1ee9a9105f3aba700c15c9613cea13519d273d5a08f31f4f94b4df5f7af84a47bc354747113536b5c240b22fb60616cb72c61bc0ec4b2f179bca0b262c3b679cd3490a5de6b643ff0623785cc94f55b6841a13c08cd517a73f8f841047f4f235cc7f10755c12881b5ce766066f31c4b8ac0e1687861141d1bedb0f5243856d8701ca8fdbd53afe27b950837fe2c6d3e94a712fd23c9f5c749d3c6085cd097b648343c800cd204a6b71fee983f0986174164814129e0b9979aa7a1be4317a0f4026593cc905e3cd535e4c0bedc9dcdde375f206cdd122357b8856f7981a771f39fb1d8e791dec389c8c62fec0a4f2464ce0e13c53dc173ca8529bc8421814be9f90ffa30e643532f4076d1177960a031585e709f74a681ad831700b73c9be8f0caba789caa83844f6b56cc5050488202912990a895134c2cc0723d7958e560c439f3424402d4a17f143e6c96d9e5939d4a1c610b9efdf27794e335f40eb7a01ba7b8633d447b9519ef40e1dd6f6c6eb398173242f2684962f2b9efd9f34f33a9ebdb3cab8b33749f3025da7f7b9e82f52576c3ba0edc80e99b5a2a4e92bc37856f560ead408c2bf99cdfe30de66be54ca5d141b4e485c2aecd68ee9170928ee991ea0d2ce1a84cea82e50626bbd6e8390cea12500838ae0d7bed0acf8912a01ef9bcc229cd39ec9b0e10cdcd7efcfa14dd8fb094282f01f3f440bd3b9965ea3d39bbd0c799c0590b8901f4954cb9cd3ad6bde37be86c550c9932f354778ad467e3a633df2045bb6806f288ca0fe9b52c9bedc3d5d44deeebec3d6e303c81306af5067c22dea387d679546bef6bd9d636542c8206ffb92b1dcccac65df6968ed9468687fec6b5db9f30cb7be320912ad8a3d91278f8cab1ee05fa52c8c3d77b01f6aae40b8eddeb500a0b3c557c82dbd94f6f23d95d91b51bcef105fe86f68f9dca32f8a68fef8feb9b56d1d47269e22e1b06d306225b7cd46f11b0636d44ca75fbe6be9701c24a0f40acec80ad8f527719e353fdc631350b028016c0543bcd0ab3bad9089655244fd5119bf73c250d2c306e0131534a85dcbadab8addf13108f3f2e28c49f439f84426ea3e3fd7df2066386738a346c943ce9f476468b6ca2fa23ae2a5e293304a9ff8f618c790cfdb4c50aa1d904c4c4d5e6302b705cc88babbf8e90fae87ed25a0ccb0b56343578ffdb3b00d601b7fed89922644dd011b9677ee1a200d9d06bde1ec9301f40636811a6ed5033479588fdcdb50fd3c8e1f3a10d2cfc30c2b6ed47c926e9dc31ea64d4e55643cde2ce4986173111ff4b3ed68251542a3ae3b90a38767b68dc4474b19f436900caba016e99fd1820ac710b422ad32e75a896f9f7609d5f44e40adbc792c6bf71672b7a5ab845e8ae0c5fb49e2e6fe9a60ab4b1a1938bbeb3f8ad5a55b5d55732dcef12362fc5fa263d57cee63411f4f7e536b0c1d38474294daf1e7ffc5e70f39eff87e1d3c7ade76eba18ff9d0f13b150d05188ee18049c8b59b5c7393814203b364fdad9128a642d1ad0a97443c3388f24e2c592e1d432fcbe5fe86e40c352075407e1e0ec299852d1f331959f482c6c9e3af1cce715cd48e04fc78d0bdf60bd0943d189d595f9787134de0d2812af45f6fe87ed9c804e2d4d866eddf03713a2c04a0da83df0b580b27664cc1725a44d95aae8b6a819bda654f3e8e0b3b051a14899fb83910ec51966b6ce8ef77719fa16abd02ede10d8933d038c82996f737f4e0620b8142118e0f83235a2d5d4c8afbc49fcec7c5f411d6b04d8ef1fa823e94251973a604c630fa07d45cc1ba130bc6e48c84e3a59c1f8c9ce698975c5a208e6764ff5d2fc066e37b4594d20dcb6bdf845dd371f720bb5f0110424a70a20c7b5bb9762ba46ccb1b1e1d1d0fe255fe884c8ba8f6ad1d95d09948ff6e042d0a1c8a76f58ffeda4f99a8357d92c363ea6af31968f047c5fe1199ac915e04370565fee1f0c2d1507fabf3760721955f160f528e3511af9b8d8d39bb735be44d36693f372c3647111a5b931e11d30151cc9d65cf599d3c050c580a902a4f52d110f99a823e40e57324feaee292797411050253e4ec5432f7774fb81ee6ab88fd295f98b2da11b0f197af92c9c6dc263c8ead80cf41dd436aef915da9924a1a1f346ce068013edf0e74621ef95114544addfb0e88a2fa1ac48eb7af6a44f7a517185381d4c3004a42ccec8a024440ef37718aeca21c6dfbb8725e53cac57e0ff81193a77974c8a8cff10f0460a65c3aed1d5be1b150648aaac603680bf6947ae23e90c3d287f056c32e7a7d4ee567eab30a0eda534dc5500feed2e7b930b33ffd2ec0e6e1b1bf4b400aa60479ddaf4ab68e9555ef29a20540d44fdacaf0e76d1603b83b1d31bc3c812db8bfcd5bff7f3e9c25a34ec71edf4e3d284ffdec79736138473416180cc51b17875a4c49604c73916699b8fbbbd4d0b8b7c87d84d7c1a70e4c7c67c2d5c168914e297a77de6884d65952e03fbada40ddb3ddb2fa2c2a742b74c1685411de9a638678f7cd8a5e689c9a11028a412e222192935abbd783d210dd13618c10208ffa5947567fafe6c1769eb269712048ac0471dafabe98ce8f231ae7ff7ce5a2f8b0640b00db2f438bc2084ba52030c6d73917e86937cff20523525c834e0c35c398da8bf758ed3e60457681cfe29b16ae806bdd03a455bd360fc43bd6d21600fd8595df6d04bbe2f07be9197e88c793c22d8a81ee2c48c504a99e21c7e035e96d57b2aad58330baf43730ad76d9407c7c599e32629c161435372f776cf70f3bfeb3c1285b2a74126b90ab56a5fd1bb8d5c0e32229310e5dd510895ff02174c102a88a18e253d928c42fb31155f80b0e52a356a0e995a8828830e604f5c3072137208203ab3704eedf8aa00983ad048289026540070b2627622fc06bc37a977c11ffef850823915fcde092f1511689373acedd77a96b36cedc0b3931585e20120b81f2a025e332c4b4705597f8b61798d801daea8bc4e725eabf5a08cb84e26f6ff967d3a7e84cbd25c93be19981fe31bdc452792f4651cc072f4e958b29bc0ade569f0c9150788184cded6c008a9e0a4ffaeabe9d3d0ee51e2d755bf8594b5b2b3d509c4cf4c0a7a2ec6e8cca43cf25ad028caceae8b55b48a6a165a5376c88ee042723f930810887e94c2a4f64644141891f99fd059f2b3d08c77c9e3a602a2dc1c128ebe05ee669ebd0cb7bf22ebeccf4f89cc60d825b341772f323afc7bea1102accf9ea17d51611d82dada48224b9948a07279238275a101a25f9c4732d051411109542c6f68a15352f79a961f23e9ac3c1bb6ae4c05a9bdf7730e548ef36953fae3a9282be604266ac981c0510e4eb9a76852776e1db833ff1ab8f4d430f886fc3170c1ec97b08d03179d9401f8c6e4881891b2e27399985cc49ef542848d682eca06eecba4ce654d11c5088b220fd761f76c9b45dceb6f17d556c1b5c757c0b7e3b5ed82a8fded93d33cdc923607188c13c6afd93239b090f4d6222f63ad50295561d30a7d533806fcd8d06b8e05c71a0cc601f3f220113be9ef67119df61158a8a85414d07702d16ef110649bebea8d1e7a3706775180ad2ad9005436bfc87692f88d9ae0b6c58e0713b2f48f59a7828069c401d1ba38d20e30bcac0c0c2580f1224606b5ad24c1cc16430a4be60f1a7e7cb598e57315ba81d4304c6bfea512f46b5b97023d0e13e0b8d3d26f05b01a86c95b185a986cf44ee4e613698ee70d1e5ac24997e529c2fb8f27c60ac1aaea008c0229b42bd1f55deca61f73916ed9f330462ff76c97b7e5e2fc2f03ff3ad574fbcfc39fd246ec35130a763051d6107a4aad1965b3a82abffd1d3b25f09ee3f35faf026791c5943d3d1d0b08db7cf86b47ea77b5984e86e1b238c694ef9832130470ed2e03d4adf2c06198e81b89833dcf307b0ae9c7cffe3b177076960d462341f494b11ab6ababa9fecdc44cac92e96f1401c1eec1a851f5df02c173bf8a088f10232799ba2f0564714b12399726bc03382775bbb19fad60b935f14e1b1dc93ab0e0be9107b03d3da1e0acea7d724b48fac5d31edbf8328dcd0e60287bc54be104b806f380100ebf20a747b8baf3616a1d1d37d127aee6edd7fcfe79be425a80f515446069fa2836eb53169f38c26b3cb48aeb10d2cd2bc955af73ce2fe7c1615b9b7d260a1200100f68ccb7184d91dc5f953a2d7995b56515cd4fba3f08fe3a7f4828ed81559630ec4e71551c92c7368c2c5778a8d966c4bf1dd6f48b3a82642bf41acf88855fe2f00000000000000000100f48732fe61d7d9dd2bd0f7d87cc859b050798002075d3d1c0946eebb79d6fa28bc4dba2c580005375cc5a8380e5281105e6d5fb9c80b3268cc46627957de6a1300000600008077777777d80a1977000000001c1d1c00000000000102a6bd0668f395f60493504c84fa31f9ef4c5b91db0d7680c8a306c59dcfb0700bbca04009c930d604b0570b57deaf4498a0a7e8ba68735c65a1db17d769c66f1dec1e5765887f99be678e9a69fbd999845a079ceb04971758a5d5a7750d4be03271445314e677da41dd93388598f39681841f47dbebb89d45d8b4a346424c3d1739b1a1e3d1869801214b48f9010ee2655db32a761969b13b2ef1f288a456243c1d8ab2ca04f2c1d192d6720ea08c18261c76107f27f0779e05c95dab7bcebbb2f440f8866cd8cfaf71aad9cf308d1fbe993cffbadb687ae6e0743c818fb45728d63791424fcb816f7352e890b6d92a107afdc785a55bdc2ab82280dd343d347b78cdfdd83adb577525373a67483b3ac42ececeb0a3a080930be42008e1011539f295f9ddd1481a8d6ace4a15246fa8446ae9eab0df28866284b68a0c4034a7aecf1fc720d9465afb64460707071134a190c9e02ff5c002a4b596b109f04d3aabfef46b7081101fb807d2b702181b44027b94aab47b8c0a482afbb398150dfc7db2d64fc164d9e75f519e9e38d5e4cbbadf799ee816e5e58ad2900dce38096c0fcec4d259be1bbe7a60cb22230d47a70e1252bfd8ca3d96376329aef2018a6edf938b3b07c8c10ab6a5a623370267565ae3e51d3b71f42bc452c03e9bd25af0c7911886d98b7b4c79c03fa585b2acefaccfc00d9533e3a5a5a4b1c263d427974dd66e6f1012b0ba2a5e76e76a7cd3c31c50c393e58c739a452ded07100e48cc5beeb59d003784b7aace99b35e4dd0a65b38c1c075164e54c065c1828faa37e837e8568925671035c82997b22e8f79370daf300b2471c13b80690494cd008d6a3d74200640dbbdf0ba0f0a6ec70ed1dca3a44cec2dd3ae2fc8dd0e5047b46d4ca159a9041f51d2b8c6c2a16b71bde1c750d53c6487a5453e8d83bbff9a00d67fbcd0a667c91394b6b83796d200e022e9648e76dd0ebc4ee597558caca1ef835df89d3e2bdc637c1ad722ec1e4f0f1305e4676f32ac14f1e38b13bb581e9a0a1313638fef25c14b36c215df8c86cc05a5a50145e5e942b05e27cfddc42295b0f103b79510de175c5b4be227eeb26240fb7586b0b986da1a1cad038ddce03c62632107e74b14d88d9d04f142b41d7f1a666359647b4d2b6de21eb7e14cbdac6117a590fe125136963d885dae17cb251ba79fa8054bf8b4707aad3290ea8e10be59b8fd52d9017e53e9e329ee632559b1e2c54c4344a066749da8e962c21abc1e17502e39c1f8a5d4aacacd1da4e5f5ad3ee5825e5d3528af14eb06409e1178c4118c91d2ecca9484b2eb9fdd3af76dfb21cca08e56a66869b337d80f2bc0cd86b0416809332bdd246ce438cc04f6e0266850bcd3bc07199080b94e29c04a1c94e8bcb90d6a1a233fd5eb78dcfd05c29e80b48a6fba9f0b50bf051623337a9305783841f3a1e907b63a262c1a18230e88ff6f2d358fc2b4ef247c1087258f7691cfe9dd188f8d743534c08999747d5d539e2b409f9ab90cfc29da288c392145c1acf05a3c099b8d1d41d53cc3f80c0418fdec0b125feca2a4b0165ceccd9c23582116762c401c294c80fd03b392698e50207aecee626ee9da4c788eed8ade089eb17f37db0170bb2a33d96763c771f62f31b8048482c51a8bbd961bc5248dfc8c6944f6c24c17f7299f4edd9a3ccde228175c6f092620bfb0f3740fd1619e98982234d21aa9b610c9bc257f16ebb58f07215a7ea41959ad725139da7a88f8c47396b3ed9c98d90029961811d80e2b6d931646a8a875eb736b5eba16f35bcd77e820ce96ab231a6279b5f5e12b02b1f3324932d346df1235ce2319f77a1d07bf6614273c7a1e35eeb4aa6ef1ad2507775eda1bd2f0b59d6700f86ea318941e730126b5ba4e20bc6bfe992f9dcda4adf2a34134594e6d94d89807154a40ae24d7d55014120b712984017c7292ae37c4c42f6bfaa4af5252555b994098a27d44ec833c70e8a42f62417f9b089ef4ede34abec75020be18486bff2457265f41f88560b366a0e050d670c11c467b465f8cc06137573282f1f6344955f1a32498ddc162bd888c9c5f25ee8b05b5941e307decc726866d4f7f9b3b71c66e3cf410c8f30799674efff1f6f4c720f671deeb4acbff16c75567ceb03656974fdc6c81dba378b7767033812e6f91099b168a177758058bccc267234cce4f88a00e2236d55c2de099c7304ffb8be26f3b746fdb80d73ba262902f859193fbbf6bf8e2ea0fd222d1d835e5f75e9f9f3c3621b9362bfa330d454b73ec244bdf1cfb694b0aeaec94ccd55dc17c09052dd1440f95f9bc433b8e0f8fbff31e2036d015e0895df5daa58a2752b1cc18054d0f83ff6810b3aa24f66e2ab9013b52a1643cde90fc2242bb164177a7382598b80a2c076244412fe3a29d63420289e26e5600631ec2c335138bab56b0d186fc6df0b01f0000000001917cb982d92f5e3b74e5c2f3451a5b027dc882a75b0ce76a3f22f83507085fa20200000000000000fde01ce22549ecb4ad2de881226dd97be9afc4ed87d1b38b4b1aaff9b3d2fef9ed5d81becc57695e87b17e01daffd65d9b492dd27c13b57a521345a2db9b28445f2cbdd0d5d2ff26f16d3f3525e6d924090292c78337ce3f750e015b5962f6905c493e53e35649ae8754d983c4887b4d6ed1a72f167e5b7de6299dc60f6791333287af5968bd098af4527f6f16e07acf0966cfebb733154c4abd02f0a95ff08cf0690128c5adcfba7a43f2a5c006ed19a48abcfe12d30074dba878335d48ced244cd28287a24cba787ee1c5cf7b27971aa48998d9d241159b986147b46f3f3a7f4930343115f8005763f6dd71599796e1672d6b7d1f0b7d8f596bad51c27274201092b8f380df21889046eae37b2465d042f6dac1f271790025d2a2fce441d86a1398142f544668319ffa2ea09419c5d9b32dcc727af180cf85dc633c8fce7b1f9f6924a6471ca7e55862f32ee12ec8c1e8b8772f235c07973c68cd543186927ae26b908920eb270805ac4281016d0708edc0c3458164cbfaa3b728197be2ea0511d06d1d9f41654e89c5ad0fc62417345251af81fc3e8401c8b2c2331984ffe9f3c9b0cc502e0e6a24fddaa3dbd3927e71eccd53c80930802b3f97e1bb57191adeb98d03712970153f5e4e118545211ccdc14787dd70b6af429d773ceb00f859edf9119c3dd963fa66ee8efa339efd98575fe71a359d1d6743c60cda345bddf4b4d230eb3870a3f4082aedb0343f02ba5d91d480eeeaf754e14d06067e25e589b88aa9202bba9b9f612452176f1cda6571115275f275d67b927664cff9f27119aa13c3c9834f5567e98a17b2aac359680f27adba33a20bd8045a77ca0fdf8cd63e6395127d04846bfddb76b4aca75156e465cc44d835317e7ba16ee154c25ed572020ca4537c966c26ab29f5284f0c4abd8cf1389efe2b961eeb867d2061cd8002d13f98efbff93f644d912b1fa6869d901590e5e07b146a7bc8ab7a30e6a28c425a7cd066d58a53ec0fd861bfc913a9b586c45201a3ff25daa48abbf4919e8436b85ee9ad2bbab09bb4354b9378a9d1f81eca13667ea6a00dd87abf27226425d0c9a503fbec5cb268b1baab1152317cb8da0ea4a4d7a44f2c4a4d1d521883bed2eb57655308e4b91dc8dd44e6eca83453b0b6eda55522f4dcfeaffafd2260e61842f04b5cadd6c281354566db8a054ae3805b074ad2f43c35c8c792b2f0003c3e9033b421d697afc08272edd1e9be40ae95c53305f0f550bb5725422ff24e2fdd52e58f7d60701e6fd1c29c45d84a601fb09979d15fb46a31ce4a6c643f8efc5e50bde2c70c362caeb6a471b48409f7ec3d34af1732e96e0858fe1c33c25b70a8b0e77aff19b3c7ad1d461fdb8ab34bc4d073ae5e819ee1e6fa647163e4be69ed980be8eed9bd72ab27c535a69d05e86aa0c3a44f43ea7de40ad6a8f6433224040b1145e76ec8e5d34fb234bc93da3d88e9d20b035e88bbb7b95451e8628c3c725857deb0fecd360f4ea9132a8f7d050ace1b904961094bb495ce4cf7e9705247b34d18339b25a5e25542ca454ad40022cbddf45a82f3233ffaf0b083288fcc6952764f2a6df496e5a0fc148b757a9b48c94a6b57a246e0d541b9c0814903242eb9a052fe2d20baf2b54c62a1ec276260c6f388db80ef04dd2c8ac6f77dc2c3a9f80236ac3cdcfbe8053d675142536141cce25e4f4c8649ee776d6d7e1669a5349978bfb296b08cf3325ae5e9d70d9cd4243d310dc24620b14bd331df5b4444aca31407ba8a45d75e4e1bf818fb859629305312ae0c1527fc4711774fe2afc42d42b3269e576d9692a5a25175ed3c3d737e69d1637213ae8570285c5c59c9707608feb0699eeaa562fba4197144ee30be505c39c7ca62529757ec1760b9ea2d85512e6d33721265a5e55ce294d7a31d4ac3da76b1f8066e4e0a1ca59b81fdb01068f9a52847182a9ab323a5058d91a2bb8de0bfffc84c1938fa47ab098d60d6801976210efb15f679435930e6329a0034e49bac8ad70800034ffadeb7dd2c18a9f324a2001d63fc5fec0d20a6e22641f1ea90785d81b9da37e574cdd42c0554c9b83de25233d18bd27c990d9f32fb63667e902d463551c35ed44f365a3761819800b36178e8f4d609f5e9ff5b7d5e72773680db0096a8434484308e46c4efde16a370e06affe071c65b6c4f730ad7569f811e0c637379e3a4032be09045b942c7393b2ca43f5bc5ed297f8d6973b215a42fc785eb41f74858c6f5ab113555342a30493385783fba9f7cd4e67a042e7f11a777bcfee756afffd6f6e4e3ba63e2a92be1328d295308768f680a2340b68d4b644b99a0deb9e075d9dd0324b9f76124f2c14cafc4845b19521872b17fd2bdeea332a5924a3b7d1c551eb94376f9ce12720e8cbd957676aaf0c530ab0da5a5dc2a16cd542ab54b73d98f6d53009fd36ce0892c201658bf181ea6d9a9b8996a71ca5fa4377a48162a026615872e6089746297d85039fd1ce1007231f2ba185bc13a5322f7e862d5719d52defeb9a8d424b193913d9acd4376d1c990f319e083dffa0aa6322948886ab390d26703573195c2b7e64a5e457817080480da5cc0eb1759efbd64d87a44365a678bdb598511c7e133aa67384b62d63c5a8ddde1e8c7c590dd02dacf8c8e54738c9890799fd5ca83ebf7c86cc3ee95b8e54a22a7c507da5bb55dc8f75e34abea9fa8e2296bc199a0cdbd5aae8001d4fbd98d51e7c0ba67c58e656e3f3e53c8496fdf457c7f931b1389e7d6edff3dc29df0baf929dd18f045afa1133716705045cc9459bd25dbf0622ee8f21b50814f59f0a39df6252a9b30300760e695af5b153145abc9c95eae20efac39206d2b84c1e9242d2b5bb7e0a7a3da5edf70a3aee5a5715b8add667bd1481e9c0033acb6d5c753b1c0ba420d934d5787c5d86b764d2a14e4ec8bb8f20180ce2e518fcb10a1fc36178bdfbcea1eb68584553fecfac99ed1bda5b1965513c12ae2a5d987ba7e3a37a8addb927ef82f90a823454ab3f08fe33f896d4e5d52140334a82b49d287c9b03eab486430c1cd462fed36d7b009a56a5c118482b572ae74717fe3cb135f2d2c863f09314db092c2a15bac418fdce46e879cf55491813fab7cabf6d5753f8e172a98b003916851e3ae9bd3ede75791dfc24cd232248058e1e87b99c36e2676e449d6fbd807b2a09adfb4cdfb456c32d92564220da951084c3033d23c626a098ab257776b4849a1e7cb7c84a6e99c4e5638d3eaa7b6a16c4a2c5d488e1e89a9d32f6c9d545970b5d750a165fb14224e0f3f05f0c1b9e3ffc54aa667bd83a7abe2bd85c69c7d41b78811ee42a0038fc8e2d57cbb855de3946e0c9d562e2b8bc04f18577210a36568c8bc3a558c54c05a16979a926a1f70d32b443320e60573834cb9cbf189cceab7cb14a0ea087b3013ed72a55d113381c346837f8d0a72864157b2f95dc6dd3c65f84826a31e2f456ef4be3338d69b2288e9e99caf0606fc33d2021a712e0c75cf7dffe6af7dbc1d770e56c768d28d101276e448e99ac649811b0a628c6f74a11bfdf1cb5da4f1dccdca1989c3ce8c321038d2e0f111f6a9929fb4294ba937e8868bdb446a174fc7bb3f975bbfa09c71506a755b1829026bf29200736e7c6bac1e8c2033f9522eb8451657e7744d6cb335df5f4ffdf6106590da5dbe6ba74f9e766e039024a11b52de7a3f0119a3dff0c306c66167d1c5a7a2573d8b404b9b377b1fc0a2fd86ccc2971256161b1a88501634ad7c42cab02662055e9e9aea6f2f0f83d6d02566ef334a931605e7f63511dfb36b806c6d42eb5631774cfcdbe8d286b8c4624d90210fa7bd048b315d85c18870323f67d9b89145cdb6acb5351271095c2e25e75914108fa6b38c172929d3953ded865c10ec730a666366611b63815532db2a6bc5dd4a97b705bbd3e818c3f26e6e93095abbe3170fd723eb44d6a0915613f05c630d69c911f676e6bb89f12d2ce5640f06ba4cbe00932c746bf626c0e4629948fdafcae4773fcc00c959a2da092f8bd622bb2825e4ce204a0676c2e706ca69662bd97aeadba92183a60c53d0fcbcbfacd6dfb009afb89bc813bf4f6e9d8019c8926e4aa04c780779bf5222d22e744abace9cf4d04b1f1991daa146cd7af8d5d13110f51687f84e90ae6b71efebf4a2a75a5a05b71ddd410a2d188e62365626628dd3a446ae6e4b3dfd555321000fb7eb77105673075b3c1901fca2ecd63baeef5e6ac23888b218ae3af400dd026aaaaba60ce4830ccd59814f2fd9383f69875cd324dcf648e00f61612aa0101efa5139eb3675563bc853eea0bc4117c7f1080e1ee4574b0fa8ca0baa34f2a7f1bb33013a8be6ea75412b7edc1db09d5e3944401c2cd8a42b5d6b4257f3e3421b9503c253eb2acc4e408d124393b10625e369a48e85518a8c2660c0efb1b3ffc769cb89c034e188dc294d21d6aeac0baff02211a5876de26c6f5b8601dd10ca4de173468a7d5ee2e37c34ba5878813462225ecfae44abb961dfc68ccc1303daded3e611461c6da6b6926546459d95efe2923547c60c18d00504f5d173f69231b8b58f4e9f834f021d8a922193644581b48ac98810624db0478190dfcf4612672dd8537a86b2d447438f2bcbbe19b8eabac018690f0e4f6f5d54744ceeea316f0f61e5682b3cbb67ba147798433f40fed150145d1ab01cfd0a8ed5d08a0242317e101f6beadc1b5430863c16a9856e7d82a7985ec86e207fd6758182786ef1fbd1732b9c394262be1f5a453366317d88fe739b08a59bc41faea239e1c8e5c2d1476a4434838842b1e45aaa1d26b09af1492996c3e519aa91c749aa077740c3171106f5a82d32a8015dde4eb3a25d5c471896443c6f462d20ec791923f851312f5cc09898733b9ea87d4037dfe53f95ba81e630a6ec521638bd657bc773ce41df5c39c8f1450fde31abf9d78f61816fc5481922d3216f89b24d3d3bc21d6793114f50cc2582ab94ed543ed5400c9f89761414e44415b8525f11fe5b4589b91397ebfcadca32f688634655b1330f5e186c982357426f432eedd9883fc8485302efc2783c14bc53990349e622f88fa6eb0926f5ca6b964155b5d62f45091f94123745a2982f420dd45fc4b347dc7dc06d5d634bcab5553d75fbb72d2b0041a4a1e758cb02305cfd09823280e7865787d6cf5d8037549644d52af5311b6b94ecd02c8cd9b9697912001612a870b37f2d82e9904738ba11d01a139a0c30b382646166b7b2b416b82c87b511169ddef7294ab3c081903cda1289aebea20e31c8bef2a6cac3136816e842114996c0cfb159f42fe8dcfb7abcbee358ba1ac62c75ed43c019d7652fd38ce38d532b797c0d5f84d34a23f4944c66bbc3a88b91759f29714c6830225950f6ec8f93f8abc0abc858233dd0fad4d307d034c3a3000d895263948141e143a6de17d825c0a9b11d40ac26a309b9a319ba67e7dcb30c176fb263f1e4d6e9a92a973ac1f61765afb18d0b93eb2b64c085969f529bebf6bc125210c8c0fe9adfe15d4d3278c6152eb94b41c120736d30be267fbee1c0489337e9c18be08bb6d1fb893567f840b6e763cb23a4b1ea79011a38ba53cb09f4ace7a293143cca129345187eabf9b5e551eb173bbae341a6542ffb47b50e36340de2a7a02de37c58b4d78924b55affa2e105cae7b0ecc5c7c43245db720ddba377347270eb907b0add918cb30157ee0a238081cacc4b12e4c3fcb935b1401e9305f20cb24ceda5bee4b0c9cd2289209dcb81ae11003f5ceb8cc1f50673d9a3b2be13d64384475f402ce5a33a3e9e4eea10b621ad7dfb566d47eecc1c8ebc9e914d597be3928fcf3cdf36c78a3b46f0ec99766359291faa68c407ab6d211a5477ca8e0ef38e5319b540f4f8244165317751bd8e3e964d00e6f7ef438a686160bf8759a3e1655a13ecbec00edc0121a1fd458d93555411c83ca9d934111253aea08294e573e3ce36e7d8aff40f709dd35e864665f8b813ba96f134b826ef7329df1568479117f60df4dcb0fc39304728d4522f07dd6be21f3e101f28ee3958bc5305bf8e01239b6c00fda41aff18b29cbc62910a9d4d8898cb6d934af44997248cd8375410513669cec0ab9383e724b23d102321ed0a42ec0e98d726370793ae6b8edcdf00e207c6e70f0a4e7b2589a1e5f4280c7e78d4e95dc5b83ea0f341e1b623e664b0f6244c087abac22bb7161db45dad05f752730c0463d1fba37b083875d15dec93cc34b39af821031b18123370e0ea3e7c8a89d83cd16d056655cfb6bd8d6d7de19e996f61076725313aba4e8fe23242b1ccb81b6b1132a339fda5117c503a90c1d9d80258925754e6fa9d2d95bbf9c8fb7adbd4d14ae7829fd47a665a936062602294c610a0d1fe7219b03c615d3ecf31bc767b199a17e4082b5a6934df4001c30b5385b67d053caf91551f5412c6da755a179f18e1d646374671b82c5dfec212ed76db3cdf3dcdb55cea1d9397e4ecf92ea441af29d835c91d5f626863132f4303f0587cee80cfbd198ccfcecf293dea3fb2aa5c1177df38a1a4440099579551e587624ffddbad17478194eec4d4c72ae52ec1195c5383f3be9c0ee2b369cb203a07d6b2175a0fd8737bcee3b0209a374028f86793bd1933dbfb2ccd2c2fe6e1c60f216b70aba69d86aea02c338d457b88f65a99f5365001815b9875c1940ba1d387d6d0257fc787318283dabe3819dd57064f526ce45dc593804bff72c04c30b53258dc37d325f30cca46c1951d31f8e35807067a166aa2dd1a91cb3c1ef7a38a7e9b9d2160e10a4468f8b8f4d0494e85ae59aa88a5c4397c53b0a28270bdf0c1d168a48eeefc88476c1a9c89beb8e215a2587d9b86314fac20f63a5df186a0c5455b3374d3b8036696aaf31232b9f672ed5ed6e533853fd338ab69ffe6d2610b4a230abf450120ec2472d15fc1fbe43d1102a896ce5ac4f9c1260d6b13cd51a3afd114ed70bfa996685629db2e7dd332c3728fc01e2dbd2e584960e9d350c27e12b99b7de5fc19228e9e6afe73c7c0b57c602c32f5ca4a88b33ad93e3621f08d51493c2b7eb01ba52176df94cad1dde033572b6b556277a5a2c5314b2a4930be21c6f76e167f683771de64ad7f63d446a412fb975e776c38d414cc935785c2a42afed04b31ba476b0f66e9cefcac2f3d3b051fd4906307e16fe493e9ca69723113b7ffb8670deea5c0f20aaa2a59f0206916350ee1044dab3ae7518eb5e58230f9a4ef1e4110426f99cb10c50283021255b60a88ed3a5f98ce2446991e72b2bbe98c3be89195944ec243471a9c91dd72ac05a5c291ff206603e78c9957d263151e9169ef357c311db4aa4c643c4953f972e7bfdd637931d35618bbc35e0c80c3992fc777119b0bd26a78931dd8556d4833d91d1beef758cb5400d8a253e1f310a87abbc6a2479322540a1d437ad93e5cebc8ac5defbbcd0bdb9c08e31156e10b486b290fcd417e0765df551769bf133e251d0065d8b85ae161ce0ee9ef9b30cb6d9e6d89946dc4302b04be39c6a687b4a8601d52a88074975d1422282f67a2b8326ca79aa61243bfb5294614779dc51695d750ed5eb9d32bb2306aac062be2bf39eb647c746178add99289b9fa888ce408a36908a315c6168060efe972ac9090d3db3fae104ffc52d16e186c8c51bc761dff612e76ac1073443f5a41992be1ccd861d702f08f1cf557250537baf794e515cb9880a14a4f24d35cb1b8f323221e99be9abbc7dd59a8cd098e3644b338890b73745e8cdb3259fae6a6e88e90c3caec188ff8645d98edae416c9fbd6ff8e13b625d7c0bfded4da323ee948f0430433db02c6fd0d7dff04b6f6155f4258106da6eb3e60fd980d9fe2f9768866ad03f2a34a36f8b70adbb2b85c18fcbbb42add8a371b5305369825fb349189b59d1820ccbca779e911fe3c2d74f1af2420e7a598fc0134862930835c22f79452a125a9485744b005da8dab851009f05e4aeb14ff5e31b538626e85081ffc9eb4000608af195749d015699cc9bb76efd435bb541d7ba4f017fc1b6d0329fef87eb13dd74cda86d596a1dd6e9dfc416f73be5b5fd337ae8738de8db38f4713820865315d3d7df0c7e31789b0a77cbf6dce98ef4dd0f628444b1398c821372e8df3e61c240432ed9a1b22a0d20c5df67fc2de03867b93dc3fed170d192ef7970575c23760ba6d18c52ae37d15c3210de51b40437a3ce608d31a1261d9c3f178f93d642c106e7460aec45fc70c6413ebb02cf12af85c1fa7b13972fc0655ff718a1f8616801eddf9d6133389466ea14046efdc82857902537baeb3cf22488394c4e7be2bbf9137f4ad4fe7ff4d7cc312b52886ad3f42cb36ecbbed9377c48e675d4fdf12c257d0c989497e1707e6712749cd211a3cc59702b7404adc9741fdce53c3eb023a51c35c92bd7ee392ec83cb8bd209657c5a9f8e7996fe38759ead1d7a95460f2d1e0033944771b8f7a20863a3bb65241b11bff03d6b8b443bc1c5f34656f73ba7f83568ae24f43c820d89c953e90565cd97cf0639ec548bb8cede1186f7f13e5220bae819a8dc8909c8ed8b275a3cfa23e844fb1f0300d41fd8bf8484159c3d520b3e9408e21a6f44d67f8d3e322ac696b69a4b9f0ba0c6b8617941b06f0a0dd27ab3df7a5f47d28db8568bcf12b2045d5426c18d87b39b375b3efb3efda612b4a1ab6a65dab6113f0e6daff1471a0363967864c45f2b8d0f90a23e5c2c152106fa209504480ad74c6b87ebf4b80269f2acfb719df7871aa9776b87e8954408bbd298a011ed3a2e43afae6fde60fdb8985966f4873dabb0aaddbdce515edc128a687d488728e00b3076aa6b4b69d3d9a92b17ae3dd9078fde8c5937bf3d67348887412124b82c44ffb68f3b7e10ae8e730e8bd627430a72130c2a23ff875107d83ace72a4a63024042c616396949aa124555f6f74f39e85a4cdb82a8e6db4235c2221fe17a26957e62abaaf49300fc93cb2874ec730483521d0d07807ec410b5cadce93dd5131d9adb5c1fe7db9b0399a4739da4b2a9ee2aa806c882e1c400991314206b5923090da3a6e5248bf453c3b1b690512da0481dd46198fe0887923abf4ccdd18328da41d9cab5c630609cb33363ba3eb28858ce5c456543ee3373f26133185d48b34cc390be2026a18fd43412e2583ea140408b6cf1269e0d7a2005f746beb07c3023ee7dcad648927f47029d67d23ad5f929c1754a7d07cd26638797bf33abd838bd6b8ae7c7684b6dfb50dfd55a6cbf369f2ec1d001858b8b01c7ee96cd2b5681cf29a153d69ed46d89ed573ea51ad7a4d09ead4cf16d2c8760e8189b9abfeadc476defd46bf2462a6f30ebb45ed336994ab2a9cfc460d6a9034cdbeb35c38b4e9d07f7e4018a9228cdfc1a0207fa9e2a4e84df3ffc3b4e4902e7a86f84ee90737e24ce957c6f2f48f672efc8bf4e9ae862bd04d30a85ff48234f306952585d29f10e8cbbe804641d7a76d144ba67820677eef901d80876ff80cee4aca014516e51950f8c9c72f27977cc02d9ce6b30e9575c5a2ac6d6c59900bd50dedc0b3916c97521dfadc5000ea237806d09ff9908f53c57bd6164cc57a93b899ed36421fd062b29aaed05e3e388b87013403a1818e91f3de61070288591d46f845c2c63bbacf217615abc1b25b6f9f5df128e1f688c498677d4ea7e128bb24041de619a90ce6a8d8c2fcf7c8455d15a5c18f44bf9f11a752007973d15a8d2f06dc7b28bd80336b6ad387fa8d2ea855e5140a14c7ccf63418106f5486d6967aec7f9ea071a6e34797b793b2f362856e0503d22b91ed367c71b11906d5ca84cb9b5befe63ccfb1ba3c79ee7bd38a0a33d7beecb09ff6a5ad3996709184e022af8dc4436cda0c62b9948ab3a5739ce75be148825355e97fad013b940e7847a246ec8ce6680ea83bfe553a93c74afea7134fbe8deb36dd59e9124f024213c12ec7fb853b5734e07ec6c54bbc1ec80d2926b8fc6eea056eb12e9fddf0b692b59fdaf8b5542007e3ae9ccb7d575908d98cf85c2c52a1ef781841ea1bc46535f0113fb6d6db368fb2068ad788400076fd2fc833f33e7b55ae7ff8946cae929507a227d335f24eca76f8e589691e3387a68e8076af9b11dcfe0154a2567c0b14fab240e53683531170f9801ccefa7b97765903f929fe314c92f7e5f46d15800f6fbfd081aaed0b704f5e1b8cb36f83253b5296c1241a058ccf301b3daea48a4b968e7a478ff1e319e282131df793e47577a5fbc63f455fd8d4d218188908efbd3c99e24567c76ec68bf0085466d478067c93290baf83062e0bead72c1dfb9836219d2b2014870fd339072898e2dab9fd1ba27cb2a2c360847e44403235b4cf928697693afa82cf9eb0fe75aa0349db9b2dd9bee2fa63fcb67e77983223b6815fc91628d51cff62bed4648c00e510f8544130e7054ae0ca60db12bde60e97c47656290100b7486df3f00336949a2a3ed968d86e07b04ea4f008552c55f2d2aa2fac8409006855ff058e289bb443ea992b948936963a1090365b0473417e3c6b784c5a4e1401005ece851aa35065f27efe1bfee6955ed7e6fd90e01fb4df8811980f7e561be6afc4faac1e059176e68e0c2fb170f28743f23e374f4ff70f96de50c6b2fb73a43a0000000000000000010082853ab42c9dcf1a0ebf6656acb888910924cf96853f66041ff47e2812c6e3a5546975e3e93bde047fffe1b5f3be160dd61746a427adaeba3b932fa2e833bc0c0000 \ No newline at end of file +040000007c343c47e44c2f3004e40763a0d8388c60376f3aefa9d063fa8dc6dd73be7f9ef7fc5f9d2ae75417af8906f6125232ee50554d5a2f938c7549d611b795acf0b34296cb8cafd5994c24b33480ffa43febd642f04677cb8d70615cf2d13e0ceeda0a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025300ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000300000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000000000000000000010208179941cc27efd1424eb3178d809f951ee46d60459ec07be830d494d3ddb13138d97ec19c34475c0834f97cdaeabc410ea9da96a2cac7e6077dc69b42866512b4d2aeda73dfa5378463e2b47b50c277bbbb915b423ec24a8e9d20b7ad7c462ce5c551a752b68a35a69be03b4c292055da834daa37bed577a3fb5cec4b06e02bae7d0b195ec859f6b3499e0d35e765aa78297da8f49c2b3330f455e487c3062cfcabf1dc7b781df617c58319562d32e55a28672808650c6a1a7e7fe2fac1ccc97a9a59e44c2c816c56286d5e2656a520b7d834f0de839cb7004dcd7d9622e60690000d5c8da7724a404bb34d1531ee3444158cac680a85ad9fb9cd543f4dcbf458a5b01201addc876ba118d591c464bb70b05787887f678c06a1afd45421ec49eb33eee084f961ed57050fdd2edab16c5132a6267f79210ee2240111c1b8e556866da678d61a54ad2845e60d0da280b6f6e190d8a0fd2e7afcbb29b07d5ea65bbdd23fd6b6bc3fd618e751e4967fed651711c4affe68f0ec18a4fac8ce2c2f9dda72ae4776d4112e3a19af7e56560f95977d12d9733decd02c4edcb49e705bf2176d8b7be72c43fca605e9adab0a0c4032de71943d16fa8027cae086af29b4ec906e44210e2e96bc011f54dc13cd0eb69b4859486c0d5c105b3ff9d0e5fd60831eed98c823bcee4d4626956b394dc5686fc88fefd4c952f8edd127d4035d7caabd1dcabd4a15d8b2bc7a8e03343d54b9393d72925bdf185f33c731453f4a6ce9d181623e8d1b1de61a64b19b52bc0bd335d7a43ceeb7a4dc70a4fdd27457cc48adc864a170ac183127336123a7cc21f8143381ee07afe23be23f0081e134809c73487689c6248b526da8b4469f3657f4362de37201277665226258773d36a496cc167bcdb042dea3290c03c0d29858fce8f0efc5924caa85720659f4e3c299e54d3f65afc1375604128e9ea08c41cc29b27b39de885852b6a4caa2b06cb707f269ee74bf826e882a62a4a09526c147cf8d5b0dffc81cebb40935b0c44afd306e3ef46418d641286528ec6f7927e00884a78ce8ba041c22754c5d43aee834e4324071d8122dfa8121036ca977de96ce1409b2d5f6d3d52dade45dc9438a2e009b4ebc1070b076ced0a0346125440cfcb97e9a11dbf89b2ef17453265c491e7caf3c73c67c3ba0df0aaf55a3097c2c1969cfa87a5023b7fc1ff11cdf6ea575d74e00136231303282a03dc25b79876bb7192e21b2a436f8df3db93e89c44850e43ae841962bdce38414164e91b6ea84121e233b501531d7c88ea8250af3d92a6d6652266b736fa0aeb33b153d8087f665cf396638081d88de61f56fb720237c650bfe1628fe69df60328fecfd63b40051c331910c18b5b28a788bf377e6d9537e19e4b7875fac433b7e1f40359a1537fcd434480786cd73c546590822d9280bc6d9fdbb3fb553dd391369c88ab2005780b94bcc3f73819b625d9ee702de518b42dce1ef631648f4b3e186c6a7fb6687efe3fe575ba1ffb33d8a00d7d2a7aea45a9bc69eb4eadf572903d6378b8a09dc8dfaf9f93e8216e867dd2b133d176d55df57adc982d720a586d95fe9d827b6c65b2a99c7b82f4c3762f18f042fbc3d6f3dc696d3170f8b194689ee52986a46ca2b12b2301992bbb79abe9e5efd1a09a59100821b30d2884216f80fa5de5915cdee82b7a9380c9af24f89eefc02460a7af55bce079da6bd098d87638dcaf9175f6091ecab3b60568763b5afbb42bf90528f2fb75458eb805e5209c3f13aadf24a02ee054bfa1c718438e3b1c95ca9149a6ffce2f94826dd84efba0abe3dea656e842d21760305fb1ad4f24c624e9e17f97bc9589501e47c5b94022e6ba6bc495206b41f3014a348cdc2c620afdd2a37f03597f1f160a002789a75cede7331cd301eaec28c3433f962ef3c0eb45dd9ee891618f4b55bd85cac72141680f5aed64bc97f30e5891c797fa2ce38b6d9edbbf2bda94fd9b6f776fa2c4be93e02d9e100fb3784d89a823e16cf8660504b52d793ce38db405b2bba53c0739a0ab29e99774d8bde3e9a0c002c1eb15cdcff21fc3f642a695fa5b027213b2f0ded88566a00bcd0a932f8313ecf74428935ea92d7f7b7ea346fa2f3232c4eb7ee8ca26affcd1a39f5219950189ddde8a824038957852969d42e2b368e332b8d6bc87ade80e467ec2ff4577db9858b3d43c7e45b6dcd81098579008ce79e58f1efc816198bf84a56527e8baa2f64f8229331de79f19c3e775eeaa56df3916843c15c3153d421e29b94262e35e1000c73570011e4a4d7901e5693c81b50bb22032bd62d389e2467da4ea820318038de1387e32e8eac8ebaea73956620a51ff071e12b363ce21c9fbe4649a07c09ae8e3eb752fdc0753e2b332739270e0fcf73fe07192e5f7b7d311cf9f8c782e6b52cdab23d4f127c4a1cd24c4c3dacd108a5073b000000000189417040ec111f12210b709548f9e1cc72c8573b22b4fe8ebc2b367fba44619b0700000000000000fde01c8058e7742a96de3335e593337703e088bc001e07057194958b355776108b2a03b6aee92575ac396842b4885628906dc34aa3231642ace87bbcd339c100a33aa835b0d7c9ddd32f743a3ed75aa780b46b3a6e6fe904448918842144774846bc858eae4d5ebe546e829728c2d5e9054b5364c2162f56db8c417e2fc3c68e0b218b7a87ac2d72f4764016d279338adae53d94a46b323474d65b5eed8dff240facbed9e30f5a8489f4bb7e9a9967af53fb52050ff4266850cb5695cd4aed169fcd37e3517b98150a05b7e890a18bb7d8464785b11e8ba977119491179fe35d408f87922b59c979adb9333cc16a2ba0802c51b3654b61101a29ca875d3ed6391aa0080c22fb3a8422c0cc49265feb5cf11a6d9af2ea78aa71dd64c5a49d573e6bfe1a1889a1c33568ee8e079d0939d6f82345f429588991f538b0cf5ffd141237d63a1f46a21018d0ee67b806dd6d84c787453708e54d77ef56335e406930917cd59e2fcd7894c0874e9f33d810b4d2a3f7a9a1777842bc83895c7272315dba98b60f43e00ff33eb461c481023a7d5952c8c3126a3d8e7717e1bc25e98f3c0148de9c0fb393269fbdd6cb6525cbfe3986b514f6dc8a84b0db284a174627ed685359ae81f47aacbec39396a6b22371bdce3baedaa26712a3a5fc10e6b736e855010ab111a09671488927f7c37dfdd21b117248175f7ca11191c01f288c554ff8a73da675f8a0eb442c7bcc9db9586e74f8aafe4b53dffdcebb91861d7d5880f7309937ba80477d7b0f024fa698ef32526b0f77a7b57b62c9f90ea42cda9306174953bf7d3d6a7982fd2a15188e3eb173ceeb794d161af8e3ada8cf8d110ba90bb903a7b9468f2060d272c74e4041ccff905e5063e219439da71a414bef4ed7ae3659161db17deb30afe85c81a9d240190ffec960125d760ce460e0bedb86787d3dd780db4665c5d868d7cfa19cb93510532336d789a3646cb0f0135ebca4ccdf8e71a5ff81ebef8d688b98350713059571dcd61f48ea85a51bf31b63810c514ca9f086717776d3562c2de47ba8df330d863330e20d83c31abb2cc7b9d2057984814615f931da172f6fd213832842b1d9f1c91315ebf0929f0b5aa8c1cd12f8efccd921e7c38347d1f3e1d9a7426ff2b34e888b76099eda31a3eb809d74231d5817580df2f785a2bd5ec755c201a77a64616508c1e5dc0496b2c1133651a9b9c5de7d8adead01772ffc3d2c287fcfdc674309f1938f04a88f071e2ce464395aa0164913924dab231b878601efcbce51cd71dc472b50efd13836ad50faca2c5036cc6a14df4c0218a3b0539db3519fdb3cd7c0ddf8c1cd7c194a01651cb240d81fe0f22b57d66f1c3908872f70f482763dfaf570dd4a245cbcabc24a0f58ed2c1a14c594d40cf0703576bc851b93c301fda7514fe6e08ec9a5d47538c00f526987a4de808b1814c2795099eb81e36565b028f8242922e855726e4021eae5ae4e766bfea6506b3386afebb9ffde59fc73eba6f6fc38e97c3c118b01ce743d99810df8d18e8ee225afdbfa00e0d1e4dd10237125e79d5e6301f055a361c9b63f40c214e730c33b28c2934232b4996812b4fcbb999be95c4ff2b76e59b1c82cb8020f38ae15f1befc6ff684529bd1222f8e767a0c03bc993bd0defdd5637e206014d3404628082b44864e8d1a478c42dec28b8a23c23efa357833df6a8becac6be5eb1be00dd21c4f528fa5ba8b7c753e2df288ffaeb191d42353ddecb142fef90c0b43ea3344910ff498e675139a8e5e1f38949d6a77ef34290e7a6204b12110d11d6b1a2f0b5a3182e5db5aa4ea8cefcdb5bc9f55cb2dc1f38d01fc63f1e601fbf008a995f68a3aee8262f71669d80a7bfaecf4002085468513289ce16012f1c67f1dc9801abd004b2875986ea03c8dbd9ea014acd3e6740de8e2987171e759f6a82eea2dbf1f8ad139a3220ecafa8e66c8a8628d9d73906614bb361a91e28af2ec5d782eae93a0dbca8286f24179a29d91b45f8994dfa640c0c3b773d8032081f132741081b52f3bddc740c1af6234a1ce28bd71f69a0448093ea3f326a5e1baa0dd90b1f62c1ffdcd55e69cae0266e70d2fb072a455e4f17a813c524f1dbd4020a961976dee5de199abd9d9dd57ba296df218d7ecd977e493929f906505ed4b5780dc035c378f7a2e15dcabeac875a56d7a27313aa11e46c0dcea60f89fd3ce9dbdadb6c77c89c917cbef1bc4c5fe5adc0f2cd15a8cece3ebd056c209ad17c9349b9797867a49a89dd6ee20c930d81320570214b8201c5c74068833b483338a6df66107aae6df0ec39327ca20e2ff295825361756eff82724022afdab05b3360640c31d9640598e4dc8d99654bd5ffe6cfe0bcf63e85fff3ef54a716699ec98bbb2d537fd2c5aad0e16281cdea896b1b1bd167ebcef81713addb5fff034044bad920113c36cac545157b5645f2d0b987a7a5f99187a8d27154297178ddfe6166701ac1fcffd819960982eeee766c9f4be5f7063d52711c1157f0d37f462d47b4c02a91aa4d2d50d44c7a8170316c84f7dc32b0127b5262bf7e58068db6952716f3eba13ea09d663e9ad917ac816e349c952d414ab7be5a5b7d7fde519b0531c7aa8e229d58158c7250016b5f20839636e50ab528c6bbee6215f921907cf3f779d804405fca883f2390041714eb2f2270245c5d4d871f39a94e33eaf45757dd49ee3f403730eed9a4a07722fa58b7d1acbdfe429c4329f8836ee7399cb037900399f1a30cf7bc69c2836092585950b8dbbc35a9b2d3117c2fd249e4f6e4c2244418d602cf4976d08376dac912dfe320dac4bff8a9b9116ba49445a9788db03d24b6d200e5fa61069b59e2784885590f52a4fb2a38b1eb9290188798e62ed1c1f94f0240622035d1d812886cb3c2f68f553910771226cc8c8597adc2038fcf58df64eff3e5b5cd8e506c8a28f63645ddb363d3a6e8e999bd7be3bb9653221fb48dc96480ea6db9edf2db3ff03e2530c26a0b3e7e11531b4128d0391fdc4cc1fab14cc8c2dd3de15e254414ec3b2960fc21903ae6ad41af937b386a9a1e10406af81cc0f1c451883008f2341453121c94a7b0aa606862c9f20b8dba452f634ed95a8636622fbe7ad83959d9afee675b43dd118aecaaa9ac56c694fabdf138b3d6fc501052ce50e659efc1693853dddb0c3643b89c74ce882539c02b2881e97020b071a6328078237afe1c85abf5a3a7af8a64303e917001e037a576cd426631d30cb364e0693e087b5b640b45fad1f80a7fc937140e206715499242abfbdc12946604b6b0d37002682035b423965f39beee02d945648ec2e9537319f8c0890ce0c8c2c490cce80ad720763d606751b0906cd2af0a6637f518c6574b79403c4b8291fda5d174bf788076e0f2a21c1e433d7745e9a3292232ae22e6d9855f5a6a08dfd52de3bd67faf6e0c91c41dc9cd34b673ef60d20f5d60591275b37d06718134ffe98a1d1f91f2129ceb9b481e65e79c8a7f76f1b1795211d44e6f08eb9bfd479c85083abfbea53f2c3bc7e1ea28f8883b70c2b3fd3a97ca0aea6991f6735e6508502212e1a869b6b3bf0b3c9b178003914a0e28882c14afe13684c67db80d7e2cf3030aeb6b9c4bb7b527ca02321347a2f8830d3947cd1ddafab7335b9e81c5018783089b15600f95b79b843115d18b6741e9a8920ba2b1636500666cafa0e9231a76009308a85c6ff6e61923baa9ce7ad9217577ce1cbe66d8a99f1faedec5c48fa20a868e192cb7d6bbe4942782d0f46e899a40112d45312f923171d42c9890ebe5053d5004d18ddf153ac75b8e088c05bdb86d5902e188c93a005d1d5288f651f608fb2ae3a6bcaf515d6f962fd675e32833fbfe9efdb2301a8a7182200fdfec8306b69ebb50fe21fd0206a601fc69360205c397cf0394b52cf1ef420e4f2c4b573da0c97863e04a7b2948fbfd8bac5ab68e12b1959998ebdd0cdefef5a6d84a30300c550a64f0549c0c0aab76b02de34f024b8c351233117cc2787a3c53a7cca51f6f8ef2076896c602d29f14e36ca4e60ae9dd00a46acd2bf38d3c11fcb950380d517dbfaa3ac7f31a4dcf4c2d1b902c1bd237aeb7891d7bb9ff08e0e2eba9521201731c8f9617c3492eefa3fadcb30877d485601bd1fe11a2cab2a4cd11c33d2627b1fd02abf30a564fd82afee0f9155daa6583bc51228fad6f05e2ac0356650d77effe8321725b4dfb095ab79975a2eec00ddd47df1d825ab7d0d3fb414da1093772f311b4b783f1513291bccd3634ff67900e6861222f8066e9520344050112603f6584bd077d01f68c61f7d3956013ba6bc359fe2cbf12b8cc80b30a084b0554b66f84d9815534d4e06ced52c7abb7e83f78cf7a96a0841fc31e3614d8543adcc7476b057ec57e868e0e2607d4066ef4887907e8d4c024568c6e1318eee91b14b808a96932d83dfd07e69f559f3cddfff556201ee7616238906f6f5bc3f73976bc41458123ff09740626014271b002ce249dfd0ecc1ffdb5897fe086f85f07f40dc3e0cfc12af882e537092149e249e0417a705a1d9613d6f711c39b6a95386444f25fbff8cc73e27040e3050b4ebe6ce5fa788328979efa4ba3937eecea2bc70402d1b03c0bb305790c70a6da36ac0ebef7b242bbe623d040f84e90728115130494983ef208760d537c20d3f1f29e172639b3ac91fb218ecd13a2c3307516212692c9c4d9f48e138d6fc762917d5926c4f188001c963d7fab414b0042ae30a0aac21991efb96b94db00be63072433376ff194ee9858a1eb9d5365d94b6d1316e54cf35ead5ceb8d7753844a6bccf44e0c6397833ef9aef090e3ba86dd6935b5d866081de2da9ff9217b9f14a17f9a88f4467f7d6eb19dc784d0f3add23e31d61d454932661d709b7a9b744b0296e7dbe67aac9d4f749b836a18f3aef33b1b2d7e238bb4ed0b2b0ff458fb6bcab3257548336385d8e3c9406079c29b18ef066615ae70c6a41c0914c78eefa870d61f72c5f0c53baf242251c751ce0e0b6f3b20b536638d470eb20a995071d3ad53578440d0e861d004b717ba7af4f5066a00eefcb51fc061669e26259409a6fdb6cbe8016232b8f053d9e3aa7b23c5a6981db514e084dd4114ed220f7414734002c97c5c12cd3686d4e1f26a8626265316301f7d855fd9315d5b29ee08884e313d165a1667ed0329119cbd1cf08a64edd638b409439a86f9cfeca82e0cebf2ad5aa2b0d7d3b078f44455f5ce982b51ad8f0b0a3691790094ecc268327456e9b28596649d507127b6d098e987e39f7c0ffe259f4fddbc72923e9b5d9761e960a3b55449d7c8de52a31ea641ecc16729db1d11e12392ac770b667e40c072ed36f47ccc7b18f1f6834c73dd980d0ac0b87e9c1809e42b766f2741c78854a7571b2806fa3f7facd816c75d9be41cc0e56d3f9a183ba1a2953a2aa4cc122e6a7fcd6df5dcbdd12d2d1ccc052a251f59477742c9143abd8907dd878bade670b175c74625ce585eedc068627601dac8d1ad4446b01a19d01bc827c638ab448c16a36ddb6961e4e330b54f35435258b9f10dcd39bb278a3e22727cde5fa688b674e38ab934ee8d592f44297ce6c43d10159a781ee30f61f26dafb42ad4b7efd74fb3bf10f50461845f9695f6a5345924392a52ac4f1f5f5b7de204afff794cab99afa32e49a2791ab9ba36bd899478709e642c52192f40772de7753efdf8eeee89d183749cea7dfe764fbf0ee965a043a445b5d55b0d232b1ba556bf541ce5ea79a02d209b393b1a6e564055e8a2bbec3e6cea7a0e3b2cd2f0a52568fcb39c0bcc28b4262f9a75901e373aaef42cc67839a029f10c12eaf8789bec9b0d6a7aaf59e3db32ba2b4fb210df585ecc987a208425e7eb4f08fb40181be25f0f08b64b557f04bb803ef2f1fe71dfb8397b3af94228ea0f0900ba781239c60cfd6e636233b21fd5befd2d07c03aab39f329428d310efb035c2f1c354afdb336734ec96cb13df5b0d6960b789aa572fddbde28c5f7026ee79f2e5c3ac2ac5861b7b1b0b29007c6b397a9d7c6ec3117fa9b0b32f1369fb59314213c30032dc2cb89222c35c19ce930bc0d093f77b6e6c6a18fe0e47344d304c2306e5109ad861c4cf7897784c5b62a53a465af034a10dadec667a4cbf34c37401ce0fa4d163ffa17920a2ed21e6b58a66c73bfb59d0562f7786ddddb831863b325783f32850a76be9df6583c2a1dd4d1594df57aacd33b5fbf02438de3742377354aafb3e7988392949a9a31651f2779cce74dfb618cf70a391a0e207918f989100e8adc94dd09e0c36924b9abf8041abd66afffeb3895a798367601257d90383072647c6548aff2a8a84cb372f095861404331c02ef5f66ce21bf60d0196f3f3e6080f71d28d9e76a91d373990dc792f81392703e7412c7fec20bf308c4c67d1d560d9f9a186e839e68c0673a9fb21bdeb66ad46523679d72134565cf26ee8231691724b3897351f945137b278a022fac1e67f1fb58f94615a5de51bbddd11a3b4c410aeb977308b4df9e6c6a7ca60d21d0304d499d68de1f19e7f0ef46295e3c9c2999ab214fbe1cac7910d2d006819e9bc345c08ad812094eef6770850b160b1cf9b5a89186fefb431dd693a4e76b54f9a00a3abef0f2aaf074a6a56fd94e12904d9a56ea763896001414216d7ad295547002b913197cd588df3d80baa5ab1ba8e66966b5728cca01dcdfe46a1a004071d7597065b6053fb4c092c9d34734032826e5e33fb1c1aed3acc86a6d40bb8aef7eb865913878efca765cf8ffea0f0eb1a9ebc446fcb9af630ab93911814297a02d824a69d179c8238961112779a616b3910de6206be798c0d6a805175bfee0ab2d81cfa36d360382c5cf5b8a22fd0b92df8adb0fcc8d287c9629831f11d05586781ec446039ec11f247b2911bdc523244f6a5acf9fc1d94a8c6300335c08e147493a67ed3eae9b5dcb308ca4b1013e70363c83d59d015f6b8e56157250342edbe336b94732a476ed76d074b72fa6053c5186cb23dabdd5eb0948045b59c3886d3a9bb8c895d610c2f0014d7ad77f07bccdc21b9dbf11ba601ab24d8a49e2d683864bed45aecfcc535745214e5efe12cfbfd7f6fa4e07d6a99da0fae35c6a43b1bfe0b3b029c158b94c44ba7cabd30ceafcdea59e16384400123550da1ce8a557482d00df3dd10be50bc1d1469a1b2b7e0a92868f5e623d201ad9a3187390cf7126828651e00573419dc71dc8e1a20020e691ac45f94679e4dab4708b6d4789c76a530073a900f850b1c13cb293942738d1c69cb52634c6613969b96a73840b96ba54c6acbaa63995252ba0ae34330bb9870826f5016214313002c2bae9f289e1850cf5faa7a866f8c19f50938e6d248e75581bdd0b5793519413f4c927a1b504192f58a16a419f0ef6b7a756e1d7182e376fe23fc050200508db98c24dc8dfd7e21db222d8fe345198ccf2a18d613ddd78596730a6d62a15e1d89a63e4d760b0210e38a7376c10eab659393e4147312e1eb968ae4b13ffb084a4e9ab407a2a080c3572ce04ba42dcdb0eed473e6f317c6f306dce541e8bd4dc413b56c3ff43260641d07f6cec38b406a205966138382200881aeb74b86f7e868347bc8d222b62f186ae9908e645cf3bbaa163ad2d216db04f67a803d5ef7bdd9f1af3423b09b6e1fa798be91da85706ccc3541ddd022f2f8c37b66569d9d0f2de39831056d9479b99c06ed735090748709d598d720ba3b738018120a7be3f119c9b0d79db949f421d968aba7462736db56ead7843191afc2dc8fc5136a373f9279f803cb7508ade06c0fd69a7a09b0b2c85a7833a1390b5037685dd5461b313224074ef893f6c08d7e4d5c808eb98c32eb103fb1b0cc425f44116168d1be1e72cf59b5a0878440c8316410821c2cff1f63286be803b2c48816326827190456321fd3ce680d3e50bf9ceaf6b1d7ef1dcf75705b6bd2ccef26422c794711f31184e29b161213c653325591530c3ee6aa96cff7806b73093d6f7c5e21860aa5cbdb3c720b6c07c64d2b7e67c46319de33993ad8d5d6e1c194ec0b90405031e6b0f78d309d398203f954855af91e4dc0747ad0bda63800daa5e316156537f47d10db6d940b034cc1de7a004aa7b0ef853f9807e28d58d048b46d2413ba0931fc1d95a09a9fa989ecd0698ab3efa18169db5a8b92d35033607a01af715e16fb13a64c12398f04215e8ca53d71b131fc6896acd89a7a96b2dd30b28dc5c5b8f8eef31cab4c6a2d6ff9971dbb1684fd3d918ccc160383a4206d7794e007aa6e270785e9453f48d2bca001c4acc8d3b7ada3cd405dabfe51f200047fc60f559f5dae8c1e5c6c223257537e37baf9b33ca48109b646d1238520eb5e907cd1d797523ce509d26e3eb8b9bf22207cd2c8ca831784e7d2d012b2324ff4af79734a120f2dac701dd51ef39e3437827bf829f66032a55b729c72ae330c0ba05cceba6fe40d25d1a2b435ffb2935b272088b441cbbab33d7334152ff263c865396e786b7d898c5103c4c5b6ae11355a21a7112fd19e8c8dd29f6c2ca117e48e4524aefe115e25910c6be924a03e75966f4cf11a52045fbb72eac99062e0419500b28aefc640d7e8c6aee30237f6e91cf268f85edcee3fd30a215f7a029cef884bcf5a51e8f9d56168a0a94985967217316f99c3e98edddca4e8c8517075af8a76e4dc17a71941c1d5217c25dd6bed73896a5ebfb3a47f517e2a77055328aa0108078b5312de421e7b8f7d3254224005fccf651f699bd442b9a953d9602f57e030aab33287f4c773b8f50a1b327a83827b3d61fb881745f22451320843c47fa2e359458408b4ed36aa945fb136edf7274b598e9998e85981a5cafbb6723477e774b5093e17cdb9f60b748f3cbd5babcebb4957c4eddbf9c9d0da7e6a41c211cdf0e550ae4f688240d65cbeebd3f63e096d7438091d63a0fbab5fb99fb006b18b4845a6515a0019be6beab1190e36069bc2f029782922445a2d387833b052f2e087fe190073eca8e0cd89790c26587de79288bcb99654ed913b085541a2741bd62fb23c174e923b8bcd5df81bae8eefa6981af6d904f067763153e467d8946919fa53aec059917b7bc0c44f940f1d430f2d5184008a40f6931940641421f81e1a21632b84e290d7988ebf0d21c0b764b2ae5b549eac0e8ca9e067e593306c8267aeaa639423025c2a068adf55cb9771aeaa152576758988deff73b0f20220b5768da1865345c70ea5fbbefb05e89c2563d2bd60dcf29a8e0d211f2491c1f1682efeb83ce0fdf3ff66284b3f0be220920b8d3c2c074cc2c90da1114387613adfcad952a8df5526845f3896d2ba2e6c2f40bdf7d953ea27e33e1602eb37a0fe7df337147bb42bd399603a7572101d306968633878c448100b1c7fcad0e74b890f6e0241b058b28b613eca454e24302bf19c4b24445fef6763f70737561671040eb1807a06cdf574fea5cab324a365b683e344e789d11f3dc8cc7d8cf3bff379192c25a9f2325d32d0da28a2e5fb6bd8f55b9a5332b0839df8756ed077317a4eb1be9ea217307c705a86a18e0aeef02ce66c322975b667dcdd3d218e6c8641416296c1c67b3d927cb0ca8c819a241211ec0b99f058e06116fae7d0481838584cd454f3bf815aad272087fa10057bd50da301ca2cc29ddbff99b7f9e255a4a81fe333ac60e7893dfeb9c2dacbeaef3ebcfc99bbfa0689d966bd5fd58ef07d184c818ffff47813ff294426f5228ae746a2369ccb30dc699c2545f0d5639633e0b4701aecaec57f1ad2deb0a9eeb9d9ccbd7118b6d1f19711e1cf2d345f8056d21a93f246ac5b1958ac321bb198f13b32ae3e4e0746c5d6465fd1a44a004d7268a9141d3b735a3a2b19c8435400dcbc0fa46cd442fce7b53eb33557f9f0b753c86875d1ea0f0512ee35e16d5eeddff9d2ed70c5831544715c41e4b08a469e22b3460f4330eb496e607dc616f4e031ec92f3e262866b2e4445d4268cbdcaf86b5b40a94704f6d75cb42786f8263cfd9511fb5c76c88fa0a56700847af19e9afbe2d440223d3b4b2b9e28b5c3da81c518a0b0ba395fb4fe17f8a5331bcbc09f628802d02ce837b9c9916e0b68bf4f5ae4da2159822e9e1006b9b523844ba58a47ba659f4569773e62a10ee3b091fd37a904bcf22908028620d9dd4e3720b693d2f3c6ded2ca1dd0d049d8d1529eccce31fd6a81fb9c664d278881333ec9208c5bab6603b47c1ac76beb693e2f38420c963937ca144a2c8df46eab52ba142cd719d843c3cb7b356fe19876acd283cf15adbbb465871fa91e55d6109927d045227d8bc21cf187c84569ecd0ab0d1794f56681965fb183763e296e6a16779b470827299056a4dc366419d7f09d6202bacd90772e0f6b747e35f7051d78bc0d3cfc1a215d2c546b39bcb3eca5afb4897dabffc786fa1dcb6a5be62ef6986cef752b24021010095c43a10bab1a6a7699239244905e9a47cfd4f07e5be1dfba7d303387ef0df3e133cc65b3bec5d4d698df47b8843f97c9fa43c96db5192fe0f9728f9abc9831701006ec32520323f319b887b8bd80186235dbed10e7bae006383f1fc0cbd310e463189c096d57ecbff55e386b0256262e5263fba24795ea52e526dbb65955661b613000000000000000001003a3bdb3ab1d76025840c5243e3d8f7d0312568fa3de328917aa9d7e2007443261ae331f41bae85356357ac6a14bbe68c4c5a607bf56d20c7387bd9f90ae9a30700000600008077777777d80a1977000000001c1d1c000000000000000000000000000102acab2d4e10c854609271c50c2c117a099bacd3907a3a58605d5d3e0e8d6e5ea71c0ff65fcedfeaa2ba963479bbaf9b3f746a038c7053f1fe3aab09c47f5afb3df77bc7b1b11f86213729005f2f6f93fc566b449e92024191466dd87ec769e205a5a428342bdbfafbd738eb3ca7ac65be69289a620f123a62e4459c1dbe6fd4297343bf12c53539db2264e828fff0d59e20310b28b29414a47c2fb1f9cc7d8435e333246e6d976712978c71138a9ecdca241b683bcbea9a472880b5d43a830af267eb5656a7687c947710e46a15423f531fd78c52995be0bd29542691590aa500c9a13aeccc00a7baa834d40c66a87178df386f98c880fa335b3a76f97814b4bc632a6e25b2e8278f720ab48af736f8c0c1920ecf942d401156ac9c325569b458bd1d4e63b75b32d6a2395995c3748b32853c1b1a17d16a5ce81acde4f66519d91bf405dac4846b30adb02fa7c049e9a62df8918bcfb68648af95ec147d0a621c403bf99c6a5e5f9cc31a95509bcf494f390e6e13fdd5e22d9cccd857bc18aa79bd3a87ba507b2206539166a47a8ad3d95f04e3abdc8da6253924aeb2d1e863fb93a71aa852413b6fd8f467a51f949d31f3c86d7dd0e68b76a91bd7f3c3088bd3250e5c0694d2dfe26f1fc702ed4f0b448643f1af6e4dbda88710f46dfcc4b9d47fea0305d6a2cbb7598f583d845489fa9e521ad5bc772b300c864da96df21ccdbbbfe954be73f1977c1e83109527c2f7a2a20198921c9cabae12152139c5957d82351a3e80c13d76c920fbcbfc45ace0c6b26649d886cf856e3a007aa56b99da61d9b031fbfb3870b32ba66d5872afdfdd3ecda96c7ef12cf516508f87ec53da419e6b4fd0688c78ab6a9e1a52bc389fca6c45b9f50044ef78192c96a477c1ae001017c836fe2cd6115606bd53a1cc61f0cbe8d755de6485d2ad4dca67ac0e626d4d8d820442b9b05bbf88a16fadeb8bf621d3186e9e891f36311b307681f6c76e148ee30eac97e6e5ccb50c3ffeeb3c5d536fa697a3892cb83f2689cdeb68656f532fade0329fc9ab165a782a4cff262e1cd3523b9f09f9dc4bf56f209e909361e5799072c19dc05d61a402238da4c68d069508860d1f52af17b0a4aeae12a92c7c5bb222b6fca4a8650905c163bcdb59ad763bfbf8ea8c49cdf2154655bc297761f552a69db930e7a9dc68851e33950fbfb6c8d1740b23bdfdd434dce055a1afa8f1f12b4ad56d300479735906e8f39fad353651bb4b21d779748058e94e8e25771e300517b53cab94847aad8c4961f78e9b3bde28eed420cbd0c77395c15a8321d482087bd3b390f5c10e168dd083a076f9bed95aa9a9410392cf904d9da66d27c3865a1b1934057b6c7d5f6be7a47694a90b1646809084fecce659bd57ed10087ad3671a4a32932871eef82021bcf3051b310ae195f322a0909c48356b43b9109f99654fcc58e297c304dbc02a525de21eea717a93f7fdb0013950354ff70e2c0aead2147bf193eb468cf2efa3766f3a860d0774aaa86395dab02a5f53e163b98cff0aba0cfed1928f8a9a79be9e387518cb75a5d66fe37f540cc58df5bbcc86b31edf52c39426fbe4c52c360f643acac10e2743f35620bde3369a08371dc1c2644c659e2bdf5fa9846647554392bf32166d69408d080350130a7eb985027a3fe777fda9c894aa049ff9b670d7436a4f54f900c55946ff7f036d87511432240fa50131d99423f8fcb35ea474b52ae17a0d4a77aeb7764d8e0fa6167632faab10351eb3bc1cd6d617b6857855c2f7fa063edc34bc304e0a17d4411a8af39e61097b19245ad9249dd1762552974cc59e609e821a86d008655f75fc38d23fbd61efcb94ba37d979d9fdd992624384751abbe6124a81d980a419061abfb9426ffe6178aaeae18e2595f42ce3a02817a515980bba234980acfdddd3a9517628fc1e0e707573df5f29d48d6789a7daf36c20a68a60bf29ac3fcc2596a692305377b151a3136742d679cd3a9c6949db3cc787cf05db00a4253837bb0232b73670f508c9494a608adc1e65a67cb60c80444b7f17d5ae8a94d14c8ee6dd62ead17dfbc300347e6767fbdd4007cea3256f193b39df77d756125516cbc718d64cdd655d8b75566a545bc7cab21c04c362e39828fd39d9eb6638be076cb4e1000957423964eed733339a944bede03b33fc0991557542161fa326050b8b12d58a3d996ea02c9540b42b038f0f0673de36eb41a10e6d3fd2a789e0d660747d7261d46fe25adb071e2cac4324deb6e49db15eaa7f5458a578bb40b95f72cef5dbc20084c03d849651b84939094c1f6dabcdb2736ee5e22913e3c0560520f30dcfd1fb69e119ce664fb5126d13e1459499a40873000715ac593376347c124d371ca604375201ca58480995b39eb507192e5f7b7d311cf9f8c782e6b52cdab23d4f127c4a1cd24c4c3dacd108a5073b000000000189417040ec111f12210b709548f9e1cc72c8573b22b4fe8ebc2b367fba44619b0200000000000000fde01cf01e733534b2547c5da63419234833bee33c895c80fb7f91faf4eb65a3343399dd2f754c0c481ef9371bfda08ddd39c10309f01eacd1f476a62fabc23b52d59c2f25f1a15db7017dae141fc0ad9567167b6bb3ab515a15818c54c83fe9ddd1197a8dc868acb6bb50a8512d4786a1d98065f9d2134b39a33adae58062b351c20954cd855fc785db23240c0d1caa57e10d93d95b15682cf812c974de326a3ba914b1ea47273b76091095fa84d16cf8358f2d42081b07a4f8271cea907db167ab2cd8c750eb88054ea7aba8892d6369df064085e916dcb6d54e40a5c9d7a566b283bfb0dcee58daf282542f25d22835167ab75232355c344bddf58d76eb65a9900a6128afba7f0835ab345616973cd538148789ece31fd14b4bc932ccb222e5489dda19e68c36cca6c493b28bf74b3cd32c79096ee56e49d16748eecc698c5bde9de29240635b20f69a521678c20734f45b7c421ed53185d6deda3d36e51949aa89cc64c7de2a5812fb31732114e0dca247e6009f32c5da52dbdb941cb6310e6a85eb56dafa6a3569c7263b68b7e9814c212cb24a056a96a717cc06996f363b04b1baed98a8532bbe1555e2cc1542bdf2897f711f979321e07c5065952be5d4c9ac025f7656091d6d3b3ce05ba889c27e65eed466e6ea8c235965146b0fe32f84190df15d8f7d4fa1a6b3068a5686702fd22432ca2d8ba2c2353213ff47877fb8bc2deefd1f88e54979c68a199a6b018aed5551c194f07832eb36ac78f1fad7dc20fd92fe4ea0581f08af7932cd6cf607f6388476e37460f54ebc7651e7ac7c4511fb2f090354c487487cf66e8272a5e1eab545ce0521ec21b81a7c613cd5c323b835afd0f89405699a98b3a301efb06f00e55d7350d762e4d9b0a395f903a3f11a059806b377a6ec23edf52c90cb559163f7fc829f4f25b4a3a20ed09e9ed2cd02df19f9e13b15b1d485019ce53b17898cbcba27c5b35af24bf69b73eac497c007cc7277c156000468ed0f72cb633a32c4b0557c0b63456d116fcfb21e575dc7b18743a335e65e4a63255880dc309e0975f5a5fb082b8fb75c13f9ced1e09a7719ff3384d2326e825717d2503d3c5767dbc03ffbf864e33f52f84178e86ce482112186ceae89446c8d54dbb9995c482e3529b87820410fbee1f3ebd843ed517d2003d3395fff426a8913e60ea93e4ce2b669e22b45a5977d3a214d52040b1906bee392e3cb6fd2f0e76af1494dadd0e9edd3d1213b3929858369edd3dfbe38f18c96038b4e9b5fe5984d47524bdf6d6963609f114672062d559087da13bce187a1894330ef060ae677a209d5278370d0dda8613e055ecb5824688ad60c1c6f3121be7486f234c9779bc6e72b4095ecc080a2ac042c00962cd9bd573209abd3c80265ca366647c364363312aadb82d62a69b66553904bd3c4c2b731aee10101d4abc799c9c568fac4f2b2dde5e18f72ad038e77c1cbda478811097883d26f75e4872f6e9abccf5036e0a80f426d8e44d0c7085cac3f2c0167d144d22d5e4a5a4926f7cf8f61390bc6ea71483b1e1e233b5c29190c78e3d64eb4d9169faa23bacf10bb6b7b8f9d2ff28b15b3b51d45cba1ef62e1c218176956388fcc1fa7ace34e8b2cae88fc6849ac2489fe05f934106f4ce5f6d49597304a02d729b96fcb7fc93d643ad685eead45820c7c9cec3cb12b71105cabd2e1cbb89fe0e469898c6dcd107dbdbbf017f6285c8879c3c52494a71f49d6231b8664fa33823e9d2d1854c48bf041c012730ac7af06e402ef034c44aea171713aaa7488fa20f9bf9a7926c7825431dfb78867585aa8259379876fcd4bbc260d9036acc0bb3236e073e576070ee8c2b970abbb217065c3b99a11a20432e68feb01545ad84886b763fdce091425b03b8343ac79c6d9b0dc3c46143ca08a12f5f347a4235f6c6cc5eaa15ca0c11dea21e33da84aba1455ef9680169453f54e06e3959623e6758a899e06ae6d0f34dbce4fbfcc0df699dcaf715403d2de4d534d0d65cff96d6b273599a23ee28bac49968c582ad298bd976b92c3fdc25c6b8cb55a6e11316f2f7a75b8af2de49735234fd53271174dd97e8085f20432deb86de8a2dde1bec0476371776c33537d0a793468cbd26dd74587fd65f6d147042cb6a87468208e62204de93dc1ffe460bda9fb188f7d20561147ea2c82d4c6a375f25135b01f50345ba603afcfae114f3e8dc0ae40491a517766e0c1e0d09c3a53ca0a70fde8170abfc3cc41d5b8e86e37b20e6b8566a7da37c85d9421500d664e57ff4ea70e63655e9dec9776581f1f18117474758488ce2c15b11cc9dddb8ede676c14473797a945ff91c88fece3b79f802979c2a51066745052ad61f47fc036c673bf4eb662a1c1bfcd9fbcad1b2e992f382ba75cc0b66bed86699b44250561e2cb8ecb3e3adb9b6b38987ea0782b08b7db10b493e9aa476ec5530f474f48aed45a96c3973e69f243e4cf8d7562dc3ba7478c80c284052cde0430326ade78233df565c1763ebdcca2029c5daca7eb07a1cfffa0facba0de03679e4e2f4901a2e6a4d4a44c5b8ae4b23e118d546c95278e6183b1ea3a584e7195c37ad0b233588b7be5f4628796af90266faab6597837ec4163825e603cb91183a5e6af5e65bdb9dcc6dbdde227626f9bcdaaa0608e2b476f55741ace4daaa88bb5ca512f9834d11b9d067f274db268f790e8f2462729298b32fdd047afaff839418bd8d324474bda31ecd933e8ab6ed8e7a043137f1a7f1e47d8d6de184eeeb0d488d7784b35dde159dcbf12cae8ae47f49aafa28d1b824831117e0e4974f1f9a869e8965c5524a97b72873c9ca5855e8161ff34e33200916831e5cbf7842c6ec4820d562560ef39f0c86954137227d5ccd2e12e3903a3e5ba41007ae1b6e63380e322604562c675eaccc451be6ea10d91f424813e1a53e0898e57668a3587a979b15abe23a8edab51e9291abbb5fcc51d8b64ca280182ef86d03847a03df062dc9a3d6e16af5fb0b5ecaf01a15ddd02273db492a130f091d2ce6c43524b86ad09ca4be1defc563a4ba3e0d81d814492300fcc1e6121757a3d2e66305697a88297caed3d2c96d435ffe4abb10b8c000296f7657f260ba88e270b201355ad5f5738c31df44d6f9df64088f6fa9f8ef6e8e32c44d13e1e9706cb4d9f3e09cbf67d002c181c875757bf28c038f49e16e6f1b18421c5ea1e7bdcaf7f060e32ff66f06926f76f8027535d21e52b7fc50312583377b928d327cfc030dfc5884368c02b8dd93d8ccf42c44542a52a179bcedcd0c277f94c8314b7714be22a22ceaaf14e3b279aa2066fbd4c641ed17184152076505adf7f733004e51df26264722586d65170e5837df78c222d5301906386b6c2b9594a978827275209733e690f664fc0ad6e8a09839e8eb03efe2fbfcfeaf3b6efe14aa90d33a68098b42f0401ad9b80a1f301d6841dba1690f04f420c14996b2790291b091c076d9b683cf59d691607e1fe4cbdc8ac5288f20f047828d594b7bfa93ccb810e4f66d7494cfa8f312a4263630efb9730fcb49c3e25ffec758e19bbc6074c321e7e3e18c074cf1fe78e2b643566086f105f4ea7cc32e8c22bbee2f514a006440ea180a536d1aae7a6754f3f4196af0fe0f3a2a3eb365e66035809653b7eff17061a4bc142bd597361b7777973cb37a02f28a0121c8a6973a1faf48f68407ca737410073cd56b1381dc7022367cdf26ce8716e88659a5876ccfef99da0767be80ae872ef1a03085c3f0bca2c00373aaeafd9caa989f99f47e1d96e34f3918bd426e3070f6d98ee3dafd83647d9919b7e621eb2a3ca573a4657c19864721bc5601f18d41b84803ed57c3a67b4a81c031b13319a785bdec9316536d814cb75beb3016c4444f2ad0401d136cd8a9986c63d5182406de8e399d729cced21fb86aeea0ba629b4386b9a08f8f4962e44e4ec4099b4577b1b8d08ca72f952c612e057eb0f32d6ce1879b8496219e2d52d6fef86f0adabc5f719bb7bea5daf2daa2aaab202ab3b8f1dd82dc36abb4f5a357a0d10de2bad6e7e8409e55cb5697ae3a6678c3c90ac18bedfcc8ce5e7c264d13ba9eb9eda1e7b0a3f484495e3dcef48c494452b778db24d51620fe26ffb3ca6d58d613d489acee30b5afc98b41a2e2f6b82b82d7161164c7da34e55424ed3207c6556c92de6b0da96f41970ec6403dc9b6ebc1ebec5af2e892f5cf00eca29e2445a88aacb827e98ff60c153fd46ece716eac01f801b03f71d608773df4b7e01fa9e4b4e7dd2cd6858a48e5abb7f42cbf2559205aef801d90c13030c369c09cb89a72c2916b3232b44ba7cc43006206165793e134f7d9d605cbb8ea9650dd1bac0ae3b6f24259429ff5cfb6b33554a9827ef731c7631de06a270036d35e5d200eda11f9a5b4e9fae4b0be94240709eab6caa0b192c1931a9c20beb5749b5e9d7bf35e594c9eb715a7d6aeb1a7b799e1b9568e411e3abb55f9886ad36fe77a1addaeda4129f1a7c44889cfb759891438778630c343fbe4e0aa08cac485991d4ceacc6e36e0ff4009b339aaff1c20b995aa7537e181f3acd8b33874149f6948ab877575c4c46cf45fa18940c3b0b01cf8e26f7ef206f31cd3cfbdb50c4895a8b7994071892aa56e5731f79ea813dd68d38dc252a0a448d3476e1861218ebd644690271e2a1df09591c13326bd43dae5ceece57a53b82c6fb37d315e5266d8be1adf11871e9d0b3254757a2b89d212fb1a5aed71c3089d537de9caf3a05836a12ce3f06ba03b1c2e3abe04e20f85a5075f10efb5632dd250a46d36793d81fbbca08078b3cc5344849b2f4777ba96c4c51f50931f210e5cfeaf2fc0dde8c868ecd87b9ff7fc063e869186d9ef8f42ffeee75303634270b02c49f414484223ef6bacb10224472c72a6d2b4a55dd35754beaa51129d5066a4a79108c2707676debb8d8460792be989f6abc32a9955697e86f88b5c70f1324666e021da9b50a2536f90deb21fdd37d412825b6b789183cd7a08855570c249046d5fa1b78a9074c86b2064271956722b5b9b9098b47ab36e1813ae202082f9e4f2fcb1a23cc9b8c65b8eb101ee19bc52fcd59066db23e1c910b4fad04a40191bdbdafd60d411e63bb4be37590a62e6f67031969cfbf2d5c10b05f1e8da33598e1003d3683a2def2751ee9d5ca63d13706782855e30bdf8df5dd7e16f1ac100f6165f40176444d5fe0bad04b9474a0a733463ea02008ee8dbf421048b89528f775a67d8ec4df0c3e70ddd983f7837930ed1c9764ac97dd2cc6335dc961f524086dcc3dd1590766962e0a19bd99a9a0443b334aa44375c0633e1752dcdacf22024c04c7f8f134d66177140e9fe1716464881377cfa5af8fb846ce8d0a892b23ecf6d589e6524bf0d31a5becb0db9b647ffd3cf1f445633e60e2ab740c48e837c6e2f5d152898059437ef5f7ba571687b5d8a19c233c876b6dc86c4bc7712c1ff7638de3a6e2f342b74627bc8562277f2af8e38e25a49698a5cd4d8f30fa4c3ca31a75f1d04f9207cbea2141019262431e51ff0abbefb94d92a8dee90c5cfe12c87c7587072c6972fde6cdf0e5ae87764bfd4bb92084c774605de23ae0c208151dfa4e96a47ce5d37a6279a2ae96ed85e38e691bfbbc791ef6fdc0571b9d0839e626f8a957c350bb3975eb71658eca91eec2016f947993f799839f51a6b16730f96282ae945979795d57a20cbc4f8b8da574ba52a7f33f46debf5868fa942015d8b0321b96553d3b1f9642fff3f8385f7cd57f59990459c87287a652069b9215854090f87b88bd28164b357482208b7acad2e69b59ecc148ecf8fc8260d0d217e29b059ea79ab5528f0ace07e86bd02a19f424ceadde5d4e9da5871acecbb7034c57807c0d3f501546b2cab10032a0b78a43a5c49094bd70cb437cf06aae693069b7e971695966c96ba88d637aa1380a05a5b6b17838d79aed8d451917b8ec39e14a33b4607e0ea05cacf2c1e37a9989d3b7333a791e637ac120067ec6c9b13f485728ec3db798c472d30c8847c7219b01df20a36a7a62174ce521f78f7f631f884d9de89cf367dd64233ce6ce529c8b3510e64e94130f75b7b3840eb74de02b19e8db036f4a35477601122f5b319a473b48c15aa9a17528ad70dcc0c6ae9f30d2a27dd07ba5d7f46fd2434d6dce48d6b9be99c27aa9241cf06a47a91c634a3e80eb1226f947c6263191953508eb81574088df331529c9adadb11000ef88df3afe3e1a94dadbdd9498f25c831a6856ba99d7f562e6418522629eb174a9a1763460c226953bcf3f2cb1d1eeb9901093eb562066ce2b53ebfa99190d40adde5e22d1258650a7017c4eadb306e3a3399161fdc26f324efbe1bdf79d31b670e3f4399052f2abc29e75e54817ef5721b8abaef8859f0b44466e461e2c0e782511b729b9586eae3639a8433666c03efb296d3ef358146bd8149df652684fc77f12ed17cdebaa3e54603cca58549839a18818552a261f910c65880cecc7b71e67bb030ea66de315db35e8ca9195221d5957601473ab3e8b265f33deb6e9830dc8e5b5318534ad86822dc889d07e120e4409dc71299d0563fc7f1e3e42d0bcb5e5546c060c624f25d794c4fc2b9c123eee53dd7433b950dabcdd5df4b6c09f9662636422ce099e1928fffb914ab9677e92a000aac69c8124f2da52ccc7047e8f4548733b7651089b41db72ef606b1d1800db6998e768a7b791f8167a9a8e8fa74a5f6306db79068e7c087b5445f99e4f4e4f2f7e7cb5e2063e040a3576b1fd54489a4d0b5cfdc4ae87529d9dee46272d0f333b890cda54d2445790939a53b4f09ef8f735e4ad5617f92efd672ad348ffbd6d0fb24ca12db8aa231364aa5443b628ae2a1ca25191a3b9dbaf3607151dcb82fbcf0343a004057a7fd92aa65821bff82f89224c1fa32a4b0f95f71003889d29f0294710927c552e268f24cb8d506f83f4b401493bcc8eefd2571b21304588fad0b3bffd452c1afa297241edd5f64a4aad6f063fa52e0debce8199b77b499feaed1b3341a275d7bf0c3c420744b7a731897c142626fd47f092b8dd3591cdadfd5786d19b2cc0d44ae2503e43c914a716d82429b90f1e62a7378a9ecbbcacd6428283961c862bf43337ae4aa88820f40c8d180407c9db4160ecc1f31b75c1a96b203fb12dd679b22d499f9e29972c41634b6c17984dffd55a357e01e0063eb4595539d486e47abcf623cd22e4cda816d972080dc7c49900acab496f4fb5899b1ce857a1e4e050e59c89aef8a1326f391cf875015f92321e786cface98366282b621a5468426fd3ef54a71f65ee42fbb8925743989b88ca3e1fff7c0a099f541dc08379245133a8613e7070b102caa175a1fe60d90d8629e601586b9db1927243a1560cc6169531d1bd2f57f10066ea998f57824bac8e957c8b40443a865b253a3078a3d6ba987b4e7a138b59a9bc5104749df348a9c82a79611b8bcd32ebeeee7dcc5afabcb59017e0ef3eee9833df563f64a2c5f755da6126d509cc7c2efab1cef5d317f5ba4b8d44428f2391ff1f8715a7f2ab1e80d549ba628429bf9229ea0a7311871ea4bff78bf9d9c5545fb0c13bfb437d4844091c048a7e1d9191b66a3c3519c93208ec73943ce6d9ebd6d0b9009a2115a8ae0700ab08a33dc598a7f4e11bdcf254643454863267d52ae8a34fc831d339821febd2befb7825d267b4f762270642e423aa818dc3a9d25dbde3e2391181e8e90090ea3532fefb077b1c5c71ff4d2d73793ef25bba11f5e429ea97c28a815ce4839476c2ae3e41e430c7b32e14a7739f95bc1c71704dd834a7eaf16cdf30508f0f35a4369170f2a8ea3ce2feb07f2c765781c17649f187b6c62b0ae4a000993b0b2769e60268a63e80708fee95924942214b300ea2c694156ba68774488246daee6e11e90eed74afc4cf7aec4f9ed635d852e012e4b383cbee19f9c0a37114b13a04b639e9e203bc8cb74bafc12d7303a6545d55597d083ce2beff0e5e31ca0c075dbd333aa7af34e99cf9023308e7a8b087e3ddfa0716dbf14121ce3d50ac786605ab44375fc7f6d9164c390fa531bd9989aefb38a81e8529226f2e94d1861905c264489f2191de08505b5c65e64fac823c126c03bbaf9f8f0c138251b082b5bc63789c4b83655993e60753a600b82a08a3d862777b492a2050007afd42cb3c06dfce9164caa3e7dc4f32963d41109ca612d864cc0368b62d161f4e61c33d9cece10eeaff277ac07e6982e4bcc634b51eca9318b0275f84eca0507f1cb3d158eab4d7e8bd4e9daa079010cbb254f0c4718431069c3c8b550f1f48132fa3c7cedc60676ce4eefcfdfde4cfaa462a16635aa1223cdb89a69c2cdb5b25c2811bea7794f5aa30cd64df324e4f2b82d6f455e3ee97415b3e798e8ba53282c520045417276b84fff56de4d964cb9577def437c009c4f1ac2009683b933dbc5b707f0f06f6cb08e1878244be4b4a78217c167da249f97501f010b665654b84dc83ee6635445da33169875044a963e19de81ead7325c997c98b518af8774e9e7782bf3346414e72a57cafdf40b12fed84cc1f80112dedd7a88d50f994c21f8fc15022e293bd141feb1cdda6f34f987bcace87047ddc29da7a7d3d51780f600308821368570d9a67b7fee9339bca8b9e5c7e6536023e61c791badb5ec75a32fa57134aad944508a3196d5d1681b86ae4845d94d491625acaa784131f673ac395c051f06a72180cf0f248e328ebfd48a635344790bf8b6ace9f74d6a80b28c00a8bf36bcdade6077f316f57e0556def217f5c7388b20854112da38e280221f61a06b1797038b28fce2950a12a5a5c2398c1d59f22763c9d9013ec9bd3958dc8614eb2dad0c98d4f491620918ec5d6297d6c14e5438a221d4c536b1e3684bdc8a4d200cfdb2ef15c4321fa4d70596d0b7415ac784a5bd998844e292303db2f0da28f139e1f79dc4972e35061a785c98a3e753057be3347593b484fbdc0eee36419bc02a0e125f830947ed869c1d6adbbc7f6d27737b05d4fa0d774092c09640600862138b7eadf16430932d85552e09073dd75c1bffae66cacbf410c8df76a9e70de128890eb8ae0978a786647991d78e042e3d844e144d26dcac8fd7a5c5408aee5f3f6540d44b14a1fd959baea319e875e10f4ab47f5b55b14b68fdcc90b654d6632c648ca67a4f4e30afbbdfa87b374171ddf2674bd17602d694c91a068282dbbd16f56f2ad22d69eb444d49d64e4d4043e5f9774b2d6463b1ea600f0162f20f723474ce358b213422077822e0a5451ce0eb70cd9b584e9a7251e589f90f2de25b233e8252f141a495339f0c15b8c3c8bf449733808b1eda375254d81f4a6ea24282e082e0faf0c2f53ee31dc7200e65a77a5a29256707a86c94413f4df519ecc2821927d559234fe7e4e8fddac39b97aa5b55d81a9f4f56f0254a5a829e211b861e853ce2fff882f76fff4f338692b2b62e41d6aab5acca525b83e3937e7efe472e286833ae910d58aded56ef57e3a3067f18ae7fa7653b93a52aa7ded330b43abd4c7a7fce499fc6efa4b3f51f418157552ea0403dc9247cb647dc8d62570dc732a69c0123125f62f5632b49bca1f51bf1f32d7c0aa6958cb016999ecac58a13071391bad3ea482715057fbaeea04c79f8fb33dc0584db96c5860054a4bbe129b92a4db227e678e197fe78629ab55931876c4079ebb9fa8b5346c2aff2008b85b6005314578e790cbd802a03a80e41511061d41de6e6d6fbb1b142ccc1880a8c948b4804716bf87498b48759f9780cc2a34d8393485b8fc3df1bbe3f2dc248c223dedd696203f4e863e14e4b458c8915fa9594d30308b001d569cecac5e884058d24874f15915f1acd6bcf0ebaabaca6a87071c56642923fe5aee1d93a3701dc8e093441c8d1c664a8e25f4d64328118119df354eaf19f4b7be3edc43068d1aa8f20c3bc2cfee7dae423b4bb29d3acc81ae732a9b5fa555b607febc60172abaf24f5ec3ff2d34f2f5fe893262b418878c76fa829acb3d6e8836148db96ff48f91e55cf7a67da226e2f8eaf1e750fb9b94e2cee55f5d58e26003d87b246ea94a9ba076fb9a33492ec5356ec350b0a3f81aad9036f24d777aa50717e1409d51a092fcddd1eadcc9a60a794739a20860a440564bbe1229cd645f939a8b5ef2393bd85f18c5868e22d0074e754b4178662c0172f7b8a36890133502f0350a6714fa799b195bd4753c6e7fc3febdb9fdbf451e79ce456772ffc12f2f6ec78f568287ab7c7206935af9d97f9677c54716fd7f19f617cb1ab5b3370cf4a827cb0d9668814253cb2ee0a7e3640d4c8c3a9aa7b067bb5f208539857a28c7e88a7ee0f1f593852f4edbca5c10ac64310aed3d02434dba785cf2f31b984dfeb28a9119c5f170484c17a6e8b1ada9cbfa830c5b6cef83f6a40835c6ea58195cac815f40941b02e01004349e72ae81bdc54aeb7d9130d0faf5af0585614f3f015307e3b37ceead2b995fe70ea9b874d4dde076de36e022e52f4076f9947168ad143b45d8c719198951c0100fa8ed39ef7993bdaa5b29da8d6a278cef6575c6e18492c3c81b8da2504c796390c357e8a7d4b921af4150f798756a351eb0d0b8e6cca953d59c955f0bccaf52b000000000000000001008651450b384a1be4f25b05d9b6ac552307a1498473cb4bf377bf17fe90463b3bc3cda080d9c9c4c1d8861a4aa776de4b3887ca857f97d0c151a88577e7d7c9330000 \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-4.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-4.txt index a850d0d4dba..91e06ec63bc 100644 --- a/zebra-test/src/vectors/orchard-zsa-workflow-block-4.txt +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-4.txt @@ -1 +1 @@ -040000004a54459c6c04efb7b4f1107ce4b1c4b12366914df3d3f1cfebfc3a2c53effae9dd8e57a96a960d651f3b25ab4ff91994e9d3ed895acd3507fbec3c2813d27bcf9fa3291d421289e47111138dfeb1dfa3ae3be9e40080f168a28a971090ecc61322254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025400ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000400000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000010291442b349e6b79567e68d960efe013e53d938872327facc9cef87c0929f820250fd884dcef65f5b634dbc0b12967bd3f496e64f6934de2fc77a7bdb75a10452d2b9856cf6181989f065acc166d50b6ec4c206c0d7b6b2e1e3e2f0128a2ad870e72a7f7516b6687e9dac5a0bab76b1254daddf0d11054d26435425d2524d37d0eae86b235728ba5becc70302e186a41b4feb78a6eda7893645812d3e21db5b99dbc544af3bfbbba53e6a7fda43f5dac3a3924c657f58d1d814869c7ff5707103846fa7374b76bba0215e6e572664471195583f37d932051f2e78612d7ce7abf348faed55bdb56eacfe82a0dc8e59668a0445eebdf8663dc98c137be0bfc2edb90464c50c202dc642684e20b87bdd4774d1b2fa10dc5003127b2be03b64fb8cf38f5c9a32f579c084061254e574075deb45f9098424aab3fdc520d2a4f2f5320fb6ec49cd0c31e12c2232447ef887532d2ca7f4e8e61779527d18f3543129e368da25c6a3c5273666d1fb6a313e32e755880e084b58dfb8dbcffb4f4a257c778c81d84fb9fec5e86609562089da4775d51cd3872e9708ba5568847cb9617eafdaa5427df4f8872d664f85561c3813b0cde75a163e9629bdc085d96f987898137ad1771861e39895f68bcb5a6b0afcbdcb4e6bdd62d92a1ba4a94ffde7b1e502a49fa1da5c32b24f048218281229042ec6021caaac28b974472a64510b730476785d9f48fdc93a919516205fd83a2e13bab64fa419ab47bd6f75987cc1b61bcd5be74835ecd1b36b1560f8409217e7677044692f3eaedd07761bf54b6623e738f993806d540bd0d4de7f6eeff0a045afdf07c7844029b1cd4e95f58b795177b7543a975c2a24fac20caf2ccfdef8236c922599669347a4f99029205b97de30048df26610f196b6f5684b97e2a0ae6f4e2619a62f64910cc0ba7cc11c8aee5c04dec78cbf3189b035ab4f80a98e04795237c62145b45369af435e6e7d97fe8fc47da039f1e716c3cb8bf363794e6454360f25b1656fe3959c916bc5124ac9118f063437f43d9f32b73c05267b44ccfef97b16e39728990b2bbb9acb826e0274f3e4e3040f5edd7da04d972bd9287356c6872eaa4f273f1d976a9e4135e484a79f54476383c4b4f060bdaaaf18051086017a75967f8f8889cf1b1d0a263da3c09df7aa4c72a9cc5c9397d141c39ff91d5dc2473b57a1a11ebc1802cb44d4001f0fd6e5ab3e4084a9e61d839aa78ee49c64bedc2cffe383a454711c51c1a078dd3f124d9c0e56e96410c4f6413185e123c6043c7a34120dcee0c448ff68378885ae716e759168a824666fa88c0dc584cc86f40698e1bb280c51b31c6db7df88df699acf1fdb78e78963d51f49790ff835afcf0a27615362a224504353fed8274394902d83886590fb635bd5d10f7fbd4b2f2b0305c280cd5cef0e168efa26ae124d83d96f11bfdd8f3e6d9c3cd9e9322a1dd850d0c39df9a7f7b1e4ae3d199df282fe65dabfa7f6ab21a14d790ba2ebb1041c3cb632974b79418ac0fa5dbe0ba053ef37cd2eb143d5871188624db529bb8af3daef4edd976825029b93ef6781458efdad8d06c28dcf3442a2c283d0b2341f700943574363c13ad939d35b3d2cb82f89d486bb4dc55c967c8db57764cc7a6607506fd946c5aed0fe32efffcc3da5a724aae9ab51d2c8984224eb657f69e6ea22e7a6d390284c37b4e451d7a82a060bf4e5d9570ee67c59173a60afb5cfd305a5ad51854a033596d3f7d03c85734084c3256d81a02c8e91f1421d19c4c4ddb5e1da98e8c511d6ae6dbc9483e2300f7c6ba6bfdb57e47f359c7f19de099c3bcba9705758039314521a6742656fae99f5284036dc7d7c9d4ef0aa0a33239ddd16ccb8c040896baa960ead31b33d5572030782e48c210558c621b14304078775366b031386bf14465c43723cf3ce5441b8fe20f7c1e31c3601b5ffafb2b1b698647078ccad70819b326935a44abb04ce4dd57f5ed1939dd6e833544fc9a51e786c065feb4ebefcc91d259935f5877bab8769115029d207fc9d4d3ef430c42acc010ea6e692c0b03055bff1f569bb5a65b08fd292ca216c24ab328f4664085084a17028fa3842d1167d8c0cc8459dde371d2a7bcc0b27a45a33455e1356ca2d0aec0f9f1c4c7a6630aa080c65c7bf55cd7955ee00992400b829a4a76b32f67d2926c7df31e2d4e16d4761322fbf21f91c7ff87cc160a065ca81870c1a3b9951d6b72f47f597a2775a246c05968ddb8289c7b6f081e0549890beaeab9329af73f8b9677aef8f6198397b7c73f88010a88ed55513d20d1915e70dd6b0547781ade569e3377542c17388040b1493e990324e02ce377ee78c8d05ffdeb69dc73f3b0810d1e2d294270c4a020749b4c11a3420a4941e10d36a160b996b307493108e84f99b3624d86feaf09b99c7c0adfd78aa6e89b6f7f1ea1860045513a0000000000fde01c4bcb7daf150f02871a1440addce9977b2c67e1e163b4ac60d9fd2ff15a385aa0a3133f7a70b3af4993bc1f4f9b75b9c1107fcb6be76d3b7516cff30094bd443c4527578836a9a5da05ebd5964406324e132b10ea5ce22c4fb3cf9ae146cfda1a80622a5c10ff924b252957ec24feb4d9e290bc9d55e66b8d50279cea005ea28440db88e6f6ecc5cc04bfa3a5ab055fc455012bafba9ee9570b7ba75454fa40bd97e6feab8a993d5d27b229fd4f5d43fe1a3a743d1e285ad77209935a2abe8f8d0ee4ea90161d78521b71f95e1ac80cf0722b6357a61d25b7282ddc45ff6f7d1b38604d14b7b4390c9a8478f18bc77ac3baaf7bfe744887a97952b27a270ee1bcf8efecfe111a64eb046b6ee69b8dac43da74216ad49ad0bc8adbfc1a6f19a00f247e878559a237450801b8c6c3b3f8c022ec5bdf6e9604bf14253df80b85401807d5e63c85950296c20ff0d268c90c8a08c01e52ebcfd314fc4fbe4fa20277293cd444b074aa1b4d4f52887b3b59c089fd61978358414a7fc38de8b1acb9cd1ba936e8314db17c757a1658d3024c729e7315e7d2a69a8208297651123f62890fb819e944fd808c77c72494bed55f29644f25eddc9ae2a7dc24fd4c3f3d4e512841d689b28dfd804d798d5ae8a0bc5fbac76668b6b9dc29b8f1f30f811c9c310208a910b65698fbc406892952e50b2c38a47f0861268ebf6cee0eb1b5831ca0a1d70d6ffb8aeedeb9373bd5f0974a3482d9f951bd2edb97d15e7d9cfac977373d7b0a674106cf8299c74a820d7dca51fbf72b73ba5612866f2a583fbb38b9bb1f889a569fd8be4afed5c17e74691debffab1ff098b2e39122afa8f7402228a32cd0112ccb046ba663fbf165758b033c7937de2f2e78b46147c0f6bc26a3763b2d6ceb2ea11be97efab1f473add301f2ec6ad50a0a3db25592a2ff5303e74b4ebd3c1205a2fbca5387649dc1c3eb041a83488220663897bce3a56f6248bc9f129bea884f8323aa30bf7d1fa4f3256124b481a26cdb237fd06f09967b4872bd77ba105c3d468780ec9939b13492fea1dd7c2e91b7f5d25c981cfa4fa65a81182c8178027ac33bb1e23d22d122294d6887e5bdde03171ba3734c6e557e61109927b7e35991fcdc9546627d92029051d1907166041e52811c03d8947882651a484f96a24ea1a45d8bd1945b626968d61862c3985cee51e16370bc80a6cf0354ad222a43cdaf2cebb5da011f7124ecbf2ac4f34723d8efb06d9f7d0b7c81606d35c209724ca1be74369cbb84f84d6e41887002ffd11beb49febc8372549d8b823803362afc9dd42523f90a32802eb6d8dda8935b07f5a55865aded7cfd8108e4850c9ac404f48e9de0243a8d342b38cb028ba054d341f1aa3d9a0c4c68d6d2da57c09bd0f05b50223d23a7d0516f68342c6cdea04fe2e0eaddf543c2f567347b7169abc7a51826ec7c54a47a98b72884cce01acccf9a758508489b5f22f06caba8fe870b237a10297b6b25cedb1f619ad8da1345d2525df7c0d743dedbca9d6601f08c44aebe05097c3427956216df4b3a5cae230014407f626b22967c71ab6f780d2f9d3bf0e9f048741bdd302cd4d51e1b5c5cdba168251579bb98d68191100b74390ef2fe3a5a98c3c18433697d98cc332ab83a6b00e35c3259239b2256df9bda9b61fbbe781069500d3fe8a2271df7e75629ab4e5924bf6b37397e0036eb523db5d403fea1a8e8f35abc2cdfd32032832de5eb14685d6418ca2067fbcc3bb9eb9c2a4c89cd3a52339ca0ed413bc103a4f29947200f1d8588988afb02fed593ccbb4eb2f9fb004b16b163f23cdccf0cf6ff773785f103a3e1dc68fabfc043066daa7bc0f7d74bddf2a0b5c8a0b6c8d23c112344b29b2402b9b51c12a08839dc60a031d64641c62786d92adaa09af2d470af6f0a5ce7ec4a6d3505b7b6f7757426a672024c0ff9f25928da34ff599cfda96c2ac2cc180fe1f7e4605c6a379baf3e83b8aad07c351db105d4d799433aeab2d891ba2be9fc89586d0d72809b7c740c8e8146a317924a60578788eec8848b2953d03210c8b74261a68d961908e3df871e96b5b9fbc63a619d90dd8513819ba83e243d997303694f2dfa8afd41350a4213f9675e7b298ce7b8dff844acc42bb42352c5066cd658c938c23f2fa9e37f4fa4a6c45030b37799e8f8992016496e7b229d5630110858fe94fb95b5d67fafbd2df1a6115ee974b79347797cffe610386a76e8097717bbde0fe924483a4ee28b8b69c7512dd5350d7776ea40de18e91a163b33cb7714d241f5611b5048637907b34ec928206ffeefc1afd997e91178acaec557bc654f6fb03334de8df05f2751a2f3382c690a16977ea8ccad2d1683386ab5ac944d31f75250d65fd22e6e8baf1bdd633ef08b4d9640e637a57e116c4c1026eaafb4809789627d547774692db01ba0f50b899a37ece47fe27393e9a48fcb800c3c5bce08ea4fb9afa524ad534c3a4312f824f63a01f627419e04f2d032273e257216869ffc3515223afd0f2cb5207fa18502de12c3b00ee0aefdbfbb69046645f96ef6610d3ca8d459f9234a550e5bfd404967d60790f52df24a73ca2d2d85b4e2a05d616c1561df3c2e6c740c360429e7182cb11507617ffc31597bb93cf30a84e5dedabf138c399963416073323888056848d705f0e5e0fed30fb44e4af14b7a60824adfb034c0a19f1283951fd38bb8be8b1d6f199dc5ac6c6de2d924322c62bad1c137956586819c283fb63575ad35ead5a8270c98f75cb4a8b71b734d06d01131901302a03105aff7dabb227cdf4e2293e9e9719a132ec26937345d14ed22b6005cc7fe0ec44974ffccac3dc9a7eab539d51ab158487298ed471c9a9e562bfcc27a5105158fc96e0c645c22c1365fc3de818f6b4f1c9b26e708d5c00c3d5e7cc8b5f6910ca76227190d0710123e9525e156aec144e19bb41473759d479b53bcd0db6b491467304fa5685f27dc528de61cb036c1399fe5523cb45665d6cb8e88a8d13dec00ff35aaaa54f20a40f1b0761be10826ef74476cf20418df6d543fb2a765dabd1bf7698221c9e02518a8671d494c4377b2a46bfc55520ccb309ba327674fb29850905346b0ff6e1970418349756726d8252157e1f788b906db305796d9afdd97db936397f85b5004b7c75fb05be8a49753f706598a13b7b390e0dca3c57a889bf279f7d856922c2246ff215fec5281b29dd6c54b1181f59270ee5ac6cdf81791c537a4a933f09b04234c4f1184874d95c1f6dd03e258097f9d0612a9734e9df83679ceb5d0321d10df92aa1fda15ec4c6c6fe21d8b23c39f635a995065237f23c400124731bf011226a0c71c8fcfc2569b6f610cae9f58c4ff8088224dc20cf282ffba85fe0ccd2aed8faef245426415343a5d7705f75fbdafd2224e0c9c77dce9b34b959b770d03d2274a5392dc4fc1f1f0b867403f407fe4ba71b4344839ac96ab96324c7ada1ec4a16cc5ed1622ec860768e2920b1e03e447632b5243179c2c51b770f6c3b41813d78a370894808065cfc42f80d162c6c7de9a8c31e2e2404a76b53d885b6f262563075722c048dfe1adca75d27a62a4fbe856a77b359e599e66317c6faf180402166960fbdcf0db63291b562a2c31eba97fc804c54d1ad6b726a6c0be65ec2deb21f7a8dff38f043b91bfeea9757cc889cfce962b85f8043820144475e92031b27364231797c0463a4887264b3dbb92401a6d0cf2310ba414c4791089d2d63a55fb9db709ada3194cf081568771670d762e30abca7aa1d6219ad240813087030f03f231388e49f35b12e818e285c9b5ce3a5a2d9a9a84c608f1add6799ed2219be66f9f5b263f0176a09323479110b5ab4e12119e08d212106eae1209d7a128208fd61088e7608af5502ed23d958058bd3e07d201b44109a4f858af1e726e2d30f1fca652e7c44dc8696bf067f742fbb1cd27aa72ca1d82a81706a196a9973c7618aecd0fe9d94094f8d9cf5bde84f55b267de8a1dc821b0ca9ba9b88694333ca08060fd7ccdba4618134b300b35e5c7570e11034d4cb6394b629f8ad261207a7d00bf100895cc20962b1e1af9e2ce01e514c64d71d88ac63722a89031c4e168772ca4374a0f4a4b7e59af46a5a55b52ce02f40de9b9b283fff8feb13afa8210f8566e8009f40158a94452dad543c10ea8e00ab6d1aeb1a36fac551c97fbf23946f18b6b1c8e0fd0e626ab0aa37ab08c409696822de6eda7b9f59366560a3167e2ae0e3692ae1dcfa04a096a9b775202fae03924efbcf37c2b5e29d9059970afa7eddec2cbf6b638f001bd5aa7f41f8eeebbcc29036b1441eb5a6f02eda3d17700b4892d08e4fcafba743bdb8589b4b9d4dcadaa70cbe1651a57102cf25283a9c0bad063240a0c0e7d0f7c4da6a0e2edaa5c4213e8ad21f88e7065b9f0d943950a1ec66acbcd43a3e0ffe21650a81b38b91aa364b5e5f26526e0559c90e8437e344731b6882a71bfe6e43ef082a4cbbb1bd8ac765fe10c59a2837fc976573120915c627cdbc32533af915d25c52015525e3db94c9fcb4c01b3734de39b9fe34734095e22e9d9c514ef04198e3f537237ad54c49e47b196fbe76d91054297616bc11e60c83e91eab4e59beb9a3cf507a84f74270cccee8766c238d9325adcb3fb8a3b36dcaceffd56f933ba1cf440968044933bce5c7871d7475f2708682a238b4e8c42ab48a784ff0ade9bc73b7708eaccf2fa46acab29d4cf1dd7298d28d0ec01bff89051f43a71497cfd124becae848aa0fa5cfb126ecdfbcb9ab34e31a2ca5e47ca03eb8bc56a00dac4f27857f810b4e7bd72afe2fe50b8d996e03bda33b89652c622025c397d65239625d1a73fb7db31c920e3cb943f4c15321ae815d39eb42fe3bfdb9dbf77ee2e4baaa3a3e73dfa04ff5fad7eeaaa547ad988e143d01285ce32a15b6a99311169b17a74dcb8f1434179776743846d27e579d0f4a7e29d515231234045610dc5f98bb35a8e512a998a7521a796975cd09799289563121a43b2755ba4ed4d045aaab3e2c0a2db2ba88d453240694b9ee3bae3c840e552d59ab55f873fdd5fbf272a7088a8ca06a7e865b959ece5ec88016a327a8dacb198fcdacf258d4503a8f5d1f3dbd84aeb0e1783b9153e48e68d024b4a752d6003f9b6f5d19fb1abca75b47628674138c25e0864ec1e84b3440d6cb5227af39bc30ea5c704e8420cf303ee0473e03a570be5c0e784ea2af54881ae6a08f08914a30c5d465e587ce7609bd3020c0fc1393385c072146753f7c2067ba3453fd2bf51c1df88d0bb7c83764bec6f56a5a7c7bbdf3ff7317f0c91372ad66099443e3c204921a4bd7afa91d95ab22beb7c0a5fa952db549fa86c615b6fa612918e97cad2c2398d6074b58358f5c23c51b2b0697e5fa7a001bf2dab4cc9f4fc3bb8e56102773e270180c3e8f85acc10f38e2367238958503bf9aa5b8e1a76dac934203dc250155f53b1567c62e078e4e43ef1a0b3c730e1782049e21783e65ca1b755e0d24526b5300d5839b2e16b501a5a6113aea6969073f3d1dca2eeb0df5dd4b10802613f65c54d3eef4d5b9e72befb7769247f1446a70f87a09f3db4bf29a7d0200306f532c6ff6e16f67b58d150fd9be41a0da78fc947631f2c9b5f929a0f2ddcc237ec0969fc042a3728c75d0ea25794914f9eaf67710daf3480f5b6465e610fa19cd4cc1a28ddb82f5add77d629f9f87e626d71e10ecb5a5ba53743cde4c5dae048e6997695cc873993d01e5b2903ada88a1b9056ffce6c823106726e34a87a60ac8711e2d3be3ca7a4d9c9fc501d74baff8fe080ff845151dedd4bc54af0c03334db452b4f2af145d117c2446ace0b6b3daa4786471a04b64b157fc37720b13210f791ccadb500c0171ddcf8573808140709518bb3f120c031b4c4ff4369d183cf3d122895d74db417a45104b70091d8cd81e7e2e28c7c558dc7f0e6ef7f6411402c8086e42a71987159fcd92be6c33120dd8400e18ebf92f5e288e28d7678d0764569ff556ea282a27b9821cc0d0ba5a532904c219a6ec8eba337f1fc32d903ff03c83f475a0d83cfbb3cf0bfe39e40799ef162db2df9d6d9dffcc99a80ce300fd9248e40b669b8333271db8e097584b0029ae0b1b2f26dd1ae751f63f4bc6272e30850fab392137939d8ea3b5c7a26b38b22c8c6dd8bc29a3374844386dc0393c3b06ece37b1631447b8eda30f6e81d44f5ea9fab8c6e6eeea8c72ad4543d16fdd5df812a38fb81cbc0de55323d583c5ed6f6b86a299a6b3e56b779bd1534205771a136156565dc19fdbae6c588fea58c9d4eefc02a77b8fcc4e025abb2cb1f789d155ce52dcc8b61badfafc67cb95a043aea83220f26683378b3e4c7dc673008e53002f7aaba82e80e2692ca897c845c080c3d2a37e6b81f5af6f2cda03e330eb8fb7a35b96cf72207d1fdb194e71adc4e0668286d56a3bccfdeed4da2ab35a73de2e4857ec8a0e43ee3e6bce31f7b7721b572590059a61e9d6982292d9b0b43e8c71368a1e24577ee6c4400d95c777905f261b07e25f255ee5a64c997612ab0547dabaab96d7e775c51f816eec49a4992e2acef7dadcb43b97a28da590420b0fe94fc0cd7c846b91756332a5e65c403dcda14af976dda0ee1b4c121e7f8220a26f01fea6f57146b544a618f772103486d32b9573c6245947207ad62450829f747326096839dc602cbd110a27fee9a6da09debc48349d2bf01bc7e1c1c3127746cd2cae30bbaa4573cc5fe0758971e57a04bfb88d5fcd0769b4ed2c2f8053c86a53f64b3ba0f2f42e3ce632fed09431feec605b9a7425ac05d20753bc56d158d8a07f7d7d9bfffed6e4cd2041fb8366f1a391cb0e81962a20851ed5a58b017ac870e8a52bc16491626f1ce2aa65c4e09512a963ba24b09bc962029c90b8522ab473369d1cb64373c56fdbf775146e683c26bca67abdcd13f6205e9cb7f811635a60393f8fe6dc8103a59fe6127f259b863e93c2d94df46f975f174854f83149f3b9af249f0bd0c0e59d4f266cb3551e7b95ec00f2455c30b59907f321d043b8de304ad33566e4f58f1df8e3b69596168c86a0631a187a7b8a9ffab34ad912310c82c3ffd1d771f9210bad56f31e5ff5da97d780ba58a105d1cb7066f22c10cd428c56d0b74c00f8b3eaaf25e95d809b6153db31bba4d3d2ce550defaf8b804611376bdfd31458de4ffd368fa27d915a47cfa3a8a19a8ac7146b070979bff2e72e8971bde1c77c4773397f8c537de7afd1c2f2ee7ec03981a62d6cef7c0113346906cfd4d2596dd6d6246ace3a79f190b88bb3245f88243cdc52f1c72431e16600c6ee1bb17334dfe91e1285bd11ec49ed70d0f42cf49ce2c6919788647970815a07e5334622a4543dc621b0c1f3975394e23928bf03045d48369c1a44a7720e66c1a877caca8d9ea59cb56023b580ff10afc968a25fc68511ab7ca61cda50ca9f083fc34141ce80285934322274671e174bd49e0da3c32a7f0a2656f742609d269f2154cf939bf3553a58e26673c91105f86a4dcdb293b79d43cdbd53fd7334b4b2212bc20c48d1295ac5b8daaa5b611d71e04afdc7ec56f7e71efb6d64d091f720543afc6b40879c0e20c4f6c73d30a9c46dba343c1b0160b91930e7e3a20066420a1a1e9901e47b3a0039235b8917fa853d000ef72bb8e3507d2121894387cb32ab6d397beadfe6d1205b56c4bb23fdf4b1fcbcee78db47eaebed40bc9143337e09c054ece2940feddcdc31ea78976a0f24e2d8b80eeac6529e2c9db26379936a1cec3e52c5eddc79a4ea55128ee826a03769a3e2141d9caa0cc2935201ea5c39f53118a3bcc5063a8726f742490a83b9326a8a0cbe4475a3040d9e238134ac709354b68c60f46323c8fb5e5f9deedd1935d9bdf3d64f23c85ef8d26eb26b62a43ca3e46b224cc78d2a708ff9ffaef1fa877bfc45bc724ad7d617ec9bc27b2736a96652d7e7e9a407358b18d7bbad9a5812b2883f27b9b166eb880aefd3764b4bc4246861f7229ad3ac2c1d3466baf8f33ed425e2a68ea6d3a5ecf641d2c1293beade93ed6cbde2e0cecdf53ca172685f9a67b2076c468af815cb35d8513691f01bc49c4c106dc491b7c3ec5954cccd346ae13ed686f2f6eb9c50d25861552329c3131116753cac647a9293b8cce67ba5b4b657e941f023dd86d4c7a1b074ea34f580b17fff6ee83a2c4621a425a588c5b7e187f0de39266b0b4e46ef10dc50c4c031ce7d9ba98444f70fed504581d36e197a4dffd9d4a7387d88bc3f72ca9d80fb2ae75dddc5bf88feca1d95668a7902fceeb8890a53252d094274b0b1116b674d333c5ffcc4316fe786df7090c958f01192762258fc71003e3c99511395f08263517e3146de82d9acf056d991e6b33c76140a87c1444ced342469c8b3861f77db08e985a12517e632a35a1ce1ea1cdeb62f15976a13d336f677b81d03d4f8666f0e812c5ef819cbec653b5805f0dc721ee5845717a8c680fc4f3d19c2588cf1331d42dbdff7cee8f4b8dd3bc08b64e84b512f81f9d6871313c4bcaae1b9bbddc88199b779636aa7e956146132b297b9e43890912467a68f05a3a95d01012ae70fa4bb55456269d04ca6f71c0311c51ae12d48a2533e70dae1e6edb530b0c163bfeac8ebd0cc05894c0cdad9b3e117dce724522121e9c3ae9f22aac1305c4784e142bdcec12308b6abc8cb9935f9269aa43d6deaf8e337a92579f9abb04c3d9414df0a487d52dc1b11dbd62903c2f6b0e8a3e95181e8d0bd1261422de00a2701c760a7b8bfa21fd32a82e2f1e06fc3a873eb888ccbfe7cc81fa4d0a542a4d9cc64514239efd46bf2aeaff097e8475721cc54e881266f8813a0bfcea9318ba2209a3494b2fa0e4829569bcc39d4f9bf43a231813fecc45fe63a4093a4d366c30db9aa9a979958a0c13e538e3d03197efea06d127136f22f4d0b291e0493d2ceb40c2a6af016fc3f1715a46a8dcce96a2ff24c97a7e1f16f7e335a1cb000651c9196930ea1355e3b0293d9d4efc93cb352e0264f2ebdad7aafac968378433ac8735bcb0e0c278e431e285bc6069771088d0e6c3d295b022ab78e7cfa5891562bebe78c10feb0efc71447d4d8b2f75b0bb7d7ca3b95551caaa04037b393c17a8a5d9ba9326a450cc31a84ec0a902bca5b41e9a6ce326090a4195ed1947772b6e51e804378f2030a505965f15e7d207cf3b4d4d68a57f6381f024871615ec2e8ac921d38284bdc447219c406dd1f84211f93dc9585ada378fc6b704ec398c02d9926392d83a209314304e09a135fbce2a4cbc0b9d6a688ef9d3b81849d94e0a93e8cddf964a10d8b0f6ae66dbc1b6fda1f98373529d407239464f2e5a8e873df968324696e7c28de4cb7e639fb337f4395578818301ea9d26c8fc4f5b975617c00ddfa88d764a9ea3450e028f2b84abdc5aca12f9bc7cce6aef97a94884792865b889f247f0a445969b88a0868738803cc542f3e3e4a891742533ede58bd69d6ebb98b7aaee00dd8e14e7a48c13a72c1e6004d03e4c93272e9e1341bc4c772f875ea6e4c182999d857242fd98d59a2239854bc84fae95550f938cc76475efa6fa2af8f7fc091c9d3a72691f5fb6311d4e6f39b13fd81f9f1c1dacabc365c41cb420bd15a70ccf4776c80efd0a55a98287f1b5733797d832f4fb6a24b461f2aa7c8390729a7fed77579a11af4d58037ce7201c5b4db134e3c3ee6c2849563b13c23e0289f027149a35ed7034ae5e9561b37bcba6621f29841231dbae1b5e7932b077690d2f605ceec2022265d9b15b8f6dfba97305740e13ac305a8c1d58439c0a45fdd2dd5b0849a8b7c83c97fe8b9d9eb4c98246a71af5be66e5bcd0d505095569a90e7f4c355e4e7e93bb15f752ee7b314d8f37d5645944d8a1a10f752d00463c726e4d177a46e02504d5e45fc9e4570aea238597bc545ef66b710e5f58290e1d09688113c3fb65fe73681bd37443449396ac4a67aea37e63b98e0c6dae0abe276214e8f291676c73146b2c5a7e0a77698790592db2afe7bce921c5ddc11e19f69535e4d185ca77c76bd309910458fd0a2c49ffd10514ae503073fd853ebd9bf03314aaa5dbb4f0a46e5965ec63de4a0f4498568a1e54c97eaa2b73e0f91df8c88419ee95d9cabe1a10f30dfafa3f11c8b9e42b9abab126e4e124be43bf1564d6467793229c17b03653f78ee20e77202350a88bda0f8d6d715f5daea3e691c15a2d1b72cc2cc4c62ac46808a89a8c1bd0af6a48cc6ae0deb11699d24d51b9185617bc415b3d47894621d69c6da4182033fd739297132752165de37954748153df8f38707c7e75de332464206ef1614b60786afead7c245b077bc1d6150e040c288dc1be59407d19c74d849ebbb141c769a915f421f2162e2551f20138d835ab3ab1b69022a95a7d69e415e961d49e7dd298d7cce37ec873946b01778a30250100e63eecdf55b2f8ac28cdc425fdb5aefd15d8f8098e96b4a9bbd407d801c3179838b54a096947557e834631d3ffdb453f091738cf8e155729ecbef8cad7159e3c0100ecd6dae25fae63e719880fbf59d76c207ce2e370fa947385be2a6508c4b2ee080029de141487b5686c8a25f782db94ea11970dd24ddd83e801f097bc331984050000000000000000010012aed79ad19b14b505d2e76d87d570dbd40a1eeec8f5423cf9609ac38a50763b3600f3513cbf9b9a25adb7810e2483165d18f8232a2cef74235e29278c155738210080a0835b85a29f68b95d0e0339bd20ca08b2280240bc0741c33918a6a25c138601ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd00010100410018495934af52154aed322e093e8548927d969ea0b95beb91c45e031ceadc32ec5df0395df5a202b88f6d6239ef11bd29e82c53ea3f6d52b4cb972845ba874db3 \ No newline at end of file +04000000b62a58bd0a9c8b7a85d1c9668469a821209fceac32b88294b34a229ea4c9618a642c58f092852382aea7864344d14577b5d2fbf6a33b3757e70d46ac11e2e6400ab09ef702ccc930a93b15cdba4535d50bafbad953feffa6106599bd7abb333722254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025400ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000400000000000000000000000000000600008077777777d80a1977000000001c1d1c000000000000000000000000000102a61ab77f99d15677db6daeee41bce55515f2b94494edcfbb2c4b245cebd29d81468d2a08ebeb6e2174b0cac2c5146daa73af7939f80a182b9f64fedc4332ed251a476e888fb7112dcd77415de117ed4e84d195e9140b4810eea253f74943550721ac7b54eda526e5bc8e5d9cfdb128129321e1b0f27a9968f8652bb8d3ab0f2e7782aafaabfe4ca1b9206270f571790f1e5ddeb686b24afcdc32c59ac16a029ffe816cc0aa56d7954acc308577706d13aa1fcc93d6863b48daefb3d853bfb3dd8cda43f36c07d64647539f830bf5af5dbffcf4111f1bbf6e41275b9c246482f8fd5adc40ebaf07e8a397b5f5443561361d3550f5753b23994225d1cc41be22397eb3c4b1b4bb7f47c045c21b466eadbdadf96e832c1f7218ab3caeb78ce0165b5b4a9b5bcb77c2db63dc697299d906613aa9208336c08aaa99546aeb9f024b7b182f5473b3c8e2d19812fe359b5a25c52175d3c40219e3769dd032af9d3fed29110784c0921edc1715ef70698b37217e07f1aeb47b44c2e6ffd2fee66cfdbb8780a355fc3884f05e6465a5eedc3c9ac8ccbbaf55d947a8e781a40ea865ccb88272662a46c66faa0450d2e2b92197a655c8a142e8023fcc46c477f93bb9e6dbe635c0044fbdbb7013430c1db33330c57829d694dac21addd26dced5a42193cb8b3f1cea152b262fc7ddd973df487d36b8722e2d2d9fac1677b598253dce0648e948677b818b71d9d8eea4bc7c00c5b4c9a0c3641e58e1215573c9b885185bb698bb892eeab0dd77f43e4d9f7eeefe7c23f308c0453c015bec793dacc16f82444ee4782074bba0998de85f48204fd7cec03e19a474b316c0df4c4f360cf894722283a0a1e2d9bf5c3b3f9dbc8bf57e0d5b1ec70d414e01ae59ee8a26cce1e0f2a230b8a5495e9f5234831df9796e88c19b2865a55fe14e761bfe46eac9a86fa34aa6dc918ffb011cd3f79370eb2b80a53d0e20e29c6e688d2aed78ac60441a7dc8deaf313e6343ccdf16bb920a0d403e1e3bd7231d53217680e730308d7857d31a28b940381eadcfcf6f45f9dc3a312680d54ae75991a678a3e683513ee7b100b0b5cf6353876c60ba2feb4089812d837655c9e85ec75dea73f8a27c82ecfc74a91249149d81013e6c37bd1d23665bb2f95206f3d2c2ec907a2ccc292e0128577a83b80cb30b4911953dc3b8b37e2d5880965c400314ecfe07497bcd910968fb13d3bf80ad52d26e30155ff51878e86916e74e1d3fe26f5f97e8ab22de3ea067f48c20852ba592d297ce13a6a9fa480eb1c4408a0d747015990b036c9438eed6c462484dc0f630c0a9d2558f87dc83b8b58a1a6e0bf4fd59d49717f5337191d0554cd1cefe74d2f73cb8f401b10c5c90ee1f55b83c2fd8ec62738f3e0662bd96479073e4939f1f6cabb21b927836bdffadd0e3b6186caf59e63903d500c5525517e419b4d2d9c7f493d5cf36fd7115d1fef916022111a31db97863077bafb9fe9a0d17175eae1281942c66ffea2c8193319cc32af840e1ad5d2a7999e10ab90f723654760a1e448e9ec1a66e2d039be4081dbe838ba28f0c96340364372f019ddda85290a15c07a72605f85ee2a4b17756a306e60b986169b780acee465839030910811ba00af00b9c408949c7e6d02f342e2db6609bf17c7ba1686b905542121f01412fcd4e40fc7f38d747180c7c197d2c7128612701511262fb6509f4c9b3119abcc57aa6bbf1752b9d6cfcbd16b7a959bb9e1d3c39e32e1b3603cd1d97b8ec564c49fb20f5ee40cdaaefba448f04a577f3d91665db4efc8f51bed235df7f29b1a9fc6e8bfb02ede69eb626ea6ac5eca63a5dc1b07f32acfde517045921718c0e3bb8539dc6557dc3794e0367e5bd47bfd213fef9570818931588a5bb6f1545d79d3bbe36de936e33b6a3c5c31a18e1fb7cb25ec38434f165640be6b20f5ea300114420449e886d8cefeb47d4688fda50a3d5d05aa0ab039be78ae889509c8c885e4be14b756740f7025733e8e42ca77c4bd6dc02cdf33085e83c631f1867d0f5afcd1dbb735ff59737ae2f3e309e86f3e30e629a2a9194f5bcf0e200676752f6821283401ce3a108ba8785d651a4345dd030ef3216135373a16e8c4942167618b68c2ab9036ebb6378df975f00b75e2f907bc343741044ac4b7332677a6bd0b915cebaccb8da0387dcade509be63bdb4b126b4e74d327ee3944bba493f9a607837e7922cbadb8d39a8510e6a806fbc1b648a474dea3b417178faa1274bfd96e0e8ee841cf8ae47a34fe7b0d300e6326e85752f7b04427450d8110226b55695c0795ea866bff9520b84e199bcd58b0628558dc102f3cb17eac9d28b7cc70c651be173d4a12707e15a9e81d1eb5f0255322ea5e1e84ce1e004396c04949873362beaade613ff6cee0779ee33ac03ed7c41cbbcb4346a933b2bd706de42f40832c1af9e5afcc313c7310000000000fde01ceb14769d26edc3e1ec0b6c3cf4f012e5f3bb0699eeda7098e32772a2e7a5789f42bde0a9a3d22ee79679c83b7929ee7d85a9ef790df92ed99f1568f234bf6327a7e6b0b9a9f83864cffedabd0f4b7d05cf38d308466d3c8f615f6dfdb54686988ced6e545fb3dc0e3b667c1b86b529af140add483c97f03e79781b36fdc9c7348b8f6bb5c367ca37d98d9494568a5ba2efe112bbe0a4d8f2985f0685b760e98d4b1b6a310ac4df2f7c35c2c090b340e9570f246844dd1f08408dbf33dc30fc1e5324d0136755e1c972490710fe88e921b82cbcf1ce77f1381198ee399ee8cd36b71e3f76ab846147d753472420b3f95356814b35d3c4a09ddb88017553a6b88eefc75edea98bb62f0ca6c0d96b294cb39c26fdfdc53dcd846923ed6fa4cba81d4c7284325d4cabc834ab07ec5c6315841c1b37971a078785bef4f1b9d4c784824e15580ff4d22ccabe4228308a6e1c8997600573b157a762440c003f7675f9ba84cd61ca6e4cc963fe167930d6dbe248be0b60d702f38298ad32292ca65c0b8c722a11884a85971e85ae6c7ceae43c58a6ca60e73b226375379da8b21edd8f9c62dab681f448f00b805c562f864a9d552e8920d436eb90b03d511df86d2b9007ff38b9bbf2185de7e2f83d40b272c57074cdeb0a4a136b62ae6085c4eea6529ebcc2668df2c833515cf8a3c9f0771777d42d08edd47494d828ccefc8a8218f8be6ca78a7e715940e9ec0b6f4bc61b2273c3f9af3e7ccff5143c502530de51481665313011067846e1db2af82d4968d68d6fcbf6e0d4eabdc26d5afe3914fb60ac2915eb768c03b132977e09f39e1a6762b697c2cf3565248e5792b142ab2b8081b252156e8e7fd697faa78812e06b0cf702122d342918c42de5950c0c655cab82c6e778feaca8fefb135e598016698e42cfdc2cd17b3645c8f9c06cb1178b6282683874f9397cda1a0c4f82342afea89f968acc92273ecfd3abd3060a6ec7c185ff5e595fa77db09731ee7ae847dccdfc730b01f3bd08d2335dd46447e5251a49513eb733557e9598ca8b9194464f98a84dddefd38c8d95fae4c6546e2d60a0a56c5094d45f534bd54e3638586438ed21843edda4ba96652bc0fb6142900b2bac653a1a8e5b47cbbe3c6236123cab2f081cf8869f0c7f6b95d1317c71695dfa66ef1a8f73508c489bf895c77681a67cbc0336289b8d321158aca0ee71c5fd833abda69b736cbe4d3925409b00c1f7aa1c2436b83f0a2eb7f4617f7cbad46f9399afcc67d9cd0a633ee7d8640f60d5cfbcf5a59038f6c35c5b78be9c3b9a20005f22dd003ec32dc887a6f05e9e63c2f2bd55e7e1dc6a16158d18303cc825e0ca3ea9c14b00ce65d179ffa91e9823333b286436c19fe1f6b7fd4e5745fb57dcf304a8181ed986134f8d958c4aeb9aaea0f3c5839f2c2ccc40702e4f2df5d1b373c12fd900e3061d37ab827bb45d8cec4c318e0bf9bb8b1efc28e55c2cf5143a104c2d2f67fe14c7b084662e87ed070b687fafb3c338beb0bdaf156689e97623b388006dbb7001d81c1336b931ca5ee62a217680dc3c5e6a3867227aa3f4b3c0688254a21da00ed13187e94bc3ff8dddaecd22a6f69dfba6faf5f4f1420d7570b01648a634ebb58253d5fbcd5087e9d6c40b58c9888d24cde24c53aa03ac95fd00ac0f3883595259a08e377805185d88251d02cdb1e07d8317dc3c49d5ac7d51f251ffdad6a5924712078df4f978fa698678d5f5cc98aacfc4a1515fcfcc9894c1b93b408c32f570f6d25440d9f54dfd10b04ec332659cc295963be57d966204c825e5ac79c49a07e3fd25eac4420d826e4268c8194458ffb42930fb1fe86504081c609d6dbfddd60476a395aafdf29961d710026cb301045b5afb969eef89eb68da988649589966c5609fa88912dd6922f57a9c4b5585979c635a94a227a9a679c1c12f4c06de51e3cc5d799769917d735755eb6b83c1e62effd850dfc4e97deb5af370caccd79c130aba98b5bfc18d641924def73a094848bc9871eb2c68b56a71de350dd0b1ca1326bf4ea898117a9fd1cba7ff487dccfb1cbaa1dd5260f19988dd5ad54daee2747ecbe3d20c38254ff6114972e80343f17f85dbe5f8121a7a746ef0dc5f4eadfc2516d15103f099c212860163554c4946f05dba082b1a8683bfa8b681fbd07d725ab36b41596d650c89c5879ccf3bfcba4c2dde9151e4c8508bac86b19d6da570d114650a659ef72c8c2a114ce78416b7c92ae8cc04421dc0d38bda88d702942fde9e0d887e30a68d928df94b49839f0cf965376adf852c922df049dd57e6d9426f76d173de491c956930b378544fbce65263323d2a05bad1f5ae4ff2fa4a2c48913647c37bf1f924327a3901736687e3e1c91706b1a9ae8b44178853071df81287c4b35df69348a84d7c7b678dfc7f0285d45b5ec38cc4b06dbfbb8eb1f58a5e032ad73a19c491eb0da69c7c2eefaf5745b1c29c172df460b63ea5ffbed0f5b523f60133b67f9541aea2495222d9d0d85fe1df8e109d976132afacbb839b05177f205c45e66d6601b3af686626f2dc6ad85780470db507617e70a00cfa20096fb48cebedd9f0fced03bb05497d6b5a79e133063e56134b810de9272d45068f155004a2a874cd501d711fd514c3777402de53671e88c08601930ed9e6d290ce94f7ed773bec230a56762dde7ae45663444ee9cfbb8e520fb2e952136ce8aee8c67bf67aa4263c9c9ed27f371e3fad47f54c0fb6bee650ee51c27018eadf25d872b2e778530da23f8902ea1fb8f987ec5a9b8fc5e02d1de942b3160fe1c328d8238160bc72f198b7b755630bde4c8c6e0bafc09878006966712af6eb173accde952176f2ada6e73a57e258a67b63334bf671165891cfbffcf02b72468e3f41306eb6f614ea1574c22552243ac7fbce43fee330539ddd9ad050a65d418391d5dfac66b88af326e9afbed52da3285a1ff37042297fdbf28c234375aae863308d16c18f32c35981bdc25527581e72c950c8b102486ad2443653513babd3304c97af50e0e7c47c3bc4913e3105fadc330955f2aa8457c500352531bf75a81b5c36c3537957c9310129657eeb9764331d8916fb8c4b93d603f2f451df90fe4b18fcff5937f0763f9f9d9e43aa9a48955ca43d721dd958f251ce3c12a65dbed0d6310a2420ee51c1d66c370feb868fb47852107c9c3c2e4a150bf90074938b41ac2cc344302cea6dc108d657c2664b6ac3554ffe290f63f41b88ba62799922c8bd5713c96645d6af1a02ebd99483d4bbb22c349f00096949b8fa72f008a68aebdfef62690f96f0b0690b2f05b8fa06da539da1086add1bdc5e41ca418c8a9e62f75a1d5c3064be3b763d02cbd4ec986a6d4db0b5def1caf3b7e6b2b18cc119fd1ace8ef93b2e67f153248a844c6dd39e955fa7ae2d10ab386ba0e3c09983259aaf80654818421f299ae4f76c1977ea6b22e0117ab8e9327470a0803104c31d9c50f595df4b59457dcbabf610e8efd6ce42e210028ef2a40daeeb7b52b5dee87fbd9edd4812c70d3b03655cf8c527b76470d7a168cd23ac716ec057419d362d88901e093dd6fa5a05279c0fdb23b868da33fd26032a07e0935e3e41e2ff879c07710a5a0554208caab5fd12c707f68998d013f3fd858749c37e34a15366230abddaf53384789df8575856e335083cce50200f35f4f0c01741227f7633f64748dff363f0d5d2741f015b3b0da58e7550c96da8eebdc3ce9010857fe3f1224e8485476f772c4ec3a0b1914bbb953bbe634de98ee085481dde0a5b3dc2d0b9b1c0eeacb67257e295b5871e8b90d5bd4702169999b0caf297fda250c3d67270fe651ed3f13f927ca8dcae6fecb4b14dfd14650724e1d85fa0be29a6126ec1f6d9b4bb5d7d01d132ad4caaa4f5158abe9cc90285c931a747f85ccb444995e3794eabb8a62852bc7c8e4262261118dbd39905e02cc4e87f6309e31dcbd7afe3362cc3902337156eb37c38588e2132969a8b460caa70581932cb872bd5eb62030acf78e5bbe017d7beef6378d53388baf133984d611f9c1ada7415a710c7af40ea8a18b462ec900cccfd48c9c9f6d75a0bec518eea39bd729b6af9a41a3823f088c62ec6e494cb081c011d6f72611d147d7297bda13dc1c5cad5e2f64e0eda824eef11a8e99ac24ad312041b7f16587b2ee5da66751076a45d87a0004cff4830b178d6becbfa7fb0fbb59af805a2bc047ea82308732b7170f5cff6213bad74b34a24a40208eb1c3caf10cdc7f5e075893896a7ad83612f996a5ccdf4439f9be232e0c70c8b4bf1815cacdddf09f391d2daa3f185752edaf3b178daf972d9a0d2075b1e86e6329a4ee9fc3701416246569e6d6b574d3359cc7fdc81b7e7f6e9118a707d9a8e19fba7f0bde9fc9f0d57ad42a4ed8acc888573de8a42deacd3ab60b13a1b8e7b4d6c3889a13c9f83dca28e22882223ce887d3e754c03d13d6435e25f8629b32c45125878379f382124092a85575aae2f7d85c068952e5ee2963c82f0f52dd2659a3dfedfe80345bc6b448371f6e98f89396113bafada3d7d00ad708ccd0aa4da642e47c3c3125e1fb7007fc3428e8039b237784ad373997b21dc11eca74a5c5c76ea0d28016789fd5e71b53b98caa0403518b48fec07d7deda6bd3502f6e845ff4d38f561fffb712ef1574e5c67b18250848cd7b7417e38f9c2762063728b3ff65d3b1308d52384d93ef6106378ca74e73785edcbccb87d190fa42852bc0e7cc674aef23d5c4f6c7f25d3c6406222440f30395bee17078618dec30471cf0f329f7944944d224696b984f06b4d6f38aa012d8f468a379ea694786916fafba3ee538cd5302597aea4b9e36176037fb75b1eb9f00f4e814b10dfa4593eb4de89e8672475715d9ce9f296a37b7cd1c04a7d2ad6adc2313a63650ae6f3243cc519684ee439e62d4f05c9cd1210bca14569501f73bd304100e91c3125ca1856d0f14ba4cf60858ba376e8953401eb0e34ddcc79a3630536c44b51dc9cd709c0a0c56cc995485458f558c9499d70015847975f1b0bca691170a19b00a5ee341b153d382d12f17bfe6e524c8d1c3ddd2ae4e8b820fe3fada07d5f11b0d1cc20d4f3837ca323bc3aca166310afc414b5e6bc9a8b5b14511a3d53ab0651e16f35f71788c25d5699f0e8993d10f5ffed58bb5e0e1fee76c0dbb6bcd3c57742043cfb6af8cb5a939e051bb880e57d450f3b422c4261481e5707d463502fa1a247339e29df438ce7f89e0e589fd38c495e0f18ded68b47dabebf0a524ae202b06a0f3ee6c4f03470327b4de92c3f542653dc56425dfe320f40bdedbdc05119310e35da22b5750cfb30e5cb0ed0716e6db9db5e7e6abb07b92d4a953340bc2e5f190ad4c35e289954c971c727bc15e9b7aabd7f03c20531fea2eae19f8e9d79adcd2f8224bcfe7f439b4f3709c01b3597ef79cae722ecec8031a958b0b8ceaf85121b4a07daf02f8e2cf0b3de383af65691660f59e165837b7cf6bde2dadeed1bd43d3f73ae1698911b0536f7dbf4e2d6c56335726bd1ec5656507382eb988fc02139bc2abd9c4054dead0b8708d1c82bc2522a0dac1b12a4e12482643b52f6dbd336a5c9e18f3e1277108fe3c748f1921b51120a6ef937ac5ec0d7987be5e162330df3051a5d73e1d2d58277003a3816dd7b8b3f959bc6f211d443c38be9ddd3b9300ed7c57b80896ea353979c025686d0131368bb090ddc1b416968888543bf181484b30c117cfd0ba7cb47fbdd9353aca2ad8c3e232ab347c8fcee3da949461d254ebb4efa58323397c7437d14ae41188c1091a1e5c19a812bb9f3c0e3052d703fde44e4bc644eb9868cd3e81be7c593e112e4b16332a0edc158679568d68898215401067e152674e9b08a91f1545039209540ee07d606534d9d3cae8e2ba7c41696d98e81cfb51ac75093cdcecbe4e151e7e11dd2f5acd46c4b81510b058aee02a6443b5ed15eab6ca414f558d717e713fd72a1508344d11dd855b3ddea04a91e9e17e801b1864a11ba7c691faba839181faebea6fedc5e17f73b86c9f79c153de886a466b2bf576b13e20ee04ee7a812f9c282f12ecc84b4664fa8f0d7ba28342e62a5f26636812c85a2fc52e2713821dc14f4d88d6c7c3b04d1ebfeac6fb52c1c3deda7ad9ca81f3198479f8aaec97788c4e36a60faf9f35466ea0817a7580b7e2c1dd7d5c91f825eb390649ff2ef59043a78e81af318ca02d8e51530893e2c476ab939be3fb7f97e7e1c876102db4f933c6515a0ea3815f8692d5882149f27228de5d3ee3a189e442f7aeba9d2f224b4a7350b972ed7804024285cb5ffbf190c658042b34a65f74e2206386d72f58c1971e13ee1618111e4abbbac42a4df018dd5095f5d34f8bedba7e8c016d43d5976b53f64b8ba69bee894bbe6592ba91091b477245b4c7fbb3b117f43e153dd3ce5b8e729db69166667aa8a262f499f1ffcaf96400926b7310e8dad490e172b3a28a1372e8269b700843040145d0fff123e774cdd7d13a107f941e54c3b101a75a7b89123c730b23c5242541160b81b2486f0d43b48b630ebff077349b932d4b1dca86ac7b0eb09ec210c6e60b8023b1bd764f45575aa0ff8cf428e73967b893ed99a3677df98181ce1e747ba05e4d22856602b65940feff1572b85070cbf3f3667d9006ac9e2a823bde9bc6e4802ac245a1a0593c9ed21bb20bd96ed1d6dc372c68fef06bbdcddf157fc986f12592b09b47bd57a93abdebe379afbad3cbcee4fd3d3c06ab7e077c3a592d7f05dfecb21dab193ed2f1cacc73b36f234d19cd9aec0820a5f07d885ab875cf95ae0ea422b479f899185683f450a80bbe3243e675ab78d03ff3c8b892162bb6ed140d6693f23c1db1ce1c44e72bc8cbbd78cfaa7276e307a9790367739bf7ae9c41910690b17da552948128e954d0c0358127a6c5cde7138e4143dfd124879d345bffd973ff61bd59ef05b29b375bf361a91ff36e5d944ebe5eaaa3bd9219192cceb83b03f6e01c03773d0875b125b474eda6b638815df36a9897cd04bb0627875acab6916945cb4ad4a15c7789ecd6545710ce02c4a95511a936dbce1d23f63cf2aa1b4168eb79125c272c84984ff6b90509dca11528b90ebcfec843d8031e1262e44492d91380c1e48fe40b69a7f972a4b65f2a1272e69dc81f9265944e7a7fa5292d716114f81e5e2d85ca8e5bef65cf0c2b48b8de8ecad9666060f8bce33197c9c8d3bd23cbf12d5f38b2bbf558834a1f087e829393f6d894f406d5648fbf02c033c0c9c7c2cb5765bdec28ac013550af841af8dd75daaa595c21604d27b4865791e19764aaef69747f939a26d390cd2a13c5c7393212791d47feb4c7f997825007212ec03106caa9313a057aaede6c32e61b56aa7d2ff6dab413ea0024a52229d3424ca20b13ced2f99eff9ab0ece84c3742825c1b1af21c43b10f92c9b77e860ac2c979d06114976002b556cba9d2b397138802defd025c2eee86981f62a0577e3255e257305c56fd6b36dfe8bdc474c11509b2aa6a6e2d0b0c9940933727683460a9d76ab3a5173ce8c12cbbd642f5aca35f712b0e6a09954347a129a8bd9d7b03c85667a8ab55baaa182630888a363429710fcc3a1053a2f133a5a0ee00801ef0044046c0c83ad2fc7e9004761f23c6c56c68ef820658f10e9548eac148b676b17ba4da920681aa72b7457836312e6b5bc5fa94d432c4f474c6e463cf489cfee3759027eb768ada86f9caaee53e8fd04f38d0d77c2b0162ff6b9526d46f0931e306a7ff8507264fe9a28668e232524b55ab6c4265bb45732328f3ab319cc762920efca7be63c01e45676abcc53fcc8127bf623df583878a6467afbed0107d82f39c0a07a91667c030c1ca68e51efb39b4de79dc884a8c1d3e08f92d79ced7e0726f794cf707804370c03b15798d889386011a398083bc39b8bd025becbda55461144cc64d2a87f66fe36395a7cc04a4d526903c009c7e5b879ad42b55cf100dd2874343a9b5c28f814aa9bdef9da9ddf3e28afb7d7252fcadeddefbf3871266722f4cc91e1cac1ef6d7bf79c8041e9c3bdd67ab98128f846511aa3f3508002b72e266ad7dc1e6d7980bd52080632e8b34517a175ee55d6cdb603482ba3b08494208127e10ca59ae0464b6065129b14c5f64dc094e6b3c58381facb0ac54edeb50551d146501b9e73db713a9dd2f7da7ac59b7127817b6c7e2e967189257d63ba1086acd83077cd1a472739146b872eabbde85a95c7365fe4014b4dceef5c8d162308280291425ce7a154731d7827d00fba81664aa1799117256d3122e3683b453c57a5c89c0ef29f5ff01378502117bb625c06519debb93adaa8fedbc4a5b5c333c44d69506a9defe5ff6316447e14be33ce2b346eeb0ffe9e8eae127934ea47297114a20f1571ff9410b6aae38c103958d9e2f744f96e7a5c863fa5e1f7946435916dfaea569c98e52cbce21f316a386190fc8a8bc758207eaf47418f22cb383a3c0b76ed430803088ac2ea93a59675dfdb5e54acc2a344df2e79edb82a8c353acf494b7875dd61d2c671fb93c9f0705e0c706350ad9a63bd3ccdf5319e72a83570b865e4a16f64ede5fca465f827afd75a94cad5f504af82e6c2d20439f7b9072feaf64a46f0bd37514d5ac23dedd40130eb796b239b20b17d01dd24d103aa1bbd0b34c98a33bfefd305f678153c1da63db75b3519aad211c89866a06e14053fec9ab201a15f0968f5a7351b746ffb59fb9072e1d31c6f6a3893fc4a08c30f2c2f77b6ebbbe541ef66261ea201ee1dc28c9c3fd5331146d17c860c3ecbe6f03d7fc3ab3bec46892b484a30ce4e368e9ce6c7deb8551a62bffb57265999bc0a06d433a147a9455b52dc0206d07ba97461cac5cb1e6af61dbdcfcf224fd383c82d8c3c2db90bdc2a1de556fb17e74d717519ca2c67fade699607baf4584a714f36a9a48619caa3388af28ea027b3e2093ffb100e60b62c7d7bce5389ced95cae2876d22766b9bb1916266b6298803b1d2942f80650dd52e3a6e68a2381b54ae03f8c72b70d74ff8d62664e7feffa4957a7c5ad39d61310db12e67bb689cc1f3020271161dad2e1bea43512eca10f9806a04dfbd5dd2a7cd407d8bb7bba1e8a632f6ef20dfdab100533b29c8ee155390ea7280b1c6f08518cd51a280be4ad60e01bef867332df9dd00bb5855db742193990382f4ab17dec02ea779057ef4a17973395fa42b06a4f025190de6f453db9de88fc3ab30f925e4252a3f068840147f63651ad9fc4dce4a25ef04827475ee32b6323d631d32c3b5e56c2a55f7165ffcb3214171ae35d46238ecc97c6ea7c33b642f464f65e47e207ad3f39db36aa89fd812af4200d822ce12633bc286be2df3fc11fb9a86ec3a1b8e866925d2f3ce9a6832b906f4168107cb6dd819206c526a615de50e7490c51afb22438166d36dd591afcf832c7cd0b00caa086f25ee17510f577b1414bce7b402d89a048705da79fa27d7ba3e1df1bf09a8413fc367764d504480886e0c28d818d53f533ec4bcfe6843f9c8acba036b249e9e9b03094ab9c865178d5e6d368b3600ed0bbbad0cca699e39a6dfc3d7ab8419e4e73befc32bebacf848c72f5ffd1fa33dd0ec763e3028f22b33164f7725fa90bcace4bffc5a1a44c5ead8d7a4ef1312e28665f62e09d3e845ac5eb5322381ccd8dd25d6601cff44954e9cef0e719c7f5945ea91a95ed32a69fe2676e62db8e98bf1ee4cbbfc2270e7f97a3791b7daa2d6e0696a994aea73c9096077da55ea9788947c4ac575933fa3c9ebede2f9d933efe13ccc28762a32f349c72c6243699117f64c8fb498960838c3ed33a6bde0add7043f55d41d925138a216b1244e9c743abf31d38ef74d1e5567dd9cf3d18dfa2013d15feac483f84109cbbe50e021ca914421c870ef33cea73eb8e4bf44c26c9110e75daf3f584ea74715b8fe9517facf675226697e6196be80d476fe6b2c3ed0a6871a7d6c2b449d4b77c76eacc645d8230164eeae097df3039486d5649c859020b4835bcc09eded052f0c81bd98422731cc213f2d4c94b0691311fa5631ff40fa1fb8afcb89cef84c2c098ece64c510e0ec6b0ce3b886dd0af654a8b122051fdc506ce2a489e3de4917dfd6005f7bf17f71ac249df616b7e2ee433592ec122b417d98f1f6880fd9fd99796ba509dc018c7942ac7d2760b757c1fc939bf87ed34c49cb0e0f49eab2dbc1d50151806e97c82a7142eaeed30ac605a05ff4323b5995ec50fe39b861a6974af802b796807030554e3f95ffa95b274dfc015a56b09108ad0f21edfa3fc2316c0320fd5d87ce538bf8ffbf04c4d76c74d1b08c54fccbe4640f0421591d99083b87058e15610aedf2bccf8ef41652f3759634ea9dd2975871579270d082651715490332d03109cb48d788664eacedc3660799f586f66e70c913dd8511d0100ab86b3d84c5e23d9a89735e02d3c43a51649693cc38b69171ca4e6c67642212f986efa43cd2dd4d9fe67fabda4b9eec56deaa85229d2463ec6723fb24eb82b3f01007ce2a4739d42a02bcb331317607dca2fd3b13142053b49d83650bbe8630e379caa7bd1679d6585378b949cecad4d2f43a105c05490c458870bfe6ccefced4d01000000000000000001007a72132d71c9756eb315ba91e59625029e5206e81e4af0ad928025faede51b9ae0be61d9a7b73adefc0d73542658393ebe3cc3bcc20a286e86a7ea68becfe5122100333473f16d482c8725d51b4cef4e26ec24b4681f9ef8523b160fc0880c6bc1cd01ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd000101004100fa525783d544902802f52c822d0e3e842660b577b7e4d74627e34350f1aced59f6ea356aa2a936b6e9ace648eefe288d0e10402de99b8e426d946ddddc2873dc \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard-zsa-workflow-block-5.txt b/zebra-test/src/vectors/orchard-zsa-workflow-block-5.txt index b9f3960256d..b4a993ffc85 100644 --- a/zebra-test/src/vectors/orchard-zsa-workflow-block-5.txt +++ b/zebra-test/src/vectors/orchard-zsa-workflow-block-5.txt @@ -1 +1 @@ -04000000287a2bed23aa02b10d4fc939faa37a7b5d8d92c84300afcada7e16b24affa26514e2ebe3f8acbf926cb983b9031c6933119ebe605bdace1792e537f5546fc7587bf5f7c1be2ae3818cabc18466bb1b63043c18cd68838b606c6b6ed5d4528c8d22254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025500ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000500000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000010292c184d23c3870ed8aa275c9aeaa0a93d30b041372e4e6fcc942496e65767db4e92d0a5b9a15aae11dc2e3f35118396bae5181869ba61e8b63334d5d7631a31968a109732249190107638729972dada017338cd27d19f6ad4dd741e54599c09d542d1a9ffc55be4fb5a059814547106abd1bb4c4b4deffc9de1f6dca19a95d192582c15afc195255038e12a31022f48e8a6f79de0887d6ee88589403bbc0823e9a6bf81d4ffc13a2c1cf3cae11a6ad5b395cde83346599c771181eed56990cf3fe1220c254bbf8f3098151c59e418f2d13d6ed3ecf5f6e6ae644e292af23d9fcfaa9b1b33f61013a003048e9d7cf628ccdbfc50f0d967445e1bc80bc1e4a3810c94fbf724825695cb44ad3eb2549dad9093e18812d43627a2e9a578fba1634a2355e43df744e665b2b67f2d6f9c4108d832d3e139990cc9c98aa19831e03817f68d9ed9148fd1dd998b3a7131768da1ed293985d8282c40f150d43811a358371459f3db393e91d0073baf8f435a5d451e1364fd9d7586e52fd6697cad639849b1483917273eef779c434e3c464b0c98b66139716f6d65d9412950496de39a32429ec29ac8dc3b127d7c5d9f0ca644dbf28c3b5e265efde2dfd3cbb56a1c9d83aadd9b1ea1066f38a8a4d8f73afd6b27e65be6ba30c02ab72ed32021fb9e477ebab31abde7a57ed17a5e9a0cd0e2c202483c9a9127de49bf48bc6f0445f3321b4cb7cd5919dba5f56956cd84f97d65e38d05c92c9b1047d44ffec8a199fe06845d81c5555e4fd9f6a946bba614555b56fff4570ece8443d333ffbdea4456de6735200e0baa79a76f950a09cfe2aef75eaf733f61ca442d1e11487eacd5a2332a5f043ca2ee1e016ee5a19992447e44bf5f72d309ac66f60f6c2a8bf04d2452fe486d8ac0d14b13598d05c19a6081d73c72c55d375ba6e0ffea534982dfd335058f3a01bf6b5d1d13891113c57147f47697ef19d25b242bf875ceae0577ffb604e1acec03ba8982c6a785b849eb1b57d565ac9f6c4baa31940b2bfa16677f0d5803e78bf6a78eb0d9e6278b035c406947406dd1fab4c21bc1228d24ca9b674aad200175e1198c56da90299400fa5e921a3e2d1b88ae3342c641e8e2782a6a343f13fff9325c8cc1aac346be6be6dbdebcc4cc30ff9244c5e9882be3a80576509411086d30ee2e689100cf323ab4a84dc87967b5d98aec639b3599f2873b754b1e3e08738b3891f5b32b1f28221a564da7f3770190604bdb02d9422b412fbfb3bb4d22aabe69cc06f558000ccc6ea1459738055a82acd82713ffdb8999144dd15c61dbdf52f062346a48bf40a524d3d4b4085ee361ebca17ab2f6be7f3765091c91d72c907ab3c2bd670c3e977d23d958c4170f1f1ce58da191fb7b97fc0980577eb04cf0b171930f8ef71fd7bd5603cf23e4d80e3f8f6ecb771e765ec2b5ab25df5ec6fc677f119ef53b9c55a606011236243d4371f9a5dbaa0985e98e0af0010f74997a367c580caa7437211a46a3c6c1d747bfd6b46f38fda115565753236ecc1b11c072ba5a047e59e2d7a36308dfdb5337a7f2c077fb5d2ada30bebd2b649697dd06950fa05b4592d122e0614963b54fe38bb6da3ab017193c3e3172e63f7c127182552345ab2fdf797007928528673b0b06f34016e60d6d318f5eacd4e8aea3a996048bb8285efa7812e8aac974f89807fc2f1746e0b1003fb5847c4bae58b148a37fa6fd8f4f2cd20394a7d1079da9c913ecad2740e8c989cf1d563f9e5ab309af669139049630862bfdd72a43e231abf765847f0141afe15d8dc3d5e2e7d8e3254adf183326a588fbda94942eee5e758cfa12b5043ee0fbdad3406412d55c52cfc94f7f01e0134ae97e96dcec67ca6b0b68f6bd18e3f30737bd01d68ee900875ff46f4a317ae1d5e4e6b02817867edfe0a5cdefed180b381923837b6e1eede9129253d3f642a4b85afa5aa47009c03493d020a0697577ae72a1852db9aa9fd0f5216aec68ac210ce64f742f0017234ad28a71078fd0d43699ae5240503d3fe37c5355f008c351a9f75b7ceed7f4a5c8f2a9ee10fe7384060dbbb27ac9bd10c5b718ba63dc3f15dfeaa18b67a0e319ae6cb2492069e429aa0a5dcc4bfbe770fc6aa498c1615c590c8037fe11e8ae22945344cb2a9bf070add4302f5d60b7fa34d5d0eee55eedd9e7dcb46693b11d2c4e528951012af4f836b541ad8206e3b345d9098723d5ae80e675841ed296fa21e69542a77fa26eb5054330a609b31298efc84bec53c69e812181d79ad4648f3cfa4c04afd8bec7ce832796b016ff06676122fd33861975c8e7bd63bf64de82abb5b4ced18048294d511cd883e9436f7eafa4c7277882fa5907fcd5f2c8183a2354fd2e2576d574d5de43c9907033916a005e185a730bd00704af9446ee497bed61cc203189519532bd81f2b42ff1aada8a44f19965f373390000000000fde01c7a9b866111dab0226e8b93d05767ed3acd90d5d4d3e828760b4e01c4057781b8862e723866b4f4dd35d5a54bf65d69734baa73391df377948ecde4c3a68795101546bed2d59c6faf41e1d5c38ea7b495b445458c89d9e270502d0c1c2a97a419abcf4d19536a885ac19c98cc41e387a8e4939b045b27e133ec07acb6946cc40e080c4632918735ebcba8283109c15435d9ea82e12be569ad5c12fceaa83818b7aa6caaca065c14fa3307c5c3a089616d353b39e1de7edf917a9c1b47b33ec12829f8841da86d8d16333632055adc3dbd4543ae9ac8905e6c8953b1c0e72ca39590247259aea14391ae8c95de00172591467fd3445fe925d30e1d8c3a4d6bd722661683d8451b0d135e69a06300f28794b4109278c5f7151f3bd66f56446ce02222e502a91eb0825b5bf2f030a94eacc7ea40ebf1df2475affcfe9fa3637f541b56806cf012785d190c9d0acedbb004b25e8f4c4ea7c472c0895fa86c7a46383b3aa1d611ab4a15ce0fe87ed7ca2d44c3275da659c0d69d48a70ee116e0bf4881d635655ffd693bb902be32ef520833ab6aa1b0877d61ed7fbcb16f19be6aa0b4f82542018be4be3352507d3fc7e7496f4af0d9d8131e23a8afb8924526e760a5966b0a088669a0bcbc0e0482b2c9ace52739037efbe62fde7ab8c8cd15cb520bebbb83ff581f562024ceeac9e8ee10656bbc1ea5c19ea1ad83bb342c087db991dd26f72bd39ce4bb76c2df26f54caab5311e0654d24d726322a5b614c696ac94b5b74706ae4427df8d0491d43f0ad0d7cdb200c4ce1c884003545d46b7de5ba5be25b58a8750fb3ca944a931a07a9090d47d688f0f6f5d0cc44347b5d0a66b05ad40892b9f217618535f7157409f9aa4e394d8ac5df54e18c2a1aa5f15cd730df120516f8636d7de121e6eee75503c8dd9ba2ab078654a449185b5e072f1e98dfed10c3e76d87a84573f97387741652508b16dd3ccebba5fd792340fb8e1a19de4dcc699ca23ac6bfb3597ca65643601fae54efc83ad82dfc34b857e21f2abbd0f0bee91750561a2e1518e06ed2533be6742a99cdefebe1e42c40f59f346940fab81b26557f54fae69ee604824a93b8d52993e9dfd1f3f462c738cc148b07c00eff0a959766b4ad599d12948de617be9b42f1ce93195ebf1518eaac440da2b82042a334686c5635e6da1e19ccb570d17bca6e7d095bcbf1cbc6f6271e410a697f55c531b2b5e303317f43f09ec09cf62eac60d626fe20c102842f0e233c7d4b5b5821a44c4f5f0a172557a74f913bea849cd1f872d1b1f562e1143217b3572058309312a2f909447de9ae7424028225986d51670ab1aa28e6996667ad453432625b1626313fd2af1d07d69df7bc2d841d07f8be876053cdac3fa14bf4319809d85cc8d2a68be59f58f583ec8743c24b999bcad51e97881407f4a1a3bdff72f0b24e72649a9f4aeced575a85d61429ffd2a74fa99b16eacf0fdf2bc81740e1516a096b3ca402cd196607163969b4af6bc1845c41409fc92b16d8699fc201956b2569cadc602d463102d4d82999ffda72981915f79f5370f5dbca451ae8be4922fe7877fa93224276c0a4919290bfef987b7870d3f976faf78a0d0bf25b328123a711828e0403f744aadfeb694832867c7574a05fff5f363634a7d4a89aab0bc04a708a0d529253ccfdf95a6b57d48be2d2108c36fc12d37b67eb3f5342d66aa21436cab37734f55f5162c697c127e5890d5318081ee8b4ba287b6be53ee8cc53f916cb88c066efe17df0eb8b79e15bfc694168a68136beab88ec7e0f0026d182dcde6285714a65ef267fd15edbb948ceaab0e8658b8b5fe12e26e7f4d4600529ec160dd7c4aee06b254878583f021095791ca0c7e6f082a371c49fea430e3a8081a52767147919b41fc0c20ab57b5799a0a8299497c13d56837b741da03aa86171abb71d5806f05d6eb642235ca9efa2856c00bfa8b1feca1cf8814efc7c62627a7c3da402b419fb5a287c4d1be72d078bf50bee3e6910baffde9145a205886931738a965b7dea24328b290d7a1b2570e23723ea53c9cea5fc2ed80236b3f273b9612a40029e3aefa4f3b98beafb35931ec529d702bd6fd828b98d45197a4701d23afa38373e418dbf3e7f76342c0f9a82ae9295edabc639450efe99115c7e5197df41db3a7f9b14788620e2404bf1c133645279decdf5f2e8f0237c871801a283df32ca5e175f49d90961c18e559caafdff38fe168d17a00db47ef9cded18f09b1f2871cc36501d1d83651287851a158236ee2591ed663c6e2bb071118eec1a343e2033ec4191fbfa9b649fb9ec561e20f4f245a37ac949d1da26aba58afee88517db7ad3f3b39c6157a2bec575cb594dab03d1f719c770dd7bc2d9f8cb4451a9d37e9f007561a265c565482c2dc2896cdc2c65816e2bb3fb4ece2536452a5306364661465648ac7c9b60d4fee00e4c58a24408b399ad87f2c3d98ea3b7410050968eba0e9b4895240441dcd9d3d15f026b7362bb0c756d57c5a49769fd18a1cc73eb42b4345ca6096d1c304320869023d94eb2c8e2999ae0c15a482b4308b2ea6874ce63be00641631b290a1b43a83ac6b4f0414f18974eaff55f728c3e362f2babd6eaaa2f682468beb7086928f56a5b63cd2fdce8fd84d7d41659f85baa35531aaeca294ae73683df2b9820cb935f92a5e93d44917a4d18b8d41cbff29b3287040eaf5adf7e2f09ea2fb26377af79cfc9fff1503774e15266d9b725be40137afce9e4dddaa22255de4daa7e6ecb7fb58e63f34c5ec44fc1135b6b0dc1cf38b8396cad9cd4ab743cfb8aacb03e756fa7020f65230bbb5e1b60c7a82c7c4f2ebe2188e73427a6d4aa146711872f791fc4748a9f4624697a04eedf0f9e4a443ee29df5f37cb1d13601a277f2deaf652eef584311dd39dda759af6613577cc72b1a4db56f5679c242572e21f1a880b23269f9ee61f01ff8a617a6f4c71941ad00cbfbc38a9440f06d4621c437e0bca9223497cc9ec804b709ab87739c68c0713a356c3132359e5149e566f9933486493a3a74634c599a27a338f5586402aac52e615760a76f14a60ca89d6899b46af094188b836871a8222b1a6613d48ae2df0d020defcf8d8870fe007783edfdc58dbb383cd9d933a030cc60720288e5ac733a553633e41f6f7886b72bfd981c1c4733f2acc6197b6a7b8f660de6a5786e100c9e17ced8ad769ec3381e51b6ac1ddd476dbd8100013b1f446b9ce40eb41ebd04b481d69799b6d9bf5f3da6badaba83251fdc2de6d207f38087e6562e370b78352cb470cc6ea0ba2e24dfbcfa1378b8d2ab1cdef65ef42ddd44ac9714fee67134d5e2357738099075753adbd50bf74a44ce8ba4f6f472bea70e0a72006210800e36656f329117f4e9239053fbba11d22b422af95a3541a196addfa804062048189e915487813097c7ede4d6849e53988e88032949a281d201ca9216fc8da816040ea2e2ee146cfa7c37175367425c882cbcc8ab3acb4dcf87b1fdfe52d347bd32a3cd43ad8330f74db9da313feee5a82726ea18a63371c40d29b3ad28eb6d291f4f4940fb1fc43ac7c1af69d132c04130f0fd35519cfa7e17e3595a424b00fb3dffb57ff10a7c5acd49d8782687aae08e281f4596cc9a433f59eab740a7d0c716dc5790806aacf7fe8a27670d2a6022ac73a38b47f89d28aa4b9e52c147ecfb3929c1cbf5c3d79b9ec122b106bae8e10cd03dc6e160990051d9bea7501636450890a176b88233c5df00be5e79f2ff86d42f9e803164db995e807e501d23cae525cacad65bc2f870d7476ae9f83e165a877e605e735c0bc62cfa281a7133e0631da04dd5f3ca6c644fc43314e11e0a4124c07bdeb52763a3db32b24c3c948efe1ca71f63d1bbab3cf49cc9e73d0d6748a826aafaa4e4069b3e84bb1e6be5ce491bb8704c1b3b7d4644b2c9e41f1241529cc12c8bf0b2d3e9a853a33b27806fec17f2e55831082952dadb357cb030f7d9b244943d8913494596b24f94be4caa1617694be0dea7025d7fbb3acb14c3c9c18a49de003d43b8ff10eaf258e4cf312d326f0ad5ed59342ceabb8a8ed5f82752784384f21388c03a3d4bc606da90386f179d7a3d337f2150f85bcc05149ab0e9ef73892e48f0c3d88f569462782fd79e2729d9d5a2d5e0a51357928af3c69483f26852b49b4ccf0513d647c3f0e018e621360a2b3c3dbafdd6350e0171361450774d15f408eeee7b8a8c4612a30f35402cd46e59d9af71fafe2575af2f2ad11e01b9d674dd2738da8a6b27d53ec4d6702f34b3328067fbdb088e485891ed6eb4f296a501dbbd90b8606af8f5e3c2cc2b1722a920fc07b60774719862d07a613f3288706e8572e29183af8fa382d8e268075c7e911812c9b60b8e2693b6d20770d72717f204b5ae598b4c5c1ee600d1451f89f5e22b0850319346dc560a89a8344b5e794b98141be40518b7f27fb856900722f19c4c014eaae065fea32963c8bec8f69da977a9fa8f9980a81dd0a128552feb128be464c873ca6e8e78f49d4b2547b5bba8dfc8a69cbaba044e5b9e05cb092ac239c44a10362f1b6d41b66c0dfaed132ff494a70fd753c06d97cf0d0e7a18eaf36df46151f69a387fa7dcb921d0709a1f40ecc244daaa2effb4b255cd6d0054cb6780a8a695d3e78a664c4b1f76634c0a1e6645f01e6c07aead791e1ca03f88995b7b764c3b59c56aa43c459fc6ad2fbadd8799335ef382ec53575faf8116f7bb520e9b49c5622c6ecc57d09410ac4904257c8533a7777e1f3e5707367a3021c9e93fec43d4cc269d59de0a1049209049dab9538d97e8dbda6ef7de4bad0a96b0d88ad54edc7a30e788d92ca11c3d55a874b923301cb3f6291217d5752721c2bb7b69c2bdc25657cc8e73df1278153911b63365fa6c2018e3debce2b8db2900261a9408eb11242f0584a76c8089fdbf1eb5d289742f171bb934376b8e5d13d76f5dde3b00513c87502ab8b5b5590475be9ab64b5a6971706ed66db44db301a459fa56ca2c4adf157b0706109b4ff5a7af72cecabd85fd2d39b1fc53a1c83ef90779a1ca5bfca3a9a49061c6736b31738b712bce31bad51a548d131d3bd70ca7be7c330ef3c5257fc0c508d0db852920122ccf07c20d727ebc02a620bd122fbe9e65bb47a5580798bf7ade436f6aaf79307c1484c73a0206f615d5860b7432801dd6771c43a483a9b5020c49d01208f7f499d24410de01eef939c70d757727a700731ab09c8a9707da01b9ccf6b4ebf78d49da0099232e54c0df652f0a723d833b8159686008c71d45b72d87fb092234de65011bfeaa96a01f2f1a67cb3e2250f1924c8f052a7f1e0f843352cd19be0192f9a1a550380c17b2a2bbc5c87927d33e4b7b2afacd3d8329adbf2a7b1fb2bd970294715b5b09aa6d6d8e307994160e8be8b636e5bd2f9eada01ea5dd3df4a054a169a034e29100e5d13b84032c1c7a0a6e310414844390fa9c96a59bd4fae74ca315c3fb75543b5653dde2c7df18ea83e80925e61cdcc915493988da613669971970f87e021eba449ed1958b6d29434e5a0ec98a6121231f0354393144284f21f92f4567f18cfc82daae6d4a0d23750d133cc47b226c492fb9f2e884e0c0f438288c341689afaea46f12128f351f764724e317a3a6f72a356f1b6dc427e39162771f53e7a8b5166d69b6f3005e08cb5522c27c637998ab08071dc148745e87f80e9dcee2822ddfb3f227de1cca105969daecc28c4fa5f7165a844218c9a5ac0d98c17fbefa21b726c050a0061a183f0e92d27fb097533ceecc236a74bb32a5d77ca6946246cbde703015ffdf9a35658cd887b59f57dff7bee8d8562af2efe13a2eeddf42016c5ebe60898233e83eaebac183d9dbadc8fabc82e6bb7fd7eb4d8ea92812c5c3b7c439976c4c7bba0774f5cd1734923306b8125bef027788412b9594a8b13f78a5cbcd9babae1d7d16457aebfe2f3e9376c325451e68796a0359b8d36e0a56b81f1f3218b0ee24e510c3064bf4c7fc464008e23a099e3cbf19fdfe273cde4ea8fd4207c223d9eefa16a55a4629c4abf8d702cf69bc6c9c572db01288e613c4e3a5edfbb3d256270b19f4717ce7d2c2488eb093811aceeedc85bfeb03f88d79f695de67e81b2ff9ad3781c4351a6bef1c6860257d2ef532ad03250edf48702fe60abd70aaf6475dcb10237ba57c0e61ecede278b179ac86946ac84bbe15a7f221fa84430344102eae0af8bc25cc29e032fce6c4cf6e9a6c129bd90f58c35ab42d2daf01bccce5b2453b2736e17cec732886afc8388a4d71298a62561ffbb8a7903163d28060c3f7203a9a8f8ab32eff98e7729bb9380c687610e5a09fc289c5f95f45aacf1984cb6f259d741134fa9bb3bcd6692f18a836a065268bab9dcf23a9d657a8b6dd321dad1326e9ca14053ab620a4bb104ac99e1a953cbe775a720068c7dcd065d7a50c3c0d713817431231e289cc01c0187f1deafc390654373fb4e824c5962ef5b40f812be67f43e05e034c0555483414507fe7bbdb302018d661770361370d99bb471d36ef66d24342871e3288cc4e66330770af1170605c2c88f523220990c2174dee1fe17362d2c714b316ebc7d665a3760ab341e24bb889ee38295dd66d1c88a675001655409dff64427641639db8b74cc91451154a255440a550a44d5f53b97241252053f9472631235a290981043660d695104e5273baa1bb76c3b536b6519a5a268f6174a1569a4671a9b583bbb3539a121e52d633d2384d2a55d2d50d360109095f6c8c59150b23069ac85222450024a79da05a95d54263e6f4fedbda41275e29217cd888f397fe02051afd5c532a1f3f73ade49c1be9bb4853b6ff18ec488a0e87fd4c6006aac81ec12590525b26a6c72355f4bad32050354eac757b0cbd1205b750343cb75373c7abaca352bccfeb07fe57363e10256aa57e937efc0e1fab0c7414ea0b2796199550dae7a245da89099a65a95e2076a026c1975302d201840e4cae2478408c5e31dfa14d4f42a316fa8c7aab7fc96d85d7bcdd910e9d6ff81f04b24ff48783c99f20e0c2e5f877c414895d65ae51d44b83d1703766a1383f07744d1de63c330ced8611db60753f64ea9f5f474d1da0f50c674b8b2058c4551f3791bca32f2b2e56906b0e29e17b00096fc52113a3c38182ca3349b7ff3eda3f2b1b0508a8b1aa24cdce11367c6a030647e1b137ea3a4dc3b525e54fc3ffdc3765e73769888957d2c19bb4f9b46a176b69838b7bc48bc0978f59df202371bb2f880a272e84a2efa617e6ec8162a9c06b99504686cf0619f7ba13a9971fe9f1328deabee75adf3095773b05902e95e9cdf904719a11081313899d9475bb23262b2d7b3a9433d588e60a41cea0ee5e4273d1e0ec01dc022f5d869644639787b910ca07ac75bb6a08c78d5394e54a73e6b8f7a36c57f45ac4062f1a914e693c990ed848351ef61858fb88fc10452ede7709bc40a76a71d015c9a02a4816178daf1526abcecb04f129b65e5f7ed08cd4bd9b4ce98d2509e383897fbac1f26f476c34dd524fa83782b523a504328c90c9471ba1a8b6671e4cfd6e73259c15574e8a3bbbe5465127c512d048419d0db087e9fbf911a011576e06772c7d2ce5ebf2b61b27725564a78a6ce9185647b9b73934a505bbf707dab3d9032a71bd4fe6226410ca4257a881365f96e8d96f0f2c539dfc322b48fd79fb29245fa4dd8f5250bc0198c28c8f1b1d2009bfae471d738bd6dd3c0f29b3d413f5ba96fde2460e51db18b66ad6bee8c8046aa66a7af9eeaf73d632a5b302332ae10e170662785c498303916c5d3db7dec64483f9f55c706bc9e9c1c9f0edf97385517b402f443188670ac48d1998ea76e508ed0f254b535b879cc83ad2dc78c9ffadd28ac1ff98082c001bbae2532b570e23be66836d25fffd555bb0b208849dc9f7f536c832be679030be882c55a777ce0785820f4e41e593405d1214f2acdf8b4f302381da2572b625b2be40a3585bc9c30ccd27525068d70387d8c2cf204fa9f6866de875abcb2716d6509fbd73a99341acba06f0f65cd95c5552c9b6a58f87b1c9e7531886bfeb30071a8370fd688326b5011c7835a93831a67fa48e43033ec9904f1497626a8529be0dae098685f001b18a14649233f72d536f276350d710ec57fa58ee8a9eff35fa36e9d7249614e30b1045d4f164f1f9029b3ae4634a7163bca936f3e0bae01799a45ac8b1da7da27e3715e235eaab21c73dd965fc6630f1abb1c2dacbaeaa2d4dd7d095dfc1df46c87f9ce9059138187899e1fa12c682022ce714d89aac741ee0b037f8e34d6a5163b2dbc1921ac209796f4af27f2197b471e0849272a91f2a32749a034cca05520b762a32a22da0210e038c1485378720d1df8fcbdbb9c00d33fad3f150358f230478ddd492f4f4eba97ff7cece470d2bfd3e43e38991632ac06b6342ace010ac4263186affa5ec2bb00cc9986204476129aeeff96e2bfd0cbfc250e1c32bda2f153547cc3be80a557f141eecbdae505fe8072072a818150b1773c24963448a7e1568379d23e80ebe0f76a0444f988679e263cdc4b31c921629e4d49d12ff31d55af17998dc067235811af368ffceac71d99efe162bff0220283a80487e177ccd2299538dcf653f5307012afffe867dcedfa67c83e83ea309998998070f403a1c6fef6cdc6f3b1d7736959f4d8571cc398dd1605139fe88392afcd40444fa4d094036f4cce9fb6980403900336cbc7b0097ae176409e6d93fe1660b3659a579a9f0f85306d3a2a5060a1241f2adc469113af174e7b71e7d2fca9000d8002747c7065b232cea32d1169c82bb315df0d602a5f9cfd9713a2625c795b2938d6a9cdc58347466f81a1fc25a0f33bbae3ce114dcbb55d5c126850445d125c52e53cb7f3109295cc920ef2e931413021a0ba8ad610b363698090b14991a093f5542f2871a104ba928e59a2f0c63cf762afeae2c8fcba64b2747e62ebe4a77d0af3d3e2d05ca6dbcf2533f92ddcdc24702d67d9fc21a5d8a2ff5090ba6ab1f3fbded762d09022f059a122fdb75aff00cd688fa9c485c3bf0b0695f9f15b37afa92e0fcd88337d8a645b272e033776ae474690d0691a644b3e905bb1ad2f2ec80a4fe7e588978b1e11c52069cd046d4c5e5b3a8f73386e30be6fe193ffa82ad3a15fd832e55aa8266aa0d7d70fee48d980872646725349905f469430127439cbc5eb74cdd85aff5269f4a328e8c8e610dd5fe6ff661ebb0f551f7dc1090a1f2e5c0ce97f014e44bffbc02dfa57aa6dfc17d810194a4bec163dfff5f3dd91fe8311c6d6962d1fcf1931017a93042e41d0cac8929f5a44c4b176777bd029ce983a08da0fcff0293b2a5a11108513b7a82dfb52abdd6d753179ce9b18ab83f33d398d4f19af884b6e6dcae6f82aac92c7cf7e36c50d9f8d66350a1e18c948b73eb631564a1eb5a825470401c3c41d51409be2eff1430180617de248ef63ea017b4ff0bce4e843c5873dc06748644187e25a562e5105fc8f41bb5daa91333d66c57f545f3313de6997926c05454f8eb9b423eca2d0a0a3a560783b5f02a9860154f14102c0346bbaea850e3a4fcd9567af660a1b8f2136f4dbdb701d12e38b0b4c91abbd6e3a02510aad8882f2fa7a238024028624db7c5616d8c5abca496291e4456d0980dcc2b10c856583701e796dcf5cc919a4f4d29111a8b994dd03116c9f663a88e0d24c8d4e732fe0151755f13eff676beead78b5696ca85e9c53c64539975e83d1a50b8771600e58c192cd3c28f81fbfc8f673846a8efecf87a3557c2849ddd4ae76625799c240fb56ac075ceb820406a9244408607f9ce680e2929d3fa3a04fabb2785aec2bcbd287d4432e4e7e59a082e5e8b36bed08e001db8d02534c5468af81fcc19ee4303ce13897dd517d8cacdff65bfbf22428a412b18642510e714542b44ab892ee2a9014600e359d5f2b1645d314ce28136276a7518e659ba470bbd71ec24fe4fcb9cc3395d34668ad1100aabeb78ae5fd1b6da273680e926df2bde485e327e4deb37a883595a8bda57948aa7a7d86a5bb5f2d296a3cd6030839b1b135495cf342cd8152e9e2279535d8ba676b0d49f7e385dd708026eaf6944995df260685b28791dc09e77ac4ecf64af7023171fa2653ded414fad0e9ee87f9b24df60fffdfdcea23ce647822361b609c0a87e70b10fb2bb0b291976acfb90d69bcf92464d7b78c46875f52197243ba4320448b258ddeb84b0412c5ea5e94bbe4a6fe02cba122d75b5da83978c54f53270d38f5f4507f2eba9ddae49feed9174186e1b99d6337809d186b8dbeb8a86687d51cf9168e27f1047fb0eec332321243364319f5fa24e14dd223faf5ec04891083e3670c1d90134a7252695e84411747e15b8fcc87edc904807af29c24d02b99387c1edf61eaaa4dac61f01009f36c0a4e83e421fb04144405c34718b10078f5bcb7dc7cb228799b59bcf2787c4a6dd9a58bb6db2734a3d23153453ff712c89abe71d1aebb617ab392a9ae81c0100fd7bd3a315eebe47ff72369aacd32aad2a048d4d84fce13450b0f5245952d59aeb828159e9c26b2ba18797781ca87badbede3575d8d2406299c14dba1c8e5a2f00000000000000000100ed05b1a83eb82d579348ac909f20a2f4b84f54b548e9c72eb863c45b808c091d5767989ec3881db204bca70e9d0991a76c75f4d254fbc49bc45c78196cd6c325210080a0835b85a29f68b95d0e0339bd20ca08b2280240bc0741c33918a6a25c138601ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b0000000000000000068b155695cc01cb84051a921b2d7af4a2d571d9a581db37eda3107870d644a1a5bc6a5386ff4e8d589d673fd34c6b13f572d9fa308dfba669903a260d304178f70bb16e6a1b5b714290ec87832f086e3a375125a34d32814fb4a0063e5242fe684ff8b144c6e6aa918eb92d0070000000000000ad166c1957f0f7a34f9ba457eaa2c4f4f8857cea9e4f899035c1cc5ff37050b57bfff0bb62dd7b8813ed9db70b840a13231494c2056543df9fe09701912f01b0001004100c4678517dcb7ce02898a15e1d9f5d1c8b553e8732b369039e903989891a14efccc5c98e22d5168970aa8c850429ac680a20a4c7ebae477fe22aa14d34d92b513 \ No newline at end of file +04000000af7f0881ba7a0e96d3c3c69092f27cf0fe0944aeecd3d5ef27e90413668d2d501995c43e4ce9356caf6eb198529f595bff8ff3b1c35943bcc5ca6519a382e8d411f3c2c438a42ab898cff58ac4b6c2c3a97d4f3d7662b71f2c7905aaaa25201522254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025500ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000500000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000000000000000000001020dae0f6b69ef92d4f64c2e8e5de3c7f75d46817399ab9aa789bb36ff2e9083b77f2557946492cb76ef9a7606b1c7e80ea69eb1965d1a63df175c876560b1712710facb52f1d783eb8937eb8674bf5e64297e07a54fe2588fe12e71a2bd09a9b73f4a58aa3f8c718c4e0f42ddb0dba2652f947cc48a221f834dd58f92d0172716f68c76f9cb4faf8a7f21648c85328c39e0afae0c7c39616fc0d321a8233e48a8a9a9920f6feecd8f115ea0d378cd4c50f6e39b8e48b25e32758bc96f3db5f3d87652d1700430f0a611afc1131415dadbbcdcca83a0edaa0a9b1b4dbb768ce92131b3d98637a9bb58c3566adaeaa42ef767cda79297697f9556e55ebc56b94ab8077efd8deed2436a2ccb9188a3f887e2f3a507b83f8794622574d8ce3a0f5b29f6304e612e44b169aa41a1677ce4074f02618ac400bd24e1c464c7bdf5e6f8c968cc964c62b40a4db30c154babb39c1f9dcab495c5de6c3000a1eecae312595ffd40ca907adbb74f6d76a26192c7ce592f8206a0ee78727ad14b3fb76ce88c225cd6cfda207c8dcd1f90bedf39e7c6f7c1cd569a24b40ca1d243c5abab615475ccf885f7ac1bbb18cf259f9cc48f0546704d790281df2078059b45c25fcf2b989371602621ccd816ce2efedc8022020ba37581156dc18ec25bec578f86a3ede281a114bc18baa2c14931c9be402ae5b847a69c54bded786a02707f3abe602ea974e00d963eb5bc671bbc46793be751345cec2ca9fe409b0e97ad085e9479a99807aceeffa918e1063c6358cf17c01589cb1200d87e79c467637ea7ad32b7f930e8d2d237dc6423167b1a4347928eefaba5f610dc18a72f7821a7569b264837d6d03f442a378bfa38f4f15b9c6fff5c756d169e9c3f647f0a42134d83b5638467fe01c425e6b0addeeca03cccc47c4e8534810c9a2eec4ab3382393fb85c0592f73937aa7cf5231f7aa21550f64d7a026bf645c0537991b267796f7912b15ba325d24f8ce945d016384653024dd1b0e5a40c78026a94ce033f814e942736a0fd10716189fad4c004622262cfc7c023b1610dd1e24b9b65cc0f20fff8ae11e1e258758f95b1e65d67c49c7498605548b2ff77fe72cbcdd2184f2a03de7754bff3b7da0e8b62ff6df10f1b9fe6cdc3329a3ecc7dfdc86ff3324d83457ca4b44ee3de5e20b93fd534acbe5034027bd739fc603f68638a0527562e8a3996ac6d9c997ed037bb6c6bde8d29a0c449602e6fa5ab55c0a9e4e48897fa3208b0de92255439fa7aa00e334de9bed92fd5c5a9a1d41ceb2de0559ab64fc528c361aa76567dd09a77583e306f9e6da86fc8a9739b87c3e7c9f1bbc5266615a722c258ed9c3a494de1c61932d2b612ea3ea5c88b2e2ab6f93591ba63aa2423401aab0e0472333ec5fa360d06cb46b8d11d6fda67389046ca83f8156bcb705bc22cac98f446b727272d29faa4eb02af2bc4b4f1bbfc0d31058e75e631da028b07b7a24942db691c2b9dc46c294d59267025f0ad95bb9a1c52c085b85e499b1ae946fbf41a9210361d75719c588c5c983fe35fa17d7880c49eb35fb42969855fd30f77cb2ddbd872c4f90d76607aabeaa6914f2c349a9da896c6b102e7b17a82b8e6a51b44f29eeb8d0f76ddd3461d47d8a1667b0028aa4ec1c14a12934a337e21990f0d51ac92702af380f38874b5310d17d3a7dcdbdeaa7ebfd8618638ca2448c75cf2ca6499b5f62087a75e0b317ee735b0d2bd37e9ba09025df2a584631ee38abbc27bce96c2628b161209d6519610f55997145cf7d2c62e294e4ba595023b7c1aa297d080a4a0184c5f57e4db23dbc48d29923f49c3422f970ce220e183d80411733ec0771837243ea16306387f6f0ff3e38c88fe8b270287ecb4665079aa1d7b4bd81dc5274ebdfef7bbade1b38d7ea5a0c2e67b0a406202a50b23bfdf46d3ad7cc5853cac4f526f09673bd5d0eaecd32d740f8d0a2e803c457f484cdadfbebfbea9d855fe336a2cc958b8971c4e8a78927759097a31800cc29afae86cb4f80557f0bad1fa47204afd3575121f26462e2a0b7541e39634fde8ad3962ec1488a680babd379aef88b4369e1ee477442494dbe5b6504714c037f4f8f9a53862cb4d7a1370bf65ea317852bf432b4edfe6ac2b7386e0828e1753d2c44ee6aedc06c4e5041fe0d8627d32f7afb6f9da79c3ca35b0bcdd0a522ca7f25d85e2c32c164e4089192e9e28365ae7eed8e34113270ebf8885b34dfa8eaaddb5470d29a04cc0d332434923a9629e56bcce61620b2adb2566b933199d35da08c785dbdfcd6ce0c6a12d38b9567a5e762b184beff3b2e527ed803c157d1d8ba1065d7b39f4a7cb1cb3c53da778fa8998c6eec11e995520773a8c6eef5ae030b2e6ae4d0be533ab45fd0c31d41135c119ef0ecb307e4e4f84d46511eaba518e0910e29c957a57efdc72573aadb8bac36ec3267f9210000000000fde01c188e377136780dfdbe3c75749e3bf53da48b46976c017a66364901a285b1831336d2998204a418553339d6364740ded8308a980219b0ffed2907294d91fec112c22ce8ebdcde3041f1125ea56ebfcff90b662f0d6e964d1dfeec3afdc11f593d51ced59eb2920a9870cb1c37d9f4fcecc6bd60b2bb9d8b51a5318a102da92c11359f2c2f38c91b13183d41568fd05f49002dd77e0d7dda2311db5da850b5628a946d4137d1e951c472cfbb7a848a9a63c62cf9bd06badffd005c1394e13aa5170a51b759cba5533bb20b900830910ab476a4acdb0752905795251d10146f7105b78210f2e942f5c74aceb95b1efb0c6c3093a199a6aae1cb9c104f85a89c3ea5e56ad1c14925cd5b8ee61da55b3de7814648d1d36b422799d03a06f8fb8354a995a988d993ed46462b0548f96d611ecae494d0d5e1ffa060bd0d48afdcdf242966642c57e5d16b6bf54c184c288f2b594da2fb9e04f3c352bf10295d49a3c69319938e84f7ac582b06f7d3256a61cbcccc9adbf4245e0716255a34fd7c71c80a67c77f50784e79ca6eb281275823309a6c7bf5babc0ea0d5943837f96e0dd00cb2bf73d8300f43c9c3f02e6dba291dc30204cf0bee2841995600162376c8372046f8dac7b3f32adc3206498f66907a0482ff70254832c4bb6e1497ab562f89bf20c82bcd2746136ec25ffa0bd1c62c8f44bfb726ff7c49aed573058ab054989b65dc4a6c4cea72584371ceb4d4ef9e56adb6d1bb438d1940b7df1eaed3680711907a9a5c89d3cc2ba7093dabd32bd51c0c0903fbfc0a47847046d5d027b0c30693d46f16db4f24b582c2d7adeb493d5a4f6e88a3b858a22e5b7ab2d3000202a71902986d3784fcfa40ce43c293f48eca6d572a6e3e97a459fc9e3b1e6e0091befb7e1e81998e5b554bff5a98cdad3f62cc63806ff620073d0f91fbdcb0feeea41726dee4d1801c1e8ff87a5be515cd6594173de2fa03932465b2a62905c3ab25830816877924d089b1a508f5490446a0b0b2d3f73e1d6f383ec20d1ce16e312cc73afaeab770aebf479e5f19f7bd1a497f1e891598406258c9181ee7d56f93be0de5d8506b447d5378fb9cb004aaad64a61cf8e993d1e9426f81b614b8a8d42a1d56cd805c86c75afa8c0f4143cbc5df71e61dffee9d5098e6cb83c85d7e41b587bb9412f5606b7db04c6e88d94ce52813f61663544cf6f3ed0b59314bbe422dc0380287fd79af38ea59028812d7ba6680cfd50662bca04151f8692677fe53a305a2e3d22678975d1aec6e9b16d5d10b91e8f555dd5bfec61a2cefbbd811b7a7cd2709d4398c588e0ae78263a7196f917f6b834a1b5251143d92ed10e32966b8b4aa199e4efff6916bc688b17b0c7ab594e1a17a92b62b37cf7f4b44856c5b89cf96414f34bc7acb5a6e5d70c744197f3a4828a0ed2564c3eaaa18e3cc50a4bd365a2a5e00662c905994ad97faf959773409f88d76f8f76b628a621cf1f0901e5091a566940c07044cd4499570872b036c526ad23c24c2276668424f9d97e1016c08ffc320e0f6ee1a0f3c1ef9f5ee7583a0d85b843f380399bfc17178bbf3915805b6cd916f3795762b5213377d5cfee4d3dd572041c7137cffaec61a13100a39751129e6c1376c3e53394cbfba46740ed2a5ccdd475bff0058fc788121bc26d7bb59d5e3fa8e06bae50796aaca2ddd550ee1aaba926fb2b901b0211284203dba4a5cc7ff6fdd9e023836cd54f8383041716e8ba5c620114fba66aab5638e346e4d3fbca3562cbfc0bf3ce75ff8402d3e4e5eac269d296e83a2b7136296dc9597e840bd8435c5df11bc67c485b8907b144bd647cf3d124f712f8a6149dee99cc2dc86cc86b9608ba6d4e9f4d9e09e4b2401141a0a0b842db652e61b8a9a27b2e0af0b609515a0546633e986386ec0d76d297fd4139fabdedb91e30b85016f3db57df2da16ca7f460f9a16f556898ec9cd8a2289fe081cb92d0d4b04316edc3b640d2221d6bf17aff815b9b0d688913feedb0c600bb424bf2c9ebacc4db4529cf6bba509d85d7bbd8b674f2232211687c9f42745ac8269e5905f1ac3a509c52fdb460f68331a5b0bc927fe3196178603b533511f4535748081f6d425df18bfaa941d8abe449b48b5379179dad980fb1b019e8c2c9dc9d54cd0e6c3fd98ba4336a7a7899d5cf8c44f3cc04c9e91eef7f5b6f0222cd317b980aa791fe0fd827baad7316db7ba07f2626cde56e8cc7d1892aa0ea7af0130c6430df01e69c12a4c12e367ffab4bbd8d14f156dfc215a45cc4269e158c7a65a9be76a488890648f69ac60d7fab0b7eb2f639c051e89e4e6026c79d4542b9fdd169ae78a5cae801f81e6870b9912917ec53367a1466695d10cba3293ed61f7f3a0b142b0bd249fcd3acf7ba2d4f60d2861aee378542348d7a491ab8f3ccc7bb493504a472d124298a1c8d6b5abc370cea464da6bd6278d9e50c54314cc04fd9bc05f573faf98694420b71e5dfbee53bbcd1e7361325ec0b11dfb438df925b3009aa3e613e5c382bea27b6c10c210540826c5e789c84ef45b7f352b012416c48c800a5c631dc0739143e75f2623722354380f7e503928314d28dac7082972b76f171260b75cc44e8e31ea09f319d183b487929e34f3bd6414743517c640f7e3016ee92cf91d2b4cf631c232a2264fb6d7c631f9ffcd774cec0060ee9fdc3d913a6f10a236029a89f3b116f511d57ff50933340d1cb645b2522a7a1c4d12b6087d29d2c1a56f2776bd92f20520f7cd5bdecc055ca85312d59fb2d9fc1b8d48da64906c415ad32a2b0ba07d7dd2d8df84d739786167fe9eb5ac42534317d8ec6de56830440179bc7007a0560ad11c153f760576de6c3236a34ca5a88f54271cb23d24b0d60b5ec9e733f1b514ae64a593d91254c7b128ae3b9da73a217fc68133cca8bd5841361cd2fb428a1756a9ccd0d728017acd1cd25988f3467d05bd98b3e3bdae39b696aef2fad097b7dc664229693b7b1e50500d7493e7ad7b286c32a59c583e959e9b9047b1f190b84fa1f2edbbca09a7d9dd36958f089e6ed849a3ff1535fa16eeca51d066229239049b78efa90d3210749285d251184b53ffadb50b05e6f477eb7a899a7533622c64a22f79c6992b01d358ad410644d355f5a8ad5446c58e93217719d28362fe1311e861cc788d00dd0dc1ad027e24ec8e5c0484d1a64fa6418694818114510b75a310b80be2dd00fba25471e8be0a8dd580114ce97aa41fb09b89622c24423821b57fd9bdf6f352e642f11d55d3d7e0ea6fb087f88c1f101a618b3909e123922ce03a66cdff7ced587d7dad1bba0f1d28dd2976f7c63018ff0d6a41b1e6910e0778dc679175a3058edeaadb6d3b61d51e12ddcc612dcf63fe4b77c3e67f92841af883b5821916955734c9b13604f2686aa92a2a7e65fd4599328918fda86022a7e6dea0ba6227a54e4135bb786fea3b12b5e51837073ed8a94a8b9dc65e431f6f87fceac236da4de63a3184d3eb26234a447f27034e4261b4513b666c848086ff04e8bf3bae807c4b07e2c474b4efbbe456eb73d1896609d068a168a811422941264a4620919e43b0ba752ed47595876ef533e9b1ea4a19fdb48baf2afb401d629a6d7575a2cb2f2fae94e5f4b496be23d42075048b518658caf35112ba800eed77ade00462d013cc9847ee0b9fc7d0f9fa248ce0e6dab49a7b86c5ccb0b0909c41c11ea5cb67b90bac37ec93d1b73d1e9bb7f8199126b825a80dd04497f1c6ea27d84d08146134062c03a4959f7f2aceac748b77d2b6df5c55feec6511625adf18900359832ed074c8bfb23db24aeba790ce905a1341718d43a098f923712fb6c8deccf274138268a1917e674c83c21b37193e01ab90bee904aafa55104178f2d3e77e43cb02d34f03178f07b8970a56260964db4646afc8d7c437052663a5116804fcf70c2f420a443e32c6d97d63cb2f3a1c2aa394cb7ae4814ee03c115701e58caa281402eddf60ceed479cda61fe221ff83c58af5357da9bf62cd8821d79ec69c8605c75de6d111dfe875fb31f3b2410326e4d6b407d1a1b827ca9c3cfe8679d77b4ca33f28e69989968350ba670b96cb0a4f4974bd231d1b79baa209e85e0af05c80c56db027e4c646dc88c032bb6fb928b079cb6acf97b51818990523f89fd8aa7261379186ac0e1edc4b0398767c8a4c508045567f81a16561fc11048ab7959969d241407c38283148402597880904db5223cb765e71f668c5182ca947c89b9eecb2afb08e953d90d7c3bdbd978385d661599406243c94b18d2128c24c0b1eebee47bc674fbd75dea65b493cf43c0a582668b294d059e03555962f1540896bdaa7e122ef345c40e5824c538fa0b9473cd34870ee4eaa8e2470cf3ddec01c46097c1a80327afe723bd63b037c1f2b3d8c53246e27b80149ba67f703838ca689dd8c4b103694197b162eb8b13c657a5d2f5972ef9650624ff631170b3813a9ffccd2bda18fb29e3b1686ed313c0ad18a93a40b8607a791384d50e4149c7ad441f4a7c40ad140a2c8d0c80e5355918836c1b493511a5637fde687ef211ab1ad66cce847626208f9d46e3751e4ce66c5888417c8f74d3250a15c1ac234acc49ce674ad2caa17ca1fbe02d13e2cbb53c91139da0c139e6bae21e6298e308bfe05c09c8c0b9b66eab09afedc26683a95d7f69c1d52c12aec4ea73a86f107b2e0c6861663edd25500cef4a3834c8144aa8434091ec6a9a8c17ecac51aa53698aabcedfa598d09e9a85bbb5ceaaa0b19f4614da7d74b94069b469eb09cd805b4876cccea363b8f9ea84b491fa6adefb15bcd753cb935bc5c40bb6933ddc438303102c2f87ad7ca761c18b177f01462be95c43845215035d042b6c8b76fc53ad48f161bc2def2348fe5c63ae39e9b28a5532168e4c1dae52f4603c5f5feb60a66151fff1fd3497c09ee0696a60fc5a9691b348f9e2f75d6e3064532041d79034eab7e6b2b6fb35a47647fd18544e63f3377b91015473da13609b7230a88602dcc86496ead8765f08988d4c12f1477932ee6441ae2ba0c9337e05333ba590136b321fa74c95957365a86a68beb217e43e5abc61b0a36110ebdf334d0282dd12e6aa5124234247007fc46d97bc645fc18f10c91ce5fcec5d53e6631b031e4160eb666d90398b2ccc845e3bd397be544d01c63379d720fb90278f160b2c31fa90151ecff86d4a69f889932546689dec81b54416d19e9a788c78c33ce3ca806743ae1cae0f66a43cd18bd8c43bedae3b2e2dd3c12fd7fa709d7080b7d7567fbb63cb49e3ccb133af45e4ea1195fd0d82aaa613e18d78e140b3438ed98ce39ce8f3814de6494b7807198d757c0a4e285df138908310cb71b63a665793afd9e9f8d1c72f5e453b6695369cc4137317a1a84648469f0dae399b5658471dee594123d076a232883b31baa50a507445baa16b40f678c421cdf377e469d16f8a1e529933c66847b9598f3920c8ed6b50a598fa798616919b5f4d902e986955dbc1792f224932a0cdd8310b1b0d272d722a99b1c4879a39aa0b1ea5e0a1704cf2695863a089f57e7a417a7e58679f27822f57a0331d1a95d56e6d9cbe1b088f89ea98b390534e77fac03bf1664b67a6a580ee418acc34962defcd92cb2a53e8a8557ec0b25eb8403c0cc2a92502d347641b32a1a0c98ed28aae6a9e0f8aea0fb641aa2393c22f41be5c7061affecdee0356232735d4bb49b0a1dbadbdf89628bbf2f77e836c350d7ccc396b10ec6ad5d854a98644e574898ff3c3aa94cffa2b6e9f89a5436a62c4edf93785ee45772c31a0eb5741fc0092226921d13dd79c41f0c2bbe8231956d7f0579d80a301998b525dcadae007786a5fcf45d5fd2b91609cc1d84963b051202d210ce17bbbfda528bbd645da09f5a7bfa7007315b42753390b8836903022916292a760c3443fb17b22830b2229d79280e26b4824bc6c5d7721b70093149434f6e3ddd43f2e3bc65aa38fe53407bbbded59a78978e1ec7bcf66d59ac04e2f0bafc89cdabb25c47b9790bbd6048c711191a1617842ceee0acdb3bf9d20ced32cadafe375c00a1e6725295d84e97ab3be1c29d1d82efaaa882a54ddeab0856e66e15a568c6236a147d6d83a2d432bfa94e04f82420933ead871c3d76fc39c3fea133fd5a7de2498b06946939efe1d77866768d238b7bd3b402c66cadf011269e940f723964f60d60759211c83e95d9fc09c1c1ab486e933d6ed544504f302d26cabfaf0a167861a81c789ab6d8aa403b5d960d61d5bb156895588f914a1a46f7be67a7076a6c0c5b02f67347f927d3e53db46b57db56753bc4d666f0b91933b150b39144e6aa105913306758bb56788562dc655ee4dcded65ef58a91d60ed3ae805c6290d5a8f07c91f461f0cb6b32d8bd6bfa24001edc51f960d07cc22ee32a22163e460c644f65688460dfc12bf411f2cf83ce3acab2698e5ac0093639d233c6c6dc5a1d8906909d342eca52bc76077139f14d5de7d0290cc4b6d9b42ccf0a2ef187dc8e88e252b7159e0b4bd6d21caed0cfbf59dc393e00bb45dab2228ebe2175a43d40b98a4b39129473495b2df4d40233c48528a007a7e180c72716b7bd21c3331d6078f06977a311b2399e41395d0e8211197e133179533a24d420b7e4da12281161e6727c38a99762165bd5a31c285e6beac56852b218c8b96532a45c2e9ff6cf4beabb22a9a2468352456441bb6a1864af1605a28e16175b3b17267db8f0215c708a6d53d6dd3a7ec2cdcea31d8e1f09e9f859873282fe607f29959e06efdaf749120a72145801564da1c874e119014b1b7d84de381fc0dfb63feca99c59154a34d7812e8e4de28f0afc73cf23e10ead4bbb52439757646b991ebd11fbe848029f17b0086828e9c881c6360471057ded13a4658f7a908205b800169023f653e5acaf73472332576ef9335e3aa446afd29106d184edade5a3c2049bcc9ce1cab5303fb398bcb790a94854a8e8b50a96f81d447ab5941dd00ccd040eb3a4c3aba9257fbf381fcaf9447577213550bea19d24ac0f402c6f815f812b48eb31842c7b256a59cf1d5f728157cced8a7b86f1e5f867ec33bc2700b81e23d1c076264c9b57288f1d094a86ee64dbbb9581f8df00ed449278080e9dc9cc2fc2edc65a3bcb1b89a260399483d6af3ffcbc60a5af5f4a4a2e5e5d6038eff308bc63a48c0b9c1cb84c410929fbc5e164141264e30bbce50d2decb8aba967e21e8e8b5da53616a7d01afee1671a69a7893e49e6d933506ec4f6862c71dc397c167222543aa96fce5c2f12e2a296c18786648027bc59b8b6d1310d5d386115e123611cfcc7fcc41ddca1e999b923e9cab5090fac1f5196fd789595c689c660e62f34600c86d1069fe2e5178f72a4f0698b2fd7e4f7ca4ed08a5fcbff29599e011af73a5c98ca62787fdb7fc8eec0c653d4050866a501b237bd51c1f99d375e220ef6df4e3de65b789efc49ccad76bb521f3fdfd2a8aea0ca472d248ef48d2b9b18983cf1e0f1fcc870fdebb73df9a5bc57822cd318aa97e16f7ffdf87cd866071b6d46531872fbaa8688cd365fd503725e3d2d26bd0e2a5d9b7b628abeae09983079ef510098e1ea4f19921f53ba4dcf11eaab49bb27cfef03061258403f770c0349c0d1e63c9f5510df94916d497373235eda172b700a74fd03d1f6c4037a5e26205d4f0ffa4ecfca3e9a5cefd5bb10961910f521a9a7644615233696d77be22667564f128b83382dc02d9dca64735fbdffc71edc2893fb2456028d969cb48238fc4a89f485ea7f75b0e093b92e15713f45e32d8847a0d95c2ac67ba015b9a63c6ac30ec42fa4eb67faf137b4aeb1f08d0cf7f6e54c888941d4cd68808b018017bc9d63d2b73a96010eb766c3b3805fa11b59281edee7f5469162b2fb3d9aac2c36868ebd44dbbbf269d6d41a903cf16f1a5e865859df2602d61eafcde89ec822e23e1c541197fc23ebe6ef7da65aec5881a98e4fe8b4eb121519aba3bd7dd3117c9d72d3ec236edff272bec449343fd75c7fa9ade9684a3f3ee9ec7a647f5504cd2333c3c48e72fb9d750d3d38203f93dcd07499111c3aba25722d67266e0f103187cad58a2dce3aac9ed4b2bac919b65d9600c1d6b9e9ab0adb93cd359b283b65cb21dd79df971c53072927ad4b7ec26344143f04e9026a8c935c619d10833295949348693c6c850303800a82b09c549da3e024c11a4e7d3ec9e590fc92911d93ffa57b9c3b8319add04d2b08d7b0b3ef52962701c5215d670922b26f3457193dcfee7d6cd48ba0da67a08be6b234c34842b0a1919ebe472dbb63d61a6dd22093095aac6f66cf4b4681b169819e0c546266a9e5cb59a444121c597cb37b5728d6ad97a46f062d362cb2554af6c2d38e1c89dbe8128514b18e128a2c5d6458087b80d1ba859b051409d3c81734abc6b38fff1fa8fbf2ccb3d50afef7ecfc7a3ba476f22d4f1504550cf15c9ecf04bde80f4a6b78ac9c6c7b1c818e47b3a24315cd569bef09aebeabf440aa58f696a779a4ad57c5d139bb51d730db1c950b38367e691da2883c76358b953b9e3f1ddfa4a8a43c24f3bc9564bc7885512d2ed41ba876914b60ef4f2f430c4de3e05dd476c88b90b6e4ed301521887f2be0773b015da86f753bb42a1208bcc6ce91109d0b2f867f4c261ff8ee81cc688d11cb9e049e371319fcae9bca3d71ed72e15ebff6dce15ee19d98da8cfb71374e442ceb23a532841bb92cd637bc52cd46a4d6273f4d5ca12e7861cf43d002801aeb2e841a29db4b598489d14cd8ded30b7b5f9b514ab259bd9e4b0d78ab7d9ce3e13565277a76e1e221a0a0669240741c37a680d61fb11499ea422b798f29ddd510390e3cc8935420a52021df741ef1cbf9029952e595e9747624b6949cbb55664e46af0e465ba3a64e240858ce9ae63a8e3057ec26fbb6e80fcd48ef64528cefb2218a1cacbfe0c5ab04b988e6466fd74dcf5b216de799737c4bf6a30ecf0cb3645c830f3b61ea9e1e7a43663d214fe89fddb8865829a013c1eaa389f9792623d061bd3a7dc45902d7927d496cd12451f010926835f24da4323366514cb435cf35309b381125863c8d9a392cc3db81951aa24b17e5e9f5e134a627c526abf4be7cc33b3df0e18e2e9d8a1703f8847d0d8285b11120bb54535115f373628d6f88e49ddc2e523c8548da55ca656045e38c8227dea861f7a9fb5a7c7026fd47f74e9662c015331791732c29d5849337ede4aad274b4106fb34a1256095f443f771a8eb64c14074c332ec6da0f6e920f07d237c70118ebe5347246a5999519c32289d039a6123f8892c5f00443e345cdfb45f1b7fe28ffb2b6a5def67f235920bcfd10bf11b58a6c6bd01b8f700c0ac4f361513258e1093eda26bb055bff436aee4e2df8cb9d25c7b7ca7203e908ba9da64119921813c35984567c5a045f4eece12b5803a6152ef624c2938d025fef0c493444daec4bedf781e7a59d517e1fbd36fdf1355829e5326d3648a6e393247d5a57b041e49fef691af1408fda20ba16d23c8b4db28d7103333fda1a8f2397ff68796d9d940ca11969d8accc76c9e4556d92dc68049c56f7867e550c193d1e1ca8dacdae77d857b460764f66998462d8f29cfeff131afbc6e58bb78db441634d67528a9b2ea82cfc0757bf421bca1ad2b549851f50355e7f04ec9a4a8e07e42ff38034378f64b276753c22ab2f1310619b95c0a870b654a3d17ac6f07782a259fd471c6b21e6566cbd52d1c4c501345c98b8600e88a1d37b8a60f4bd246b700797fb2c072fa44c299bffb965605b0a97b5430a8c2f8fd55c2d38c45410f834757167d59f7aff3ef8e44c356f1b8396d15bd75b986aa63a5d94301b815bcc496cb54316f0ab48e7fd488227aaa8e0975c402113af080853b891129b1fdd7b0c02c768dc39afcf3409a528c91db70e62980b4e9226748147716b4ff70708575c15d7de7119ef30dfa2f98de797efeda0230db6fb75e60cd2c4f85bf875a01e20c751a1beec240cceaa6bd92aff8f640ba699e3a9e15193a1320880aa11a1048c7a6c5696bf351cb69739fb2b26643ee6cb141d5458b40da6264cd9d268e96d4f8b55d876559992e5a454a3154b5a44554f9bb19dd678264f308d3f7d9aa817275a9a092601f07498ffa7a8d0125cee8a7c563c3cb74231fcc9ef586d6bd8d1da774b27267cc2f1e73e4f3e6b3cacc1d0c80da5db15dca5bbc3046b259a830045ea6ac6c7e16321a74342f01a236c43b5fe13c93a33e736f2905452925c0a42816ddb5dd33593071ae71ee87f4e8ab116770f7bf6d3a2860594ab1a30581ccdbb7d9d8ba64f31e8c1635f6bf88c767a265338d0f768b1a9efc89596641d00552ec434495d878197b53932decc451c5cc03d915248d6390da3bf7eb6c68f92ec7b23a7ead609052ba972d649d2b2c8fae788d603674021290100bdd47536e0e9ddd0ed7f6ca78696fdc9354cd66f719116f2d6c79fc16c78ac1cfeccc36ee57eabe610199ddb57a5610d2c57c8ad584f85ea587dfa9e1045112f0100a10624b94bc7e5897857d9c9c6bd7db34f49999ae691af7cae1ec1091934b118989f88cf0648aa0165647cae3943ce67f43a5457b79a9853f4c279a79c87ff1c00000000000000000100a63ee2baec827e97d5bf72d553e9cb96425d9511b028d4dbb9d1bbbe99728b3780afe98c73ac2dcb34a366c34d5a2d07cc1d9e6918791fbacdd81456a3e50e072100333473f16d482c8725d51b4cef4e26ec24b4681f9ef8523b160fc0880c6bc1cd01ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b00000000000000000ce51b2430a969dcca7f33fc6e3b70e812a62b8c2682661909aaa36c5d956fe3fd69d8d271e71fd393e3b660615f3607584494362271c529492f4a4db06d138f7db82ac459e02256acaf1a8ccac2c4b5c581c3f178dcc4b047e32fa1a7e751bdcfc6f42d46381f89dedaa04d007000000000000508bfc11533594e458bf22848099fc56c00d4c279d02b08201381ae3041c382ebfc9445172a0430d3645bcf95d72d3ba53ccec0f46904d9dcc1a82c3531256a30001004100f1d963f532671dbecd062b0cf1d37a7eeaff9eb90361e021d1f766fca0fc6c1ec38f518dbb2b2d70d03c87cb9bd87034b3334c972d27d1a5a34f7181a9772081 \ No newline at end of file diff --git a/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs index ee597f38e67..93058bd7ff9 100644 --- a/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs +++ b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs @@ -1,23 +1,86 @@ -//! OrchardZSA test vectors +//! OrchardZSA workflow test blocks + +#![allow(missing_docs)] use hex::FromHex; use lazy_static::lazy_static; +/// Represents a serialized block and its validity status. +pub struct OrchardWorkflowBlock { + /// Block height. + pub height: u32, + /// Serialized byte data of the block. + pub bytes: &'static [u8], + /// Indicates whether the block is valid. + pub is_valid: bool, +} + +fn decode_bytes(hex: &str) -> Vec { + >::from_hex(hex.trim()).expect("Block bytes are in valid hex representation") +} + lazy_static! { -/// Test blocks for a Zcash Shielded Assets (ZSA) workflow. -/// The sequence demonstrates issuing, transferring and burning a custom -/// asset, then finalising the issuance and attempting an extra issue. -pub static ref ORCHARD_ZSA_WORKFLOW_BLOCKS: [Vec; 5] = [ + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_0_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-0-genesis.txt")); + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_1_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-1.txt")); + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_2_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-2.txt")); + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_3_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-3.txt")); + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_4_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-4.txt")); + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCK_5_BYTES: Vec = + decode_bytes(include_str!("orchard-zsa-workflow-block-5.txt")); + + /// Test blocks for a Zcash Shielded Assets (ZSA) workflow. + /// The sequence demonstrates issuing, transferring, and burning a custom + /// asset, then finalizing the issuance and attempting an extra issue. + /// + /// Block 0 is the Regtest genesis block, copied from + /// `zebra-chain/src/block/genesis/block-regtest-0-000-000.txt`. + /// The remaining workflow blocks were generated using `zcash_tx_tool` + pub static ref ORCHARD_ZSA_WORKFLOW_BLOCKS: Vec = vec![ + // Genesis + OrchardWorkflowBlock { + height: 0, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_0_BYTES.as_slice(), + is_valid: true + }, + // Issue: 1000 - include_str!("orchard-zsa-workflow-block-1.txt").trim(), + OrchardWorkflowBlock { + height: 1, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_1_BYTES.as_slice(), + is_valid: true + }, + // Transfer - include_str!("orchard-zsa-workflow-block-2.txt").trim(), + OrchardWorkflowBlock { + height: 2, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_2_BYTES.as_slice(), + is_valid: true + }, + // Burn: 7, Burn: 2 - include_str!("orchard-zsa-workflow-block-3.txt").trim(), + OrchardWorkflowBlock { + height: 3, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_3_BYTES.as_slice(), + is_valid: true + }, + // Issue: finalize - include_str!("orchard-zsa-workflow-block-4.txt").trim(), + OrchardWorkflowBlock { + height: 4, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_4_BYTES.as_slice(), + is_valid: true + }, + // Try to issue: 2000 - include_str!("orchard-zsa-workflow-block-5.txt").trim(), - ] - .map(|hex| >::from_hex(hex).expect("Block bytes are in valid hex representation")); + OrchardWorkflowBlock { + height: 5, + bytes: ORCHARD_ZSA_WORKFLOW_BLOCK_5_BYTES.as_slice(), + is_valid: false + }, + ]; }