diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7ec37bf --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +name: Publish release image + +on: + release: + types: [published] + +jobs: + publish: + name: Publish service image to GHCR + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Check out release tag + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: refs/tags/${{ github.event.release.tag_name }} + persist-credentials: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 + + - name: Log in to GHCR + uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate image tags and labels + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: ghcr.io/${{ github.repository }} + flavor: latest=false + tags: | + type=raw,value=${{ github.event.release.tag_name }} + type=raw,value=latest,enable=${{ !github.event.release.prerelease }} + + - name: Build and publish image + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12bc0a8..4ea6a0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,10 @@ Contributions of all kinds are welcome — bug reports, documentation improvemen The workspace is organised into focused crates. Keep changes scoped to the appropriate crate and discuss cross-crate changes in an issue first. -- [`core`](core/README.md): Simplicity execution engine and Bitcoin/Elements environments. -- [`service`](service/README.md): Axum HTTP service exposing the sign/PSBT, sign/PSET, and key-tweak endpoints. -- [`cli`](cli/README.md): `clap`-based CLI for interacting with the service and accessing ecosystem utilities. +- [`policy`](policy/README.md): Portable `bitcoin-policy-v1` registry and verified context. +- [`core`](core/README.md): Policy execution adapter and shared Bitcoin/Elements primitives. +- [`service`](service/README.md): Axum HTTP service exposing separate Bitcoin/Liquid policy APIs and the version endpoint. +- [`cli`](cli/README.md): Primary `policy` workflow and key generation. ## Issues @@ -47,14 +48,13 @@ The service reads `config.toml` from the current directory by default. You can p cargo run --quiet -- start --config path/to/your/config.toml ``` -To exercise CLI commands against the running service, use the demo scripts: +To exercise the policy CLI against a matching Testnet4 service, follow the +[demo's artifact requirements](cli/README.md#demo): ```bash -# Bitcoin regtest demo -cd cli && ./scripts/demo_btc.sh - -# Elements / Liquid testnet demo -cd cli && ./scripts/demo_elements.sh +./cli/scripts/demo_btc.sh --help +# With reviewed policy artifacts configured: +./cli/scripts/demo_btc.sh review ``` ## Docker diff --git a/Cargo.lock b/Cargo.lock index ca87534..0b87c40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" version = "0.6.21" @@ -89,10 +80,10 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.4.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.8.1", + "hyper", "hyper-util", "itoa", "matchit", @@ -104,7 +95,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -120,12 +111,12 @@ checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", - "http 1.4.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -174,6 +165,7 @@ dependencies = [ "hex-conservative 0.2.2", "hex_lit", "secp256k1", + "serde", ] [[package]] @@ -221,6 +213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ "bitcoin-consensus-encoding", + "serde", ] [[package]] @@ -231,6 +224,7 @@ checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", "hex-conservative 0.2.2", + "serde", ] [[package]] @@ -242,6 +236,7 @@ dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals 0.6.0", "hex-conservative 1.3.0", + "serde", ] [[package]] @@ -270,9 +265,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" @@ -342,6 +337,7 @@ dependencies = [ "serde", "serde_json", "simplicity-lang", + "simplicity-unchained-policy", ] [[package]] @@ -360,6 +356,7 @@ dependencies = [ "jet_plugin_abi", "script_jets", "simplicity-lang", + "simplicity-unchained-policy", "thiserror", ] @@ -379,41 +376,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -461,6 +423,8 @@ dependencies = [ "bitcoin_hashes 1.2.0", "hex-conservative 1.3.0", "secp256k1-zkp", + "serde", + "serde_json", ] [[package]] @@ -500,12 +464,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foreign-types" version = "0.3.2" @@ -537,6 +495,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -571,6 +530,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -609,25 +569,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" -[[package]] -name = "h2" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.16.1" @@ -676,17 +617,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.4.0" @@ -697,17 +627,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -715,7 +634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http", ] [[package]] @@ -726,8 +645,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -743,30 +662,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.8.1" @@ -777,8 +672,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "http 1.4.0", - "http-body 1.0.1", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -786,19 +681,23 @@ dependencies = [ "pin-utils", "smallvec", "tokio", + "want", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.32", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -807,14 +706,24 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ + "base64 0.22.1", "bytes", + "futures-channel", "futures-core", - "http 1.4.0", - "http-body 1.0.1", - "hyper 1.8.1", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", + "system-configuration", "tokio", "tower-service", + "tracing", + "windows-registry", ] [[package]] @@ -898,12 +807,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "1.1.0" @@ -1163,28 +1066,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.103" @@ -1239,73 +1120,44 @@ dependencies = [ "getrandom 0.2.16", ] -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", + "sync_wrapper", "tokio", "tokio-native-tls", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", ] [[package]] @@ -1322,12 +1174,12 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls-pki-types" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ - "base64 0.21.7", + "zeroize", ] [[package]] @@ -1367,6 +1219,7 @@ dependencies = [ "bitcoin_hashes 0.14.101", "rand", "secp256k1-sys", + "serde", ] [[package]] @@ -1388,6 +1241,7 @@ dependencies = [ "rand", "secp256k1", "secp256k1-zkp-sys", + "serde", ] [[package]] @@ -1507,11 +1361,12 @@ dependencies = [ "clap", "core", "hex", - "http-body 1.0.1", + "http-body", "log", "serde", "serde_json", "simplicity-lang", + "simplicity-unchained-policy", "tempfile", "thiserror", "tokio", @@ -1519,7 +1374,6 @@ dependencies = [ "tower", "tracing", "tracing-subscriber", - "validator", ] [[package]] @@ -1564,6 +1418,18 @@ dependencies = [ "cc", ] +[[package]] +name = "simplicity-unchained-policy" +version = "0.1.0" +dependencies = [ + "elements", + "hex", + "serde", + "serde_json", + "simplicity-lang", + "thiserror", +] + [[package]] name = "slab" version = "0.4.11" @@ -1576,16 +1442,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.1" @@ -1619,17 +1475,14 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -1725,7 +1578,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.6.1", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1751,19 +1604,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml" version = "0.9.10+spec-1.1.0" @@ -1812,13 +1652,31 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1925,36 +1783,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "validator" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa" -dependencies = [ - "idna", - "once_cell", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive", -] - -[[package]] -name = "validator_derive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" -dependencies = [ - "darling", - "once_cell", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "valuable" version = "0.1.1" @@ -2088,21 +1916,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-registry" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-targets 0.48.5", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -2111,7 +1950,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -2123,37 +1962,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - [[package]] name = "windows-targets" version = "0.53.5" @@ -2161,148 +1969,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "windows_x86_64_msvc" version = "0.53.1" @@ -2315,16 +2033,6 @@ version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.46.0" @@ -2401,6 +2109,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 0e97841..c989421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "cli", "core", "jet_plugin_abi", + "policy", "plugin_tests/script_jets", "service", ] diff --git a/Dockerfile b/Dockerfile index 0800c54..16c0279 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY Cargo.toml Cargo.lock rust-toolchain.toml ./ # Copy all workspace members (required by Cargo workspace) COPY cli ./cli COPY core ./core +COPY policy ./policy COPY service/Cargo.toml ./service/Cargo.toml COPY service/src ./service/src COPY jet_plugin_abi ./jet_plugin_abi diff --git a/README.md b/README.md index a3b0750..7b0c9f8 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,50 @@ # Simplicity Unchained (Prototype Phase) -Simplicity Unchained brings [Simplicity](https://simplicity-lang.org/) to Bitcoin networks. It acts as an oracle that executes Simplicity programs and, on success, produces side effects such as co-signing Bitcoin transactions, triggering events, or performing any other action a server is capable of. +Simplicity Unchained brings [Simplicity](https://simplicity-lang.org/) to Bitcoin networks. It acts as an off-chain oracle that executes a committed Simplicity policy and, on success, co-signs a transaction. -The primary target is **Bitcoin** (including Bitcoin testnets), with additional support for Elements-based networks such as Liquid. +The primary target is **Bitcoin**, using `bitcoin-policy-v1`, with confidential multi-asset support for Liquid through `liquid-policy-v1`. The CLI and demo default to Bitcoin Testnet4 and Liquid Testnet. > ⚠️ **Warning: Project Under Development** > -> This project is still in active development and conceptualization. It is **not safe for use in production environments** at this time. Features and functionality may change, and there may be critical bugs or incomplete implementations. +> This project has not been independently audited and is **not safe for use in production environments**. ## How It Works -See [core/README.md](core/README.md) for a detailed explanation of the unchained model, the taproot tree structure, and the co-signing flow. +See [core/README.md](core/README.md) for the unchained model, Taproot tree structure and policy co-signing flow. ## Project Structure -- [`core`](core/README.md): Contains the fundamental logic for executing Simplicity programs and interacting with Bitcoin and Elements environments. (Note: In the future, parts of this logic may be migrated to [hal-simplicity](https://github.com/BlockstreamResearch/hal-simplicity)) +- [`core`](core/README.md): Executes Simplicity policies and provides cryptographic, Taproot and transaction-environment utilities. -- [`service`](service/README.md): Implements a web service providing endpoints to interact with Simplicity Unchained. Currently, it supports a 2-of-2 multisig co-signing flow on Bitcoin and Elements, triggered by the successful execution of a Simplicity program. +- [`service`](service/README.md): Web service for policy-bound key derivation, transaction preparation, evaluation and co-signing. -- [`cli`](cli/README.md): Command-line interface for communicating with the Simplicity Unchained service and accessing various ecosystem utilities. (Note: In the future, parts of this CLI will use [hal-simplicity](https://github.com/BlockstreamResearch/hal-simplicity)) +- [`cli`](cli/README.md): Command-line policy workflows and key generation. -Each component of the project has its own README file with more detailed information. +- [`policy`](policy/README.md): Portable Bitcoin and Liquid policy kernels and registries, shared by native clients and the browser/WASM implementation. -## Web Demo +Each component has its own README with more detailed information. -A live testnet demo is hosted at **[testnet.simplicity-unchained.blockstream.com](https://testnet.simplicity-unchained.blockstream.com/)**. It provides a browser interface for writing Simplicity programs, compiling them, generating a P2TR address, and sending co-signed transactions on Bitcoin Testnet4 or Liquid Testnet — no CLI or local setup required. +## Web Demo -![Simplicity Unchained Web Demo](doc/demo_website.png) +The [web demo](https://github.com/BlockstreamResearch/simplicity-unchained-web-demo) provides a browser interface for writing SimplicityHL policies, compiling them, deriving funding addresses and co-signing transactions on Bitcoin Testnet4 or Liquid Testnet. It opens on a landing page, with the interactive workflow at `/demo`. ## Capabilities demoed so far ### Bitcoin -You can explore the Bitcoin capabilities by running the [Bitcoin demo script](cli/scripts/demo_btc.sh). This demo guides you through: +The [Bitcoin demo script](cli/scripts/demo_btc.sh) uses `bitcoin-policy-v1` on Bitcoin Testnet4: -1. Tweaking a public key with a Simplicity program via the Simplicity Unchained service. -2. Creating a 2-of-2 multisig P2TR address on Bitcoin regtest. -3. Funding the address and creating a PSBT spending transaction. -4. Using the Simplicity Unchained service to co-sign the PSBT after successful Simplicity program execution. -5. Broadcasting the fully signed transaction to Bitcoin regtest. +1. Check the service's profile, network and known signing key. +2. Derive a policy-bound P2TR address before funding. +3. Prepare and review a PSBT using full parent transactions. +4. Evaluate the policy with its witness and request the service signature. +5. Add the user's signature and finalize locally. -### Elements / Liquid +See the [CLI readme](cli/README.md#demo) for setup and the [policy examples](examples/policy/README.md) for recipient restrictions, debit and fee caps, and transaction approvals. -The [Elements demo script](cli/scripts/demo_elements.sh) demonstrates the same 2-of-2 multisig co-signing flow on the Liquid Testnet. +### Elements / Liquid -To run either demo, follow the instructions in the [CLI readme](cli/README.md). +The [`liquid-policy` CLI](cli/README.md#liquid-policy-workflow) supports confidential PSETs and multiple assets. It verifies asset/value disclosure proofs and per-asset accounting before evaluation and signing. See the [Liquid profile](policy/LIQUID.md) for schemas and asset semantics. ## Licence diff --git a/SECURITY-REVIEW.md b/SECURITY-REVIEW.md new file mode 100644 index 0000000..e8242da --- /dev/null +++ b/SECURITY-REVIEW.md @@ -0,0 +1,110 @@ +# Security review + +Date: 2026-11-06 + +## Scope + +Reviewed the native SU working tree based on `7a8853e`, including modified and +untracked policy code. This report does not commit or snapshot those changes. + +The review covered service routes and admission limits, Bitcoin and Liquid +transaction evidence, policy commitments and approvals, execution bounds, CLI +signature verification and finalization, native plugin trust boundaries, and +locked Rust dependencies. + +The browser demo, deployment configuration, infrastructure and operator key +storage were not audited. This was not a formal verification or penetration +test. No transactions were funded or broadcast, and the running service and +signer configuration were not changed. + +## Findings + +No application-level policy bypass or fund-theft exploit was confirmed. RustSec +identified two affected dependencies. Confidence below refers to the affected +locked version, not to exploitability through SU's current request paths. +Severity reflects review triage priority. + +| # | Severity | File | Lines | Vulnerability | Confidence | +|---|----------|------|-------|---------------|------------| +| 1 | 🟡 MEDIUM | [Cargo.lock](Cargo.lock) | 267-270 | `bytes` 1.11.0: integer overflow in `BytesMut::reserve` can cause memory corruption | 10/10 | +| 2 | ⚪ LOW | [Cargo.lock](Cargo.lock) | 577-580 | `h2` 0.3.27: empty DATA frames can accumulate without limit | 10/10 | + +### 1. bytes: RUSTSEC-2026-0007 + +The dependency is used by both the service and CLI. In release builds, an +unchecked capacity addition can wrap and permit out-of-bounds access. + +The upstream reproduction requires a reservation near `usize::MAX` after +splitting a buffer. No SU request path reaching that condition was identified; +the public policy routes limit request bodies to 2 MiB. The advisory's unsafe +reproduction was not executed. + +Upgrade `bytes` to **1.11.1 or later** and re-run the relevant tests. + +Source: [RustSec advisory](https://rustsec.org/advisories/RUSTSEC-2026-0007.html). + +### 2. h2: RUSTSEC-2026-0258 + +The affected dependency chain is: + +```text +cli -> reqwest 0.11.27 -> hyper 0.14.32 -> h2 0.3.27 +``` + +A peer can supply empty HTTP/2 DATA frames that consume no flow-control bytes +but still occupy receive-queue entries. Undrained streams can exhaust memory. + +The signing service does not depend on this `h2` version and is built with +HTTP/1 support only. The CLI's current native-TLS features do not enable ALPN, +and it does not request HTTP/2 prior knowledge. No active HTTP/2 exposure was +identified in the reviewed build. + +The fix is **h2 0.4.16 or later**. Since the current Hyper dependency requires +the 0.3 series, update the HTTP client dependency chain rather than forcing an +incompatible `h2` version. + +Source: [RustSec advisory](https://rustsec.org/advisories/RUSTSEC-2026-0258.html). + +## Other observations + +- RustSec also reported unmaintained `pin-utils` and `rustls-pemfile`, and + unsoundness warnings for `anyhow`, `rand` and `tokio`. These were not counted as + demonstrated SU vulnerabilities. +- The [native plugin loader](core/src/plugin/loader.rs) documents a race between + optional file-hash verification and opening the library. Keep plugin files and + their directories protected from untrusted writes. Plugins are operator-trusted + native code and are not loaded through the policy endpoints. +- Parent transaction verification establishes content, not confirmation or + unspentness. Stateless caps, independent recovery paths and operator-held + signing keys are documented trust constraints, not guarantees established by + this review. + +## Verification + +RustSec advisories were fetched during the review. `cargo audit` scanned 248 +locked dependencies and exited with two vulnerabilities and five warnings. +Dependency and feature trees confirmed the package chains and HTTP/2 conditions. + +| Command | Result | +|---|---| +| `cargo audit` | Failed with the two dependency advisories above. | +| `cargo tree --locked --offline -i h2@0.3.27` | Passed; dependency is CLI-only. | +| `cargo tree --locked --offline -i bytes@1.11.0` | Passed; dependency is shared by service and CLI. | +| `cargo tree --locked --offline -e features -i reqwest` | Passed; native-TLS ALPN feature is absent. | +| `cargo tree --locked --offline -e features -i axum` | Passed; service HTTP/2 feature is absent. | +| `cargo test -p service -p simplicity-unchained-policy -p core --locked` | Core: 30 passed, 1 ignored. Service unit tests: 20 passed. Startup: 7 passed, 1 failed with a connection reset; run stopped before the kernel tests. | +| `cargo test -p simplicity-unchained-policy --locked` | Passed: 20 Bitcoin kernel tests and 11 Liquid tests. | +| `cargo test -p service --test startup explicit_read_only_configuration_starts_and_serves_requests --locked -- --exact --test-threads=1` | Passed: the initially failing startup test. | +| `cargo test -p service --test startup --locked -- --test-threads=1` | Passed: all 8 startup tests. | +| `cargo test -p cli --test policy --test liquid_policy --test public_surface --test policy_templates --locked` | Passed: 32 tests, 1 ignored. | + +After reruns, **121 distinct selected tests passed; two were ignored**. These +included parent/proof tampering, stale approvals, wrong keys and signatures, +profile/network mismatches, multi-input finalization, body limits, admission +timeouts and preallocation cost rejection. Passing tests do not establish the +absence of other vulnerabilities. + +## Remediation status + +No fixes were applied. Update the affected dependencies, review the resulting +HTTP client behavior, and repeat the audit and tests before deployment. diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9cda983..fe67a2f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,8 +11,9 @@ path = "src/main.rs" anyhow = "1.0" clap = { workspace = true } core = { path = "../core" } +simplicity-unchained-policy = { path = "../policy" } simplicity-lang = { workspace = true } hex = { workspace = true } -reqwest = { version = "0.11", features = ["blocking", "json"] } +reqwest = { version = "0.12.28", default-features = false, features = ["blocking", "json", "native-tls", "charset", "system-proxy"] } serde = { workspace = true } serde_json = { workspace = true } diff --git a/cli/README.md b/cli/README.md index c5ac1b0..dbbf71b 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,133 +1,224 @@ # Simplicity Unchained CLI -Command-line interface for communicating with the Simplicity Unchained service and accessing various ecosystem utilities. +Command-line interface for communicating with the Simplicity Unchained service and preparing, signing and finalizing policy transactions. ## Commands -Use the `--help` flag with any command or subcommand for the full list of arguments. +Run from the repository root with `cargo run --quiet -p cli -- `, or use the built `simplicity-unchained` binary. Use `--help` with any command or subcommand for its arguments. ```plaintext Usage: simplicity-unchained Commands: - address Address operations - keypair Keypair operations - tx Transaction operations (Elements / Liquid) - btc-tx Transaction operations (Bitcoin) - help Print this message or the help of the given subcommand(s) + liquid-policy Confidential multi-asset Liquid policy utilities + policy Bitcoin policy funding, evaluation and signing + keypair Keypair operations + help Print this message or the help of the given subcommand(s) ``` -### address - -- `address multisig` — Generate a P2TR 2-of-2 multisig address from two public keys and a user leaf hash. - -Pass the updated service `/tweak` response's `tweaked_public_key_hex` as -`--pubkey1`, not the untweaked service key or an old CMR-only tweaked key. -When using script-reading jets, include the intended `redeem_script_hex` in the -`/tweak` request before funding and supply the identical bytes to `/sign/psbt` -or `/sign/pset`. Use that same tweaked key for `--cosigner-pubkey` when signing -or spending the recovery leaf. The demo scripts omit the script in both -requests, consistently committing to an empty script. - ### keypair - `keypair generate` — Generate a new secp256k1 keypair. -### tx (Elements / Liquid) +## Bitcoin policy workflow -- `tx create` — Create a PSET from UTXOs. -- `tx sign` — Sign a PSET with one secret key (for local co-signing). -- `tx finalize` — Finalize a PSET into a broadcastable transaction. -- `tx build-csv-leaf` — Build a CSV recovery leaf script from a user pubkey and timelock. -- `tx spend-user-leaf` — Spend the user leaf (Leaf 1) of a P2TR output independently. -- `tx finalize-user-leaf` — Finalize a user leaf PSET into a broadcastable transaction. +Compile programs for `bitcoin-policy-v1`. See the [policy examples](../examples/policy/README.md) for recipient allowlists, debit and fee caps, threshold approvals and source/locktime restrictions. -### btc-tx (Bitcoin) +| `policy` operation | Behavior | +|---|---| +| `capabilities` | Check the service's registry, fingerprint, network and genesis hash. | +| `tweak` | Derive and verify the service's policy-bound cooperative key before funding. | +| `build-csv-leaf` | Build a Bitcoin recovery script from `--user-pubkey` and `--timelock` (1–65535 blocks). Retain this script privately. | +| `address` | Derive the funding address locally from the program, network, base service key, user key and recovery hash. | +| `prepare` | Compare the service's context with locally verified full parent evidence, without approval signatures. | +| `evaluate` | Locally evaluate the witnessed policy and compare the service context and bounded cost; no transaction signature. | +| `approval-hash` | Locally compute J53/J54 from verified parents; no signing-service contact. | +| `sign` | Evaluate the policy, request the service signature and verify the returned signature and PSBT. | +| `user-sign` | Verify the context and service signature, then add the user's signature locally using `--secret-key-file`. | +| `finalize` | Verify the policy binding, parent evidence and both signatures for **every** input before producing transaction hex. No broadcast. | -- `btc-tx create` — Create a PSBT from UTXOs. -- `btc-tx approval-hash` — Compute the digest an approver must sign for a Bitcoin `tx_approval_hash` policy. -- `btc-tx sign` — Sign a PSBT with one secret key (for local co-signing). -- `btc-tx finalize` — Finalize a PSBT into a broadcastable transaction. -- `btc-tx build-csv-leaf` — Build a CSV recovery leaf script from a user pubkey and timelock. -- `btc-tx spend-user-leaf` — Spend the user leaf (Leaf 1) of a P2TR output independently. -- `btc-tx finalize-user-leaf` — Finalize a user leaf PSBT into a broadcastable transaction. +`--profile` defaults to `bitcoin-policy-v1`; `--profile-hash` can pin the compiled fingerprint. `--network` defaults to **`testnet4`** and must match the service. Accepted networks are `bitcoin`, `testnet` and `testnet4`. -### Bitcoin Transaction Approvals +`--service` defaults to `http://127.0.0.1:30431`. Supply an HTTP(S) origin without an endpoint path, credentials, query or fragment. Use TLS for remote deployments. Remote Bitcoin calls use `/simplicity-unchained/policy/bitcoin`. -Use a program that explicitly verifies the approver's BIP-340 signature against -`jet::tx_approval_hash()`. Build it with the updated core `CoreExtension` jet table. -Choose the approver public key and script data before generating and funding the -address. +### Before funding -For each spend, populate `witness_utxo` for **every** PSBT input, independently -verify the prevouts, review the transaction, and calculate its approval digest: +Choose the reviewed program, exact script data, independently known base signing +key and user/recovery keys first. `tweak` returns `public_key_hex` as an **x-only** +cooperative leaf key, not the Taproot internal key. ```bash -cargo run --quiet -- btc-tx approval-hash \ - --psbt "$PSBT_HEX" \ - --input-index 0 \ - --program "$PROGRAM" \ - --redeem-script-hex "$REDEEM_SCRIPT_HEX" \ +SERVICE_ORIGIN=http://127.0.0.1:30431 +cargo run --quiet -p cli -- policy capabilities \ + --network testnet4 --service "$SERVICE_ORIGIN" +cargo run --quiet -p cli -- policy build-csv-leaf \ + --user-pubkey "$USER_PUBLIC_KEY_HEX" --timelock 144 +# Retain its script and use its raw leaf_hash as RECOVERY_LEAF_HASH_HEX. +POLICY_ARGS=( --network testnet4 + --program "$POLICY_PROGRAM_BASE64" + --redeem-script-hex "$REDEEM_SCRIPT_HEX" +) +cargo run --quiet -p cli -- policy tweak "${POLICY_ARGS[@]}" \ + --base-public-key "$BASE_SERVICE_PUBLIC_KEY_HEX" --service "$SERVICE_ORIGIN" +cargo run --quiet -p cli -- policy address "${POLICY_ARGS[@]}" \ + --base-public-key "$BASE_SERVICE_PUBLIC_KEY_HEX" \ + --user-pubkey "$USER_PUBLIC_KEY_HEX" \ + --user-leaf-hash-hex "$RECOVERY_LEAF_HASH_HEX" ``` -Run this from the `cli` directory. Omit `--redeem-script-hex` for an empty script. -`--network` is required and must match the service's configured `bitcoin_network` -(`bitcoin`, `testnet`, or `testnet4`), even for a local test deployment. - -The JSON response contains `approval_hash_hex` and `cmr_hex`, both 64-character -hex strings. Have the approver sign the **32 decoded approval-hash bytes**, not -the ASCII hex text, using BIP-340. Encode the signature into the program's witness -and submit the same PSBT, input, program and script to `/sign/psbt`. Recompute and -obtain a fresh approval after changing the transaction or prevouts. +`--program` takes standard base64. `--redeem-script-hex` commits exact bytes +(empty by default); changing them changes the address. `address` works offline +with the known base key and compiled profile. Check service capabilities and a +key-pinned `tweak` before funding. -The approval digest is not the co-signer's Taproot sighash. See the -[versioned digest definition](../core/README.md#bitcoin-per-spend-approvals). -Static hashlocks and fixed-message signature policies retain their intentionally -replayable behavior; existing funded static policies are not upgraded by this -command. +### Prepare, approve and co-sign -## Demo +Construct an unsigned version-2 PSBT using your Bitcoin wallet or PSBT tooling. +All inputs must spend the same policy-bound P2TR output, with one to eight inputs +and outputs. Provide full `non_witness_utxo` parents for **every** input. -First, provision a private configuration and unique signing key following the -service's [startup instructions](../service/README.md#starting-the-service). -Then ensure the Simplicity Unchained service is running: +Parent verification is offline unless a resolver is configured, and fails if +evidence is missing. Use `--offline` to forbid fetching, or set +`--esplora-url https://mempool.space/testnet4/api` (or your own network-matching +Esplora API). Missing parents are fetched from `/tx//raw`, deduplicated and +checked against the outpoints. Missing `witness_utxo` is populated from the +verified parent; conflicting values/scripts fail. Context is validated **before +contacting the signing service**. There are no automatic signing retries. ```bash -cd service -cargo run --quiet -- start +TX_ARGS=( + "${POLICY_ARGS[@]}" + --psbt "$PSBT_HEX" --input-index 0 + --user-pubkey "$USER_PUBLIC_KEY_HEX" + --user-leaf-hash-hex "$RECOVERY_LEAF_HASH_HEX" + --base-public-key "$BASE_SERVICE_PUBLIC_KEY_HEX" + --offline +) +cargo run --quiet -p cli -- policy approval-hash "${TX_ARGS[@]}" +cargo run --quiet -p cli -- policy prepare "${TX_ARGS[@]}" --service "$SERVICE_ORIGIN" +# Review the context, sign the decoded J54 or J53 bytes, and encode the witness. +cargo run --quiet -p cli -- policy evaluate "${TX_ARGS[@]}" \ + --witness "$APPROVAL_WITNESS_BASE64" --service "$SERVICE_ORIGIN" +cargo run --quiet -p cli -- policy sign "${TX_ARGS[@]}" \ + --witness "$APPROVAL_WITNESS_BASE64" --service "$SERVICE_ORIGIN" ``` -Then, in a separate terminal, navigate to the `cli` directory and run one of the demo scripts: +`prepare` needs no witness. `evaluate`/`sign` require standard-base64 `--witness`; +use `--witness ''` only for a witness-free program. The returned `context` includes +amounts, fees, debit, output scripts, keys and verification provenance for review: -### Bitcoin (regtest) +- `policy_approval_hash` (J53) binds the selected input and actual signing digest. +- `policy_transaction_approval_hash` (J54) can deliberately authorize all + same-contract inputs in this transaction. +- Sign the **decoded 32-byte digest** directly with BIP-340, not ASCII hex or + another SHA256. Any approved transaction/context change requires fresh approvals. +- Static hashlocks and fixed-message signatures are replayable; caps apply per transaction. -Requires a running `bitcoind` in regtest mode and `bitcoin-cli` in your `PATH`: +Repeat `sign` and `user-sign` for every input, using the latest `psbt_hex` each +time. `user-sign` takes all the same transaction arguments and a file containing +only the user's 64-character private-key hex (surrounding whitespace is allowed). +Keep this file private, never commit it, and never send it to the service. +The CLI checks the secret against the committed user key. ```bash -bitcoind -regtest -fallbackfee=0.0002 -txindex=1 -daemon -bitcoin-cli -regtest loadwallet +# Rebuild TX_ARGS with the service-returned PSBT_HEX before each invocation. +cargo run --quiet -p cli -- policy user-sign "${TX_ARGS[@]}" \ + --secret-key-file "$USER_SECRET_KEY_FILE" +# Rebuild TX_ARGS with user-sign's psbt_hex; repeat for all remaining inputs. +cargo run --quiet -p cli -- policy finalize "${TX_ARGS[@]}" ``` +Finalization verifies the committed canonical leaf/control block and both +SIGHASH_DEFAULT signatures; missing signatures or tampered metadata fail. +Broadcast the resulting `transaction_hex` using your wallet only after separately +checking chain status, fee rate and transaction acceptance. + +Bitcoin policy commands print JSON. Failures after argument parsing print +`{"error":"..."}`, diagnostic stderr and a nonzero exit. Clap usage errors print +normal stderr usage. `parent-txid-verified` proves parent **content**, not active +chain membership, confirmation, consensus validity or unspentness; +`chain_status_verified` is always false. + +## Liquid policy workflow + +Compile programs for `liquid-policy-v1`. The network defaults to `liquidtestnet`; +`liquid` is also supported. See the [Liquid profile](../policy/LIQUID.md) for +disclosure proofs, asset semantics and creation schemas. + +`liquid-policy registry` prints the manifest. Other operations, except +`generate-blinding-key`, accept `--request-file path.json`. A transaction request +has this shape: + +```json +{ + "profile": "liquid-policy-v1", + "network": "liquidtestnet", + "program": "", + "redeem_script_hex": "", + "base_public_key_hex": "", + "pset_hex": "", + "input_index": 0, + "user_pubkey": "", + "user_leaf_hash_hex": "", + "witness": "" +} +``` + +Optional `profile_hash` pins the compiled fingerprint. `address` can add +`blinding_public_key_hex` for a confidential address. `create` uses a separate +request shape with full parents and optional local blinding secrets; keep these +creation files private. Receiving secrets are not sent to the service. + +| `liquid-policy` operation | Behavior | +| --- | --- | +| `registry` | Print the local registry and fingerprint. | +| `generate-blinding-key` | Generate a receiving keypair locally; keep the secret private. | +| `address`, `tweak` | Derive the funding address or service key locally. | +| `capabilities` | Check the remote manifest, network, genesis hash and known base key. | +| `create` | Build and blind a multi-asset PSET with disclosure proofs. | +| `prepare`, `approval-hash` | Verify evidence and produce the local context or approval hashes. | +| `evaluate` | Evaluate the witnessed policy locally. | +| `sign` | Evaluate locally, request a service signature and verify the returned PSET and signature. | +| `user-sign` | Verify the service signature and add the user's signature using `--secret-key-file`. | +| `finalize` | Verify proofs and both signatures for every input, then produce transaction hex. | + +Only `capabilities` and `sign` contact the service. They accept `--service`, +defaulting to `http://127.0.0.1:30431`. + ```bash -cd cli -./scripts/demo_btc.sh # 2-of-2 co-signing via Simplicity program -./scripts/demo_btc_user_leaf.sh # CSV recovery leaf spend +cargo run --quiet -p cli -- liquid-policy registry +cargo run --quiet -p cli -- liquid-policy capabilities --request-file request.json +cargo run --quiet -p cli -- liquid-policy prepare --request-file request.json +cargo run --quiet -p cli -- liquid-policy evaluate --request-file request.json +cargo run --quiet -p cli -- liquid-policy sign --request-file request.json ``` -### Elements / Liquid Testnet +Update `pset_hex` with each returned PSET and repeat `sign` and `user-sign` for +every input before `finalize`. Approval signatures use decoded J53/J54 digest +bytes, as in the Bitcoin workflow. + +## Demo -Requires `curl` and `jq` in your `PATH`. Transactions are broadcast to Liquid Testnet via the Blockstream API. +Provision a unique private service key following the +[startup instructions](../service/README.md#starting-the-service), then run: ```bash -cd cli -./scripts/demo_elements.sh # 2-of-2 co-signing via Simplicity program -./scripts/demo_elements_user_leaf.sh # CSV recovery leaf spend +cd service +cargo run --quiet -- start +# In another terminal, from the repository root: +./cli/scripts/demo_btc.sh --help ``` -> ⚠️ **Warning: Liquid Testnet Faucet rate limit** -> -> The Elements demo scripts interact with the Liquid Testnet Faucet, which imposes a rate limit. If the script fails due to rate limiting, wait at least one minute before retrying. +The Bitcoin script requires `cargo` and `jq`. It defaults to Testnet4 and has +three modes: `address`, `review` (default) and `sign`. Its `--help` lists the +required environment variables for the program, script data, base service key, +user key and recovery hash. `review` and `sign` also require a PSBT and witness; +`sign` requires a private user key file. + +The script handles one input. It does not fetch faucet funds or broadcast, and +`review` does not request a signature. Use the commands above for multi-input +spends or Liquid. ## Licence diff --git a/cli/scripts/demo_btc.sh b/cli/scripts/demo_btc.sh index cee2aa2..9b7f1c3 100755 --- a/cli/scripts/demo_btc.sh +++ b/cli/scripts/demo_btc.sh @@ -1,155 +1,105 @@ -#!/bin/bash - -set -e - -echo "==== Spend type: P2TR ====" -echo - -BTC="bitcoin-cli -regtest" -PROGRAM="zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA" -SIMPLICITY_WITNESS="" -NETWORK="regtest" -CSV_BLOCKS=10 - -echo "==== Sanity checks ====" -if ! command -v bitcoin-cli &> /dev/null; then - echo "ERROR: bitcoin-cli not found in PATH" - exit 1 +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Bitcoin policy-v1 demo (experimental; Bitcoin Testnet4 by default) +Usage: ./cli/scripts/demo_btc.sh [address|review|sign|--help] + +Required environment: + POLICY_PROGRAM_BASE64 Reviewed bitcoin-policy-v1 program, not legacy bytes + BASE_SERVICE_PUBLIC_KEY_HEX Independently known untweaked service key + USER_PUBLIC_KEY_HEX Cooperative user public key + RECOVERY_LEAF_HASH_HEX Exact raw recovery TapNodeHash + PSBT_HEX Required for review/sign; one input, full parents + APPROVAL_WITNESS_BASE64 Required for review/sign; set '' if witness-free + USER_SECRET_KEY_FILE Required for sign; private file of user key hex + +Optional: + SERVICE_ORIGIN Default http://127.0.0.1:30431 + NETWORK Default testnet4; must exactly match the service + REDEEM_SCRIPT_HEX Default empty; exact committed script-data bytes + ESPLORA_URL Explicit parent resolver; default strictly offline + +address verifies capabilities/tweak before deriving funding data. +review (default) prepares/evaluates only; it never requests a signature. +sign revalidates a pre-reviewed PSBT/witness, co-signs and finalizes locally. +No faucet, broadcast, recovery spend, legacy endpoint or automatic signing retry. +For multi-input spends use the policy CLI directly. +EOF +} + +mode="${1:-review}" +case "$mode" in + -h|--help|help) usage; exit 0 ;; + address|review|sign) ;; + *) usage >&2; exit 2 ;; +esac +if [[ $# -gt 1 ]]; then + usage >&2 + exit 2 fi -if ! $BTC getblockchaininfo > /dev/null 2>&1; then - echo "ERROR: bitcoind is not running or not reachable." - echo "Start it with:" - echo " bitcoind -regtest -fallbackfee=0.0002 -txindex=1 -daemon" - echo " bitcoin-cli -regtest loadwallet " - exit 1 +command -v cargo >/dev/null +command -v jq >/dev/null +cd "$(dirname "${BASH_SOURCE[0]}")/../.." +su=(cargo run --quiet --locked -p cli -- policy) +service="${SERVICE_ORIGIN:-http://127.0.0.1:30431}" +network="${NETWORK:-testnet4}" +: "${POLICY_PROGRAM_BASE64:?Set a reviewed policy-profile program}" +: "${BASE_SERVICE_PUBLIC_KEY_HEX:?Pin the known untweaked service key}" +: "${USER_PUBLIC_KEY_HEX:?Set the cooperative user key}" +: "${RECOVERY_LEAF_HASH_HEX:?Set the committed raw recovery hash}" + +policy_args=( + --network "$network" --program "$POLICY_PROGRAM_BASE64" + --redeem-script-hex "${REDEEM_SCRIPT_HEX:-}" +) +binding_args=( + "${policy_args[@]}" + --base-public-key "$BASE_SERVICE_PUBLIC_KEY_HEX" + --user-pubkey "$USER_PUBLIC_KEY_HEX" + --user-leaf-hash-hex "$RECOVERY_LEAF_HASH_HEX" +) + +if [[ "$mode" == address ]]; then + "${su[@]}" capabilities --network "$network" --service "$service" >&2 + "${su[@]}" tweak "${policy_args[@]}" \ + --base-public-key "$BASE_SERVICE_PUBLIC_KEY_HEX" --service "$service" >&2 + "${su[@]}" address "${binding_args[@]}" + exit 0 fi -echo "OK — bitcoind running" -echo - -echo "==== Setup: Ensure wallet has funds ====" -MINER_ADDR=$($BTC getnewaddress) -BAL=$($BTC getbalance) -if (( $(echo "$BAL < 1.0" | bc -l) )); then - echo "Mining initial coins..." - $BTC generatetoaddress 101 "$MINER_ADDR" > /dev/null +: "${PSBT_HEX:?Supply an already constructed, pre-reviewed policy PSBT}" +if [[ ! ${APPROVAL_WITNESS_BASE64+x} ]]; then + echo "Set APPROVAL_WITNESS_BASE64 explicitly (empty only for witness-free policies)" >&2 + exit 2 fi -echo "Wallet balance: $($BTC getbalance) BTC" -echo - -echo "==== Step 0: Get Tweaked Cosigner Public Key ====" -TWEAK_RESPONSE=$(curl -s -X POST http://localhost:30431/simplicity-unchained/tweak \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg program "$PROGRAM" '{program: $program, jet_env: "bitcoin"}')") - -COSIGNER_PUBKEY=$(echo "$TWEAK_RESPONSE" | jq -r '.tweaked_public_key_hex') -if [ -z "$COSIGNER_PUBKEY" ] || [ "$COSIGNER_PUBKEY" = "null" ]; then - echo "ERROR: Failed to get tweaked public key" - echo "Response: $TWEAK_RESPONSE" - exit 1 +resolver=(--offline) +if [[ -n "${ESPLORA_URL:-}" ]]; then + resolver=(--esplora-url "$ESPLORA_URL") fi -echo "Cosigner pubkey: $COSIGNER_PUBKEY" -echo - -echo "==== Step 1: Generate User Keypair ====" -USER_KEYPAIR=$(cargo run --quiet -- keypair generate) -USER_SECKEY=$(echo "$USER_KEYPAIR" | jq -r '.secret_key') -USER_PUBKEY=$(echo "$USER_KEYPAIR" | jq -r '.public_key') -echo "User public key: $USER_PUBKEY" -echo - -echo "==== Step 2: Build User Leaf (CSV recovery) ====" -cargo run --quiet -- btc-tx build-csv-leaf --user-pubkey "$USER_PUBKEY" --timelock "$CSV_BLOCKS" -LEAF_DATA=$(cargo run --quiet -- btc-tx build-csv-leaf \ - --user-pubkey "$USER_PUBKEY" \ - --timelock "$CSV_BLOCKS") -USER_LEAF_HASH=$(echo "$LEAF_DATA" | jq -r '.leaf_hash') -echo "User leaf hash: $USER_LEAF_HASH" -echo - -echo "==== Step 3: Create P2TR Address ====" -ADDRESS_DATA=$(cargo run --quiet -- address multisig \ - --pubkey1 "$COSIGNER_PUBKEY" \ - --pubkey2 "$USER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH" \ - --network "$NETWORK") - -ADDRESS=$(echo "$ADDRESS_DATA" | jq -r '.address') -if [ -z "$ADDRESS" ] || [ "$ADDRESS" = "null" ]; then - echo "ERROR: Failed to generate address" - echo "Response: $ADDRESS_DATA" - exit 1 +tx_args=("${binding_args[@]}" --psbt "$PSBT_HEX" --input-index 0 "${resolver[@]}") +prepared=$("${su[@]}" prepare "${tx_args[@]}" --service "$service") +echo "$prepared" >&2 +if [[ "$(jq -er '.context.input_count' <<<"$prepared")" != 1 ]]; then + echo "Demo requires exactly one input; use the policy CLI for multiple inputs" >&2 + exit 2 fi -echo "Address: $ADDRESS" -echo - -echo "==== Step 4: Fund Address ====" -FUNDING_TXID=$($BTC sendtoaddress "$ADDRESS" 0.001) -$BTC generatetoaddress 1 "$MINER_ADDR" > /dev/null -echo "Funding txid: $FUNDING_TXID (confirmed)" -VOUT=$($BTC gettransaction "$FUNDING_TXID" | jq '.details[0].vout') -echo "Output index: $VOUT" -echo - -echo "==== Step 5: Create PSBT ====" -PSBT_CREATE_DATA=$(cargo run --quiet -- btc-tx create \ - -i "$FUNDING_TXID:$VOUT" \ - -o "$ADDRESS:90000" \ - --network "$NETWORK") -PSBT_HEX=$(echo "$PSBT_CREATE_DATA" | jq -r '.psbt') -echo "Created PSBT: $PSBT_HEX" -echo - -echo "==== Step 6: Cosigner Signature (Service) ====" -SIGN_REQUEST=$(jq -n \ - --arg psbt "$PSBT_HEX" \ - --arg program "$PROGRAM" \ - --arg witness "$SIMPLICITY_WITNESS" \ - --arg user_pubkey "$USER_PUBKEY" \ - --arg user_leaf_hash "$USER_LEAF_HASH" \ - '{ - psbt_hex: $psbt, - input_index: 0, - program: $program, - witness: $witness, - jet_env: "bitcoin", - user_pubkey: $user_pubkey, - user_leaf_hash_hex: $user_leaf_hash - }') - -PSBT_SIGN1_DATA=$(curl -s -X POST http://localhost:30431/simplicity-unchained/sign/psbt \ - -H "Content-Type: application/json" \ - -d "$SIGN_REQUEST") - -if echo "$PSBT_SIGN1_DATA" | jq -e '.error' > /dev/null 2>&1; then - echo "ERROR from sign service: $(echo "$PSBT_SIGN1_DATA" | jq -r '.error')" - exit 1 +"${su[@]}" approval-hash "${tx_args[@]}" >&2 +"${su[@]}" evaluate "${tx_args[@]}" --witness "$APPROVAL_WITNESS_BASE64" \ + --service "$service" >&2 +if [[ "$mode" == review ]]; then + echo "$prepared" + exit 0 fi -PSBT_SIGNED1=$(echo "$PSBT_SIGN1_DATA" | jq -r '.psbt_hex') -echo "Cosigner signature: $(echo "$PSBT_SIGN1_DATA" | jq -r '.signature_hex')" -echo - -echo "==== Step 7: User Signature ====" -PSBT_SIGN2_DATA=$(cargo run --quiet -- btc-tx sign \ - --psbt "$PSBT_SIGNED1" \ - --secret-key "$USER_SECKEY" \ - --input-index 0 \ - --cosigner-pubkey "$COSIGNER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH") - -PSBT_SIGNED2=$(echo "$PSBT_SIGN2_DATA" | jq -r '.psbt') -echo "User signature added" -echo - -echo "==== Step 8: Finalize ====" -FINALIZE_DATA=$(cargo run --quiet -- btc-tx finalize --psbt "$PSBT_SIGNED2") -FINAL_TX_HEX=$(echo "$FINALIZE_DATA" | jq -r '.transaction_hex') -echo "TXID: $(echo "$FINALIZE_DATA" | jq -r '.txid')" -echo - -echo "==== Step 9: Broadcast ====" -BROADCAST_TXID=$($BTC sendrawtransaction "$FINAL_TX_HEX") -$BTC generatetoaddress 1 "$MINER_ADDR" > /dev/null -echo "Done! TXID: $BROADCAST_TXID" \ No newline at end of file +: "${USER_SECRET_KEY_FILE:?Provide a private file containing the user secret key hex}" +signed=$("${su[@]}" sign "${tx_args[@]}" --witness "$APPROVAL_WITNESS_BASE64" \ + --service "$service") +psbt=$(jq -er '.psbt_hex' <<<"$signed") +tx_args=("${binding_args[@]}" --psbt "$psbt" --input-index 0 "${resolver[@]}") +user_signed=$("${su[@]}" user-sign "${tx_args[@]}" --secret-key-file "$USER_SECRET_KEY_FILE") +psbt=$(jq -er '.psbt_hex' <<<"$user_signed") +tx_args=("${binding_args[@]}" --psbt "$psbt" --input-index 0 "${resolver[@]}") +"${su[@]}" finalize "${tx_args[@]}" diff --git a/cli/scripts/demo_btc_user_leaf.sh b/cli/scripts/demo_btc_user_leaf.sh deleted file mode 100755 index 559dfd5..0000000 --- a/cli/scripts/demo_btc_user_leaf.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash - -set -e - -# Funds a P2TR address where Leaf 1 is a CSV recovery script, -# then spends it via the user leaf after the timelock. - -BTC="bitcoin-cli -regtest" -PROGRAM="zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA" -SIMPLICITY_WITNESS="" -NETWORK="regtest" -CSV_BLOCKS=10 -BLOCK_TO_MINE=10 - -echo "==== Sanity checks ====" -if ! command -v bitcoin-cli &> /dev/null; then - echo "ERROR: bitcoin-cli not found in PATH" - exit 1 -fi - -if ! $BTC getblockchaininfo > /dev/null 2>&1; then - echo "ERROR: bitcoind is not running or not reachable." - echo "Start it with:" - echo " bitcoind -regtest -fallbackfee=0.0002 -txindex=1 -daemon" - echo " bitcoin-cli -regtest loadwallet " - exit 1 -fi - -MINER_ADDR=$($BTC getnewaddress) -BAL=$($BTC getbalance) -if (( $(echo "$BAL < 1.0" | bc -l) )); then - echo "Mining initial coins..." - $BTC generatetoaddress 101 "$MINER_ADDR" > /dev/null -fi -echo "OK — balance: $($BTC getbalance) BTC" -echo - -echo "==== Step 0: Get Tweaked Cosigner Pubkey ====" -TWEAK_RESPONSE=$(curl -s -X POST http://localhost:30431/simplicity-unchained/tweak \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg program "$PROGRAM" '{program: $program, jet_env: "bitcoin"}')") -COSIGNER_PUBKEY=$(echo "$TWEAK_RESPONSE" | jq -r '.tweaked_public_key_hex') -[ -z "$COSIGNER_PUBKEY" ] || [ "$COSIGNER_PUBKEY" = "null" ] && { echo "ERROR: $TWEAK_RESPONSE"; exit 1; } -echo "Cosigner pubkey: $COSIGNER_PUBKEY" -echo - -echo "==== Step 1: Generate User Keypair ====" -USER_KEYPAIR=$(cargo run --quiet -- keypair generate) -USER_SECKEY=$(echo "$USER_KEYPAIR" | jq -r '.secret_key') -USER_PUBKEY=$(echo "$USER_KEYPAIR" | jq -r '.public_key') -echo "User pubkey: $USER_PUBKEY" -echo - -echo "==== Step 2: Build CSV Recovery Leaf Script ====" -# OP_CSV OP_DROP OP_CHECKSIG -# Built by the CLI so the pubkey encoding is correct -echo "==== Step 2: Build CSV Recovery Leaf Script ====" -LEAF_DATA=$(cargo run --quiet -- btc-tx build-csv-leaf \ - --user-pubkey "$USER_PUBKEY" \ - --timelock "$CSV_BLOCKS") -USER_LEAF_SCRIPT=$(echo "$LEAF_DATA" | jq -r '.script') -USER_LEAF_HASH=$(echo "$LEAF_DATA" | jq -r '.leaf_hash') -echo "User leaf hash: $USER_LEAF_HASH" -echo - -echo "==== Step 3: Create P2TR Address ====" -ADDRESS_DATA=$(cargo run --quiet -- address multisig \ - --pubkey1 "$COSIGNER_PUBKEY" \ - --pubkey2 "$USER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH" \ - --network "$NETWORK") -ADDRESS=$(echo "$ADDRESS_DATA" | jq -r '.address') -[ -z "$ADDRESS" ] || [ "$ADDRESS" = "null" ] && { echo "ERROR: $ADDRESS_DATA"; exit 1; } -echo "Address: $ADDRESS" -echo - -echo "==== Step 4: Fund Address ====" -FUNDING_TXID=$($BTC sendtoaddress "$ADDRESS" 0.001) -$BTC generatetoaddress 1 "$MINER_ADDR" > /dev/null -VOUT=$($BTC gettransaction "$FUNDING_TXID" | jq '.details[0].vout') -echo "Funded: $FUNDING_TXID:$VOUT" -echo - -echo "==== Step 5: Create PSBT ====" -PSBT_DATA=$(cargo run --quiet -- btc-tx create \ - -i "$FUNDING_TXID:$VOUT" \ - -o "$ADDRESS:90000" \ - --network "$NETWORK" \ - --sequence "$CSV_BLOCKS") -PSBT_HEX=$(echo "$PSBT_DATA" | jq -r '.psbt') -echo "PSBT: $PSBT_HEX" -echo - -echo "==== Step 6: Mine past CSV timelock ====" -$BTC generatetoaddress "$BLOCK_TO_MINE" "$MINER_ADDR" > /dev/null -echo "Mined $BLOCK_TO_MINE blocks" -echo - -echo "==== Step 7: User Signs Leaf 1 (CSV recovery) ====" -PSBT_SIGN_DATA=$(cargo run --quiet -- btc-tx spend-user-leaf \ - --psbt "$PSBT_HEX" \ - --secret-key "$USER_SECKEY" \ - --user-leaf-script "$USER_LEAF_SCRIPT" \ - --cosigner-pubkey "$COSIGNER_PUBKEY" \ - --input-index 0 \ - --network "$NETWORK") -PSBT_SIGNED=$(echo "$PSBT_SIGN_DATA" | jq -r '.psbt') -echo "Signed" -echo - -echo "==== Step 8: Finalize (user leaf) ====" -FINALIZE_DATA=$(cargo run --quiet -- btc-tx finalize-user-leaf --psbt "$PSBT_SIGNED") -FINAL_TX_HEX=$(echo "$FINALIZE_DATA" | jq -r '.transaction_hex') -echo "TXID: $(echo "$FINALIZE_DATA" | jq -r '.txid')" - -echo "==== Step 9: Broadcast ====" -BROADCAST_TXID=$($BTC sendrawtransaction "$FINAL_TX_HEX") -$BTC generatetoaddress 1 "$MINER_ADDR" > /dev/null -echo "Done! TXID: $BROADCAST_TXID" \ No newline at end of file diff --git a/cli/scripts/demo_elements.sh b/cli/scripts/demo_elements.sh deleted file mode 100755 index 1c0b066..0000000 --- a/cli/scripts/demo_elements.sh +++ /dev/null @@ -1,156 +0,0 @@ -#!/bin/bash - -set -e - -echo "==== Spend type: P2TR ====" -echo - -# Wait for transaction to be visible in the mempool. -wait_for_transaction() { - local TXID=$1 - echo "Waiting for transaction to be visible in the mempool..." - for i in {1..30}; do - TX_DATA=$(curl -s "https://blockstream.info/liquidtestnet/api/tx/$TXID" 2>/dev/null) - if [ -n "$TX_DATA" ] && [ "$TX_DATA" != "Transaction not found" ]; then - sleep 10 - echo "Transaction found in mempool!" - return 0 - fi - sleep 5 - done - echo "Error: Transaction not found after waiting. Please try again." - exit 1 -} - -PROGRAM="zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA" -SIMPLICITY_WITNESS="" -NETWORK="liquid_testnet" -CSV_BLOCKS=10 - -echo "==== Sanity checks ====" -if ! command -v curl &> /dev/null; then - echo "ERROR: curl not found in PATH" - exit 1 -fi -echo "OK" -echo - -echo "==== Step 0: Get Tweaked Cosigner Public Key ====" -TWEAK_RESPONSE=$(curl -s -X POST http://localhost:30431/simplicity-unchained/tweak \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg program "$PROGRAM" '{program: $program, jet_env: "elements"}')") - -COSIGNER_PUBKEY=$(echo "$TWEAK_RESPONSE" | jq -r '.tweaked_public_key_hex') -if [ -z "$COSIGNER_PUBKEY" ] || [ "$COSIGNER_PUBKEY" = "null" ]; then - echo "ERROR: Failed to get tweaked public key" - echo "Response: $TWEAK_RESPONSE" - exit 1 -fi -echo "Cosigner pubkey: $COSIGNER_PUBKEY" -echo - -echo "==== Step 1: Generate User Keypair ====" -USER_KEYPAIR=$(cargo run --quiet -- keypair generate) -USER_SECKEY=$(echo "$USER_KEYPAIR" | jq -r '.secret_key') -USER_PUBKEY=$(echo "$USER_KEYPAIR" | jq -r '.public_key') -echo "User public key: $USER_PUBKEY" -echo - -echo "==== Step 2: Build User Leaf (CSV recovery) ====" -LEAF_DATA=$(cargo run --quiet -- tx build-csv-leaf \ - --user-pubkey "$USER_PUBKEY" \ - --timelock "$CSV_BLOCKS") -USER_LEAF_HASH=$(echo "$LEAF_DATA" | jq -r '.leaf_hash') -echo "User leaf hash: $USER_LEAF_HASH" -echo - -echo "==== Step 3: Create P2TR Address ====" -ADDRESS_DATA=$(cargo run --quiet -- address multisig \ - --pubkey1 "$COSIGNER_PUBKEY" \ - --pubkey2 "$USER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH" \ - --network "$NETWORK") - -ADDRESS=$(echo "$ADDRESS_DATA" | jq -r '.address') -if [ -z "$ADDRESS" ] || [ "$ADDRESS" = "null" ]; then - echo "ERROR: Failed to generate address" - echo "Response: $ADDRESS_DATA" - exit 1 -fi -echo "Address: $ADDRESS" -echo - -echo "==== Step 4: Fund Address from Faucet ====" -echo "Running curl to connect to Liquid Testnet faucet..." -FAUCET_TRANSACTION=$(curl "https://liquidtestnet.com/faucet?address=$ADDRESS&action=lbtc" 2>/dev/null \ - | sed -n "s/.*with transaction \([0-9a-f]*\)\..*$/\1/p") -echo "Faucet transaction ID: $FAUCET_TRANSACTION" -echo - -wait_for_transaction "$FAUCET_TRANSACTION" -echo - -echo "==== Step 5: Create PSET ====" -PSET_CREATE_DATA=$(cargo run --quiet -- tx create \ - -i "$FAUCET_TRANSACTION:0" \ - -o "$ADDRESS:99000" \ - -o "fee:1000" \ - --network "$NETWORK") -PSET_HEX=$(echo "$PSET_CREATE_DATA" | jq -r '.pset') -echo "Created PSET: $PSET_HEX" -echo - -echo "==== Step 6: Cosigner Signature (Service) ====" -SIGN_REQUEST=$(jq -n \ - --arg pset "$PSET_HEX" \ - --arg program "$PROGRAM" \ - --arg witness "$SIMPLICITY_WITNESS" \ - --arg user_pubkey "$USER_PUBKEY" \ - --arg user_leaf_hash "$USER_LEAF_HASH" \ - '{ - pset_hex: $pset, - input_index: 0, - program: $program, - witness: $witness, - jet_env: "elements", - user_pubkey: $user_pubkey, - user_leaf_hash_hex: $user_leaf_hash - }') - -PSET_SIGN1_DATA=$(curl -s -X POST http://localhost:30431/simplicity-unchained/sign/pset \ - -H "Content-Type: application/json" \ - -d "$SIGN_REQUEST") - -if echo "$PSET_SIGN1_DATA" | jq -e '.error' > /dev/null 2>&1; then - echo "ERROR from sign service: $(echo "$PSET_SIGN1_DATA" | jq -r '.error')" - exit 1 -fi - -PSET_SIGNED1=$(echo "$PSET_SIGN1_DATA" | jq -r '.pset_hex') -echo "Cosigner signature: $(echo "$PSET_SIGN1_DATA" | jq -r '.signature_hex')" -echo - -echo "==== Step 7: User Signature ====" -PSET_SIGN2_DATA=$(cargo run --quiet -- tx sign \ - --pset "$PSET_SIGNED1" \ - --secret-key "$USER_SECKEY" \ - --input-index 0 \ - --cosigner-pubkey "$COSIGNER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH") - -PSET_SIGNED2=$(echo "$PSET_SIGN2_DATA" | jq -r '.pset') -echo "User signature added" -echo - -echo "==== Step 8: Finalize ====" -FINALIZE_DATA=$(cargo run --quiet -- tx finalize --pset "$PSET_SIGNED2" --network "$NETWORK") -FINAL_TX_HEX=$(echo "$FINALIZE_DATA" | jq -r '.transaction_hex') -echo "TXID: $(echo "$FINALIZE_DATA" | jq -r '.txid')" -echo - -echo "==== Step 9: Broadcast ====" -echo -n "Resulting transaction ID is " -TXID=$(curl -s -X POST "https://blockstream.info/liquidtestnet/api/tx" -d "$FINAL_TX_HEX" 2>/dev/null) -echo "$TXID" -echo -echo "You can view it online at https://blockstream.info/liquidtestnet/tx/$TXID?expand" \ No newline at end of file diff --git a/cli/scripts/demo_elements_user_leaf.sh b/cli/scripts/demo_elements_user_leaf.sh deleted file mode 100755 index b8ee5e0..0000000 --- a/cli/scripts/demo_elements_user_leaf.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash - -set -e - -# Funds a P2TR address where Leaf 1 is a CSV recovery script, -# then spends it via the user leaf after the timelock. - -PROGRAM="zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA" -SIMPLICITY_WITNESS="" -NETWORK="liquid_testnet" -CSV_BLOCKS=3 - -# Wait for transaction to be visible in the mempool. -wait_for_transaction() { - local TXID=$1 - echo "Waiting for transaction to be visible in the mempool..." - for i in {1..30}; do - TX_DATA=$(curl -s "https://blockstream.info/liquidtestnet/api/tx/$TXID" 2>/dev/null) - if [ -n "$TX_DATA" ] && [ "$TX_DATA" != "Transaction not found" ]; then - sleep 10 - echo "Transaction found in mempool!" - return 0 - fi - sleep 5 - done - echo "Error: Transaction not found after waiting. Please try again." - exit 1 -} - -echo "==== Sanity checks ====" -if ! command -v curl &> /dev/null; then - echo "ERROR: curl not found in PATH" - exit 1 -fi -echo "OK" -echo - -echo "==== Step 0: Get Tweaked Cosigner Pubkey ====" -TWEAK_RESPONSE=$(curl -s -X POST http://localhost:30431/simplicity-unchained/tweak \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg program "$PROGRAM" '{program: $program, jet_env: "elements"}')") -COSIGNER_PUBKEY=$(echo "$TWEAK_RESPONSE" | jq -r '.tweaked_public_key_hex') -[ -z "$COSIGNER_PUBKEY" ] || [ "$COSIGNER_PUBKEY" = "null" ] && { echo "ERROR: $TWEAK_RESPONSE"; exit 1; } -echo "Cosigner pubkey: $COSIGNER_PUBKEY" -echo - -echo "==== Step 1: Generate User Keypair ====" -USER_KEYPAIR=$(cargo run --quiet -- keypair generate) -USER_SECKEY=$(echo "$USER_KEYPAIR" | jq -r '.secret_key') -USER_PUBKEY=$(echo "$USER_KEYPAIR" | jq -r '.public_key') -echo "User pubkey: $USER_PUBKEY" -echo - -echo "==== Step 2: Build CSV Recovery Leaf Script ====" -LEAF_DATA=$(cargo run --quiet -- tx build-csv-leaf \ - --user-pubkey "$USER_PUBKEY" \ - --timelock "$CSV_BLOCKS") -USER_LEAF_SCRIPT=$(echo "$LEAF_DATA" | jq -r '.script') -USER_LEAF_HASH=$(echo "$LEAF_DATA" | jq -r '.leaf_hash') -echo "User leaf hash: $USER_LEAF_HASH" -echo - -echo "==== Step 3: Create P2TR Address ====" -ADDRESS_DATA=$(cargo run --quiet -- address multisig \ - --pubkey1 "$COSIGNER_PUBKEY" \ - --pubkey2 "$USER_PUBKEY" \ - --user-leaf-hash "$USER_LEAF_HASH" \ - --network "$NETWORK") -ADDRESS=$(echo "$ADDRESS_DATA" | jq -r '.address') -[ -z "$ADDRESS" ] || [ "$ADDRESS" = "null" ] && { echo "ERROR: $ADDRESS_DATA"; exit 1; } -echo "Address: $ADDRESS" -echo - -echo "==== Step 4: Fund Address from Faucet ====" -echo "Running curl to connect to Liquid Testnet faucet..." -FAUCET_TRANSACTION=$(curl "https://liquidtestnet.com/faucet?address=$ADDRESS&action=lbtc" 2>/dev/null \ - | sed -n "s/.*with transaction \([0-9a-f]*\)\..*$/\1/p") -echo "Faucet transaction ID: $FAUCET_TRANSACTION" -echo - -wait_for_transaction "$FAUCET_TRANSACTION" -echo - -echo "==== Step 5: Create PSET (with CSV sequence) ====" -PSET_DATA=$(cargo run --quiet -- tx create \ - -i "$FAUCET_TRANSACTION:0" \ - -o "$ADDRESS:99000" \ - -o "fee:1000" \ - --network "$NETWORK" \ - --sequence "$CSV_BLOCKS") -PSET_HEX=$(echo "$PSET_DATA" | jq -r '.pset') -echo "PSET: $PSET_HEX" -echo - -echo "==== Step 6: Wait for CSV timelock ====" -echo "Liquid Testnet produces blocks automatically — waiting for $CSV_BLOCKS blocks..." -echo "Check https://blockstream.info/liquidtestnet/ for block progress." -echo "Sleeping 3 minutes to allow blocks to pass..." -sleep 200 # just to be sure -echo "Done waiting" -echo - -echo "==== Step 7: User Signs Leaf 1 (CSV recovery) ====" -PSET_SIGN_DATA=$(cargo run --quiet -- tx spend-user-leaf \ - --psbt "$PSET_HEX" \ - --secret-key "$USER_SECKEY" \ - --user-leaf-script "$USER_LEAF_SCRIPT" \ - --cosigner-pubkey "$COSIGNER_PUBKEY" \ - --input-index 0 \ - --network "$NETWORK") -PSET_SIGNED=$(echo "$PSET_SIGN_DATA" | jq -r '.pset') -echo "Signed" -echo - -echo "==== Step 8: Finalize (user leaf) ====" -FINALIZE_DATA=$(cargo run --quiet -- tx finalize-user-leaf --psbt "$PSET_SIGNED") -FINAL_TX_HEX=$(echo "$FINALIZE_DATA" | jq -r '.transaction_hex') -echo "TXID: $(echo "$FINALIZE_DATA" | jq -r '.txid')" -echo - -echo "==== Step 9: Broadcast ====" -echo -n "Resulting transaction ID is " -TXID=$(curl -s -X POST "https://blockstream.info/liquidtestnet/api/tx" -d "$FINAL_TX_HEX" 2>/dev/null) -echo "$TXID" -echo -echo "You can view it online at https://blockstream.info/liquidtestnet/tx/$TXID?expand" \ No newline at end of file diff --git a/cli/src/commands/address/mod.rs b/cli/src/commands/address/mod.rs deleted file mode 100644 index a9cc842..0000000 --- a/cli/src/commands/address/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -// TODO: remove when elements updated -#![allow(dead_code)] -pub mod multisig; - -#[derive(Debug)] -enum ElementsNetwork { - Elements, - Liquid, - LiquidTestnet, -} - -impl TryFrom<&str> for ElementsNetwork { - type Error = anyhow::Error; - - fn try_from(s: &str) -> Result { - match s { - "elements" => Ok(ElementsNetwork::Elements), - "liquid" => Ok(ElementsNetwork::Liquid), - "liquid_testnet" => Ok(ElementsNetwork::LiquidTestnet), - _ => Err(anyhow::anyhow!("Unsupported elements network: {}", s)), - } - } -} - -#[derive(Debug)] -enum BitcoinNetwork { - Bitcoin, - Testnet, - Testnet4, - Signet, - Regtest, -} - -impl TryFrom<&str> for BitcoinNetwork { - type Error = anyhow::Error; - - fn try_from(s: &str) -> Result { - match s { - "bitcoin" => Ok(BitcoinNetwork::Bitcoin), - "testnet" => Ok(BitcoinNetwork::Testnet), - "testnet4" => Ok(BitcoinNetwork::Testnet4), - "signet" => Ok(BitcoinNetwork::Signet), - "regtest" => Ok(BitcoinNetwork::Regtest), - _ => Err(anyhow::anyhow!("Unsupported bitcoin network: {}", s)), - } - } -} diff --git a/cli/src/commands/address/multisig.rs b/cli/src/commands/address/multisig.rs deleted file mode 100644 index 36a8c56..0000000 --- a/cli/src/commands/address/multisig.rs +++ /dev/null @@ -1,97 +0,0 @@ -use anyhow::{Context, Result}; -use serde_json::json; -use simplicity::bitcoin::hashes::Hash; -use simplicity::{bitcoin::TapNodeHash, elements::AddressParams}; - -use simplicity::elements::bitcoin::PublicKey; -use simplicity::{bitcoin, elements}; - -use simplicity_unchained_core::utils::{generate_p2tr_address_btc, generate_p2tr_address_elements}; - -use crate::commands::address::{BitcoinNetwork, ElementsNetwork}; - -pub fn execute( - pubkey1: &str, - pubkey2: &str, - user_leaf_hash_hex: &str, - network: &str, -) -> Result<()> { - let pk1_bytes = hex::decode(pubkey1).context("Failed to decode pubkey1")?; - let pk2_bytes = hex::decode(pubkey2).context("Failed to decode pubkey2")?; - - let pk1 = PublicKey::from_slice(&pk1_bytes).context("Invalid pubkey1")?; - let pk2 = PublicKey::from_slice(&pk2_bytes).context("Invalid pubkey2")?; - - let user_leaf_hash_bytes = hex::decode(user_leaf_hash_hex) - .map_err(|e| anyhow::anyhow!("Failed to decode user leaf hash hex: {}", e))?; - - if let Ok(network) = ElementsNetwork::try_from(network) { - let output = execute_over_elements( - &pk1, - &pk2, - elements::hashes::sha256::Hash::from_byte_array( - user_leaf_hash_bytes.as_slice().try_into()?, - ), - network, - )?; - println!("{}", serde_json::to_string_pretty(&output)?); - return Ok(()); - } - - if let Ok(network) = BitcoinNetwork::try_from(network) { - let output = execute_over_bitcoin( - &pk1, - &pk2, - TapNodeHash::from_slice(&user_leaf_hash_bytes).map_err(|e| anyhow::anyhow!(e))?, - network, - )?; - println!("{}", serde_json::to_string_pretty(&output)?); - return Ok(()); - } - - Err(anyhow::anyhow!( - "Unsupported network: {}. Supported networks are elements, liquid, liquid_testnet, bitcoin, testnet, testnet4, signet, regtest", - network - )) -} - -fn execute_over_bitcoin( - service_key: &PublicKey, - user_key: &PublicKey, - user_leaf_hash: bitcoin::TapNodeHash, - network: BitcoinNetwork, -) -> Result { - let network = match network { - BitcoinNetwork::Bitcoin => bitcoin::Network::Bitcoin, - BitcoinNetwork::Testnet => bitcoin::Network::Testnet, - BitcoinNetwork::Testnet4 => bitcoin::Network::Testnet4, - BitcoinNetwork::Signet => bitcoin::Network::Signet, - BitcoinNetwork::Regtest => bitcoin::Network::Regtest, - }; - - let (address, _spend_info) = - generate_p2tr_address_btc(service_key, user_key, user_leaf_hash, network)?; - - Ok(json!({ - "address": address.to_string(), - })) -} - -fn execute_over_elements( - service_key: &PublicKey, - user_key: &PublicKey, - user_leaf_hash: elements::hashes::sha256::Hash, - network: ElementsNetwork, -) -> Result { - let params = match network { - ElementsNetwork::Elements => &AddressParams::ELEMENTS, - ElementsNetwork::Liquid => &AddressParams::LIQUID, - ElementsNetwork::LiquidTestnet => &AddressParams::LIQUID_TESTNET, - }; - - let (address, _spend_info) = - generate_p2tr_address_elements(service_key, user_key, user_leaf_hash, params)?; - Ok(json!({ - "address": address.to_string(), - })) -} diff --git a/cli/src/commands/btc_tx/approval_hash.rs b/cli/src/commands/btc_tx/approval_hash.rs deleted file mode 100644 index ff7dad5..0000000 --- a/cli/src/commands/btc_tx/approval_hash.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::str::FromStr; - -use anyhow::{Context, Result}; -use serde_json::json; -use simplicity::{bitcoin::psbt::Psbt, elements::Script}; -use simplicity_unchained_core::{ - BitcoinNetwork, jets::bitcoin::CoreExtension, program::Program, - runner::bitcoin_execution_environment, -}; - -pub fn execute( - psbt_hex: &str, - input_index: usize, - program: &str, - redeem_script_hex: Option<&str>, - network: &str, -) -> Result<()> { - let psbt = Psbt::deserialize(&hex::decode(psbt_hex).context("Failed to decode PSBT hex")?) - .context("Failed to deserialize PSBT")?; - let program = Program::::from_str(program, None).context("Invalid program")?; - let script = Script::from( - hex::decode(redeem_script_hex.unwrap_or("")).context("Invalid redeem script hex")?, - ); - let network = BitcoinNetwork::from_str(network).map_err(anyhow::Error::msg)?; - let env = bitcoin_execution_environment(&psbt, input_index, program.cmr(), &script, network) - .context("Failed to construct approval context")?; - let output = json!({ - "approval_hash_hex": hex::encode(env.approval_hash), - "cmr_hex": hex::encode(program.cmr().to_byte_array()), - }); - println!("{}", serde_json::to_string_pretty(&output)?); - Ok(()) -} diff --git a/cli/src/commands/btc_tx/create.rs b/cli/src/commands/btc_tx/create.rs deleted file mode 100644 index 3dd7744..0000000 --- a/cli/src/commands/btc_tx/create.rs +++ /dev/null @@ -1,236 +0,0 @@ -use anyhow::{Context, Result, anyhow}; - -use simplicity::bitcoin; - -use bitcoin::{ - Address, Amount, Network, OutPoint, Transaction, TxIn, TxOut, psbt::Psbt, script::ScriptBuf, -}; - -use std::process::Command; -use std::str::FromStr; -use std::thread; -use std::time::Duration; - -#[derive(serde::Deserialize)] -struct BlockstreamTx { - vout: Vec, -} - -#[derive(serde::Deserialize)] -struct BlockstreamVout { - value: Option, - scriptpubkey: Option, -} - -fn fetch_tx_output(txid: &str, vout: u32, network: &str) -> Result { - let api_url = match network { - "bitcoin" => format!("https://blockstream.info/api/tx/{}", txid), - "testnet" => format!("https://blockstream.info/testnet/api/tx/{}", txid), - "testnet4" => format!("https://mempool.space/testnet4/api/tx/{}", txid), - "regtest" => { - return fetch_tx_output_rpc(txid, vout); - } - _ => return Err(anyhow!("Cannot fetch transaction for network: {}", network)), - }; - - const MAX_RETRIES: u32 = 10; - const RETRY_DELAY_SECS: u64 = 3; - - let mut last_error = None; - - for attempt in 1..=MAX_RETRIES { - match reqwest::blocking::get(&api_url) - .context("Failed to fetch transaction") - .and_then(|resp| resp.text().context("Failed to read response")) - .and_then(|response| { - serde_json::from_str::(&response) - .context("Failed to parse transaction JSON") - }) { - Ok(tx_data) => { - let output = tx_data - .vout - .get(vout as usize) - .ok_or_else(|| anyhow!("Output {} not found in transaction", vout))?; - - let value = output - .value - .ok_or_else(|| anyhow!("Output {} has no value", vout))?; - let scriptpubkey_str = output - .scriptpubkey - .as_ref() - .ok_or_else(|| anyhow!("Output {} has no scriptpubkey", vout))?; - - let script_bytes = - hex::decode(scriptpubkey_str).context("Invalid scriptpubkey hex")?; - - return Ok(TxOut { - value: Amount::from_sat(value), - script_pubkey: ScriptBuf::from_bytes(script_bytes), - }); - } - Err(e) => { - last_error = Some(e); - if attempt < MAX_RETRIES { - eprintln!( - "Attempt {}/{} failed, RPC is not responding, retrying in {} seconds...", - attempt, MAX_RETRIES, RETRY_DELAY_SECS - ); - thread::sleep(Duration::from_secs(RETRY_DELAY_SECS)); - } - } - } - } - - Err(anyhow!( - "Server is down or unreachable after {} attempts: {}", - MAX_RETRIES, - last_error.unwrap() - )) -} - -fn fetch_tx_output_rpc(txid: &str, vout: u32) -> Result { - let output = Command::new("bitcoin-cli") - .args(["-regtest", "getrawtransaction", txid, "true"]) - .output() - .context("failed to call bitcoin-cli")?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(anyhow!("bitcoin-cli failed: stderr={}", stderr,)); - } - - let json: serde_json::Value = serde_json::from_slice(&output.stdout).context("invalid JSON")?; - - let vout_obj = json["vout"] - .get(vout as usize) - .ok_or_else(|| anyhow!("vout not found"))?; - - let value_btc = vout_obj["value"] - .as_f64() - .ok_or_else(|| anyhow!("no value"))?; - - let script_hex = vout_obj["scriptPubKey"]["hex"] - .as_str() - .ok_or_else(|| anyhow!("no script"))?; - - let script_bytes = hex::decode(script_hex)?; - - Ok(TxOut { - value: Amount::from_btc(value_btc)?, - script_pubkey: ScriptBuf::from_bytes(script_bytes), - }) -} - -fn get_network_kind(network: &str) -> Result { - match network { - "bitcoin" => Ok(Network::Bitcoin), - "testnet" => Ok(Network::Testnet), - "testnet4" => Ok(Network::Testnet4), - "regtest" => Ok(Network::Regtest), - _ => Err(anyhow!( - "Unsupported network '{}'. Supported networks are: bitcoin, testnet.", - network - )), - } -} - -pub fn execute( - inputs: &[String], - outputs: &[String], - network: &str, - sequence: Option, -) -> Result<()> { - let network_type = get_network_kind(network)?; - - // Parse inputs (txid:vout) - let mut tx_inputs = Vec::new(); - for input_str in inputs { - let parts: Vec<&str> = input_str.split(':').collect(); - if parts.len() != 2 { - return Err(anyhow!( - "Invalid input format. Expected txid:vout, got: {}", - input_str - )); - } - - let txid = - bitcoin::Txid::from_str(parts[0]).context(format!("Invalid txid: {}", parts[0]))?; - let vout: u32 = parts[1] - .parse() - .context(format!("Invalid vout: {}", parts[1]))?; - - tx_inputs.push(TxIn { - previous_output: OutPoint::new(txid, vout), - script_sig: ScriptBuf::new(), - sequence: sequence - .map(bitcoin::Sequence::from_height) - .unwrap_or(bitcoin::Sequence::MAX), - witness: Default::default(), - }); - } - - // Parse outputs (address:value) - let mut tx_outputs = Vec::new(); - for output_str in outputs { - let parts: Vec<&str> = output_str.split(':').collect(); - if parts.len() != 2 { - return Err(anyhow!( - "Invalid output format. Expected address:value, got: {}", - output_str - )); - } - - let value: u64 = parts[1] - .parse() - .context(format!("Invalid value: {}", parts[1]))?; - - let address = Address::from_str(parts[0]) - .context(format!("Invalid address: {}", parts[0]))? - .require_network(network_type) - .context(format!( - "Address {} is not valid for network {}", - parts[0], network - ))?; - - tx_outputs.push(TxOut { - value: Amount::from_sat(value), - script_pubkey: address.script_pubkey(), - }); - } - - let tx = Transaction { - version: bitcoin::transaction::Version::TWO, - lock_time: bitcoin::absolute::LockTime::ZERO, - input: tx_inputs, - output: tx_outputs, - }; - - let mut psbt = Psbt::from_unsigned_tx(tx)?; - - // Populate witness UTXO for each input by fetching from the blockchain - for (i, input_str) in inputs.iter().enumerate() { - let parts: Vec<&str> = input_str.split(':').collect(); - let txid_str = parts[0]; - let vout: u32 = parts[1].parse().unwrap(); - - match fetch_tx_output(txid_str, vout, network) { - Ok(prev_output) => { - psbt.inputs[i].witness_utxo = Some(prev_output); - } - Err(e) => { - return Err(anyhow!("Could not fetch UTXO for input {}: {}", i, e)); - } - } - } - - let output = serde_json::json!({ - "psbt": hex::encode(psbt.serialize()), - "inputs": psbt.inputs.len(), - "outputs": psbt.outputs.len(), - "network": network, - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/commands/btc_tx/mod.rs b/cli/src/commands/btc_tx/mod.rs deleted file mode 100644 index 13564ab..0000000 --- a/cli/src/commands/btc_tx/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod approval_hash; -pub mod create; -pub mod finalize; -pub mod sign; -pub mod spend_user_leaf; diff --git a/cli/src/commands/btc_tx/sign.rs b/cli/src/commands/btc_tx/sign.rs deleted file mode 100644 index 73a9136..0000000 --- a/cli/src/commands/btc_tx/sign.rs +++ /dev/null @@ -1,121 +0,0 @@ -use anyhow::{Context, Result}; -use serde_json::json; -use simplicity::bitcoin::taproot::{LeafVersion, TaprootBuilder}; -use simplicity::bitcoin::{self, TapNodeHash}; -use simplicity::bitcoin::{PublicKey, hashes::Hash, psbt::Psbt, sighash::SighashCache}; -use simplicity::elements::secp256k1_zkp::{Message, Secp256k1, SecretKey}; -use simplicity_unchained_core::utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc}; - -pub fn execute( - psbt_hex: &str, - secret_key_hex: &str, - input_index: usize, - cosigner_pubkey_hex: Option<&str>, - user_leaf_hash: &str, -) -> Result<()> { - let psbt_bytes = hex::decode(psbt_hex).context("Failed to decode PSBT hex")?; - let mut psbt: Psbt = Psbt::deserialize(&psbt_bytes).context("Failed to deserialize PSBT")?; - - if input_index >= psbt.inputs.len() { - return Err(anyhow::anyhow!( - "Input index {} out of bounds (PSBT has {} inputs)", - input_index, - psbt.inputs.len() - )); - } - - let secret_key_bytes = - hex::decode(secret_key_hex).context("Failed to decode secret key hex")?; - let secret_key = SecretKey::from_slice(&secret_key_bytes).context("Invalid secret key")?; - - let secp = Secp256k1::new(); - - let public_key = PublicKey::from_private_key( - &secp, - &simplicity::elements::bitcoin::PrivateKey { - compressed: true, - network: simplicity::elements::bitcoin::NetworkKind::Main, - inner: secret_key, - }, - ); - - let tx = psbt.clone().extract_tx()?; - - let mut sighash_cache = SighashCache::new(&tx); - - let cosigner_pubkey_bytes = hex::decode( - cosigner_pubkey_hex - .ok_or_else(|| anyhow::anyhow!("--cosigner-pubkey required for P2TR"))?, - ) - .context("Failed to decode cosigner pubkey hex")?; - let cosigner_pubkey = - PublicKey::from_slice(&cosigner_pubkey_bytes).context("Invalid cosigner pubkey")?; - - let multisig_leaf = p2tr_multisig_leaf_btc(&cosigner_pubkey, &public_key); - - let user_leaf = TapNodeHash::from_slice( - &hex::decode(user_leaf_hash).context("Failed to decode user leaf script hex")?, - )?; - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf.clone()) - .context("Failed to add multisig leaf")? - .add_hidden_node(1, user_leaf) - .context("Failed to add recovery leaf")? - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| anyhow::anyhow!("Failed to finalize taproot"))?; - - let control_block = spend_info - .control_block(&(multisig_leaf.clone(), LeafVersion::TapScript)) - .ok_or_else(|| anyhow::anyhow!("Failed to get control block"))?; - - let prevouts: Vec<_> = psbt - .inputs - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| anyhow::anyhow!("Missing witness_utxo")) - }) - .collect::>()?; - - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &bitcoin::sighash::Prevouts::All(&prevouts), - bitcoin::sighash::ScriptPath::with_defaults(&multisig_leaf), - bitcoin::TapSighashType::Default, - ) - .context("Failed to compute recovery sighash")?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let keypair = bitcoin::key::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr(&msg, &keypair); - - let tap_sig = bitcoin::taproot::Signature { - signature, - sighash_type: bitcoin::TapSighashType::Default, - }; - - let leaf_hash = bitcoin::sighash::ScriptPath::with_defaults(&multisig_leaf).leaf_hash(); - let user_xonly = public_key.inner.x_only_public_key().0; - - let input = &mut psbt.inputs[input_index]; - input - .tap_script_sigs - .insert((user_xonly, leaf_hash), tap_sig); - input - .tap_scripts - .insert(control_block, (multisig_leaf, LeafVersion::TapScript)); - - let output = json!({ - "psbt": hex::encode(psbt.serialize()), - "signature_hex": hex::encode(signature.as_ref()), - "public_key_hex": hex::encode(public_key.to_bytes()), - "input_index": input_index, - "partial_sigs_count": 0, - }); - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/commands/btc_tx/spend_user_leaf.rs b/cli/src/commands/btc_tx/spend_user_leaf.rs deleted file mode 100644 index 7bff8b1..0000000 --- a/cli/src/commands/btc_tx/spend_user_leaf.rs +++ /dev/null @@ -1,125 +0,0 @@ -use anyhow::{Context, Result}; -use bitcoin::taproot::TaprootBuilder; -use bitcoin::{ - TapLeafHash, TapSighashType, - key::UntweakedKeypair, - psbt::Psbt, - script::ScriptBuf, - secp256k1::{Message, PublicKey, Secp256k1, SecretKey}, - sighash::{Prevouts, SighashCache}, - taproot::{LeafVersion, Signature as TapSig}, -}; -use serde_json::json; -use simplicity::bitcoin::base64::Engine; -use simplicity::bitcoin::hashes::Hash; -use simplicity::bitcoin::{self, base64}; -use simplicity_unchained_core::utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc}; -use std::str::FromStr; - -use crate::demo_script::csv::validate_sequence; - -pub fn execute( - psbt_hex: &str, - secret_key_hex: &str, - user_leaf_script_hex: &str, - cosigner_pubkey_hex: &str, - input_index: usize, - _network: &str, -) -> Result<()> { - let secp = Secp256k1::new(); - - let psbt_bytes = hex::decode(psbt_hex).context("Failed to decode PSBT hex")?; - let mut psbt = Psbt::deserialize(&psbt_bytes).context("Failed to deserialize PSBT")?; - - let secret_key = SecretKey::from_str(secret_key_hex).context("Failed to parse secret key")?; - let keypair = UntweakedKeypair::from_secret_key(&secp, &secret_key); - let (user_xonly, _) = keypair.x_only_public_key(); - - let user_leaf_bytes = - hex::decode(user_leaf_script_hex).context("Failed to decode user leaf script hex")?; - let user_leaf_script = ScriptBuf::from_bytes(user_leaf_bytes); - - let cosigner_pubkey = - PublicKey::from_str(cosigner_pubkey_hex).context("Failed to parse cosigner pubkey")?; - let cosigner_full_pk = bitcoin::PublicKey::new(cosigner_pubkey); - let user_full_pk = bitcoin::PublicKey::new(keypair.public_key()); - - let multisig_leaf = p2tr_multisig_leaf_btc(&cosigner_full_pk, &user_full_pk); - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf) - .context("Failed to add multisig leaf")? - .add_leaf(1, user_leaf_script.clone()) - .context("Failed to add user leaf")? - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| anyhow::anyhow!("Failed to finalize taproot"))?; - - let control_block = spend_info - .control_block(&(user_leaf_script.clone(), LeafVersion::TapScript)) - .ok_or_else(|| anyhow::anyhow!("Failed to get control block for user leaf"))?; - - let tx = psbt - .clone() - .extract_tx() - .context("Failed to extract transaction")?; - - let input = tx - .input - .get(input_index) - .ok_or_else(|| anyhow::anyhow!("Input index {} out of bounds", input_index))?; - validate_sequence( - user_leaf_script.as_bytes(), - i64::from(tx.version.0), - input.sequence.0, - input_index, - )?; - - let prevouts = psbt - .inputs - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| anyhow::anyhow!("Missing witness_utxo")) - }) - .collect::>>()?; - - let mut sighash_cache = SighashCache::new(&tx); - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &Prevouts::All(&prevouts), - bitcoin::sighash::ScriptPath::with_defaults(&user_leaf_script), - TapSighashType::Default, - ) - .context("Failed to compute sighash")?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let signature = secp.sign_schnorr(&msg, &keypair); - - let tap_sig = TapSig { - signature, - sighash_type: TapSighashType::Default, - }; - let leaf_hash = TapLeafHash::from_script(&user_leaf_script, LeafVersion::TapScript); - - psbt.inputs[input_index] - .tap_script_sigs - .insert((user_xonly, leaf_hash), tap_sig); - - psbt.inputs[input_index] - .tap_scripts - .insert(control_block, (user_leaf_script, LeafVersion::TapScript)); - - let enc_engine = base64::engine::general_purpose::STANDARD; - let output = json!({ - "psbt": hex::encode(psbt.serialize()), - "psbt_base64": enc_engine.encode(psbt.serialize()), - "signature": hex::encode(signature.as_ref()), - "input_index": input_index, - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/commands/liquid_policy.rs b/cli/src/commands/liquid_policy.rs new file mode 100644 index 0000000..2a9319f --- /dev/null +++ b/cli/src/commands/liquid_policy.rs @@ -0,0 +1,371 @@ +//! New, proof-verified Liquid policy CLI. No legacy command alias. +use anyhow::{Context as _, Result, ensure}; +use clap::Subcommand; +use serde::Deserialize; +use serde_json::{Value, json}; +use simplicity::base64::{Engine, engine::general_purpose::STANDARD}; +use simplicity_unchained_policy::liquid::{ + self as policy, + elements::{ + Address, + encode::{deserialize, serialize}, + pset::PartiallySignedTransaction as Pset, + secp256k1_zkp::{PublicKey, Secp256k1, SecretKey, XOnlyPublicKey, rand::rngs::OsRng}, + taproot::TapNodeHash, + }, +}; +use std::{fs, io::Read, path::PathBuf, str::FromStr, time::Duration}; + +#[derive(Subcommand)] +pub enum Command { + /// Print this binary's canonical liquid-policy-v1 registry + Registry, + /// Check service profile/network/base-key identity against a local request + Capabilities { + #[arg(long)] + request_file: PathBuf, + #[arg(long, default_value = "http://127.0.0.1:30431")] + service: String, + }, + /// Derive a Liquid profile-bound service key locally + Tweak { + #[arg(long)] + request_file: PathBuf, + }, + /// Reconstruct the cooperative funding address, optionally confidential + Address { + #[arg(long)] + request_file: PathBuf, + }, + /// Generate a local receiving blinding key; protect the private output + GenerateBlindingKey, + /// Build/blind a PSET with full parents and browser/local unblinding keys + Create { + #[arg(long)] + request_file: PathBuf, + }, + /// Verify all evidence/proofs/signatures and prepare a local context + Prepare { + #[arg(long)] + request_file: PathBuf, + }, + /// Compute input-specific and transaction-wide approval hashes locally + ApprovalHash { + #[arg(long)] + request_file: PathBuf, + }, + /// Execute a witnessed policy locally without signing + Evaluate { + #[arg(long)] + request_file: PathBuf, + }, + /// Request service signing and independently verify the returned PSET/signature + Sign { + #[arg(long)] + request_file: PathBuf, + #[arg(long, default_value = "http://127.0.0.1:30431")] + service: String, + }, + /// Reverify disclosures/service signature and add a local user signature + UserSign { + #[arg(long)] + request_file: PathBuf, + #[arg(long)] + secret_key_file: PathBuf, + }, + /// Reverify disclosures and both signatures for every input, then finalize + Finalize { + #[arg(long)] + request_file: PathBuf, + }, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct Request { + #[serde(default = "profile")] + profile: String, + #[serde(default)] + profile_hash: Option, + #[serde(default)] + network: policy::Network, + program: String, + #[serde(default)] + redeem_script_hex: String, + base_public_key_hex: String, + #[serde(default)] + pset_hex: Option, + #[serde(default)] + input_index: usize, + #[serde(default)] + user_pubkey: Option, + #[serde(default)] + user_leaf_hash_hex: Option, + #[serde(default)] + blinding_public_key_hex: Option, + #[serde(default)] + witness: Option, +} +fn profile() -> String { + policy::PROFILE_NAME.into() +} +fn read(path: PathBuf) -> Result { + let size = fs::metadata(&path)?.len(); + ensure!( + size <= (simplicity_unchained_policy::MAX_REQUEST_BYTES * 2) as u64, + "request exceeds evidence limit" + ); + Ok(serde_json::from_slice(&fs::read(path)?)?) +} +fn key(s: &str) -> Result { + match s.len() { + 64 => Ok(XOnlyPublicKey::from_str(s)?), + 66 => Ok(PublicKey::from_str(s)?.x_only_public_key().0), + _ => anyhow::bail!("expected compressed/x-only public key"), + } +} +impl Request { + fn binding(&self) -> Result<(Vec, Vec, XOnlyPublicKey)> { + ensure!( + self.profile == policy::PROFILE_NAME, + "explicit Liquid profile mismatch" + ); + ensure!( + self.profile_hash + .as_ref() + .is_none_or(|h| *h == hex::encode(policy::profile_hash())), + "Liquid fingerprint mismatch" + ); + Ok(( + STANDARD.decode(&self.program)?, + hex::decode(&self.redeem_script_hex)?, + key(&self.base_public_key_hex)?, + )) + } + fn recovery(&self) -> Result { + let bytes: [u8; 32] = hex::decode( + self.user_leaf_hash_hex + .as_ref() + .context("missing user_leaf_hash_hex")?, + )? + .try_into() + .map_err(|_| anyhow::anyhow!("recovery hash must be 32 bytes"))?; + Ok(TapNodeHash::from_byte_array(bytes)) + } + fn prepare(&self) -> Result<(Pset, policy::PreparedPolicy)> { + let (program, data, base) = self.binding()?; + let pset_hex = self.pset_hex.as_ref().context("missing pset_hex")?; + ensure!( + pset_hex.len() <= simplicity_unchained_policy::MAX_REQUEST_BYTES * 2, + "PSET exceeds profile limit" + ); + let pset: Pset = deserialize(&hex::decode(pset_hex)?)?; + let prepared = policy::PreparedPolicy::new( + &program, + &pset, + self.input_index, + &data, + self.network, + base, + key(self.user_pubkey.as_ref().context("missing user_pubkey")?)?, + self.recovery()?, + )?; + Ok((pset, prepared)) + } + fn witness(&self) -> Result> { + Ok(STANDARD.decode( + self.witness + .as_ref() + .context("witness required; use empty string for no witness")?, + )?) + } + fn remote(&self) -> Value { + json!({"profile_hash":hex::encode(policy::profile_hash()),"network":self.network.name(),"program":self.program,"redeem_script_hex":self.redeem_script_hex,"pset_hex":self.pset_hex,"input_index":self.input_index,"user_pubkey":self.user_pubkey,"user_leaf_hash_hex":self.user_leaf_hash_hex,"witness":self.witness}) + } +} +fn http(service: &str, operation: &str, body: Option) -> Result { + let origin = reqwest::Url::parse(service)?; + ensure!( + matches!(origin.scheme(), "http" | "https") + && origin.username().is_empty() + && origin.password().is_none() + && origin.query().is_none() + && origin.fragment().is_none() + && origin.path() == "/", + "service must be a bare HTTP(S) origin without credentials/query" + ); + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(10)) + .redirect(reqwest::redirect::Policy::none()) + .build()?; + let url = format!( + "{}/simplicity-unchained/policy/liquid/{operation}", + service.trim_end_matches('/') + ); + let response = if let Some(body) = body { + client.post(url).json(&body).send()? + } else { + client.get(url).send()? + }; + let status = response.status(); + let mut bytes = Vec::new(); + response + .take((simplicity_unchained_policy::MAX_REQUEST_BYTES + 1) as u64) + .read_to_end(&mut bytes)?; + ensure!( + bytes.len() <= simplicity_unchained_policy::MAX_REQUEST_BYTES, + "service response exceeds limit" + ); + ensure!( + status.is_success(), + "service failed ({status}): {}", + String::from_utf8_lossy(&bytes) + ); + Ok(serde_json::from_slice(&bytes)?) +} +fn capabilities(r: &Request, service: &str) -> Result { + let (_, _, base) = r.binding()?; + let v = http(service, "capabilities", None)?; + ensure!( + v["profile"] == policy::PROFILE_NAME + && v["profile_hash"] == hex::encode(policy::profile_hash()), + "service profile/fingerprint mismatch" + ); + ensure!( + v["network"] == r.network.name() + && v["genesis_hash"] == hex::encode(r.network.genesis_hash().to_byte_array()), + "service network/genesis mismatch" + ); + ensure!( + v["base_public_key_hex"] == base.to_string() && v["manifest"] == policy::manifest(), + "service base key/manifest mismatch" + ); + Ok(v) +} + +pub fn execute(command: Command) -> Result<()> { + let value = match command { + Command::Registry => { + json!({"profile":policy::PROFILE_NAME,"profile_hash":hex::encode(policy::profile_hash()),"manifest":policy::manifest()}) + } + Command::GenerateBlindingKey => { + let secret = SecretKey::new(&mut OsRng); + json!({"blinding_secret_key_hex":hex::encode(secret.secret_bytes()),"blinding_public_key_hex":PublicKey::from_secret_key(&Secp256k1::new(),&secret).to_string()}) + } + Command::Create { request_file } => { + let request: policy::CreateRequest = read(request_file)?; + let pset = policy::create_pset(&request)?; + json!({"pset_hex":hex::encode(serialize(&pset)),"network":request.network.name(),"inputs":pset.inputs().len(),"outputs":pset.outputs().len()}) + } + Command::Capabilities { + request_file, + service, + } => capabilities(&read(request_file)?, &service)?, + Command::Tweak { request_file } => { + let r: Request = read(request_file)?; + let (program, data, base) = r.binding()?; + let cmr = policy::policy_cmr(&program)?.to_byte_array(); + json!({"profile":policy::PROFILE_NAME,"profile_hash":hex::encode(policy::profile_hash()),"network":r.network.name(),"genesis_hash":hex::encode(r.network.genesis_hash().to_byte_array()),"cmr":hex::encode(cmr),"signing_commitment":hex::encode(policy::signing_commitment(cmr,&data,r.network)?),"public_key_hex":policy::tweak_public_key(base,cmr,&data,r.network)?.to_string(),"base_public_key_hex":base.to_string()}) + } + Command::Address { request_file } => { + let r: Request = read(request_file)?; + let (program, data, base) = r.binding()?; + let output = policy::cooperative_output( + base, + policy::policy_cmr(&program)?.to_byte_array(), + &data, + r.network, + key(r.user_pubkey.as_ref().context("missing user_pubkey")?)?, + r.recovery()?, + )?; + let blinding = r + .blinding_public_key_hex + .as_deref() + .map(PublicKey::from_str) + .transpose()?; + let address = + Address::from_script(&output.script_pubkey, blinding, r.network.address_params()) + .context("cannot encode funding address")?; + json!({"address":address.to_string(),"script_pubkey_hex":hex::encode(output.script_pubkey.as_bytes()),"profile":policy::PROFILE_NAME,"profile_hash":hex::encode(policy::profile_hash()),"network":r.network.name()}) + } + Command::Prepare { request_file } | Command::ApprovalHash { request_file } => { + let r: Request = read(request_file)?; + json!({"context":r.prepare()?.1.summary()}) + } + Command::Evaluate { request_file } => { + let r: Request = read(request_file)?; + let (_, prepared) = r.prepare()?; + json!({"accepted":true,"context":prepared.summary(),"execution_cost_milliweight":prepared.evaluate_default(&r.witness()?)?}) + } + Command::Sign { + request_file, + service, + } => { + let r: Request = read(request_file)?; + let (_, before) = r.prepare()?; + let cost = before.evaluate_default(&r.witness()?)?; + capabilities(&r, &service)?; + let response = http(&service, "sign", Some(r.remote()))?; + let encoded = response["pset_hex"] + .as_str() + .context("missing signed PSET")?; + ensure!( + encoded.len() <= simplicity_unchained_policy::MAX_REQUEST_BYTES * 2, + "signed PSET exceeds limit" + ); + let signed: Pset = deserialize(&hex::decode(encoded)?)?; + before.context().revalidate(&signed)?; + ensure!( + response["context"] == serde_json::to_value(before.summary())?, + "service context changed" + ); + ensure!( + response["execution_cost_milliweight"] == cost, + "service execution cost changed" + ); + ensure!( + response["public_key_hex"] + == before.context().cooperative().service_pubkey.to_string() + && response["input_index"] == r.input_index, + "service signer/index changed" + ); + let sig = signed.inputs()[r.input_index] + .tap_script_sigs + .get(&( + before.context().cooperative().service_pubkey, + before.context().cooperative().leaf_hash, + )) + .context("missing verified service signature")?; + ensure!( + response["signature_hex"] == hex::encode(sig.sig.as_ref()), + "service signature response mismatch" + ); + response + } + Command::UserSign { + request_file, + secret_key_file, + } => { + let r: Request = read(request_file)?; + let (mut pset, prepared) = r.prepare()?; + ensure!( + pset.inputs()[r.input_index].tap_script_sigs.contains_key(&( + prepared.context().cooperative().service_pubkey, + prepared.context().cooperative().leaf_hash + )), + "missing verified service signature" + ); + let secret = SecretKey::from_str(fs::read_to_string(secret_key_file)?.trim())?; + let sig = policy::add_signature(prepared.context(), &mut pset, secret, false)?; + json!({"pset_hex":hex::encode(serialize(&pset)),"signature_hex":hex::encode(sig.sig.as_ref()),"public_key_hex":prepared.context().user_pubkey().to_string(),"input_index":r.input_index,"context":prepared.summary()}) + } + Command::Finalize { request_file } => { + let r: Request = read(request_file)?; + let (pset, prepared) = r.prepare()?; + let tx = policy::finalize(prepared.context(), &pset)?; + json!({"transaction_hex":hex::encode(serialize(&tx)),"txid":tx.txid().to_string(),"finalized":true,"inputs":tx.input.len(),"outputs":tx.output.len()}) + } + }; + println!("{}", serde_json::to_string_pretty(&value)?); + Ok(()) +} diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 02f3b04..35fd2b4 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -1,4 +1,3 @@ -pub mod address; -pub mod btc_tx; pub mod keypair; -pub mod tx; +pub mod liquid_policy; +pub mod policy; diff --git a/cli/src/commands/policy.rs b/cli/src/commands/policy.rs new file mode 100644 index 0000000..7c1b68e --- /dev/null +++ b/cli/src/commands/policy.rs @@ -0,0 +1,895 @@ +mod finalize; + +use std::{collections::HashMap, io::Read, path::PathBuf, time::Duration}; + +use anyhow::{Context as _, Result, bail, ensure}; +use clap::{Args, Subcommand}; +use reqwest::{ + Url, + blocking::{Client, Response}, + redirect::Policy, +}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use serde_json::{Value, json}; +use simplicity::{ + base64::prelude::{BASE64_STANDARD, Engine as _}, + bitcoin::{ + Address, Network, Psbt, PublicKey, TapLeafHash, TapNodeHash, Transaction, XOnlyPublicKey, + consensus::{deserialize, serialize}, + hashes::Hash, + secp256k1::{Keypair, Message, Secp256k1, SecretKey, schnorr}, + taproot::LeafVersion, + }, +}; +use simplicity_unchained_core::policy::{PreparedPolicy, policy_cmr}; +use simplicity_unchained_policy::{ + ContextSummary, Limits, MAX_EXECUTION_COST_MILLIWEIGHT, MAX_INPUTS, MAX_OUTPUTS, + MAX_REQUEST_BYTES, PROFILE_NAME, cooperative_output, genesis_hash, manifest, profile_hash, + signing_commitment, tweak_public_key, +}; + +const POLICY_PATH: &str = "/simplicity-unchained/policy/bitcoin"; +const MAX_BODY_BYTES: usize = MAX_REQUEST_BYTES; + +#[derive(Subcommand)] +pub enum PolicyCommand { + /// Derive the exact policy-bound funding address locally before funding + Address { + #[command(flatten)] + policy: ProgramArgs, + #[arg(long)] + base_public_key: String, + #[arg(long)] + user_pubkey: String, + #[arg(long)] + user_leaf_hash_hex: String, + }, + /// Build a canonical Bitcoin CSV recovery leaf before deriving the address + BuildCsvLeaf { + #[arg(long)] + user_pubkey: String, + #[arg(long)] + timelock: u16, + }, + /// Check the exact compiled profile, manifest and configured service network + Capabilities { + #[command(flatten)] + identity: IdentityArgs, + #[command(flatten)] + service: ServiceArgs, + }, + /// Derive the profile-bound service key before funding + Tweak { + #[command(flatten)] + policy: ProgramArgs, + #[command(flatten)] + service: ServiceArgs, + /// Optionally pin the service's known untweaked x-only or compressed key + #[arg(long)] + base_public_key: Option, + }, + /// Verify full parent evidence and compare the service's prepared context locally + Prepare { + #[command(flatten)] + transaction: TransactionArgs, + #[command(flatten)] + service: ServiceArgs, + }, + /// Evaluate a witnessed policy without requesting a transaction signature + Evaluate { + #[command(flatten)] + transaction: TransactionArgs, + #[command(flatten)] + service: ServiceArgs, + /// Standard-base64 encoded Simplicity witness (an empty string is allowed) + #[arg(long)] + witness: String, + }, + /// Compute J53/J54 locally from verified parents and a known base service key + ApprovalHash { + #[command(flatten)] + transaction: TransactionArgs, + }, + /// Revalidate and evaluate the exact policy, then verify the service's signature + Sign { + #[command(flatten)] + transaction: TransactionArgs, + #[command(flatten)] + service: ServiceArgs, + /// Standard-base64 encoded Simplicity witness (an empty string is allowed) + #[arg(long)] + witness: String, + }, + /// Add the user's cooperative signature after verifying the policy context + UserSign { + #[command(flatten)] + transaction: TransactionArgs, + /// File containing the user's 32-byte secret key as hex; keep it private + #[arg(long)] + secret_key_file: PathBuf, + }, + /// Verify the policy binding and both signatures for every input, then finalize + Finalize { + #[command(flatten)] + transaction: TransactionArgs, + }, +} + +#[derive(Args)] +pub struct IdentityArgs { + /// Explicit policy profile; no legacy/profile fallback is permitted + #[arg(long, default_value = PROFILE_NAME)] + profile: String, + /// Expected registry fingerprint; defaults to this binary's compiled fingerprint + #[arg(long)] + profile_hash: Option, + /// Expected Bitcoin network; must exactly match the configured service + #[arg(long, default_value = "testnet4")] + network: String, +} + +#[derive(Args)] +pub struct ServiceArgs { + /// Service origin, without an endpoint path + #[arg(long, default_value = "http://127.0.0.1:30431")] + service: String, +} + +#[derive(Args)] +pub struct ProgramArgs { + #[command(flatten)] + identity: IdentityArgs, + /// Standard-base64 encoded program compiled for bitcoin-policy-v1, not legacy + #[arg(long)] + program: String, + /// Exact committed script-data bytes; omission means an empty script + #[arg(long, default_value = "")] + redeem_script_hex: String, +} + +#[derive(Args)] +pub struct TransactionArgs { + #[command(flatten)] + policy: ProgramArgs, + /// Hex serialized PSBT; full parent transactions are required for every input + #[arg(long)] + psbt: String, + #[arg(long)] + input_index: usize, + /// Cooperative user's compressed or x-only public key, in hex + #[arg(long)] + user_pubkey: String, + /// Exact 32-byte recovery TapNodeHash, in raw-byte hex (not explorer byte order) + #[arg(long)] + user_leaf_hash_hex: String, + /// Known untweaked service public key, compressed or x-only hex + #[arg(long)] + base_public_key: String, + /// Explicit Esplora API base; fetch missing full parents from /tx//raw + #[arg(long, conflicts_with = "offline")] + esplora_url: Option, + /// Forbid parent fetching; all non_witness_utxo evidence must already be present + #[arg(long)] + offline: bool, +} + +#[derive(Serialize)] +struct ProgramRequest { + profile_hash: String, + network: String, + program: String, + redeem_script_hex: String, +} + +#[derive(Serialize)] +struct TransactionRequest { + #[serde(flatten)] + policy: ProgramRequest, + psbt_hex: String, + input_index: usize, + user_pubkey: String, + user_leaf_hash_hex: String, + #[serde(skip_serializing_if = "Option::is_none")] + witness: Option, +} + +#[derive(Deserialize, Serialize)] +struct CapabilitiesResponse { + profile: String, + profile_hash: String, + network: String, + genesis_hash: String, + base_public_key_hex: String, + manifest: Value, + limits: Value, + provenance: String, +} + +#[derive(Deserialize, Serialize)] +struct TweakResponse { + profile: String, + profile_hash: String, + network: String, + genesis_hash: String, + cmr: String, + signing_commitment: String, + public_key_hex: String, + base_public_key_hex: String, +} + +#[derive(Deserialize, Serialize)] +struct PrepareResponse { + context: ContextSummary, +} + +#[derive(Deserialize, Serialize)] +struct EvaluateResponse { + accepted: bool, + context: ContextSummary, + execution_cost_milliweight: u64, +} + +#[derive(Deserialize, Serialize)] +struct SignResponse { + psbt_hex: String, + signature_hex: String, + public_key_hex: String, + input_index: usize, + context: ContextSummary, + execution_cost_milliweight: u64, +} + +struct ServiceClient { + client: Client, + base: String, +} + +impl ServiceClient { + fn new(args: &ServiceArgs) -> Result { + let origin = validate_url(&args.service)?; + ensure!( + origin.path() == "/", + "--service must be an origin without an endpoint path" + ); + Ok(Self { + client: http_client()?, + base: format!("{}{}", origin.as_str().trim_end_matches('/'), POLICY_PATH), + }) + } + + fn capabilities( + &self, + identity: &IdentityArgs, + expected_key: Option, + ) -> Result { + let response = self + .client + .get(format!("{}/capabilities", self.base)) + .send() + .context("Failed to fetch policy capabilities")?; + let result: CapabilitiesResponse = decode_response(response)?; + check_identity( + identity, + &result.profile, + &result.profile_hash, + &result.network, + &result.genesis_hash, + )?; + let compiled_manifest = manifest(); + ensure!( + result.manifest == serde_json::to_value(&compiled_manifest)?, + "Service policy manifest differs from the compiled registry" + ); + let limits: Limits = serde_json::from_value(result.limits.clone()) + .context("Invalid service policy limits")?; + ensure!( + limits == compiled_manifest.limits, + "Service policy limits differ from the compiled profile" + ); + ensure!( + result.provenance == "parent-txid-verified", + "Service policy verification provenance mismatch" + ); + let base_key = parse_public_key(&result.base_public_key_hex)?; + if let Some(expected_key) = expected_key { + ensure!( + base_key == expected_key, + "Service base public key does not match --base-public-key" + ); + } + Ok(result) + } + + fn post(&self, operation: &str, body: &T) -> Result { + let body = serde_json::to_vec(body)?; + ensure!( + body.len() <= MAX_BODY_BYTES, + "Policy request exceeds the 2 MiB body limit" + ); + let response = self + .client + .post(format!("{}/{}", self.base, operation)) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(body) + .send() + .with_context(|| format!("Policy {operation} request failed"))?; + decode_response(response) + } +} + +pub fn execute(command: PolicyCommand) -> Result<()> { + match run(command) { + Ok(output) => { + println!("{}", serde_json::to_string_pretty(&output)?); + Ok(()) + } + Err(error) => { + println!("{}", json!({ "error": format!("{error:#}") })); + Err(error) + } + } +} + +fn run(command: PolicyCommand) -> Result { + match command { + PolicyCommand::Address { + policy, + base_public_key, + user_pubkey, + user_leaf_hash_hex, + } => { + let (network, program, script) = policy.validate()?; + let cmr = policy_cmr(&program)?; + let output = cooperative_output( + parse_public_key(&base_public_key)?, + cmr.to_byte_array(), + &script, + network, + parse_public_key(&user_pubkey)?, + parse_recovery_hash(&user_leaf_hash_hex)?, + )?; + Ok(json!({ + "profile": PROFILE_NAME, + "profile_hash": hex::encode(profile_hash()), + "network": policy.identity.network, + "genesis_hash": hex::encode(genesis_hash(network)?), + "cmr": hex::encode(cmr.to_byte_array()), + "signing_commitment": hex::encode(signing_commitment(cmr.to_byte_array(), &script, network)?), + "address": Address::from_script(&output.script_pubkey, network)?.to_string(), + "script_pubkey_hex": hex::encode(output.script_pubkey.as_bytes()), + })) + } + PolicyCommand::BuildCsvLeaf { + user_pubkey, + timelock, + } => { + ensure!(timelock > 0, "Recovery timelock must be at least one block"); + let script = simplicity::bitcoin::script::Builder::new() + .push_int(i64::from(timelock)) + .push_opcode(simplicity::bitcoin::opcodes::all::OP_CSV) + .push_opcode(simplicity::bitcoin::opcodes::all::OP_DROP) + .push_x_only_key(&parse_public_key(&user_pubkey)?) + .push_opcode(simplicity::bitcoin::opcodes::all::OP_CHECKSIG) + .into_script(); + Ok(json!({ + "script": hex::encode(script.as_bytes()), + "leaf_hash": hex::encode(TapLeafHash::from_script(&script, LeafVersion::TapScript).to_byte_array()), + })) + } + PolicyCommand::Capabilities { identity, service } => { + identity.validate()?; + let result = ServiceClient::new(&service)?.capabilities(&identity, None)?; + Ok(serde_json::to_value(result)?) + } + PolicyCommand::Tweak { + policy, + service, + base_public_key, + } => { + let (network, program, script) = policy.validate()?; + let expected_key = base_public_key + .as_deref() + .map(parse_public_key) + .transpose()?; + let client = ServiceClient::new(&service)?; + let capabilities = client.capabilities(&policy.identity, expected_key)?; + let base_key = parse_public_key(&capabilities.base_public_key_hex)?; + let cmr = policy_cmr(&program).context("Invalid policy-profile program")?; + let cmr_bytes = cmr.to_byte_array(); + let expected_commitment = signing_commitment(cmr_bytes, &script, network)?; + let expected_tweaked = tweak_public_key(base_key, cmr_bytes, &script, network)?; + let result: TweakResponse = client.post("tweak", &policy.request())?; + check_identity( + &policy.identity, + &result.profile, + &result.profile_hash, + &result.network, + &result.genesis_hash, + )?; + ensure!( + result.cmr == hex::encode(cmr_bytes) + && result.signing_commitment == hex::encode(expected_commitment) + && result.public_key_hex == expected_tweaked.to_string() + && parse_public_key(&result.base_public_key_hex)? == base_key, + "Service tweak does not match the locally derived policy commitment/key" + ); + Ok(serde_json::to_value(result)?) + } + PolicyCommand::ApprovalHash { transaction } => { + let local = LocalTransaction::new(&transaction)?; + Ok(serde_json::to_value(PrepareResponse { + context: local.prepared.summary(), + })?) + } + PolicyCommand::Prepare { + transaction, + service, + } => { + let local = LocalTransaction::new(&transaction)?; + let client = checked_service(&service, &transaction, &local)?; + let result: PrepareResponse = + client.post("prepare", &local.request(&transaction, None))?; + check_context(&result.context, &local.prepared.summary())?; + Ok(serde_json::to_value(result)?) + } + PolicyCommand::Evaluate { + transaction, + service, + witness, + } => { + let witness_bytes = decode_base64(&witness, "witness")?; + let local = LocalTransaction::new(&transaction)?; + let evaluated = local.prepared.evaluate_default(&witness_bytes)?; + let client = checked_service(&service, &transaction, &local)?; + let result: EvaluateResponse = + client.post("evaluate", &local.request(&transaction, Some(witness)))?; + ensure!(result.accepted, "Service did not accept the policy"); + check_context(&result.context, &local.prepared.summary())?; + check_cost(result.execution_cost_milliweight)?; + ensure!( + result.execution_cost_milliweight == u64::from(evaluated.cost_milliweight), + "Service execution cost differs from local policy evaluation" + ); + Ok(serde_json::to_value(result)?) + } + PolicyCommand::Sign { + transaction, + service, + witness, + } => { + let witness_bytes = decode_base64(&witness, "witness")?; + let local = LocalTransaction::new(&transaction)?; + let evaluated = local.prepared.evaluate_default(&witness_bytes)?; + let client = checked_service(&service, &transaction, &local)?; + let result: SignResponse = + client.post("sign", &local.request(&transaction, Some(witness)))?; + check_context(&result.context, &local.prepared.summary())?; + check_cost(result.execution_cost_milliweight)?; + ensure!( + result.execution_cost_milliweight == u64::from(evaluated.cost_milliweight), + "Service execution cost differs from local policy evaluation" + ); + local.verify_signature(&transaction, &result)?; + Ok(serde_json::to_value(result)?) + } + PolicyCommand::UserSign { + transaction, + secret_key_file, + } => { + let mut local = LocalTransaction::new(&transaction)?; + let secret = std::fs::read_to_string(secret_key_file) + .context("Failed to read user secret key file")?; + let secret = hex::decode(secret.trim()).context("Invalid user secret key hex")?; + let key = Keypair::from_secret_key( + &Secp256k1::new(), + &SecretKey::from_slice(&secret).context("Invalid user secret key")?, + ); + let context = local.prepared.context(); + ensure!( + key.x_only_public_key().0 == context.user_pubkey(), + "User secret key does not match the committed user public key" + ); + let signature = Secp256k1::new().sign_schnorr( + &Message::from_digest(context.cooperative_sighash_default()), + &key, + ); + let input = &mut local.psbt.inputs[transaction.input_index]; + input.tap_script_sigs.insert( + (context.user_pubkey(), context.cooperative_leaf_hash()), + simplicity::bitcoin::taproot::Signature { + signature, + sighash_type: simplicity::bitcoin::TapSighashType::Default, + }, + ); + input.tap_scripts.insert( + context.control_block().clone(), + (context.cooperative_script().clone(), LeafVersion::TapScript), + ); + Ok(json!({ + "psbt_hex": hex::encode(local.psbt.serialize()), + "input_index": transaction.input_index, + "context": local.prepared.summary(), + })) + } + PolicyCommand::Finalize { transaction } => { + let local = LocalTransaction::new(&transaction)?; + let tx = finalize::finalize(&local.psbt)?; + Ok(json!({ + "transaction_hex": hex::encode(serialize(&tx)), + "txid": tx.compute_txid().to_string(), + "inputs": tx.input.len(), + "outputs": tx.output.len(), + "finalized": true, + })) + } + } +} + +impl IdentityArgs { + fn validate(&self) -> Result { + ensure!( + self.profile == PROFILE_NAME, + "Unsupported policy profile '{}'; expected {PROFILE_NAME}", + self.profile + ); + if let Some(expected_hash) = &self.profile_hash { + ensure!( + hex::decode(expected_hash).context("Invalid profile fingerprint hex")? + == profile_hash(), + "Policy fingerprint does not match this binary's compiled registry" + ); + } + match self.network.as_str() { + "bitcoin" => Ok(Network::Bitcoin), + "testnet" => Ok(Network::Testnet), + "testnet4" => Ok(Network::Testnet4), + _ => bail!("Unsupported policy network; expected bitcoin, testnet, or testnet4"), + } + } +} + +impl ProgramArgs { + fn validate(&self) -> Result<(Network, Vec, Vec)> { + let network = self.identity.validate()?; + let program = decode_base64(&self.program, "program")?; + ensure!(!program.is_empty(), "Policy program cannot be empty"); + policy_cmr(&program).context("Invalid policy-profile program")?; + let script = hex::decode(&self.redeem_script_hex).context("Invalid redeem script hex")?; + Ok((network, program, script)) + } + + fn request(&self) -> ProgramRequest { + ProgramRequest { + profile_hash: hex::encode(profile_hash()), + network: self.identity.network.clone(), + program: self.program.clone(), + redeem_script_hex: self.redeem_script_hex.clone(), + } + } +} + +struct LocalTransaction { + psbt: Psbt, + prepared: PreparedPolicy, + base_key: XOnlyPublicKey, +} + +impl LocalTransaction { + fn new(args: &TransactionArgs) -> Result { + let (network, program, script) = args.policy.validate()?; + let base_key = parse_public_key(&args.base_public_key)?; + let user_key = parse_public_key(&args.user_pubkey)?; + let recovery = parse_recovery_hash(&args.user_leaf_hash_hex)?; + let mut psbt = parse_psbt(&args.psbt)?; + let resolver = if args.offline { + None + } else { + args.esplora_url.as_deref() + }; + populate_parents(&mut psbt, resolver)?; + let prepared = PreparedPolicy::new( + &program, + &psbt, + args.input_index, + &script, + network, + base_key, + user_key, + recovery, + ) + .context("Failed to construct verified policy context")?; + Ok(Self { + psbt, + prepared, + base_key, + }) + } + + fn request(&self, args: &TransactionArgs, witness: Option) -> TransactionRequest { + TransactionRequest { + policy: args.policy.request(), + psbt_hex: hex::encode(self.psbt.serialize()), + input_index: args.input_index, + user_pubkey: args.user_pubkey.clone(), + user_leaf_hash_hex: args.user_leaf_hash_hex.clone(), + witness, + } + } + + fn verify_signature(&self, args: &TransactionArgs, result: &SignResponse) -> Result<()> { + ensure!( + result.input_index == args.input_index, + "Service signed an unexpected input index" + ); + let context = self.prepared.context(); + ensure!( + result.public_key_hex == context.service_pubkey().to_string(), + "Service signature public key differs from the reconstructed policy key" + ); + let signature = schnorr::Signature::from_slice( + &hex::decode(&result.signature_hex).context("Invalid service signature hex")?, + ) + .context("Service must return a 64-byte SIGHASH_DEFAULT signature")?; + Secp256k1::verification_only() + .verify_schnorr( + &signature, + &Message::from_digest(context.cooperative_sighash_default()), + &context.service_pubkey(), + ) + .context("Service returned an invalid cooperative signature")?; + let returned_psbt = parse_psbt(&result.psbt_hex)?; + ensure!( + returned_psbt.unsigned_tx == self.psbt.unsigned_tx + && returned_psbt.inputs.len() == self.psbt.inputs.len(), + "Service changed the transaction" + ); + for (index, (returned, original)) in returned_psbt + .inputs + .iter() + .zip(&self.psbt.inputs) + .enumerate() + { + ensure!( + returned.witness_utxo == original.witness_utxo + && returned.non_witness_utxo == original.non_witness_utxo, + "Service changed parent evidence for input {index}" + ); + } + let input = &returned_psbt.inputs[args.input_index]; + let leaf_hash = + TapLeafHash::from_script(context.cooperative_script(), LeafVersion::TapScript); + let stored = input + .tap_script_sigs + .get(&(context.service_pubkey(), leaf_hash)) + .context("Returned PSBT does not contain the verified service signature")?; + ensure!( + stored.signature == signature + && stored.sighash_type == simplicity::bitcoin::TapSighashType::Default, + "Returned PSBT contains a different service signature/sighash type" + ); + ensure!( + input.tap_scripts.get(context.control_block()) + == Some(&( + context.cooperative_script().to_owned(), + LeafVersion::TapScript + )), + "Returned PSBT is missing the reconstructed cooperative leaf/control block" + ); + Ok(()) + } +} + +fn checked_service( + service: &ServiceArgs, + transaction: &TransactionArgs, + local: &LocalTransaction, +) -> Result { + let client = ServiceClient::new(service)?; + client.capabilities(&transaction.policy.identity, Some(local.base_key))?; + Ok(client) +} + +fn check_identity( + expected: &IdentityArgs, + profile: &str, + fingerprint: &str, + network: &str, + genesis: &str, +) -> Result<()> { + let expected_network = expected.validate()?; + ensure!(profile == PROFILE_NAME, "Service policy profile mismatch"); + ensure!( + fingerprint == hex::encode(profile_hash()), + "Service policy fingerprint mismatch" + ); + ensure!( + network == expected.network, + "Service policy network mismatch" + ); + ensure!( + genesis == hex::encode(genesis_hash(expected_network)?), + "Service genesis hash mismatch" + ); + Ok(()) +} + +fn check_context(remote: &ContextSummary, local: &ContextSummary) -> Result<()> { + ensure!( + remote == local, + "Service policy context/digests differ from the locally verified context" + ); + Ok(()) +} + +fn check_cost(cost: u64) -> Result<()> { + ensure!( + cost <= u64::from(MAX_EXECUTION_COST_MILLIWEIGHT), + "Service reported a cost above the policy execution limit" + ); + Ok(()) +} + +fn parse_recovery_hash(encoded: &str) -> Result { + TapNodeHash::from_slice(&hex::decode(encoded).context("Invalid recovery leaf hash hex")?) + .context("Recovery leaf hash must be exactly 32 bytes") +} + +fn parse_public_key(encoded: &str) -> Result { + let bytes = hex::decode(encoded).context("Invalid public key hex")?; + match bytes.len() { + 32 => XOnlyPublicKey::from_slice(&bytes).context("Invalid x-only public key"), + 33 => PublicKey::from_slice(&bytes) + .map(|key| key.inner.x_only_public_key().0) + .context("Invalid compressed public key"), + _ => bail!("Public key must be 32-byte x-only or 33-byte compressed hex"), + } +} + +fn parse_psbt(encoded: &str) -> Result { + let bytes = hex::decode(encoded).context("Invalid PSBT hex")?; + ensure!( + bytes.len() <= MAX_BODY_BYTES, + "PSBT exceeds the policy body limit" + ); + Psbt::deserialize(&bytes).context("Invalid serialized PSBT") +} + +fn decode_base64(encoded: &str, label: &str) -> Result> { + ensure!( + encoded.len() <= MAX_BODY_BYTES, + "Policy {label} exceeds the body limit" + ); + BASE64_STANDARD + .decode(encoded) + .with_context(|| format!("Invalid standard-base64 policy {label}")) +} + +fn http_client() -> Result { + Client::builder() + .timeout(Duration::from_secs(30)) + .redirect(Policy::none()) + .build() + .context("Failed to create HTTP client") +} + +fn validate_url(value: &str) -> Result { + let url = Url::parse(value).context("Invalid HTTP URL")?; + ensure!( + matches!(url.scheme(), "http" | "https") + && url.host_str().is_some() + && url.username().is_empty() + && url.password().is_none() + && url.query().is_none() + && url.fragment().is_none(), + "Expected an HTTP(S) URL without credentials, query, or fragment" + ); + Ok(url) +} + +fn read_body(response: Response) -> Result<(reqwest::StatusCode, Vec)> { + let status = response.status(); + let mut body = Vec::new(); + response + .take((MAX_BODY_BYTES + 1) as u64) + .read_to_end(&mut body) + .context("Failed to read HTTP response body")?; + ensure!( + body.len() <= MAX_BODY_BYTES, + "HTTP response exceeds the 2 MiB policy limit (status {status})" + ); + Ok((status, body)) +} + +fn decode_response(response: Response) -> Result { + let (status, body) = read_body(response)?; + ensure!( + status.is_success(), + "Policy service returned HTTP {status}: {}", + String::from_utf8_lossy(&body) + ); + serde_json::from_slice(&body).context("Invalid policy service response JSON/shape") +} + +fn populate_parents(psbt: &mut Psbt, esplora: Option<&str>) -> Result<()> { + ensure!( + !psbt.inputs.is_empty() && psbt.inputs.len() == psbt.unsigned_tx.input.len(), + "Invalid PSBT input count" + ); + ensure!( + psbt.inputs.len() <= MAX_INPUTS && psbt.unsigned_tx.output.len() <= MAX_OUTPUTS, + "Policy profile accepts at most eight inputs and eight outputs" + ); + let esplora = esplora.map(validate_url).transpose()?; + let client = esplora.as_ref().map(|_| http_client()).transpose()?; + let mut parents = HashMap::<_, Transaction>::new(); + for (index, (input, txin)) in psbt.inputs.iter().zip(&psbt.unsigned_tx.input).enumerate() { + if let Some(parent) = &input.non_witness_utxo { + ensure!( + parent.compute_txid() == txin.previous_output.txid, + "Input {index} parent transaction txid mismatch" + ); + if let Some(known) = parents.insert(txin.previous_output.txid, parent.clone()) { + ensure!( + known == *parent, + "Conflicting full parent transaction evidence" + ); + } + } + } + for (index, (input, txin)) in psbt + .inputs + .iter_mut() + .zip(&psbt.unsigned_tx.input) + .enumerate() + { + let outpoint = txin.previous_output; + if input.non_witness_utxo.is_none() { + if let Some(parent) = parents.get(&outpoint.txid) { + input.non_witness_utxo = Some(parent.clone()); + } else if let (Some(esplora), Some(client)) = (&esplora, &client) { + let url = format!( + "{}/tx/{}/raw", + esplora.as_str().trim_end_matches('/'), + outpoint.txid + ); + let response = client + .get(url) + .send() + .with_context(|| format!("Failed to fetch parent for input {index}"))?; + let (status, bytes) = read_body(response)?; + ensure!( + status.is_success(), + "Esplora returned HTTP {status} for input {index}: {}", + String::from_utf8_lossy(&bytes) + ); + input.non_witness_utxo = + Some(deserialize::(&bytes).with_context(|| { + format!("Invalid parent transaction for input {index}") + })?); + } else { + bail!( + "Input {index} missing non_witness_utxo full parent evidence in offline mode; provide --esplora-url or populate every parent" + ); + } + } + let parent = input.non_witness_utxo.as_ref().unwrap(); + ensure!( + parent.compute_txid() == outpoint.txid, + "Input {index} parent transaction txid mismatch" + ); + let prevout = parent + .output + .get(outpoint.vout as usize) + .with_context(|| format!("Input {index} parent output index out of bounds"))?; + if let Some(witness_utxo) = &input.witness_utxo { + ensure!( + witness_utxo == prevout, + "Input {index} witness_utxo conflicts with verified parent output" + ); + } else { + input.witness_utxo = Some(prevout.clone()); + } + parents.insert(outpoint.txid, parent.clone()); + } + Ok(()) +} diff --git a/cli/src/commands/btc_tx/finalize.rs b/cli/src/commands/policy/finalize.rs similarity index 79% rename from cli/src/commands/btc_tx/finalize.rs rename to cli/src/commands/policy/finalize.rs index bdfff25..0dab5f1 100644 --- a/cli/src/commands/btc_tx/finalize.rs +++ b/cli/src/commands/policy/finalize.rs @@ -1,8 +1,6 @@ use anyhow::{Context, Result, anyhow, ensure}; -use serde_json::json; use simplicity::bitcoin::{ Psbt, TapLeafHash, Transaction, TxOut, Witness, XOnlyPublicKey, - consensus::serialize, hashes::Hash, psbt, secp256k1::{Message, Secp256k1}, @@ -11,40 +9,7 @@ use simplicity::bitcoin::{ }; use simplicity_unchained_core::utils::extract_pubkeys_from_p2tr_multisig_leaf_btc; -pub fn execute(psbt_hex: &str) -> Result<()> { - let psbt_bytes = hex::decode(psbt_hex).context("Failed to decode PSBT hex")?; - let psbt: Psbt = Psbt::deserialize(&psbt_bytes).context("Failed to deserialize PSBT")?; - - let tx = finalize(&psbt)?; - - let witnesses: Vec<_> = tx - .input - .iter() - .enumerate() - .map(|(idx, input)| { - json!({ - "input_index": idx, - "witness_elements": input.witness.len(), - "script_sig_bytes": input.script_sig.len(), - }) - }) - .collect(); - - let output = json!({ - "transaction_hex": hex::encode(serialize(&tx)), - "txid": tx.compute_txid().to_string(), - "inputs": tx.input.len(), - "outputs": tx.output.len(), - "finalized": true, - "witnesses": witnesses - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -fn finalize(psbt: &Psbt) -> Result { +pub(super) fn finalize(psbt: &Psbt) -> Result { let mut tx = psbt.unsigned_tx.clone(); ensure!(!tx.input.is_empty(), "Transaction has no inputs"); ensure!( diff --git a/cli/src/commands/tx/create.rs b/cli/src/commands/tx/create.rs deleted file mode 100644 index 95b119f..0000000 --- a/cli/src/commands/tx/create.rs +++ /dev/null @@ -1,229 +0,0 @@ -use anyhow::{Context, Result, anyhow}; - -use simplicity::elements; - -use elements::{ - Address, AddressParams, AssetId, OutPoint, Transaction, TxIn, TxOut, confidential, - encode::serialize, pset::PartiallySignedTransaction, -}; - -use std::str::FromStr; -use std::thread; -use std::time::Duration; - -#[derive(serde::Deserialize)] -struct BlockstreamTx { - vout: Vec, -} - -#[derive(serde::Deserialize)] -struct BlockstreamVout { - asset: Option, - value: Option, - scriptpubkey: Option, -} - -fn fetch_tx_output(txid: &str, vout: u32, network: &str) -> Result { - let api_url = match network { - "liquid" => format!("https://blockstream.info/liquid/api/tx/{}", txid), - "liquid_testnet" => format!("https://blockstream.info/liquidtestnet/api/tx/{}", txid), - _ => return Err(anyhow!("Cannot fetch transaction for network: {}", network)), - }; - - const MAX_RETRIES: u32 = 10; - const RETRY_DELAY_SECS: u64 = 3; - - let mut last_error = None; - - for attempt in 1..=MAX_RETRIES { - match reqwest::blocking::get(&api_url) - .context("Failed to fetch transaction") - .and_then(|resp| resp.text().context("Failed to read response")) - .and_then(|response| { - serde_json::from_str::(&response) - .context("Failed to parse transaction JSON") - }) { - Ok(tx_data) => { - let output = tx_data - .vout - .get(vout as usize) - .ok_or_else(|| anyhow!("Output {} not found in transaction", vout))?; - - let asset_str = output - .asset - .as_ref() - .ok_or_else(|| anyhow!("Output {} has no asset", vout))?; - let value = output - .value - .ok_or_else(|| anyhow!("Output {} has no value", vout))?; - let scriptpubkey_str = output - .scriptpubkey - .as_ref() - .ok_or_else(|| anyhow!("Output {} has no scriptpubkey", vout))?; - - let asset = AssetId::from_str(asset_str).context("Invalid asset ID")?; - let script_bytes = - hex::decode(scriptpubkey_str).context("Invalid scriptpubkey hex")?; - - return Ok(TxOut { - asset: confidential::Asset::Explicit(asset), - value: confidential::Value::Explicit(value), - nonce: confidential::Nonce::Null, - script_pubkey: elements::script::Script::from(script_bytes), - witness: elements::TxOutWitness::default(), - }); - } - Err(e) => { - last_error = Some(e); - if attempt < MAX_RETRIES { - eprintln!( - "Attempt {}/{} failed, RPC is not responding, retrying in {} seconds...", - attempt, MAX_RETRIES, RETRY_DELAY_SECS - ); - thread::sleep(Duration::from_secs(RETRY_DELAY_SECS)); - } - } - } - } - - Err(anyhow!( - "Server is down or unreachable after {} attempts: {}", - MAX_RETRIES, - last_error.unwrap() - )) -} - -fn get_network_params(network: &str) -> Result<&'static AddressParams> { - match network { - "elements" => Ok(&AddressParams::ELEMENTS), - "liquid" => Ok(&AddressParams::LIQUID), - "liquid_testnet" => Ok(&AddressParams::LIQUID_TESTNET), - _ => Err(anyhow!( - "Unsupported network '{}'. Supported networks are: elements, liquid, liquid_testnet.", - network - )), - } -} - -fn get_default_asset(network: &str) -> &'static str { - match network { - "liquid" => "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d", - "liquid_testnet" => "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", - _ => "0000000000000000000000000000000000000000000000000000000000000000", - } -} - -pub fn execute( - inputs: &[String], - outputs: &[String], - asset_id: Option<&str>, - network: &str, - sequence: Option, -) -> Result<()> { - let params = get_network_params(network)?; - - let asset = if let Some(asset_str) = asset_id { - AssetId::from_str(asset_str).context("Invalid asset ID")? - } else { - AssetId::from_str(get_default_asset(network)).context("Invalid default asset ID")? - }; - - // Parse inputs (txid:vout) - let mut tx_inputs = Vec::new(); - for input_str in inputs { - let parts: Vec<&str> = input_str.split(':').collect(); - if parts.len() != 2 { - return Err(anyhow!( - "Invalid input format. Expected txid:vout, got: {}", - input_str - )); - } - - let txid = - elements::Txid::from_str(parts[0]).context(format!("Invalid txid: {}", parts[0]))?; - let vout: u32 = parts[1] - .parse() - .context(format!("Invalid vout: {}", parts[1]))?; - - tx_inputs.push(TxIn { - previous_output: OutPoint::new(txid, vout), - is_pegin: false, - script_sig: elements::script::Script::new(), - sequence: sequence - .map(elements::Sequence::from_height) - .unwrap_or(elements::Sequence::MAX), - asset_issuance: elements::AssetIssuance::default(), - witness: elements::TxInWitness::default(), - }); - } - - // Parse outputs (address:value) - let mut tx_outputs = Vec::new(); - for output_str in outputs { - let parts: Vec<&str> = output_str.split(':').collect(); - if parts.len() != 2 { - return Err(anyhow!( - "Invalid output format. Expected address:value, got: {}", - output_str - )); - } - - let value: u64 = parts[1] - .parse() - .context(format!("Invalid value: {}", parts[1]))?; - - let script_pubkey = match parts[0] { - "fee" => elements::script::Script::new(), - address_str => { - let address = Address::parse_with_params(address_str, params) - .context(format!("Invalid address: {}", address_str))?; - address.script_pubkey() - } - }; - - tx_outputs.push(TxOut { - asset: confidential::Asset::Explicit(asset), - value: confidential::Value::Explicit(value), - nonce: confidential::Nonce::Null, - script_pubkey, - witness: elements::TxOutWitness::default(), - }); - } - - let tx = Transaction { - version: 2, - lock_time: elements::LockTime::ZERO, - input: tx_inputs, - output: tx_outputs, - }; - - let mut pset = PartiallySignedTransaction::from_tx(tx); - - // Populate witness UTXO for each input by fetching from the blockchain - for (i, input_str) in inputs.iter().enumerate() { - let parts: Vec<&str> = input_str.split(':').collect(); - let txid_str = parts[0]; - let vout: u32 = parts[1].parse().unwrap(); - - match fetch_tx_output(txid_str, vout, network) { - Ok(prev_output) => { - pset.inputs_mut()[i].witness_utxo = Some(prev_output); - } - Err(e) => { - return Err(anyhow!("Could not fetch UTXO for input {}: {}", i, e)); - } - } - } - - let output = serde_json::json!({ - "pset": hex::encode(serialize(&pset)), - "inputs": pset.inputs().len(), - "outputs": pset.outputs().len(), - "network": network, - "asset": asset.to_string() - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/commands/tx/finalize.rs b/cli/src/commands/tx/finalize.rs deleted file mode 100644 index fd8a24f..0000000 --- a/cli/src/commands/tx/finalize.rs +++ /dev/null @@ -1,160 +0,0 @@ -use anyhow::{Context, Result, anyhow, ensure}; -use serde_json::json; -use simplicity::elements::taproot::{LeafVersion, TapLeafHash}; -use simplicity::elements::{ - BlockHash, Transaction, TxOut, - encode::{deserialize, serialize}, - pset::{self, PartiallySignedTransaction}, - schnorr::TapTweak, - secp256k1_zkp::{Message, Secp256k1, XOnlyPublicKey}, - sighash::{Prevouts, SighashCache}, -}; -use simplicity_unchained_core::{ - ElementsNetwork, utils::extract_pubkeys_from_p2tr_multisig_leaf_elements, -}; -use std::str::FromStr; - -pub fn execute(pset_hex: &str, network: &str) -> Result<()> { - let pset_bytes = hex::decode(pset_hex).context("Failed to decode PSET hex")?; - let pset: PartiallySignedTransaction = - deserialize(&pset_bytes).context("Failed to deserialize PSET")?; - - let genesis_hash = ElementsNetwork::from_str(network) - .map_err(anyhow::Error::msg)? - .genesis_hash(); - let tx = finalize(&pset, genesis_hash)?; - - let witnesses: Vec<_> = tx - .input - .iter() - .enumerate() - .map(|(idx, input)| { - json!({ - "input_index": idx, - "witness_elements": input.witness.script_witness.len() - }) - }) - .collect(); - - let output = json!({ - "transaction_hex": hex::encode(serialize(&tx)), - "txid": tx.txid().to_string(), - "inputs": tx.input.len(), - "outputs": tx.output.len(), - "finalized": true, - "witnesses": witnesses - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -fn finalize(pset: &PartiallySignedTransaction, genesis_hash: BlockHash) -> Result { - let mut tx = pset - .extract_tx() - .context("Failed to extract transaction from PSET")?; - ensure!(!tx.input.is_empty(), "Transaction has no inputs"); - let prevouts = pset - .inputs() - .iter() - .enumerate() - .map(|(i, input)| { - input - .witness_utxo - .clone() - .ok_or_else(|| anyhow!("Input {} missing witness_utxo", i)) - }) - .collect::>>()?; - for (i, input) in pset.inputs().iter().enumerate() { - tx.input[i].witness.script_witness = - finalize_p2tr(&tx, i, input, &prevouts, genesis_hash)?.into(); - } - Ok(tx) -} - -fn finalize_p2tr( - tx: &Transaction, - i: usize, - input: &pset::Input, - prevouts: &[TxOut], - genesis_hash: BlockHash, -) -> Result>> { - let spent_script = &prevouts[i].script_pubkey; - ensure!( - spent_script.is_v1_p2tr(), - "Input {} spent output is not P2TR", - i - ); - ensure!( - tx.input[i].script_sig.is_empty(), - "Input {} native P2TR script_sig must be empty", - i - ); - // The witness program already contains the tweaked output key. - let output_key = XOnlyPublicKey::from_slice(&spent_script.as_bytes()[2..]) - .with_context(|| format!("Input {} invalid Taproot output key", i))? - .dangerous_assume_tweaked(); - let secp = Secp256k1::verification_only(); - let mut candidate = None; - for (control_block, (leaf_script, leaf_version)) in &input.tap_scripts { - if *leaf_version != LeafVersion::default() - || control_block.leaf_version != *leaf_version - || !control_block.verify_taproot_commitment(&secp, &output_key, leaf_script) - { - continue; - } - let Ok(pubkeys) = extract_pubkeys_from_p2tr_multisig_leaf_elements(leaf_script, i) else { - continue; - }; - ensure!( - candidate.is_none(), - "Input {} has ambiguous committed multisig leaves", - i - ); - candidate = Some((control_block, leaf_script, pubkeys)); - } - let (control_block, leaf_script, pubkeys) = - candidate.ok_or_else(|| anyhow!("Input {} has no committed canonical multisig leaf", i))?; - let leaf_hash = TapLeafHash::from_script(leaf_script, LeafVersion::default()); - - let signature = |position| { - input - .tap_script_sigs - .get(&(pubkeys[position], leaf_hash)) - .ok_or_else(|| { - anyhow!( - "Input {} missing tap_script_sig for multisig key {}", - i, - position - ) - }) - }; - let cosigner_sig = signature(0)?; - let user_sig = signature(1)?; - let mut cache = SighashCache::new(tx); - for (key, signature) in pubkeys.iter().zip([cosigner_sig, user_sig]) { - let sighash = cache - .taproot_script_spend_signature_hash( - i, - &Prevouts::All(prevouts), - leaf_hash, - signature.hash_ty, - genesis_hash, - ) - .with_context(|| format!("Input {} failed to compute signature sighash", i))?; - secp.verify_schnorr( - &signature.sig, - &Message::from_digest(sighash.to_byte_array()), - key, - ) - .with_context(|| format!("Input {} invalid tap_script_sig for {}", i, key))?; - } - - Ok(vec![ - user_sig.to_vec(), - cosigner_sig.to_vec(), - leaf_script.to_bytes(), - control_block.serialize(), - ]) -} diff --git a/cli/src/commands/tx/mod.rs b/cli/src/commands/tx/mod.rs deleted file mode 100644 index e3ddd95..0000000 --- a/cli/src/commands/tx/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod create; -pub mod finalize; -pub mod sign; -pub mod spend_user_leaf; diff --git a/cli/src/commands/tx/sign.rs b/cli/src/commands/tx/sign.rs deleted file mode 100644 index 3bab74c..0000000 --- a/cli/src/commands/tx/sign.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::str::FromStr; - -use anyhow::{Context, Result, anyhow}; -use serde_json::json; -use simplicity::{ - ToXOnlyPubkey, bitcoin, - elements::{ - self, SchnorrSig, - encode::{deserialize, serialize}, - pset::PartiallySignedTransaction, - secp256k1_zkp::{Message, Secp256k1, SecretKey}, - sighash::{SchnorrSighashType, SighashCache}, - taproot::{LeafVersion, TapNodeHash, TaprootBuilder}, - }, -}; -use simplicity_unchained_core::{ - ElementsNetwork, - utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_elements}, -}; - -pub fn execute( - pset_hex: &str, - secret_key_hex: &str, - input_index: usize, - cosigner_pubkey_hex: &str, - user_leaf_hash_hex: &str, - network: &str, -) -> Result<()> { - let pset_bytes = hex::decode(pset_hex).context("Failed to decode PSET hex")?; - let mut pset: PartiallySignedTransaction = - deserialize(&pset_bytes).context("Failed to deserialize PSET")?; - - if input_index >= pset.inputs().len() { - return Err(anyhow::anyhow!( - "Input index {} out of bounds (PSET has {} inputs)", - input_index, - pset.inputs().len() - )); - } - - let secret_key_bytes = - hex::decode(secret_key_hex).context("Failed to decode secret key hex")?; - let secret_key = SecretKey::from_slice(&secret_key_bytes).context("Invalid secret key")?; - - let genesis_hash = ElementsNetwork::from_str(network) - .map_err(|e| anyhow!(e))? - .genesis_hash(); - - let secp = Secp256k1::new(); - - let public_key = bitcoin::PublicKey::from_private_key( - &secp, - &simplicity::elements::bitcoin::PrivateKey { - compressed: true, - network: simplicity::elements::bitcoin::NetworkKind::Main, - inner: secret_key, - }, - ); - - let cosigner_pubkey_bytes = - hex::decode(cosigner_pubkey_hex).context("Failed to decode cosigner pubkey hex")?; - let cosigner_pubkey = bitcoin::PublicKey::from_slice(&cosigner_pubkey_bytes) - .context("Invalid cosigner pubkey")?; - - let multisig_leaf = p2tr_multisig_leaf_elements(&cosigner_pubkey, &public_key); - - let user_leaf = TapNodeHash::from_byte_array( - hex::decode(user_leaf_hash_hex) - .context("Failed to decode user leaf hash hex")? - .as_slice() - .try_into() - .context("Invalid user leaf hash")?, - ); - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf.clone()) - .context("Failed to add multisig leaf")? - .add_hidden(1, user_leaf) - .context("Failed to add user leaf")? - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| anyhow::anyhow!("Failed to finalize taproot"))?; - - let control_block = spend_info - .control_block(&(multisig_leaf.clone(), LeafVersion::default())) - .ok_or_else(|| anyhow::anyhow!("Failed to get control block"))?; - - let tx = pset.extract_tx().context("Failed to extract transaction")?; - - let prevouts: Vec<_> = pset - .inputs() - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| anyhow::anyhow!("Missing witness_utxo")) - }) - .collect::>()?; - - let mut sighash_cache = SighashCache::new(&tx); - - let leaf_hash = elements::sighash::ScriptPath::with_defaults(&multisig_leaf).leaf_hash(); - - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &elements::sighash::Prevouts::All(&prevouts), - elements::sighash::ScriptPath::with_defaults(&multisig_leaf), - SchnorrSighashType::Default, - genesis_hash, - ) - .context("Failed to compute sighash")?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let keypair = elements::secp256k1_zkp::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr(&msg, &keypair); - - let user_xonly = public_key.to_x_only_pubkey(); - - let input = &mut pset.inputs_mut()[input_index]; - input.tap_script_sigs.insert( - (user_xonly, leaf_hash), - SchnorrSig { - sig: signature, - hash_ty: SchnorrSighashType::Default, - }, - ); - input - .tap_scripts - .insert(control_block, (multisig_leaf, LeafVersion::default())); - - let partial_sigs_count = pset.inputs()[input_index].tap_script_sigs.len(); - - let output = json!({ - "pset": hex::encode(serialize(&pset)), - "signature_hex": hex::encode(signature.as_ref()), - "public_key_hex": hex::encode(public_key.to_bytes()), - "input_index": input_index, - "partial_sigs_count": partial_sigs_count, - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/commands/tx/spend_user_leaf.rs b/cli/src/commands/tx/spend_user_leaf.rs deleted file mode 100644 index 4365dc5..0000000 --- a/cli/src/commands/tx/spend_user_leaf.rs +++ /dev/null @@ -1,126 +0,0 @@ -use anyhow::{Context, Result, anyhow}; -use serde_json::json; -use simplicity::{ - bitcoin::{self, key::UntweakedKeypair}, - elements::{ - self, SchnorrSig, - encode::{deserialize, serialize}, - pset::PartiallySignedTransaction, - secp256k1_zkp::{Message, Secp256k1, SecretKey}, - sighash::{Prevouts, SchnorrSighashType, SighashCache}, - taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, - }, -}; -use simplicity_unchained_core::{ - ElementsNetwork, - utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_elements}, -}; -use std::str::FromStr; - -use crate::demo_script::csv::validate_sequence; - -pub fn execute( - pset_hex: &str, - secret_key_hex: &str, - user_leaf_script_hex: &str, - cosigner_pubkey_hex: &str, - input_index: usize, - network: &str, -) -> Result<()> { - let secp = Secp256k1::new(); - - let pset_bytes = hex::decode(pset_hex).context("Failed to decode PSET hex")?; - let mut pset: PartiallySignedTransaction = - deserialize(&pset_bytes).context("Failed to deserialize PSET")?; - - let secret_key = SecretKey::from_str(secret_key_hex).context("Failed to parse secret key")?; - let keypair = UntweakedKeypair::from_secret_key(&secp, &secret_key); - let (user_xonly, _) = keypair.x_only_public_key(); - let user_full_pk = bitcoin::PublicKey::new(keypair.public_key()); - - let user_leaf_bytes = - hex::decode(user_leaf_script_hex).context("Failed to decode user leaf script hex")?; - let user_leaf_script = elements::script::Script::from(user_leaf_bytes); - - let cosigner_pubkey = bitcoin::PublicKey::from_str(cosigner_pubkey_hex) - .context("Failed to parse cosigner pubkey")?; - - let genesis_hash = ElementsNetwork::from_str(network) - .map_err(|e| anyhow!(e))? - .genesis_hash(); - - let multisig_leaf = p2tr_multisig_leaf_elements(&cosigner_pubkey, &user_full_pk); - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf) - .context("Failed to add multisig leaf")? - .add_leaf(1, user_leaf_script.clone()) - .context("Failed to add user leaf")? - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| anyhow::anyhow!("Failed to finalize taproot"))?; - - let control_block = spend_info - .control_block(&(user_leaf_script.clone(), LeafVersion::default())) - .ok_or_else(|| anyhow::anyhow!("Failed to get control block for user leaf"))?; - - let tx = pset.extract_tx().context("Failed to extract transaction")?; - - let input = tx - .input - .get(input_index) - .ok_or_else(|| anyhow!("Input index {} out of bounds", input_index))?; - validate_sequence( - user_leaf_script.as_bytes(), - i64::from(tx.version), - input.sequence.0, - input_index, - )?; - - let prevouts = pset - .inputs() - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| anyhow::anyhow!("Missing witness_utxo")) - }) - .collect::>>()?; - - let mut sighash_cache = SighashCache::new(&tx); - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &Prevouts::All(&prevouts), - elements::sighash::ScriptPath::with_defaults(&user_leaf_script), - SchnorrSighashType::Default, - genesis_hash, - ) - .context("Failed to compute sighash")?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let signature = secp.sign_schnorr(&msg, &keypair); - - let leaf_hash = TapLeafHash::from_script(&user_leaf_script, LeafVersion::default()); - - pset.inputs_mut()[input_index].tap_script_sigs.insert( - (user_xonly, leaf_hash), - SchnorrSig { - sig: signature, - hash_ty: SchnorrSighashType::Default, - }, - ); - - pset.inputs_mut()[input_index] - .tap_scripts - .insert(control_block, (user_leaf_script, LeafVersion::default())); - - let output = json!({ - "pset": hex::encode(serialize(&pset)), - "signature": hex::encode(signature.as_ref()), - "input_index": input_index, - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} diff --git a/cli/src/demo_script/csv.rs b/cli/src/demo_script/csv.rs deleted file mode 100644 index a49131d..0000000 --- a/cli/src/demo_script/csv.rs +++ /dev/null @@ -1,149 +0,0 @@ -use anyhow::{Context, Result, anyhow, ensure}; -use simplicity::bitcoin::{ - Script, Sequence, XOnlyPublicKey, - opcodes::all::{OP_CHECKSIG, OP_CSV, OP_DROP}, - relative::LockTime, - script::{Builder, Instruction}, -}; - -pub(crate) fn validate_sequence( - script: &[u8], - version: i64, - sequence: u32, - input_index: usize, -) -> Result<()> { - // Bitcoin and Elements use the same encoding for this recovery script. - let instructions = Script::from_bytes(script) - .instructions() - .collect::, _>>() - .with_context(|| format!("Input {input_index}: failed to parse recovery script"))?; - if !instructions.contains(&Instruction::Op(OP_CSV)) { - return Ok(()); - } - - let unsupported = || { - anyhow!( - "Input {input_index}: unsupported CSV recovery script; expected \ - OP_CSV OP_DROP OP_CHECKSIG" - ) - }; - let [ - delay, - Instruction::Op(OP_CSV), - Instruction::Op(OP_DROP), - Instruction::PushBytes(key), - Instruction::Op(OP_CHECKSIG), - ] = instructions.as_slice() - else { - return Err(unsupported()); - }; - let blocks = delay - .script_num() - .and_then(|value| u16::try_from(value).ok()) - .ok_or_else(unsupported)?; - let key = XOnlyPublicKey::from_slice(key.as_bytes()) - .with_context(|| format!("Input {input_index}: invalid CSV recovery public key"))?; - let canonical = Builder::new() - .push_int(i64::from(blocks)) - .push_opcode(OP_CSV) - .push_opcode(OP_DROP) - .push_x_only_key(&key) - .push_opcode(OP_CHECKSIG) - .into_script(); - ensure!(script == canonical.as_bytes(), "{}", unsupported()); - ensure!( - version >= 2, - "Input {input_index}: CSV recovery requires transaction version >= 2" - ); - ensure!( - LockTime::from_height(blocks).is_implied_by_sequence(Sequence::from_consensus(sequence)), - "Input {input_index}: CSV recovery requires a block-based sequence of at least \ - {blocks} with the disable flag clear (got {sequence:#010x}); \ - set --sequence before signing" - ); - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use simplicity::bitcoin::opcodes::all::{OP_ENDIF, OP_IF}; - use simplicity_unchained_core::utils::UNSPENDABLE_KEY_P2TR; - - fn leaf(blocks: i64) -> simplicity::bitcoin::ScriptBuf { - Builder::new() - .push_int(blocks) - .push_opcode(OP_CSV) - .push_opcode(OP_DROP) - .push_x_only_key(&UNSPENDABLE_KEY_P2TR) - .push_opcode(OP_CHECKSIG) - .into_script() - } - - #[test] - fn validates_block_delays_and_bip68_flags() { - for blocks in [0, 1, 16, 17, 127, 128, 144, u16::MAX] { - let script = leaf(i64::from(blocks)); - for sequence in [ - u32::from(blocks), - u32::from(u16::MAX), - 0x2000_0000 | u32::from(blocks), - ] { - validate_sequence(script.as_bytes(), 2, sequence, 1).unwrap(); - validate_sequence(script.as_bytes(), 3, sequence, 1).unwrap(); - } - for sequence in [ - u32::MAX, - 0x8000_0000 | u32::from(blocks), - Sequence::from_512_second_intervals(blocks).0, - ] { - let error = validate_sequence(script.as_bytes(), 2, sequence, 1).unwrap_err(); - assert!( - error - .to_string() - .contains("Input 1: CSV recovery requires a block-based") - ); - } - if blocks > 0 { - assert!(validate_sequence(script.as_bytes(), 2, u32::from(blocks - 1), 0).is_err()); - } - for version in [-1, 0, 1] { - assert!( - validate_sequence(script.as_bytes(), version, u32::from(blocks), 0).is_err() - ); - } - } - } - - #[test] - fn rejects_unsupported_csv_scripts_but_preserves_non_csv_leaves() { - for blocks in [-1, 65_536, 1 << 22, 1 << 31] { - let error = validate_sequence(leaf(blocks).as_bytes(), 2, u32::MAX, 0).unwrap_err(); - assert!( - error - .to_string() - .contains("unsupported CSV recovery script") - ); - } - let conditional = Builder::new() - .push_opcode(OP_IF) - .push_int(144) - .push_opcode(OP_CSV) - .push_opcode(OP_ENDIF) - .into_script(); - assert!(validate_sequence(conditional.as_bytes(), 2, 144, 0).is_err()); - let script = leaf(144).into_bytes(); - let mut nonminimal = vec![0x4c, script[0]]; - nonminimal.extend_from_slice(&script[1..]); - assert!(validate_sequence(&nonminimal, 2, 144, 0).is_err()); - let mut nonminimal_number = leaf(0).into_bytes(); - nonminimal_number.splice(..1, [1, 0]); - assert!(validate_sequence(&nonminimal_number, 2, 0, 0).is_err()); - for script in [ - Builder::new().push_int(1).into_script(), - Builder::new().push_slice([OP_CSV.to_u8()]).into_script(), - ] { - validate_sequence(script.as_bytes(), 1, u32::MAX, 0).unwrap(); - } - } -} diff --git a/cli/src/demo_script/csv_leaf_btc.rs b/cli/src/demo_script/csv_leaf_btc.rs deleted file mode 100644 index 0e5023d..0000000 --- a/cli/src/demo_script/csv_leaf_btc.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Timelocked user script for demo purposes -use anyhow::{Context, Result, anyhow}; -use serde_json::json; -use simplicity::{ - ToXOnlyPubkey, - bitcoin::{ - self, Psbt, PublicKey, TapLeafHash, Transaction, Witness, consensus::serialize, - hashes::Hash, psbt, taproot::LeafVersion, - }, -}; -use std::str::FromStr; - -use super::csv::validate_sequence; - -pub fn build_csv_script(user_pubkey_hex: &str, timelock: u16) -> Result<()> { - let user_pubkey = - PublicKey::from_str(user_pubkey_hex).context("Failed to parse user pubkey")?; - - let script = p2tr_recovery_leaf_btc(&user_pubkey, timelock as i64); - - let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript); - - let output = json!({ - "script": hex::encode(script.as_bytes()), - "leaf_hash": hex::encode(leaf_hash.to_byte_array()), - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -pub fn finalize_user_leaf(psbt_hex: &str) -> Result<()> { - let psbt_bytes = hex::decode(psbt_hex).context("Failed to decode PSBT hex")?; - let psbt = Psbt::deserialize(&psbt_bytes).context("Failed to deserialize PSBT")?; - - let mut tx = psbt - .clone() - .extract_tx() - .context("Failed to extract transaction")?; - - for (i, input) in psbt.inputs.iter().enumerate() { - finalize_internal(&mut tx, i, input)?; - } - - let output = json!({ - "transaction_hex": hex::encode(serialize(&tx)), - "txid": tx.compute_txid().to_string(), - "inputs": tx.input.len(), - "outputs": tx.output.len(), - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -fn finalize_internal(tx: &mut Transaction, i: usize, input: &psbt::Input) -> Result<()> { - let (control_block, (leaf_script, _)) = input - .tap_scripts - .iter() - .next() - .map(|(cb, s)| (cb.clone(), s.clone())) - .ok_or_else(|| anyhow!("Input {} missing tap_scripts", i))?; - - validate_sequence( - leaf_script.as_bytes(), - i64::from(tx.version.0), - tx.input[i].sequence.0, - i, - )?; - - let leaf_hash = TapLeafHash::from_script(&leaf_script, LeafVersion::TapScript); - - let sig = input - .tap_script_sigs - .iter() - .find(|((_, lh), _)| lh == &leaf_hash) - .map(|(_, sig)| sig) - .ok_or_else(|| anyhow!("Input {} missing tap_script_sig", i))?; - - let mut witness = Witness::new(); - witness.push(sig.serialize()); - witness.push(leaf_script.as_bytes()); - witness.push(control_block.serialize()); - - tx.input[i].witness = witness; - - Ok(()) -} - -/// Builds the P2TR recovery leaf script for the unilateral user recovery path. -/// -/// Allows the user to spend without the cosigner after the timelock expires. -/// -/// Script structure: -/// ```text -/// OP_CSV OP_DROP OP_CHECKSIG -fn p2tr_recovery_leaf_btc(user_pk: &PublicKey, timelock: i64) -> bitcoin::script::ScriptBuf { - bitcoin::script::Builder::new() - .push_int(timelock) - .push_opcode(bitcoin::opcodes::all::OP_CSV) - .push_opcode(bitcoin::opcodes::all::OP_DROP) - .push_x_only_key(&user_pk.to_x_only_pubkey()) - .push_opcode(bitcoin::opcodes::all::OP_CHECKSIG) - .into_script() -} diff --git a/cli/src/demo_script/csv_leaf_elements.rs b/cli/src/demo_script/csv_leaf_elements.rs deleted file mode 100644 index 04050fa..0000000 --- a/cli/src/demo_script/csv_leaf_elements.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Timelocked user script for demo purposes -use anyhow::{Context, Result, anyhow}; -use serde_json::json; -use simplicity::{ - ToXOnlyPubkey, - bitcoin::PublicKey, - elements::{ - self, - encode::{deserialize, serialize}, - pset::{self, PartiallySignedTransaction}, - taproot::{LeafVersion, TapLeafHash}, - }, -}; - -use super::csv::validate_sequence; - -pub fn build_csv_script(user_pubkey_hex: &str, timelock: u16) -> Result<()> { - let user_pubkey_bytes = - hex::decode(user_pubkey_hex).context("Failed to decode user pubkey hex")?; - let user_pubkey = - PublicKey::from_slice(&user_pubkey_bytes).context("Failed to parse user pubkey")?; - - let script = p2tr_recovery_leaf_elements(&user_pubkey, timelock as i64); - let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::default()); - - let output = json!({ - "script": hex::encode(script.to_bytes()), - "leaf_hash": hex::encode(leaf_hash.to_byte_array()), - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -pub fn finalize_user_leaf(pset_hex: &str) -> Result<()> { - let pset_bytes = hex::decode(pset_hex).context("Failed to decode PSET hex")?; - let pset: PartiallySignedTransaction = - deserialize(&pset_bytes).context("Failed to deserialize PSET")?; - - let mut tx = pset.extract_tx().context("Failed to extract transaction")?; - - for (i, input) in pset.inputs().iter().enumerate() { - finalize_internal(&mut tx, i, input)?; - } - - let output = json!({ - "transaction_hex": hex::encode(serialize(&tx)), - "txid": tx.txid().to_string(), - "inputs": tx.input.len(), - "outputs": tx.output.len(), - }); - - println!("{}", serde_json::to_string_pretty(&output)?); - - Ok(()) -} - -fn finalize_internal(tx: &mut elements::Transaction, i: usize, input: &pset::Input) -> Result<()> { - let (control_block, (leaf_script, leaf_version)) = input - .tap_scripts - .iter() - .next() - .map(|(cb, s)| (cb.clone(), s.clone())) - .ok_or_else(|| anyhow!("Input {} missing tap_scripts", i))?; - - validate_sequence( - leaf_script.as_bytes(), - i64::from(tx.version), - tx.input[i].sequence.0, - i, - )?; - - let leaf_hash = TapLeafHash::from_script(&leaf_script, leaf_version); - - let sig = input - .tap_script_sigs - .iter() - .find(|((_, lh), _)| lh == &leaf_hash) - .map(|(_, sig)| sig) - .ok_or_else(|| anyhow!("Input {} missing tap_script_sig", i))?; - - tx.input[i].witness.script_witness = vec![ - sig.to_vec(), - leaf_script.to_bytes(), - control_block.serialize(), - ] - .into(); - - Ok(()) -} - -/// Builds the P2TR recovery leaf script for the unilateral user recovery path. -/// -/// Allows the user to spend without the cosigner after the timelock expires. -/// -/// Script structure: -/// ```text -/// OP_CSV OP_DROP OP_CHECKSIG -/// ``` -fn p2tr_recovery_leaf_elements(user_pk: &PublicKey, timelock: i64) -> elements::script::Script { - let user_xonly_bytes = user_pk.to_x_only_pubkey().serialize(); - - elements::script::Builder::new() - .push_int(timelock) - .push_opcode(elements::opcodes::all::OP_CSV) - .push_opcode(elements::opcodes::all::OP_DROP) - .push_slice(&user_xonly_bytes) - .push_opcode(elements::opcodes::all::OP_CHECKSIG) - .into_script() -} diff --git a/cli/src/demo_script/mod.rs b/cli/src/demo_script/mod.rs deleted file mode 100644 index e1edd04..0000000 --- a/cli/src/demo_script/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod csv; -pub mod csv_leaf_btc; -pub mod csv_leaf_elements; diff --git a/cli/src/main.rs b/cli/src/main.rs index 971f51d..739fb2a 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,12 +1,11 @@ mod commands; -mod demo_script; use anyhow::Result; use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(name = "simplicity-unchained")] -#[command(about = "A CLI tool for Simplicity Unchained utilities", long_about = None)] +#[command(about = "Profile-bound policy signing utilities for Simplicity Unchained")] struct Cli { #[command(subcommand)] command: Commands, @@ -14,51 +13,21 @@ struct Cli { #[derive(Subcommand)] enum Commands { - /// Address operations - Address { + /// Confidential multi-asset liquid-policy-v1 utilities (not legacy tx) + LiquidPolicy { #[command(subcommand)] - command: AddressCommands, + command: Box, + }, + /// Bitcoin policy-profile funding, evaluation and signing + Policy { + #[command(subcommand)] + command: Box, }, - /// Keypair operations Keypair { #[command(subcommand)] command: KeypairCommands, }, - - /// Transaction operations (Elements/Liquid) - Tx { - #[command(subcommand)] - command: TxCommands, - }, - - /// Bitcoin transaction operations - BtcTx { - #[command(subcommand)] - command: BtcTxCommands, - }, -} - -#[derive(Subcommand)] -enum AddressCommands { - /// Generate a P2TR 2-of-2 multisig address from two public keys. - Multisig { - /// Script-bound tweaked cosigner public key from the service /tweak endpoint, in hex - #[arg(short = '1', long)] - pubkey1: String, - - /// Second public key in hex format - #[arg(short = '2', long)] - pubkey2: String, - - /// User leaf hash in hex format - #[arg(long)] - user_leaf_hash: String, - - /// Network (elements, liquid, liquid_testnet, bitcoin, testnet, testnet4) - #[arg(short, long, default_value = "elements")] - network: String, - }, } #[derive(Subcommand)] @@ -67,394 +36,12 @@ enum KeypairCommands { Generate, } -#[derive(Subcommand)] -enum BtcTxCommands { - /// Compute the per-spend digest for a tx_approval_hash policy (not a Taproot sighash) - ApprovalHash { - /// PSBT in hex, with witness_utxo populated for every input - #[arg(long)] - psbt: String, - /// Selected input - #[arg(long)] - input_index: usize, - /// Encoded Bitcoin Simplicity program (hex or base64) - #[arg(long)] - program: String, - /// Exact script data committed at address generation - #[arg(long)] - redeem_script_hex: Option, - /// Must match the service's bitcoin_network (bitcoin, testnet, testnet4) - #[arg(long)] - network: String, - }, - - /// Create a PSBT (PartiallySignedTransaction) from UTXOs - Create { - /// Transaction input in format: txid:vout - #[arg(short = 'i', long = "input", num_args = 1.., required = true)] - inputs: Vec, - - /// Transaction output in format: address:value - #[arg(short = 'o', long = "output", num_args = 1.., required = true)] - outputs: Vec, - - /// Network (bitcoin, testnet) - #[arg(short, long, default_value = "bitcoin")] - network: String, - - /// Relative locktime in blocks for all inputs; CSV recovery requires >= the leaf delay - #[arg(long)] - sequence: Option, - }, - - /// Sign a PSBT with one secret key (for co-signing) - Sign { - /// PSBT in hex format - #[arg(short, long)] - psbt: String, - - /// Secret key in hex format - #[arg(short = 'k', long)] - secret_key: String, - - /// Input index to sign - #[arg(short, long)] - input_index: usize, - - #[arg(short, long)] - cosigner_pubkey: Option, - - /// User leaf script in hex format - #[arg(long)] - user_leaf_hash: String, - }, - - /// Finalize a PSBT into a broadcastable transaction - Finalize { - /// PSBT in hex format - #[arg(short, long)] - psbt: String, - }, - - /// Spend the user leaf (Leaf 1) of a P2TR output independently - SpendUserLeaf { - /// PSBT in hex format - #[arg(short, long)] - psbt: String, - - /// User secret key in hex format - #[arg(short = 'k', long)] - secret_key: String, - - /// User leaf script in hex format - #[arg(long)] - user_leaf_script: String, - - /// Cosigner public key in hex format - #[arg(long)] - cosigner_pubkey: String, - - /// Input index to sign - #[arg(short, long, default_value_t = 0)] - input_index: usize, - - /// Network (bitcoin, testnet) - #[arg(short, long, default_value = "bitcoin")] - network: String, - }, - - /// Build a CSV recovery leaf script from a user pubkey and timelock - BuildCsvLeaf { - /// User public key in hex format - #[arg(long)] - user_pubkey: String, - - /// Timelock in blocks - #[arg(long)] - timelock: u16, - }, - - /// Finalize a user leaf (Leaf 1) PSBT into a broadcastable transaction - FinalizeUserLeaf { - /// PSBT in hex format - #[arg(short, long)] - psbt: String, - }, -} - -#[derive(Subcommand)] -enum TxCommands { - /// Create a PSET (PartiallySignedTransaction) from UTXOs - Create { - /// Transaction input in format: txid:vout - #[arg(short = 'i', long = "input", num_args = 1.., required = true)] - inputs: Vec, - - /// Transaction output in format: address:value - #[arg(short = 'o', long = "output", num_args = 1.., required = true)] - outputs: Vec, - - /// Asset ID (defaults to L-BTC on Liquid/Elements) - #[arg(short, long)] - asset: Option, - - /// Network (elements, liquid, liquid_testnet) - #[arg(short, long, default_value = "elements")] - network: String, - - /// Relative locktime in blocks for all inputs; CSV recovery requires >= the leaf delay - #[arg(long)] - sequence: Option, - }, - - /// Sign a PSET with one secret key (for co-signing) - Sign { - /// PSET in hex format - #[arg(short, long)] - pset: String, - - /// Secret key in hex format - #[arg(short = 'k', long)] - secret_key: String, - - /// Input index to sign - #[arg(short, long)] - input_index: usize, - - #[arg(short, long)] - cosigner_pubkey: String, - - /// User leaf script in hex format - #[arg(long)] - user_leaf_hash: String, - - /// Network (elements, liquid, liquid_testnet) - #[arg(short, long, default_value = "liquid_testnet")] - network: String, - }, - - /// Finalize a PSET into a broadcastable transaction - Finalize { - /// PSET in hex format - #[arg(short, long)] - pset: String, - - /// Network used for signature verification (liquid, liquid_testnet) - #[arg(short, long, default_value = "liquid_testnet")] - network: String, - }, - - /// Spend the user leaf (Leaf 1) of a P2TR output independently - SpendUserLeaf { - /// PSET in hex format - #[arg(short, long)] - psbt: String, - - /// User secret key in hex format - #[arg(short = 'k', long)] - secret_key: String, - - /// User leaf script in hex format - #[arg(long)] - user_leaf_script: String, - - /// Cosigner public key in hex format - #[arg(long)] - cosigner_pubkey: String, - - /// Input index to sign - #[arg(short, long, default_value_t = 0)] - input_index: usize, - - /// Network (elements, liquid, liquid_testnet) - #[arg(short, long, default_value = "liquid_testnet")] - network: String, - }, - - /// Build a CSV recovery leaf script from a user pubkey and timelock - BuildCsvLeaf { - /// User public key in hex format - #[arg(long)] - user_pubkey: String, - - /// Timelock in blocks - #[arg(long)] - timelock: u16, - }, - - /// Finalize a user leaf (Leaf 1) PSBT into a broadcastable transaction - FinalizeUserLeaf { - /// PSET in hex format - #[arg(short, long)] - psbt: String, - }, -} - fn main() -> Result<()> { - let cli = Cli::parse(); - - match cli.command { - Commands::Address { command } => match command { - AddressCommands::Multisig { - pubkey1, - pubkey2, - user_leaf_hash, - network, - } => { - commands::address::multisig::execute(&pubkey1, &pubkey2, &user_leaf_hash, &network)? - } - }, - - Commands::Keypair { command } => match command { - KeypairCommands::Generate => { - commands::keypair::generate::execute()?; - } - }, - - Commands::Tx { command } => match command { - TxCommands::Create { - inputs, - outputs, - asset, - network, - sequence, - } => { - commands::tx::create::execute( - &inputs, - &outputs, - asset.as_deref(), - &network, - sequence, - )?; - } - - TxCommands::Sign { - pset, - secret_key, - input_index, - cosigner_pubkey, - user_leaf_hash, - network, - } => { - commands::tx::sign::execute( - &pset, - &secret_key, - input_index, - &cosigner_pubkey, - &user_leaf_hash, - &network, - )?; - } - - TxCommands::Finalize { pset, network } => { - commands::tx::finalize::execute(&pset, &network)?; - } - TxCommands::SpendUserLeaf { - psbt: pset, - secret_key, - user_leaf_script, - cosigner_pubkey, - input_index, - network, - } => { - commands::tx::spend_user_leaf::execute( - &pset, - &secret_key, - &user_leaf_script, - &cosigner_pubkey, - input_index, - &network, - )?; - } - - TxCommands::BuildCsvLeaf { - user_pubkey, - timelock, - } => { - demo_script::csv_leaf_elements::build_csv_script(&user_pubkey, timelock)?; - } - - TxCommands::FinalizeUserLeaf { psbt: pset } => { - demo_script::csv_leaf_elements::finalize_user_leaf(&pset)?; - } - }, - - Commands::BtcTx { command } => match command { - BtcTxCommands::ApprovalHash { - psbt, - input_index, - program, - redeem_script_hex, - network, - } => { - commands::btc_tx::approval_hash::execute( - &psbt, - input_index, - &program, - redeem_script_hex.as_deref(), - &network, - )?; - } - - BtcTxCommands::Create { - inputs, - outputs, - network, - sequence, - } => { - commands::btc_tx::create::execute(&inputs, &outputs, &network, sequence)?; - } - - BtcTxCommands::Sign { - psbt, - secret_key, - input_index, - cosigner_pubkey, - user_leaf_hash, - } => { - commands::btc_tx::sign::execute( - &psbt, - &secret_key, - input_index, - cosigner_pubkey.as_deref(), - &user_leaf_hash, - )?; - } - - BtcTxCommands::Finalize { psbt } => { - commands::btc_tx::finalize::execute(&psbt)?; - } - - BtcTxCommands::SpendUserLeaf { - psbt, - secret_key, - user_leaf_script, - cosigner_pubkey, - input_index, - network, - } => { - commands::btc_tx::spend_user_leaf::execute( - &psbt, - &secret_key, - &user_leaf_script, - &cosigner_pubkey, - input_index, - &network, - )?; - } - - BtcTxCommands::BuildCsvLeaf { - user_pubkey, - timelock, - } => { - demo_script::csv_leaf_btc::build_csv_script(&user_pubkey, timelock)?; - } - - BtcTxCommands::FinalizeUserLeaf { psbt } => { - demo_script::csv_leaf_btc::finalize_user_leaf(&psbt)?; - } - }, + match Cli::parse().command { + Commands::LiquidPolicy { command } => commands::liquid_policy::execute(*command), + Commands::Policy { command } => commands::policy::execute(*command), + Commands::Keypair { + command: KeypairCommands::Generate, + } => commands::keypair::generate::execute(), } - - Ok(()) } diff --git a/cli/tests/approval_hash.rs b/cli/tests/approval_hash.rs deleted file mode 100644 index 13a5ab4..0000000 --- a/cli/tests/approval_hash.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::process::Command; - -use simplicity::{ - bitcoin::{self, psbt::Psbt}, - elements::Script, -}; -use simplicity_unchained_core::{ - BitcoinNetwork, jets::bitcoin::CoreExtension, program::Program, - runner::bitcoin_execution_environment, -}; - -#[test] -fn approval_hash_command_matches_the_execution_context() { - let program = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA"; - let tx = bitcoin::Transaction { - version: bitcoin::transaction::Version::TWO, - lock_time: bitcoin::absolute::LockTime::ZERO, - input: vec![bitcoin::TxIn::default()], - output: vec![bitcoin::TxOut { - value: bitcoin::Amount::from_sat(50_000), - script_pubkey: bitcoin::ScriptBuf::new(), - }], - }; - let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); - psbt.inputs[0].witness_utxo = Some(bitcoin::TxOut { - value: bitcoin::Amount::from_sat(100_000), - script_pubkey: bitcoin::ScriptBuf::new(), - }); - let cmr = Program::::from_str(program, None) - .unwrap() - .cmr(); - let expected = bitcoin_execution_environment( - &psbt, - 0, - cmr, - &Script::from(vec![0x52]), - BitcoinNetwork::Testnet4, - ) - .unwrap(); - let invoke = |psbt: &Psbt| { - Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) - .args([ - "btc-tx", - "approval-hash", - "--psbt", - &hex::encode(psbt.serialize()), - "--input-index", - "0", - "--program", - program, - "--network", - "testnet4", - "--redeem-script-hex", - "52", - ]) - .output() - .unwrap() - }; - let result = invoke(&psbt); - assert!(result.status.success()); - let output: serde_json::Value = serde_json::from_slice(&result.stdout).unwrap(); - assert_eq!( - output, - serde_json::json!({ - "approval_hash_hex": hex::encode(expected.approval_hash), - "cmr_hex": hex::encode(cmr.to_byte_array()), - }) - ); - psbt.inputs[0].witness_utxo = None; - let result = invoke(&psbt); - assert!(!result.status.success()); - assert!(String::from_utf8_lossy(&result.stderr).contains("witness_utxo")); -} diff --git a/cli/tests/common/mod.rs b/cli/tests/common/mod.rs new file mode 100644 index 0000000..630ae13 --- /dev/null +++ b/cli/tests/common/mod.rs @@ -0,0 +1,35 @@ +use std::{ + fs::{self, OpenOptions}, + io::Write, + path::PathBuf, + sync::atomic::{AtomicUsize, Ordering}, +}; + +pub struct SecretFile(pub PathBuf); + +impl SecretFile { + pub fn new(secret: [u8; 32]) -> Self { + static NEXT: AtomicUsize = AtomicUsize::new(0); + let path = std::env::current_dir().unwrap().join(format!( + ".policy-test-key-{}-{}", + std::process::id(), + NEXT.fetch_add(1, Ordering::Relaxed), + )); + let mut options = OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o600); + } + let mut file = options.open(&path).unwrap(); + file.write_all(hex::encode(secret).as_bytes()).unwrap(); + Self(path) + } +} + +impl Drop for SecretFile { + fn drop(&mut self) { + fs::remove_file(&self.0).unwrap(); + } +} diff --git a/cli/tests/csv_recovery.rs b/cli/tests/csv_recovery.rs deleted file mode 100644 index 8c7a772..0000000 --- a/cli/tests/csv_recovery.rs +++ /dev/null @@ -1,358 +0,0 @@ -use std::process::{Command, Output}; - -use simplicity::{ - bitcoin::{self as btc, hashes::Hash}, - elements::{ - self as el, - secp256k1_zkp::{Keypair, Message, Secp256k1, SecretKey}, - }, -}; -use simplicity_unchained_core::{ - ElementsNetwork, - utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc, p2tr_multisig_leaf_elements}, -}; - -#[derive(Clone, Copy)] -enum Chain { - Bitcoin, - Elements, -} - -impl Chain { - fn command(self) -> &'static str { - match self { - Self::Bitcoin => "btc-tx", - Self::Elements => "tx", - } - } - - fn envelope_key(self) -> &'static str { - match self { - Self::Bitcoin => "psbt", - Self::Elements => "pset", - } - } -} - -const CHAINS: [Chain; 2] = [Chain::Bitcoin, Chain::Elements]; - -fn keys() -> [Keypair; 2] { - [0x11, 0x22].map(|value| { - Keypair::from_secret_key( - &Secp256k1::new(), - &SecretKey::from_slice(&[value; 32]).unwrap(), - ) - }) -} - -fn invoke(args: &[&str]) -> Output { - Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) - .args(args) - .output() - .unwrap() -} - -fn success(output: Output) -> serde_json::Value { - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - serde_json::from_slice(&output.stdout).unwrap() -} - -fn assert_failure(output: Output, expected: &str) { - assert!(!output.status.success(), "invalid recovery spend succeeded"); - assert!(output.stdout.is_empty(), "failure emitted success JSON"); - let error = String::from_utf8_lossy(&output.stderr); - assert!(error.contains(expected), "expected {expected}, got {error}"); -} - -fn recovery_leaf(chain: Chain, blocks: u16) -> Vec { - let result = success(invoke(&[ - chain.command(), - "build-csv-leaf", - "--user-pubkey", - &btc::PublicKey::new(keys()[1].public_key()).to_string(), - "--timelock", - &blocks.to_string(), - ])); - hex::decode(result["script"].as_str().unwrap()).unwrap() -} - -fn fixture(chain: Chain, version: u32, sequences: [u32; 2], script: &[u8]) -> String { - let secp = Secp256k1::new(); - let [cosigner_key, user_key] = keys(); - let cosigner = btc::PublicKey::new(cosigner_key.public_key()); - let user = btc::PublicKey::new(user_key.public_key()); - match chain { - Chain::Bitcoin => { - use btc::{ - sighash::{Prevouts, SighashCache}, - taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, - }; - let leaf = btc::ScriptBuf::from_bytes(script.to_vec()); - let spend = TaprootBuilder::new() - .add_leaf(1, p2tr_multisig_leaf_btc(&cosigner, &user)) - .unwrap() - .add_leaf(1, leaf.clone()) - .unwrap() - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .unwrap(); - let tx = btc::Transaction { - version: btc::transaction::Version(i32::try_from(version).unwrap()), - lock_time: btc::absolute::LockTime::ZERO, - input: sequences - .into_iter() - .enumerate() - .map(|(index, sequence)| btc::TxIn { - previous_output: btc::OutPoint { - txid: btc::Txid::all_zeros(), - vout: u32::try_from(index).unwrap(), - }, - sequence: btc::Sequence(sequence), - ..btc::TxIn::default() - }) - .collect(), - output: vec![btc::TxOut { - value: btc::Amount::from_sat(190_000), - script_pubkey: btc::ScriptBuf::new(), - }], - }; - let prevouts = vec![ - btc::TxOut { - value: btc::Amount::from_sat(100_000), - script_pubkey: btc::ScriptBuf::new_p2tr_tweaked(spend.output_key()), - }; - 2 - ]; - let mut psbt = btc::Psbt::from_unsigned_tx(tx).unwrap(); - let leaf_hash = TapLeafHash::from_script(&leaf, LeafVersion::TapScript); - let control = spend - .control_block(&(leaf.clone(), LeafVersion::TapScript)) - .unwrap(); - for (index, input) in psbt.inputs.iter_mut().enumerate() { - input.witness_utxo = Some(prevouts[index].clone()); - input - .tap_scripts - .insert(control.clone(), (leaf.clone(), LeafVersion::TapScript)); - let digest = SighashCache::new(&psbt.unsigned_tx) - .taproot_script_spend_signature_hash( - index, - &Prevouts::All(&prevouts), - leaf_hash, - btc::TapSighashType::Default, - ) - .unwrap(); - input.tap_script_sigs.insert( - (user_key.x_only_public_key().0, leaf_hash), - btc::taproot::Signature { - signature: secp - .sign_schnorr(&Message::from_digest(digest.to_byte_array()), &user_key), - sighash_type: btc::TapSighashType::Default, - }, - ); - } - hex::encode(psbt.serialize()) - } - Chain::Elements => { - use el::{ - pset::PartiallySignedTransaction as Pset, - sighash::{Prevouts, SighashCache}, - taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, - }; - let leaf = el::Script::from(script.to_vec()); - let spend = TaprootBuilder::new() - .add_leaf(1, p2tr_multisig_leaf_elements(&cosigner, &user)) - .unwrap() - .add_leaf(1, leaf.clone()) - .unwrap() - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .unwrap(); - let tx = el::Transaction { - version, - lock_time: el::LockTime::ZERO, - input: sequences - .into_iter() - .enumerate() - .map(|(index, sequence)| el::TxIn { - previous_output: el::OutPoint { - txid: el::Txid::from_byte_array([0; 32]), - vout: u32::try_from(index).unwrap(), - }, - sequence: el::Sequence(sequence), - ..el::TxIn::default() - }) - .collect(), - output: vec![el::TxOut { - asset: el::confidential::Asset::Explicit(el::AssetId::default()), - value: el::confidential::Value::Explicit(190_000), - ..el::TxOut::default() - }], - }; - let prevouts = vec![ - el::TxOut { - asset: el::confidential::Asset::Explicit(el::AssetId::default()), - value: el::confidential::Value::Explicit(100_000), - script_pubkey: el::Script::new_v1_p2tr_tweaked(spend.output_key()), - ..el::TxOut::default() - }; - 2 - ]; - let mut pset = Pset::from_tx(tx.clone()); - let leaf_hash = TapLeafHash::from_script(&leaf, LeafVersion::default()); - let control = spend - .control_block(&(leaf.clone(), LeafVersion::default())) - .unwrap(); - for (index, input) in pset.inputs_mut().iter_mut().enumerate() { - input.witness_utxo = Some(prevouts[index].clone()); - input - .tap_scripts - .insert(control.clone(), (leaf.clone(), LeafVersion::default())); - let digest = SighashCache::new(&tx) - .taproot_script_spend_signature_hash( - index, - &Prevouts::All(&prevouts), - leaf_hash, - el::SchnorrSighashType::Default, - ElementsNetwork::LiquidTestnet.genesis_hash(), - ) - .unwrap(); - input.tap_script_sigs.insert( - (user_key.x_only_public_key().0, leaf_hash), - el::SchnorrSig { - sig: secp - .sign_schnorr(&Message::from_digest(digest.to_byte_array()), &user_key), - hash_ty: el::SchnorrSighashType::Default, - }, - ); - } - hex::encode(el::encode::serialize(&pset)) - } - } -} - -fn sign(chain: Chain, envelope: &str, script: &[u8], index: usize) -> Output { - invoke(&[ - chain.command(), - "spend-user-leaf", - "--psbt", - envelope, - "--secret-key", - &hex::encode([0x22; 32]), - "--user-leaf-script", - &hex::encode(script), - "--cosigner-pubkey", - &btc::PublicKey::new(keys()[0].public_key()).to_string(), - "--input-index", - &index.to_string(), - ]) -} - -fn finalize(chain: Chain, envelope: &str) -> Output { - invoke(&[chain.command(), "finalize-user-leaf", "--psbt", envelope]) -} - -fn txid(chain: Chain, envelope: &str) -> String { - let bytes = hex::decode(envelope).unwrap(); - match chain { - Chain::Bitcoin => btc::Psbt::deserialize(&bytes) - .unwrap() - .unsigned_tx - .compute_txid() - .to_string(), - Chain::Elements => el::encode::deserialize::(&bytes) - .unwrap() - .extract_tx() - .unwrap() - .txid() - .to_string(), - } -} - -#[test] -fn recovery_commands_reject_incompatible_sequences_on_each_chain() { - for chain in CHAINS { - let script = recovery_leaf(chain, 144); - for sequence in [u32::MAX, 0x8000_0090, 0x0040_0090, 0, 143] { - let envelope = fixture(chain, 2, [144, sequence], &script); - assert_failure( - sign(chain, &envelope, &script, 1), - "Input 1: CSV recovery requires a block-based sequence of at least 144", - ); - assert_failure( - finalize(chain, &envelope), - "Input 1: CSV recovery requires a block-based sequence of at least 144", - ); - success(sign(chain, &envelope, &script, 0)); - } - let default_sequences = fixture(chain, 2, [u32::MAX; 2], &script); - assert_failure( - finalize(chain, &default_sequences), - "Input 0: CSV recovery requires a block-based sequence", - ); - } -} - -#[test] -fn recovery_commands_reject_old_transaction_versions_on_each_chain() { - for chain in CHAINS { - let script = recovery_leaf(chain, 144); - let envelope = fixture(chain, 1, [144; 2], &script); - assert_failure( - sign(chain, &envelope, &script, 0), - "transaction version >= 2", - ); - assert_failure(finalize(chain, &envelope), "transaction version >= 2"); - } -} - -#[test] -fn valid_recovery_delays_preserve_transactions_through_signing_and_finalization() { - for chain in CHAINS { - for blocks in [0, 1, 16, 144, u16::MAX] { - let script = recovery_leaf(chain, blocks); - let envelope = fixture(chain, 2, [u32::from(blocks), u32::from(u16::MAX)], &script); - let expected_txid = txid(chain, &envelope); - let mut signed = envelope; - for index in 0..2 { - let result = success(sign(chain, &signed, &script, index)); - signed = result[chain.envelope_key()].as_str().unwrap().to_string(); - assert_eq!(txid(chain, &signed), expected_txid); - } - let finalized = success(finalize(chain, &signed)); - assert_eq!(finalized["txid"], expected_txid); - assert!(!finalized["transaction_hex"].as_str().unwrap().is_empty()); - } - } -} - -#[test] -fn other_csv_scripts_fail_explicitly_and_non_csv_leaves_remain_supported() { - for chain in CHAINS { - let mut script = recovery_leaf(chain, 144); - script.push(btc::opcodes::all::OP_NOP.to_u8()); - let envelope = fixture(chain, 2, [144; 2], &script); - assert_failure( - sign(chain, &envelope, &script, 0), - "unsupported CSV recovery script", - ); - assert_failure( - finalize(chain, &envelope), - "unsupported CSV recovery script", - ); - - let script = btc::script::Builder::new() - .push_x_only_key(&keys()[1].x_only_public_key().0) - .push_opcode(btc::opcodes::all::OP_CHECKSIG) - .into_script() - .into_bytes(); - let envelope = fixture(chain, 1, [u32::MAX; 2], &script); - let result = success(sign(chain, &envelope, &script, 0)); - let finalized = success(finalize( - chain, - result[chain.envelope_key()].as_str().unwrap(), - )); - assert_eq!(finalized["txid"], txid(chain, &envelope)); - } -} diff --git a/cli/tests/finalize.rs b/cli/tests/finalize.rs deleted file mode 100644 index 3fa1a97..0000000 --- a/cli/tests/finalize.rs +++ /dev/null @@ -1,530 +0,0 @@ -use std::process::{Command, Output}; - -use simplicity::{ - bitcoin::{self as btc, hashes::Hash}, - elements::{ - self as el, - secp256k1_zkp::{Keypair, Message, Secp256k1, SecretKey}, - }, -}; -use simplicity_unchained_core::{ - ElementsNetwork, - utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc, p2tr_multisig_leaf_elements}, -}; - -#[derive(Clone, Copy)] -enum Leaves { - Cooperative, - OpcodeLookalike, - Ambiguous, -} - -fn keypairs() -> [Keypair; 2] { - let secp = Secp256k1::new(); - [0x11, 0x22] - .map(|byte| Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[byte; 32]).unwrap())) -} - -fn public_keys(keys: &[Keypair; 2]) -> [btc::PublicKey; 2] { - keys.map(|key| btc::PublicKey::new(key.public_key())) -} - -fn invoke(args: &[&str]) -> Output { - Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) - .args(args) - .output() - .unwrap() -} - -fn assert_failure(output: Output, expected: &str) { - assert!( - !output.status.success(), - "invalid envelope was reported finalized" - ); - assert!( - output.stdout.is_empty(), - "failure emitted success-shaped stdout" - ); - let error = String::from_utf8_lossy(&output.stderr); - assert!(error.contains(expected), "expected {expected}, got {error}"); -} - -fn success(output: Output) -> String { - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - let result: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); - assert_eq!(result["finalized"], true); - assert_eq!(result["inputs"], 2); - result["transaction_hex"].as_str().unwrap().to_string() -} - -mod bitcoin { - use super::*; - use btc::{ - psbt::Psbt, - sighash::{Prevouts, SighashCache}, - taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, - }; - - fn fixture(leaves: Leaves) -> Psbt { - let secp = Secp256k1::new(); - let keys = keypairs(); - let [cosigner, user] = public_keys(&keys); - let mut leaf = p2tr_multisig_leaf_btc(&cosigner, &user); - if matches!(leaves, Leaves::OpcodeLookalike) { - let mut bytes = leaf.into_bytes(); - bytes[33] = btc::opcodes::all::OP_DROP.to_u8(); - leaf = btc::ScriptBuf::from_bytes(bytes); - } - let other_leaf = if matches!(leaves, Leaves::Ambiguous) { - p2tr_multisig_leaf_btc(&user, &cosigner) - } else { - btc::script::Builder::new().push_int(1).into_script() - }; - let spend_info = TaprootBuilder::new() - .add_leaf(1, leaf.clone()) - .unwrap() - .add_leaf(1, other_leaf.clone()) - .unwrap() - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .unwrap(); - let tx = btc::Transaction { - version: btc::transaction::Version::TWO, - lock_time: btc::absolute::LockTime::ZERO, - input: (0..2) - .map(|vout| btc::TxIn { - previous_output: btc::OutPoint { - txid: btc::Txid::all_zeros(), - vout, - }, - ..btc::TxIn::default() - }) - .collect(), - output: vec![btc::TxOut { - value: btc::Amount::from_sat(190_000), - script_pubkey: btc::ScriptBuf::new(), - }], - }; - let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); - let prevouts = vec![ - btc::TxOut { - value: btc::Amount::from_sat(100_000), - script_pubkey: btc::ScriptBuf::new_p2tr_tweaked(spend_info.output_key()), - }; - 2 - ]; - let leaf_hash = TapLeafHash::from_script(&leaf, LeafVersion::TapScript); - let control = spend_info - .control_block(&(leaf.clone(), LeafVersion::TapScript)) - .unwrap(); - for (index, input) in psbt.inputs.iter_mut().enumerate() { - input.witness_utxo = Some(prevouts[index].clone()); - input - .tap_scripts - .insert(control.clone(), (leaf.clone(), LeafVersion::TapScript)); - if matches!(leaves, Leaves::Ambiguous) { - let other_control = spend_info - .control_block(&(other_leaf.clone(), LeafVersion::TapScript)) - .unwrap(); - input - .tap_scripts - .insert(other_control, (other_leaf.clone(), LeafVersion::TapScript)); - } - for (key, sighash_type) in keys - .iter() - .zip([btc::TapSighashType::Default, btc::TapSighashType::All]) - { - let hash = SighashCache::new(&psbt.unsigned_tx) - .taproot_script_spend_signature_hash( - index, - &Prevouts::All(&prevouts), - leaf_hash, - sighash_type, - ) - .unwrap(); - input.tap_script_sigs.insert( - (key.x_only_public_key().0, leaf_hash), - btc::taproot::Signature { - signature: secp - .sign_schnorr(&Message::from_digest(hash.to_byte_array()), key), - sighash_type, - }, - ); - } - } - psbt - } - - fn run(psbt: &Psbt) -> Output { - invoke(&[ - "btc-tx", - "finalize", - "--psbt", - &hex::encode(psbt.serialize()), - ]) - } - - #[test] - fn earlier_decoys_do_not_change_a_verified_cooperative_witness() { - let original = fixture(Leaves::Cooperative); - let expected = success(run(&original)); - for swapped in [false, true] { - let mut psbt = original.clone(); - for input in &mut psbt.inputs { - let (control, (leaf, version)) = input.tap_scripts.first_key_value().unwrap(); - let mut decoy = control.clone(); - decoy.merkle_branch = Default::default(); - let decoy_leaf = if swapped { - let [cosigner, user] = public_keys(&keypairs()); - p2tr_multisig_leaf_btc(&user, &cosigner) - } else { - btc::script::Builder::new().push_int(1).into_script() - }; - assert!(decoy < *control); - assert_ne!(decoy_leaf, *leaf); - let version = *version; - input - .tap_scripts - .insert(decoy.clone(), (decoy_leaf, version)); - assert_eq!(input.tap_scripts.first_key_value().unwrap().0, &decoy); - input.final_script_witness = Some(btc::Witness::from_slice(&[vec![1, 2, 3]])); - } - assert_eq!(success(run(&psbt)), expected); - } - let tx: btc::Transaction = - btc::consensus::deserialize(&hex::decode(expected).unwrap()).unwrap(); - let [cosigner, user] = public_keys(&keypairs()); - let leaf = p2tr_multisig_leaf_btc(&cosigner, &user); - let hash = TapLeafHash::from_script(&leaf, LeafVersion::TapScript); - for (input, finalized) in original.inputs.iter().zip(&tx.input) { - let expected = [ - input.tap_script_sigs[&(user.inner.x_only_public_key().0, hash)] - .serialize() - .to_vec(), - input.tap_script_sigs[&(cosigner.inner.x_only_public_key().0, hash)] - .serialize() - .to_vec(), - leaf.to_bytes(), - input.tap_scripts.first_key_value().unwrap().0.serialize(), - ]; - assert_eq!(finalized.witness.to_vec(), expected); - } - } - - #[test] - fn rejects_committed_opcode_lookalikes_and_ambiguous_leaves() { - assert_failure( - run(&fixture(Leaves::OpcodeLookalike)), - "no committed canonical multisig leaf", - ); - assert_failure( - run(&fixture(Leaves::Ambiguous)), - "ambiguous committed multisig leaves", - ); - } - - #[test] - fn rejects_invalid_control_versions_and_foreign_output() { - let original = fixture(Leaves::Cooperative); - let mut changed = original.clone(); - let (control, value) = changed.inputs[0].tap_scripts.pop_first().unwrap(); - let mut invalid = control.clone(); - invalid.merkle_branch = Default::default(); - changed.inputs[0].tap_scripts.insert(invalid, value.clone()); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - let mut changed = original.clone(); - changed.inputs[0].tap_scripts.insert( - control, - (value.0, LeafVersion::from_consensus(0xc2).unwrap()), - ); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - let mut changed = original.clone(); - changed.inputs[0] - .witness_utxo - .as_mut() - .unwrap() - .script_pubkey = original.unsigned_tx.output[0].script_pubkey.clone(); - assert_failure(run(&changed), "spent output is not P2TR"); - let mut changed = original.clone(); - changed.inputs[0] - .witness_utxo - .as_mut() - .unwrap() - .script_pubkey = - btc::ScriptBuf::new_p2tr(&Secp256k1::new(), keypairs()[0].x_only_public_key().0, None); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - } - - #[test] - fn verifies_each_signature_and_all_transaction_inputs() { - let original = fixture(Leaves::Cooperative); - for key in keypairs() { - let mut changed = original.clone(); - let input = &mut changed.inputs[1]; - let entry = input - .tap_script_sigs - .iter_mut() - .find(|((public_key, _), _)| *public_key == key.x_only_public_key().0) - .unwrap(); - entry.1.signature = Secp256k1::new().sign_schnorr(&Message::from_digest([0; 32]), &key); - assert_failure(run(&changed), "Input 1 invalid tap_script_sig"); - } - let mut changed = original.clone(); - changed.inputs[1].tap_script_sigs.pop_first(); - assert_failure(run(&changed), "Input 1 missing tap_script_sig"); - let mut changed = original.clone(); - changed.unsigned_tx.output[0].value = btc::Amount::from_sat(180_000); - assert_failure(run(&changed), "invalid tap_script_sig"); - let mut changed = original.clone(); - changed.inputs[1].witness_utxo.as_mut().unwrap().value = btc::Amount::from_sat(99_999); - assert_failure(run(&changed), "invalid tap_script_sig"); - let mut changed = original.clone(); - changed.inputs[1].witness_utxo = None; - assert_failure(run(&changed), "Input 1 missing witness_utxo"); - } -} - -mod elements { - use super::*; - use el::{ - pset::PartiallySignedTransaction as Pset, - sighash::{Prevouts, SighashCache}, - taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, - }; - - fn fixture(leaves: Leaves) -> Pset { - let secp = Secp256k1::new(); - let keys = keypairs(); - let [cosigner, user] = public_keys(&keys); - let mut leaf = p2tr_multisig_leaf_elements(&cosigner, &user); - if matches!(leaves, Leaves::OpcodeLookalike) { - let mut bytes = leaf.to_bytes(); - bytes[33] = el::opcodes::all::OP_DROP.into_u8(); - leaf = el::Script::from(bytes); - } - let other_leaf = if matches!(leaves, Leaves::Ambiguous) { - p2tr_multisig_leaf_elements(&user, &cosigner) - } else { - el::script::Builder::new().push_int(1).into_script() - }; - let spend_info = TaprootBuilder::new() - .add_leaf(1, leaf.clone()) - .unwrap() - .add_leaf(1, other_leaf.clone()) - .unwrap() - .finalize(&secp, *UNSPENDABLE_KEY_P2TR) - .unwrap(); - let tx = el::Transaction { - version: 2, - lock_time: el::LockTime::ZERO, - input: (0..2) - .map(|vout| el::TxIn { - previous_output: el::OutPoint { - txid: el::Txid::from_byte_array([0; 32]), - vout, - }, - ..el::TxIn::default() - }) - .collect(), - output: vec![el::TxOut { - asset: el::confidential::Asset::Explicit(el::AssetId::default()), - value: el::confidential::Value::Explicit(190_000), - ..el::TxOut::default() - }], - }; - let mut pset = Pset::from_tx(tx.clone()); - let prevouts = vec![ - el::TxOut { - asset: el::confidential::Asset::Explicit(el::AssetId::default()), - value: el::confidential::Value::Explicit(100_000), - script_pubkey: el::Script::new_v1_p2tr_tweaked(spend_info.output_key()), - ..el::TxOut::default() - }; - 2 - ]; - let leaf_hash = TapLeafHash::from_script(&leaf, LeafVersion::default()); - let control = spend_info - .control_block(&(leaf.clone(), LeafVersion::default())) - .unwrap(); - for (index, input) in pset.inputs_mut().iter_mut().enumerate() { - input.witness_utxo = Some(prevouts[index].clone()); - input - .tap_scripts - .insert(control.clone(), (leaf.clone(), LeafVersion::default())); - if matches!(leaves, Leaves::Ambiguous) { - let other_control = spend_info - .control_block(&(other_leaf.clone(), LeafVersion::default())) - .unwrap(); - input - .tap_scripts - .insert(other_control, (other_leaf.clone(), LeafVersion::default())); - } - for (key, hash_ty) in keys - .iter() - .zip([el::SchnorrSighashType::Default, el::SchnorrSighashType::All]) - { - let hash = SighashCache::new(&tx) - .taproot_script_spend_signature_hash( - index, - &Prevouts::All(&prevouts), - leaf_hash, - hash_ty, - ElementsNetwork::LiquidTestnet.genesis_hash(), - ) - .unwrap(); - input.tap_script_sigs.insert( - (key.x_only_public_key().0, leaf_hash), - el::SchnorrSig { - sig: secp.sign_schnorr(&Message::from_digest(hash.to_byte_array()), key), - hash_ty, - }, - ); - } - } - pset - } - - fn run(pset: &Pset) -> Output { - invoke(&[ - "tx", - "finalize", - "--pset", - &hex::encode(el::encode::serialize(pset)), - "--network", - "liquid_testnet", - ]) - } - - #[test] - fn earlier_decoys_do_not_change_a_verified_cooperative_witness() { - let original = fixture(Leaves::Cooperative); - let expected = success(run(&original)); - for swapped in [false, true] { - let mut pset = original.clone(); - for input in pset.inputs_mut() { - let (control, (leaf, version)) = input.tap_scripts.first_key_value().unwrap(); - let mut decoy = control.clone(); - decoy.merkle_branch = Default::default(); - let decoy_leaf = if swapped { - let [cosigner, user] = public_keys(&keypairs()); - p2tr_multisig_leaf_elements(&user, &cosigner) - } else { - el::script::Builder::new().push_int(1).into_script() - }; - assert!(decoy < *control); - assert_ne!(decoy_leaf, *leaf); - let version = *version; - input - .tap_scripts - .insert(decoy.clone(), (decoy_leaf, version)); - assert_eq!(input.tap_scripts.first_key_value().unwrap().0, &decoy); - input.final_script_witness = Some(vec![vec![1, 2, 3]].into()); - } - assert_eq!(success(run(&pset)), expected); - } - let tx: el::Transaction = el::encode::deserialize(&hex::decode(expected).unwrap()).unwrap(); - let [cosigner, user] = public_keys(&keypairs()); - let leaf = p2tr_multisig_leaf_elements(&cosigner, &user); - let hash = TapLeafHash::from_script(&leaf, LeafVersion::default()); - for (input, finalized) in original.inputs().iter().zip(&tx.input) { - let expected = [ - input.tap_script_sigs[&(user.inner.x_only_public_key().0, hash)].to_vec(), - input.tap_script_sigs[&(cosigner.inner.x_only_public_key().0, hash)].to_vec(), - leaf.to_bytes(), - input.tap_scripts.first_key_value().unwrap().0.serialize(), - ]; - assert_eq!( - finalized.witness.script_witness, - el::Witness::from(expected.to_vec()) - ); - } - } - - #[test] - fn rejects_committed_opcode_lookalikes_and_ambiguous_leaves() { - assert_failure( - run(&fixture(Leaves::OpcodeLookalike)), - "no committed canonical multisig leaf", - ); - assert_failure( - run(&fixture(Leaves::Ambiguous)), - "ambiguous committed multisig leaves", - ); - } - - #[test] - fn rejects_invalid_control_versions_and_foreign_output() { - let original = fixture(Leaves::Cooperative); - let mut changed = original.clone(); - let (control, value) = changed.inputs_mut()[0].tap_scripts.pop_first().unwrap(); - let mut invalid = control.clone(); - invalid.merkle_branch = Default::default(); - changed.inputs_mut()[0] - .tap_scripts - .insert(invalid, value.clone()); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - let mut changed = original.clone(); - changed.inputs_mut()[0] - .tap_scripts - .insert(control, (value.0, LeafVersion::from_u8(0xc2).unwrap())); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - let mut changed = original.clone(); - changed.inputs_mut()[0] - .witness_utxo - .as_mut() - .unwrap() - .script_pubkey = el::Script::new(); - assert_failure(run(&changed), "spent output is not P2TR"); - let mut changed = original.clone(); - changed.inputs_mut()[0] - .witness_utxo - .as_mut() - .unwrap() - .script_pubkey = - el::Script::new_v1_p2tr(&Secp256k1::new(), keypairs()[0].x_only_public_key().0, None); - assert_failure(run(&changed), "no committed canonical multisig leaf"); - } - - #[test] - fn verifies_each_signature_all_transaction_inputs_and_network() { - let original = fixture(Leaves::Cooperative); - for key in keypairs() { - let mut changed = original.clone(); - let input = &mut changed.inputs_mut()[1]; - let entry = input - .tap_script_sigs - .iter_mut() - .find(|((public_key, _), _)| *public_key == key.x_only_public_key().0) - .unwrap(); - entry.1.sig = Secp256k1::new().sign_schnorr(&Message::from_digest([0; 32]), &key); - assert_failure(run(&changed), "Input 1 invalid tap_script_sig"); - } - let mut changed = original.clone(); - changed.inputs_mut()[1].tap_script_sigs.pop_first(); - assert_failure(run(&changed), "Input 1 missing tap_script_sig"); - let mut changed = original.clone(); - changed.outputs_mut()[0].amount = Some(180_000); - assert_failure(run(&changed), "invalid tap_script_sig"); - let mut changed = original.clone(); - changed.inputs_mut()[1].witness_utxo.as_mut().unwrap().value = - el::confidential::Value::Explicit(99_999); - assert_failure(run(&changed), "invalid tap_script_sig"); - let mut changed = original.clone(); - changed.inputs_mut()[1].witness_utxo = None; - assert_failure(run(&changed), "Input 1 missing witness_utxo"); - assert_failure( - invoke(&[ - "tx", - "finalize", - "--pset", - &hex::encode(el::encode::serialize(&original)), - "--network", - "liquid", - ]), - "invalid tap_script_sig", - ); - } -} diff --git a/cli/tests/liquid_policy.rs b/cli/tests/liquid_policy.rs new file mode 100644 index 0000000..8ea4289 --- /dev/null +++ b/cli/tests/liquid_policy.rs @@ -0,0 +1,207 @@ +#[allow(dead_code)] +mod common { + include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../policy/tests/common/liquid.rs" + )); +} +use common::*; +use serde_json::{Value, json}; +use simplicity::base64::{Engine, engine::general_purpose::STANDARD}; +use simplicity_unchained_policy::liquid::{self as policy, elements::encode::serialize}; +use std::{path::PathBuf, process::Command}; + +struct File(PathBuf); +impl File { + fn new(label: &str, data: &[u8]) -> Self { + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!( + "target-liquid-test-{}-{label}.json", + std::process::id() + )); + std::fs::write(&path, data).unwrap(); + Self(path) + } +} +impl Drop for File { + fn drop(&mut self) { + std::fs::remove_file(&self.0).unwrap(); + } +} +fn request(f: &Fixture) -> Value { + json!({"profile":"liquid-policy-v1","network":"liquidtestnet","program":STANDARD.encode(&f.program),"redeem_script_hex":hex::encode(&f.data),"base_public_key_hex":f.base.to_string(),"pset_hex":hex::encode(serialize(&f.pset)),"input_index":0,"user_pubkey":f.user.to_string(),"user_leaf_hash_hex":hex::encode(f.recovery.to_byte_array()),"witness":""}) +} +fn run(op: &str, file: &File) -> std::process::Output { + Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["liquid-policy", op, "--request-file"]) + .arg(&file.0) + .output() + .unwrap() +} +#[test] +fn liquid_cli_registry_prepare_evaluate_address_finalize_and_rejections() { + let mut f = Fixture::new(); + let registry = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["liquid-policy", "registry"]) + .output() + .unwrap(); + assert!(registry.status.success()); + let registry: Value = serde_json::from_slice(®istry.stdout).unwrap(); + assert_eq!( + registry["profile_hash"], + hex::encode(policy::profile_hash()) + ); + let file = File::new("unsigned", &serde_json::to_vec(&request(&f)).unwrap()); + for operation in ["prepare", "approval-hash", "evaluate", "tweak", "address"] { + let output = run(operation, &file); + assert!( + output.status.success(), + "{operation}: {}", + String::from_utf8_lossy(&output.stderr) + ); + let response: Value = serde_json::from_slice(&output.stdout).unwrap(); + if let Some(context) = response.get("context") { + assert_eq!( + context, + &serde_json::to_value(f.context(0).unwrap().summary()).unwrap() + ); + } + } + f.sign_all(); + let signed = File::new("signed", &serde_json::to_vec(&request(&f)).unwrap()); + let key = File::new( + "user-key", + hex::encode(f.user_secret.secret_bytes()).as_bytes(), + ); + let cosigned = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["liquid-policy", "user-sign", "--request-file"]) + .arg(&signed.0) + .arg("--secret-key-file") + .arg(&key.0) + .output() + .unwrap(); + assert!( + cosigned.status.success(), + "{}", + String::from_utf8_lossy(&cosigned.stderr) + ); + let cosigned: Value = serde_json::from_slice(&cosigned.stdout).unwrap(); + assert_eq!(cosigned["public_key_hex"], f.user.to_string()); + let secp = policy::elements::secp256k1_zkp::Secp256k1::new(); + let blinding = + policy::elements::secp256k1_zkp::PublicKey::from_secret_key(&secp, &f.blinding_secret); + let creation = json!({ + "network":"liquidtestnet", + "inputs":f.pset.inputs().iter().map(|i|json!({"txid":i.previous_txid.to_string(),"vout":i.previous_output_index,"parent_tx_hex":hex::encode(serialize(i.non_witness_utxo.as_ref().unwrap())),"blinding_secret_key_hex":hex::encode(f.blinding_secret.secret_bytes())})).collect::>(), + "outputs":f.pset.outputs().iter().map(|o|json!({"address":if o.script_pubkey.is_empty() {"fee".into()} else {policy::elements::Address::from_script(&o.script_pubkey,Some(blinding),f.network.address_params()).unwrap().to_string()},"asset_id":o.asset.unwrap().to_string(),"value_sat":o.amount.unwrap()})).collect::>(), + }); + let create_file = File::new("create", &serde_json::to_vec(&creation).unwrap()); + let created = run("create", &create_file); + assert!( + created.status.success(), + "{}", + String::from_utf8_lossy(&created.stderr) + ); + let created: Value = serde_json::from_slice(&created.stdout).unwrap(); + let p = policy::elements::encode::deserialize( + &hex::decode(created["pset_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + assert!(f.context_for(&p, 0).is_ok()); + let output = run("finalize", &signed); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + assert_eq!( + serde_json::from_slice::(&output.stdout).unwrap()["finalized"], + true + ); + let mut wrong = request(&f); + wrong["profile_hash"] = json!(hex::encode(simplicity_unchained_policy::profile_hash())); + let bad = File::new("profile", &serde_json::to_vec(&wrong).unwrap()); + assert!(!run("prepare", &bad).status.success()); + let mut p = f.pset.clone(); + p.inputs_mut()[0].amount = Some(1); + let mut wrong = request(&f); + wrong["pset_hex"] = hex::encode(serialize(&p)).into(); + let bad = File::new("disclosure", &serde_json::to_vec(&wrong).unwrap()); + assert!(!run("prepare", &bad).status.success()); +} + +#[test] +fn liquid_cli_remote_sign_independently_checks_complete_context_and_signature() { + use std::{ + io::{Read, Write}, + net::TcpListener, + time::Duration, + }; + let mut f = Fixture::new(); + let unsigned = request(&f); + let prepared = policy::PreparedPolicy::new( + &f.program, &f.pset, 0, &f.data, f.network, f.base, f.user, f.recovery, + ) + .unwrap(); + let context = prepared.context().clone(); + let cost = prepared.evaluate_default(&[]).unwrap(); + let secret = f.service_secret(); + let sig = policy::add_signature(&context, &mut f.pset, secret, true).unwrap(); + let capabilities = json!({"profile":"liquid-policy-v1","profile_hash":hex::encode(policy::profile_hash()),"network":"liquidtestnet","genesis_hash":hex::encode(f.network.genesis_hash().to_byte_array()),"base_public_key_hex":f.base.to_string(),"manifest":policy::manifest()}); + let signed = json!({"pset_hex":hex::encode(serialize(&f.pset)),"signature_hex":hex::encode(sig.sig.as_ref()),"public_key_hex":context.cooperative().service_pubkey.to_string(),"input_index":0,"context":context.summary(),"execution_cost_milliweight":cost}); + for tamper in [false, true] { + let mut response = signed.clone(); + if tamper { + response["signature_hex"] = json!(hex::encode([1; 64])); + } + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + let caps = capabilities.clone(); + let server = std::thread::spawn(move || { + for response in [caps, response] { + let (mut socket, _) = listener.accept().unwrap(); + socket + .set_read_timeout(Some(Duration::from_secs(10))) + .unwrap(); + let mut bytes = Vec::new(); + let mut buffer = [0; 4096]; + loop { + let read = socket.read(&mut buffer).unwrap(); + assert!(read > 0); + bytes.extend_from_slice(&buffer[..read]); + if let Some(header) = bytes.windows(4).position(|w| w == b"\r\n\r\n") { + let length = String::from_utf8_lossy(&bytes[..header]) + .lines() + .find_map(|line| { + line.to_ascii_lowercase() + .strip_prefix("content-length: ") + .map(|s| s.parse::().unwrap()) + }) + .unwrap_or(0); + if bytes.len() >= header + 4 + length { + break; + } + } + } + let body = response.to_string(); + write!(socket,"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",body.len(),body).unwrap(); + } + }); + let file = File::new( + if tamper { "remote-bad" } else { "remote" }, + &serde_json::to_vec(&unsigned).unwrap(), + ); + let result = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["liquid-policy", "sign", "--request-file"]) + .arg(&file.0) + .args(["--service", &format!("http://127.0.0.1:{port}")]) + .output() + .unwrap(); + server.join().unwrap(); + assert_eq!( + result.status.success(), + !tamper, + "{}", + String::from_utf8_lossy(&result.stderr) + ); + } +} diff --git a/cli/tests/policy.rs b/cli/tests/policy.rs new file mode 100644 index 0000000..35e7b1c --- /dev/null +++ b/cli/tests/policy.rs @@ -0,0 +1,1081 @@ +mod common; + +use std::{ + io::{Read, Write}, + net::{TcpListener, TcpStream}, + process::{Command, Output}, + sync::Arc, + thread::{self, JoinHandle}, + time::{Duration, Instant}, +}; + +use serde_json::{Value, json}; +use simplicity::{ + ConstructNode, + base64::prelude::{BASE64_STANDARD, Engine as _}, + bitcoin::{ + self as btc, Amount, Network, OutPoint, Psbt, PublicKey, ScriptBuf, TapNodeHash, + Transaction, TxIn, TxOut, XOnlyPublicKey, + consensus::serialize, + hashes::Hash, + key::TapTweak, + secp256k1::{Keypair, Message, Parity, Secp256k1, SecretKey}, + taproot::LeafVersion, + }, + node::CoreConstructible, + types, +}; +use simplicity_unchained_core::{ + policy::{PreparedPolicy, policy_cmr}, + utils::generate_p2tr_address_btc, +}; +use simplicity_unchained_policy::{ + PROFILE_NAME, genesis_hash, manifest, profile_hash, signing_commitment, tweak_public_key, +}; + +const PREFIX: &str = "/simplicity-unchained/policy/bitcoin"; + +struct Fixture { + program: Vec, + script: Vec, + psbt: Psbt, + base: Keypair, + user: Keypair, + recovery: TapNodeHash, +} + +impl Fixture { + fn new() -> Self { + let program = types::Context::with_context(|ctx| { + Arc::::unit(&ctx) + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + let script = vec![0x52]; + let secp = Secp256k1::new(); + let key = + |byte| Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[byte; 32]).unwrap()); + let base = key(1); + let user = key(2); + let recovery = TapNodeHash::from_byte_array([3; 32]); + let cmr = policy_cmr(&program).unwrap(); + let service = tweak_public_key( + base.x_only_public_key().0, + cmr.to_byte_array(), + &script, + Network::Testnet4, + ) + .unwrap(); + let full = |key: XOnlyPublicKey| PublicKey::new(key.public_key(Parity::Even)); + let (address, _) = generate_p2tr_address_btc( + &full(service), + &full(user.x_only_public_key().0), + recovery, + Network::Testnet4, + ) + .unwrap(); + let parent = Transaction { + version: btc::transaction::Version::TWO, + lock_time: btc::absolute::LockTime::ZERO, + input: vec![TxIn::default()], + output: vec![ + TxOut { + value: Amount::from_sat(100_000), + script_pubkey: address.script_pubkey(), + }; + 2 + ], + }; + let tx = Transaction { + version: btc::transaction::Version::TWO, + lock_time: btc::absolute::LockTime::ZERO, + input: (0..2) + .map(|vout| TxIn { + previous_output: OutPoint::new(parent.compute_txid(), vout), + ..TxIn::default() + }) + .collect(), + output: vec![TxOut { + value: Amount::from_sat(199_000), + script_pubkey: ScriptBuf::new(), + }], + }; + let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); + for (index, input) in psbt.inputs.iter_mut().enumerate() { + input.non_witness_utxo = Some(parent.clone()); + input.witness_utxo = Some(parent.output[index].clone()); + } + Self { + program, + script, + psbt, + base, + user, + recovery, + } + } + + fn prepared(&self) -> PreparedPolicy { + PreparedPolicy::new( + &self.program, + &self.psbt, + 0, + &self.script, + Network::Testnet4, + self.base.x_only_public_key().0, + self.user.x_only_public_key().0, + self.recovery, + ) + .unwrap() + } + + fn capabilities(&self) -> Value { + let mut limits = serde_json::to_value(&manifest().limits).unwrap(); + limits["max_body_bytes"] = json!(2 * 1024 * 1024); + limits["max_blocking_jobs"] = json!(8); + json!({ + "profile": PROFILE_NAME, + "profile_hash": hex::encode(profile_hash()), + "network": "testnet4", + "genesis_hash": hex::encode(genesis_hash(Network::Testnet4).unwrap()), + "base_public_key_hex": self.base.x_only_public_key().0.to_string(), + "manifest": manifest(), + "limits": limits, + "provenance": "parent-txid-verified", + }) + } + + fn args(&self, operation: &str) -> Vec { + [ + "policy".into(), + operation.into(), + "--network".into(), + "testnet4".into(), + "--program".into(), + BASE64_STANDARD.encode(&self.program), + "--redeem-script-hex".into(), + hex::encode(&self.script), + "--psbt".into(), + hex::encode(self.psbt.serialize()), + "--input-index".into(), + "0".into(), + "--user-pubkey".into(), + self.user.public_key().to_string(), + "--user-leaf-hash-hex".into(), + hex::encode(self.recovery.to_byte_array()), + "--base-public-key".into(), + self.base.x_only_public_key().0.to_string(), + ] + .into() + } + + fn sign_response(&self, include_user: bool) -> Value { + let prepared = self.prepared(); + let context = prepared.context(); + let commitment = signing_commitment( + prepared.cmr().to_byte_array(), + &self.script, + Network::Testnet4, + ) + .unwrap(); + let tweaked = self + .base + .tap_tweak( + &Secp256k1::new(), + Some(TapNodeHash::from_byte_array(commitment)), + ) + .to_keypair(); + let message = Message::from_digest(context.cooperative_sighash_default()); + let signature = Secp256k1::new().sign_schnorr(&message, &tweaked); + let leaf_hash = context.cooperative_leaf_hash(); + let mut psbt = self.psbt.clone(); + psbt.inputs[0].tap_script_sigs.insert( + (context.service_pubkey(), leaf_hash), + btc::taproot::Signature { + signature, + sighash_type: btc::TapSighashType::Default, + }, + ); + if include_user { + psbt.inputs[0].tap_script_sigs.insert( + (context.user_pubkey(), leaf_hash), + btc::taproot::Signature { + signature: Secp256k1::new().sign_schnorr(&message, &self.user), + sighash_type: btc::TapSighashType::Default, + }, + ); + } + psbt.inputs[0].tap_scripts.insert( + context.control_block().clone(), + (context.cooperative_script().clone(), LeafVersion::TapScript), + ); + json!({ + "psbt_hex": hex::encode(psbt.serialize()), + "signature_hex": hex::encode(signature.as_ref()), + "public_key_hex": context.service_pubkey().to_string(), + "input_index": 0, + "context": prepared.summary(), + "execution_cost_milliweight": prepared.evaluate_default(&[]).unwrap().cost_milliweight, + }) + } +} + +struct Reply { + method: &'static str, + path: String, + status: u16, + body: Vec, +} + +impl Reply { + fn json(method: &'static str, operation: &str, body: Value) -> Self { + Self { + method, + path: format!("{PREFIX}/{operation}"), + status: 200, + body: serde_json::to_vec(&body).unwrap(), + } + } +} + +struct MockServer { + origin: String, + thread: JoinHandle)>>, +} + +impl MockServer { + fn start(replies: Vec) -> Self { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let origin = format!("http://{}", listener.local_addr().unwrap()); + listener.set_nonblocking(true).unwrap(); + let thread = thread::spawn(move || { + let mut requests = Vec::new(); + for reply in replies { + let deadline = Instant::now() + Duration::from_secs(10); + let (mut stream, _) = loop { + match listener.accept() { + Ok(connected) => break connected, + Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => { + assert!( + Instant::now() < deadline, + "Expected HTTP request was not made" + ); + thread::sleep(Duration::from_millis(5)); + } + Err(error) => panic!("Mock accept failed: {error}"), + } + }; + stream.set_nonblocking(false).unwrap(); + stream + .set_read_timeout(Some(Duration::from_secs(5))) + .unwrap(); + let (headers, body) = read_request(&mut stream); + assert!( + headers.starts_with(&format!("{} {} HTTP/1.1", reply.method, reply.path)), + "Unexpected request: {headers}" + ); + write!( + stream, + "HTTP/1.1 {} Test\r\nContent-Length: {}\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n", + reply.status, + reply.body.len() + ) + .unwrap(); + stream.write_all(&reply.body).unwrap(); + requests.push((headers, body)); + } + requests + }); + Self { origin, thread } + } + + fn finish(self) -> Vec<(String, Vec)> { + self.thread.join().unwrap() + } +} + +fn read_request(stream: &mut TcpStream) -> (String, Vec) { + let mut bytes = Vec::new(); + let header_end = loop { + let mut chunk = [0; 4096]; + let count = stream.read(&mut chunk).unwrap(); + assert_ne!(count, 0, "Request ended before headers"); + bytes.extend_from_slice(&chunk[..count]); + if let Some(position) = bytes.windows(4).position(|part| part == b"\r\n\r\n") { + break position + 4; + } + }; + let headers = String::from_utf8(bytes[..header_end].to_vec()).unwrap(); + let content_length = headers + .lines() + .find_map(|line| { + let (name, value) = line.split_once(':')?; + name.eq_ignore_ascii_case("content-length") + .then(|| value.trim().parse::().unwrap()) + }) + .unwrap_or(0); + while bytes.len() < header_end + content_length { + let mut chunk = [0; 4096]; + let count = stream.read(&mut chunk).unwrap(); + assert_ne!(count, 0, "Request body ended early"); + bytes.extend_from_slice(&chunk[..count]); + } + (headers, bytes[header_end..].to_vec()) +} + +fn invoke(args: &[String]) -> Output { + Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(args) + .output() + .unwrap() +} + +fn success(output: Output) -> Value { + assert!( + output.status.success(), + "stdout: {}\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + serde_json::from_slice(&output.stdout).unwrap() +} + +fn failure(output: Output, expected: &str) -> Value { + assert!(!output.status.success(), "Command unexpectedly succeeded"); + let value: Value = serde_json::from_slice(&output.stdout).unwrap(); + let error = value["error"].as_str().expect("Stable JSON error output"); + assert!(error.contains(expected), "Expected {expected}, got {error}"); + assert!(value.get("signature_hex").is_none()); + value +} + +fn set_arg(args: &mut [String], name: &str, value: &str) { + let index = args.iter().position(|arg| arg == name).unwrap(); + args[index + 1] = value.to_owned(); +} + +fn remote_args(fixture: &Fixture, operation: &str, origin: &str) -> Vec { + let mut args = fixture.args(operation); + args.extend(["--service".into(), origin.into()]); + if matches!(operation, "evaluate" | "sign") { + args.extend(["--witness".into(), "".into()]); + } + args +} + +#[test] +fn policy_help_exposes_all_primary_operations() { + let result = invoke(&["policy".into(), "--help".into()]); + assert!(result.status.success()); + let help = String::from_utf8(result.stdout).unwrap(); + for name in [ + "capabilities", + "tweak", + "prepare", + "evaluate", + "approval-hash", + "sign", + "address", + "build-csv-leaf", + "user-sign", + "finalize", + ] { + assert!(help.contains(name)); + } +} + +#[test] +fn local_approval_uses_the_shared_verified_constructor_without_contacting_service() { + let fixture = Fixture::new(); + let expected = serde_json::to_value(fixture.prepared().summary()).unwrap(); + let mut args = fixture.args("approval-hash"); + args.push("--offline".into()); + let actual = success(invoke(&args)); + assert_eq!(actual, json!({ "context": expected })); + assert_eq!(actual["context"]["provenance"], "parent-txid-verified"); + assert_eq!(actual["context"]["chain_status_verified"], false); + set_arg(&mut args, "--input-index", "1"); + let another_input = success(invoke(&args)); + assert_eq!( + actual["context"]["policy_transaction_approval_hash"], + another_input["context"]["policy_transaction_approval_hash"] + ); + assert_ne!( + actual["context"]["policy_approval_hash"], + another_input["context"]["policy_approval_hash"] + ); + set_arg(&mut args, "--input-index", "0"); + let mut mutated = fixture.psbt.clone(); + mutated.unsigned_tx.output[0].value = Amount::from_sat(198_999); + set_arg(&mut args, "--psbt", &hex::encode(mutated.serialize())); + let changed = success(invoke(&args)); + assert_ne!( + actual["context"]["policy_approval_hash"], + changed["context"]["policy_approval_hash"] + ); + assert_ne!( + actual["context"]["policy_transaction_approval_hash"], + changed["context"]["policy_transaction_approval_hash"] + ); +} + +#[test] +fn profile_fingerprint_network_and_base64_fail_closed_before_network_io() { + let fixture = Fixture::new(); + for (flag, value, expected) in [ + ("--network", "regtest", "Unsupported policy network"), + ("--network", "Testnet4", "Unsupported policy network"), + ("--program", "not-base64!", "standard-base64"), + ("--base-public-key", "00", "Public key must be"), + ("--user-leaf-hash-hex", "00", "exactly 32 bytes"), + ] { + let mut args = fixture.args("approval-hash"); + set_arg(&mut args, flag, value); + failure(invoke(&args), expected); + } + for (flag, value, expected) in [ + ("--profile", "legacy", "Unsupported policy profile"), + ( + "--profile-hash", + "0000000000000000000000000000000000000000000000000000000000000000", + "compiled registry", + ), + ] { + let mut args = fixture.args("approval-hash"); + args.extend([flag.into(), value.into()]); + failure(invoke(&args), expected); + } +} + +#[test] +fn every_input_requires_full_parent_evidence_even_when_witness_utxo_is_present() { + let mut fixture = Fixture::new(); + fixture.psbt.inputs[1].non_witness_utxo = None; + // Both inputs share a parent. An available full parent can be reused safely. + success(invoke(&fixture.args("approval-hash"))); + fixture.psbt.inputs[1].non_witness_utxo = fixture.psbt.inputs[0].non_witness_utxo.take(); + success(invoke(&fixture.args("approval-hash"))); + for input in &mut fixture.psbt.inputs { + input.non_witness_utxo = None; + } + let mut args = fixture.args("prepare"); + args.extend([ + "--offline".into(), + "--service".into(), + "http://127.0.0.1:1".into(), + ]); + failure( + invoke(&args), + "missing non_witness_utxo full parent evidence", + ); +} + +#[test] +fn parent_content_is_verified_and_missing_witness_utxo_is_populated() { + let mut fixture = Fixture::new(); + let expected = fixture.prepared().summary(); + fixture.psbt.inputs[0].witness_utxo = None; + let result = success(invoke(&fixture.args("approval-hash"))); + assert_eq!(result["context"], serde_json::to_value(expected).unwrap()); + + fixture.psbt.inputs[0].witness_utxo = Some(TxOut { + value: Amount::from_sat(1), + script_pubkey: ScriptBuf::new(), + }); + failure( + invoke(&fixture.args("approval-hash")), + "conflicts with verified parent", + ); + fixture.psbt.inputs[0].witness_utxo = None; + fixture.psbt.inputs[0] + .non_witness_utxo + .as_mut() + .unwrap() + .output[0] + .value = Amount::from_sat(1); + failure( + invoke(&fixture.args("approval-hash")), + "parent transaction txid mismatch", + ); +} + +#[test] +fn capabilities_checks_full_manifest_network_profile_and_genesis() { + let fixture = Fixture::new(); + let expected = fixture.capabilities(); + let server = MockServer::start(vec![Reply::json("GET", "capabilities", expected.clone())]); + let args = [ + "policy".into(), + "capabilities".into(), + "--network".into(), + "testnet4".into(), + "--service".into(), + server.origin.clone(), + ]; + assert_eq!(success(invoke(&args)), expected); + server.finish(); + + for (field, replacement, expected_error) in [ + ("profile", json!("legacy"), "profile mismatch"), + ("profile_hash", json!("00"), "fingerprint mismatch"), + ("network", json!("testnet"), "network mismatch"), + ("genesis_hash", json!("00"), "genesis hash mismatch"), + ( + "provenance", + json!("confirmed"), + "verification provenance mismatch", + ), + ] { + let mut caps = fixture.capabilities(); + caps[field] = replacement; + let server = MockServer::start(vec![Reply::json("GET", "capabilities", caps)]); + let mut args = args.clone(); + set_arg(&mut args, "--service", &server.origin); + failure(invoke(&args), expected_error); + server.finish(); + } + for (field, child, value, expected_error) in [ + ("manifest", "status", json!("wrong"), "manifest differs"), + ( + "manifest", + "unknown_semantic_field", + json!(true), + "manifest differs", + ), + ("limits", "max_inputs", json!(9), "limits differ"), + ] { + let mut caps = fixture.capabilities(); + caps[field][child] = value; + let server = MockServer::start(vec![Reply::json("GET", "capabilities", caps)]); + let mut args = args.clone(); + set_arg(&mut args, "--service", &server.origin); + failure(invoke(&args), expected_error); + server.finish(); + } +} + +#[test] +fn tweak_is_profile_bound_and_validated_locally_before_address_generation() { + let fixture = Fixture::new(); + let prepared = fixture.prepared(); + let context = prepared.context(); + let expected = json!({ + "profile": PROFILE_NAME, + "profile_hash": hex::encode(profile_hash()), + "network": "testnet4", + "genesis_hash": hex::encode(genesis_hash(Network::Testnet4).unwrap()), + "cmr": hex::encode(context.cmr()), + "signing_commitment": hex::encode(context.signing_commitment()), + "public_key_hex": context.service_pubkey().to_string(), + "base_public_key_hex": fixture.base.x_only_public_key().0.to_string(), + }); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "tweak", expected.clone()), + ]); + let args = [ + "policy".into(), + "tweak".into(), + "--network".into(), + "testnet4".into(), + "--service".into(), + server.origin.clone(), + "--program".into(), + BASE64_STANDARD.encode(&fixture.program), + "--redeem-script-hex".into(), + hex::encode(&fixture.script), + "--base-public-key".into(), + fixture.base.x_only_public_key().0.to_string(), + ]; + assert_eq!(success(invoke(&args)), expected); + let requests = server.finish(); + let body: Value = serde_json::from_slice(&requests[1].1).unwrap(); + assert_eq!( + body, + json!({ + "profile_hash": hex::encode(profile_hash()), + "network": "testnet4", + "program": BASE64_STANDARD.encode(&fixture.program), + "redeem_script_hex": hex::encode(&fixture.script), + }) + ); +} + +#[test] +fn prepare_matches_local_digests_and_uses_the_new_http_field_names() { + let fixture = Fixture::new(); + let expected = json!({ "context": fixture.prepared().summary() }); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "prepare", expected.clone()), + ]); + assert_eq!( + success(invoke(&remote_args(&fixture, "prepare", &server.origin))), + expected + ); + let requests = server.finish(); + let body: Value = serde_json::from_slice(&requests[1].1).unwrap(); + assert_eq!(body["psbt_hex"], hex::encode(fixture.psbt.serialize())); + assert_eq!(body["input_index"], 0); + assert_eq!(body["program"], BASE64_STANDARD.encode(&fixture.program)); + assert_eq!(body["redeem_script_hex"], "52"); + assert_eq!(body["user_leaf_hash_hex"], hex::encode([3; 32])); + assert_eq!(body["user_pubkey"], fixture.user.public_key().to_string()); + assert!(body.get("witness").is_none()); + assert!(body.get("psbt").is_none()); + assert!(body.get("user_leaf_hash").is_none()); +} + +#[test] +fn remote_digest_or_signer_key_mismatches_are_not_accepted() { + let fixture = Fixture::new(); + let mut context = serde_json::to_value(fixture.prepared().summary()).unwrap(); + context["policy_transaction_approval_hash"] = json!(hex::encode([9; 32])); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "prepare", json!({ "context": context })), + ]); + failure( + invoke(&remote_args(&fixture, "prepare", &server.origin)), + "differ from the locally verified context", + ); + server.finish(); + + let mut caps = fixture.capabilities(); + caps["base_public_key_hex"] = json!(fixture.user.x_only_public_key().0.to_string()); + let server = MockServer::start(vec![Reply::json("GET", "capabilities", caps)]); + failure( + invoke(&remote_args(&fixture, "prepare", &server.origin)), + "base public key does not match", + ); + server.finish(); +} + +#[test] +fn evaluate_executes_a_real_subcommand_and_rejects_success_shaped_denial() { + let fixture = Fixture::new(); + for accepted in [true, false] { + let expected = json!({ + "accepted": accepted, + "context": fixture.prepared().summary(), + "execution_cost_milliweight": fixture.prepared().evaluate_default(&[]).unwrap().cost_milliweight, + }); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "evaluate", expected.clone()), + ]); + let result = invoke(&remote_args(&fixture, "evaluate", &server.origin)); + if accepted { + assert_eq!(success(result), expected); + } else { + failure(result, "did not accept the policy"); + } + let requests = server.finish(); + let body: Value = serde_json::from_slice(&requests[1].1).unwrap(); + assert_eq!(body["witness"], ""); + } +} + +#[test] +fn sign_verifies_the_actual_cooperative_signature_and_psbt_metadata() { + let fixture = Fixture::new(); + let expected = fixture.sign_response(false); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "sign", expected.clone()), + ]); + assert_eq!( + success(invoke(&remote_args(&fixture, "sign", &server.origin))), + expected + ); + server.finish(); + for (field, value, expected_error) in [ + ( + "signature_hex", + json!(hex::encode([0; 64])), + "invalid cooperative signature", + ), + ("input_index", json!(1), "unexpected input index"), + ( + "execution_cost_milliweight", + json!(10_000_001), + "above the policy execution limit", + ), + ( + "execution_cost_milliweight", + json!(expected["execution_cost_milliweight"].as_u64().unwrap() + 1), + "differs from local policy evaluation", + ), + ] { + let mut invalid = fixture.sign_response(false); + invalid[field] = value; + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "sign", invalid), + ]); + failure( + invoke(&remote_args(&fixture, "sign", &server.origin)), + expected_error, + ); + server.finish(); + } + let mut invalid = fixture.sign_response(false); + let mut psbt = + Psbt::deserialize(&hex::decode(invalid["psbt_hex"].as_str().unwrap()).unwrap()).unwrap(); + psbt.unsigned_tx.output[0].value = Amount::from_sat(198_999); + invalid["psbt_hex"] = json!(hex::encode(psbt.serialize())); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "sign", invalid), + ]); + failure( + invoke(&remote_args(&fixture, "sign", &server.origin)), + "Service changed the transaction", + ); + server.finish(); +} + +#[test] +fn service_errors_have_stable_json_and_nonzero_status_without_signing_retries() { + let fixture = Fixture::new(); + for status in [400, 413, 422, 503, 504] { + let mut denied = Reply::json("POST", "sign", json!({ "error": "explicit denial" })); + denied.status = status; + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + denied, + ]); + let error = failure( + invoke(&remote_args(&fixture, "sign", &server.origin)), + &format!("HTTP {status}"), + ); + assert!(error["error"].as_str().unwrap().contains("explicit denial")); + assert_eq!(server.finish().len(), 2); + } +} + +#[test] +fn malformed_success_responses_and_unexpected_redirects_fail_explicitly() { + let fixture = Fixture::new(); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "prepare", json!({ "accepted": true })), + ]); + failure( + invoke(&remote_args(&fixture, "prepare", &server.origin)), + "Invalid policy service response JSON/shape", + ); + server.finish(); + + let mut redirect = Reply::json("GET", "capabilities", json!({})); + redirect.status = 302; + let server = MockServer::start(vec![redirect]); + failure( + invoke(&remote_args(&fixture, "prepare", &server.origin)), + "HTTP 302", + ); + server.finish(); +} + +#[test] +fn policy_service_signature_flows_through_policy_user_sign_and_verified_finalize() { + let mut fixture = Fixture::new(); + fixture.psbt.unsigned_tx.input.truncate(1); + fixture.psbt.inputs.truncate(1); + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(99_000); + let service_response = fixture.sign_response(false); + let server = MockServer::start(vec![ + Reply::json("GET", "capabilities", fixture.capabilities()), + Reply::json("POST", "sign", service_response), + ]); + let signed = success(invoke(&remote_args(&fixture, "sign", &server.origin))); + server.finish(); + let key_file = common::SecretFile::new([2; 32]); + let mut args = fixture.args("user-sign"); + set_arg(&mut args, "--psbt", signed["psbt_hex"].as_str().unwrap()); + args.extend([ + "--secret-key-file".into(), + key_file.0.to_str().unwrap().into(), + ]); + let user_signed = success(invoke(&args)); + let mut args = fixture.args("finalize"); + set_arg( + &mut args, + "--psbt", + user_signed["psbt_hex"].as_str().unwrap(), + ); + let finalized = success(invoke(&args)); + assert_eq!(finalized["finalized"], true); + let tx: Transaction = btc::consensus::deserialize( + &hex::decode(finalized["transaction_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + assert_eq!(tx.input[0].witness.len(), 4); + assert_eq!(tx.output, fixture.psbt.unsigned_tx.output); +} + +#[test] +fn policy_funding_address_matches_verified_context_and_defaults_to_testnet4() { + let fixture = Fixture::new(); + let args = vec![ + "policy".into(), + "address".into(), + "--program".into(), + BASE64_STANDARD.encode(&fixture.program), + "--redeem-script-hex".into(), + hex::encode(&fixture.script), + "--base-public-key".into(), + fixture.base.x_only_public_key().0.to_string(), + "--user-pubkey".into(), + fixture.user.x_only_public_key().0.to_string(), + "--user-leaf-hash-hex".into(), + hex::encode(fixture.recovery.to_byte_array()), + ]; + let address = success(invoke(&args)); + assert_eq!(address["network"], "testnet4"); + assert_eq!(address["profile_hash"], hex::encode(profile_hash())); + assert_eq!( + address["script_pubkey_hex"], + hex::encode( + fixture.psbt.inputs[0] + .witness_utxo + .as_ref() + .unwrap() + .script_pubkey + .as_bytes() + ), + ); + let mut other_network = args.clone(); + other_network.extend(["--network".into(), "testnet".into()]); + let different = success(invoke(&other_network)); + assert_ne!(different["script_pubkey_hex"], address["script_pubkey_hex"]); + let mut bad_profile = args; + bad_profile.extend(["--profile".into(), "legacy".into()]); + failure(invoke(&bad_profile), "Unsupported policy profile"); +} + +#[test] +fn policy_csv_leaf_accepts_xonly_keys_and_requires_a_real_delay() { + let fixture = Fixture::new(); + let args = vec![ + "policy".into(), + "build-csv-leaf".into(), + "--user-pubkey".into(), + fixture.user.x_only_public_key().0.to_string(), + "--timelock".into(), + "144".into(), + ]; + let recovery = success(invoke(&args)); + let script = ScriptBuf::from_hex(recovery["script"].as_str().unwrap()).unwrap(); + assert_eq!( + recovery["leaf_hash"], + hex::encode(btc::TapLeafHash::from_script(&script, LeafVersion::TapScript).to_byte_array()), + ); + let mut bad_delay = args; + set_arg(&mut bad_delay, "--timelock", "0"); + failure( + invoke(&bad_delay), + "Recovery timelock must be at least one block", + ); +} + +#[test] +fn user_sign_rejects_wrong_key_and_unverified_or_different_contracts() { + let mut fixture = Fixture::new(); + let wrong_key = common::SecretFile::new([4; 32]); + let mut args = fixture.args("user-sign"); + args.extend([ + "--secret-key-file".into(), + wrong_key.0.to_str().unwrap().into(), + ]); + failure(invoke(&args), "User secret key does not match"); + for input in &mut fixture.psbt.inputs { + input.non_witness_utxo = None; + } + set_arg(&mut args, "--psbt", &hex::encode(fixture.psbt.serialize())); + failure( + invoke(&args), + "missing non_witness_utxo full parent evidence", + ); + + let fixture = Fixture::new(); + let mut args = fixture.args("user-sign"); + args.extend([ + "--secret-key-file".into(), + wrong_key.0.to_str().unwrap().into(), + ]); + set_arg(&mut args, "--redeem-script-hex", "53"); + failure(invoke(&args), "Failed to construct verified policy context"); +} + +#[test] +fn policy_finalize_requires_valid_signatures_and_committed_leaf_metadata() { + let mut fixture = Fixture::new(); + fixture.psbt.unsigned_tx.input.truncate(1); + fixture.psbt.inputs.truncate(1); + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(99_000); + let mut args = fixture.args("finalize"); + failure(invoke(&args), "no committed canonical multisig leaf"); + let signed = fixture.sign_response(true); + let signed_psbt = + Psbt::deserialize(&hex::decode(signed["psbt_hex"].as_str().unwrap()).unwrap()).unwrap(); + set_arg(&mut args, "--psbt", signed["psbt_hex"].as_str().unwrap()); + assert_eq!(success(invoke(&args))["finalized"], true); + + let prepared = fixture.prepared(); + let signature_key = ( + prepared.context().user_pubkey(), + prepared.context().cooperative_leaf_hash(), + ); + let mut missing = signed_psbt.clone(); + missing.inputs[0].tap_script_sigs.remove(&signature_key); + set_arg(&mut args, "--psbt", &hex::encode(missing.serialize())); + failure(invoke(&args), "missing tap_script_sig"); + + let mut invalid = signed_psbt.clone(); + invalid.inputs[0] + .tap_script_sigs + .get_mut(&signature_key) + .unwrap() + .signature = Secp256k1::new().sign_schnorr(&Message::from_digest([0; 32]), &fixture.user); + set_arg(&mut args, "--psbt", &hex::encode(invalid.serialize())); + failure(invoke(&args), "invalid tap_script_sig"); + + let mut wrong_leaf = signed_psbt.clone(); + wrong_leaf.inputs[0] + .tap_scripts + .values_mut() + .next() + .unwrap() + .0 = ScriptBuf::new(); + set_arg(&mut args, "--psbt", &hex::encode(wrong_leaf.serialize())); + failure(invoke(&args), "no committed canonical multisig leaf"); + + let mut wrong_sighash = signed_psbt.clone(); + wrong_sighash.inputs[0] + .tap_script_sigs + .get_mut(&signature_key) + .unwrap() + .sighash_type = btc::TapSighashType::All; + set_arg(&mut args, "--psbt", &hex::encode(wrong_sighash.serialize())); + failure(invoke(&args), "Failed to construct verified policy context"); + + let mut missing_parent = signed_psbt; + missing_parent.inputs[0].non_witness_utxo = None; + set_arg( + &mut args, + "--psbt", + &hex::encode(missing_parent.serialize()), + ); + failure( + invoke(&args), + "missing non_witness_utxo full parent evidence", + ); +} + +#[test] +fn policy_user_sign_and_finalize_verify_every_input() { + let fixture = Fixture::new(); + let mut psbt = fixture.psbt.clone(); + let secp = Secp256k1::new(); + let commitment = fixture.prepared().context().signing_commitment(); + let service_key = fixture + .base + .tap_tweak(&secp, Some(TapNodeHash::from_byte_array(commitment))) + .to_keypair(); + for index in 0..psbt.inputs.len() { + let prepared = PreparedPolicy::new( + &fixture.program, + &fixture.psbt, + index, + &fixture.script, + Network::Testnet4, + fixture.base.x_only_public_key().0, + fixture.user.x_only_public_key().0, + fixture.recovery, + ) + .unwrap(); + let context = prepared.context(); + psbt.inputs[index].tap_script_sigs.insert( + (context.service_pubkey(), context.cooperative_leaf_hash()), + btc::taproot::Signature { + signature: secp.sign_schnorr( + &Message::from_digest(context.cooperative_sighash_default()), + &service_key, + ), + sighash_type: btc::TapSighashType::Default, + }, + ); + psbt.inputs[index].tap_scripts.insert( + context.control_block().clone(), + (context.cooperative_script().clone(), LeafVersion::TapScript), + ); + } + let key_file = common::SecretFile::new([2; 32]); + let mut hex_psbt = hex::encode(psbt.serialize()); + for index in 0..2 { + let mut args = fixture.args("user-sign"); + set_arg(&mut args, "--input-index", &index.to_string()); + set_arg(&mut args, "--psbt", &hex_psbt); + args.extend([ + "--secret-key-file".into(), + key_file.0.to_str().unwrap().into(), + ]); + hex_psbt = success(invoke(&args))["psbt_hex"].as_str().unwrap().into(); + if index == 0 { + let mut finalize = fixture.args("finalize"); + set_arg(&mut finalize, "--psbt", &hex_psbt); + failure(invoke(&finalize), "Input 1 missing tap_script_sig"); + } + } + let mut args = fixture.args("finalize"); + set_arg(&mut args, "--psbt", &hex_psbt); + let output = success(invoke(&args)); + assert_eq!(output["inputs"], 2); + let tx: Transaction = btc::consensus::deserialize( + &hex::decode(output["transaction_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + assert!(tx.input.iter().all(|input| input.witness.len() == 4)); + assert_eq!(tx.output, fixture.psbt.unsigned_tx.output); +} + +#[test] +fn configured_esplora_fetches_full_raw_parent_once_and_verifies_before_service_io() { + let mut fixture = Fixture::new(); + let parent = fixture.psbt.inputs[0].non_witness_utxo.clone().unwrap(); + let expected = fixture.prepared().summary(); + for input in &mut fixture.psbt.inputs { + input.non_witness_utxo = None; + input.witness_utxo = None; + } + let explorer = MockServer::start(vec![Reply { + method: "GET", + path: format!("/tx/{}/raw", parent.compute_txid()), + status: 200, + body: serialize(&parent), + }]); + let mut args = fixture.args("approval-hash"); + args.extend(["--esplora-url".into(), explorer.origin.clone()]); + assert_eq!( + success(invoke(&args))["context"], + serde_json::to_value(expected).unwrap() + ); + assert_eq!(explorer.finish().len(), 1); + + let mut wrong_parent = parent; + wrong_parent.output[0].value = Amount::from_sat(1); + let explorer = MockServer::start(vec![Reply { + method: "GET", + path: format!( + "/tx/{}/raw", + fixture.psbt.unsigned_tx.input[0].previous_output.txid + ), + status: 200, + body: serialize(&wrong_parent), + }]); + set_arg(&mut args, "--esplora-url", &explorer.origin); + failure(invoke(&args), "parent transaction txid mismatch"); + explorer.finish(); +} diff --git a/cli/tests/policy_templates.rs b/cli/tests/policy_templates.rs new file mode 100644 index 0000000..737bdc4 --- /dev/null +++ b/cli/tests/policy_templates.rs @@ -0,0 +1,627 @@ +mod common; + +use std::{collections::BTreeMap, process::Command, time::Duration}; + +use serde::Deserialize; +use simplicity::{ + base64::prelude::{BASE64_STANDARD, Engine as _}, + bitcoin::{ + self as btc, Amount, Network, OutPoint, Psbt, ScriptBuf, Sequence, TapNodeHash, + Transaction, TxIn, TxOut, XOnlyPublicKey, + hashes::{Hash, sha256}, + secp256k1::{Keypair, Message, Secp256k1, SecretKey, schnorr::Signature}, + }, +}; +use simplicity_unchained_core::policy::{PolicyError, PreparedPolicy, policy_cmr}; +use simplicity_unchained_policy::{PROFILE_NAME, cooperative_output, profile_hash}; + +#[derive(Deserialize)] +struct Bundle { + profile: String, + profile_hash: String, + templates: BTreeMap, +} + +#[derive(Deserialize)] +struct Template { + source_sha256: String, + program_base64: String, + cmr_hex: String, + profile_hash: String, + duplicate_keys_program_base64: Option, + duplicate_keys_cmr_hex: Option, +} + +fn templates() -> Bundle { + serde_json::from_str(include_str!("../../examples/policy/compiled.json")).unwrap() +} + +fn program(name: &str) -> Vec { + BASE64_STANDARD + .decode(&templates().templates[name].program_base64) + .unwrap() +} + +fn scalar_key(value: u8) -> Keypair { + let mut secret = [0; 32]; + secret[31] = value; + Keypair::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&secret).unwrap()) +} + +#[derive(Clone)] +enum Destination { + Return, + Script(ScriptBuf), +} + +fn recipient() -> Destination { + Destination::Script( + ScriptBuf::from_hex("00141111111111111111111111111111111111111111").unwrap(), + ) +} + +struct Fixture { + program: Vec, + psbt: Psbt, + script: Vec, + base: XOnlyPublicKey, +} + +impl Fixture { + fn new(program: Vec, inputs: usize, outputs: &[(u64, Destination)]) -> Self { + Self::with_base( + program, + inputs, + outputs, + scalar_key(41).x_only_public_key().0, + ) + } + + fn with_base( + program: Vec, + inputs: usize, + outputs: &[(u64, Destination)], + base: XOnlyPublicKey, + ) -> Self { + let script = vec![0x52]; + let cmr = policy_cmr(&program).unwrap(); + let output = cooperative_output( + base, + cmr.to_byte_array(), + &script, + Network::Testnet4, + scalar_key(42).x_only_public_key().0, + TapNodeHash::from_byte_array([43; 32]), + ) + .unwrap(); + let parent = Transaction { + version: btc::transaction::Version::TWO, + lock_time: btc::absolute::LockTime::ZERO, + input: vec![TxIn::default()], + output: vec![ + TxOut { + value: Amount::from_sat(100_000), + script_pubkey: output.script_pubkey.clone(), + }; + inputs + ], + }; + let tx = Transaction { + version: btc::transaction::Version::TWO, + lock_time: btc::absolute::LockTime::from_consensus(1000), + input: (0..inputs) + .map(|vout| TxIn { + previous_output: OutPoint::new(parent.compute_txid(), vout as u32), + sequence: Sequence::from_height(16), + ..TxIn::default() + }) + .collect(), + output: outputs + .iter() + .map(|(value, destination)| TxOut { + value: Amount::from_sat(*value), + script_pubkey: match destination { + Destination::Return => output.script_pubkey.clone(), + Destination::Script(script) => script.clone(), + }, + }) + .collect(), + }; + let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); + for (index, input) in psbt.inputs.iter_mut().enumerate() { + input.non_witness_utxo = Some(parent.clone()); + input.witness_utxo = Some(parent.output[index].clone()); + } + Self { + program, + psbt, + script, + base, + } + } + + fn prepare(&self, index: usize) -> Result { + PreparedPolicy::new( + &self.program, + &self.psbt, + index, + &self.script, + Network::Testnet4, + self.base, + scalar_key(42).x_only_public_key().0, + TapNodeHash::from_byte_array([43; 32]), + ) + } + + fn update_parent(&mut self, parent: Transaction) { + for (txin, input) in self + .psbt + .unsigned_tx + .input + .iter_mut() + .zip(&mut self.psbt.inputs) + { + txin.previous_output.txid = parent.compute_txid(); + input.witness_utxo = Some(parent.output[txin.previous_output.vout as usize].clone()); + input.non_witness_utxo = Some(parent.clone()); + } + } +} + +fn signature(digest: [u8; 32], key: u8) -> Signature { + Secp256k1::new().sign_schnorr_no_aux_rand(&Message::from_digest(digest), &scalar_key(key)) +} + +// Witness encoding is compact sums, not the padded Option frames used by VM jets. +fn signature_witness(signatures: &[Option]) -> Vec { + let mut bits = Vec::new(); + for signature in signatures { + bits.push(signature.is_some()); + if let Some(signature) = signature { + for byte in signature.as_ref() { + bits.extend((0..8).map(|offset| byte & (1 << (7 - offset)) != 0)); + } + } + } + let mut bytes = vec![0; bits.len().div_ceil(8)]; + for (index, bit) in bits.into_iter().enumerate() { + bytes[index / 8] |= u8::from(bit) << (7 - index % 8); + } + bytes +} + +fn approval_witness(prepared: &PreparedPolicy, input_scoped: bool) -> Vec { + let digest = if input_scoped { + prepared.context().policy_approval_hash() + } else { + prepared.context().policy_transaction_approval_hash() + }; + signature_witness(&[Some(signature(digest, 1)), Some(signature(digest, 2)), None]) +} + +fn source_witness(prepared: &PreparedPolicy) -> Vec { + let context = prepared.context(); + let tag = sha256::Hash::hash(b"SimplicityUnchained/PolicySourceOutpoint/v1").to_byte_array(); + let mut signatures = Vec::new(); + for input in &context.tx().input { + let mut preimage = Vec::new(); + preimage.extend_from_slice(&tag); + preimage.extend_from_slice(&tag); + preimage.extend_from_slice(&context.signing_commitment()); + preimage.extend_from_slice(&input.previous_output.txid.to_byte_array()); + preimage.extend_from_slice(&input.previous_output.vout.to_be_bytes()); + let digest = sha256::Hash::hash(&preimage).to_byte_array(); + signatures.push(Some(signature(digest, 1))); + } + signatures.resize(8, None); + signature_witness(&signatures) +} + +fn rejects(prepared: &PreparedPolicy, witness: &[u8]) { + assert!( + matches!( + prepared.evaluate_default(witness), + Err(PolicyError::PolicyRejected(_)) + ), + "Expected policy rejection, got {:?}", + prepared.evaluate_default(witness) + ); +} + +#[test] +fn compiled_templates_pin_the_reviewed_source_cmr_and_registry() { + let bundle = templates(); + assert_eq!(bundle.profile, PROFILE_NAME); + assert_eq!(bundle.profile_hash, hex::encode(profile_hash())); + let sources = [ + ( + "recipient_allowlist.simf", + include_str!("../../examples/policy/recipient_allowlist.simf"), + ), + ( + "capped_debit.simf", + include_str!("../../examples/policy/capped_debit.simf"), + ), + ( + "approvals_2_of_3.simf", + include_str!("../../examples/policy/approvals_2_of_3.simf"), + ), + ( + "approvals_2_of_3_per_input.simf", + include_str!("../../examples/policy/approvals_2_of_3_per_input.simf"), + ), + ( + "constrained_source.simf", + include_str!("../../examples/policy/constrained_source.simf"), + ), + ]; + assert_eq!(bundle.templates.len(), sources.len()); + for (name, source) in sources { + let template = &bundle.templates[name]; + assert_eq!(template.profile_hash, bundle.profile_hash); + assert_eq!( + template.source_sha256, + hex::encode(sha256::Hash::hash(source.as_bytes()).to_byte_array()), + "Stale compiled source for {name}" + ); + assert_eq!( + template.cmr_hex, + policy_cmr(&program(name)).unwrap().to_string(), + "Stale compiled CMR for {name}" + ); + if let Some(duplicate) = &template.duplicate_keys_program_base64 { + let bytes = BASE64_STANDARD.decode(duplicate).unwrap(); + assert_eq!( + template.duplicate_keys_cmr_hex.as_deref(), + Some(policy_cmr(&bytes).unwrap().to_string().as_str()), + "Stale duplicate-key mutation CMR for {name}" + ); + } + } +} + +#[test] +fn allowlist_checks_every_output_including_zero_value_and_last_slot() { + let bytes = program("recipient_allowlist.simf"); + for count in [1, 8] { + let mut fixture = Fixture::new(bytes.clone(), 1, &vec![(10_000, recipient()); count]); + fixture.prepare(0).unwrap().evaluate_default(&[]).unwrap(); + let last = fixture.psbt.unsigned_tx.output.last_mut().unwrap(); + last.value = Amount::ZERO; + let mut script = last.script_pubkey.to_bytes(); + *script.last_mut().unwrap() ^= 1; + last.script_pubkey = ScriptBuf::from_bytes(script); + rejects(&fixture.prepare(0).unwrap(), &[]); + } + let returning = Fixture::new(bytes.clone(), 1, &[(99_000, Destination::Return)]); + returning.prepare(0).unwrap().evaluate_default(&[]).unwrap(); + let excess = Fixture::new(bytes, 1, &vec![(1_000, recipient()); 9]); + assert!(excess.prepare(0).is_err()); +} + +#[test] +fn debit_and_fee_caps_use_all_eight_inputs_and_inclusive_satoshi_boundaries() { + let bytes = program("capped_debit.simf"); + let mut outputs = vec![(14_000, recipient()); 7]; + outputs.push((700_000, Destination::Return)); + let mut fixture = Fixture::new(bytes.clone(), 8, &outputs); + let prepared = fixture.prepare(7).unwrap(); + assert_eq!(prepared.summary().fee, 2_000); + assert_eq!(prepared.summary().debit, 100_000); + prepared.evaluate_default(&[]).unwrap(); + + // Same total outputs/fee, but one satoshi more leaves the common script. + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(14_001); + fixture.psbt.unsigned_tx.output[7].value = Amount::from_sat(699_999); + rejects(&fixture.prepare(7).unwrap(), &[]); + + // Same debit, one satoshi above the absolute fee limit. + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(13_999); + fixture.psbt.unsigned_tx.output[7].value = Amount::from_sat(700_000); + rejects(&fixture.prepare(7).unwrap(), &[]); + + let aggregate = Fixture::new( + bytes, + 2, + &[(99_000, recipient()), (99_000, Destination::Return)], + ); + assert_eq!(aggregate.prepare(0).unwrap().summary().debit, 101_000); + rejects(&aggregate.prepare(0).unwrap(), &[]); +} + +#[test] +fn approval_threshold_requires_distinct_keys_and_checks_every_present_signature() { + let fixture = Fixture::new( + program("approvals_2_of_3.simf"), + 2, + &[(199_000, Destination::Return)], + ); + let prepared = fixture.prepare(0).unwrap(); + let digest = prepared.context().policy_transaction_approval_hash(); + let [sig1, sig2, sig3] = [1, 2, 3].map(|key| signature(digest, key)); + prepared + .evaluate_default(&signature_witness(&[Some(sig1), Some(sig2), None])) + .unwrap(); + prepared + .evaluate_default(&signature_witness(&[Some(sig1), Some(sig2), Some(sig3)])) + .unwrap(); + rejects(&prepared, &signature_witness(&[Some(sig1), None, None])); + rejects( + &prepared, + &signature_witness(&[Some(sig1), Some(sig1), None]), + ); + rejects( + &prepared, + &signature_witness(&[Some(sig1), Some(sig2), Some(sig1)]), + ); +} + +#[test] +fn duplicate_committed_keys_cannot_inflate_either_approval_threshold() { + for name in ["approvals_2_of_3.simf", "approvals_2_of_3_per_input.simf"] { + let bundle = templates(); + let duplicate_program = BASE64_STANDARD + .decode( + bundle.templates[name] + .duplicate_keys_program_base64 + .as_ref() + .expect("Compiler must include the duplicate-key mutation fixture"), + ) + .unwrap(); + let fixture = Fixture::new(duplicate_program, 1, &[(99_000, Destination::Return)]); + let prepared = fixture.prepare(0).unwrap(); + let digest = if name.contains("per_input") { + prepared.context().policy_approval_hash() + } else { + prepared.context().policy_transaction_approval_hash() + }; + let repeated = signature(digest, 1); + rejects( + &prepared, + &signature_witness(&[Some(repeated), Some(repeated), None]), + ); + } +} + +#[test] +fn transaction_and_input_approval_scopes_reject_relevant_mutations() { + for (name, input_scoped) in [ + ("approvals_2_of_3.simf", false), + ("approvals_2_of_3_per_input.simf", true), + ] { + let mut fixture = Fixture::new(program(name), 2, &[(199_000, Destination::Return)]); + let first = fixture.prepare(0).unwrap(); + let witness = approval_witness(&first, input_scoped); + first.evaluate_default(&witness).unwrap(); + let second = fixture.prepare(1).unwrap(); + if input_scoped { + rejects(&second, &witness); + second + .evaluate_default(&approval_witness(&second, true)) + .unwrap(); + } else { + second.evaluate_default(&witness).unwrap(); + } + let original = fixture.psbt.clone(); + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(198_999); + rejects(&fixture.prepare(0).unwrap(), &witness); + fixture.psbt = original.clone(); + let mut bytes = fixture.psbt.unsigned_tx.output[0].script_pubkey.to_bytes(); + *bytes.last_mut().unwrap() ^= 1; + fixture.psbt.unsigned_tx.output[0].script_pubkey = ScriptBuf::from_bytes(bytes); + rejects(&fixture.prepare(0).unwrap(), &witness); + fixture.psbt = original.clone(); + fixture.psbt.unsigned_tx.input[1].sequence = Sequence::from_height(17); + rejects(&fixture.prepare(0).unwrap(), &witness); + fixture.psbt = original.clone(); + fixture.psbt.unsigned_tx.lock_time = btc::absolute::LockTime::from_consensus(1001); + rejects(&fixture.prepare(0).unwrap(), &witness); + fixture.psbt = original; + let mut parent = fixture.psbt.inputs[0].non_witness_utxo.clone().unwrap(); + parent.output[1].value = Amount::from_sat(100_001); + fixture.update_parent(parent); + rejects(&fixture.prepare(0).unwrap(), &witness); + } +} + +#[test] +fn source_constraints_check_all_inputs_csv_cltv_script_bytes_and_authorization() { + let one_input = Fixture::new( + program("constrained_source.simf"), + 1, + &[(99_000, Destination::Return)], + ); + let prepared = one_input.prepare(0).unwrap(); + prepared + .evaluate_default(&source_witness(&prepared)) + .unwrap(); + let mut fixture = Fixture::new( + program("constrained_source.simf"), + 8, + &vec![(99_000, Destination::Return); 8], + ); + let prepared = fixture.prepare(0).unwrap(); + let witness = source_witness(&prepared); + prepared.evaluate_default(&witness).unwrap(); + fixture + .prepare(7) + .unwrap() + .evaluate_default(&witness) + .unwrap(); + let original = fixture.psbt.clone(); + for sequence in [Sequence::MAX, Sequence::from_height(15)] { + fixture.psbt.unsigned_tx.input[7].sequence = sequence; + rejects(&fixture.prepare(0).unwrap(), &witness); + } + fixture.psbt = original.clone(); + for lock_time in [999, 500_000_000] { + fixture.psbt.unsigned_tx.lock_time = btc::absolute::LockTime::from_consensus(lock_time); + rejects(&fixture.prepare(0).unwrap(), &witness); + } + fixture.psbt = original.clone(); + let mut script = fixture.psbt.unsigned_tx.output[7].script_pubkey.to_bytes(); + script[0] = 0x52; + fixture.psbt.unsigned_tx.output[7].script_pubkey = ScriptBuf::from_bytes(script); + rejects(&fixture.prepare(0).unwrap(), &witness); + fixture.psbt = original.clone(); + let mut damaged = witness.clone(); + damaged[1] ^= 1; + rejects(&fixture.prepare(0).unwrap(), &damaged); + + // Even an authenticated source cannot exceed the committed vout restriction. + fixture.psbt = original; + let mut parent = fixture.psbt.inputs[0].non_witness_utxo.clone().unwrap(); + parent.output.push(parent.output[0].clone()); + fixture.psbt.unsigned_tx.input[7].previous_output.vout = 8; + fixture.update_parent(parent); + let prepared = fixture.prepare(0).unwrap(); + rejects(&prepared, &source_witness(&prepared)); +} + +fn live_cli(args: &[String]) -> serde_json::Value { + let output = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(args) + .output() + .unwrap(); + assert!( + output.status.success(), + "stdout: {}\nstderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + serde_json::from_slice(&output.stdout).unwrap() +} + +#[test] +#[ignore = "requires an explicitly configured unfunded testnet4 policy service"] +fn live_cli_compiled_policy_prepare_approve_cosign_and_verified_finalize() { + let service = std::env::var("SIMPLICITY_TEST_SERVICE_URL") + .expect("Set SIMPLICITY_TEST_SERVICE_URL to an unfunded testnet4 policy service"); + let capabilities = live_cli(&[ + "policy".into(), + "capabilities".into(), + "--network".into(), + "testnet4".into(), + "--service".into(), + service.clone(), + ]); + // This local integration fixture deliberately discovers the ephemeral test signer's key. + let base = XOnlyPublicKey::from_slice( + &hex::decode(capabilities["base_public_key_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + let program = program("approvals_2_of_3.simf"); + let tweaked = live_cli(&[ + "policy".into(), + "tweak".into(), + "--network".into(), + "testnet4".into(), + "--program".into(), + BASE64_STANDARD.encode(&program), + "--redeem-script-hex".into(), + "52".into(), + "--base-public-key".into(), + base.to_string(), + "--service".into(), + service.clone(), + ]); + let fixture = Fixture::with_base(program, 2, &[(199_000, Destination::Return)], base); + let prepared = fixture.prepare(0).unwrap(); + assert_eq!( + tweaked["public_key_hex"], + prepared.context().service_pubkey().to_string() + ); + let witness = BASE64_STANDARD.encode(approval_witness(&prepared, false)); + let policy_args = |operation: &str, psbt: &str, index: usize| { + let mut args = vec![ + "policy".into(), + operation.into(), + "--network".into(), + "testnet4".into(), + "--profile-hash".into(), + hex::encode(profile_hash()), + "--program".into(), + BASE64_STANDARD.encode(&fixture.program), + "--redeem-script-hex".into(), + hex::encode(&fixture.script), + "--psbt".into(), + psbt.into(), + "--input-index".into(), + index.to_string(), + "--user-pubkey".into(), + scalar_key(42).x_only_public_key().0.to_string(), + "--user-leaf-hash-hex".into(), + hex::encode([43; 32]), + "--base-public-key".into(), + base.to_string(), + "--offline".into(), + ]; + if matches!(operation, "prepare" | "evaluate" | "sign") { + args.extend(["--service".into(), service.clone()]); + } + if matches!(operation, "evaluate" | "sign") { + args.extend(["--witness".into(), witness.clone()]); + } + args + }; + let mut psbt_hex = hex::encode(fixture.psbt.serialize()); + let local = live_cli(&policy_args("approval-hash", &psbt_hex, 0)); + let remote = live_cli(&policy_args("prepare", &psbt_hex, 0)); + assert_eq!(local, remote); + assert_eq!( + remote["context"], + serde_json::to_value(prepared.summary()).unwrap() + ); + let evaluated = live_cli(&policy_args("evaluate", &psbt_hex, 0)); + assert_eq!(evaluated["accepted"], true); + assert_eq!(evaluated["context"], remote["context"]); + for index in 0..2 { + let signed = live_cli(&policy_args("sign", &psbt_hex, index)); + psbt_hex = signed["psbt_hex"].as_str().unwrap().to_owned(); + } + let mut user_secret = [0; 32]; + user_secret[31] = 42; + let key_file = common::SecretFile::new(user_secret); + for index in 0..2 { + let mut args = policy_args("user-sign", &psbt_hex, index); + args.extend([ + "--secret-key-file".into(), + key_file.0.to_str().unwrap().into(), + ]); + let signed = live_cli(&args); + psbt_hex = signed["psbt_hex"].as_str().unwrap().to_owned(); + } + let finalized = live_cli(&policy_args("finalize", &psbt_hex, 0)); + assert_eq!(finalized["finalized"], true); + let tx: Transaction = btc::consensus::deserialize( + &hex::decode(finalized["transaction_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + assert!(tx.input.iter().all(|input| input.witness.len() == 4)); + assert_eq!(tx.output, fixture.psbt.unsigned_tx.output); + + let mut changed = fixture.psbt.clone(); + changed.unsigned_tx.output[0].value = Amount::from_sat(198_999); + let denied = reqwest::blocking::Client::new() + .post(format!( + "{service}/simplicity-unchained/policy/bitcoin/sign" + )) + .timeout(Duration::from_secs(30)) + .json(&serde_json::json!({ + "profile_hash": hex::encode(profile_hash()), + "network": "testnet4", + "program": BASE64_STANDARD.encode(&fixture.program), + "redeem_script_hex": hex::encode(&fixture.script), + "psbt_hex": hex::encode(changed.serialize()), + "input_index": 0, + "user_pubkey": scalar_key(42).x_only_public_key().0.to_string(), + "user_leaf_hash_hex": hex::encode([43;32]), + "witness": witness, + })) + .send() + .unwrap(); + assert!(!denied.status().is_success()); + let body: serde_json::Value = denied.json().unwrap(); + assert!(body.get("error").is_some()); + assert!(body.get("signature_hex").is_none()); +} diff --git a/cli/tests/public_surface.rs b/cli/tests/public_surface.rs new file mode 100644 index 0000000..31e4d0c --- /dev/null +++ b/cli/tests/public_surface.rs @@ -0,0 +1,74 @@ +use std::process::Command; + +#[test] +fn primary_help_is_policy_only() { + let output = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .arg("--help") + .output() + .unwrap(); + assert!(output.status.success()); + let help = String::from_utf8(output.stdout).unwrap(); + assert!(help.contains("policy")); + assert!(help.contains("keypair")); + assert!(help.contains("liquid-policy")); + for removed in ["btc-tx", "Elements", "address ", "tx "] { + assert!(!help.contains(removed), "{help}"); + } +} + +#[test] +fn legacy_command_workflows_are_unavailable() { + for namespace in ["tx", "btc-tx"] { + for operation in [ + "create", + "sign", + "finalize", + "approval-hash", + "build-csv-leaf", + "spend-user-leaf", + "finalize-user-leaf", + ] { + let output = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args([namespace, operation, "--help"]) + .output() + .unwrap(); + assert!(!output.status.success(), "{namespace} {operation}"); + assert!(output.stdout.is_empty()); + assert!(String::from_utf8_lossy(&output.stderr).contains("unrecognized subcommand")); + } + } + let output = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["address", "multisig", "--help"]) + .output() + .unwrap(); + assert!(!output.status.success()); + let output = Command::new(env!("CARGO_BIN_EXE_simplicity-unchained")) + .args(["btc-tx", "policy", "--help"]) + .output() + .unwrap(); + assert!(!output.status.success()); +} + +#[test] +fn native_demo_help_and_syntax_are_policy_only() { + let script = format!("{}/scripts/demo_btc.sh", env!("CARGO_MANIFEST_DIR")); + assert!( + Command::new("bash") + .args(["-n", &script]) + .status() + .unwrap() + .success() + ); + let output = Command::new("bash") + .args([&script, "--help"]) + .output() + .unwrap(); + assert!(output.status.success()); + let help = String::from_utf8(output.stdout).unwrap(); + assert!(help.contains("policy-v1")); + assert!(help.contains("testnet4")); + assert!(help.contains("never requests a signature")); + for removed in ["btc-tx", "Liquid", "regtest", "/sign/psbt", "/sign/pset"] { + assert!(!help.contains(removed), "{help}"); + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 84cee35..f9725b4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,6 +15,7 @@ thiserror = { workspace = true } hex = { workspace = true } dlopen2 = "0.8.2" jet_plugin_abi = { path = "../jet_plugin_abi" } +simplicity-unchained-policy = { path = "../policy" } [dev-dependencies] script_jets = { path = "../plugin_tests/script_jets" } diff --git a/core/README.md b/core/README.md index 23122db..4872a4a 100644 --- a/core/README.md +++ b/core/README.md @@ -1,114 +1,83 @@ # Simplicity Unchained Core -Contains the fundamental logic for executing Simplicity programs with Bitcoin and Elements environments. +Contains the fundamental logic for executing Simplicity policies with Bitcoin and Elements environments. ## Capabilities -- Execute Simplicity programs with provided data and environment. -- Utilities for interacting with Elements and Simplicity environments. -- Custom execution environment extending the Bitcoin and Elements jet sets with additional jets. +- Execute `bitcoin-policy-v1` policies against verified PSBT context. +- Execute [`liquid-policy-v1`](../policy/LIQUID.md) policies using the portable VM and proof-verified PSET context. +- Provide cryptographic, Taproot and transaction-environment utilities. +- Load [custom jet plugins](src/plugin/README.md) for native callers. The policy profiles use fixed registries. ## How It Works ### The Unchained Model -The core idea is to bind a Simplicity program to a co-signer's key using taproot key tweaking, without requiring Simplicity to be a consensus rule on-chain. The oracle commits to a specific program at address-generation time; it can only produce a valid signature if that exact program executes successfully. +The service commits to a Simplicity policy through its Taproot key tweak, without requiring Simplicity as an on-chain consensus rule. -1. The user writes a Simplicity program encoding the conditions they want enforced (e.g. a signature check, a hash preimage, an oracle assertion). -2. The service computes the **CMR** (Commitment Merkle Root) of the program — a 32-byte hash that uniquely identifies the program's structure and semantics. -3. The service commits to both the CMR and the exact script bytes exposed to script-reading jets, then applies the standard taproot tweak: `tweaked_key = tap_tweak(service_key, signing_commitment(CMR, redeem_script))`. This binds the co-signer's key to that program and its script data. -4. A P2TR address is constructed using the tweaked co-signer key and the user's key (see below). Funds sent to this address can only move with the oracle's cooperation — and the oracle will only cooperate if the Simplicity program passes. +1. The user writes and compiles a policy for the intended profile, choosing its keys and script data before funding. +2. The service computes the program's **CMR** (Commitment Merkle Root). +3. The service derives a cooperative key that commits to the profile fingerprint, network genesis hash, CMR and exact script-data bytes. +4. The user constructs a P2TR address from that key, their own key and a recovery leaf hash. -### Taproot Tree Structure +Keep the base signing key and published profile available for funded outputs. Enforcement is off-chain: someone holding the signing key can bypass policy evaluation. -Each P2TR address produced by Simplicity Unchained has the following structure: +### Taproot Tree Structure ```plaintext P2TR output -├── Internal key: unspendable key +├── Internal key: standard NUMS (unspendable) └── Script tree ├── Leaf 0 — Co-signing leaf (2-of-2 multisig) - │ OP_CHECKSIG + │ OP_CHECKSIG │ OP_CHECKSIGADD OP_2 OP_EQUAL - └── Leaf 1 — User recovery leaf (CSV + user key, hidden from the service) + └── Leaf 1 — User recovery leaf (hidden from the service) ``` -**Internal key**: A provably unspendable key is used as the internal key. - -**Leaf 0 — Co-signing leaf**: A 2-of-2 Schnorr multisig script. Both the co-signer and the user must sign. The co-signer's x-only key embedded here commits to the program and script data, so this leaf is only spendable with signatures from a service that has already executed the matching Simplicity program with the matching script successfully. +**Leaf 0 — Co-signing leaf**: Both the service and user must sign. The service key commits to the policy; the service checks that each input spends the reconstructed output before signing. -**Leaf 1 — User recovery leaf**: A user-controlled script (example is a CSV timelock + user key) that allows the user to recover funds unilaterally after the timelock expires, with no involvement from the service. The service receives only the *hash* of this leaf (`user_leaf_hash`) and never sees the script itself, preserving the user's privacy for the recovery path. +**Leaf 1 — User recovery leaf**: The user chooses a recovery script, such as a CSV timelock and user key, and keeps the full script privately. The service needs only its hash. A valid recovery spend does not require service cooperation or policy evaluation. ### Co-signing Flow -When a user wants to spend from the address: +1. The user prepares a PSBT/PSET with full parent transactions, the committed program and script data, their public key and the recovery hash. +2. The service verifies parent txids, referenced outputs and `witness_utxo` data, then reconstructs every input's policy-bound output. Liquid also requires verified asset/value disclosures and transaction proofs. +3. The user reviews the transaction context and obtains any approvals required by the policy. +4. The service evaluates the witnessed program and, if it passes, returns a cooperative script-path signature for the selected input. +5. The user verifies the service signature, adds their own signature and finalizes after both signatures are present for every input. -1. The user constructs a PSBT/PSET spending transaction and submits it to the service along with the Simplicity program, the same `redeem_script_hex` used at address generation, any required witness data, their public key, and the user leaf hash. -2. The service executes the Simplicity program against the transaction environment. Per-spend authorization requires the program to actually verify a transaction-dependent condition; a hash preimage or signature over a fixed message remains reusable. -3. If execution succeeds, the service re-derives the tweaked co-signer key from the CMR and script, reconstructs the taproot tree (co-signing leaf + hidden user leaf), checks that it matches the input's `witness_utxo.script_pubkey`, computes the script-path sighash for Leaf 0, and returns a Schnorr signature. -4. The user adds their own signature for the same leaf and finalizes the transaction. - -If the Simplicity program fails, the service refuses to sign and the funds remain locked. +Parent verification proves transaction content, not confirmation or unspentness. Both profiles report `chain_status_verified: false`; check live chain status separately. ### Bitcoin Per-Spend Approvals -The Bitcoin extension exposes `tx_approval_hash: ONE -> 2^256`. A policy requiring -an approver's authorization for each spend must verify a BIP-340 signature over -this digest, not a constant message or a requester-supplied digest: +The 55 Bitcoin policy jets expose transaction amounts, output scripts, debit including fees, declared locktime conditions and approval hashes: -```text -approval_hash = jet::tx_approval_hash() -jet::bip_0340_verify((committed_approver_pubkey, approval_hash), witness_signature) -``` +- J53 `policy_approval_hash` binds the transaction context, selected input and its cooperative sighash. +- J54 `policy_transaction_approval_hash` permits one approval across all same-contract inputs of the same transaction. -The approver can calculate the exact digest locally using -`bitcoin_execution_environment` or the CLI's -[`btc-tx approval-hash`](../cli/README.md#bitcoin-transaction-approvals) command. -All inputs must have `witness_utxo` populated. The approver must independently -verify those prevouts and review the transaction before signing the digest. +Use [`policy approval-hash`](../cli/README.md#prepare-approve-and-co-sign) to reconstruct the context locally from full parents and a known base service key. Sign the decoded 32-byte digest directly with BIP-340 and compile the signatures into the policy witness. Changing the approved transaction or context requires fresh approvals. -The version 1 digest is a single SHA256 hash of this concatenation: +A policy must verify a transaction-dependent condition to authorize individual spends. Static hashlocks and fixed-message signatures are replayable; debit and fee caps apply per transaction, not per day. See the [policy templates](../examples/policy/README.md) and [approval preimages](../policy/README.md#commitments-and-approval-preimages). + +### Script Commitment + +For Bitcoin, the cooperative key is derived as follows: ```text -tag_hash || tag_hash || network_id || input_index_be32 || -signing_commitment(CMR, redeem_script) || -SHA256(bitcoin_consensus_encode(unsigned_transaction)) || -SHA256(bitcoin_consensus_encode(prevouts_vector)) +commitment = TaggedHash("SimplicityUnchained/PolicySigningCommitment/v1", + profile_hash || genesis_hash || CMR || SHA256(exact_script_data)) +service_key = BIP341 TapTweak(base_service_key, root = commitment) ``` -`tag_hash = SHA256("SimplicityUnchained/BitcoinApproval/v1")`. -`network_id` is one byte: `0` for `bitcoin`, `1` for `testnet`, `2` for `testnet4`. -The input index is four bytes, big-endian. The prevouts vector contains each -input's `witness_utxo` in transaction order, including its value and script; its -Bitcoin consensus encoding includes the CompactSize vector length. All hashes -here are single SHA256, not SHA256d. The program/script commitment is defined -below. - -Changing outputs, input outpoints, sequences, amounts, scripts, locktime, version, -selected input, network, or program/script commitment changes the digest. PSBT -signatures and other non-transaction metadata are not included. Repeating an -identical approved transaction is allowed; this is not a one-use-token registry. - -This is a dedicated off-chain **approval hash**, not a BIP-341 signing sighash and -not the Elements `sig_all_hash` jet. The service still produces its cooperative -Taproot signature over the actual BIP-341 sighash. The dependency's native -Bitcoin transaction jets are not implemented; programs must use the updated -`CoreExtension` jet table. The new custom jet uses encoding `1111111` and CMR -`SHA256("SimplicityUnchained/Jet/tx_approval_hash/v1")`. - -**Compatibility:** existing jet encodings, CMRs and static-policy addresses are -unchanged. Static Bitcoin policies remain intentionally replayable. Adding this -jet to an existing policy changes its CMR and address; already-funded static -contracts do not gain per-spend approval automatically. Deploy the new policy -and migrate funds using an available original spending path. - -### Script Commitment and Migration - -`signing_commitment` is the 32-byte tagged SHA256 hash -`SHA256(tag_hash || tag_hash || CMR || SHA256(redeem_script))`, where -`tag_hash = SHA256("SimplicityUnchained/SigningCommitment/v1")`. -The original program CMR is unchanged. Omitted, null, and empty scripts all mean -the same zero-length byte string, which is also committed. +The [portable kernel](../policy/README.md) defines the registry, fingerprint, context verification and approval preimages. The [Liquid profile](../policy/LIQUID.md) defines its asset semantics and genesis-bound signing rules. + +## Execution API + +For Bitcoin, use `policy::policy_cmr` to decode a program without its witness, then `policy::prepare_policy` or `PreparedPolicy::new` to validate the transaction and obtain an immutable `context()` and serializable `summary()`. + +The profile accepts version 2, one to eight same-contract inputs and one to eight outputs, with SIGHASH_DEFAULT script-path signing. It rejects duplicate inputs, missing or conflicting parents, overspending and finalized witnesses. + +`evaluate_default(witness)` uses a 10,000,000-milliweight cap. `evaluate(witness, Cost)` and `evaluate_milliweight(witness, u32)` can lower it. Cost is checked before BitMachine allocation and is a static bound, not elapsed time. Errors distinguish decoding, invalid context, policy rejection, execution limits and VM failure. Signing prepares and evaluates again rather than trusting a previous evaluation. ## Licence diff --git a/core/src/jets/liquid_policy.rs b/core/src/jets/liquid_policy.rs new file mode 100644 index 0000000..c0f0d2a --- /dev/null +++ b/core/src/jets/liquid_policy.rs @@ -0,0 +1 @@ +pub use simplicity_unchained_policy::liquid::PolicyJet as LiquidPolicyJet; diff --git a/core/src/jets/mod.rs b/core/src/jets/mod.rs index afb20a4..e00a87b 100644 --- a/core/src/jets/mod.rs +++ b/core/src/jets/mod.rs @@ -3,6 +3,8 @@ pub mod bitcoin; pub mod elements; pub mod environments; pub mod exec; +pub mod liquid_policy; +pub mod policy; #[cfg(test)] pub(crate) mod tests { diff --git a/core/src/jets/policy.rs b/core/src/jets/policy.rs new file mode 100644 index 0000000..0a288d1 --- /dev/null +++ b/core/src/jets/policy.rs @@ -0,0 +1,230 @@ +//! Explicit, built-in-only Bitcoin policy jet namespace. + +use std::io::Write; +use std::str::FromStr; + +use simplicity::ffi::CFrameItem; +use simplicity::ffi::c_jets::frame_ffi::{c_readBit, c_writeBit}; +use simplicity::jet::type_name::TypeName; +use simplicity::jet::{CoreEnv, Jet, JetEnvironment}; +use simplicity::{BitIter, BitWriter, Cmr, Cost, decode}; +use simplicity_unchained_policy::{BitValue, Builtin, Context}; + +use super::base::{AsBaseEnv, BaseJet, JetFn, base_c_jet_ptr}; +use super::bitcoin::CoreExtension; +use super::environments::{BitcoinTxEnv, BitcoinUnchainedEnv, UnchainedEnv}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum PolicyJet { + Legacy(CoreExtension), + Builtin(Builtin), +} + +impl PolicyJet { + pub const ALL: [Self; CoreExtension::ALL.len() + Builtin::ALL.len()] = { + let mut jets = [Self::Legacy(CoreExtension::TxApprovalHash); + CoreExtension::ALL.len() + Builtin::ALL.len()]; + let mut index = 0; + while index < CoreExtension::ALL.len() { + jets[index] = Self::Legacy(CoreExtension::ALL[index]); + index += 1; + } + let mut builtin = 0; + while builtin < Builtin::ALL.len() { + jets[index] = Self::Builtin(Builtin::ALL[builtin]); + index += 1; + builtin += 1; + } + jets + }; + + /// Enumeration position, independent of the legacy/base trampoline table. + pub fn index(self) -> usize { + match self { + Self::Legacy(jet) => jet.index(), + Self::Builtin(jet) => CoreExtension::ALL.len() + usize::from(jet.id()), + } + } +} + +/// A validated immutable policy context and a compatibility view for legacy jets. +pub struct PolicyEnv { + context: Context, + legacy: BitcoinUnchainedEnv, +} + +impl PolicyEnv { + pub fn new(context: Context) -> Self { + let legacy = UnchainedEnv::new( + simplicity::elements::Script::from(context.script_data().to_vec()), + BitcoinTxEnv { + tx: context.tx().clone(), + input_index: context.input_index() as u32, + approval_hash: context.legacy_approval_hash(), + core_env: CoreEnv::new(), + }, + ); + Self { context, legacy } + } + + pub fn context(&self) -> &Context { + &self.context + } +} + +impl AsBaseEnv for PolicyEnv { + fn base_env(&self) -> &CoreEnv { + &self.legacy.env.core_env + } +} + +impl JetEnvironment for PolicyEnv { + type Jet = PolicyJet; + type CJetEnvironment = Self; + + fn c_jet_env(&self) -> &Self { + self + } + + fn c_jet_ptr(jet: &PolicyJet) -> JetFn { + match jet { + PolicyJet::Legacy(CoreExtension::Core(core)) => base_c_jet_ptr(core), + PolicyJet::Legacy(CoreExtension::GetOpcodeFromScript) => legacy_opcode, + PolicyJet::Legacy(CoreExtension::GetPubkeyFromScript) => legacy_pubkey, + PolicyJet::Legacy(CoreExtension::TxApprovalHash) => legacy_approval, + PolicyJet::Builtin(jet) => BUILTIN_FNS[usize::from(jet.id())], + } + } +} + +fn legacy_opcode(dst: &mut CFrameItem, src: CFrameItem, env: &PolicyEnv) -> bool { + super::exec::get_opcode_from_script(dst, src, &env.legacy) +} + +fn legacy_pubkey(dst: &mut CFrameItem, src: CFrameItem, env: &PolicyEnv) -> bool { + super::exec::get_pubkey_from_script(dst, src, &env.legacy) +} + +fn legacy_approval(dst: &mut CFrameItem, src: CFrameItem, env: &PolicyEnv) -> bool { + super::exec::bitcoin_tx_approval_hash(dst, src, &env.legacy) +} + +fn builtin(dst: &mut CFrameItem, mut src: CFrameItem, env: &PolicyEnv) -> bool { + let jet = Builtin::from_id(ID).expect("fixed built-in dispatch index"); + let bit_len = jet.source_bits(); + let mut input = BitValue { + bytes: vec![0; bit_len.div_ceil(8)], + bit_len, + }; + for bit in 0..bit_len { + if unsafe { c_readBit(&mut src) } { + input.bytes[bit / 8] |= 1 << (7 - bit % 8); + } + } + let Ok(output) = env.context.evaluate(jet, &input) else { + return false; + }; + if output.validate(jet.target_bits()).is_err() { + return false; + } + // Write sum padding too: a None result must not expose stale frame contents. + for bit in 0..output.bit_len { + unsafe { c_writeBit(dst, output.bytes[bit / 8] & (1 << (7 - bit % 8)) != 0) }; + } + true +} + +macro_rules! builtin_functions { + ($($id:literal),* $(,)?) => { + [$(builtin::<$id> as JetFn),*] + }; +} + +const BUILTIN_FNS: [JetFn; 55] = builtin_functions![ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, +]; + +impl Jet for PolicyJet { + fn cmr(&self) -> Cmr { + match self { + Self::Legacy(jet) => jet.cmr(), + Self::Builtin(jet) => Cmr::from_byte_array(jet.cmr()), + } + } + + fn source_ty(&self) -> TypeName { + match self { + Self::Legacy(jet) => jet.source_ty(), + Self::Builtin(jet) => jet.source_type(), + } + } + + fn target_ty(&self) -> TypeName { + match self { + Self::Legacy(jet) => jet.target_ty(), + Self::Builtin(jet) => jet.target_type(), + } + } + + fn encode(&self, writer: &mut BitWriter<&mut dyn Write>) -> std::io::Result { + match self { + Self::Legacy(jet) => { + writer.write_bit(false)?; + jet.encode(writer).map(|bits| 1 + bits) + } + Self::Builtin(jet) => writer.write_bits_be(64 | u64::from(jet.id()), 7), + } + } + + fn decode>(bits: &mut BitIter) -> Result { + match bits.next().ok_or(decode::Error::EndOfStream)? { + false => CoreExtension::decode(bits).map(Self::Legacy), + true => { + let mut id = 0; + for _ in 0..6 { + id = (id << 1) | u8::from(bits.next().ok_or(decode::Error::EndOfStream)?); + } + Builtin::from_id(id) + .map(Self::Builtin) + .ok_or(decode::Error::InvalidJet) + } + } + } + + fn cost(&self) -> Cost { + match self { + Self::Legacy(jet) => jet.cost(), + Self::Builtin(jet) => Cost::from_milliweight(jet.cost_milliweight()), + } + } + + fn parse(name: &str) -> Result { + name.parse() + } +} + +impl std::fmt::Display for PolicyJet { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Legacy(jet) => jet.fmt(formatter), + Self::Builtin(jet) => jet.fmt(formatter), + } + } +} + +impl FromStr for PolicyJet { + type Err = simplicity::Error; + + fn from_str(name: &str) -> Result { + if let Ok(jet) = name.parse::() { + Ok(Self::Builtin(jet)) + } else { + name.parse().map(Self::Legacy) + } + } +} + +#[cfg(test)] +mod tests; diff --git a/core/src/jets/policy/tests.rs b/core/src/jets/policy/tests.rs new file mode 100644 index 0000000..0927482 --- /dev/null +++ b/core/src/jets/policy/tests.rs @@ -0,0 +1,577 @@ +use super::*; +use std::collections::HashSet; +use std::sync::Arc; + +use simplicity::bit_machine::{ExecTracker, NodeOutput}; +use simplicity::bitcoin::{Sequence, absolute}; +use simplicity::ffi::c_jets::c_frame::uword_width; +use simplicity::ffi::ffi::UWORD; +use simplicity::node::{CoreConstructible, Inner, WitnessConstructible}; +use simplicity::{BitMachine, ConstructNode, RedeemNode, Value, types}; +use simplicity_unchained_policy::Error; + +use crate::policy::tests::{Fixture, unit_program}; + +fn packed(bits: impl IntoIterator) -> BitValue { + let bits = bits.into_iter().collect::>(); + let mut bytes = vec![0; bits.len().div_ceil(8)]; + for (index, bit) in bits.iter().enumerate() { + if *bit { + bytes[index / 8] |= 1 << (7 - index % 8); + } + } + BitValue { + bytes, + bit_len: bits.len(), + } +} + +fn word(value: u64, bits: usize) -> BitValue { + packed((0..bits).rev().map(|bit| value & (1 << bit) != 0)) +} + +fn arguments(jet: Builtin, env: &PolicyEnv) -> Vec { + match jet.source_bits() { + 0 => vec![BitValue::unit()], + 16 => vec![word(0, 16), word(10, 16), word(11, 16), word(65_535, 16)], + 32 => [0, 1, 7, 8, 500, 500_000_000, u32::MAX] + .map(|value| word(value.into(), 32)) + .to_vec(), + 64 => [ + (0, 0), + (0, 1), + (0, 33), + (0, 34), + (0, 9_999), + (0, 10_000), + (8, 0), + (u32::MAX, u32::MAX), + ] + .map(|(index, offset)| { + let mut bytes = index.to_be_bytes().to_vec(); + bytes.extend_from_slice(&offset.to_be_bytes()); + BitValue { bytes, bit_len: 64 } + }) + .to_vec(), + 256 => { + use simplicity::bitcoin::hashes::{Hash, sha256}; + vec![ + BitValue { + bytes: vec![0; 32], + bit_len: 256, + }, + BitValue { + bytes: sha256::Hash::hash(env.context().prevouts()[0].script_pubkey.as_bytes()) + .to_byte_array() + .to_vec(), + bit_len: 256, + }, + BitValue { + bytes: sha256::Hash::hash( + env.context().tx().output[0].script_pubkey.as_bytes(), + ) + .to_byte_array() + .to_vec(), + bit_len: 256, + }, + ] + } + bits => panic!("unhandled source width {bits} for {jet}"), + } +} + +struct Capture(Option); + +impl ExecTracker for Capture { + fn visit_node( + &mut self, + node: &RedeemNode, + _input: simplicity::bit_machine::FrameIter, + output: NodeOutput, + ) { + if matches!(node.inner(), Inner::Jet(_)) + && let NodeOutput::Success(frame) = output + { + self.0 = Some(packed(frame.take(node.arrow().target.bit_width()))); + } + } +} + +fn vm_program(jet: PolicyJet, source: &BitValue) -> Arc { + types::Context::with_context(|ctx| { + let value = Value::from_padded_bits( + &mut BitIter::from(source.bytes.as_slice()), + &jet.source_ty().to_final(), + ) + .unwrap(); + let argument = Arc::::witness(&ctx, Some(value)); + let function = Arc::::jet(&ctx, &jet); + let apply = Arc::::comp(&argument, &function).unwrap(); + let discard = Arc::::unit(&ctx); + Arc::::comp(&apply, &discard) + .unwrap() + .finalize_unpruned() + .unwrap() + }) +} + +fn ffi_call(jet: PolicyJet, source: &BitValue, env: &PolicyEnv, offset: usize) -> (bool, BitValue) { + const GUARD: UWORD = 0x5a5a; + let target_bits = jet.target_ty().to_bit_width(); + let input_bits = offset + source.bit_len + 9; + let output_bits = offset + target_bits + 9; + let input_words = uword_width(input_bits); + let output_words = uword_width(output_bits); + let mut input = vec![GUARD; input_words + 2]; + let mut output = vec![UWORD::MAX; output_words + 2]; + output[0] = GUARD; + output[output_words + 1] = GUARD; + unsafe { + let mut frame = CFrameItem::new_write(input_bits, input.as_mut_ptr().add(input_words + 1)); + for _ in 0..offset { + c_writeBit(&mut frame, true); + } + for index in 0..source.bit_len { + c_writeBit(&mut frame, source.bit(index).unwrap()); + } + for bit in 0..9 { + c_writeBit(&mut frame, bit % 2 == 0); + } + let saved_input = input.clone(); + let mut src = CFrameItem::new_read(input_bits, input.as_ptr().add(1)); + for _ in 0..offset { + assert!(c_readBit(&mut src)); + } + let mut dst = CFrameItem::new_write(output_bits, output.as_mut_ptr().add(output_words + 1)); + for _ in 0..offset { + c_writeBit(&mut dst, true); + } + let before = dst.len; + let result = PolicyEnv::c_jet_ptr(&jet)(&mut dst, src, env); + if result { + assert_eq!(before - dst.len, target_bits, "{jet}"); + } else { + assert_eq!( + before, dst.len, + "rejected jet must not partially write: {jet}" + ); + } + assert_eq!(input, saved_input, "{jet} modified its source"); + let written = before - dst.len; + for bit in written..target_bits { + c_writeBit(&mut dst, false); + assert!(bit < target_bits); + } + for bit in 0..9 { + c_writeBit(&mut dst, bit % 2 == 0); + } + assert_eq!(output[0], GUARD, "{jet} underflow"); + assert_eq!(output[output_words + 1], GUARD, "{jet} overflow"); + let mut read = CFrameItem::new_read(output_bits, output.as_ptr().add(1)); + for _ in 0..offset { + assert!(c_readBit(&mut read), "{jet} prefix canary"); + } + let actual = packed((0..target_bits).map(|_| c_readBit(&mut read))); + for bit in 0..9 { + assert_eq!(c_readBit(&mut read), bit % 2 == 0, "{jet} suffix canary"); + } + (result, actual) + } +} + +#[test] +fn every_builtin_executes_in_vm_and_matches_reference_at_frame_offsets() { + let program = unit_program(); + let mut fixture = Fixture::new(&program, 8, 8, true); + let mut observed = HashSet::new(); + for (sequence, lock_time) in [(10, 500), ((1 << 22) | 10, 500_000_010), (u32::MAX, 500)] { + for input in &mut fixture.psbt.unsigned_tx.input { + input.sequence = Sequence::from_consensus(sequence); + } + fixture.psbt.unsigned_tx.lock_time = absolute::LockTime::from_consensus(lock_time); + let prepared = fixture.prepare(&program, 0).unwrap(); + let context = Context::new( + &fixture.psbt, + 0, + prepared.cmr().to_byte_array(), + &fixture.script, + fixture.network, + fixture.base, + fixture.user, + fixture.recovery, + ) + .unwrap(); + let env = PolicyEnv::new(context); + for builtin in Builtin::ALL { + observed.insert(builtin.id()); + let jet = PolicyJet::Builtin(builtin); + for argument in arguments(builtin, &env) { + let expected = env.context().evaluate(builtin, &argument); + let redeem = vm_program(jet, &argument); + let mut capture = Capture(None); + let result = BitMachine::for_program(&redeem).unwrap().exec_with_tracker( + &redeem, + &env, + &mut capture, + ); + match &expected { + Ok(value) => { + result.unwrap(); + assert_eq!(capture.0.as_ref(), Some(value), "{builtin} {argument:?}"); + } + Err(Error::CheckFailed) => assert!(result.is_err(), "{builtin}"), + Err(error) => panic!("unexpected reference error {builtin}: {error}"), + } + for offset in [0, 1, 3, 7] { + let (ok, output) = ffi_call(jet, &argument, &env, offset); + assert_eq!(ok, expected.is_ok(), "{builtin} offset={offset}"); + if let Ok(expected) = &expected { + assert_eq!(&output, expected, "{builtin} offset={offset}"); + } + } + } + } + } + assert_eq!(observed.len(), 55); +} + +#[test] +fn shared_core_and_all_three_legacy_jets_execute_in_policy_environment() { + let program = unit_program(); + let fixture = Fixture::new(&program, 2, 2, false); + let prepared = fixture.prepare(&program, 1).unwrap(); + let context = Context::new( + &fixture.psbt, + 1, + prepared.cmr().to_byte_array(), + &fixture.script, + fixture.network, + fixture.base, + fixture.user, + fixture.recovery, + ) + .unwrap(); + let env = PolicyEnv::new(context); + for (jet, argument, expected) in [ + ( + CoreExtension::Core(simplicity::jet::Core::Eq32), + word(0, 64), + word(1, 1), + ), + ( + CoreExtension::GetOpcodeFromScript, + word(0, 8), + word(0x21, 8), + ), + ( + CoreExtension::GetPubkeyFromScript, + word(0, 8), + BitValue { + bytes: fixture.user.serialize().to_vec(), + bit_len: 256, + }, + ), + ( + CoreExtension::TxApprovalHash, + BitValue::unit(), + BitValue { + bytes: env.context().legacy_approval_hash().to_vec(), + bit_len: 256, + }, + ), + ] { + let jet = PolicyJet::Legacy(jet); + let redeem = vm_program(jet, &argument); + let mut capture = Capture(None); + BitMachine::for_program(&redeem) + .unwrap() + .exec_with_tracker(&redeem, &env, &mut capture) + .unwrap(); + assert_eq!(capture.0, Some(expected.clone()), "{jet}"); + if !matches!(jet, PolicyJet::Legacy(CoreExtension::Core(_))) { + assert_eq!(ffi_call(jet, &argument, &env, 3), (true, expected)); + } + } +} + +#[test] +fn namespace_metadata_indices_and_all_offset_roundtrips_are_complete() { + let mut cmrs = HashSet::new(); + let mut names = HashSet::new(); + for (index, jet) in PolicyJet::ALL.into_iter().enumerate() { + assert_eq!(jet.index(), index); + assert!(cmrs.insert(jet.cmr())); + assert!(names.insert(jet.to_string())); + assert_eq!(jet.to_string().parse::().unwrap(), jet); + assert_eq!(PolicyJet::parse(&jet.to_string()).unwrap(), jet); + assert_eq!( + jet.source_ty().to_bit_width(), + jet.source_ty().to_final().bit_width() + ); + assert_eq!( + jet.target_ty().to_bit_width(), + jet.target_ty().to_final().bit_width() + ); + match jet { + PolicyJet::Legacy(legacy) => { + assert_eq!(jet.cmr(), legacy.cmr()); + assert_eq!(jet.source_ty(), legacy.source_ty()); + assert_eq!(jet.target_ty(), legacy.target_ty()); + assert_eq!(jet.cost(), legacy.cost()); + } + PolicyJet::Builtin(builtin) => { + assert_eq!(jet.source_ty().to_bit_width(), builtin.source_bits()); + assert_eq!(jet.target_ty().to_bit_width(), builtin.target_bits()); + assert_eq!(jet.cmr().to_byte_array(), builtin.cmr()); + assert_eq!(jet.cost(), Cost::from_milliweight(10_000)); + } + } + for offset in 0..8 { + let mut encoded = Vec::new(); + let mut writer = BitWriter::new(&mut encoded as &mut dyn Write); + for _ in 0..offset { + writer.write_bit(true).unwrap(); + } + let length = jet.encode(&mut writer).unwrap(); + writer.write_bits_be(0x55, 7).unwrap(); + writer.flush_all().unwrap(); + let mut bits = BitIter::byte_slice_window(&encoded, offset, offset + length + 7); + assert_eq!( + PolicyJet::decode(&mut bits).unwrap(), + jet, + "offset={offset}" + ); + assert_eq!(bits.n_total_read(), length); + for bit in (0..7).rev() { + assert_eq!(bits.next(), Some((0x55 >> bit) & 1 != 0)); + } + for _ in offset + length + 7..encoded.len() * 8 { + assert_eq!(bits.next(), Some(false)); + } + assert_eq!(bits.next(), None); + } + } + assert!("definitely_not_a_policy_jet".parse::().is_err()); +} + +#[test] +fn reserved_codes_and_truncated_prefixes_fail_at_every_bit_offset() { + for offset in 0..8 { + for id in 0..64 { + let mut bytes = Vec::new(); + let mut writer = BitWriter::new(&mut bytes as &mut dyn Write); + for _ in 0..offset { + writer.write_bit(false).unwrap(); + } + writer.write_bits_be(64 | id, 7).unwrap(); + writer.flush_all().unwrap(); + let mut bits = BitIter::byte_slice_window(&bytes, offset, offset + 7); + let decoded = PolicyJet::decode(&mut bits); + if id < 55 { + assert_eq!( + decoded.unwrap(), + PolicyJet::Builtin(Builtin::from_id(id as u8).unwrap()) + ); + } else { + assert!(matches!(decoded, Err(decode::Error::InvalidJet))); + } + } + } + for jet in PolicyJet::ALL { + let mut bytes = Vec::new(); + let mut writer = BitWriter::new(&mut bytes as &mut dyn Write); + let length = jet.encode(&mut writer).unwrap(); + writer.flush_all().unwrap(); + for prefix in 0..length { + // BitIter only bounds whole bytes at its right edge. Right-align a + // truncated prefix so padding cannot accidentally finish the code. + let offset = (8 - prefix % 8) % 8; + let mut truncated = Vec::new(); + let mut writer = BitWriter::new(&mut truncated as &mut dyn Write); + for _ in 0..offset { + writer.write_bit(false).unwrap(); + } + for index in 0..prefix { + writer + .write_bit(bytes[index / 8] >> (7 - index % 8) & 1u8 != 0u8) + .unwrap(); + } + writer.flush_all().unwrap(); + let mut bits = BitIter::byte_slice_window(&truncated, offset, offset + prefix); + let result = PolicyJet::decode(&mut bits); + assert!(result.is_err(), "{jet} offset={offset} prefix={prefix}"); + if matches!(jet, PolicyJet::Builtin(_)) { + assert!(matches!(result, Err(decode::Error::EndOfStream))); + } + } + } +} + +#[test] +fn fixed_namespace_vectors_and_option_widths() { + for (jet, byte) in [ + (PolicyJet::Builtin(Builtin::PolicyProfileHash), 0x80u8), + (PolicyJet::Builtin(Builtin::TxVsizeLowerBound), 0xec), + (PolicyJet::Legacy(CoreExtension::GetOpcodeFromScript), 0x7c), + (PolicyJet::Legacy(CoreExtension::GetPubkeyFromScript), 0x7e), + (PolicyJet::Legacy(CoreExtension::TxApprovalHash), 0x7f), + ] { + let mut bytes = Vec::new(); + let mut writer = BitWriter::new(&mut bytes as &mut dyn Write); + jet.encode(&mut writer).unwrap(); + writer.flush_all().unwrap(); + assert_eq!(bytes, [byte]); + } + for (name, width) in [ + ("input_prev_outpoint", 289), + ("input_value", 65), + ("input_script_hash", 257), + ("input_script_size", 33), + ("input_script_byte", 9), + ("input_sequence", 33), + ("output_script_class", 9), + ("input_lock_blocks", 17), + ("current_lock_intervals", 17), + ] { + let jet: PolicyJet = name.parse().unwrap(); + assert_eq!(jet.target_ty().to_bit_width(), width, "{name}"); + } +} + +#[test] +fn all_policy_type_names_match_the_frozen_contract() { + let types: [(&[u8], &[u8]); 55] = [ + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"i"), + (b"1", b"i"), + (b"1", b"i"), + (b"1", b"i"), + (b"1", b"i"), + (b"i", b"+1*hi"), + (b"i", b"+1l"), + (b"i", b"+1h"), + (b"i", b"+1i"), + (b"*ii", b"+1c"), + (b"i", b"+1i"), + (b"1", b"*hi"), + (b"1", b"l"), + (b"1", b"h"), + (b"1", b"i"), + (b"i", b"+1l"), + (b"i", b"+1h"), + (b"i", b"+1i"), + (b"*ii", b"+1c"), + (b"i", b"+1c"), + (b"1", b"l"), + (b"1", b"l"), + (b"1", b"l"), + (b"h", b"l"), + (b"h", b"l"), + (b"h", b"i"), + (b"1", b"l"), + (b"1", b"l"), + (b"1", b"2"), + (b"1", b"2"), + (b"i", b"+1s"), + (b"i", b"+1s"), + (b"1", b"+1s"), + (b"1", b"+1s"), + (b"s", b"1"), + (b"s", b"1"), + (b"i", b"1"), + (b"i", b"1"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"h"), + (b"1", b"i"), + ]; + for (jet, (source, target)) in Builtin::ALL.into_iter().zip(types) { + let wrapper = PolicyJet::Builtin(jet); + assert_eq!(wrapper.source_ty().0, source, "{jet}"); + assert_eq!(wrapper.target_ty().0, target, "{jet}"); + } +} + +#[test] +#[ignore = "bounded local timing report; not a consensus-cost calibration"] +fn timing_at_profile_maxshape_and_execution_cap() { + use std::hint::black_box; + use std::time::Instant; + let program = unit_program(); + let fixture = Fixture::new(&program, 8, 8, true); + let started = Instant::now(); + let prepared = fixture.prepare(&program, 0).unwrap(); + let preparation = started.elapsed(); + let context = Context::new( + &fixture.psbt, + 0, + prepared.cmr().to_byte_array(), + &fixture.script, + fixture.network, + fixture.base, + fixture.user, + fixture.recovery, + ) + .unwrap(); + let env = PolicyEnv::new(context); + let mut calls = 0; + let started = Instant::now(); + for _ in 0..10 { + for builtin in Builtin::ALL { + let argument = arguments(builtin, &env).remove(0); + let jet = PolicyJet::Builtin(builtin); + let redeem = vm_program(jet, &argument); + black_box( + BitMachine::for_program(&redeem) + .unwrap() + .exec(&redeem, &env), + ) + .ok(); + calls += 1; + } + } + eprintln!( + "experimental policy timing: 8 inputs, 8 x 10000-byte scripts; prepare={preparation:?}, {calls} VM calls={:?}; 10000mw/jet is a conservative application budget, not consensus calibration", + started.elapsed() + ); + assert!(calls * 10_000 <= 10_000_000); + let program = types::Context::with_context(|ctx| { + let current = + Arc::::jet(&ctx, &PolicyJet::Builtin(Builtin::CurrentScriptHash)); + let aggregate = + Arc::::jet(&ctx, &PolicyJet::Builtin(Builtin::InputValueForScript)); + let call = Arc::::comp(¤t, &aggregate).unwrap(); + let unit = Arc::::unit(&ctx); + let call = Arc::::comp(&call, &unit).unwrap(); + let mut program = Arc::clone(&call); + for _ in 1..450 { + program = Arc::::comp(&program, &call).unwrap(); + } + program.finalize_types().unwrap().to_vec_without_witness() + }); + let fixture = Fixture::new(&program, 8, 8, true); + let started = Instant::now(); + let prepared = fixture.prepare(&program, 0).unwrap(); + let construction = started.elapsed(); + let started = Instant::now(); + let evaluation = prepared.evaluate_default(&[]).unwrap(); + eprintln!( + "near-cap actual policy: cost={}mw, prepare={construction:?}, evaluate={:?} (includes decode, cost check, allocation and 900 jet calls)", + evaluation.cost_milliweight, + started.elapsed(), + ); + assert!(evaluation.cost_milliweight >= 9_000_000); + assert!(evaluation.cost_milliweight <= 10_000_000); +} diff --git a/core/src/lib.rs b/core/src/lib.rs index f2271fc..f5f1e0f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,7 +4,9 @@ use simplicity::bitcoin::NetworkKind; use simplicity::elements::BlockHash; pub mod jets; +pub mod liquid_policy; pub mod plugin; +pub mod policy; pub mod program; pub mod runner; pub mod utils; diff --git a/core/src/liquid_policy.rs b/core/src/liquid_policy.rs new file mode 100644 index 0000000..8b1b247 --- /dev/null +++ b/core/src/liquid_policy.rs @@ -0,0 +1,2 @@ +//! Native entry point reuses the portable, WASM-compatible Liquid policy VM. +pub use simplicity_unchained_policy::liquid::{Error, PreparedPolicy, policy_cmr}; diff --git a/core/src/plugin/README.md b/core/src/plugin/README.md index 457b040..bf4b9dd 100644 --- a/core/src/plugin/README.md +++ b/core/src/plugin/README.md @@ -14,15 +14,12 @@ through the host's own implementation; custom jets are called through the function pointers from the descriptor. -## Bitcoin transaction context +## Policy integration -Bitcoin plugin environments now contain the consensus-serialized unsigned -transaction and the actual selected input index rather than empty transaction -bytes and index zero. Elements behavior and the `JetEnv` ABI layout are unchanged. +Native plugins are loaded by a Rust host, not by the policy service endpoints. +`bitcoin-policy-v1` and `liquid-policy-v1` use fixed portable registries. Adding a +jet to a policy profile requires a versioned registry and matching compiler, VM, +service and WASM implementations. -The Bitcoin base set adds `tx_approval_hash` (`ONE -> 2^256`, code `1111111`). -Existing base jet encodings, CMRs and indices are unchanged. Plugin descriptors -must not use this newly reserved encoding or a conflicting prefix; the loader -rejects such conflicts. See the core -[approval-hash protocol](../../README.md#bitcoin-per-spend-approvals) for its -semantics and the distinction from a Taproot signing sighash. +See the [script_jets example](../../../plugin_tests/script_jets/src/lib.rs) for +a plugin implementation and the [loader test](tests.rs) for host setup. diff --git a/core/src/policy.rs b/core/src/policy.rs new file mode 100644 index 0000000..4beb6f4 --- /dev/null +++ b/core/src/policy.rs @@ -0,0 +1,184 @@ +//! Opt-in Bitcoin policy preparation and bounded execution. +//! +//! Programs and witnesses are binary here. There is no legacy decoder fallback. + +use std::sync::Arc; + +use simplicity::bit_machine::{ExecutionError, LimitError}; +use simplicity::bitcoin::{self, XOnlyPublicKey, psbt::Psbt, taproot::TapNodeHash}; +use simplicity::{BitIter, BitMachine, Cmr, CommitNode, Cost, DecodeError, RedeemNode}; + +use crate::jets::policy::{PolicyEnv, PolicyJet}; +use crate::runner::MAX_EXECUTION_COST; + +pub use simplicity_unchained_policy::{Builtin, Context, ContextSummary}; + +#[derive(Debug, thiserror::Error)] +pub enum PolicyError { + #[error("invalid policy program or witness: {0}")] + Decode(#[source] DecodeError), + #[error("invalid policy context: {0}")] + Context(#[from] simplicity_unchained_policy::Error), + #[error("witnessed program CMR differs from the prepared policy")] + CommitmentMismatch, + #[error("policy rejected: {0}")] + PolicyRejected(#[source] ExecutionError), + #[error("program execution cost {cost} exceeds maximum {max} milliweight")] + ExecutionCostExceeded { cost: Cost, max: Cost }, + #[error("bit machine memory limit exceeded: {0}")] + MemoryLimit(#[source] LimitError), + #[error("internal policy VM error: {0}")] + Vm(#[source] ExecutionError), +} + +#[derive(Debug)] +pub struct EvaluationResult { + pub cmr: Cmr, + pub context: ContextSummary, + pub cost_milliweight: u32, + /// Static upper bound, not a wall-clock measurement or consensus calibration. + pub bounded_cost: Cost, +} + +pub struct PreparedPolicy { + program: Arc<[u8]>, + commitment: Arc, + env: PolicyEnv, +} + +/// Decode the original commitment without requiring any approval witness. +pub fn policy_cmr(program_bytes: &[u8]) -> Result { + decode_commitment(program_bytes).map(|program| program.cmr()) +} + +#[allow(clippy::too_many_arguments)] +pub fn prepare_policy( + program_bytes: &[u8], + psbt: &Psbt, + input_index: usize, + script: &[u8], + network: bitcoin::Network, + base_service_xonly: XOnlyPublicKey, + user_xonly: XOnlyPublicKey, + recovery: TapNodeHash, +) -> Result { + PreparedPolicy::new( + program_bytes, + psbt, + input_index, + script, + network, + base_service_xonly, + user_xonly, + recovery, + ) +} + +fn decode_commitment(program_bytes: &[u8]) -> Result, PolicyError> { + CommitNode::decode::<_, PolicyJet>(BitIter::from(program_bytes)).map_err(PolicyError::Decode) +} + +impl PreparedPolicy { + #[allow(clippy::too_many_arguments)] + pub fn new( + program_bytes: &[u8], + psbt: &Psbt, + input_index: usize, + script: &[u8], + network: bitcoin::Network, + base_service_xonly: XOnlyPublicKey, + user_xonly: XOnlyPublicKey, + recovery: TapNodeHash, + ) -> Result { + let commitment = decode_commitment(program_bytes)?; + let context = Context::new( + psbt, + input_index, + commitment.cmr().to_byte_array(), + script, + network, + base_service_xonly, + user_xonly, + recovery, + )?; + Ok(Self { + program: Arc::from(program_bytes), + commitment, + env: PolicyEnv::new(context), + }) + } + + pub fn context(&self) -> &Context { + self.env.context() + } + + pub fn summary(&self) -> ContextSummary { + self.context().summary() + } + + pub fn cmr(&self) -> Cmr { + self.commitment.cmr() + } + + pub fn evaluate_default(&self, witness_bytes: &[u8]) -> Result { + self.evaluate(witness_bytes, MAX_EXECUTION_COST) + } + + pub fn evaluate_milliweight( + &self, + witness_bytes: &[u8], + max_milliweight: u32, + ) -> Result { + self.evaluate(witness_bytes, Cost::from_milliweight(max_milliweight)) + } + + /// A caller may lower, but never raise, the runner's default execution cap. + pub fn evaluate( + &self, + witness_bytes: &[u8], + limit: Cost, + ) -> Result { + let program = RedeemNode::decode::<_, _, PolicyJet>( + BitIter::from(self.program.as_ref()), + BitIter::from(witness_bytes), + ) + .map_err(PolicyError::Decode)?; + if program.cmr() != self.cmr() { + return Err(PolicyError::CommitmentMismatch); + } + let mut machine = bounded_machine(&program, limit)?; + machine + .exec(&program, &self.env) + .map_err(|error| match error { + ExecutionError::ReachedFailNode(..) + | ExecutionError::ReachedPrunedBranch(..) + | ExecutionError::JetFailed(..) => PolicyError::PolicyRejected(error), + ExecutionError::LimitExceeded(limit) => PolicyError::MemoryLimit(limit), + _ => PolicyError::Vm(error), + })?; + Ok(EvaluationResult { + cmr: self.cmr(), + context: self.summary(), + // Simplicity 0.9 exposes Cost's exact milliweight only through Display. + cost_milliweight: program + .bounds() + .cost + .to_string() + .parse() + .expect("Cost displays its u32 milliweight"), + bounded_cost: program.bounds().cost, + }) + } +} + +fn bounded_machine(program: &RedeemNode, limit: Cost) -> Result { + let max = limit.min(MAX_EXECUTION_COST); + let cost = program.bounds().cost; + if cost > max { + return Err(PolicyError::ExecutionCostExceeded { cost, max }); + } + BitMachine::for_program(program).map_err(PolicyError::MemoryLimit) +} + +#[cfg(test)] +pub(crate) mod tests; diff --git a/core/src/policy/tests.rs b/core/src/policy/tests.rs new file mode 100644 index 0000000..cbe8404 --- /dev/null +++ b/core/src/policy/tests.rs @@ -0,0 +1,348 @@ +use super::*; +use simplicity::bitcoin::hashes::Hash; +use simplicity::bitcoin::secp256k1::{Keypair, Message, Secp256k1, SecretKey}; +use simplicity::bitcoin::{ + Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, absolute, transaction, +}; +use simplicity::node::{CoreConstructible, WitnessConstructible}; +use simplicity::{ConstructNode, Word, jet::Core, types}; + +pub(crate) struct Fixture { + pub psbt: Psbt, + pub script: Vec, + pub network: bitcoin::Network, + pub base: XOnlyPublicKey, + pub user: XOnlyPublicKey, + pub recovery: TapNodeHash, +} + +impl Fixture { + pub fn new(program: &[u8], inputs: usize, outputs: usize, max_scripts: bool) -> Self { + Self::new_on_network( + program, + inputs, + outputs, + max_scripts, + bitcoin::Network::Testnet, + ) + } + + pub fn new_on_network( + program: &[u8], + inputs: usize, + outputs: usize, + max_scripts: bool, + network: bitcoin::Network, + ) -> Self { + let secp = Secp256k1::new(); + let key = |byte| { + Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[byte; 32]).unwrap()) + .x_only_public_key() + .0 + }; + let base = key(1); + let user = key(2); + let recovery = TapNodeHash::from_byte_array([3; 32]); + // Include a compressed pubkey for both legacy script-reader adapters. + let mut script = vec![0x21, 0x02]; + script.extend_from_slice(&user.serialize()); + let cmr = policy_cmr(program).unwrap(); + let service = simplicity_unchained_policy::tweak_public_key( + base, + cmr.to_byte_array(), + &script, + network, + ) + .unwrap(); + let full = |key| { + bitcoin::PublicKey::new(bitcoin::secp256k1::PublicKey::from_x_only_public_key( + key, + bitcoin::secp256k1::Parity::Even, + )) + }; + let (address, _) = + crate::utils::generate_p2tr_address_btc(&full(service), &full(user), recovery, network) + .unwrap(); + let parent = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn::default()], + output: vec![ + TxOut { + value: Amount::from_sat(100_000), + script_pubkey: address.script_pubkey(), + }; + inputs + ], + }; + let tx = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::from_consensus(500), + input: (0..inputs) + .map(|vout| TxIn { + previous_output: OutPoint { + txid: parent.compute_txid(), + vout: vout as u32, + }, + sequence: Sequence::from_consensus(10), + ..TxIn::default() + }) + .collect(), + output: vec![ + TxOut { + value: Amount::from_sat(90_000 * inputs as u64 / outputs as u64), + script_pubkey: if max_scripts { + ScriptBuf::from_bytes(vec![0x51; 10_000]) + } else { + address.script_pubkey() + }, + }; + outputs + ], + }; + let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); + for (index, input) in psbt.inputs.iter_mut().enumerate() { + input.non_witness_utxo = Some(parent.clone()); + input.witness_utxo = Some(parent.output[index].clone()); + } + Self { + psbt, + script, + network, + base, + user, + recovery, + } + } + + pub fn prepare(&self, program: &[u8], index: usize) -> Result { + prepare_policy( + program, + &self.psbt, + index, + &self.script, + self.network, + self.base, + self.user, + self.recovery, + ) + } +} + +pub(crate) fn unit_program() -> Vec { + types::Context::with_context(|ctx| { + Arc::::unit(&ctx) + .finalize_types() + .unwrap() + .to_vec_without_witness() + }) +} + +fn approval_program(approver: XOnlyPublicKey) -> Vec { + types::Context::with_context(|ctx| { + let pk = Arc::::const_word(&ctx, Word::u256(approver.serialize())); + let message = Arc::::jet( + &ctx, + &PolicyJet::Builtin(Builtin::PolicyTransactionApprovalHash), + ); + let pk_message = Arc::::pair(&pk, &message).unwrap(); + let signature = Arc::::witness(&ctx, None); + let arguments = Arc::::pair(&pk_message, &signature).unwrap(); + let verify = Arc::::jet( + &ctx, + &PolicyJet::Legacy(crate::jets::bitcoin::CoreExtension::Core( + Core::Bip0340Verify, + )), + ); + Arc::::comp(&arguments, &verify) + .unwrap() + .finalize_types() + .unwrap() + .to_vec_without_witness() + }) +} + +#[test] +fn prepare_approval_without_witness_then_execute_exact_approved_context() { + let secp = Secp256k1::new(); + let approver = Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[7; 32]).unwrap()); + let program = approval_program(approver.x_only_public_key().0); + let fixture = Fixture::new(&program, 2, 2, false); + let prepared = fixture.prepare(&program, 0).unwrap(); + assert_eq!(policy_cmr(&program).unwrap(), prepared.cmr()); + assert_eq!(prepared.context().cmr(), prepared.cmr().to_byte_array()); + assert!(matches!( + prepared.evaluate_default(&[]), + Err(PolicyError::Decode(_)) + )); + let digest = prepared.context().policy_transaction_approval_hash(); + let signature = secp.sign_schnorr_no_aux_rand(&Message::from_digest(digest), &approver); + let result = prepared.evaluate_default(signature.as_ref()).unwrap(); + assert_eq!(result.cmr, prepared.cmr()); + assert!(result.cost_milliweight > 10_000); + assert_eq!( + result.context.policy_transaction_approval_hash, + prepared.summary().policy_transaction_approval_hash + ); + let mut invalid = signature.as_ref().to_vec(); + invalid[0] ^= 1; + assert!(matches!( + prepared.evaluate_default(&invalid), + Err(PolicyError::PolicyRejected(_)) + )); + let mut extra = signature.as_ref().to_vec(); + extra.push(0); + assert!(matches!( + prepared.evaluate_default(&extra), + Err(PolicyError::Decode(_)) + )); + let second = fixture.prepare(&program, 1).unwrap(); + assert_eq!(digest, second.context().policy_transaction_approval_hash()); + assert_ne!( + prepared.context().policy_approval_hash(), + second.context().policy_approval_hash() + ); + second.evaluate_default(signature.as_ref()).unwrap(); + let mut changed = fixture; + changed.psbt.unsigned_tx.output[0].value = Amount::from_sat(89_999); + let changed = changed.prepare(&program, 0).unwrap(); + assert!(matches!( + changed.evaluate_default(signature.as_ref()), + Err(PolicyError::PolicyRejected(_)) + )); +} + +#[test] +fn prepared_context_is_immutable_and_preserves_legacy_approval_semantics() { + let program = unit_program(); + for (network, legacy_network) in [ + (bitcoin::Network::Bitcoin, crate::BitcoinNetwork::Mainnet), + (bitcoin::Network::Testnet, crate::BitcoinNetwork::Testnet), + (bitcoin::Network::Testnet4, crate::BitcoinNetwork::Testnet4), + ] { + let fixture = Fixture::new_on_network(&program, 2, 2, false, network); + for index in 0..2 { + let prepared = fixture.prepare(&program, index).unwrap(); + let expected = crate::runner::bitcoin_execution_environment( + &fixture.psbt, + index, + prepared.cmr(), + &simplicity::elements::Script::from(fixture.script.clone()), + legacy_network, + ) + .unwrap(); + assert_eq!( + prepared.context().legacy_approval_hash(), + expected.approval_hash + ); + assert_ne!( + prepared.context().signing_commitment(), + crate::utils::signing_commitment(prepared.cmr(), &fixture.script), + ); + } + } + let mut fixture = Fixture::new(&program, 2, 2, false); + let prepared = fixture.prepare(&program, 1).unwrap(); + let digest = prepared.context().cooperative_sighash_default(); + fixture.psbt.unsigned_tx.output[0].value = Amount::from_sat(1); + fixture.psbt.inputs[0].witness_utxo = None; + prepared.evaluate_default(&[]).unwrap(); + assert_eq!(digest, prepared.context().cooperative_sighash_default()); +} + +#[test] +fn cost_boundary_and_preallocation_rejection_are_exact() { + assert_eq!( + MAX_EXECUTION_COST, + Cost::from_milliweight(simplicity_unchained_policy::MAX_EXECUTION_COST_MILLIWEIGHT), + ); + let program = unit_program(); + let fixture = Fixture::new(&program, 1, 1, false); + let prepared = fixture.prepare(&program, 0).unwrap(); + let result = prepared.evaluate_default(&[]).unwrap(); + prepared + .evaluate(&[], result.bounded_cost) + .expect("exact limit is inclusive"); + assert!(matches!( + prepared.evaluate_milliweight(&[], result.cost_milliweight - 1), + Err(PolicyError::ExecutionCostExceeded { .. }) + )); + for levels in [20, 60] { + let expensive = types::Context::with_context(|ctx| { + let mut node = Arc::::unit(&ctx); + for _ in 0..levels { + node = Arc::::comp(&node, &node).unwrap(); + } + node.finalize_types().unwrap().to_vec_without_witness() + }); + assert!(expensive.len() < 256); + let fixture = Fixture::new(&expensive, 1, 1, false); + let prepared = fixture.prepare(&expensive, 0).unwrap(); + let redeem = RedeemNode::decode::<_, _, PolicyJet>( + BitIter::from(expensive.as_slice()), + BitIter::from(&[][..]), + ) + .unwrap(); + assert!(BitMachine::for_program(&redeem).is_ok()); + assert!(redeem.bounds().cost > MAX_EXECUTION_COST); + assert!(matches!( + prepared.evaluate(&[], Cost::from_milliweight(u32::MAX)), + Err(PolicyError::ExecutionCostExceeded { + max: MAX_EXECUTION_COST, + .. + }) + )); + } +} + +#[test] +fn malformed_program_and_invalid_context_remain_distinct() { + assert!(matches!(policy_cmr(&[]), Err(PolicyError::Decode(_)))); + let program = unit_program(); + let mut fixture = Fixture::new(&program, 1, 1, false); + fixture.psbt.inputs[0].non_witness_utxo = None; + assert!(matches!( + fixture.prepare(&program, 0), + Err(PolicyError::Context( + simplicity_unchained_policy::Error::MissingParent(0) + )) + )); +} + +#[test] +fn new_profile_program_is_not_interpreted_as_legacy() { + let program = types::Context::with_context(|ctx| { + let current = Arc::::jet(&ctx, &PolicyJet::Builtin(Builtin::CurrentIndex)); + let expected = Arc::::const_word(&ctx, Word::u32(0)); + let pair = Arc::::pair(¤t, &expected).unwrap(); + let equal = Arc::::jet( + &ctx, + &PolicyJet::Legacy(crate::jets::bitcoin::CoreExtension::Core(Core::Eq32)), + ); + let check = Arc::::comp(&pair, &equal).unwrap(); + let verify = Arc::::jet( + &ctx, + &PolicyJet::Legacy(crate::jets::bitcoin::CoreExtension::Core(Core::Verify)), + ); + Arc::::comp(&check, &verify) + .unwrap() + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + let fixture = Fixture::new(&program, 2, 2, false); + fixture + .prepare(&program, 0) + .unwrap() + .evaluate_default(&[]) + .unwrap(); + assert!(matches!( + fixture.prepare(&program, 1).unwrap().evaluate_default(&[]), + Err(PolicyError::PolicyRejected(_)) + )); + if let Ok(legacy) = CommitNode::decode::<_, crate::jets::bitcoin::CoreExtension>(BitIter::from( + program.as_slice(), + )) { + assert_ne!(legacy.cmr(), policy_cmr(&program).unwrap()); + } +} diff --git a/core/src/utils.rs b/core/src/utils.rs index d72aa6b..04f3605 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -416,6 +416,76 @@ mod tests { assert_ne!(signing_commitment(cmr, &[]), signing_commitment(cmr, &[0])); } + #[test] + fn legacy_tweak_and_address_golden_vector() { + use bitcoin::hashes::Hash; + use bitcoin::key::TapTweak; + use bitcoin::taproot::{LeafVersion, TapLeafHash, TapNodeHash}; + + // Independently calculated with SHA256 and secp256k1 point arithmetic. + let secp = bitcoin::secp256k1::Secp256k1::verification_only(); + let base = bitcoin::XOnlyPublicKey::from_str( + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + ) + .unwrap(); + let user = bitcoin::XOnlyPublicKey::from_str( + "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + ) + .unwrap(); + let mut script = vec![0x52, 0x21, 0x02]; + script.extend_from_slice(&[2; 32]); + let commitment = signing_commitment(Cmr::from_byte_array([1; 32]), &script); + assert_eq!( + hex::encode(commitment), + "d1eeca3675cdeeb910edb4644dc13ffba0b2d8f1ee9c10133ea724f089994f57", + ); + let (service, _) = base.tap_tweak(&secp, Some(TapNodeHash::from_byte_array(commitment))); + assert_eq!( + hex::encode(service.serialize()), + "c38d3764be1552da4b3a4a9e2fa2bb5334b1e95e2693d3200e300f85e6432c7d", + ); + let full = |key| { + bitcoin::PublicKey::new(bitcoin::secp256k1::PublicKey::from_x_only_public_key( + key, + bitcoin::secp256k1::Parity::Even, + )) + }; + let service = full(service.to_x_only_public_key()); + let user = full(user); + let leaf = p2tr_multisig_leaf_btc(&service, &user); + assert_eq!( + hex::encode(TapLeafHash::from_script(&leaf, LeafVersion::TapScript).to_byte_array()), + "b6c4db550a7e7aaccc121cb65c72c68af7c959775644991cdce8c862cbe9e285", + ); + for (network, expected) in [ + ( + bitcoin::Network::Testnet, + "tb1plkhkj9m23n9aqdme05a0257egx3ktlgnxlpmg0u8xvu07amq2jvsy3yfy7", + ), + ( + bitcoin::Network::Bitcoin, + "bc1plkhkj9m23n9aqdme05a0257egx3ktlgnxlpmg0u8xvu07amq2jvsnejx73", + ), + ] { + let (address, spend) = generate_p2tr_address_btc( + &service, + &user, + TapNodeHash::from_byte_array([3; 32]), + network, + ) + .unwrap(); + assert_eq!(address.to_string(), expected); + assert_eq!( + hex::encode(spend.merkle_root().unwrap().to_byte_array()), + "78d9fdaea0b27a14cbe0934f75790861efef3f584ca0319da635f67c31f9ee0a", + ); + assert_eq!( + hex::encode(address.script_pubkey().as_bytes()), + "5120fdaf69176a8ccbd037797d3af553d941a365fd1337c3b43f873338ff77605499", + ); + } + } + #[test] fn test_generate_keypair() { let (secret_key, public_key) = generate_keypair(); diff --git a/examples/liquid-policy/per_asset_budget.simf b/examples/liquid-policy/per_asset_budget.simf new file mode 100644 index 0000000..6dbb5a8 --- /dev/null +++ b/examples/liquid-policy/per_asset_budget.simf @@ -0,0 +1,17 @@ +// Liquid Testnet policy asset. Jet constants use raw bytes, not explorer asset-ID order. +// This second illustrative asset is not a claim that the asset exists on-chain. +fn main() { + let policy_asset: u256 = 0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14; + let second_asset: u256 = 0x5151515151515151515151515151515151515151515151515151515151515151; + assert!(jet::eq_256(jet::fee_asset(), policy_asset)); + let current: u256 = jet::current_asset(); + match jet::eq_256(current, policy_asset) { + true => (), + false => assert!(jet::eq_256(current, second_asset)), + }; + // The fee denomination does not change when a different asset's input is selected. + assert!(jet::le_64(jet::tx_fee(), 1000)); + // Both budgets bind explicit assets, independent of the selected input. + assert!(jet::le_64(jet::asset_current_script_debit(policy_asset), 21000)); + assert!(jet::le_64(jet::asset_current_script_debit(second_asset), 15000)); +} diff --git a/examples/policy/README.md b/examples/policy/README.md new file mode 100644 index 0000000..e6ec5e6 --- /dev/null +++ b/examples/policy/README.md @@ -0,0 +1,131 @@ +# Bitcoin policy templates (experimental) + +These SimplicityHL 0.8 programs target `bitcoin-policy-v1`. Do not fund the +example keys: their private keys are the publicly known integers 1, 2 and 3. + +| File | Committed rule | +|---|---| +| `recipient_allowlist.simf` | Inspect every output; allow two exact script hashes or an exact-script return. | +| `capped_debit.simf` | The same allowlist, at most 100,000 sat all-input debit (including fees), at most 2,000 sat absolute fee. | +| `approvals_2_of_3.simf` | Two of three distinct committed keys approve J54 for all inputs of one transaction. | +| `approvals_2_of_3_per_input.simf` | The same threshold over input-specific J53; new approvals for each selected input. | +| `constrained_source.simf` | Every input outpoint has a fixed source authority's signature, vout 0–7, at least 16-block CSV; selected-input CLTV height 1,000; all outputs have exact P2TR shape. | + +Each file demonstrates a separate policy. Combine the checks you need before +compiling and funding. The source/shape example does not restrict recipient keys, +and the approval examples do not impose fee or debit caps. + +## Review and customize before funding + +1. Replace example keys with independently generated, validated x-only public + keys. Keep the pairwise distinctness assertions: repeated keys, or the same + signature copied into another key's slot, must not satisfy the threshold. +2. Derive recipient scripts from independently verified addresses on the intended + network. Hash the **raw complete scriptPubKey bytes** with SHA256, not an + address, hex text, recovery script, or PSBT change label. The literal examples: + - `00141111111111111111111111111111111111111111` + → `82d2c7b9ded542f3024bfe1babe73021e1a4894db8b8be6215f96be6737c975b`. + - `512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798` + → `0a22f58e8e4eb93ec82d86a4f4a9d2be3a172665648449f5e9a6747caaf12ca1`. + Exact-script returns compare J22 with J19, not the recovery script or an + untrusted declaration of change. +3. Review satoshi caps, locktime literals and the fixed source-authority key. + Source txids use raw consensus hash bytes, not explorer/display txid hex. + A literal funding-parent txid compiled into its own output policy would be + circular (the parent's txid depends on that policy's funding script). + Instead this example permits only outpoints authenticated after funding by a + **committed** source key, not an unauthenticated runtime allowlist. +4. Compile with the canonical Bitcoin policy jet hinter; confirm profile + fingerprint, CMR, script data, base signer key and network against service + capabilities. Derive the service key with `policy tweak` and the funding + address with `policy address` **before** funding the cooperative output. + Bitcoin Testnet4 is the native CLI/demo default; verify the configured network. +5. Test at/below/above each cap, mutations in the last output and last input, + missing evidence, disabled CSV, final sequence, wrong locktime units, repeated + keys, reused signatures, and both approval digest scopes. + +There are eight unrolled, count-guarded input/output slots. Counts above eight +are rejected. Only slots beyond the validated count are skipped; +an accessor unexpectedly returning `None` for an existing slot fails closed. +J33 aggregates **all** verified same-script inputs and subtracts exact-script +returns. This is a per-transaction debit cap, not a daily budget or one-use quota. + +`constrained_source.simf` uses profile class code 5 for P2TR plus exact length +and byte checks (`OP_1`, push-32). A class or prefix is not proof of recipient +authorization, valid key data, or safe arbitrary script behavior. CSV/CLTV +predicates check transaction conditions, not current height/time or maturity. + +Source authorization uses `SOURCES: [Option; 8]`, in input order, +with `None` for unused slots. Each actual input requires a BIP-340 signature +over the decoded digest: + +```text +tag = SHA256(UTF8("SimplicityUnchained/PolicySourceOutpoint/v1")) +digest = SHA256(tag || tag || J03 || raw_txid || vout_u32_be) +``` + +J03 binds the profile, network, program and exact script data. Source signatures +can authorize multiple proposals spending that outpoint; combine them with +transaction approvals or debit limits to restrict destinations and fees. +Each signature binds a specific txid and output index. The example uses Core +SHA256/BIP-340 jets. + +## Approvals and evidence + +The approval witness is `APPROVALS: [Option; 3]`, with each slot tied to +one fixed public key. An absent signer supplies `None`; an included invalid +signature rejects the policy, even if other slots could reach two. Sign the +**decoded 32-byte J53/J54 digest** directly with BIP-340, without another SHA256. +Approve only after locally constructing the context from every full parent and +the known untweaked service key; never blindly sign an HTTP summary. + +Any transaction, prevout, script, profile, network, user/recovery-key or program +change needs fresh preparation and approvals. J54 intentionally excludes the +selected input so one approval can authorize all inputs of the same transaction; +J53 does not. + +Parent txid verification proves amounts/scripts against supplied outpoints, **not +confirmation, active-chain validity, or unspentness**. This remains an off-chain +co-signer: possession of the spending key can bypass policy evaluation, and an +independently valid recovery leaf remains an alternative. See the +[CLI workflow](../../cli/README.md#bitcoin-policy-workflow). + +## Reproducible compiler and execution fixtures + +`compiled.json` pins the registry fingerprint, source +SHA256, encoded programs and CMRs. It also contains **negative-only** threshold +fixtures with the second key replaced by the first, while preserving all +distinctness checks. Never use those negative fixtures for funding. + +The sibling web demo's native compiler example regenerates these vectors using +the same registry-backed hinter as the browser. From the backend root: + +```bash +cargo run --manifest-path ../simplicity-unchained-web-demo/simplicity-lib/Cargo.toml \ + --example policy_templates --locked --quiet > examples/policy/compiled.json +cargo test -p cli --test policy_templates --locked +cargo test -p cli --test policy --locked +``` + +Execution tests run the compiled templates in the core VM without requiring the +sibling web checkout. They cover 1, 8 and 9 outputs, the final slot and zero-valued +outputs, eight-input accounting, +one-satoshi cap mutations, duplicate keys/signatures, J53/J54 scope, changes to +outputs/prevouts/sequences/locktime, authenticated sources, and CSV/CLTV/script +byte failures. Regenerate and review vectors when the source or registry changes. +All parent transactions are synthetic test content; +no tests fund addresses or broadcast transactions. + +An ignored integration test exercises preparation, approvals, co-signing and +finalization against a running Testnet4 service using synthetic inputs. It also +checks that changed transactions with stale approvals are rejected: + +```bash +SIMPLICITY_TEST_SERVICE_URL=http://127.0.0.1:30441 \ + cargo test -p cli --test policy_templates --locked \ + live_cli_compiled_policy_prepare_approve_cosign_and_verified_finalize -- --ignored +``` + +Only this synthetic integration test discovers its ephemeral service key from +capabilities. Real approval workflows must independently pin their known base +signer key. diff --git a/examples/policy/approvals_2_of_3.simf b/examples/policy/approvals_2_of_3.simf new file mode 100644 index 0000000..b67147c --- /dev/null +++ b/examples/policy/approvals_2_of_3.simf @@ -0,0 +1,33 @@ +// Illustrative public keys have KNOWN private keys. Replace all three before funding. +fn not(bit: bool) -> bool { + ::into(jet::complement_1(::into(bit))) +} + +fn checksig_add(counter: u8, pk: Pubkey, digest: u256, maybe_sig: Option) -> u8 { + match maybe_sig { + Some(sig: Signature) => { + jet::bip_0340_verify((pk, digest), sig); + let (carry, next): (bool, u8) = jet::increment_8(counter); + assert!(not(carry)); + next + } + None => counter, + } +} + +fn main() { + let pk1: Pubkey = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798; + let pk2: Pubkey = 0xc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5; + let pk3: Pubkey = 0xf9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9; + // Pairwise checks also reject unsafe customization that repeats a committed key. + assert!(not(jet::eq_256(pk1, pk2))); + assert!(not(jet::eq_256(pk1, pk3))); + assert!(not(jet::eq_256(pk2, pk3))); + let digest: u256 = jet::policy_transaction_approval_hash(); + let signatures: [Option; 3] = witness::APPROVALS; + let [sig1, sig2, sig3]: [Option; 3] = signatures; + let count1: u8 = checksig_add(0, pk1, digest, sig1); + let count2: u8 = checksig_add(count1, pk2, digest, sig2); + let count3: u8 = checksig_add(count2, pk3, digest, sig3); + assert!(jet::le_8(2, count3)); +} diff --git a/examples/policy/approvals_2_of_3_per_input.simf b/examples/policy/approvals_2_of_3_per_input.simf new file mode 100644 index 0000000..b9a8499 --- /dev/null +++ b/examples/policy/approvals_2_of_3_per_input.simf @@ -0,0 +1,33 @@ +// J53 variant: fresh approvals for each input, even within the same transaction. +// Illustrative public keys have KNOWN private keys. Replace all three before funding. +fn not(bit: bool) -> bool { + ::into(jet::complement_1(::into(bit))) +} + +fn checksig_add(counter: u8, pk: Pubkey, digest: u256, maybe_sig: Option) -> u8 { + match maybe_sig { + Some(sig: Signature) => { + jet::bip_0340_verify((pk, digest), sig); + let (carry, next): (bool, u8) = jet::increment_8(counter); + assert!(not(carry)); + next + } + None => counter, + } +} + +fn main() { + let pk1: Pubkey = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798; + let pk2: Pubkey = 0xc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5; + let pk3: Pubkey = 0xf9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9; + assert!(not(jet::eq_256(pk1, pk2))); + assert!(not(jet::eq_256(pk1, pk3))); + assert!(not(jet::eq_256(pk2, pk3))); + let digest: u256 = jet::policy_approval_hash(); + let signatures: [Option; 3] = witness::APPROVALS; + let [sig1, sig2, sig3]: [Option; 3] = signatures; + let count1: u8 = checksig_add(0, pk1, digest, sig1); + let count2: u8 = checksig_add(count1, pk2, digest, sig2); + let count3: u8 = checksig_add(count2, pk3, digest, sig3); + assert!(jet::le_8(2, count3)); +} diff --git a/examples/policy/capped_debit.simf b/examples/policy/capped_debit.simf new file mode 100644 index 0000000..9877237 --- /dev/null +++ b/examples/policy/capped_debit.simf @@ -0,0 +1,36 @@ +// All-input accounting, not an output-0 or per-input cap. Values are satoshis. +fn approved_recipient(script_hash: u256) { + let recipient_a: u256 = 0x82d2c7b9ded542f3024bfe1babe73021e1a4894db8b8be6215f96be6737c975b; + let recipient_b: u256 = 0x0a22f58e8e4eb93ec82d86a4f4a9d2be3a172665648449f5e9a6747caaf12ca1; + match jet::eq_256(script_hash, jet::current_script_hash()) { + true => (), + false => match jet::eq_256(script_hash, recipient_a) { + true => (), + false => assert!(jet::eq_256(script_hash, recipient_b)), + }, + } +} + +fn inspect_output(index: u32, count: u32) { + match jet::lt_32(index, count) { + true => approved_recipient(unwrap(jet::output_script_hash(index))), + false => (), + } +} + +fn main() { + let count: u32 = jet::num_outputs(); + assert!(jet::le_32(1, count)); + assert!(jet::le_32(count, 8)); + inspect_output(0, count); + inspect_output(1, count); + inspect_output(2, count); + inspect_output(3, count); + inspect_output(4, count); + inspect_output(5, count); + inspect_output(6, count); + inspect_output(7, count); + // J33 includes fees and every input sharing the exact cooperative script. + assert!(jet::le_64(jet::current_script_debit(), 100000)); + assert!(jet::le_64(jet::tx_fee(), 2000)); +} diff --git a/examples/policy/compiled.json b/examples/policy/compiled.json new file mode 100644 index 0000000..d0cb6f2 --- /dev/null +++ b/examples/policy/compiled.json @@ -0,0 +1,40 @@ +{ + "profile": "bitcoin-policy-v1", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "templates": { + "approvals_2_of_3.simf": { + "cmr_hex": "c1f3ea0cc8d54b93f6df1f7bed3b20295c2f64188f46a01c05064b56b8b87baf", + "duplicate_keys_cmr_hex": "f3b1e458cb875e019b18696faf5feb71d224b09b6d75761af3949ed44cde2b3e", + "duplicate_keys_program_base64": "5p02l5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmAQgUJAxQKE2n5MIoBkljDEEk0T4X4nVIptTHIRYNvmbCGAfETvOA2+QQgUIMOONgcKs26AQjMAIBgwCDbxbQGoDMDIW/EDcBrA1J+oIQKHQKEGIFCbAABB+JDjhYDcVnHIMMFgFoHxOQKEGGHH47MONx8ccLAgwVbGAg+7TGghAoNyDMA5Bgcgj8JMFR+GAgGoLgADAPDgOIwOKCBQbiY/Jk444WI/GxuNjjDhYhZgOAoFBuNj8nzjjjhZD8BPyHOMOOFkFoAcRoFCbACBBuRYo04BQQBy5E0H5kwQDYBw8DjADlUByvA5ZhcuQGkXL8BrFzDANoOZcDmdA5ogA==", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "program_base64": "5qE2l5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmAQgUJtOMCP8og9r62mCKgN0rgPmwuO8clxneeU9XWBNyuOE9yghAoTafkwigGSWMMQSTRPhfidUim1MchFg2+ZsIYB8RO84Db5BCBQgw442BwqzboBCMwAgGDAINvFtAagMwMhb8QNwGsDUn6ghAodAoQYgUJsAAEH4kOOFgNxWccgwwWAWgfE5AoQYYcfjsw43HxxwsCDBVsYCD7tMaCECg3IMwDkGByCPwkwVH4YCAaguAAMA8OA4jA4oIFBuJj8mTjjhYj8bG42OMOFiFmA4CgUG42PyfOOOOFkPwE/Ic4w44WQWgBxGgUJsAIEG5FijTgFBAHLkTQfmUBANgHDwOMAOVQHK8DlmFy5AaRcvwGsXMMA2g5lwOZ0DmjAA==", + "source_sha256": "9f3313970785ed5f798d11983df8b97fa6ded723dd04baad7948d259c83c3d02" + }, + "approvals_2_of_3_per_input.simf": { + "cmr_hex": "c5f1182c65eb7a8ce8339b84ba15768fde66c3957c03631fab1f6e505fdac7aa", + "duplicate_keys_cmr_hex": "4c62d0728669151f51bf9e84290fa2ddd7dc1b2d67467fc49e18c42e3b738358", + "duplicate_keys_program_base64": "5p02l5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmAQgUJAxQKE2n5MIoBkljDEEk0T4X4nVIptTHIRYNvmbCGAfETvOA2+QQgUIMOONgcKs26AQjMAIBgwCDbxbQGoDMDIW/EDcBrA1J+gIQKHQKEGIFCbAABB+JDjhYDcVnHIMMFgFoHxOQKEGGHH47MONx8ccLAgwVbGAg+7TGghAoNyDMA5Bgcgj8JMFR+GAgGoLgADAPDgOIwOKCBQbiY/Jk444WI/GxuNjjDhYhZgOAoFBuNj8nzjjjhZD8BPyHOMOOFkFoAcRoFCbACBBuRYo04BQQBy5E0H5kwQDYBw8DjADlUByvA5ZhcuQGkXL8BrFzDANoOZcDmdA5ogA==", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "program_base64": "5qE2l5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmAQgUJtOMCP8og9r62mCKgN0rgPmwuO8clxneeU9XWBNyuOE9yghAoTafkwigGSWMMQSTRPhfidUim1MchFg2+ZsIYB8RO84Db5BCBQgw442BwqzboBCMwAgGDAINvFtAagMwMhb8QNwGsDUn6AhAodAoQYgUJsAAEH4kOOFgNxWccgwwWAWgfE5AoQYYcfjsw43HxxwsCDBVsYCD7tMaCECg3IMwDkGByCPwkwVH4YCAaguAAMA8OA4jA4oIFBuJj8mTjjhYj8bG42OMOFiFmA4CgUG42PyfOOOOFkPwE/Ic4w44WQWgBxGgUJsAIEG5FijTgFBAHLkTQfmUBANgHDwOMAOVQHK8DlmFy5AaRcvwGsXMMA2g5lwOZ0DmjAA==", + "source_sha256": "745b74cc36d1391743e17cadaa17d6012937a76aee9e70b8510a47a9e86809cb" + }, + "capped_debit.simf": { + "cmr_hex": "3fb5125ccf3f7bb08dde6be3470137bb9524d40384274677c1334359f35f5be0", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "program_base64": "5m08kEIFCbIAAAABBCDBVpwDEEYBFkAAAAEA1haAGYGVkAAAAAA4AE2m3n4AKNOA4ghAoSfR1QQhIoZ0QznIWef/b40z+a+nNzThyQhoT+7cjE0KYRLTvzYTF0GBoGIGkm0wWlj3O9qoXmBJf8N1fOYEPDSRKbcXF8xCvy18zm+S62CBNCbSFEXrHRydcn2QWw1J6VOlfHQuTMrJCJPr00zo+VXiWUIIQKD7jk9IECwZt0AhAoPpNwk44WADSgUH0G4hOOFgA4CBx+kMA1EhgHBQOIAOJgOKweOAOOgOQQHI+WQAAAAEDksE5JgZWQAAAAGDk0E5LgarIAAAAEByeCcmwN1kAAAACg5RBOT4HArIAAAAGBymCcowODWQAAAAODlUE5TgcK8AOVyzAAAAAAABhqAHLAKtOAZAgDlZ2wctlmAAAAAAAAD6AOXAVAaAOWYnLw/L8EC1gKLgoDEXCwGYuIQGkXE4DWLi0BtFxmA3i45AcBFx4A4GLmDAcFFzFAODg5lg", + "source_sha256": "66ab62f9856b10e9ae22f6524141cda9abd00c3c98528297696ae5f7e5ccc363" + }, + "constrained_source.simf": { + "cmr_hex": "ddd0500ad288c712a760ca7342e887dc8f5e847be0398176f14f08b13f41effd", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "program_base64": "6JJPIBCBQnkghAoTZAAAAAIIQYcLBpwDEEYBFkAAAAEA2BaAGYGRtot4DWBqFhoA3AbXQKEGIFCbIAAAAABB+GHIMMMMFkFpG4nNxQcKtOA4ghAoSfTygQhIoZ0QznIWef/b40z+a+nNzThyQhoT+7cjE0KYRLTvzYTF0GBoGIGkgUJtLzfMz987l3WKtAxSudDhYOBTf5tlucUbKz5QK2LfAvMAhAoQYaGHH5EGHCwJtP7TK3CRIJw+79Fd4ph2lwc0aTV553vcKp2R7P9xnfhxggThCdWQIQKEGG4IcKtVyBCBQbSfMUAZoFBt6eECBYAN6BQbg5+KDjjjhZAOEoFBuIT8XnHHHHCzarQQgUG4t1YggGAHAQOGAcSgcWAcbAceAcgQuRI/K44445IuWhBgeUIGAGYW1sYCEgcywuSoDlqBzNn5ZHeMEISKEGB5agYAaCBQmxABAEG1CjTgGAIA5sRNB+cMEA2BcLAYC4iAZA5fAcwgPMmBzKgczwHNVzjn5pjDDBc1YWQDTZAAAAAQObMfmuMMFzZhYgOA2QAAAAGDm7H4CYYLm7CxAcJsgAAAAQHOGPzemC5wgsAHEFkAAAACg5yh+HmC5yQsAHE9kAAAADA50R+HGC50AsAHF1kAAAADg52B+GmC51wsAHGp+ew4XO8Nz+H5/RQBzqIFCT59gEISKEGB51QMANCwBQbQqzbQQB0BvWBwIhIoDzx9AwCAZLIAAAAiBwsKs24ggDoGwcQdAMLiB1wQBwqwUQcTBUBwkDoJQcVdBcLioBqA4jsCAHF4VAcRAdBcJxmBy9FsAcvxcIAcwIuKAHMGHjsDj0DkIFy75DAUXLTkQBiLldyKAzFyo5GAaRco+RwGsXJ7kgBtFyZ5JAbwdBnzO+cEWQAAAfQDoN3pBAnQdAczoswHM+LYA5oRcAAc0YuCAOaUXCAHNOLhgDmpFxAA5qxcSAOa0XFADmvFyxAc2IuXQDmzFzBgObUXMYA5txcyoDm5FzPgObsXNSA5vRc2YDm/B0L4HQyBdDQA5wxdDaA5xRdDmA5xxdD0A5yQdEOB0Ro=", + "source_sha256": "66883c742d3d0feb8992b21cf6557aee8f6366c02c7853ae65fa3821dcf48a27" + }, + "recipient_allowlist.simf": { + "cmr_hex": "b4fdb5022e9dd9afa3f59d6938e77fa77f23871ab96dbeb1958aa4be413517d5", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "program_base64": "5iE8kEIFCbIAAAABBCDBVpwDEEYBFkAAAAEA1haAGYGVkAAAAAA4AE2m3n4AKNOA4ghAoSfR1QQhIoZ0QznIWef/b40z+a+nNzThyQhoT+7cjE0KYRLTvzYTF0GBoGIGkm0wWlj3O9qoXmBJf8N1fOYEPDSRKbcXF8xCvy18zm+S62CBNCbSFEXrHRydcn2QWw1J6VOlfHQuTMrJCJPr00zo+VXiWUIIQKD7jk9IECwZt0AhAoPpNwk44WADSgUH0G4hOOFgA4CBx+kMA1EhgHBQOIAOJgOKweOAOOgOQQHI+WQAAAAEDksE5JgZWQAAAAGDk0E5LgarIAAAAEByeCcmwN1kAAAACg5RBOT4HArIAAAAGBymCcowODWQAAAAODlUE5TgcKE5Wn5YAgWgBRbgGIuCAMxcKAaRcQANYuJgG0XFIDeLlgA4CLluA4GDmDA=", + "source_sha256": "06bb712ebbd71de1e8b5c588bcef991a96af1c8ccd7943a1c3a955541e5dabdc" + } + } +} diff --git a/examples/policy/constrained_source.simf b/examples/policy/constrained_source.simf new file mode 100644 index 0000000..e86eea3 --- /dev/null +++ b/examples/policy/constrained_source.simf @@ -0,0 +1,69 @@ +// Independent source/locktime/script-shape example, not a recipient authorization. +fn source_digest(txid: u256, vout: u32) -> u256 { + // SHA256("SimplicityUnchained/PolicySourceOutpoint/v1"), used as a tagged-hash domain. + let tag: u256 = 0xfda656e12241387ddfa2bbc530ed2e0e68d26af3cef7b8553b23d9fee33bf0e3; + let ctx: Ctx8 = jet::sha_256_ctx_8_init(); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, jet::signing_commitment_hash()); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, txid); + let ctx: Ctx8 = jet::sha_256_ctx_8_add_4(ctx, vout); + jet::sha_256_ctx_8_finalize(ctx) +} + +fn inspect_input(index: u32, count: u32, maybe_sig: Option) { + match jet::lt_32(index, count) { + true => { + let (txid, vout): (u256, u32) = unwrap(jet::input_prev_outpoint(index)); + // Replace this publicly known key before funding. Sources need its signature. + let source_authority: Pubkey = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798; + jet::bip_0340_verify((source_authority, source_digest(txid, vout)), unwrap(maybe_sig)); + assert!(jet::lt_32(vout, 8)); + let blocks: u16 = unwrap(jet::input_lock_blocks(index)); + assert!(jet::le_16(16, blocks)); + }, + false => (), + } +} + +fn inspect_output(index: u32, count: u32) { + match jet::lt_32(index, count) { + true => { + // A shape check does NOT authenticate a recipient or prove spendability. + assert!(jet::eq_8(unwrap(jet::output_script_class(index)), 5)); + assert!(jet::eq_32(unwrap(jet::output_script_size(index)), 34)); + assert!(jet::eq_8(unwrap(jet::output_script_byte(index, 0)), 0x51)); + assert!(jet::eq_8(unwrap(jet::output_script_byte(index, 1)), 0x20)); + }, + false => (), + } +} + +fn main() { + let inputs: u32 = jet::num_inputs(); + let outputs: u32 = jet::num_outputs(); + assert!(jet::le_32(1, inputs)); + assert!(jet::le_32(inputs, 8)); + assert!(jet::le_32(1, outputs)); + assert!(jet::le_32(outputs, 8)); + let sources: [Option; 8] = witness::SOURCES; + let [sig0, sig1, sig2, sig3, sig4, sig5, sig6, sig7]: [Option; 8] = sources; + inspect_input(0, inputs, sig0); + inspect_input(1, inputs, sig1); + inspect_input(2, inputs, sig2); + inspect_input(3, inputs, sig3); + inspect_input(4, inputs, sig4); + inspect_input(5, inputs, sig5); + inspect_input(6, inputs, sig6); + inspect_input(7, inputs, sig7); + inspect_output(0, outputs); + inspect_output(1, outputs); + inspect_output(2, outputs); + inspect_output(3, outputs); + inspect_output(4, outputs); + inspect_output(5, outputs); + inspect_output(6, outputs); + inspect_output(7, outputs); + jet::check_csv_blocks(16); + jet::check_cltv_height(1000); +} diff --git a/examples/policy/recipient_allowlist.simf b/examples/policy/recipient_allowlist.simf new file mode 100644 index 0000000..077f764 --- /dev/null +++ b/examples/policy/recipient_allowlist.simf @@ -0,0 +1,35 @@ +// Experimental bitcoin-policy-v1 only. Replace reviewed literals BEFORE funding. +fn approved_recipient(script_hash: u256) { + // SHA256(0014 || 20 bytes of 0x11), an exact P2WPKH scriptPubKey. + let recipient_a: u256 = 0x82d2c7b9ded542f3024bfe1babe73021e1a4894db8b8be6215f96be6737c975b; + // SHA256(5120 || x-only 1*G), an exact P2TR scriptPubKey. + let recipient_b: u256 = 0x0a22f58e8e4eb93ec82d86a4f4a9d2be3a172665648449f5e9a6747caaf12ca1; + match jet::eq_256(script_hash, jet::current_script_hash()) { + true => (), + false => match jet::eq_256(script_hash, recipient_a) { + true => (), + false => assert!(jet::eq_256(script_hash, recipient_b)), + }, + } +} + +fn inspect_output(index: u32, count: u32) { + match jet::lt_32(index, count) { + true => approved_recipient(unwrap(jet::output_script_hash(index))), + false => (), + } +} + +fn main() { + let count: u32 = jet::num_outputs(); + assert!(jet::le_32(1, count)); + assert!(jet::le_32(count, 8)); + inspect_output(0, count); + inspect_output(1, count); + inspect_output(2, count); + inspect_output(3, count); + inspect_output(4, count); + inspect_output(5, count); + inspect_output(6, count); + inspect_output(7, count); +} diff --git a/policy/Cargo.toml b/policy/Cargo.toml new file mode 100644 index 0000000..6fe6b63 --- /dev/null +++ b/policy/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "simplicity-unchained-policy" +version = "0.1.0" +edition = "2024" +rust-version = "1.92.0" +description = "Portable, experimental Bitcoin policy context and off-chain extension registry" +license = "MIT" + +[lib] +name = "simplicity_unchained_policy" +path = "src/lib.rs" + +[dependencies] +simplicity-lang = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +hex = { workspace = true } +thiserror = { workspace = true } +elements = { version = "0.27.0", features = ["serde"] } diff --git a/policy/LIQUID.md b/policy/LIQUID.md new file mode 100644 index 0000000..429ba79 --- /dev/null +++ b/policy/LIQUID.md @@ -0,0 +1,303 @@ +# liquid-policy-v1 + +New off-chain policy profile, **not** the removed legacy Liquid flow or Elements +consensus Simplicity. Never fund an old output expecting this profile to recognize +it. Bitcoin profile identities and semantics are unchanged. + +## Cryptographic context + +Every input requires its full parent transaction and matching `witness_utxo` base +fields. Parent txid/vout are checked. Parent content verification is not a chain +inclusion, consensus-validity, confirmation or unspentness proof. + +Confidential inputs and outputs require standard PSET explicit `asset`/`amount` +fields **and** `blind_asset_proof`/`blind_value_proof`. The kernel calls Elements' +single-asset surjection disclosure verifier and exact-value range disclosure +verifier against the authenticated commitments. It also verifies original input +rangeproofs, transaction output range/surjection proofs and Pedersen balance. +Missing evidence, invalid labels, proofs, existing signatures, or checked +per-asset conservation failure abort preparation and signing. Disclosure proofs +are not a commitment-opening assertion supplied by the caller. + +This profile requires disclosure of **every** input/output asset and value to +the co-signer and immutable summary; it does not preserve amount secrecy from +the service. On-chain asset/value commitments can remain confidential. Receiving +keys and blinding factors are never sent to the service. Partial/unknown +disclosures cannot produce a signable context. + +Issuance/pegin, annex, code-separator overrides, key-path signatures and already +finalized PSET input witnesses are unsupported and rejected. Standard fully +confidential inputs and multiple assets are supported. Local unblinding handles +fully explicit or fully confidential outputs; mixed asset/value commitments can +be submitted with independently generated valid PSET disclosure proofs. They +never receive a trusted-label fallback. + +All inputs spend the same reconstructed NUMS cooperative tree: Elements +tapscript version `0xc4`, service `CHECKSIG`, user `CHECKSIGADD`, `2 EQUAL`, hidden +recovery node at depth one. The new manifest fingerprint and configured Elements +genesis enter the service-key tweak. Elements' actual DEFAULT script-path sighash +uses that genesis too. The summary is immutable after construction. + +## Asset semantics + +Asset words in jets are `AssetId::to_byte_array()` (raw hash bytes). JSON +`asset_id` uses explorer/display order; reverse that hex before writing a `u256` +asset constant in a policy. Individual amount fields are integer smallest units +of their disclosed asset, **not Bitcoin satoshis across all assets**. + +`input_value` and `output_value` return `None` for a different current-input asset. +Totals, script-hash sums/counts, returns and debit project on the current verified +input asset. There is no all-asset scalar total. + +**Exception:** `tx_fee` always means the one explicit configured network-fee-asset +output's value. Its denomination is `fee_asset`, not `current_asset`; fee caps +cannot be bypassed by selecting an input of another asset. `total_output_value` +and `asset_total_output_value` include fee outputs. Each asset's input/output +totals must be equal; debit is all that asset's same-contract inputs minus +exact-script returns, including fees where applicable. + +Additional jets (all fixed 10000 milliweight): + +| Jet | Arguments | Return | +| --- | --- | --- | +| `input_asset`, `output_asset` | `u32` index | `Option` raw asset | +| `current_asset`, `fee_asset` | none | raw asset `u256` | +| `asset_total_input_value`, `asset_total_output_value` | raw asset `u256` | checked `u64` total | +| `asset_fee` | raw asset `u256` | `u64` fee (zero for nonfee asset) | +| `asset_current_script_debit` | raw asset `u256` | checked `u64` debit | +| `disclosures_hash` | none | `u256` | +| `input_value_asset`, `output_value_asset` | `u32` index | `Option<(u256,u64)>` | + +Use an explicit constant asset in `asset_current_script_debit` for each budget, +independent of which input is being authorized. If intentionally using a scalar +budget, first assert `current_asset` equals the intended constant. A transaction +with multiple assets needs independently specified budgets, not a sum of unlike +units. Hash-keyed sums only assert script equality by hash, not ownership. + +## Rust/WASM API + +All WASM functions synchronously accept JS objects, return JSON-compatible objects, +and throw errors. No endpoint silently succeeds on invalid evidence. +`compile({environment:"elements",profile:"liquid-policy-v1",script,witness, +include_debug})` returns the existing compiler response. + +```ts +type Binding = { + network?: "liquidtestnet" | "liquid"; // default liquidtestnet + cmr_hex: string; redeem_script_hex: string; base_public_key_hex: string; +}; +type ContextRequest = Binding & { + pset_hex: string; input_index: number; + user_pubkey: string; user_leaf_hash_hex: string; +}; +type Disclosure = { + asset_id: string; value: number; + confidential_asset: boolean; confidential_value: boolean; +}; +type AssetAccounting = { + asset_id: string; total_input_value: number; total_output_value: number; + fee: number; returned_value: number; debit: number; +}; +type ContextSummary = { + profile: "liquid-policy-v1"; profile_hash: string; + network: "liquidtestnet" | "liquid"; genesis_hash: string; + cmr: string; signing_commitment: string; redeem_script_hash: string; + input_index: number; input_count: number; output_count: number; + unsigned_tx_hash: string; prevouts_hash: string; disclosures_hash: string; + cooperative_leaf_hash: string; cooperative_sighash_default: string; + policy_approval_hash: string; policy_transaction_approval_hash: string; + service_pubkey: string; user_pubkey: string; recovery_leaf_hash: string; + current_asset_id: string; fee_asset_id: string; + assets: AssetAccounting[]; inputs: Disclosure[]; outputs: Disclosure[]; + output_scripts_hex: string[]; script_pubkey_hex: string; + vsize_lower_bound: number; provenance: string; chain_status_verified: false; +}; +``` + +Amounts cross the JS boundary only within `Number.MAX_SAFE_INTEGER`; larger +amounts/totals are supported natively but rejected by WASM rather than rounded. +Sign raw decoded 32-byte approval/sighash digest bytes, with no extra hash. + +| WASM export | Request | Exact response | +| --- | --- | --- | +| `liquid_policy_registry` | none | `{profile,profile_hash,manifest}` | +| `liquid_policy_generate_blinding_key` | none | `{blinding_secret_key_hex,blinding_public_key_hex}` | +| `liquid_policy_recovery_script` | human-readable `string` | `{redeem_script_hex,user_leaf_hash_hex}` using Elements TapLeaf/default `0xc4` | +| `liquid_policy_local_tweak` | `Binding` | `{profile,profile_hash,network,genesis_hash,cmr,signing_commitment,public_key_hex,base_public_key_hex}` | +| `liquid_policy_address` | `{binding:Binding,user_pubkey,user_leaf_hash_hex,blinding_public_key_hex?:string}` | `{address,unconfidential_address,script_pubkey_hex,service_pubkey,cooperative_script_hex,control_block_hex,profile,profile_hash,network}` | +| `liquid_policy_unblind_output` | `{parent_tx_hex,txid,vout,blinding_secret_key_hex?:string}` | `{txid,vout,script_pubkey_hex,chain_status_verified:false,disclosure:{asset_id,value_sat,confidential,blind_asset_proof_hex:string|null,blind_value_proof_hex:string|null}}` | +| `liquid_policy_create_pset` | creation below | `{pset_hex,transaction_hex,txid,inputs:number,outputs:number,network}` (unsigned transaction for subsequent full-parent evidence) | +| `liquid_policy_context` | `ContextRequest` | `{context:ContextSummary}` | +| `liquid_policy_evaluate` | `{prepared:ContextRequest,program_base64,witness_base64}` | `{accepted:true,context,execution_cost_milliweight:number}` | +| `liquid_policy_verify_signed_pset` | `{prepared:ContextRequest,signed_pset_hex,signature_hex,public_key_hex}` | `{verified:true,context}` | +| `liquid_policy_user_sign` | `{prepared:ContextRequest,signed_pset_hex,secret_key_hex}` | `{pset_hex,signature_hex,public_key_hex,input_index,context}` | +| `liquid_policy_finalize_pset` | `{prepared:ContextRequest,signed_pset_hex,signatures:[{input_index,signature_hex,public_key_hex}]}` | `{transaction_hex,txid,finalized:true,inputs:number,outputs:number}` | + +`signature_hex` is exactly 64 Schnorr signature bytes, without a DEFAULT suffix. +Public keys are x-only hex. All hash fields use raw bytes unless explicitly +named `txid`/`asset_id`, which use their display conventions. Optional keys can +be omitted/null. Unknown request fields are rejected. + +Creation shape: + +```ts +{ + network?: "liquidtestnet", lock_time?: number, + inputs: [{ + txid: string, vout: number, parent_tx_hex: string, + blinding_secret_key_hex?: string, sequence?: number // default 0xfffffffd + }], + outputs: [{address: string, asset_id: string, value_sat: number}] +} +``` + +Use `address:"fee"` exactly once with the network fee asset. Confidential +destination addresses trigger audited `Pset::blind_last`, which generates output +proofs and balances blinding factors. Fully confidential inputs normally require +at least one confidential destination to balance their factors. Unblinding secrets +are browser-local and never retained in the resulting PSET or service request. +Reuse the local funding blinding key to discover/unblind raw parent outputs. + +## Service and restart + +`/simplicity-unchained/policy/liquid/{capabilities,tweak,prepare,evaluate,sign}`. +Capabilities response: + +```ts +{ + profile:"liquid-policy-v1", profile_hash:string, + network:"liquidtestnet"|"liquid", genesis_hash:string, + base_public_key_hex:string, manifest:object, + limits:{max_body_bytes:number,max_blocking_jobs:number}, provenance:string +} +``` + +Tweak request: `{profile_hash,network,program,redeem_script_hex}`; `program` is +standard base64. Response matches local tweak. Transaction requests add +`{pset_hex,input_index,user_pubkey,user_leaf_hash_hex,witness?:string}`. +Prepare/evaluate responses match the local APIs. +Sign response: +`{pset_hex,signature_hex,public_key_hex,input_index,context, +execution_cost_milliweight}`. Both signers must independently reconstruct and +compare the complete context; signatures alone cannot authenticate disclosure +interpretation. Finalization rechecks all proofs and both signatures per input. + +Service configuration gains optional `liquid_network="liquidtestnet"`. It is +independent from Bitcoin's configured network. The existing running binary +cannot expose these routes until rebuilt/restarted. No generated WASM package, +running PID, actual configuration or private key is modified by source work. + +## Reproducible test fixture + +[`tests/fixtures/liquid-policy-v1.json`](tests/fixtures/liquid-policy-v1.json) +contains deterministic, **public test-only** signing/blinding keys, synthetic +parent transactions, source/script/key bindings, fully confidential two-asset +PSET, all input summaries, real service/user signatures, final transaction and +12 negative PSET encodings (rejected during decoding or cryptographic context +verification). Never fund its known keys or unconditional test +program. Its parents are content evidence only, not mined/unspent outputs. + +Exact fixture schema (all fields required; `ContextSummary` is defined above): + +```ts +type FixtureSignature = { + input_index: number; public_key_hex: string; signature_hex: string; +}; +type LiquidPolicyFixture = Record< + | "warning" | "profile" | "profile_hash" | "network" | "source" + | "program_base64" | "witness_base64" | "cmr_hex" | "redeem_script_hex" + | "recovery_script" | "user_leaf_hash_hex" | "base_public_key_hex" + | "user_pubkey" | "blinding_public_key_hex" | "source_parent_tx_hex" + | "source_parent_txid" | "parent_tx_hex" | "parent_txid" | "pset_hex" + | "service_signed_pset_hex" | "fully_signed_pset_hex" + | "transaction_hex" | "txid", string +> & { + contexts: ContextSummary[]; // input-index aligned, exactly two + service_signatures: FixtureSignature[]; // input-index aligned + user_signatures: FixtureSignature[]; // input-index aligned + test_only_secrets: { + base_secret_key_hex: string; user_secret_key_hex: string; + blinding_secret_key_hex: string; + }; + negative_psets: Record< + | "input_amount" | "input_asset" | "output_amount" | "output_asset" + | "missing_parent" | "missing_input_asset_proof" + | "missing_input_value_proof" | "wrong_input_value_proof" + | "missing_output_asset_proof" | "missing_output_value_proof" + | "missing_output_rangeproof" | "missing_output_surjection_proof", string + >; +}; +``` + +Regenerate with the fixed test seed (production creation uses `OsRng`): + +```bash +cargo run --quiet -p simplicity-unchained-policy --example liquid_fixture \ + > policy/tests/fixtures/liquid-policy-v1.json +``` + +Fingerprint: `6097ac61cbd923269edaaed8cf14691cc8ba756e45949f7478f7f907f42d8ce2`. +The Bitcoin golden fingerprint remains +`0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7`. + +The fixture must not be submitted unchanged to a differently keyed live signer: +its funding output commits its test base key. For live-service tests derive a +fresh address from that signer's independently known base key and use the +fixture's explicit source parent to construct a new blinded synthetic funding +transaction, followed by the spending PSET. Creation now returns the unsigned +`transaction_hex` and `txid` for that full-parent construction. Do not substitute +PSET asset labels or wallet-provided values for local unblinding/disclosure proofs. + +The Rust/WASM crate includes a Node-target release integration harness: + +```bash +cargo build --manifest-path ../simplicity-unchained-web-demo/simplicity-lib/Cargo.toml \ + --target wasm32-unknown-unknown --release --locked +wasm-bindgen --target nodejs \ + --out-dir ../simplicity-unchained-web-demo/simplicity-lib/target/liquid-node-validation \ + ../simplicity-unchained-web-demo/simplicity-lib/target/wasm32-unknown-unknown/release/simplicity_lib.wasm +node ../simplicity-unchained-web-demo/simplicity-lib/tests/liquid-policy-node.cjs \ + ../simplicity-unchained-web-demo/simplicity-lib/target/liquid-node-validation/simplicity_lib.js +``` + +If the surrounding Node package is ESM, place `{"type":"commonjs"}` in the isolated +binding directory's `package.json`; do not replace the browser's generated `pkg`. +This executes all 12 real Liquid WASM exports, rechecks both native fixture +signatures, co-signs/finalizes, blinds a fresh two-asset PSET, and rejects the +negative encodings/network/profile/signature mutations. + +## Recorded implementation validation + +Executed from the native repository root; all commands below passed. The four +native and three browser ignored cases are pre-existing. + +| Exact command | Result | +|---|---| +| `cargo test -p cli --test liquid_policy --quiet` | 2 passed; real CLI creation, user co-signing and finalization, independent remote response verification | +| `cargo test -p simplicity-unchained-policy --test liquid bitcoin_profile_funding_script_is_not_reinterpreted_as_liquid_contract --quiet` | 1 passed; Bitcoin-profile funding scripts rejected | +| `TMPDIR="$PWD/target" cargo test --workspace --quiet` | 121 passed, 4 ignored; includes confidential multi-asset kernel, HTTP and CLI roundtrips and proof/value/asset/evidence/signature/approval replay rejection | +| `cargo clippy -p simplicity-unchained-policy -p core -p cli -p service --all-targets --quiet -- -D warnings` | Passed without warnings | +| `cargo check -p simplicity-unchained-policy --target wasm32-unknown-unknown --quiet` | Portable kernel standalone WASM target passed | +| `cargo build -p service --release --locked --quiet` | Release service built | +| `cargo test --manifest-path /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/Cargo.toml --quiet` | 22 passed, 3 ignored; all 66 compiler jets and explicit two-asset budgets covered | +| `cargo clippy --manifest-path /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/Cargo.toml --all-targets --quiet -- -D warnings` | Passed without warnings | +| `cargo build --manifest-path /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/Cargo.toml --target wasm32-unknown-unknown --release --locked --quiet` | Release WASM built | +| `wasm-bindgen --target nodejs --out-dir /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/target/liquid-node-validation /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/target/wasm32-unknown-unknown/release/simplicity_lib.wasm` | Isolated Node bindings generated, browser `pkg` untouched | +| `node /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/tests/liquid-policy-node.cjs /Users/user/Documents/Projects/blockstream/simplicity-unchained-web-demo/simplicity-lib/target/liquid-node-validation/simplicity_lib.js` | All 12 Liquid exports; genuine confidential two-asset roundtrip; all 12 negative proof/label/evidence encodings; signature/network/profile failures; both canonical fingerprints matched | +| `git --no-pager diff --check` | Passed | + +The actual HTTP handlers were exercised in-process; the existing old external +service was not restarted or claimed to support these routes. Once authorized, +the coordinator can preserve its exact existing working directory/configuration +and signing key with: + +```bash +cd /Users/user/Documents/Projects/blockstream/simplicity-unchained/service +kill -TERM 49509 +../target/release/service start --config config.toml +``` + +Run the last command under the coordinator's managed long-lived process facility, +then verify the Liquid capabilities route and its `liquidtestnet` network and +canonical fingerprint before enabling live signing. Regenerate the browser WASM +package from final Rust sources separately; source validation does not modify it. diff --git a/policy/README.md b/policy/README.md new file mode 100644 index 0000000..85d372d --- /dev/null +++ b/policy/README.md @@ -0,0 +1,152 @@ +# Portable policy kernel + +`simplicity-unchained-policy` / `simplicity_unchained_policy` implements the +`bitcoin-policy-v1` off-chain authorization profile used by the `policy` CLI and +Bitcoin policy API. It has no dependency on `core`, the service, native plugins, +a resolver, a clock or secrets. +It builds natively and for `wasm32-unknown-unknown`. + +The [`liquid-policy-v1`](LIQUID.md) module provides confidential multi-asset PSET +support, verified disclosures and a portable VM. The sections below describe +the Bitcoin profile. + +The 55 custom jet identities commit to semantic contracts and standard +Simplicity types under `SimplicityUnchained/PolicyJetIdentity/v1`. These are +application-specific identities, not formally derived Bitcoin consensus jets. + +## Registry and fingerprint + +`Builtin::ALL` is ordered J01–J55, wire IDs 0–54. Methods provide names, standard +`TypeName`s, bit widths, identities, wire encodings, fixed costs, and full +semantic contracts. `manifest()` serializes the base and policy jet metadata, +contracts and limits. Jet codes are: + +```text +0 || CoreExtension code +1 || id:u6, most significant bit first +``` + +IDs 55–63 are reserved/invalid. This is the jet code, without the enclosing +Simplicity node encoding. `TypeName` uses the dependency's standard alphabet: +`1` unit, `2` bit, `c` u8, `s` u16, `i` u32, `l` u64, `h` u256, `+` sum, `*` +product. These encodings are verified against `simplicity-lang` 0.9. + +`profile_hash()` is single SHA256 of `manifest().canonical_bytes()`: compact +UTF-8 serde JSON of `ManifestBody` in declared field order, not generic +alphabetically sorted JSON. The `profile_hash` field is excluded. Jet identities +do not include that resulting fingerprint, preventing circular hashing. +Contracts, ordered types, encodings, CMRs, costs, and all limits are included. +Registry changes require a new profile version. + +Export the complete client registry: + +```sh +cargo run -p simplicity-unchained-policy --example manifest --locked +``` + +## Validated context + +```text +Context::new( + &bitcoin::psbt::Psbt, + input_index: usize, + cmr: [u8; 32], + script_data: &[u8], + configured_network: bitcoin::Network, + base_service_pubkey: bitcoin::XOnlyPublicKey, + user_pubkey: bitcoin::XOnlyPublicKey, + recovery_hash: bitcoin::TapNodeHash, +) -> Result +``` + +The immutable context accepts version exactly 2, 1–8 inputs and outputs, and +output scripts/script data up to 10,000 bytes. Input/output map counts must +match. Every input requires both `non_witness_utxo` and `witness_utxo`; computed +parent txid, vout, and exact referenced TxOut equality are checked. Duplicate +outpoints, money-range violations (including totals), arithmetic failure, +overspend, non-default sighashes, and nonempty final witnesses are rejected. +Only Bitcoin, Testnet, and Testnet4 are accepted; configuration is external. + +Evidence and serialized PSBTs are limited to 2 MiB. The context retains only the +unsigned transaction and verified prevouts. HTTP request decoding, +program/witness limits and execution timeout/admission are the caller's +responsibility. The profile VM admission ceiling is 10,000,000 +milliweight; each policy jet costs 10,000, including bounded scans of at most eight +cached entries and at most 321 total source/target frame bits. + +Parent verification proves **content only**: not confirmation, chain inclusion, +consensus validity, or current unspentness. `ContextSummary` always reports +`provenance = "parent-txid-verified"` and `chain_status_verified = false`. +CSV/CLTV report declared transaction conditions, not chain maturity; RBF reports +explicit signaling, not mempool acceptance. + +`cooperative_output(...)` reconstructs public funding data without a PSBT. +`signing_commitment(cmr, script_data, network)` and +`tweak_public_key(base_key, cmr, script_data, network)` share this derivation. +`Context` exposes only immutable getters for its transaction, verified prevouts, +keys, leaf, scriptPubKey, control block, signing commitment, and actual sighash. +`summary()` is serializable and includes exact output summaries for review. + +## Commitments and approval preimages + +`TaggedHash(tag, m) = SHA256(SHA256(tag) || SHA256(tag) || m)`. +Every hash uses `Hash::to_byte_array`, **never explorer display reversal**. +All fields below are fixed-width 32 bytes except the explicit input index. + +```text +commitment = TaggedHash( + "SimplicityUnchained/PolicySigningCommitment/v1", + profile_hash || genesis_hash || cmr || SHA256(exact script_data)) + +service_key = BIP341 TapTweak(base_service_key, root = commitment) +``` + +The canonical leaf is ` CHECKSIG CHECKSIGADD 2 EQUAL` +(leaf version `0xc0`). The output uses the standard NUMS internal key and a +depth-one cooperative leaf plus a depth-one hidden recovery hash. Every input +must spend this reconstructed output. Signing uses the BIP341 script-path +`SIGHASH_DEFAULT` with all verified prevouts, no annex and the default +code-separator position. + +```text +J54 = TaggedHash("SimplicityUnchained/PolicyTransactionApproval/v1", + profile_hash || genesis_hash || commitment || unsigned_tx_hash || + prevouts_hash || cooperative_leaf_hash || recovery_hash) +J53 = TaggedHash("SimplicityUnchained/PolicyInputApproval/v1", + J54 || input_index:u32-big-endian || actual_default_sighash) +``` + +J54 deliberately permits reuse across all same-contract inputs of this exact +transaction. J53 is input-specific. Sign decoded digest bytes directly with +BIP-340; approvals do not provide a one-use permission registry. + +J44 is single SHA256 of stripped consensus transaction bytes. J45 is single +SHA256 of the consensus TxOut vector in input order. J46/J47 are single SHA256 +of unsigned input/output vectors. **Every vector includes its CompactSize +count.** These differ from BIP341 component hashes. Outpoints use +raw txid bytes and little-endian vout; consensus amounts/sequences/locktimes are +little-endian; only jet words and approval input indices are big-endian. +J55 is stripped byte size, a conservative final-vsize lower bound. + +## Pure execution and vectors + +`Context::evaluate(Builtin, &BitValue)` is independent of VM/FFI. +`BitValue { bytes, bit_len }` packs bits MSB-first with exact byte length and +zero low trailing padding. Products concatenate. Sums contain a tag, zero +padding on the left of the active payload, then that payload. +`Either<(), T>` is `0 || zeroes` or `1 || T`; bool is one actual bit, and `Check` +returns zero-bit unit or an explicit error. Invalid source shapes are errors. + +`tests/fixtures/golden.json` freezes the manifest hash, first/last identities, +raw transaction/vector preimages, public keys/tree, both input sighashes, +J53/J54, and summary. The vectors were independently checked with +Python's hashlib and pure-integer secp256k1 arithmetic, including BIP341 +preimages and the output TapTweak. Tests cover every jet, bounds, sums/options, +accounting, malformed proofs, CSV/CLTV flags and units, and replay mutations. +Synthetic parents do not assert chain validity. + +```sh +cargo test -p simplicity-unchained-policy --locked +cargo clippy -p simplicity-unchained-policy --all-targets --locked -- -D warnings +cargo check -p simplicity-unchained-policy --target wasm32-unknown-unknown --locked +``` diff --git a/policy/examples/liquid_fixture.rs b/policy/examples/liquid_fixture.rs new file mode 100644 index 0000000..24f39d6 --- /dev/null +++ b/policy/examples/liquid_fixture.rs @@ -0,0 +1,97 @@ +//! Generate a public TEST-ONLY fixture. It contains known private keys; never fund it. +#[allow(dead_code)] +mod common { + include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/common/liquid.rs" + )); +} +use common::*; +use serde_json::json; +use simplicity::base64::{Engine, engine::general_purpose::STANDARD}; +use simplicity_unchained_policy::liquid::{ + self as policy, + elements::{ + encode::serialize, + secp256k1_zkp::{PublicKey, Secp256k1}, + }, +}; + +fn main() { + let mut f = Fixture::deterministic(); + let original = f.pset.clone(); + let contexts = (0..2) + .map(|i| f.context(i).unwrap().summary()) + .collect::>(); + let mut service_signatures = Vec::new(); + for index in 0..2 { + let c = f.context(index).unwrap(); + let secret = f.service_secret(); + let sig = policy::add_signature(&c, &mut f.pset, secret, true).unwrap(); + service_signatures.push(json!({"input_index":index,"signature_hex":hex::encode(sig.sig.as_ref()),"public_key_hex":c.cooperative().service_pubkey.to_string()})); + } + let service_signed = hex::encode(serialize(&f.pset)); + let mut user_signatures = Vec::new(); + for index in 0..2 { + let c = f.context(index).unwrap(); + let sig = policy::add_signature(&c, &mut f.pset, f.user_secret, false).unwrap(); + user_signatures.push(json!({"input_index":index,"signature_hex":hex::encode(sig.sig.as_ref()),"public_key_hex":f.user.to_string()})); + } + let tx = policy::finalize(&f.context(0).unwrap(), &f.pset).unwrap(); + let mut negative = serde_json::Map::new(); + for (name, mutation) in [ + ("input_amount", 0), + ("input_asset", 1), + ("missing_input_value_proof", 2), + ("missing_input_asset_proof", 3), + ("missing_parent", 4), + ("output_amount", 5), + ("output_asset", 6), + ("missing_output_value_proof", 7), + ("missing_output_asset_proof", 8), + ("missing_output_rangeproof", 9), + ("missing_output_surjection_proof", 10), + ("wrong_input_value_proof", 11), + ] { + let mut p = original.clone(); + match mutation { + 0 => p.inputs_mut()[0].amount = Some(99_999), + 1 => p.inputs_mut()[0].asset = Some(f.other_asset), + 2 => p.inputs_mut()[0].blind_value_proof = None, + 3 => p.inputs_mut()[0].blind_asset_proof = None, + 4 => p.inputs_mut()[0].non_witness_utxo = None, + 5 => p.outputs_mut()[0].amount = Some(20_001), + 6 => p.outputs_mut()[0].asset = Some(f.other_asset), + 7 => p.outputs_mut()[0].blind_value_proof = None, + 8 => p.outputs_mut()[0].blind_asset_proof = None, + 9 => p.outputs_mut()[0].value_rangeproof = None, + 10 => p.outputs_mut()[0].asset_surjection_proof = None, + _ => { + p.inputs_mut()[0].blind_value_proof = original.inputs()[1].blind_value_proof.clone() + } + } + assert!(f.context_for(&p, 0).is_err()); + negative.insert(name.into(), hex::encode(serialize(&p)).into()); + } + let parent = original.inputs()[0].non_witness_utxo.as_ref().unwrap(); + let fixture = json!({ + "warning":"PUBLIC TEST-ONLY KNOWN PRIVATE KEYS; NEVER FUND; parents are synthetic content evidence, NOT chain inclusion/consensus-validity/unspentness.", + "profile":"liquid-policy-v1","profile_hash":hex::encode(policy::profile_hash()),"network":"liquidtestnet", + "source":"fn main() {}","program_base64":STANDARD.encode(&f.program),"witness_base64":"", + "cmr_hex":hex::encode(f.cmr),"redeem_script_hex":hex::encode(&f.data), + "recovery_script":format!("OP_5 OP_CSV OP_DROP OP_PUSHBYTES_32 {} OP_CHECKSIG",f.user), + "user_leaf_hash_hex":hex::encode(f.recovery.to_byte_array()), + "base_public_key_hex":f.base.to_string(),"user_pubkey":f.user.to_string(), + "blinding_public_key_hex":PublicKey::from_secret_key(&Secp256k1::new(),&f.blinding_secret).to_string(), + "test_only_secrets":{"base_secret_key_hex":hex::encode(f.base_secret.secret_bytes()),"user_secret_key_hex":hex::encode(f.user_secret.secret_bytes()),"blinding_secret_key_hex":hex::encode(f.blinding_secret.secret_bytes())}, + "parent_tx_hex":hex::encode(serialize(parent)),"parent_txid":parent.txid().to_string(), + "source_parent_tx_hex":hex::encode(serialize(&f.source_parent)), + "source_parent_txid":f.source_parent.txid().to_string(), + "pset_hex":hex::encode(serialize(&original)),"contexts":contexts, + "service_signed_pset_hex":service_signed,"service_signatures":service_signatures, + "user_signatures":user_signatures,"fully_signed_pset_hex":hex::encode(serialize(&f.pset)), + "transaction_hex":hex::encode(serialize(&tx)),"txid":tx.txid().to_string(), + "negative_psets":negative, + }); + println!("{}", serde_json::to_string_pretty(&fixture).unwrap()); +} diff --git a/policy/examples/manifest.rs b/policy/examples/manifest.rs new file mode 100644 index 0000000..5624899 --- /dev/null +++ b/policy/examples/manifest.rs @@ -0,0 +1,7 @@ +fn main() { + println!( + "{}", + serde_json::to_string_pretty(&simplicity_unchained_policy::manifest()) + .expect("manifest serialization") + ); +} diff --git a/policy/src/context.rs b/policy/src/context.rs new file mode 100644 index 0000000..9a3df3a --- /dev/null +++ b/policy/src/context.rs @@ -0,0 +1,706 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::str::FromStr; +use std::sync::LazyLock; + +use serde::{Deserialize, Serialize}; +use simplicity::bitcoin::{ + self, Network, Script, ScriptBuf, TapSighashType, Transaction, TxOut, XOnlyPublicKey, + consensus::Encodable, + hashes::{Hash, HashEngine, sha256}, + psbt::{self, Psbt}, + secp256k1::{Scalar, Secp256k1}, + sighash::{Prevouts, SighashCache}, + taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTweakHash, TaprootBuilder}, +}; + +use crate::{ + Error, MAX_INPUTS, MAX_MONEY, MAX_OUTPUTS, MAX_REQUEST_BYTES, MAX_SCRIPT_BYTES, PROFILE_NAME, + profile_hash, +}; + +static NUMS: LazyLock = LazyLock::new(|| { + XOnlyPublicKey::from_str("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0") + .expect("fixed valid NUMS key") +}); + +pub fn network_name(network: Network) -> Result<&'static str, Error> { + match network { + Network::Bitcoin => Ok("bitcoin"), + Network::Testnet => Ok("testnet"), + Network::Testnet4 => Ok("testnet4"), + _ => Err(Error::UnsupportedNetwork), + } +} + +pub fn genesis_hash(network: Network) -> Result<[u8; 32], Error> { + network_name(network)?; + Ok(bitcoin::blockdata::constants::genesis_block(network) + .block_hash() + .to_byte_array()) +} + +pub(crate) fn tagged_hash(tag: &str, fields: &[&[u8]]) -> [u8; 32] { + let tag = sha256::Hash::hash(tag.as_bytes()); + let mut engine = sha256::Hash::engine(); + engine.input(tag.as_byte_array()); + engine.input(tag.as_byte_array()); + for field in fields { + engine.input(field); + } + sha256::Hash::from_engine(engine).to_byte_array() +} + +fn script_data_check(script: &[u8]) -> Result<(), Error> { + if script.len() > MAX_SCRIPT_BYTES { + Err(Error::ScriptDataTooLarge) + } else { + Ok(()) + } +} + +pub fn signing_commitment( + cmr: [u8; 32], + script: &[u8], + network: Network, +) -> Result<[u8; 32], Error> { + script_data_check(script)?; + Ok(tagged_hash( + "SimplicityUnchained/PolicySigningCommitment/v1", + &[ + &profile_hash(), + &genesis_hash(network)?, + &cmr, + sha256::Hash::hash(script).as_byte_array(), + ], + )) +} + +pub fn tweak_public_key( + base_key: XOnlyPublicKey, + cmr: [u8; 32], + script: &[u8], + network: Network, +) -> Result { + let commitment = signing_commitment(cmr, script, network)?; + let tweak = + TapTweakHash::from_key_and_tweak(base_key, Some(TapNodeHash::from_byte_array(commitment))); + let scalar = Scalar::from_be_bytes(tweak.to_byte_array()).map_err(|_| Error::Taproot)?; + let (key, _) = base_key + .add_tweak(&Secp256k1::verification_only(), &scalar) + .map_err(|_| Error::Taproot)?; + Ok(key) +} + +/// Public funding reconstruction; contains no secrets or unverified PSBT fields. +#[derive(Clone, Debug)] +pub struct CooperativeOutput { + pub service_pubkey: XOnlyPublicKey, + pub script: ScriptBuf, + pub script_pubkey: ScriptBuf, + pub control_block: ControlBlock, + pub leaf_hash: TapLeafHash, +} + +pub fn cooperative_output( + base_key: XOnlyPublicKey, + cmr: [u8; 32], + script_data: &[u8], + network: Network, + user_pubkey: XOnlyPublicKey, + recovery_hash: TapNodeHash, +) -> Result { + let secp = Secp256k1::verification_only(); + let service_pubkey = tweak_public_key(base_key, cmr, script_data, network)?; + let script = bitcoin::script::Builder::new() + .push_x_only_key(&service_pubkey) + .push_opcode(bitcoin::opcodes::all::OP_CHECKSIG) + .push_x_only_key(&user_pubkey) + .push_opcode(bitcoin::opcodes::all::OP_CHECKSIGADD) + .push_int(2) + .push_opcode(bitcoin::opcodes::all::OP_EQUAL) + .into_script(); + let spend = TaprootBuilder::new() + .add_leaf(1, script.clone()) + .map_err(|_| Error::Taproot)? + .add_hidden_node(1, recovery_hash) + .map_err(|_| Error::Taproot)? + .finalize(&secp, *NUMS) + .map_err(|_| Error::Taproot)?; + let control_block = spend + .control_block(&(script.clone(), LeafVersion::TapScript)) + .ok_or(Error::Taproot)?; + let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript); + Ok(CooperativeOutput { + service_pubkey, + script_pubkey: ScriptBuf::new_p2tr_tweaked(spend.output_key()), + script, + control_block, + leaf_hash, + }) +} + +/// Only this validating module can construct or modify a context. +/// +/// Full parent proofs and arbitrary PSBT metadata are deliberately not retained. +#[derive(Clone, Debug)] +pub struct Context { + tx: Transaction, + prevouts: Vec, + input_index: usize, + network: Network, + cmr: [u8; 32], + script_data: Vec, + commitment: [u8; 32], + cooperative: CooperativeOutput, + user_pubkey: XOnlyPublicKey, + sighash: [u8; 32], + approval_hash: [u8; 32], + transaction_approval_hash: [u8; 32], + legacy_approval_hash: [u8; 32], + pub(crate) genesis_hash: [u8; 32], + pub(crate) script_data_hash: [u8; 32], + pub(crate) input_script_hashes: Vec<[u8; 32]>, + pub(crate) output_script_hashes: Vec<[u8; 32]>, + pub(crate) total_input_value: u64, + pub(crate) total_output_value: u64, + pub(crate) fee: u64, + pub(crate) returned_value: u64, + pub(crate) debit: u64, + pub(crate) unsigned_tx_hash: [u8; 32], + pub(crate) prevouts_hash: [u8; 32], + pub(crate) tx_inputs_hash: [u8; 32], + pub(crate) tx_outputs_hash: [u8; 32], + pub(crate) recovery_hash: TapNodeHash, + pub(crate) vsize_lower_bound: u32, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ContextSummary { + pub profile: String, + pub profile_hash: String, + pub network: String, + pub genesis_hash: String, + pub cmr: String, + pub signing_commitment: String, + pub redeem_script_hash: String, + pub input_index: u32, + pub input_count: u32, + pub output_count: u32, + pub unsigned_tx_hash: String, + pub prevouts_hash: String, + pub cooperative_leaf_hash: String, + pub cooperative_sighash_default: String, + pub policy_approval_hash: String, + pub policy_transaction_approval_hash: String, + pub service_pubkey: String, + pub user_pubkey: String, + pub recovery_leaf_hash: String, + pub total_input_value: u64, + pub total_output_value: u64, + pub fee: u64, + pub debit: u64, + pub vsize_lower_bound: u32, + pub script_pubkey_hex: String, + pub provenance: String, + pub chain_status_verified: bool, + pub outputs: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct OutputSummary { + pub index: u32, + pub value: u64, + pub script_pubkey_hex: String, + pub script_hash: String, + pub script_class: u8, +} + +impl Context { + #[allow(clippy::too_many_arguments)] + pub fn new( + psbt: &Psbt, + input_index: usize, + cmr: [u8; 32], + script: &[u8], + network: Network, + base_service_pubkey: XOnlyPublicKey, + user_pubkey: XOnlyPublicKey, + recovery_hash: TapNodeHash, + ) -> Result { + let genesis_hash = genesis_hash(network)?; + script_data_check(script)?; + let tx = &psbt.unsigned_tx; + if tx.version.0 != 2 { + return Err(Error::InvalidVersion); + } + if !(1..=MAX_INPUTS).contains(&tx.input.len()) + || !(1..=MAX_OUTPUTS).contains(&tx.output.len()) + { + return Err(Error::InvalidShape); + } + if psbt.inputs.len() != tx.input.len() || psbt.outputs.len() != tx.output.len() { + return Err(Error::MapCountMismatch); + } + if input_index >= tx.input.len() { + return Err(Error::InputIndex(input_index)); + } + for (index, output) in tx.output.iter().enumerate() { + if output.script_pubkey.len() > MAX_SCRIPT_BYTES { + return Err(Error::OutputScriptTooLarge(index)); + } + } + let mut outpoints = BTreeSet::new(); + for (index, input) in tx.input.iter().enumerate() { + if !outpoints.insert(input.previous_output) { + return Err(Error::DuplicateOutpoint(index)); + } + if !input.script_sig.is_empty() || !input.witness.is_empty() { + return Err(Error::NonUnsignedInput(index)); + } + } + check_evidence_size(psbt)?; + let cooperative = cooperative_output( + base_service_pubkey, + cmr, + script, + network, + user_pubkey, + recovery_hash, + )?; + let mut parents: BTreeMap = BTreeMap::new(); + let mut prevouts = Vec::with_capacity(tx.input.len()); + let mut total_input_value = 0u64; + for (index, (input, map)) in tx.input.iter().zip(&psbt.inputs).enumerate() { + if map + .sighash_type + .is_some_and(|s| s.taproot_hash_ty() != Ok(TapSighashType::Default)) + || map + .tap_key_sig + .is_some_and(|s| s.sighash_type != TapSighashType::Default) + || map + .tap_script_sigs + .values() + .any(|s| s.sighash_type != TapSighashType::Default) + { + return Err(Error::NonDefaultSighash(index)); + } + if map.final_script_sig.as_ref().is_some_and(|s| !s.is_empty()) { + return Err(Error::FinalScriptSig(index)); + } + if map + .final_script_witness + .as_ref() + .is_some_and(|w| !w.is_empty()) + { + return Err(Error::FinalWitness(index)); + } + let parent = map + .non_witness_utxo + .as_ref() + .ok_or(Error::MissingParent(index))?; + let witness = map + .witness_utxo + .as_ref() + .ok_or(Error::MissingWitnessUtxo(index))?; + let known_parent = parents.get(&input.previous_output.txid); + if known_parent.is_none_or(|&known| known != parent) { + if parent.compute_txid() != input.previous_output.txid { + return Err(Error::ParentTxidMismatch(index)); + } + parents.insert(input.previous_output.txid, parent); + } + let verified = parent + .output + .get(input.previous_output.vout as usize) + .ok_or(Error::ParentVout(index))?; + if verified != witness { + return Err(Error::WitnessUtxoMismatch(index)); + } + if verified.script_pubkey != cooperative.script_pubkey { + return Err(Error::FundingScriptMismatch(index)); + } + total_input_value = money_add(total_input_value, verified.value.to_sat())?; + prevouts.push(verified.clone()); + } + let total_output_value = tx + .output + .iter() + .try_fold(0u64, |sum, output| money_add(sum, output.value.to_sat()))?; + let fee = total_input_value + .checked_sub(total_output_value) + .ok_or(Error::Overspend)?; + let returned_value = tx + .output + .iter() + .filter(|o| o.script_pubkey == cooperative.script_pubkey) + .try_fold(0u64, |sum, output| money_add(sum, output.value.to_sat()))?; + let debit = total_input_value + .checked_sub(returned_value) + .ok_or(Error::Arithmetic)?; + let input_script_hashes = prevouts + .iter() + .map(|o| sha256_bytes(o.script_pubkey.as_bytes())) + .collect(); + let output_script_hashes = tx + .output + .iter() + .map(|o| sha256_bytes(o.script_pubkey.as_bytes())) + .collect(); + let unsigned_tx_hash = consensus_hash(tx); + let prevouts_hash = consensus_hash(&prevouts); + let tx_inputs_hash = consensus_hash(&tx.input); + let tx_outputs_hash = consensus_hash(&tx.output); + let commitment = signing_commitment(cmr, script, network)?; + let sighash = SighashCache::new(tx) + .taproot_script_spend_signature_hash( + input_index, + &Prevouts::All(&prevouts), + cooperative.leaf_hash, + TapSighashType::Default, + ) + .map_err(|_| Error::Sighash)? + .to_byte_array(); + let transaction_approval_hash = tagged_hash( + "SimplicityUnchained/PolicyTransactionApproval/v1", + &[ + &profile_hash(), + &genesis_hash, + &commitment, + &unsigned_tx_hash, + &prevouts_hash, + cooperative.leaf_hash.as_byte_array(), + recovery_hash.as_byte_array(), + ], + ); + let approval_hash = tagged_hash( + "SimplicityUnchained/PolicyInputApproval/v1", + &[ + &transaction_approval_hash, + &(input_index as u32).to_be_bytes(), + &sighash, + ], + ); + let legacy_commitment = tagged_hash( + "SimplicityUnchained/SigningCommitment/v1", + &[&cmr, &sha256_bytes(script)], + ); + let network_id = match network { + Network::Bitcoin => 0, + Network::Testnet => 1, + Network::Testnet4 => 2, + _ => return Err(Error::UnsupportedNetwork), + }; + let legacy_approval_hash = tagged_hash( + "SimplicityUnchained/BitcoinApproval/v1", + &[ + &[network_id], + &(input_index as u32).to_be_bytes(), + &legacy_commitment, + &unsigned_tx_hash, + &prevouts_hash, + ], + ); + let vsize_lower_bound = u32::try_from(tx.base_size()).map_err(|_| Error::Arithmetic)?; + Ok(Self { + tx: tx.clone(), + prevouts, + input_index, + network, + cmr, + script_data: script.to_vec(), + commitment, + cooperative, + user_pubkey, + sighash, + approval_hash, + transaction_approval_hash, + legacy_approval_hash, + genesis_hash, + script_data_hash: sha256_bytes(script), + input_script_hashes, + output_script_hashes, + total_input_value, + total_output_value, + fee, + returned_value, + debit, + unsigned_tx_hash, + prevouts_hash, + tx_inputs_hash, + tx_outputs_hash, + recovery_hash, + vsize_lower_bound, + }) + } + + pub fn tx(&self) -> &Transaction { + &self.tx + } + pub fn prevouts(&self) -> &[TxOut] { + &self.prevouts + } + pub fn input_index(&self) -> usize { + self.input_index + } + pub fn network(&self) -> Network { + self.network + } + pub fn cmr(&self) -> [u8; 32] { + self.cmr + } + pub fn script_data(&self) -> &[u8] { + &self.script_data + } + pub fn signing_commitment(&self) -> [u8; 32] { + self.commitment + } + pub fn service_pubkey(&self) -> XOnlyPublicKey { + self.cooperative.service_pubkey + } + pub fn user_pubkey(&self) -> XOnlyPublicKey { + self.user_pubkey + } + pub fn cooperative_script(&self) -> &ScriptBuf { + &self.cooperative.script + } + pub fn script_pubkey(&self) -> &ScriptBuf { + &self.cooperative.script_pubkey + } + pub fn control_block(&self) -> &ControlBlock { + &self.cooperative.control_block + } + pub fn cooperative_leaf_hash(&self) -> TapLeafHash { + self.cooperative.leaf_hash + } + pub fn cooperative_sighash_default(&self) -> [u8; 32] { + self.sighash + } + pub fn sighash(&self) -> [u8; 32] { + self.sighash + } + pub fn policy_approval_hash(&self) -> [u8; 32] { + self.approval_hash + } + pub fn policy_transaction_approval_hash(&self) -> [u8; 32] { + self.transaction_approval_hash + } + pub fn legacy_approval_hash(&self) -> [u8; 32] { + self.legacy_approval_hash + } + pub fn unsigned_tx_hash(&self) -> [u8; 32] { + self.unsigned_tx_hash + } + pub fn prevouts_hash(&self) -> [u8; 32] { + self.prevouts_hash + } + pub fn tx_inputs_hash(&self) -> [u8; 32] { + self.tx_inputs_hash + } + pub fn tx_outputs_hash(&self) -> [u8; 32] { + self.tx_outputs_hash + } + + pub fn summary(&self) -> ContextSummary { + ContextSummary { + profile: PROFILE_NAME.to_owned(), + profile_hash: hex::encode(profile_hash()), + network: network_name(self.network) + .expect("validated network") + .to_owned(), + genesis_hash: hex::encode(self.genesis_hash), + cmr: hex::encode(self.cmr), + signing_commitment: hex::encode(self.commitment), + redeem_script_hash: hex::encode(self.script_data_hash), + input_index: self.input_index as u32, + input_count: self.tx.input.len() as u32, + output_count: self.tx.output.len() as u32, + unsigned_tx_hash: hex::encode(self.unsigned_tx_hash), + prevouts_hash: hex::encode(self.prevouts_hash), + cooperative_leaf_hash: hex::encode(self.cooperative.leaf_hash.to_byte_array()), + cooperative_sighash_default: hex::encode(self.sighash), + policy_approval_hash: hex::encode(self.approval_hash), + policy_transaction_approval_hash: hex::encode(self.transaction_approval_hash), + service_pubkey: self.service_pubkey().to_string(), + user_pubkey: self.user_pubkey.to_string(), + recovery_leaf_hash: hex::encode(self.recovery_hash.to_byte_array()), + total_input_value: self.total_input_value, + total_output_value: self.total_output_value, + fee: self.fee, + debit: self.debit, + vsize_lower_bound: self.vsize_lower_bound, + script_pubkey_hex: hex::encode(self.cooperative.script_pubkey.as_bytes()), + provenance: "parent-txid-verified".to_owned(), + chain_status_verified: false, + outputs: self + .tx + .output + .iter() + .enumerate() + .map(|(index, output)| OutputSummary { + index: index as u32, + value: output.value.to_sat(), + script_pubkey_hex: hex::encode(output.script_pubkey.as_bytes()), + script_hash: hex::encode(self.output_script_hashes[index]), + script_class: script_class(&output.script_pubkey), + }) + .collect(), + } + } +} + +fn money_add(sum: u64, value: u64) -> Result { + if value > MAX_MONEY { + return Err(Error::MoneyRange); + } + let result = sum.checked_add(value).ok_or(Error::Arithmetic)?; + if result > MAX_MONEY { + Err(Error::MoneyRange) + } else { + Ok(result) + } +} + +fn sha256_bytes(data: &[u8]) -> [u8; 32] { + sha256::Hash::hash(data).to_byte_array() +} + +fn consensus_hash(value: &T) -> [u8; 32] { + let mut engine = sha256::Hash::engine(); + value + .consensus_encode(&mut engine) + .expect("hash-engine writing"); + sha256::Hash::from_engine(engine).to_byte_array() +} + +pub(crate) fn script_class(script: &Script) -> u8 { + if script.is_p2pkh() { + 1 + } else if script.is_p2sh() { + 2 + } else if script.is_p2wpkh() { + 3 + } else if script.is_p2wsh() { + 4 + } else if script.is_p2tr() { + 5 + } else if script.is_op_return() { + 6 + } else { + 0 + } +} + +// Bound raw payloads before rust-bitcoin serializes each PSBT map into a buffer. +// The final counting writer enforces the exact serialized bound. +fn check_evidence_size(psbt: &Psbt) -> Result<(), Error> { + let mut size = Admission(0); + size.add(psbt.unsigned_tx.total_size())?; + size.add(psbt.xpub.len().saturating_mul(78))?; + for (_, path) in psbt.xpub.values() { + size.add(path.len().saturating_mul(4))?; + } + size.raw_maps(&psbt.unknown, &psbt.proprietary)?; + for input in &psbt.inputs { + if let Some(parent) = &input.non_witness_utxo { + size.add(parent.total_size())?; + } + if let Some(output) = &input.witness_utxo { + size.add(output.size())?; + } + for script in [ + &input.redeem_script, + &input.witness_script, + &input.final_script_sig, + ] + .into_iter() + .flatten() + { + size.add(script.len())?; + } + if let Some(witness) = &input.final_script_witness { + size.add(witness.size())?; + } + size.add(input.partial_sigs.len().saturating_mul(64))?; + size.add(input.tap_script_sigs.len().saturating_mul(128))?; + for (_, path) in input.bip32_derivation.values() { + size.add(33 + path.len().saturating_mul(4))?; + } + for (hashes, (_, path)) in input.tap_key_origins.values() { + size.add(32 + hashes.len().saturating_mul(32) + path.len().saturating_mul(4))?; + } + for (control, (script, _)) in &input.tap_scripts { + size.add(control.size() + script.len())?; + } + for value in input + .ripemd160_preimages + .values() + .chain(input.sha256_preimages.values()) + .chain(input.hash160_preimages.values()) + .chain(input.hash256_preimages.values()) + { + size.add(32 + value.len())?; + } + size.raw_maps(&input.unknown, &input.proprietary)?; + } + for output in &psbt.outputs { + for script in [&output.redeem_script, &output.witness_script] + .into_iter() + .flatten() + { + size.add(script.len())?; + } + for (_, path) in output.bip32_derivation.values() { + size.add(33 + path.len().saturating_mul(4))?; + } + for (hashes, (_, path)) in output.tap_key_origins.values() { + size.add(32 + hashes.len().saturating_mul(32) + path.len().saturating_mul(4))?; + } + if let Some(tree) = &output.tap_tree { + for leaf in tree.script_leaves() { + size.add(2 + leaf.script().len())?; + } + } + size.raw_maps(&output.unknown, &output.proprietary)?; + } + psbt.serialize_to_writer(&mut Admission(0)) + .map_err(|_| Error::EvidenceTooLarge)?; + Ok(()) +} + +struct Admission(usize); + +impl Admission { + fn add(&mut self, bytes: usize) -> Result<(), Error> { + self.0 = self.0.checked_add(bytes).ok_or(Error::EvidenceTooLarge)?; + if self.0 > MAX_REQUEST_BYTES { + Err(Error::EvidenceTooLarge) + } else { + Ok(()) + } + } + + fn raw_maps( + &mut self, + unknown: &BTreeMap>, + proprietary: &BTreeMap>, + ) -> Result<(), Error> { + for (key, value) in unknown { + self.add(3 + key.key.len() + value.len())?; + } + for (key, value) in proprietary { + self.add(5 + key.prefix.len() + key.key.len() + value.len())?; + } + Ok(()) + } +} + +impl bitcoin::io::Write for Admission { + fn write(&mut self, buf: &[u8]) -> Result { + self.add(buf.len()) + .map_err(|_| bitcoin::io::Error::from(bitcoin::io::ErrorKind::Other))?; + Ok(buf.len()) + } + fn flush(&mut self) -> Result<(), bitcoin::io::Error> { + Ok(()) + } +} diff --git a/policy/src/jets.rs b/policy/src/jets.rs new file mode 100644 index 0000000..8febe0a --- /dev/null +++ b/policy/src/jets.rs @@ -0,0 +1,380 @@ +use std::{fmt, str::FromStr}; + +use simplicity::jet::type_name::TypeName; + +use crate::{BitValue, Context, Error, profile_hash}; + +macro_rules! catalogue { + ($( $variant:ident, $name:literal, $source:literal, $target:literal, $contract:literal; )*) => { + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(u8)] + pub enum Builtin { $( $variant, )* } + + impl Builtin { + pub const ALL: [Self; 55] = [$( Self::$variant, )*]; + + pub fn from_id(id: u8) -> Option { + Self::ALL.get(usize::from(id)).copied() + } + + pub const fn id(self) -> u8 { self as u8 } + + pub const fn name(self) -> &'static str { + match self { $( Self::$variant => $name, )* } + } + + pub const fn source_type(self) -> TypeName { + TypeName(match self { $( Self::$variant => $source, )* }) + } + + pub const fn target_type(self) -> TypeName { + TypeName(match self { $( Self::$variant => $target, )* }) + } + + pub const fn semantic_contract(self) -> &'static str { + match self { $( Self::$variant => $contract, )* } + } + } + }; +} + +catalogue! { + PolicyProfileHash, "policy_profile_hash", b"1", b"h", + "Return the single-SHA256 fingerprint of the ordered canonical manifest body, excluding only its profile_hash field. No jet identity includes this resulting hash, avoiding circular hashing."; + PolicyCmr, "policy_cmr", b"1", b"h", + "Return the 32-byte CMR of the exact policy program being evaluated, supplied before context reconstruction."; + SigningCommitmentHash, "signing_commitment_hash", b"1", b"h", + "Return TaggedHash('SimplicityUnchained/PolicySigningCommitment/v1', profile_hash || configured genesis_hash || program_cmr || SHA256(exact script_data)). All four fields are 32 bytes."; + RedeemScriptHash, "redeem_script_hash", b"1", b"h", + "Return single SHA256 of exact script_data bytes, including an empty script. This does not claim that script_data is the hidden recovery script."; + NetworkGenesisHash, "network_genesis_hash", b"1", b"h", + "Return the configured Bitcoin/Testnet/Testnet4 genesis block hash using Hash::to_byte_array, never explorer-display byte reversal. No caller network label or chain state is read."; + CurrentIndex, "current_index", b"1", b"i", + "Return the validated zero-based input index being authorized as a big-endian unsigned 32-bit word."; + Version, "version", b"1", b"i", + "Return the transaction version's raw 32-bit bits, big-endian. Context construction requires version exactly 2."; + TxLockTime, "tx_lock_time", b"1", b"i", + "Return raw transaction nLockTime as a big-endian unsigned 32-bit word. This is not the current time or height."; + NumInputs, "num_inputs", b"1", b"i", + "Return the actual transaction input count, never a truncated or padded count. Valid contexts have 1..=8."; + NumOutputs, "num_outputs", b"1", b"i", + "Return the actual transaction output count, never a truncated or padded count. Valid contexts have 1..=8."; + InputPrevOutpoint, "input_prev_outpoint", b"i", b"+1*hi", + "For an in-range zero-based input index return Some(parent txid Hash::to_byte_array || vout:u32 big-endian); otherwise None. The hash is not explorer-display hex order."; + InputValue, "input_value", b"i", b"+1l", + "Return Some(content-verified parent TxOut value in integer satoshis) at an in-range input index; otherwise None. Required parent/witness proof absence is a construction error."; + InputScriptHash, "input_script_hash", b"i", b"+1h", + "Return Some(single SHA256 of the verified parent's raw scriptPubKey) at an in-range input index; otherwise None."; + InputScriptSize, "input_script_size", b"i", b"+1i", + "Return Some(verified parent scriptPubKey byte length as u32) at an in-range input index; otherwise None."; + InputScriptByte, "input_script_byte", b"*ii", b"+1c", + "Read (input_index:u32, byte_offset:u32). Return Some(raw verified parent scriptPubKey byte), including pushed data, or None if either index is out of bounds. This is not an instruction parser."; + InputSequence, "input_sequence", b"i", b"+1i", + "Return Some(raw nSequence:u32 including all flags) at an in-range input index; otherwise None."; + CurrentPrevOutpoint, "current_prev_outpoint", b"1", b"*hi", + "Return input_prev_outpoint at the validated current input index without an option tag. It is an error to construct a missing current input."; + CurrentValue, "current_value", b"1", b"l", + "Return input_value at the validated current input index without an option tag."; + CurrentScriptHash, "current_script_hash", b"1", b"h", + "Return input_script_hash at the validated current input index without an option tag."; + CurrentSequence, "current_sequence", b"1", b"i", + "Return input_sequence at the validated current input index without an option tag."; + OutputValue, "output_value", b"i", b"+1l", + "Return Some(output value in integer satoshis) at an in-range output index; otherwise None."; + OutputScriptHash, "output_script_hash", b"i", b"+1h", + "Return Some(single SHA256 of raw output scriptPubKey) at an in-range output index; otherwise None."; + OutputScriptSize, "output_script_size", b"i", b"+1i", + "Return Some(output scriptPubKey byte length as u32) at an in-range output index; otherwise None."; + OutputScriptByte, "output_script_byte", b"*ii", b"+1c", + "Read (output_index:u32, byte_offset:u32). Return Some(raw scriptPubKey byte), including pushed data, or None if either index is out of bounds. This is not an instruction parser."; + OutputScriptClass, "output_script_class", b"i", b"+1c", + "Return Some(u8 class) at an in-range output index, else None. Exact standard byte patterns: Other=0; P2PKH=1 (76 a9 14 [20] 88 ac); P2SH=2 (a9 14 [20] 87); P2WPKH=3 (00 14 [20]); P2WSH=4 (00 20 [32]); P2TR=5 (51 20 [32]); leading 6a OP_RETURN=6. Unknown witness versions/program lengths are Other. Classes do not establish spendability."; + TotalInputValue, "total_input_value", b"1", b"l", + "Return the checked sum of all content-verified input values. Individual values and the total must be within 0..=2100000000000000 satoshis."; + TotalOutputValue, "total_output_value", b"1", b"l", + "Return the checked sum of all output values. Individual values and the total must be within 0..=2100000000000000 satoshis."; + TxFee, "tx_fee", b"1", b"l", + "Return checked total_input_value minus total_output_value, in satoshis. Overspend is a context-construction error, never a wrapped or clamped fee."; + InputValueForScript, "input_value_for_script", b"h", b"l", + "Return the checked sum of verified input values whose raw scriptPubKey single-SHA256 equals the supplied 256-bit hash. Scan at most 8 inputs. No matches returns zero."; + OutputValueForScript, "output_value_for_script", b"h", b"l", + "Return the checked sum of output values whose raw scriptPubKey single-SHA256 equals the supplied 256-bit hash. Scan at most 8 outputs. No matches returns zero."; + OutputCountForScript, "output_count_for_script", b"h", b"i", + "Return the number of outputs whose raw scriptPubKey single-SHA256 equals the supplied 256-bit hash, as u32. Scan at most 8 outputs. No matches returns zero."; + ValueReturnedToCurrentScript, "value_returned_to_current_script", b"1", b"l", + "Return the checked sum of outputs with exact byte-for-byte scriptPubKey equality to the current verified prevout. Never use change labels, PSBT metadata, or hash equality as an ownership assertion."; + CurrentScriptDebit, "current_script_debit", b"1", b"l", + "Return checked sum of ALL funding inputs with the current exact script minus value_returned_to_current_script, including fees. All funding inputs must spend this same reconstructed output. This is not current_value minus change and is not mixed-wallet accounting."; + IsRbfSignaled, "is_rbf_signaled", b"1", b"2", + "Return one actual bit, true iff any input nSequence is strictly below 0xfffffffe. This reports explicit BIP-125 signaling only, not mempool replacement acceptance."; + HasEffectiveLockTime, "has_effective_lock_time", b"1", b"2", + "Return one actual bit, true iff raw nLockTime is nonzero and at least one input nSequence differs from 0xffffffff. This is not a current-input CLTV check or a maturity proof."; + InputLockBlocks, "input_lock_blocks", b"i", b"+1s", + "For an in-range input, return Some(nSequence & 0x0000ffff) iff transaction version >=2, disable bit0x80000000 is unset and type bit0x00400000 is unset; otherwise None, including out-of-range. Unrelated reserved sequence bits are ignored."; + InputLockIntervals, "input_lock_intervals", b"i", b"+1s", + "For an in-range input, return Some(nSequence & 0x0000ffff) iff transaction version >=2, disable bit0x80000000 is unset and type bit0x00400000 is set; otherwise None, including out-of-range. Units are 512-second intervals. Reserved bits are ignored."; + CurrentLockBlocks, "current_lock_blocks", b"1", b"+1s", + "Return input_lock_blocks at the validated current index, with the same None behavior for disabled or wrong-unit relative locks."; + CurrentLockIntervals, "current_lock_intervals", b"1", b"+1s", + "Return input_lock_intervals at the validated current index, with the same None behavior for disabled or wrong-unit relative locks."; + CheckCsvBlocks, "check_csv_blocks", b"s", b"1", + "Succeed with unit iff current input has an enabled version>=2 block-relative sequence and (nSequence & 0xffff) >= requested u16 block delay. Otherwise explicitly fail. Disable flag fails even for requested zero; time units fail. Does not prove maturity."; + CheckCsvIntervals, "check_csv_intervals", b"s", b"1", + "Succeed with unit iff current input has an enabled version>=2 time-relative sequence and (nSequence & 0xffff) >= requested u16 512-second delay. Otherwise explicitly fail. Disable flag fails even for requested zero; block units fail. Does not prove maturity."; + CheckCltvHeight, "check_cltv_height", b"i", b"1", + "Succeed with unit iff requested u32 <500000000, transaction nLockTime <500000000 and >=request, and CURRENT input nSequence !=0xffffffff. Otherwise explicitly fail. Other nonfinal inputs do not satisfy the current-input condition. No live height is read."; + CheckCltvTime, "check_cltv_time", b"i", b"1", + "Succeed with unit iff requested u32 >=500000000, transaction nLockTime >=500000000 and >=request, and CURRENT input nSequence !=0xffffffff. Otherwise explicitly fail. No live time is read."; + UnsignedTxHash, "unsigned_tx_hash", b"1", b"h", + "Return single SHA256 of Bitcoin consensus encoding: version:i32 little-endian || CompactSize(input_count) || each outpoint(txid Hash::to_byte_array,vout:u32 little-endian), empty scriptSig CompactSize0, sequence:u32 little-endian || CompactSize(output_count) || each value:u64 little-endian, CompactSize(script length), raw scriptPubKey || nLockTime:u32 little-endian. No segwit marker, flag, or witnesses."; + PrevoutsHash, "prevouts_hash", b"1", b"h", + "Return single SHA256 of consensus-encoded TxOut vector in input order: CompactSize(input_count) || each verified value:u64 little-endian, CompactSize(script length), raw scriptPubKey. This is not a BIP-341 component hash."; + TxInputsHash, "tx_inputs_hash", b"1", b"h", + "Return single SHA256 of consensus-encoded unsigned input vector: CompactSize(input_count) || each txid Hash::to_byte_array, vout:u32 little-endian, CompactSize0 empty scriptSig, sequence:u32 little-endian. No witness/marker/flag. This is not BIP-341 sha_prevouts/sha_sequences."; + TxOutputsHash, "tx_outputs_hash", b"1", b"h", + "Return single SHA256 of consensus-encoded output vector: CompactSize(output_count) || each value:u64 little-endian, CompactSize(script length), raw scriptPubKey. The count is included, unlike BIP-341 sha_outputs."; + CooperativeLeafHash, "cooperative_leaf_hash", b"1", b"h", + "Return actual BIP-341 TapLeafHash of canonical tapscript CHECKSIG CHECKSIGADD 2 EQUAL, with leaf version0xc0. Not a dummy Simplicity execution leaf."; + CooperativeSighashDefault, "cooperative_sighash_default", b"1", b"h", + "Return actual BIP-341 script-path SIGHASH_DEFAULT for the selected input, unsigned transaction, ALL parent-verified prevouts and canonical cooperative leaf. No annex; code-separator position0xffffffff; key version0. This exact digest is signed after policy success."; + CooperativeUserPubkey, "cooperative_user_pubkey", b"1", b"h", + "Return the validated 32-byte x-only user public key embedded in the reconstructed cooperative leaf."; + CooperativeServicePubkey, "cooperative_service_pubkey", b"1", b"h", + "Return the profile-tweaked 32-byte x-only service public key embedded in the reconstructed cooperative leaf. No secret key is present in the context or a jet."; + RecoveryLeafHash, "recovery_leaf_hash", b"1", b"h", + "Return the supplied 32-byte hidden recovery TapNodeHash, authenticated by reconstructing the two-depth1-leaf tree and requiring ALL funding scriptPubKeys to equal that output. It does not reveal the recovery script."; + PolicyApprovalHash, "policy_approval_hash", b"1", b"h", + "Return TaggedHash('SimplicityUnchained/PolicyInputApproval/v1', policy_transaction_approval_hash || input_index:u32 big-endian || cooperative_sighash_default). Fields are32+4+32 bytes. Excludes approval signatures; differs across inputs."; + PolicyTransactionApprovalHash, "policy_transaction_approval_hash", b"1", b"h", + "Return TaggedHash('SimplicityUnchained/PolicyTransactionApproval/v1', profile_hash || genesis_hash || signing_commitment_hash || unsigned_tx_hash || prevouts_hash || cooperative_leaf_hash || recovery_leaf_hash), seven32-byte fields in that order. Excludes input index and approval signatures; deliberately reusable across same-contract inputs of this exact transaction."; + TxVsizeLowerBound, "tx_vsize_lower_bound", b"1", b"i", + "Return stripped unsigned transaction consensus byte length as u32. This is a conservative lower bound on final virtual size, not an upper estimate or final fee rate. A conservative rate cap uses checked fee <= rate*lower_bound."; +} + +impl Builtin { + pub fn source_bits(self) -> usize { + self.source_type().to_bit_width() + } + + pub fn target_bits(self) -> usize { + self.target_type().to_bit_width() + } + + pub fn cmr(self) -> [u8; 32] { + crate::manifest::builtin_identity(self) + } + + pub const fn cost_milliweight(self) -> u32 { + 10_000 + } + + pub fn encoding(self) -> String { + format!("1{:06b}", self.id()) + } +} + +impl fmt::Display for Builtin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} + +impl FromStr for Builtin { + type Err = Error; + + fn from_str(name: &str) -> Result { + Self::ALL + .into_iter() + .find(|jet| jet.name() == name) + .ok_or_else(|| Error::UnknownJet(name.to_owned())) + } +} + +impl Context { + /// Evaluate a pure policy extension on the immutable validated context. + pub fn evaluate(&self, jet: Builtin, source: &BitValue) -> Result { + use Builtin::*; + source.validate(jet.source_bits())?; + let index = || source.uint(0, 32) as usize; + let offset = || source.uint(32, 32) as usize; + let word32 = |value: u32| BitValue::word(u64::from(value), 32); + let word64 = |value: u64| BitValue::word(value, 64); + let hash = BitValue::hash; + let input = || self.tx().input.get(index()); + let prevout = || self.prevouts().get(index()); + let output = || self.tx().output.get(index()); + let outpoint = |input: &simplicity::bitcoin::TxIn| { + use simplicity::bitcoin::hashes::Hash; + hash(input.previous_output.txid.to_byte_array()) + .concat(&word32(input.previous_output.vout)) + }; + let current = &self.tx().input[self.input_index()]; + let read_hash = || { + source + .bytes + .as_slice() + .try_into() + .expect("validated 256-bit frame") + }; + let check = |success| { + if success { + Ok(BitValue::unit()) + } else { + Err(Error::CheckFailed) + } + }; + let result = match jet { + PolicyProfileHash => hash(profile_hash()), + PolicyCmr => hash(self.cmr()), + SigningCommitmentHash => hash(self.signing_commitment()), + RedeemScriptHash => hash(self.script_data_hash), + NetworkGenesisHash => hash(self.genesis_hash), + CurrentIndex => word32(self.input_index() as u32), + Version => word32(self.tx().version.0 as u32), + TxLockTime => word32(self.tx().lock_time.to_consensus_u32()), + NumInputs => word32(self.tx().input.len() as u32), + NumOutputs => word32(self.tx().output.len() as u32), + InputPrevOutpoint => BitValue::option(input().map(outpoint), 288), + InputValue => BitValue::option(prevout().map(|o| word64(o.value.to_sat())), 64), + InputScriptHash => BitValue::option( + self.input_script_hashes.get(index()).copied().map(hash), + 256, + ), + InputScriptSize => { + BitValue::option(prevout().map(|o| word32(o.script_pubkey.len() as u32)), 32) + } + InputScriptByte => BitValue::option( + prevout() + .and_then(|o| o.script_pubkey.as_bytes().get(offset())) + .map(|&b| BitValue::word(u64::from(b), 8)), + 8, + ), + InputSequence => BitValue::option(input().map(|i| word32(i.sequence.0)), 32), + CurrentPrevOutpoint => outpoint(current), + CurrentValue => word64(self.prevouts()[self.input_index()].value.to_sat()), + CurrentScriptHash => hash(self.input_script_hashes[self.input_index()]), + CurrentSequence => word32(current.sequence.0), + OutputValue => BitValue::option(output().map(|o| word64(o.value.to_sat())), 64), + OutputScriptHash => BitValue::option( + self.output_script_hashes.get(index()).copied().map(hash), + 256, + ), + OutputScriptSize => { + BitValue::option(output().map(|o| word32(o.script_pubkey.len() as u32)), 32) + } + OutputScriptByte => BitValue::option( + output() + .and_then(|o| o.script_pubkey.as_bytes().get(offset())) + .map(|&b| BitValue::word(u64::from(b), 8)), + 8, + ), + OutputScriptClass => BitValue::option( + output().map(|o| { + BitValue::word(u64::from(crate::context::script_class(&o.script_pubkey)), 8) + }), + 8, + ), + TotalInputValue => word64(self.total_input_value), + TotalOutputValue => word64(self.total_output_value), + TxFee => word64(self.fee), + InputValueForScript => word64(self.value_for_script(true, read_hash())?), + OutputValueForScript => word64(self.value_for_script(false, read_hash())?), + OutputCountForScript => { + let wanted = read_hash(); + word32( + self.output_script_hashes + .iter() + .filter(|&&h| h == wanted) + .count() as u32, + ) + } + ValueReturnedToCurrentScript => word64(self.returned_value), + CurrentScriptDebit => word64(self.debit), + IsRbfSignaled => BitValue::word( + u64::from(self.tx().input.iter().any(|i| i.sequence.0 < 0xffff_fffe)), + 1, + ), + HasEffectiveLockTime => BitValue::word( + u64::from( + self.tx().lock_time.to_consensus_u32() != 0 + && self.tx().input.iter().any(|i| i.sequence.0 != u32::MAX), + ), + 1, + ), + InputLockBlocks | InputLockIntervals => BitValue::option( + self.relative_lock(index(), jet == InputLockIntervals) + .map(|v| BitValue::word(u64::from(v), 16)), + 16, + ), + CurrentLockBlocks | CurrentLockIntervals => BitValue::option( + self.relative_lock(self.input_index(), jet == CurrentLockIntervals) + .map(|v| BitValue::word(u64::from(v), 16)), + 16, + ), + CheckCsvBlocks | CheckCsvIntervals => { + return check( + self.relative_lock(self.input_index(), jet == CheckCsvIntervals) + .is_some_and(|delay| u64::from(delay) >= source.uint(0, 16)), + ); + } + CheckCltvHeight | CheckCltvTime => { + let requested = source.uint(0, 32) as u32; + let actual = self.tx().lock_time.to_consensus_u32(); + let time_units = jet == CheckCltvTime; + return check( + (requested >= 500_000_000) == time_units + && (actual >= 500_000_000) == time_units + && actual >= requested + && current.sequence.0 != u32::MAX, + ); + } + UnsignedTxHash => hash(self.unsigned_tx_hash), + PrevoutsHash => hash(self.prevouts_hash), + TxInputsHash => hash(self.tx_inputs_hash), + TxOutputsHash => hash(self.tx_outputs_hash), + CooperativeLeafHash => { + use simplicity::bitcoin::hashes::Hash; + hash(self.cooperative_leaf_hash().to_byte_array()) + } + CooperativeSighashDefault => hash(self.cooperative_sighash_default()), + CooperativeUserPubkey => hash(self.user_pubkey().serialize()), + CooperativeServicePubkey => hash(self.service_pubkey().serialize()), + RecoveryLeafHash => { + use simplicity::bitcoin::hashes::Hash; + hash(self.recovery_hash.to_byte_array()) + } + PolicyApprovalHash => hash(self.policy_approval_hash()), + PolicyTransactionApprovalHash => hash(self.policy_transaction_approval_hash()), + TxVsizeLowerBound => word32(self.vsize_lower_bound), + }; + debug_assert_eq!(result.bit_len, jet.target_bits()); + Ok(result) + } + + fn relative_lock(&self, index: usize, time_units: bool) -> Option { + let seq = self.tx().input.get(index)?.sequence.0; + (self.tx().version.0 >= 2 + && seq & 0x8000_0000 == 0 + && (seq & 0x0040_0000 != 0) == time_units) + .then_some((seq & 0xffff) as u16) + } + + fn value_for_script(&self, inputs: bool, wanted: [u8; 32]) -> Result { + let (hashes, outputs) = if inputs { + (&self.input_script_hashes, self.prevouts()) + } else { + (&self.output_script_hashes, self.tx().output.as_slice()) + }; + hashes + .iter() + .zip(outputs) + .try_fold(0u64, |sum, (hash, output)| { + if hash == &wanted { + sum.checked_add(output.value.to_sat()) + .ok_or(Error::Arithmetic) + } else { + Ok(sum) + } + }) + } +} diff --git a/policy/src/lib.rs b/policy/src/lib.rs new file mode 100644 index 0000000..5454c61 --- /dev/null +++ b/policy/src/lib.rs @@ -0,0 +1,86 @@ +//! Portable public transaction context for the experimental `bitcoin-policy-v1` profile. +//! +//! These are application-specific, off-chain extensions, not Bitcoin consensus jets. +//! Parent transaction verification authenticates content, not confirmation or unspentness. + +mod context; +mod jets; +pub mod liquid; +mod manifest; +mod value; + +pub use context::{ + Context, ContextSummary, CooperativeOutput, OutputSummary, cooperative_output, genesis_hash, + network_name, signing_commitment, tweak_public_key, +}; +pub use jets::Builtin; +pub use manifest::{ + JetMetadata, Limits, Manifest, ManifestBody, PROFILE_NAME, manifest, profile_hash, +}; +pub use simplicity::bitcoin; +pub use value::BitValue; + +pub const MAX_INPUTS: usize = 8; +pub const MAX_OUTPUTS: usize = 8; +pub const MAX_SCRIPT_BYTES: usize = 10_000; +pub const MAX_REQUEST_BYTES: usize = 2 * 1024 * 1024; +pub const MAX_EXECUTION_COST_MILLIWEIGHT: u32 = 10_000_000; +pub const MAX_MONEY: u64 = 21_000_000 * 100_000_000; + +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum Error { + #[error("unsupported Bitcoin network")] + UnsupportedNetwork, + #[error("script data exceeds the 10000-byte profile limit")] + ScriptDataTooLarge, + #[error("PSBT or parent evidence exceeds the 2 MiB profile limit")] + EvidenceTooLarge, + #[error("transaction must have version 2")] + InvalidVersion, + #[error("transaction must have 1..=8 inputs and 1..=8 outputs")] + InvalidShape, + #[error("PSBT input or output maps do not match the unsigned transaction")] + MapCountMismatch, + #[error("input index {0} is out of range")] + InputIndex(usize), + #[error("input {0} has a duplicate outpoint")] + DuplicateOutpoint(usize), + #[error("input {0} has a scriptSig or witness in the unsigned transaction")] + NonUnsignedInput(usize), + #[error("input {0} is missing non_witness_utxo")] + MissingParent(usize), + #[error("input {0} is missing witness_utxo")] + MissingWitnessUtxo(usize), + #[error("input {0} parent transaction id does not match the outpoint")] + ParentTxidMismatch(usize), + #[error("input {0} parent output index is out of range")] + ParentVout(usize), + #[error("input {0} witness_utxo differs from the verified parent output")] + WitnessUtxoMismatch(usize), + #[error("input {0} does not spend the reconstructed cooperative output")] + FundingScriptMismatch(usize), + #[error("input {0} requests a non-default sighash")] + NonDefaultSighash(usize), + #[error("input {0} has a nonempty finalized scriptSig")] + FinalScriptSig(usize), + #[error("input {0} has finalized witness data (annex/overrides are unsupported)")] + FinalWitness(usize), + #[error("output {0} script exceeds the 10000-byte profile limit")] + OutputScriptTooLarge(usize), + #[error("amount or total is outside Bitcoin's money range")] + MoneyRange, + #[error("arithmetic overflow")] + Arithmetic, + #[error("transaction spends more than its verified inputs")] + Overspend, + #[error("invalid Taproot reconstruction")] + Taproot, + #[error("could not construct SIGHASH_DEFAULT")] + Sighash, + #[error("invalid source frame: expected {expected} bits")] + SourceShape { expected: usize }, + #[error("policy check rejected")] + CheckFailed, + #[error("unknown policy jet {0}")] + UnknownJet(String), +} diff --git a/policy/src/liquid/context.rs b/policy/src/liquid/context.rs new file mode 100644 index 0000000..bc1656c --- /dev/null +++ b/policy/src/liquid/context.rs @@ -0,0 +1,687 @@ +use std::{ + collections::{BTreeMap, BTreeSet}, + str::FromStr, +}; + +use elements::{ + AddressParams, AssetId, BlockHash, SchnorrSighashType, Script, Transaction, TxOut, + confidential::{Asset, Value}, + encode::serialize, + hashes::sha256, + pset::PartiallySignedTransaction as Pset, + secp256k1_zkp::{Message, Scalar, Secp256k1, XOnlyPublicKey}, + sighash::{Prevouts, SighashCache}, + taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTweakHash, TaprootBuilder}, +}; +use serde::{Deserialize, Serialize}; + +use super::{Error, PROFILE_NAME, profile_hash, require}; +use crate::{MAX_INPUTS, MAX_OUTPUTS, MAX_REQUEST_BYTES, MAX_SCRIPT_BYTES}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum Network { + #[serde(rename = "liquid")] + Liquid, + #[default] + #[serde(rename = "liquidtestnet")] + LiquidTestnet, +} + +impl FromStr for Network { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "liquid" => Ok(Self::Liquid), + "liquidtestnet" => Ok(Self::LiquidTestnet), + _ => Err(Error::Invalid(format!( + "unsupported Liquid policy network: {s}" + ))), + } + } +} + +impl Network { + pub fn name(self) -> &'static str { + match self { + Self::Liquid => "liquid", + Self::LiquidTestnet => "liquidtestnet", + } + } + pub fn address_params(self) -> &'static AddressParams { + match self { + Self::Liquid => &AddressParams::LIQUID, + Self::LiquidTestnet => &AddressParams::LIQUID_TESTNET, + } + } + pub fn genesis_hash(self) -> BlockHash { + let params = match self { + Self::Liquid => elements::genesis::NetworkParams::liquidv1(), + Self::LiquidTestnet => elements::genesis::NetworkParams::liquidtestnet(), + }; + elements::genesis::genesis_block(¶ms).block_hash() + } + pub fn fee_asset(self) -> AssetId { + AssetId::from_str(match self { + Self::Liquid => "6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d", + Self::LiquidTestnet => { + "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49" + } + }) + .expect("fixed network asset") + } +} + +pub fn signing_commitment( + cmr: [u8; 32], + script: &[u8], + network: Network, +) -> Result<[u8; 32], Error> { + require( + script.len() <= MAX_SCRIPT_BYTES, + "script data exceeds 10000 bytes", + )?; + Ok(crate::context::tagged_hash( + "SimplicityUnchained/LiquidPolicySigningCommitment/v1", + &[ + &profile_hash(), + network.genesis_hash().as_byte_array(), + &cmr, + sha256::Hash::hash(script).as_byte_array(), + ], + )) +} + +pub fn tweak_public_key( + base: XOnlyPublicKey, + cmr: [u8; 32], + script: &[u8], + network: Network, +) -> Result { + let tweak = service_tweak(base, cmr, script, network)?; + base.add_tweak(&Secp256k1::verification_only(), &tweak) + .map(|(key, _)| key) + .map_err(|e| Error::Invalid(e.to_string())) +} + +pub fn service_tweak( + base: XOnlyPublicKey, + cmr: [u8; 32], + script: &[u8], + network: Network, +) -> Result { + Scalar::from_be_bytes( + TapTweakHash::from_key_and_tweak( + base, + Some(TapNodeHash::from_byte_array(signing_commitment( + cmr, script, network, + )?)), + ) + .to_byte_array(), + ) + .map_err(|e| Error::Invalid(e.to_string())) +} + +#[derive(Clone, Debug)] +pub struct CooperativeOutput { + pub service_pubkey: XOnlyPublicKey, + pub script: Script, + pub script_pubkey: Script, + pub control_block: ControlBlock, + pub leaf_hash: TapLeafHash, +} + +pub fn cooperative_output( + base: XOnlyPublicKey, + cmr: [u8; 32], + data: &[u8], + network: Network, + user: XOnlyPublicKey, + recovery: TapNodeHash, +) -> Result { + let service_pubkey = tweak_public_key(base, cmr, data, network)?; + let script = elements::script::Builder::new() + .push_slice(&service_pubkey.serialize()) + .push_opcode(elements::opcodes::all::OP_CHECKSIG) + .push_slice(&user.serialize()) + .push_opcode(elements::opcodes::all::OP_CHECKSIGADD) + .push_int(2) + .push_opcode(elements::opcodes::all::OP_EQUAL) + .into_script(); + let secp = Secp256k1::verification_only(); + let nums = XOnlyPublicKey::from_str( + "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", + ) + .expect("fixed NUMS"); + let spend = TaprootBuilder::new() + .add_leaf(1, script.clone()) + .map_err(|e| Error::Invalid(e.to_string()))? + .add_hidden(1, recovery) + .map_err(|e| Error::Invalid(e.to_string()))? + .finalize(&secp, nums) + .map_err(|_| Error::Invalid("incomplete cooperative tree".into()))?; + let control_block = spend + .control_block(&(script.clone(), LeafVersion::default())) + .ok_or_else(|| Error::Invalid("missing cooperative control block".into()))?; + Ok(CooperativeOutput { + service_pubkey, + script_pubkey: Script::new_v1_p2tr_tweaked(spend.output_key()), + leaf_hash: TapLeafHash::from_script(&script, LeafVersion::default()), + script, + control_block, + }) +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Disclosure { + /// Explorer/display order; jets use the asset ID's raw inner hash bytes. + pub asset_id: String, + pub value: u64, + pub confidential_asset: bool, + pub confidential_value: bool, +} + +impl Disclosure { + pub fn asset(&self) -> AssetId { + AssetId::from_str(&self.asset_id).expect("validated asset") + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetAccounting { + pub asset_id: String, + pub total_input_value: u64, + /// Includes explicit fee outputs. Input and output totals must be exactly equal. + pub total_output_value: u64, + pub fee: u64, + pub returned_value: u64, + pub debit: u64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ContextSummary { + pub profile: String, + pub profile_hash: String, + pub network: String, + pub genesis_hash: String, + pub cmr: String, + pub signing_commitment: String, + pub redeem_script_hash: String, + pub input_index: u32, + pub input_count: u32, + pub output_count: u32, + pub unsigned_tx_hash: String, + pub prevouts_hash: String, + pub disclosures_hash: String, + pub cooperative_leaf_hash: String, + pub cooperative_sighash_default: String, + pub policy_approval_hash: String, + pub policy_transaction_approval_hash: String, + pub service_pubkey: String, + pub user_pubkey: String, + pub recovery_leaf_hash: String, + pub current_asset_id: String, + pub fee_asset_id: String, + pub assets: Vec, + pub inputs: Vec, + pub outputs: Vec, + pub output_scripts_hex: Vec, + pub script_pubkey_hex: String, + pub vsize_lower_bound: u32, + pub provenance: String, + pub chain_status_verified: bool, +} + +#[derive(Clone, Debug)] +pub struct Context { + pub(crate) tx: Transaction, + pub(crate) prevouts: Vec, + pub(crate) inputs: Vec, + pub(crate) outputs: Vec, + pub(crate) accounting: BTreeMap, + pub(crate) index: usize, + pub(crate) network: Network, + pub(crate) cmr: [u8; 32], + pub(crate) data: Vec, + pub(crate) cooperative: CooperativeOutput, + pub(crate) user: XOnlyPublicKey, + pub(crate) base: XOnlyPublicKey, + pub(crate) recovery: TapNodeHash, + pub(crate) sighash: [u8; 32], + pub(crate) tx_hash: [u8; 32], + pub(crate) prevouts_hash: [u8; 32], + pub(crate) disclosures_hash: [u8; 32], + pub(crate) transaction_approval: [u8; 32], + pub(crate) approval: [u8; 32], +} + +/// Authenticates a disclosure using exact-value and single-asset proof APIs. +/// No blinding factors or receiving private keys enter the signing context. +pub fn verify_disclosure( + out: &TxOut, + amount: Option, + asset: Option, + value_proof: Option<&elements::RangeProof>, + asset_proof: Option<&elements::SurjectionProof>, +) -> Result { + let secp = Secp256k1::new(); + let id = match out.asset { + Asset::Explicit(id) => { + require( + asset.is_none_or(|a| a == id), + "explicit asset label mismatch", + )?; + require( + asset_proof.is_none(), + "unexpected explicit asset disclosure proof", + )?; + id + } + Asset::Confidential(generator) => { + let id = asset + .ok_or_else(|| Error::Invalid("missing confidential asset disclosure".into()))?; + require( + asset_proof + .is_some_and(|proof| proof.blind_asset_proof_verify(&secp, id, generator)), + "invalid or missing confidential asset disclosure proof", + )?; + id + } + Asset::Null => return Err(Error::Invalid("null asset".into())), + }; + let generator = match out.asset { + Asset::Explicit(id) => { + elements::secp256k1_zkp::Generator::new_unblinded(&secp, id.into_tag()) + } + Asset::Confidential(generator) => generator, + Asset::Null => return Err(Error::Invalid("null asset".into())), + }; + let value = match out.value { + Value::Explicit(value) => { + require( + amount.is_none_or(|a| a == value), + "explicit amount label mismatch", + )?; + require( + value_proof.is_none(), + "unexpected explicit value disclosure proof", + )?; + value + } + Value::Confidential(commit) => { + let value = amount + .ok_or_else(|| Error::Invalid("missing confidential value disclosure".into()))?; + require(value != u64::MAX, "value cannot be u64::MAX")?; + require( + value_proof.is_some_and(|proof| { + proof.blind_value_proof_verify(&secp, value, generator, commit) + }), + "invalid or missing confidential value disclosure proof", + )?; + out.witness + .rangeproof + .as_ref() + .ok_or_else(|| Error::Invalid("missing transaction rangeproof".into()))? + .verify(&secp, commit, out.script_pubkey.as_bytes(), generator) + .map_err(|e| Error::Invalid(format!("invalid transaction rangeproof: {e}")))?; + value + } + Value::Null => return Err(Error::Invalid("null value".into())), + }; + require(value < u64::MAX, "value cannot be u64::MAX")?; + Ok(Disclosure { + asset_id: id.to_string(), + value, + confidential_asset: out.asset.is_confidential(), + confidential_value: out.value.is_confidential(), + }) +} + +impl Context { + #[allow(clippy::too_many_arguments)] + pub fn new( + pset: &Pset, + index: usize, + cmr: [u8; 32], + data: &[u8], + network: Network, + base: XOnlyPublicKey, + user: XOnlyPublicKey, + recovery: TapNodeHash, + ) -> Result { + require( + serialize(pset).len() <= MAX_REQUEST_BYTES, + "PSET/evidence exceeds 2 MiB", + )?; + let tx = pset + .extract_tx() + .map_err(|e| Error::Invalid(e.to_string()))?; + require(tx.version == 2, "transaction version must be 2")?; + require( + (1..=MAX_INPUTS).contains(&tx.input.len()) + && (1..=MAX_OUTPUTS).contains(&tx.output.len()), + "transaction requires 1..=8 inputs and outputs", + )?; + require(index < tx.input.len(), "input index out of range")?; + let cooperative = cooperative_output(base, cmr, data, network, user, recovery)?; + let mut seen = BTreeSet::new(); + let mut prevouts = Vec::new(); + let mut inputs = Vec::new(); + for (i, (input, map)) in tx.input.iter().zip(pset.inputs()).enumerate() { + require( + seen.insert(input.previous_output), + format!("duplicate outpoint at input {i}"), + )?; + require( + !input.is_pegin && !input.has_issuance(), + "pegins/issuance are unsupported", + )?; + require( + input.script_sig.is_empty() && input.witness.is_empty(), + "unsigned input has scriptSig/witness", + )?; + require( + map.final_script_sig.as_ref().is_none_or(|s| s.is_empty()) + && map + .final_script_witness + .as_ref() + .is_none_or(|w| w.is_empty()), + "finalized input unsupported", + )?; + require( + map.sighash_type.is_none_or(|s| s.to_u32() == 0) && map.tap_key_sig.is_none(), + "only cooperative SIGHASH_DEFAULT is allowed", + )?; + let parent = map.non_witness_utxo.as_ref().ok_or_else(|| { + Error::Invalid(format!("input {i} missing full parent transaction")) + })?; + require( + parent.txid() == input.previous_output.txid, + format!("input {i} parent txid mismatch"), + )?; + let out = parent + .output + .get(input.previous_output.vout as usize) + .ok_or_else(|| Error::Invalid(format!("input {i} parent vout out of bounds")))?; + let witness = map + .witness_utxo + .as_ref() + .ok_or_else(|| Error::Invalid(format!("input {i} missing witness_utxo")))?; + // PSET witness_utxo serialization excludes output witness. Authenticate its base + // fields against the parent, and use the full parent's rangeproof for verification. + require( + serialize(out) == serialize(witness), + format!("input {i} witness_utxo mismatch"), + )?; + require( + out.script_pubkey == cooperative.script_pubkey, + format!("input {i} funding script/profile/network mismatch"), + )?; + if let Some(proof) = &map.in_utxo_rangeproof { + require( + out.witness.rangeproof.as_ref() == proof.as_ref(), + "input UTXO rangeproof differs from parent", + )?; + } + inputs.push(verify_disclosure( + out, + map.amount, + map.asset, + map.blind_value_proof.as_ref(), + map.blind_asset_proof.as_ref(), + )?); + prevouts.push(out.clone()); + } + let mut outputs = Vec::new(); + let mut fee_count = 0; + for (out, map) in tx.output.iter().zip(pset.outputs()) { + require( + out.script_pubkey.len() <= MAX_SCRIPT_BYTES, + "output script exceeds 10000 bytes", + )?; + let disclosed = verify_disclosure( + out, + map.amount, + map.asset, + map.blind_value_proof.as_ref(), + map.blind_asset_proof.as_ref(), + )?; + if out.script_pubkey.is_empty() { + fee_count += 1; + require( + out.is_fee() && disclosed.asset() == network.fee_asset() && out.nonce.is_null(), + "fee must be explicit configured policy asset", + )?; + } else { + require( + disclosed.value > 0, + "nonfee output must have positive value", + )?; + } + outputs.push(disclosed); + } + require( + fee_count == 1, + "exactly one explicit policy-asset fee output is required", + )?; + tx.verify_tx_amt_proofs(&Secp256k1::new(), &prevouts) + .map_err(|e| { + Error::Invalid(format!( + "transaction commitment/proof verification failed: {e}" + )) + })?; + let mut accounting: BTreeMap = BTreeMap::new(); + for d in &inputs { + let a = accounting + .entry(d.asset()) + .or_insert_with(|| AssetAccounting { + asset_id: d.asset_id.clone(), + ..Default::default() + }); + a.total_input_value = a + .total_input_value + .checked_add(d.value) + .ok_or_else(|| Error::Invalid("input total overflow".into()))?; + } + for (out, d) in tx.output.iter().zip(&outputs) { + let a = accounting + .entry(d.asset()) + .or_insert_with(|| AssetAccounting { + asset_id: d.asset_id.clone(), + ..Default::default() + }); + a.total_output_value = a + .total_output_value + .checked_add(d.value) + .ok_or_else(|| Error::Invalid("output total overflow".into()))?; + if out.is_fee() { + a.fee = d.value; + } + if out.script_pubkey == cooperative.script_pubkey { + a.returned_value = a + .returned_value + .checked_add(d.value) + .ok_or_else(|| Error::Invalid("returned total overflow".into()))?; + } + } + for a in accounting.values_mut() { + require( + a.total_input_value == a.total_output_value, + format!("asset {} is not conserved", a.asset_id), + )?; + a.debit = a + .total_input_value + .checked_sub(a.returned_value) + .ok_or_else(|| Error::Invalid("asset debit underflow".into()))?; + } + let tx_hash = sha256::Hash::hash(&serialize(&tx)).to_byte_array(); + let prevouts_hash = sha256::Hash::hash(&serialize(&prevouts)).to_byte_array(); + let mut disclosure_bytes = Vec::new(); + for list in [&inputs, &outputs] { + disclosure_bytes.extend_from_slice(&(list.len() as u32).to_be_bytes()); + for d in list { + disclosure_bytes.extend_from_slice(&d.asset().to_byte_array()); + disclosure_bytes.extend_from_slice(&d.value.to_be_bytes()); + disclosure_bytes.push(u8::from(d.confidential_asset)); + disclosure_bytes.push(u8::from(d.confidential_value)); + } + } + let disclosures_hash = sha256::Hash::hash(&disclosure_bytes).to_byte_array(); + let sighash = SighashCache::new(&tx) + .taproot_script_spend_signature_hash( + index, + &Prevouts::All(&prevouts), + cooperative.leaf_hash, + SchnorrSighashType::Default, + network.genesis_hash(), + ) + .map_err(|e| Error::Invalid(e.to_string()))? + .to_byte_array(); + let transaction_approval = crate::context::tagged_hash( + "SimplicityUnchained/LiquidPolicyTransactionApproval/v1", + &[ + &profile_hash(), + network.genesis_hash().as_byte_array(), + &signing_commitment(cmr, data, network)?, + &tx_hash, + &prevouts_hash, + &disclosures_hash, + cooperative.leaf_hash.as_byte_array(), + recovery.as_byte_array(), + ], + ); + let approval = crate::context::tagged_hash( + "SimplicityUnchained/LiquidPolicyInputApproval/v1", + &[ + &transaction_approval, + &(index as u32).to_be_bytes(), + &sighash, + ], + ); + let context = Self { + tx, + prevouts, + inputs, + outputs, + accounting, + index, + network, + cmr, + data: data.to_vec(), + cooperative, + user, + base, + recovery, + sighash, + tx_hash, + prevouts_hash, + disclosures_hash, + transaction_approval, + approval, + }; + context.verify_signatures(pset)?; + Ok(context) + } + + /// Reject every pre-existing signature that is not valid for this exact context. + pub fn verify_signatures(&self, pset: &Pset) -> Result<(), Error> { + let secp = Secp256k1::verification_only(); + for (index, input) in pset.inputs().iter().enumerate() { + require( + input.partial_sigs.is_empty(), + "non-Taproot signatures unsupported", + )?; + for ((key, leaf), sig) in &input.tap_script_sigs { + require( + (*key == self.cooperative.service_pubkey || *key == self.user) + && *leaf == self.cooperative.leaf_hash + && sig.hash_ty == SchnorrSighashType::Default, + "unrecognized signature key/leaf/sighash", + )?; + let digest = SighashCache::new(&self.tx) + .taproot_script_spend_signature_hash( + index, + &Prevouts::All(&self.prevouts), + self.cooperative.leaf_hash, + SchnorrSighashType::Default, + self.network.genesis_hash(), + ) + .map_err(|e| Error::Invalid(e.to_string()))?; + secp.verify_schnorr(&sig.sig, &Message::from_digest(digest.to_byte_array()), key) + .map_err(|e| Error::Invalid(format!("invalid input {index} signature: {e}")))?; + } + for (control, (script, version)) in &input.tap_scripts { + require( + control == &self.cooperative.control_block + && script == &self.cooperative.script + && *version == LeafVersion::default(), + "unrecognized cooperative leaf/control block", + )?; + } + } + Ok(()) + } + + pub fn tx(&self) -> &Transaction { + &self.tx + } + pub fn revalidate(&self, pset: &Pset) -> Result<(), Error> { + let fresh = Self::new( + pset, + self.index, + self.cmr, + &self.data, + self.network, + self.base, + self.user, + self.recovery, + )?; + require( + fresh.summary() == self.summary(), + "PSET changed after preparation", + ) + } + pub fn prevouts(&self) -> &[TxOut] { + &self.prevouts + } + pub fn input_index(&self) -> usize { + self.index + } + pub fn cooperative(&self) -> &CooperativeOutput { + &self.cooperative + } + pub fn sighash(&self) -> [u8; 32] { + self.sighash + } + pub fn user_pubkey(&self) -> XOnlyPublicKey { + self.user + } + pub fn network(&self) -> Network { + self.network + } + pub fn cmr(&self) -> [u8; 32] { + self.cmr + } + pub fn script_data(&self) -> &[u8] { + &self.data + } + + pub fn summary(&self) -> ContextSummary { + ContextSummary { + profile: PROFILE_NAME.into(), profile_hash: hex::encode(profile_hash()), network: self.network.name().into(), + genesis_hash: hex::encode(self.network.genesis_hash().to_byte_array()), cmr: hex::encode(self.cmr), + signing_commitment: hex::encode(signing_commitment(self.cmr, &self.data, self.network).expect("validated data")), + redeem_script_hash: sha256::Hash::hash(&self.data).to_string(), input_index: self.index as u32, input_count: self.inputs.len() as u32, output_count: self.outputs.len() as u32, + unsigned_tx_hash: hex::encode(self.tx_hash), prevouts_hash: hex::encode(self.prevouts_hash), disclosures_hash: hex::encode(self.disclosures_hash), + cooperative_leaf_hash: hex::encode(self.cooperative.leaf_hash.to_byte_array()), cooperative_sighash_default: hex::encode(self.sighash), + policy_approval_hash: hex::encode(self.approval), policy_transaction_approval_hash: hex::encode(self.transaction_approval), + service_pubkey: self.cooperative.service_pubkey.to_string(), user_pubkey: self.user.to_string(), recovery_leaf_hash: hex::encode(self.recovery.to_byte_array()), + current_asset_id: self.inputs[self.index].asset_id.clone(), fee_asset_id: self.network.fee_asset().to_string(), + assets: self.accounting.values().cloned().collect(), inputs: self.inputs.clone(), outputs: self.outputs.clone(), + output_scripts_hex: self.tx.output.iter().map(|o| hex::encode(o.script_pubkey.as_bytes())).collect(), + script_pubkey_hex: hex::encode(self.cooperative.script_pubkey.as_bytes()), + vsize_lower_bound: self.vsize_lower_bound(), + provenance: "parent-txid/vout + exact witness-utxo; exact-value and single-asset disclosure proofs; transaction range/surjection proofs + Pedersen balance; per-asset checked conservation; existing signatures verified; NOT chain inclusion/confirmation/consensus-validity/unspentness".into(), + chain_status_verified: false, + } + } +} diff --git a/policy/src/liquid/execution.rs b/policy/src/liquid/execution.rs new file mode 100644 index 0000000..55027a2 --- /dev/null +++ b/policy/src/liquid/execution.rs @@ -0,0 +1,221 @@ +//! Portable bounded VM; host-specific filesystem/dynamic loading is not involved. +use super::{Builtin, Context, Error, Network, require}; +use crate::BitValue; +use elements::{ + pset::PartiallySignedTransaction as Pset, secp256k1_zkp::XOnlyPublicKey, taproot::TapNodeHash, +}; +use simplicity::{ + BitIter, BitMachine, BitWriter, Cmr, CommitNode, Cost, RedeemNode, decode, + ffi::{ + CFrameItem, + c_jets::frame_ffi::{c_readBit, c_writeBit}, + }, + jet::{Core, CoreEnv, Jet, JetEnvironment, type_name::TypeName}, +}; +use std::{io::Write, str::FromStr, sync::Arc}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PolicyJet { + Core(Core), + Builtin(Builtin), +} +impl Jet for PolicyJet { + fn cmr(&self) -> Cmr { + match self { + Self::Core(j) => j.cmr(), + Self::Builtin(j) => Cmr::from_byte_array(j.cmr()), + } + } + fn source_ty(&self) -> TypeName { + match self { + Self::Core(j) => j.source_ty(), + Self::Builtin(j) => j.source_type(), + } + } + fn target_ty(&self) -> TypeName { + match self { + Self::Core(j) => j.target_ty(), + Self::Builtin(j) => j.target_type(), + } + } + fn cost(&self) -> Cost { + match self { + Self::Core(j) => j.cost(), + Self::Builtin(j) => Cost::from_milliweight(j.cost_milliweight()), + } + } + fn encode(&self, w: &mut BitWriter<&mut dyn Write>) -> std::io::Result { + match self { + Self::Core(j) => { + w.write_bit(false)?; + j.encode(w).map(|n| n + 1) + } + Self::Builtin(j) => w.write_bits_be(128 | u64::from(j.id()), 8), + } + } + fn decode>(bits: &mut BitIter) -> Result { + if !bits.next().ok_or(decode::Error::EndOfStream)? { + return Core::decode(bits).map(Self::Core); + } + let mut id = 0; + for _ in 0..7 { + id = (id << 1) | u8::from(bits.next().ok_or(decode::Error::EndOfStream)?); + } + Builtin::from_id(id) + .map(Self::Builtin) + .ok_or(decode::Error::InvalidJet) + } + fn parse(name: &str) -> Result { + name.parse() + } +} +impl std::fmt::Display for PolicyJet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Core(j) => j.fmt(f), + Self::Builtin(j) => j.fmt(f), + } + } +} +impl FromStr for PolicyJet { + type Err = simplicity::Error; + fn from_str(name: &str) -> Result { + if let Ok(j) = name.parse::() { + Ok(Self::Builtin(j)) + } else { + name.parse().map(Self::Core) + } + } +} + +struct Env { + context: Context, + core: CoreEnv, +} +type JetFn = fn(&mut CFrameItem, CFrameItem, &Env) -> bool; +fn core(dst: &mut CFrameItem, src: CFrameItem, env: &Env) -> bool { + CoreEnv::c_jet_ptr(&Core::ALL[ID])(dst, src, env.core.c_jet_env()) +} +macro_rules! core_functions { + (@c $a:literal; $b:literal; $($c:literal)*) => { [$(core::<{ $a * 64 + $b * 8 + $c }> as JetFn),*] }; + (@b $a:literal; $($b:literal)*) => { [$(core_functions!(@c $a; $b; 0 1 2 3 4 5 6 7)),*] }; + ($($a:literal)*) => { [$(core_functions!(@b $a; 0 1 2 3 4 5 6 7)),*] }; +} +const CORE: [[[JetFn; 8]; 8]; 8] = core_functions!(0 1 2 3 4 5 6 7); +fn builtin(dst: &mut CFrameItem, mut src: CFrameItem, env: &Env) -> bool { + let jet = Builtin::from_id(ID).expect("static dispatch"); + let bit_len = jet.source_bits(); + let mut input = BitValue { + bytes: vec![0; bit_len.div_ceil(8)], + bit_len, + }; + for i in 0..bit_len { + if unsafe { c_readBit(&mut src) } { + input.bytes[i / 8] |= 1 << (7 - i % 8); + } + } + let Ok(output) = env.context.evaluate(jet, &input) else { + return false; + }; + for i in 0..output.bit_len { + unsafe { c_writeBit(dst, output.bit(i).expect("validated output")) }; + } + true +} +macro_rules! functions { ($($id:literal),*) => { [$(builtin::<$id> as JetFn),*] }; } +const FUNCTIONS: [JetFn; 66] = functions![ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65 +]; +impl JetEnvironment for Env { + type Jet = PolicyJet; + type CJetEnvironment = Self; + fn c_jet_env(&self) -> &Self { + self + } + fn c_jet_ptr(jet: &PolicyJet) -> JetFn { + match jet { + PolicyJet::Core(j) => CORE.as_flattened().as_flattened()[*j as usize], + PolicyJet::Builtin(j) => FUNCTIONS[j.id() as usize], + } + } +} +pub fn policy_cmr(program: &[u8]) -> Result { + require( + program.len() <= crate::MAX_REQUEST_BYTES, + "program exceeds profile limit", + )?; + CommitNode::decode::<_, PolicyJet>(BitIter::from(program)) + .map(|p| p.cmr()) + .map_err(|e| Error::Invalid(format!("invalid Liquid program: {e}"))) +} +pub struct PreparedPolicy { + program: Arc<[u8]>, + cmr: Cmr, + env: Env, +} +impl PreparedPolicy { + #[allow(clippy::too_many_arguments)] + pub fn new( + program: &[u8], + pset: &Pset, + index: usize, + data: &[u8], + network: Network, + base: XOnlyPublicKey, + user: XOnlyPublicKey, + recovery: TapNodeHash, + ) -> Result { + let cmr = policy_cmr(program)?; + let context = Context::new( + pset, + index, + cmr.to_byte_array(), + data, + network, + base, + user, + recovery, + )?; + Ok(Self { + program: program.into(), + cmr, + env: Env { + context, + core: CoreEnv::new(), + }, + }) + } + pub fn context(&self) -> &Context { + &self.env.context + } + pub fn cmr(&self) -> Cmr { + self.cmr + } + pub fn summary(&self) -> super::ContextSummary { + self.context().summary() + } + pub fn evaluate_default(&self, witness: &[u8]) -> Result { + require( + witness.len() <= crate::MAX_REQUEST_BYTES, + "witness exceeds profile limit", + )?; + let program = RedeemNode::decode::<_, _, PolicyJet>( + BitIter::from(self.program.as_ref()), + BitIter::from(witness), + ) + .map_err(|e| Error::Invalid(format!("invalid Liquid witness/program: {e}")))?; + require(program.cmr() == self.cmr, "witnessed policy CMR mismatch")?; + let cost = program.bounds().cost; + require( + cost <= Cost::from_milliweight(crate::MAX_EXECUTION_COST_MILLIWEIGHT), + "policy execution cost exceeded", + )?; + BitMachine::for_program(&program) + .map_err(|e| Error::Invalid(e.to_string()))? + .exec(&program, &self.env) + .map_err(|e| Error::Invalid(format!("policy rejected: {e}")))?; + Ok(cost.to_string().parse().expect("Cost displays u32")) + } +} diff --git a/policy/src/liquid/jets.rs b/policy/src/liquid/jets.rs new file mode 100644 index 0000000..db63f75 --- /dev/null +++ b/policy/src/liquid/jets.rs @@ -0,0 +1,373 @@ +use super::{Context, Error, profile_hash, require}; +use crate::BitValue; +use elements::hashes::sha256; +use simplicity::jet::type_name::TypeName; +use std::{fmt, str::FromStr}; + +/// Shared operation names do not imply shared identities or Bitcoin amount semantics. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Builtin { + Policy(crate::Builtin), + InputAsset, + OutputAsset, + CurrentAsset, + FeeAsset, + AssetTotalInputValue, + AssetTotalOutputValue, + AssetFee, + AssetCurrentScriptDebit, + DisclosuresHash, + InputValueAsset, + OutputValueAsset, +} + +impl Builtin { + pub const COUNT: usize = 66; + pub fn all() -> impl Iterator { + crate::Builtin::ALL.into_iter().map(Self::Policy).chain([ + Self::InputAsset, + Self::OutputAsset, + Self::CurrentAsset, + Self::FeeAsset, + Self::AssetTotalInputValue, + Self::AssetTotalOutputValue, + Self::AssetFee, + Self::AssetCurrentScriptDebit, + Self::DisclosuresHash, + Self::InputValueAsset, + Self::OutputValueAsset, + ]) + } + pub fn from_id(id: u8) -> Option { + Self::all().nth(id as usize) + } + pub fn id(self) -> u8 { + match self { + Self::Policy(j) => j.id(), + Self::InputAsset => 55, + Self::OutputAsset => 56, + Self::CurrentAsset => 57, + Self::FeeAsset => 58, + Self::AssetTotalInputValue => 59, + Self::AssetTotalOutputValue => 60, + Self::AssetFee => 61, + Self::AssetCurrentScriptDebit => 62, + Self::DisclosuresHash => 63, + Self::InputValueAsset => 64, + Self::OutputValueAsset => 65, + } + } + pub fn name(self) -> &'static str { + match self { + Self::Policy(j) => j.name(), + Self::InputAsset => "input_asset", + Self::OutputAsset => "output_asset", + Self::CurrentAsset => "current_asset", + Self::FeeAsset => "fee_asset", + Self::AssetTotalInputValue => "asset_total_input_value", + Self::AssetTotalOutputValue => "asset_total_output_value", + Self::AssetFee => "asset_fee", + Self::AssetCurrentScriptDebit => "asset_current_script_debit", + Self::DisclosuresHash => "disclosures_hash", + Self::InputValueAsset => "input_value_asset", + Self::OutputValueAsset => "output_value_asset", + } + } + pub fn source_type(self) -> TypeName { + TypeName(match self { + Self::Policy(j) => return j.source_type(), + Self::InputAsset + | Self::OutputAsset + | Self::InputValueAsset + | Self::OutputValueAsset => b"i", + Self::AssetTotalInputValue + | Self::AssetTotalOutputValue + | Self::AssetFee + | Self::AssetCurrentScriptDebit => b"h", + _ => b"1", + }) + } + pub fn target_type(self) -> TypeName { + TypeName(match self { + Self::Policy(j) => return j.target_type(), + Self::InputAsset | Self::OutputAsset => b"+1h", + Self::InputValueAsset | Self::OutputValueAsset => b"+1*hl", + Self::AssetTotalInputValue + | Self::AssetTotalOutputValue + | Self::AssetFee + | Self::AssetCurrentScriptDebit => b"l", + _ => b"h", + }) + } + pub fn source_bits(self) -> usize { + self.source_type().to_bit_width() + } + pub fn target_bits(self) -> usize { + self.target_type().to_bit_width() + } + pub fn cmr(self) -> [u8; 32] { + super::manifest::identity(self) + } + pub fn cost_milliweight(self) -> u32 { + 10_000 + } +} + +impl fmt::Display for Builtin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} +impl FromStr for Builtin { + type Err = Error; + fn from_str(name: &str) -> Result { + Self::all() + .find(|j| j.name() == name) + .ok_or_else(|| Error::Invalid(format!("unknown Liquid policy jet: {name}"))) + } +} + +impl Context { + pub fn evaluate(&self, jet: Builtin, src: &BitValue) -> Result { + use crate::Builtin::*; + src.validate(jet.source_bits())?; + let index = || src.uint(0, 32) as usize; + let hash = BitValue::hash; + let u32v = |v: u32| BitValue::word(v as u64, 32); + let u64v = |v: u64| BitValue::word(v, 64); + let asset_hash = |d: &super::Disclosure| hash(d.asset().to_byte_array()); + let script_hash = |o: &elements::TxOut| { + hash(sha256::Hash::hash(o.script_pubkey.as_bytes()).to_byte_array()) + }; + let current = &self.tx.input[self.index]; + let asset = self.inputs[self.index].asset(); + let totals = &self.accounting[&asset]; + let requested_asset = || { + elements::AssetId::from_byte_array( + src.bytes.as_slice().try_into().expect("validated hash"), + ) + }; + let outpoint = |i: &elements::TxIn| { + hash(i.previous_output.txid.to_byte_array()).concat(&u32v(i.previous_output.vout)) + }; + let read_hash = || -> [u8; 32] { src.bytes.as_slice().try_into().expect("validated hash") }; + let same_asset_value = |d: &super::Disclosure| (d.asset() == asset).then(|| u64v(d.value)); + let result = match jet { + Builtin::InputAsset => BitValue::option(self.inputs.get(index()).map(asset_hash), 256), + Builtin::OutputAsset => { + BitValue::option(self.outputs.get(index()).map(asset_hash), 256) + } + Builtin::CurrentAsset => asset_hash(&self.inputs[self.index]), + Builtin::FeeAsset => hash(self.network.fee_asset().to_byte_array()), + Builtin::DisclosuresHash => hash(self.disclosures_hash), + Builtin::InputValueAsset => BitValue::option( + self.inputs + .get(index()) + .map(|d| asset_hash(d).concat(&u64v(d.value))), + 320, + ), + Builtin::OutputValueAsset => BitValue::option( + self.outputs + .get(index()) + .map(|d| asset_hash(d).concat(&u64v(d.value))), + 320, + ), + Builtin::AssetTotalInputValue + | Builtin::AssetTotalOutputValue + | Builtin::AssetFee + | Builtin::AssetCurrentScriptDebit => { + let a = self.accounting.get(&requested_asset()); + u64v(a.map_or(0, |a| match jet { + Builtin::AssetTotalInputValue => a.total_input_value, + Builtin::AssetTotalOutputValue => a.total_output_value, + Builtin::AssetFee => a.fee, + _ => a.debit, + })) + } + Builtin::Policy(j) => match j { + PolicyProfileHash => hash(profile_hash()), + PolicyCmr => hash(self.cmr), + SigningCommitmentHash => hash(super::signing_commitment( + self.cmr, + &self.data, + self.network, + )?), + RedeemScriptHash => hash(sha256::Hash::hash(&self.data).to_byte_array()), + NetworkGenesisHash => hash(self.network.genesis_hash().to_byte_array()), + CurrentIndex => u32v(self.index as u32), + Version => u32v(self.tx.version), + TxLockTime => u32v(self.tx.lock_time.to_consensus_u32()), + NumInputs => u32v(self.inputs.len() as u32), + NumOutputs => u32v(self.outputs.len() as u32), + InputPrevOutpoint => { + BitValue::option(self.tx.input.get(index()).map(outpoint), 288) + } + InputValue => { + BitValue::option(self.inputs.get(index()).and_then(same_asset_value), 64) + } + OutputValue => { + BitValue::option(self.outputs.get(index()).and_then(same_asset_value), 64) + } + CurrentPrevOutpoint => outpoint(current), + CurrentValue => u64v(self.inputs[self.index].value), + InputScriptHash => { + BitValue::option(self.prevouts.get(index()).map(script_hash), 256) + } + CurrentScriptHash => script_hash(&self.prevouts[self.index]), + OutputScriptHash => { + BitValue::option(self.tx.output.get(index()).map(script_hash), 256) + } + InputScriptSize => BitValue::option( + self.prevouts + .get(index()) + .map(|o| u32v(o.script_pubkey.len() as u32)), + 32, + ), + OutputScriptSize => BitValue::option( + self.tx + .output + .get(index()) + .map(|o| u32v(o.script_pubkey.len() as u32)), + 32, + ), + InputScriptByte | OutputScriptByte => { + let outputs = if j == InputScriptByte { + &self.prevouts + } else { + &self.tx.output + }; + BitValue::option( + outputs + .get(index()) + .and_then(|o| o.script_pubkey.as_bytes().get(src.uint(32, 32) as usize)) + .map(|b| BitValue::word(*b as u64, 8)), + 8, + ) + } + InputSequence => { + BitValue::option(self.tx.input.get(index()).map(|i| u32v(i.sequence.0)), 32) + } + CurrentSequence => u32v(current.sequence.0), + OutputScriptClass => BitValue::option( + self.tx.output.get(index()).map(|o| { + BitValue::word( + crate::context::script_class(crate::bitcoin::Script::from_bytes( + o.script_pubkey.as_bytes(), + )) as u64, + 8, + ) + }), + 8, + ), + TotalInputValue => u64v(totals.total_input_value), + TotalOutputValue => u64v(totals.total_output_value), + TxFee => u64v( + self.accounting + .get(&self.network.fee_asset()) + .map_or(0, |a| a.fee), + ), + ValueReturnedToCurrentScript => u64v(totals.returned_value), + CurrentScriptDebit => u64v(totals.debit), + InputValueForScript | OutputValueForScript | OutputCountForScript => { + let (outs, ds) = if j == InputValueForScript { + (&self.prevouts, &self.inputs) + } else { + (&self.tx.output, &self.outputs) + }; + let wanted = read_hash(); + let matching = outs.iter().zip(ds).filter(|(o, d)| { + d.asset() == asset + && sha256::Hash::hash(o.script_pubkey.as_bytes()).to_byte_array() + == wanted + }); + if j == OutputCountForScript { + u32v(matching.count() as u32) + } else { + u64v(matching.map(|(_, d)| d.value).sum()) + } + } + IsRbfSignaled => BitValue::word( + self.tx.input.iter().any(|i| i.sequence.0 < 0xffff_fffe) as u64, + 1, + ), + HasEffectiveLockTime => BitValue::word( + (self.tx.lock_time.to_consensus_u32() != 0 + && self.tx.input.iter().any(|i| i.sequence.0 != u32::MAX)) + as u64, + 1, + ), + InputLockBlocks | InputLockIntervals | CurrentLockBlocks | CurrentLockIntervals => { + let i = if matches!(j, CurrentLockBlocks | CurrentLockIntervals) { + self.index + } else { + index() + }; + BitValue::option( + self.relative_lock( + i, + matches!(j, InputLockIntervals | CurrentLockIntervals), + ) + .map(|v| BitValue::word(v as u64, 16)), + 16, + ) + } + CheckCsvBlocks | CheckCsvIntervals => { + require( + self.relative_lock(self.index, j == CheckCsvIntervals) + .is_some_and(|v| v as u64 >= src.uint(0, 16)), + "CSV check failed", + )?; + BitValue::unit() + } + CheckCltvHeight | CheckCltvTime => { + let requested = src.uint(0, 32) as u32; + let actual = self.tx.lock_time.to_consensus_u32(); + require( + (requested >= 500_000_000) == (j == CheckCltvTime) + && (actual >= 500_000_000) == (j == CheckCltvTime) + && actual >= requested + && current.sequence.0 != u32::MAX, + "CLTV check failed", + )?; + BitValue::unit() + } + UnsignedTxHash => hash(self.tx_hash), + PrevoutsHash => hash(self.prevouts_hash), + TxInputsHash => hash( + sha256::Hash::hash(&elements::encode::serialize(&self.tx.input)) + .to_byte_array(), + ), + TxOutputsHash => hash( + sha256::Hash::hash(&elements::encode::serialize(&self.tx.output)) + .to_byte_array(), + ), + CooperativeLeafHash => hash(self.cooperative.leaf_hash.to_byte_array()), + CooperativeSighashDefault => hash(self.sighash), + CooperativeUserPubkey => hash(self.user.serialize()), + CooperativeServicePubkey => hash(self.cooperative.service_pubkey.serialize()), + RecoveryLeafHash => hash(self.recovery.to_byte_array()), + PolicyApprovalHash => hash(self.approval), + PolicyTransactionApprovalHash => hash(self.transaction_approval), + TxVsizeLowerBound => u32v(self.vsize_lower_bound()), + }, + }; + result.validate(jet.target_bits())?; + Ok(result) + } + + fn relative_lock(&self, index: usize, time: bool) -> Option { + let seq = self.tx.input.get(index)?.sequence.0; + (seq & 0x8000_0000 == 0 && (seq & 0x0040_0000 != 0) == time).then_some(seq as u16) + } + + pub fn vsize_lower_bound(&self) -> u32 { + let mut stripped = self.tx.clone(); + for o in &mut stripped.output { + o.witness = Default::default(); + } + for i in &mut stripped.input { + i.witness = Default::default(); + } + elements::encode::serialize(&stripped).len() as u32 + } +} diff --git a/policy/src/liquid/manifest.rs b/policy/src/liquid/manifest.rs new file mode 100644 index 0000000..69dc14e --- /dev/null +++ b/policy/src/liquid/manifest.rs @@ -0,0 +1,63 @@ +use super::Builtin; +use elements::hashes::sha256; +use serde_json::{Value, json}; +use std::sync::LazyLock; + +pub const PROFILE_NAME: &str = "liquid-policy-v1"; +const CONTRACT: &str = "Experimental off-chain Elements policy, not consensus Simplicity. Immutable fully verified PSET context; version2,1..8 inputs/outputs,<=2MiB evidence,<=10000-byte scripts; native NUMS P2TR cooperative leaf plus hidden recovery at depth1; all inputs same reconstructed profile/network-bound output; Elements TapLeaf/TapTweak and genesis-bound SIGHASH_DEFAULT only; no annex,issuance,pegin,final witnesses. Required full non_witness_utxo txid/vout and exact base witness_utxo. Confidential PSET amount/asset labels require elements RangeProof::blind_value_proof_verify and SurjectionProof::blind_asset_proof_verify against authenticated commitments. Input original rangeproof verified with script commitment. Transaction verify_tx_amt_proofs verifies output range/surjection proofs and Pedersen balance. Per-asset checked u64 conservation includes exactly one explicit network-policy-asset fee output. No chain inclusion/confirmation/consensus-validity/unspentness claim. Every pre-existing signature verified against canonical leaf,known keys,DEFAULT sighash before preparation/signing. No secret keys,clock,I/O,mutable state in jets."; +const PROJECTION: &str = "Scalar value jets select the CURRENT verified input asset (raw AssetId::to_byte_array bytes), except tx_fee which ALWAYS returns the explicit configured network fee-asset amount, independent of current asset; fee_asset returns its denomination. input_value/output_value return None for another asset, not zero or an untyped amount. current_value is current asset value. total_input_value,total_output_value (including fees),value_returned_to_current_script,current_script_debit select current asset. input_value_for_script/output_value_for_script/output_count_for_script filter BOTH current asset and script SHA256; returned value uses exact script equality. debit=all inputs of selected asset minus exact-script returns. Asset-selector totals/debit/fee return zero for absent asset. No all-asset scalar totals. input/output_value_asset return Some(asset32||value_be64) for any in-range index. Asset IDs in JSON use display order; jet words use raw bytes."; +const HASHES: &str = "Words MSB-first,options tag+zero padding; canonical BitValue layout. All hashes raw library bytes,no explorer reversal. SHA256 single unless tagged. unsigned_tx_hash hashes full extracted unsigned Elements transaction including output range/surjection witnesses; prevouts_hash hashes consensus TxOut vector (base fields only); tx_inputs_hash/tx_outputs_hash hash consensus vectors (counts included). disclosures_hash=SHA256(input_count_be32||each input(asset_raw32||value_be64||confidential_asset_u8||confidential_value_u8)||output_count_be32||same output tuples). It commits values,not randomly generated disclosure proof bytes; proofs are independently verified every time. signing_commitment=TaggedHash('SimplicityUnchained/LiquidPolicySigningCommitment/v1',profile_hash||Elements_genesis_raw||program_CMR||SHA256(script_data)); service_key=Elements TapTweak(base_xonly,commitment as root). Cooperative leaf version0xc4 (Elements tapscript),NUMS50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0. J54=TaggedHash('SimplicityUnchained/LiquidPolicyTransactionApproval/v1',profile_hash||genesis||signing_commitment||unsigned_tx_hash||prevouts_hash||disclosures_hash||cooperative_leaf_hash||recovery_hash). J53=TaggedHash('SimplicityUnchained/LiquidPolicyInputApproval/v1',J54||index_be32||Elements DEFAULT sighash); actual sighash uses genesis twice as Elements defines. vsize lower bound is serialized unsigned transaction with ALL witness proofs stripped; not final fee rate."; + +fn body() -> Value { + json!({ + "schema_version":1,"profile":PROFILE_NAME,"status":"experimental-offchain", + "elements_version":"0.27","simplicity_version":"0.9", + "namespace":"0||Core jet; 1||seven-bit Liquid builtin ID; no legacy extension fallback", + "common_contract":CONTRACT,"amount_contract":PROJECTION,"hash_contract":HASHES, + "cost_contract":"Fixed 10000 milliweight per extension; bounded <=8 scans; max execution 10000000 milliweight; context/proof verification separately admission bounded; no consensus calibration claim.", + "limits":{"max_inputs":8,"max_outputs":8,"max_request_bytes":2097152,"max_script_bytes":10000,"max_value":"18446744073709551614","networks":["liquid","liquidtestnet"],"default_network":"liquidtestnet"}, + "jets":Builtin::all().map(|j| json!({"id":j.id(),"name":j.name(),"source_type":String::from_utf8_lossy(j.source_type().0),"target_type":String::from_utf8_lossy(j.target_type().0),"source_bits":j.source_bits(),"target_bits":j.target_bits(),"encoding":format!("1{:07b}",j.id()),"semantic_contract":semantics(j),"cost_milliweight":j.cost_milliweight(),"cmr":hex::encode(identity(j))})).collect::>(), + }) +} + +pub(crate) fn semantics(j: Builtin) -> String { + use crate::Builtin::*; + match j { + Builtin::Policy(op) => match op { + PolicyProfileHash => "Return SHA256 of canonical ordered-key JSON manifest without profile_hash.".into(), + NetworkGenesisHash => "Return configured Liquid/LiquidTestnet Elements genesis hash raw bytes.".into(), + SigningCommitmentHash | UnsignedTxHash | PrevoutsHash | TxInputsHash | TxOutputsHash | CooperativeLeafHash | CooperativeSighashDefault | PolicyApprovalHash | PolicyTransactionApprovalHash | TxVsizeLowerBound => HASHES.into(), + InputValue | OutputValue | CurrentValue | TotalInputValue | TotalOutputValue | TxFee | InputValueForScript | OutputValueForScript | OutputCountForScript | CurrentScriptDebit | ValueReturnedToCurrentScript => PROJECTION.into(), + // These operations only read shared public script/sequence/index/lock fields; + // their Bitcoin wording below is not used for network/hash/wire/value operations. + _ => op.semantic_contract().replace("Bitcoin", "Elements").replace("BIP-341", "Elements Taproot").replace("0xc0", "0xc4"), + }, + Builtin::InputAsset | Builtin::OutputAsset => "Return Some(verified asset ID raw32) at zero-based input/output index; out of range canonical None; no PSET labels without cryptographic disclosure proof.".into(), + Builtin::CurrentAsset => "Return current verified input asset raw32. Bind this explicitly before using current-asset scalar budgets.".into(), + Builtin::FeeAsset => "Return configured network policy fee asset raw32; tx_fee is ALWAYS denominated in this asset.".into(), + Builtin::AssetTotalInputValue => "Input asset_raw32; return checked sum of verified inputs in this asset, independent of current input asset; absent asset zero.".into(), + Builtin::AssetTotalOutputValue => "Input asset_raw32; return checked sum of verified outputs (including fees) in this asset, independent of current input asset; absent asset zero.".into(), + Builtin::AssetFee => "Input asset_raw32; return explicit fees in this asset. Only configured fee asset can have nonzero fees; absent asset zero.".into(), + Builtin::AssetCurrentScriptDebit => "Input asset_raw32; return checked sum of ALL same-contract inputs in that asset minus outputs of that asset with EXACT current script equality; independent of current input asset, includes network fee if matching; absent asset zero.".into(), + Builtin::DisclosuresHash => HASHES.into(), + Builtin::InputValueAsset | Builtin::OutputValueAsset => "Return Some(verified asset_raw32||value_be64) for any in-range input/output index, regardless of current asset; out of range canonical None. Both asset and value disclosures cryptographically verified.".into(), + } +} +pub(crate) fn identity(j: Builtin) -> [u8; 32] { + // Identity does not reference profile_hash: fingerprinting remains acyclic. + crate::context::tagged_hash("SimplicityUnchained/LiquidPolicyJetIdentity/v1", &[&serde_json::to_vec(&json!({ + "schema_version":1,"profile":PROFILE_NAME,"id":j.id(),"name":j.name(), + "source_type":String::from_utf8_lossy(j.source_type().0),"target_type":String::from_utf8_lossy(j.target_type().0), + "cost_milliweight":j.cost_milliweight(),"semantics":semantics(j),"common":CONTRACT,"projection":PROJECTION,"hashes":HASHES, + })).expect("identity serialization")]) +} + +static BODY: LazyLock = LazyLock::new(body); +pub fn profile_hash() -> [u8; 32] { + sha256::Hash::hash(&serde_json::to_vec(&*BODY).expect("manifest serialization")).to_byte_array() +} +pub fn manifest() -> Value { + let mut value = BODY.clone(); + value["profile_hash"] = hex::encode(profile_hash()).into(); + value +} diff --git a/policy/src/liquid/mod.rs b/policy/src/liquid/mod.rs new file mode 100644 index 0000000..1b13cc4 --- /dev/null +++ b/policy/src/liquid/mod.rs @@ -0,0 +1,32 @@ +//! Portable `liquid-policy-v1`: verified PSET disclosures, never wallet labels. +mod context; +mod execution; +mod jets; +mod manifest; +mod operations; + +pub use context::*; +pub use elements; +pub use execution::{PolicyJet, PreparedPolicy, policy_cmr}; +pub use jets::Builtin; +pub use manifest::{PROFILE_NAME, manifest, profile_hash}; +pub use operations::{ + CreateRequest, DisclosureProofs, Output, ProvenInput, add_signature, create_pset, + create_pset_with_rng, disclosure_proofs, finalize, unblind_output, +}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("invalid Liquid policy context: {0}")] + Invalid(String), + #[error(transparent)] + Frame(#[from] crate::Error), +} + +pub(crate) fn require(ok: bool, message: impl Into) -> Result<(), Error> { + if ok { + Ok(()) + } else { + Err(Error::Invalid(message.into())) + } +} diff --git a/policy/src/liquid/operations.rs b/policy/src/liquid/operations.rs new file mode 100644 index 0000000..bb55ac5 --- /dev/null +++ b/policy/src/liquid/operations.rs @@ -0,0 +1,391 @@ +//! Local construction/disclosure generation and cryptographically checked finalization. +use super::{Context, Error, Network, require, verify_disclosure}; +use elements::{ + Address, AssetId, OutPoint, Transaction, TxIn, TxOut, TxOutSecrets, + confidential::{Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor}, + encode::{deserialize, serialize}, + pset::PartiallySignedTransaction as Pset, + secp256k1_zkp::{Keypair, Message, Secp256k1, SecretKey, rand::rngs::OsRng}, +}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeSet, HashMap}, + str::FromStr, +}; + +#[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ProvenInput { + pub txid: String, + pub vout: u32, + pub parent_tx_hex: String, + /// Browser-local receiving key. Never included in a service request/PSET. + pub blinding_secret_key_hex: Option, + #[serde(default = "sequence")] + pub sequence: u32, +} +fn sequence() -> u32 { + 0xffff_fffd +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Output { + /// `fee` is the sole empty-script output; otherwise a network-checked address. + pub address: String, + pub asset_id: String, + pub value_sat: u64, +} +#[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateRequest { + pub inputs: Vec, + pub outputs: Vec, + #[serde(default)] + pub network: Network, + #[serde(default)] + pub lock_time: u32, +} +#[derive(Debug, Clone, Serialize)] +pub struct DisclosureProofs { + pub asset_id: String, + pub value_sat: u64, + pub blind_asset_proof_hex: Option, + pub blind_value_proof_hex: Option, + pub confidential: bool, +} + +pub fn unblind_output(out: &TxOut, secret: Option<&str>) -> Result { + let secp = Secp256k1::new(); + let secrets = match (out.asset, out.value) { + (Asset::Explicit(asset), Value::Explicit(value)) => TxOutSecrets::new(asset, AssetBlindingFactor::zero(), value, ValueBlindingFactor::zero()), + (Asset::Confidential(_), Value::Confidential(_)) => { + let key = secret.ok_or_else(|| Error::Invalid("confidential input requires a local blinding secret key".into()))?; + out.unblind(&secp, SecretKey::from_str(key).map_err(|e| Error::Invalid(e.to_string()))?).map_err(|e| Error::Invalid(format!("unblinding failed: {e}")))? + } + _ => return Err(Error::Invalid("local unblinding supports fully explicit or fully confidential outputs; mixed commitments require externally generated PSET disclosure proofs".into())), + }; + let expected_asset = if out.asset.is_confidential() { + Asset::new_confidential(&secp, secrets.asset, secrets.asset_bf) + } else { + Asset::Explicit(secrets.asset) + }; + let expected_value = if out.value.is_confidential() { + Value::new_confidential_from_assetid( + &secp, + secrets.value, + secrets.asset, + secrets.value_bf, + secrets.asset_bf, + ) + } else { + Value::Explicit(secrets.value) + }; + require( + out.asset == expected_asset && out.value == expected_value, + "unblinded openings do not match commitments", + )?; + Ok(secrets) +} + +pub fn disclosure_proofs(out: &TxOut, secrets: TxOutSecrets) -> Result { + disclosure_proofs_with_rng(out, secrets, &mut OsRng) +} +fn disclosure_proofs_with_rng< + R: elements::secp256k1_zkp::rand::RngCore + elements::secp256k1_zkp::rand::CryptoRng, +>( + out: &TxOut, + secrets: TxOutSecrets, + rng: &mut R, +) -> Result { + let secp = Secp256k1::new(); + let asset_proof = if out.asset.is_confidential() { + Some( + elements::SurjectionProof::blind_asset_proof( + rng, + &secp, + secrets.asset, + secrets.asset_bf, + ) + .map_err(|e| Error::Invalid(e.to_string()))?, + ) + } else { + None + }; + let value_proof = if let Some(commit) = out.value.commitment() { + let generator = out + .asset + .into_asset_gen(&secp) + .ok_or_else(|| Error::Invalid("null asset".into()))?; + Some( + elements::RangeProof::blind_value_proof( + rng, + &secp, + secrets.value, + commit, + generator, + secrets.value_bf, + ) + .map_err(|e| Error::Invalid(e.to_string()))?, + ) + } else { + None + }; + verify_disclosure( + out, + Some(secrets.value), + Some(secrets.asset), + value_proof.as_ref(), + asset_proof.as_ref(), + )?; + Ok(DisclosureProofs { + asset_id: secrets.asset.to_string(), + value_sat: secrets.value, + blind_asset_proof_hex: asset_proof.map(|p| hex::encode(p.to_vec())), + blind_value_proof_hex: value_proof.map(|p| hex::encode(p.to_vec())), + confidential: out.asset.is_confidential() || out.value.is_confidential(), + }) +} + +pub fn create_pset(request: &CreateRequest) -> Result { + create_pset_with_rng(request, &mut OsRng) +} +/// The caller must provide cryptographically secure randomness. Production +/// entry points use OsRng; injection makes public test fixtures reproducible. +pub fn create_pset_with_rng< + R: elements::secp256k1_zkp::rand::RngCore + elements::secp256k1_zkp::rand::CryptoRng, +>( + request: &CreateRequest, + rng: &mut R, +) -> Result { + require( + (1..=crate::MAX_INPUTS).contains(&request.inputs.len()) + && (1..=crate::MAX_OUTPUTS).contains(&request.outputs.len()), + "require 1..=8 inputs/outputs", + )?; + let size: usize = request.inputs.iter().map(|i| i.parent_tx_hex.len()).sum(); + require( + size <= crate::MAX_REQUEST_BYTES * 2, + "parent evidence exceeds 2 MiB", + )?; + let mut parents = Vec::new(); + let mut tx_inputs = Vec::new(); + let mut secrets = HashMap::new(); + let mut seen = BTreeSet::new(); + for (index, input) in request.inputs.iter().enumerate() { + let parent: Transaction = deserialize( + &hex::decode(&input.parent_tx_hex).map_err(|e| Error::Invalid(e.to_string()))?, + ) + .map_err(|e| Error::Invalid(e.to_string()))?; + let txid = + elements::Txid::from_str(&input.txid).map_err(|e| Error::Invalid(e.to_string()))?; + require(parent.txid() == txid, "parent txid mismatch")?; + let out = parent + .output + .get(input.vout as usize) + .ok_or_else(|| Error::Invalid("parent vout out of range".into()))? + .clone(); + let opening = unblind_output(&out, input.blinding_secret_key_hex.as_deref())?; + let proof = disclosure_proofs_with_rng(&out, opening, rng)?; + let outpoint = OutPoint::new(txid, input.vout); + require(seen.insert(outpoint), "duplicate outpoint")?; + tx_inputs.push(TxIn { + previous_output: outpoint, + sequence: elements::Sequence(input.sequence), + ..Default::default() + }); + secrets.insert(index, opening); + parents.push((parent, out, proof)); + } + let mut addresses = Vec::new(); + let mut fee_count = 0; + let mut outputs = Vec::new(); + for output in &request.outputs { + require(output.value_sat < u64::MAX, "value must be below u64::MAX")?; + let asset = + AssetId::from_str(&output.asset_id).map_err(|e| Error::Invalid(e.to_string()))?; + let address = if output.address == "fee" { + fee_count += 1; + require( + asset == request.network.fee_asset(), + "fee asset differs from network policy asset", + )?; + None + } else { + require(output.value_sat > 0, "nonfee output must be positive")?; + Some( + Address::parse_with_params(&output.address, request.network.address_params()) + .map_err(|e| Error::Invalid(e.to_string()))?, + ) + }; + outputs.push(TxOut { + asset: Asset::Explicit(asset), + value: Value::Explicit(output.value_sat), + nonce: Nonce::Null, + script_pubkey: address + .as_ref() + .map(|a| a.script_pubkey()) + .unwrap_or_default(), + witness: Default::default(), + }); + addresses.push(address); + } + require(fee_count == 1, "exactly one explicit fee output required")?; + // Check public per-asset conservation before any expensive/randomized blinding. + let mut totals: BTreeMapCompat = Default::default(); + for s in secrets.values() { + add(&mut totals, s.asset, s.value as i128)?; + } + for out in &outputs { + add( + &mut totals, + out.asset.explicit().expect("explicit"), + -(out.value.explicit().expect("explicit") as i128), + )?; + } + require( + totals.values().all(|v| *v == 0), + "per-asset input/output totals are not conserved", + )?; + let tx = Transaction { + version: 2, + lock_time: elements::LockTime::from_consensus(request.lock_time), + input: tx_inputs, + output: outputs, + }; + let mut pset = Pset::from_tx(tx); + for (map, (parent, out, proof)) in pset.inputs_mut().iter_mut().zip(parents) { + map.non_witness_utxo = Some(parent); + map.witness_utxo = Some(out.clone()); + map.amount = Some(proof.value_sat); + map.asset = Some(AssetId::from_str(&proof.asset_id).expect("validated")); + map.blind_asset_proof = proof.blind_asset_proof_hex.map(|h| { + elements::SurjectionProof::from_slice(&hex::decode(h).expect("encoded proof")) + .expect("generated proof") + }); + map.blind_value_proof = proof.blind_value_proof_hex.map(|h| { + elements::RangeProof::from_slice(&hex::decode(h).expect("encoded proof")) + .expect("generated proof") + }); + } + let mut blind = false; + for (map, address) in pset.outputs_mut().iter_mut().zip(addresses) { + if let Some(key) = address.and_then(|a| a.blinding_pubkey) { + blind = true; + map.blinding_key = Some(elements::bitcoin::PublicKey::new(key)); + map.blinder_index = Some(0); + } + } + if blind { + pset.blind_last(rng, &Secp256k1::new(), &secrets) + .map_err(|e| Error::Invalid(format!("output blinding failed: {e}")))?; + } + let tx = pset + .extract_tx() + .map_err(|e| Error::Invalid(e.to_string()))?; + let prevouts: Vec<_> = pset + .inputs() + .iter() + .map(|i| i.witness_utxo.clone().expect("populated")) + .collect(); + tx.verify_tx_amt_proofs(&Secp256k1::new(), &prevouts) + .map_err(|e| { + Error::Invalid(format!( + "constructed PSET failed proof/balance verification: {e}" + )) + })?; + require( + serialize(&pset).len() <= crate::MAX_REQUEST_BYTES, + "constructed PSET exceeds 2 MiB", + )?; + Ok(pset) +} +type BTreeMapCompat = std::collections::BTreeMap; +fn add(totals: &mut BTreeMapCompat, asset: AssetId, value: i128) -> Result<(), Error> { + let old = totals.entry(asset).or_default(); + *old = old + .checked_add(value) + .ok_or_else(|| Error::Invalid("asset total overflow".into()))?; + Ok(()) +} + +pub fn add_signature( + context: &Context, + pset: &mut Pset, + secret: SecretKey, + service: bool, +) -> Result { + context.revalidate(pset)?; + if !service { + require( + pset.inputs()[context.input_index()] + .tap_script_sigs + .contains_key(&( + context.cooperative().service_pubkey, + context.cooperative().leaf_hash, + )), + "user co-signing requires a verified service signature", + )?; + } + let secp = Secp256k1::new(); + let pair = Keypair::from_secret_key(&secp, &secret); + let key = pair.x_only_public_key().0; + require( + key == if service { + context.cooperative().service_pubkey + } else { + context.user_pubkey() + }, + "secret key differs from committed signer", + )?; + let sig = elements::SchnorrSig { + sig: secp.sign_schnorr_no_aux_rand(&Message::from_digest(context.sighash()), &pair), + hash_ty: elements::SchnorrSighashType::Default, + }; + let map = &mut pset.inputs_mut()[context.input_index()]; + map.tap_scripts.insert( + context.cooperative().control_block.clone(), + ( + context.cooperative().script.clone(), + elements::taproot::LeafVersion::default(), + ), + ); + map.tap_script_sigs + .insert((key, context.cooperative().leaf_hash), sig); + context.verify_signatures(pset)?; + Ok(sig) +} + +pub fn finalize(context: &Context, pset: &Pset) -> Result { + context.revalidate(pset)?; + require( + pset.extract_tx() + .map_err(|e| Error::Invalid(e.to_string()))? + == *context.tx(), + "PSET changed after context preparation", + )?; + let mut tx = context.tx().clone(); + for (index, input) in pset.inputs().iter().enumerate() { + let service = input + .tap_script_sigs + .get(&( + context.cooperative().service_pubkey, + context.cooperative().leaf_hash, + )) + .ok_or_else(|| { + Error::Invalid(format!("missing service signature for input {index}")) + })?; + let user = input + .tap_script_sigs + .get(&(context.user_pubkey(), context.cooperative().leaf_hash)) + .ok_or_else(|| Error::Invalid(format!("missing user signature for input {index}")))?; + tx.input[index].witness.script_witness = vec![ + user.to_vec(), + service.to_vec(), + context.cooperative().script.to_bytes(), + context.cooperative().control_block.serialize(), + ] + .into(); + } + Ok(tx) +} diff --git a/policy/src/manifest.rs b/policy/src/manifest.rs new file mode 100644 index 0000000..59ba2cb --- /dev/null +++ b/policy/src/manifest.rs @@ -0,0 +1,311 @@ +use std::sync::LazyLock; + +use serde::{Deserialize, Serialize}; +use simplicity::bitcoin::hashes::{Hash, sha256}; +use simplicity::jet::{Core, Jet}; +use simplicity::{BitWriter, jet::type_name::TypeName}; + +use crate::{ + Builtin, MAX_EXECUTION_COST_MILLIWEIGHT, MAX_INPUTS, MAX_MONEY, MAX_OUTPUTS, MAX_REQUEST_BYTES, + MAX_SCRIPT_BYTES, +}; + +pub const PROFILE_NAME: &str = "bitcoin-policy-v1"; + +const COMMON_CONTRACT: &str = "Experimental off-chain Bitcoin policy extensions, not Bitcoin consensus jets. All operations are deterministic and read one immutable, fully validated public context; no secret keys, I/O, clock, active chain, or mutable state. Required context never defaults. The profile accepts exactly version2, 1..=8 inputs/outputs, native P2TR cooperative script-path only. Every input has non_witness_utxo and exactly matching witness_utxo; parent txid/vout are verified; duplicate outpoints, negative fees, money-range violations and checked-arithmetic failure are rejected. All inputs spend the same reconstructed NUMS-internal-key output with canonical service CHECKSIG user CHECKSIGADD 2 EQUAL leaf and one hidden recovery hash at depth1. Every output script and script_data is at most10000 bytes; request/evidence at most2097152 bytes. Parent proofs are deduplicated and not retained in Context. Only configured Bitcoin/Testnet/Testnet4 networks. Only SIGHASH_DEFAULT; no annex or code-separator overrides; caller final witnesses cannot supply execution context. Parent verification proves content, NOT confirmation, consensus validity, chain inclusion or unspentness. Hash-keyed aggregates scan at most8 entries. Indexed missing entries/bytes return canonical None; failed Check rejects execution."; +const BIT_LAYOUT: &str = "Standard simplicity-lang0.9 TypeName prefix notation: 1=unit(0 bits),2=bit,c=u8,s=u16,i=u32,l=u64,h=u256,+=sum,*=product. Words unsigned MSB-first big-endian. Products concatenate. Sums: tag then zero left padding to maximum branch width then active payload; Either<(),T> None=0||zeroes(width(T)), Some=1||T. Unit has no bits; bool exactly one bit. Packed buffers have exact ceil(width/8) bytes with unused low bits zero; malformed source rejected."; +const IDENTITY_CONTRACT: &str = "New CMRs are application-defined off-chain custom extension identities, NOT standard/consensus Simplicity jet identities or claims of a formally derived native implementation. Identity = BIP340-style TaggedHash('SimplicityUnchained/PolicyJetIdentity/v1', compact serde_json bytes of ordered SemanticIdentity fields schema_version,profile,id,name,source_type,target_type,source_bits,target_bits,cost_milliweight,semantic_contract,common_contract,bit_layout,hash_contract,signing_contract,approval_contract,cost_contract,limits). The complete semantic contract and types, not only a name, are committed. No identity references profile_hash, so manifest fingerprinting is acyclic. Legacy Core and three existing extension identities remain byte-for-byte unchanged."; +const HASH_CONTRACT: &str = "All H values use Hash::to_byte_array in hash-library order, never explorer display reversal. SHA256 is single SHA256 unless explicitly tagged. TaggedHash(tag,m)=SHA256(SHA256(UTF8(tag))||SHA256(UTF8(tag))||m). Consensus vectors include CompactSize counts. All approval and signing-commitment fields are fixed-width32 bytes except input index:u32 big-endian. No strings, network labels, signature bytes or lengths enter these preimages."; +const SIGNING_CONTRACT: &str = "PolicySigningCommitment/v1 tag is 'SimplicityUnchained/PolicySigningCommitment/v1'; message=profile_hash||genesis_hash||program_cmr||SHA256(script_data). service_key=BIP341 TapTweak(base_service_xonly, merkle_root=that commitment), treating commitment as the32-byte root, then use tweaked xonly in canonical cooperative leaf. Internal output key is NUMS 50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0; two leaves at depth1, cooperative leaf version0xc0 plus hidden supplied recovery TapNodeHash. Every verified input script must equal the resulting p2tr scriptPubKey. This domain is isolated from legacy SigningCommitment/v1."; +const APPROVAL_CONTRACT: &str = "J54=TaggedHash('SimplicityUnchained/PolicyTransactionApproval/v1',profile_hash||genesis_hash||signing_commitment||unsigned_tx_hash||prevouts_hash||cooperative_leaf_hash||recovery_hash). J53=TaggedHash('SimplicityUnchained/PolicyInputApproval/v1',J54||selected_index_be32||actual_default_sighash). J54 deliberately reusable across all same-contract inputs; J53 input-specific. Sign decoded digest bytes directly, no extra hash. Neither digest implements one-use permissions or includes its own signatures. Existing tx_approval_hash remains BitcoinApproval/v1 using legacy SigningCommitment/v1."; +const COST_CONTRACT: &str = "Each new extension costs fixed10000 milliweight, initially conservative for <=8-entry bounded cached-hash scans and <=321 combined source/target frame bits. VM execution admission is at most10000000 milliweight including combinator costs. Context construction/proof hashing is separately admission bounded, not paid by jet cost. Legacy costs unchanged. Experimental until end-to-end VM/compiler/service verification and cost-ceiling measurements complete; changing this frozen manifest requires explicit versioning."; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Limits { + pub transaction_version: i32, + pub min_inputs: usize, + pub max_inputs: usize, + pub min_outputs: usize, + pub max_outputs: usize, + pub max_output_script_bytes: usize, + pub max_script_data_bytes: usize, + pub max_request_bytes: usize, + pub max_evidence_bytes: usize, + pub max_execution_cost_milliweight: u32, + pub max_money_satoshis: u64, + pub max_jet_scan_entries: usize, + pub max_jet_frame_bits: usize, + pub networks: Vec, +} + +impl Default for Limits { + fn default() -> Self { + Self { + transaction_version: 2, + min_inputs: 1, + max_inputs: MAX_INPUTS, + min_outputs: 1, + max_outputs: MAX_OUTPUTS, + max_output_script_bytes: MAX_SCRIPT_BYTES, + max_script_data_bytes: MAX_SCRIPT_BYTES, + max_request_bytes: MAX_REQUEST_BYTES, + max_evidence_bytes: MAX_REQUEST_BYTES, + max_execution_cost_milliweight: MAX_EXECUTION_COST_MILLIWEIGHT, + max_money_satoshis: MAX_MONEY, + max_jet_scan_entries: 8, + max_jet_frame_bits: 321, + networks: ["bitcoin", "testnet", "testnet4"] + .map(str::to_owned) + .to_vec(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct JetMetadata { + pub id: u16, + pub name: String, + pub source_type: String, + pub target_type: String, + pub source_bits: usize, + pub target_bits: usize, + pub encoding: String, + pub cmr: String, + pub cost_milliweight: u32, + pub semantic_contract: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Manifest { + pub profile_hash: String, + #[serde(flatten)] + pub body: ManifestBody, +} + +impl std::ops::Deref for Manifest { + type Target = ManifestBody; + fn deref(&self) -> &Self::Target { + &self.body + } +} + +impl Manifest { + /// The exact fingerprint preimage, without a self-referential hash field. + pub fn canonical_bytes(&self) -> Vec { + serde_json::to_vec(&self.body).expect("manifest serialization") + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ManifestBody { + pub schema_version: u32, + pub profile: String, + pub status: String, + pub simplicity_version: String, + pub namespace: String, + pub fingerprint_contract: String, + pub bit_layout: String, + pub hash_contract: String, + pub identity_contract: String, + pub common_contract: String, + pub signing_contract: String, + pub approval_contract: String, + pub cost_contract: String, + pub limits: Limits, + pub jets: Vec, + pub legacy_jets: Vec, +} + +#[derive(Serialize)] +struct SemanticIdentity { + schema_version: u32, + profile: &'static str, + id: u8, + name: &'static str, + source_type: &'static str, + target_type: &'static str, + source_bits: usize, + target_bits: usize, + cost_milliweight: u32, + semantic_contract: &'static str, + common_contract: &'static str, + bit_layout: &'static str, + hash_contract: &'static str, + signing_contract: &'static str, + approval_contract: &'static str, + cost_contract: &'static str, + limits: Limits, +} + +pub(crate) fn builtin_identity(jet: Builtin) -> [u8; 32] { + static IDENTITIES: LazyLock<[[u8; 32]; 55]> = LazyLock::new(|| { + Builtin::ALL.map(|jet| { + let identity = SemanticIdentity { + schema_version: 1, + profile: PROFILE_NAME, + id: jet.id(), + name: jet.name(), + source_type: type_string(jet.source_type()), + target_type: type_string(jet.target_type()), + source_bits: jet.source_bits(), + target_bits: jet.target_bits(), + cost_milliweight: jet.cost_milliweight(), + semantic_contract: jet.semantic_contract(), + common_contract: COMMON_CONTRACT, + bit_layout: BIT_LAYOUT, + hash_contract: HASH_CONTRACT, + signing_contract: SIGNING_CONTRACT, + approval_contract: APPROVAL_CONTRACT, + cost_contract: COST_CONTRACT, + limits: Limits::default(), + }; + crate::context::tagged_hash( + "SimplicityUnchained/PolicyJetIdentity/v1", + &[&serde_json::to_vec(&identity).expect("fixed identity serialization")], + ) + }) + }); + IDENTITIES[usize::from(jet.id())] +} + +fn type_string(name: TypeName) -> &'static str { + std::str::from_utf8(name.0).expect("standard ASCII TypeName") +} + +#[allow(clippy::too_many_arguments)] +fn metadata( + id: u16, + name: String, + source: TypeName, + target: TypeName, + encoding: String, + cmr: [u8; 32], + cost: u32, + contract: &str, +) -> JetMetadata { + JetMetadata { + id, + name, + source_type: type_string(source.clone()).to_owned(), + target_type: type_string(target.clone()).to_owned(), + source_bits: source.to_bit_width(), + target_bits: target.to_bit_width(), + encoding, + cmr: hex::encode(cmr), + cost_milliweight: cost, + semantic_contract: contract.to_owned(), + } +} + +fn legacy_metadata() -> Vec { + let mut jets = Vec::with_capacity(Core::ALL.len() + 3); + for (id, jet) in Core::ALL.into_iter().enumerate() { + let mut bytes = Vec::new(); + let bits = { + let mut writer = BitWriter::new(&mut bytes as &mut dyn std::io::Write); + let bits = jet.encode(&mut writer).expect("Vec writing"); + writer.flush_all().expect("Vec writing"); + bits + }; + let encoding = std::iter::once('0') + .chain((0..bits).map(|i| { + if bytes[i / 8] >> (7 - i % 8) & 1u8 == 1u8 { + '1' + } else { + '0' + } + })) + .collect(); + jets.push(metadata( + id as u16, + jet.to_string(), + jet.source_ty(), + jet.target_ty(), + encoding, + jet.cmr().to_byte_array(), + jet.cost().to_string().parse().expect("Cost displays milliweight"), + "Unchanged standard Core jet semantics, CMR, TypeName, cost and inner encoding pinned to simplicity-lang0.9.0; only outer policy namespace bit0 is prepended. No policy reinterpretation of this jet.", + )); + } + for (name, source, target, encoding, cmr, cost, contract) in [ + ( + "get_opcode_from_script", + b"c".as_slice(), + b"c".as_slice(), + "0111110", + "dcccd2895922e75b018b0846e5cd4963808bbfd48b47234475607f90e70ee032", + 100, + "Legacy byte accessor: u8 offset in script_data returns that raw byte; offset>=length fails. Despite name it does not parse instructions.", + ), + ( + "get_pubkey_from_script", + b"c".as_slice(), + b"h".as_slice(), + "01111110", + "27eab09068b035af619713335b73d2520ecc02090067c8fccabb4d72a655cdcb", + 100, + "Legacy compressed-key extraction: u8 offset must have34 bytes remaining, byte0=0x21, byte1=0x02 or0x03; return following32 X-coordinate bytes without curve validation; otherwise fail.", + ), + ( + "tx_approval_hash", + b"1".as_slice(), + b"h".as_slice(), + "01111111", + "f0ed96ee8fbf1a69beb20deb3a7a9b748bdc18da284d15438debc7d4faeb9962", + 356, + "Legacy BitcoinApproval/v1 tagged digest, unchanged: network byte0/1/2 for Bitcoin/Testnet/Testnet4 || input_index_be32 || legacy TaggedHash('SimplicityUnchained/SigningCommitment/v1',cmr||SHA256(script_data)) || SHA256(consensus unsigned tx) || SHA256(consensus verified TxOut vector). No new-domain alias.", + ), + ] { + jets.push(metadata( + jets.len() as u16, + name.to_owned(), + TypeName(source), + TypeName(target), + encoding.to_owned(), + hex::decode(cmr).unwrap().try_into().unwrap(), + cost, + contract, + )); + } + jets +} + +static MANIFEST: LazyLock = LazyLock::new(|| { + let body = ManifestBody { + schema_version: 1, + profile: PROFILE_NAME.to_owned(), + status: "experimental".to_owned(), + simplicity_version: "0.9.0".to_owned(), + namespace: "0 || unchanged CoreExtension code; 1 || new id:u6 MSB-first; ids0..54 valid,55..63 invalid. Jet code only; outer Simplicity node encoding is unchanged.".to_owned(), + fingerprint_contract: "profile_hash=SHA256(compact serde_json serialization of ManifestBody in declared field order, UTF8, no whitespace/newline, ordered jets ids0..54 and legacy Core::ALL then3 extensions). Manifest serializes profile_hash first then flattens body; remove only profile_hash to reproduce the body. Arrays and all contract/type/encoding/CMR/cost/limit fields are hashed. No maps with nondeterministic order, no circular self-hashing.".to_owned(), + bit_layout: BIT_LAYOUT.to_owned(), + hash_contract: HASH_CONTRACT.to_owned(), + identity_contract: IDENTITY_CONTRACT.to_owned(), + common_contract: COMMON_CONTRACT.to_owned(), + signing_contract: SIGNING_CONTRACT.to_owned(), + approval_contract: APPROVAL_CONTRACT.to_owned(), + cost_contract: COST_CONTRACT.to_owned(), + limits: Limits::default(), + jets: Builtin::ALL.into_iter().map(|jet| metadata( + u16::from(jet.id()), jet.name().to_owned(), jet.source_type(), jet.target_type(), + jet.encoding(), jet.cmr(), jet.cost_milliweight(), jet.semantic_contract(), + )).collect(), + legacy_jets: legacy_metadata(), + }; + let profile_hash = hex::encode( + sha256::Hash::hash(&serde_json::to_vec(&body).expect("fixed manifest serialization")) + .to_byte_array(), + ); + Manifest { profile_hash, body } +}); + +pub fn manifest() -> Manifest { + MANIFEST.clone() +} + +pub fn profile_hash() -> [u8; 32] { + hex::decode(&MANIFEST.profile_hash) + .expect("generated hex") + .try_into() + .expect("32-byte hash") +} diff --git a/policy/src/value.rs b/policy/src/value.rs new file mode 100644 index 0000000..537cc27 --- /dev/null +++ b/policy/src/value.rs @@ -0,0 +1,96 @@ +use crate::Error; + +/// A canonical Simplicity frame, packed most-significant bit first. +/// +/// The byte length is exactly `ceil(bit_len/8)`; unused low bits of the final byte +/// are zero. Products concatenate. Sums contain a tag, left padding to the wider +/// branch, then the active value. Thus `Either<(), T>` is `0 || zeroes` or `1 || T`. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct BitValue { + pub bytes: Vec, + pub bit_len: usize, +} + +impl BitValue { + pub fn unit() -> Self { + Self::default() + } + + pub fn from_bytes(bytes: Vec, bit_len: usize) -> Result { + let value = Self { bytes, bit_len }; + value.validate(bit_len)?; + Ok(value) + } + + pub fn validate(&self, expected: usize) -> Result<(), Error> { + if self.bit_len != expected + || self.bytes.len() != expected.div_ceil(8) + || (!expected.is_multiple_of(8) + && self + .bytes + .last() + .is_some_and(|last| last & ((1u8 << (8 - expected % 8)) - 1) != 0)) + { + return Err(Error::SourceShape { expected }); + } + Ok(()) + } + + pub fn bit(&self, index: usize) -> Option { + (index < self.bit_len).then(|| self.bytes[index / 8] >> (7 - index % 8) & 1 != 0) + } + + pub(crate) fn zero(bits: usize) -> Self { + Self { + bytes: vec![0; bits.div_ceil(8)], + bit_len: bits, + } + } + + pub(crate) fn word(value: u64, bits: usize) -> Self { + let mut result = Self::zero(bits); + for i in 0..bits { + result.set(i, value >> (bits - 1 - i) & 1 != 0); + } + result + } + + pub(crate) fn hash(value: [u8; 32]) -> Self { + Self { + bytes: value.to_vec(), + bit_len: 256, + } + } + + pub(crate) fn set(&mut self, index: usize, bit: bool) { + if bit { + self.bytes[index / 8] |= 1 << (7 - index % 8); + } + } + + pub(crate) fn uint(&self, offset: usize, bits: usize) -> u64 { + (offset..offset + bits).fold(0, |value, index| { + value << 1 | u64::from(self.bit(index).unwrap()) + }) + } + + pub(crate) fn concat(&self, other: &Self) -> Self { + let mut result = Self::zero(self.bit_len + other.bit_len); + for (start, value) in [(0, self), (self.bit_len, other)] { + for i in 0..value.bit_len { + result.set(start + i, value.bit(i).unwrap()); + } + } + result + } + + pub(crate) fn option(value: Option, bits: usize) -> Self { + match value { + Some(value) => { + debug_assert_eq!(value.bit_len, bits); + Self::word(1, 1).concat(&value) + } + None => Self::zero(1 + bits), + } + } +} diff --git a/policy/tests/common/liquid.rs b/policy/tests/common/liquid.rs new file mode 100644 index 0000000..7c4ca78 --- /dev/null +++ b/policy/tests/common/liquid.rs @@ -0,0 +1,258 @@ +use simplicity::{ConstructNode, node::CoreConstructible, types}; +use simplicity_unchained_policy::liquid::{ + self as liquid, + elements::{ + Address, AssetId, LockTime, Transaction, TxIn, TxOut, + confidential::{Asset, Nonce, Value}, + encode::serialize, + pset::PartiallySignedTransaction as Pset, + secp256k1_zkp::{Keypair, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}, + taproot::TapNodeHash, + }, +}; +use std::sync::Arc; + +pub fn unit_program() -> Vec { + types::Context::with_context(|ctx| { + Arc::::unit(&ctx) + .finalize_types() + .unwrap() + .to_vec_without_witness() + }) +} +pub struct Fixture { + pub pset: Pset, + pub program: Vec, + pub cmr: [u8; 32], + pub data: Vec, + pub network: liquid::Network, + pub base_secret: SecretKey, + pub user_secret: SecretKey, + pub blinding_secret: SecretKey, + pub base: XOnlyPublicKey, + pub user: XOnlyPublicKey, + pub recovery: TapNodeHash, + pub other_asset: AssetId, + pub source_parent: Transaction, +} +impl Fixture { + pub fn new() -> Self { + Self::with_program(unit_program()) + } + pub fn with_program(program: Vec) -> Self { + Self::with_program_rng( + program, + &mut liquid::elements::secp256k1_zkp::rand::rngs::OsRng, + ) + } + pub fn deterministic() -> Self { + use liquid::elements::secp256k1_zkp::rand::{SeedableRng, rngs::StdRng}; + Self::with_program_rng( + unit_program(), + &mut StdRng::seed_from_u64(0x53555f4c49515549), + ) + } + fn with_program_rng< + R: liquid::elements::secp256k1_zkp::rand::RngCore + + liquid::elements::secp256k1_zkp::rand::CryptoRng, + >( + program: Vec, + rng: &mut R, + ) -> Self { + let secp = Secp256k1::new(); + let base_secret = SecretKey::from_slice(&[0xcd; 32]).unwrap(); + let user_secret = SecretKey::from_slice(&[2; 32]).unwrap(); + let blinding_secret = SecretKey::from_slice(&[3; 32]).unwrap(); + let base = Keypair::from_secret_key(&secp, &base_secret) + .x_only_public_key() + .0; + let user = Keypair::from_secret_key(&secp, &user_secret) + .x_only_public_key() + .0; + let network = liquid::Network::LiquidTestnet; + let recovery_script = liquid::elements::script::Builder::new() + .push_int(5) + .push_opcode(liquid::elements::opcodes::all::OP_CSV) + .push_opcode(liquid::elements::opcodes::all::OP_DROP) + .push_slice(&user.serialize()) + .push_opcode(liquid::elements::opcodes::all::OP_CHECKSIG) + .into_script(); + let recovery = TapNodeHash::from_byte_array( + liquid::elements::taproot::TapLeafHash::from_script( + &recovery_script, + liquid::elements::taproot::LeafVersion::default(), + ) + .to_byte_array(), + ); + let data = recovery_script.to_bytes(); + let cmr = liquid::policy_cmr(&program).unwrap().to_byte_array(); + let cooperative = + liquid::cooperative_output(base, cmr, &data, network, user, recovery).unwrap(); + let blind_key = PublicKey::from_secret_key(&secp, &blinding_secret); + let funding = Address::from_script( + &cooperative.script_pubkey, + Some(blind_key), + network.address_params(), + ) + .unwrap(); + let recipient = Address::p2wpkh( + &liquid::elements::bitcoin::PublicKey::new(PublicKey::from_secret_key( + &secp, + &user_secret, + )), + Some(blind_key), + network.address_params(), + ); + let other_asset = AssetId::from_byte_array([0x51; 32]); + let source = Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: vec![TxIn::default()], + output: vec![ + TxOut { + asset: Asset::Explicit(network.fee_asset()), + value: Value::Explicit(100_001), + nonce: Nonce::Null, + script_pubkey: recipient.script_pubkey(), + witness: Default::default(), + }, + TxOut { + asset: Asset::Explicit(other_asset), + value: Value::Explicit(70_000), + nonce: Nonce::Null, + script_pubkey: recipient.script_pubkey(), + witness: Default::default(), + }, + ], + }; + let source_inputs = (0..2) + .map(|vout| liquid::ProvenInput { + txid: source.txid().to_string(), + vout, + parent_tx_hex: hex::encode(serialize(&source)), + blinding_secret_key_hex: None, + sequence: 10, + }) + .collect(); + let parent_pset = liquid::create_pset_with_rng( + &liquid::CreateRequest { + inputs: source_inputs, + outputs: vec![ + liquid::Output { + address: funding.to_string(), + asset_id: network.fee_asset().to_string(), + value_sat: 100_000, + }, + liquid::Output { + address: funding.to_string(), + asset_id: other_asset.to_string(), + value_sat: 70_000, + }, + liquid::Output { + address: "fee".into(), + asset_id: network.fee_asset().to_string(), + value_sat: 1, + }, + ], + network, + lock_time: 0, + }, + rng, + ) + .unwrap(); + let parent = parent_pset.extract_tx().unwrap(); + let inputs = (0..2) + .map(|vout| liquid::ProvenInput { + txid: parent.txid().to_string(), + vout, + parent_tx_hex: hex::encode(serialize(&parent)), + blinding_secret_key_hex: Some(hex::encode(blinding_secret.secret_bytes())), + sequence: 10, + }) + .collect(); + let pset = liquid::create_pset_with_rng( + &liquid::CreateRequest { + inputs, + outputs: vec![ + liquid::Output { + address: recipient.to_string(), + asset_id: network.fee_asset().to_string(), + value_sat: 20_000, + }, + liquid::Output { + address: funding.to_string(), + asset_id: network.fee_asset().to_string(), + value_sat: 79_000, + }, + liquid::Output { + address: recipient.to_string(), + asset_id: other_asset.to_string(), + value_sat: 15_000, + }, + liquid::Output { + address: funding.to_string(), + asset_id: other_asset.to_string(), + value_sat: 55_000, + }, + liquid::Output { + address: "fee".into(), + asset_id: network.fee_asset().to_string(), + value_sat: 1_000, + }, + ], + network, + lock_time: 500, + }, + rng, + ) + .unwrap(); + Self { + pset, + program, + cmr, + data, + network, + base_secret, + user_secret, + blinding_secret, + base, + user, + recovery, + other_asset, + source_parent: source, + } + } + pub fn context(&self, index: usize) -> Result { + self.context_for(&self.pset, index) + } + pub fn context_for(&self, p: &Pset, index: usize) -> Result { + liquid::Context::new( + p, + index, + self.cmr, + &self.data, + self.network, + self.base, + self.user, + self.recovery, + ) + } + pub fn service_secret(&self) -> SecretKey { + let secp = Secp256k1::new(); + Keypair::from_secret_key(&secp, &self.base_secret) + .add_xonly_tweak( + &secp, + &liquid::service_tweak(self.base, self.cmr, &self.data, self.network).unwrap(), + ) + .unwrap() + .secret_key() + } + pub fn sign_all(&mut self) { + for index in 0..self.pset.inputs().len() { + let context = self.context(index).unwrap(); + let secret = self.service_secret(); + liquid::add_signature(&context, &mut self.pset, secret, true).unwrap(); + liquid::add_signature(&context, &mut self.pset, self.user_secret, false).unwrap(); + } + } +} diff --git a/policy/tests/fixtures/golden.json b/policy/tests/fixtures/golden.json new file mode 100644 index 0000000..9efafe2 --- /dev/null +++ b/policy/tests/fixtures/golden.json @@ -0,0 +1,71 @@ +{ + "base_service_pubkey": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "control_block_hex": "c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac03333333333333333333333333333333333333333333333333333333333333333", + "cooperative_script_hex": "20cb01d205c1626b3c40fbe6a3abf4057ccbef8827862660d8387e273d5a1ede77ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba5287", + "first_jet_identity": "80c76efa3957253ada5235c3712bbeb4e657edb12024ad6d53dbd5959fa49e08", + "input1_approval_hash": "34fff27f49d6e8c7849c0e7d7004b1e5ccf036dcf961b7d72130ddfef2af9400", + "input1_sighash": "cdbb709ef98f89ad1186dfdae2ae2f62d10cdf5b2fee4c9991536ab682c793db", + "inputs_vector_hex": "0282ec6355a87974aa10f291d00d0ec01823692e6609af4ef3cadb11815a0d375f00000000000500000082ec6355a87974aa10f291d00d0ec01823692e6609af4ef3cadb11815a0d375f010000000008004000", + "last_jet_identity": "71d088679098261ddf07257407d7586185557f94e81a362773a588f9ff6e15dd", + "legacy_approval_hash": "d8514802ea7b1393eba76c37475a2297a8054c6ac8d07ff6d808901c926d6940", + "outputs_vector_hex": "03204e00000000000016001444444444444444444444444444444444444444447011010000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff0000000000000000036a01ff", + "parent_tx_hex": "020000000177777777777777777777777777777777777777777777777777777777777777770100000000ffffffff0260ea000000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff409c000000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff00000000", + "prevouts_vector_hex": "0260ea000000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff409c000000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "script_data_hex": "706f6c6963792d64617461", + "summary": { + "chain_status_verified": false, + "cmr": "1111111111111111111111111111111111111111111111111111111111111111", + "cooperative_leaf_hash": "7bd0bac5a98b78850810e80fc2f4ba52d9fb257caa7fd31b5cab97a1dee7fe6b", + "cooperative_sighash_default": "5e07e701d2aef0126ce28bf14109432625420853079fe076a3d2a6d9b7925e60", + "debit": 30000, + "fee": 10000, + "genesis_hash": "43f08bdab050e35b567c864b91f47f50ae725ae2de53bcfbbaf284da00000000", + "input_count": 2, + "input_index": 0, + "network": "testnet4", + "output_count": 3, + "outputs": [ + { + "index": 0, + "script_class": 3, + "script_hash": "eed5299e54314101bf96cf6629731b49e7725f7f1f21c4766f2802ad4fc72323", + "script_pubkey_hex": "00144444444444444444444444444444444444444444", + "value": 20000 + }, + { + "index": 1, + "script_class": 5, + "script_hash": "3d7c81f895977f39408a22ad211d4875630acb989b020fafffad6e069ce20e1a", + "script_pubkey_hex": "5120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff", + "value": 70000 + }, + { + "index": 2, + "script_class": 6, + "script_hash": "aa00e76c72d1b6f643cab95a05afbba749fe65779e06c73fab3e92cb4eb52803", + "script_pubkey_hex": "6a01ff", + "value": 0 + } + ], + "policy_approval_hash": "062a75e6e3706ad74f18463e9b51277bee8fb45351521ad2018f236e0dbfef0e", + "policy_transaction_approval_hash": "5a9230d8b7c96d56fdc47522776083d13aa1c46628fcd463792ff7db9b849b60", + "prevouts_hash": "73691b188ecc87c1d229cbb1c6e260a2cb7a12543910887ad766535eaa1db948", + "profile": "bitcoin-policy-v1", + "profile_hash": "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7", + "provenance": "parent-txid-verified", + "recovery_leaf_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "redeem_script_hash": "8280a6867215902acfd8366b78d4539799dca52bbb6748442e58c7f21f76ea9c", + "script_pubkey_hex": "5120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff", + "service_pubkey": "cb01d205c1626b3c40fbe6a3abf4057ccbef8827862660d8387e273d5a1ede77", + "signing_commitment": "0487ececd95595353bd2869bf2a082470fa8a0fe30228501a7f771b22bd017c5", + "total_input_value": 100000, + "total_output_value": 90000, + "unsigned_tx_hash": "d89d8918d21e6630119007b805a45abaa0a66acb0d01d18dcf362d11386dff9c", + "user_pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "vsize_lower_bound": 178 + }, + "tx_inputs_hash": "42859848dc6332d0814e635ccf14a32bbc98c78626c863868bf5f0e48f88aded", + "tx_outputs_hash": "af628a196b767214e6293c7c22fd80e7807dfc7e485751a0b7e33639dd026c60", + "unsigned_tx_hex": "020000000282ec6355a87974aa10f291d00d0ec01823692e6609af4ef3cadb11815a0d375f00000000000500000082ec6355a87974aa10f291d00d0ec01823692e6609af4ef3cadb11815a0d375f01000000000800400003204e00000000000016001444444444444444444444444444444444444444447011010000000000225120b40ddeace052510c2ab220a6e8e7f5beccce44f8c01afeea2c84edaaa48913ff0000000000000000036a01ffe8030000" +} diff --git a/policy/tests/fixtures/liquid-policy-v1.json b/policy/tests/fixtures/liquid-policy-v1.json new file mode 100644 index 0000000..1a8f2f3 --- /dev/null +++ b/policy/tests/fixtures/liquid-policy-v1.json @@ -0,0 +1,268 @@ +{ + "base_public_key_hex": "b98a7fb8cc007048625b6446ad49a1b3a722df8c1ca975b87160023e14d19097", + "blinding_public_key_hex": "02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337", + "cmr_hex": "c40a10263f7436b4160acbef1c36fba4be4d95df181a968afeab5eac247adff7", + "contexts": [ + { + "assets": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "debit": 21000, + "fee": 1000, + "returned_value": 79000, + "total_input_value": 100000, + "total_output_value": 100000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "debit": 15000, + "fee": 0, + "returned_value": 55000, + "total_input_value": 70000, + "total_output_value": 70000 + } + ], + "chain_status_verified": false, + "cmr": "c40a10263f7436b4160acbef1c36fba4be4d95df181a968afeab5eac247adff7", + "cooperative_leaf_hash": "ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff64", + "cooperative_sighash_default": "450a607ec5a5f4a0e2222ffe89514fd87d97352317626d4f3a10c8f53e1cd0b3", + "current_asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "disclosures_hash": "2fb3b25fdcca52da2f122260c3445efe888a21fc73ceea934faba044de27abc1", + "fee_asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "genesis_hash": "c1b16ae24f2423aea2ea34552292793b5b5e82999a1eed81d56aee528eda71a7", + "input_count": 2, + "input_index": 0, + "inputs": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 100000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 70000 + } + ], + "network": "liquidtestnet", + "output_count": 5, + "output_scripts_hex": [ + "0014ebc0ee0b2ab9e8277a600c251475e22a3241a1c1", + "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "0014ebc0ee0b2ab9e8277a600c251475e22a3241a1c1", + "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "" + ], + "outputs": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 20000 + }, + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 79000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 15000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 55000 + }, + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": false, + "confidential_value": false, + "value": 1000 + } + ], + "policy_approval_hash": "654dcc2dff2354dd6abdea5fe140de7c9a837603d3d3295a964d95008bed36bf", + "policy_transaction_approval_hash": "6fdd760d3e1f701ee9d7e6186bff0e47c2255b82742d5b7277e67aeaaefc2e2f", + "prevouts_hash": "07391e1a348a359f89a64d01afecfa320174a2ebe486f8591491edbecd7a2651", + "profile": "liquid-policy-v1", + "profile_hash": "6097ac61cbd923269edaaed8cf14691cc8ba756e45949f7478f7f907f42d8ce2", + "provenance": "parent-txid/vout + exact witness-utxo; exact-value and single-asset disclosure proofs; transaction range/surjection proofs + Pedersen balance; per-asset checked conservation; existing signatures verified; NOT chain inclusion/confirmation/consensus-validity/unspentness", + "recovery_leaf_hash": "47f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b28", + "redeem_script_hash": "b27b31dc273d4aca3e664b317b56ee3bdcce7e2faccdcefdab496ac8abdc6a55", + "script_pubkey_hex": "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "service_pubkey": "5b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9", + "signing_commitment": "14460853d9861de13a6ba409ecf419309b7aa0780cb9ddabee570cf4a92a5006", + "unsigned_tx_hash": "c780a1594c7e3980066340eb84c5d80d761492d81abc5e1d19353e3c62f46961", + "user_pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "vsize_lower_bound": 649 + }, + { + "assets": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "debit": 21000, + "fee": 1000, + "returned_value": 79000, + "total_input_value": 100000, + "total_output_value": 100000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "debit": 15000, + "fee": 0, + "returned_value": 55000, + "total_input_value": 70000, + "total_output_value": 70000 + } + ], + "chain_status_verified": false, + "cmr": "c40a10263f7436b4160acbef1c36fba4be4d95df181a968afeab5eac247adff7", + "cooperative_leaf_hash": "ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff64", + "cooperative_sighash_default": "ce3d84b39093e57007382401002c707a97028f3f057ad34b45585a03a9c47686", + "current_asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "disclosures_hash": "2fb3b25fdcca52da2f122260c3445efe888a21fc73ceea934faba044de27abc1", + "fee_asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "genesis_hash": "c1b16ae24f2423aea2ea34552292793b5b5e82999a1eed81d56aee528eda71a7", + "input_count": 2, + "input_index": 1, + "inputs": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 100000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 70000 + } + ], + "network": "liquidtestnet", + "output_count": 5, + "output_scripts_hex": [ + "0014ebc0ee0b2ab9e8277a600c251475e22a3241a1c1", + "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "0014ebc0ee0b2ab9e8277a600c251475e22a3241a1c1", + "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "" + ], + "outputs": [ + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 20000 + }, + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": true, + "confidential_value": true, + "value": 79000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 15000 + }, + { + "asset_id": "5151515151515151515151515151515151515151515151515151515151515151", + "confidential_asset": true, + "confidential_value": true, + "value": 55000 + }, + { + "asset_id": "144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49", + "confidential_asset": false, + "confidential_value": false, + "value": 1000 + } + ], + "policy_approval_hash": "0bdbdb55e8050d1389381f40367acc680aa158ddca0fb1e566aad6fd4d67b356", + "policy_transaction_approval_hash": "6fdd760d3e1f701ee9d7e6186bff0e47c2255b82742d5b7277e67aeaaefc2e2f", + "prevouts_hash": "07391e1a348a359f89a64d01afecfa320174a2ebe486f8591491edbecd7a2651", + "profile": "liquid-policy-v1", + "profile_hash": "6097ac61cbd923269edaaed8cf14691cc8ba756e45949f7478f7f907f42d8ce2", + "provenance": "parent-txid/vout + exact witness-utxo; exact-value and single-asset disclosure proofs; transaction range/surjection proofs + Pedersen balance; per-asset checked conservation; existing signatures verified; NOT chain inclusion/confirmation/consensus-validity/unspentness", + "recovery_leaf_hash": "47f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b28", + "redeem_script_hash": "b27b31dc273d4aca3e664b317b56ee3bdcce7e2faccdcefdab496ac8abdc6a55", + "script_pubkey_hex": "5120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e564601", + "service_pubkey": "5b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9", + "signing_commitment": "14460853d9861de13a6ba409ecf419309b7aa0780cb9ddabee570cf4a92a5006", + "unsigned_tx_hash": "c780a1594c7e3980066340eb84c5d80d761492d81abc5e1d19353e3c62f46961", + "user_pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "vsize_lower_bound": 649 + } + ], + "fully_signed_pset_hex": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000041144d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff6440f03d70340d0c5400e6d51e7501164e101f3318abd8ba53fcbca067986cdb86672846f849485a6afb30d68723cf5b829a5c7c01b137db3261f3f9181fb07f01cd41145b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff6440de2a749d4c8bcaf771c7023787defe929d1dc1a871327bc2c86cd6f6feabcd183a4a61eb4313c173020ca368b0f71b9352e7644bfa13933529d485fba01e33e74215c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b2847205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba5287c407fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000041144d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff6440a7d435d10560718f371e808ec3e60c2efd919085f56e1e261297cb6f04f7eb488a432d40c56f6bd318acc773a766b9c26a266f2d1c40ffde785bd67e270358b641145b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff64400e6e71508734fc4e30cbd9b477edce5a447240bd4a1d1eaeef195d7f7bc20ea035fc5e5f6d2531690e3ed8e66aa0a30ba5ba60d61cf2d8ee4b0d632816a12bfa4215c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b2847205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba5287c407fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "negative_psets": { + "input_amount": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc047073657411089f8601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "input_asset": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_input_asset_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_input_value_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_output_asset_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264400010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_output_rangeproof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_output_surjection_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_output_value_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "missing_parent": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "output_amount": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308214e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "output_asset": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "wrong_input_value_proof": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000" + }, + "network": "liquidtestnet", + "parent_tx_hex": "020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c30760000", + "parent_txid": "dddecc2887ef5cfe6208d9b87191101de7cf272e1ccb481ea1fc5e347902f800", + "profile": "liquid-policy-v1", + "profile_hash": "6097ac61cbd923269edaaed8cf14691cc8ba756e45949f7478f7f907f42d8ce2", + "program_base64": "JA==", + "pset_hex": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000007fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000007fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "recovery_script": "OP_5 OP_CSV OP_DROP OP_PUSHBYTES_32 4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766 OP_CHECKSIG", + "redeem_script_hex": "55b275204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ac", + "service_signatures": [ + { + "input_index": 0, + "public_key_hex": "5b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9", + "signature_hex": "de2a749d4c8bcaf771c7023787defe929d1dc1a871327bc2c86cd6f6feabcd183a4a61eb4313c173020ca368b0f71b9352e7644bfa13933529d485fba01e33e7" + }, + { + "input_index": 1, + "public_key_hex": "5b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9", + "signature_hex": "0e6e71508734fc4e30cbd9b477edce5a447240bd4a1d1eaeef195d7f7bc20ea035fc5e5f6d2531690e3ed8e66aa0a30ba5ba60d61cf2d8ee4b0d632816a12bfa" + } + ], + "service_signed_pset_hex": "70736574ff01020402000000010304f4010000010401020105010501fb0402000000000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04000000000110040a00000041145b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff6440de2a749d4c8bcaf771c7023787defe929d1dc1a871327bc2c86cd6f6feabcd183a4a61eb4313c173020ca368b0f71b9352e7644bfa13933529d485fba01e33e74215c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b2847205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba5287c407fc04707365741108a08601000000000007fc047073657412492000000000000186a0d597abc91c9d969c6ee54c881cad45fda078d88d03654bc6f99b55bbe3324a4c451a4d97615bba6f0b35187cae2ff652789a1c6fe60f9be4b8198056ee482a6c07fc04707365741320499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc04707365741443010001896b30a0fa6942dbc5ab0103cf045475c7a7237633f232b4929ff931aef69fe1a041aadd3afa38b6b994569546c4d10c092c2cbc35e0417718cfe48d6b1af1a6000100fd0923020000000102438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549600000000000a000000438006ddb4740db5e5462d1e014253e776e7aa5cd33f0eabd6fde5cbe7a2549601000000000a000000030ac27ae75a421cf175ce9ef25b2a03ea195546a476f577f0e52e889163a1f22a7c08fb15d11edb389b6e5da7635e8163f6920aec0f76e9fbad33ff995d5af1d6b2f103bd25ede1d8ae3455381d04a70ee53f33a9ba0e3cfd3c541ce8e4764a98b26643225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000000010000000000000000000000000000630200037c61bed46ab8a28a2dc28d94d960f89f9ba313c7de5b4e1e081bede99de907c62e1ddfbaa96b35549fa9a662bde4aac50288111c1219feb258bc5e61e2b078d243402bfffc88add58c5b6706b1cc5d8b34015b3f83ee4dc41931048f654fc53bfd4e1060330000000000000001cd9e0f0006e2bc4886d99aafd8eec0f65323a64f9a04cc973c85651638941889af1c7a48d502412171095f314e14a1f3460e2ab19e7d19d57efef98a305dba4719c9a66bd8e13f20747489cd32752ba53dffa144544602c8c9fa82576d942fbf31999a5d549800b93107cc8df25033aa5b8b1ef97eb2086e624ccb63bf839673d423d1e2858a0cf4bd71433f3777ea91c8fbfa204b4c6f0817abf3efc72d8b9b55134974c3850b0c1ffb67248283197d53a3e75c26b29ec7f7b88b16261718549e3265e31df6c0f32ab5924323218f3edefc00ec7c0b507f87199e5a5f29670a0eb6775ab25eae2540233c156291c9297be7baa3e3044846b6979a713d0ca2a7ff3ab5f73e9d4d99dac78ba7a67f1cb4eb21d47bcabaa2e3234f6c838db88c80c963dfa853fc0e3902b3e5d15204a3fbfbe35f97419e72f33fcdfe1096089ea34c7d175690993790bc8fd2ab379c0c635e8977a4e774dfb3785c43af528a33073b90837b4387334a11d9dd7d6e19da7e08b5b9c271c31a2c53c75bc445cd90273d36ff764d20521b63911abb34dc90e6cd78b0f25561e99172340c0dd5d125d9a6f89236aa2d534ded99bbef63a6395dbf7408771b1128e340d3800681c274790e2f276abc285fe48d2163fd8c660efbc15bb91a953b2b7ad342ad8cbfc062c36d03a6c1c3965048291c14d6c252a27cada52f7e419b3cc6e05e92c82784924e5b564813799350de210110b15ca3c4d627c7430167b6ec6f89b32f66cf8ba4078b7c59839b56bb80b51c37c668285c5750888fbab6c52c813f8c18e97fa5a511e1bfd1a062a55ae28df6da86a3cf3fb2d408e5fac47d8a2f68e7a7470b7979372aff740b01ee76eed1310d32fdbf8944a88011d2e9ec9eca22efa3904abd86b6763e11880cf8331f0d2d2f787c850aa8e899fe3103f9dd05ec988b567ed22da26a2f0e1105d97ad39ff80dde96d3f7b8e1a31c944bc670da60d2b75e05d3f28252f26d34af65caa5677609116a9276474b31ae70982f5963e2870c79ab80fed480633843b2816a03d72be57ccc950495d01c92acb9a3d53c065679b1e10214c5e8ff0e6edf4c32e41dce6dc0333b3558166814025d9a20979865d2431062227d05bf0dab05478455885887d63dad82f96a58a26b6282282457ab4184b6d55fdc808ca79e8e4e435ca6f34d34c1040eb4bfe5f453d0455a2393da32fe31582f79886c01d69e03813f6699e652865c87cfd635288b581d3038d00a916ae57c52b6bb491ef3d6a06bea03a16c173025ebaf4062fb19809d050f2538067945d0e4121d0a25e9c2c38da5b7b1af26e2da65b30625e10b62024212a0d882357909c9653a345546a8d35d1523187d5a55e35be8a0cc383d6eb6c3cfb4bfed3eba2e453e2e79c6bcda45e2a42aef17bae94b7569e08d08b69404dcb63d1e5025cff203be5e21ffa91961d468dfdbd7a31fe6249eec2a9ed39fa7651d004104ef15a11b0578476fb4cb8b30123b104b784e8eaa320bcce0c5ca60ad98802c786633127a81858bd5d46fa75c9b04cd41fbdc13de80015a608dd0fd2c188bdd273a5ca037299a0cd7696327052699f6e0a826707fe52e37a98ed74111b7437a84064ee3a056f105a436d5c3f213df0e4134a9754232ffa0c10b2affdfb3eb5b5dc8658137e0aa443584ce78337f28191e80c317716b3b86faabe17efb901755f40540596f2cc2c3f03a62df9cf1a0af75551d6ca181c84a1f2e038f25357f3eefb60cf6c401b84cfaaf1608c4a59a34c1b3b533da8f8226a1dd6bbbd3f2222b985f775f3bbb454ed3998e647e6ecbdd592c529dd0e9f241d536c7cf77f6097632a3ebc92369fdc2960c0e08daafecfea81697bbc49f7d1a1a77513a34f2a412175b4d4f605647d38843db33ca06959160b90823f100475835efc2cf9866e76f7555a0ab6c0a5c720dba5b097544381e42235131cad5b883e0d4b226b4209efd669f0927b4c015cf287579e43f13a25e16bedb8ea8da66f2206d46173eb2c8306b8fecd92619df6b99ca34ed252388b246ecf7c82aa8d9bf2fb32050cc4a887ec7ee36ffe6bab63c43fe32b86f8a2f14fcc05725922b7f54cc1bc648cd91e54c3be4033db0409c53a2ea412e4018a756fc75437b3cd5ba28cc267751731c785d7986e0c94f0eb53c9fcef9e642877a3c0e5c24a9e5c46215e3b4a7bfbd8b6c4618cee2228eb889ac5617a94876bca78e7fc172f1fe4dc66777db5c8e33e00fa91987eafbffd6399c7f5d9cadf9e22a246bb7dcc07c8581487945b8ab495b3c9a5ec9f96bde0891e37a094ce016bcc85b929202f7fbd373383a7116505bfd9a3d995ea37fda567239e45e1253481ad944a97eb5d9e52f6c322e978747be952afd8e5ecdc22bef1c575f077118bfed6441ba65a87e20029df71017101dee1f31ae15ebf723f12841a9df3a9d3f48868bceb167a7788494e1bf015d102e8cd7366ed34cfb0b47f6a39923158f2b81c87090ebce0c05f3082f8be1024317d9250a7300027875bd5e038d1f775f8185aed06307a6872f41c8e03fe7a976689ddd3054e77f036ee6477b2c6c659d8b6fe57e361d02099f4f2a17e6cd86dfd6a7b4483eae9b54ce19068c256efea74fe41d2412f2f0955424ed13c7f79a20af8dc5b526e61800e40262228eea6f06274ca72518e66d1a10b83c2d1f3d7265b79da8dc87e6d31091b3f2bf144d72327084987e87c1dfa0e5ea6ff40064582ea54abfaa161f3699583b5fbc39e353ee1c6ea93bb747960cc6df6410b89921902b764daac7db730ff29f744e1be9315d585da99df5daa3a52c51d937ffdafa4fa3ea3cca3dc8f5204966590017065c46763ef34d4c91c5a082e1ec55ec9036414ed586422f42380bbef5c1ddfa6d18b98ac2e3510dd8121709153093e4e8b48bdcd39a871866e4ae9d51492af9c5aeb165e7adc657c59d4fa39223704a46a498a7aff31152fa271edeaba2fe41dc0c816d6a78796cffee66e25e97c82ee69b5a7c98561dce0e41ae21aa28659a8f5edda3d0038a698e306d1ac6ead034dc65dd1d79ea184dee71674f64533808144865ac7fc4efbba6d36792a804694cc7f0e49efd00261b2a8dda6909e542ecd03b45c527c5bb640982d1b1e17c35bee71ded1c838487a020f22272762cb60860bc78c8c4e157d41e2a8d7dd2e58cdc572b49cac69917df30033840de8a11def7713854a431bccf3b7a1f9880802a1241b6418e2974ab37ba5593ba925bdc8e5064c23989c709a4f08bf8f4cf7e5c76f6bed680d2b327630eeb7926907ed7fea613c4b15f5d9b9c74bffd335a2a412345b89c20444869ef995d0f7755f1c130e768e65115ce958b4cc4f76ac7f582c60b72a0fe31bbb463175c83eb673fb3080befaae06568708040bec53a59bebbbe6d300c34a5aee4c6353f45a5b88b337f9e8154432ea3edc10c5a44c905bd1b30ab2b8a6a861c20de337688fc07d19b3308b13d6e8ec45baae805fbdd0eb412b532b6c667b5fffb4030d6ad3029fa21b0c93f8fa7d296f69829179083995d8081127b9b5f5efc920eddd80135c9bb73fda2ab2c4469297fd5b05aea9bd97c39ece54d70cedf8bd0481721a6f977e139e9d23953b3749e2bdfc78ae88d047b8af3846153354972f9d0fa57e6e34be225918e6996d2e4b8e116fcd3bdb6b6d963b065c6f2d5e00ab288bd300ebc01d01348f373c7b88d088af8a87c9f650a1f3501ab9d08668f9302ff31a9df06a58527c6d953649a2cd56514f43f0b82c4e3861e513bd7abdddf7ebb90b7e7bf6cffd0bb8c44b244cd80e2c034c5c549d139ee72a569728da00be436d74617d466a240f362fde561be647f7eb43e61a61437ac32ce56c1d70bb723d56bb3b5267818442e1f2631bd9b55b0570341dc4390a0186e514207bb3e3673e591562fed7d795b3ffabb498b72fa6727e11ff0bb5fa95686d59a8d3f5974df5908af0ebee0c24bdbb66e5dc8723519ba01877ea0b30d36f93f282a856189210a5c63d6861a87aeaddc9e1e38ce4850f26632dcecab23ee5cae6277dba2b1db4081b73cfdc4b206a960ce86e421c581b91edeb0dd4a2bd7ce80738fca9aedfaf5aec247c01525fea0e2f591f85cdb24f7e5197d9789b4dca30b18d7b8a0ab59392476bdcacb5cad2582f149197ba1958ca56c3a4babeca729d9d92c1506fa51c80a368e9285f71f4c9e7e43e31584b665279ffdc442f595bc6db8d197b2974142e75fac511553ca18062fc89a3c8f2d946f02bc2d6deba91270ed2ce506f6f90815e048733e000a344ea28a489900627fe35eb34f42318f1cc1ba51949c101bd03b98281a1c050cbfbe3cafeb2defec4d6e7d07fd9cc5c389a9955b2f35eec5dcd548da62ad8772f4c67557fbb79e4538d8b9738132ce2a27639f300e088bd2f19ca5ec8450cf22069980828e036f38909cfe98b27feb20783ecb942dac12ee9e2430ef39745f8a873295f219e2a3ab80ad53953482345a54664d93b291a8dfb2ba59d44ca4928adb57cb9d10e7df1aeb7c225f6ad45f518986db5d07981f5c66b5ceae6b80d52eb4ffa7b4c58d2ea7e482ac5bc119d79ad657f3ca8132d25c615aa77c21b65ce1fcf675345ae27f8a4b211457667410b9bf2a845ec14c0ddd416ec13b3c1ffc763d5e8bae9a8b9844f6ae4bc9ce9fa6439967253ea6ee9fd34f5c47ab09d58d4dd8a337e184446affd88ff0408f8f80989def033741659709533359dda2a47b1255c434385ed0c326b1440bcdd010c3c0b9c59c5450ec719c39ac76b5afa8b40509c16e1ad8f0ce29602b558858488f026f726e12db073d8180d9045182022d4d7770d53715a8789a6a6733784685d5b604d5afb51a107ee439768dc102934b4defa8cdd8dffe72d0ff98c8fbae61e09d70feb9d6c4ad137f9522a17221531e6b08d87c7be6b2801b9fb39096a2a403035d6c928f48bbef680d47f94a57dc073e1ba325905e6205a499dee5f04e734d9d0718f9435cbdd0802b163dc219204384c87e6f8cb572234d276fdaf6de4e4e50e569b6bab4abb16e21e7267898ee5bd58230d525b44571d8c606cf0da24d2c28f0a6ca118b07befcce5cafcea3c51e1a0b8fa423b83bd9ad78b9e6f0350e0779d62d7283cba85029c02a428af9f87cc4e1d84f403a8acd16d0c7e3ecdcfffc8a5c6bcb0556f46ac753ecba53c644196487b739aaeb586cb4154dafd929becf609d2ce3bda727e16a610a7e837967787cc6ec2e1f37f64d6916c4c9ebeb356fa6cf5adcba538d490cf14331c38667918ad1330444945b3f370281c14fb7265b1a0b615b94658552b16c9ebfde7dcb1d17873137f9015767a8e7cbd034d90a04f16419b77a9bf28ad29f90b4f4a8c8785ec8ecd5ab8ac385486fad1c4cc0ae8fe6bc311dcb6149faabfca0ce26e561085bc4d3cc034eefcf30e8ef20745ecfe679abbb4c7eccae38a2e68e9ad1d2539f32b5099e4adac6d566ff7ccfd4bd74c7060397d57fb462a6a903b3ea604d8dd744f13f1432b240e6aa7285ff5905350a03fac29a8f46fed49b42ca33746413b6bb2ce3689ff6251ced55a3adb992adf345824209f282034f7f549d0f1e4675070c67ddc68bc30423c60edec2beb9a728835cf27116510eeffe2378270a39deeaf7d8b46a3ab5cab737cf3a43aa14c914f061b460a4f4ff345bf7e2465e16bd2c89c82bb8ab9a8a93a2bf8b9850b741e976c64425259bef6ad313afd3fd379211a3a0157779fa68ba29073a188ac1ea95988fe792c5f67f5efa1323c3887d8c987521a00931f459a394f528b63e8ba259ed8fd2992b700014a909adf45e14035a8ee91aefb4869801b823f3710ca8060fb9f6fd18e6ac74c60eddd9622ee99b29f36ab082630200032a3ecc7d56511299902116e76e7fd4d014da01981c4985bf9adaa75172210f7010812b5c2e94ff17f21fa23085583649b429125458adeddabad92f3d5e0c5f4d8da8a8df84be2520f811e2821af81f6f0ea90767fe231daeb0ee536f0a814cc7fd4e1060330000000000000001b23979015868a839472ba7c908d77bfda3ca1107b4c5cca9d5ea48a411a1a598e05e62d3dfe3af7261a523f97e5ce0b4cf7f8360ce836d8715a0829b6a7b45924e14f071ffda713ae07f9bc3752d8d48fdd54f387e3ab4c44dac08a6f2ddf7a82eb1817ca90aabe59a95f8485a0b9a946b371715996a5444ed65c7e2aab663734f5e690a52ed1394766b07d91a92b513155431ee4899432a93f8fa268d3d4ba950e1bd22a08e97951092ad82c0d130082515f4ca03b31c6a0272406960714c23bfb75899368c6cbecde5d47ad047034bfdc3c32f6df79c11629a1cc3fb1f8ce8cd23f44082c26d3ce5ea3c1ecdb1f987c6fcae12bf61cb986ea217cecc257ca3bc9b098804eb8aae641ee9b49529e64ca5956ab1f828ad9e6b7fca9bc9c852c8ca1b2a8ee5ded40e3fb1da4590c909377c5dfb22435a373a2200f279dd85bb3219ada199b25273df32e04b74b55c04cbc5ee270b591bee44ca30f7141b75b6fe5bbe138f849bf355cfb90f78f144d1197a0b81d98bc0a6519254b029cf0265424648b286cdd990d0455cecf2c8ba851a54d69d20ab5c25f7ad850a993c6db4e28f9fc5cedb652a07a056f33199a8fdd1f7a6ae2fb9e87925bc1b517ca01889a71f60f1b5b09b1523e94628922c996f0ddfad3f83037f121326c8023e710836be37ce3bafb06a00613c50aebbb40c29794283b178cc77ff70b1214498b4d58ad88bd6a003c70d332d2e99fc47eafbc580d644edb8e7271b4e9290bc660856e1f60f8b2ff25783d614c6ce8d806ab833024462fe27f2aacebee60d7edda09f5495f5bb3cd0049522df4cd165b9457d8dc2f7d7f2b16ec9de417352d0320a26d08d87498e6e4326757084a8dd83d4c6a96dda3fd488a0f3c81c8f16add7f513149794ae7bff216f3ab3b33589b264e3db2a74b62492290a2abc2aed2f8b3d1e53997320db68c256dd5c731ea16a62a10f6091bcbcc3c5faf65be75cbcf8c3f4198f2cc910fc2a824a5c21329977bbf22b4724a239d9acb1d4e35d87d879f8b3bba8934b07ed40680cdaaec703fba3298c84aaf7dfc4705a5103c7e2a1e67c1ce09c9374033c1a1cbc42cda3bfc7cd5e93386336ee833c5597712849b96d8f1e3baa8a1d14c97bd99a10f0a093cae61c2969aa83ef0e33cd89bdb58c51e07262216db98f8e09a321228816c3b1b16f59bd064c5ef28c83c297403fc4f49b70c1613e34791cd32360fcd18707cf5c4cda3572cb267e62e7d218aafb45d2c838a99c731079d5af720d875b899773efeec6419662027379978137d4159213c7326d39ee90068557156010a5f3b1a2e5ed665669ae470e78f87e29e33f914a088baf4007c924cfdc633259f8173270c8868fe0145a47777e8d8c4e7e7c149bc327cd3336b9d65a4554877fb6209bf5b1508ff4639fd9fbf703ff4ea77c4fbaf0ce54571aea19bcb50a614e8622adeab4fa7e6692ce440d787efe0d0f7be31401723d8cbd674afc7b5cf64ce003fa51551470fd684b21cafa26d2ea38aa2425ad3bee585be0da9fa7324c0182586f1507ee6964e4de38e381287c622958fe52e2db83b028e518fd96ef8a2733f15e895fb7395a1179b7dbff9685e9f00c8356d7cd7eb4f243f6a6bf81b435a5eb215435d4761d256c262a1655689e35bc1c388ff1a5ce4aaa76d4d3309f184d21e365ca2ebdadb73ab409574cb115f5cc5aa79e4acdd4f40411cb1f3433e8da07fd0701e97ae65ec0f11d1d64026675d7e1bd225838fb5501e53f5fe43c0bfc942692df0f16b39fcc51a784d0c2e14b3944a03562123a513f7b550ad19eee40be8d80a18b21b372a0f29830ee1d96dfc899d04aa229073d695fbe56ac4338bddfb3ad493106fc8bfcae4f7fa206638d55c30ff15b6dca3e79b0509ee2ef73d03fec515ebeae1d60304ace15cfc2627bf4f937d59ab4a2217498907b8d1de84c682c26398d51cab70092fdc00703ebb56a724f3937cc82b09f90ae1dac82a58ecdd0d4d63da1c2282a86cc4a1d064a352e0f0d3d06d17859c49e47d07c23250c28dc92bf435c01e63fcdbbcaf0bcadb4c843f0ff126212b3fa06e83d7f098702119d500da54eff757fba833e7406e4aef1d050f902962d23e9393eba5c7f59227fdf5d3938e3ebe0303952b712a76d51adc31eeed8f30fa69ac1c44f8938bd8e1c7d40c4386b7974354ad45ca12537e1b86bd1299e8e14760320ffeb813b8e3e493d9ff11e803e3ce44e9432ed9ffa7b2fd772d7b7de72c53f52301d08d4a6bbe2313941cca35e208cb42b073ffbb222b9cf7c37882bd3907068fe6769c0db85a8d1b86cf16cc262e907c0b2ec346d57be672d2ed1e0d665d009eea2245149e3db1b95e128036bf2b6942badcce352a839391ae6812cc71d6ad1e8c080a000a9efc9b353c8d7f673173e2a993b42ed1dcaad62375b544e9eed3a3311685504d0b1eaa790c2bfdc501ebb615c46880479dde638f35a5d8e6826f1a1f8acf60d4e034026b4b50fa5049d6fddac49184d3550b50e60327e1bbb684dc918af879f15bc2f7bb0befd5187e9a84211a8da7ffef8013362607bffb0c5dfd4b7b7ca6c218bec1752ee9fea4252b0c5c13f369d471256db6e5108fb9faf0dabc7f65fc6288fe95ac947409d126073ff7346d07df67dced80cf74d267b59a4665ad34a539dc3b2681981000793532213decbb26535ed8cecad08054b5ada8b01bf3f7a8849a53ed85e5010af8e3f64b4867d3912a73eb499cedd0aba5ea8ea614554f77c1f5cb49b1b2030c87e92965383fa3455576739b0d0661606fab2b5bed7bf5531ae82d870d64fced2956d8edac8decb242c8c2477fe8d15f8eedf469b0fb048ed2db821e6f165dc5ea14290cdc010fb64fc20c68a6cea47638cd3076dc148a066bd3dab459109fda5d8df81d6363a240fbf5ed2928322910d49f4fd8a68cf386db425c66e017766967380d4cfd9f86392114cb8d41c6ed4d2dfd2d1b2e96a45362eb87b77a827ead922ae00207a8fd97b0b10fd1f87faa43344a93927410403c00a636051b5489921302a8fd6b00bf0c6e95a7064b1e51b22b6469d0dacfbee9a5e4d8553a5f5be3e1d97c7237024e9fca86bb0b13fdd69e7ab651f165b6dd911691790b068a82e532437c855a37b4abaeef6bc095b080e4de0a38efac5660829d113747b4a599061167cadd596cc77624eeabb1f168d37ee83b88951664324f87f3ca7af588d96e7af97c4bb1dcea67c56ca60dc382bd16c198e2314558327fc610c0e518c8c8ab146c391edeb6b5d1f564f28e879729c8febe3576d12b3ff13c8215348f323455d66f1ecb97b2a8615b41d2b83f7488314bc248744ca843b9038964666458ecd5112f7b6e4a032ff7c34d351898f254b12120627c9217df05b86ef5b556f3deb30dec5fc0cb9fb6f1f25df4258a66b770685bc16d63215b22941dd2d1dfedf84bbcd447a23c4ce455244c19a3c36ec705abac2afbe5da6c3731c580e5d5676c6b4d5377d404955696600ebfa1cee92f9550dbac1308ab2cf1a83d193ce79ba1dfc748d7029addcb7231190da8f261e3facec43407dd4116cd8293c056278c887201f13fe590f45617387ebdec3d11244694f0806c104894555771aef34e08421075372a125dd8d19034d1d04378d3fc66fba744a0698d093a0fab12bd06bd3d00e958edee88810052adc6908361e0b3ec00afd8e552fc23976c7a9cee0e05e524a97752e70cd3121ee06ab43e9b9f75aa58ffa58853272e884af2900b9860b53e90f3fd828c99c9a2b3f09aa36153ff51aaf17180f082972ada33fd6bf78fe81e443b895669fb51f30c57797f6a80d61cc24a6e2db267c3861fa9ead1f4b6abada1bc33b474fd6967205afd13abfdb578f69344605a63bf8bcf6f2fd45d88bf8047c64a043bbfa3240692f4c63315f2202cfa1f6d75156aab8e8db7c2c28d99925d0de00573f1f53719a466145f0753190d98d8266000bda47651d6e84493312812a8b2b9cef7f65de0b7eca4caf5fd46b27d1e3cf5beae0d9b4f5e3bbc5150478949dea7e6c0f738b961e2053087f0c1080531f53bc625a3365e485d466846d174f0a0e87f5b5d9575d49a45304f30b5ba67c1e17d1bdaded5c3c26118a60fbabb6c334ae022a2f5ba116808be20e874d736c24f58649771ba8a097ad625ea593a69bef042aded211f5f36b044ded5bd4408e4cb671221de969c7df7a45efc55b0e9742f225e1035ff94b4862731aee6ba46a85394bc36835e6eb25233667d6eedc2aa64c50c5ea43c77504ebda19286f7c0dc97b41fb6e5730b7cf59509a684de5e7f63231eeea91de4e5971c05cac7c5ad2a0f6698e684fdc433d817f2e8a941643ff36b7e70b496e3bab683789be6ea327befcd84ebfa4309e2409fb96cb60eaab33363f4f431e0a59c99e5d8071a83caff726346c1a256286786ca9df0e9ab6a6e71ee20d50adec56ed23602f82b39a6420f509d65e3b65626b66b7807be8cfc3de9a1397a7ebab76b3053b345cc4c6f86a7a7468fe1fd4e0359d311741235303e62b846847ed849b0aeffe944829a7db77610b4ddef439780b98ef509d6bfa6991dcc88a2d640619647f21c7770fe5e7952267765e16a62b78632d4da66d1723b6551452ddd22d91c2e49dd3d0c9888f7eace572af3297bb1756fde4a9ea86e4189d0070641bf7afb54429a706aa992d9945ef1f0e4e657f159b8b6791fef9f400c23e5a7f8d6ea2f5bb9ead002ee9c4445aa5c4f4f5a3bc7b715266b61d6856542ed74e9f21ad6c46f31004a37ba6c27b281af51f1044ef523c03ed409a0f9eb31ef0bb41c9ebc216951ed3dab06a300eda61e2b74a8ed21804e0e1831cd27b876a17180aca8fee5b2ca37e4d0163f5ec58c90ac47ffbc4efe046323b8dafdb6c4ff561f68d1b1a223b7554eabde241d7a11f4126c7b1e1c875cf62e3f8670446086a90a464faeb85f35b11202bf490a6361fdaf096f979ae48ed48860ecf0cca515711158e43253a7c430bf59493f2edcd3326af2e7879e641a6c661ba7c49e2ee09304f9d84782e5e5c903e6d52a0faaebb59d0d1b810f8ba5ef75059c74bbbab7447f1f07299ce581e19d709f6ed7d0d161a8aaa78055a0e2e025244273131c2c9f7be1ffa7e34c3bc9189c7470d9f17f2145dd461723e908f56eea7aea8eb50b630aab986648cbb724c76e6a0cffada8a49cbfa3d0ae3c1e96f7e9e250f6c9702e575a8bb1e210b50c3e39f7e3b26e72657f39d217a1328a3be4577483a21578974d3986868ea0606931d12c6036a3413a4516b9b61b81a9194268477d5b0ef6cd66de24ca39fa1d65d6e5da839cf3ceb0fdba72bfbd73277194abd94243764a9c8f51892435c216c9c1515826451a6a9a8c8b70fd4462137333f4673ebf880dbdbe85db81c4bce711e8395c8bd447b25e875a757c38d4b1ed9c99cc23d05c3334c90f1cd8d54d2d838a676cccdaa5bf2ad49e00cf5c1ff54cf826b8c073005bcab5b6f5f98afac3510f207ce2f95117718fc6bb85605b6109c01c1545ec99ae1af12de0352e41980c4e60808c83059d0ecd4ad3402dc34fd222c03be23ba246980bda051746e429e9fbd4de328700f46e556043cf7080852ede9a15034993349ae831f5ea75fe0b78c5d6fc825839994fe42254c4e7919eb44f66d7b1143007a7a594b6db1fbb9be1b108ac4c70c0bea2e04d32771169f5c3ac01350d3bad4e866de566b940447b239458375958d3a313dc9fe9ad30036a0fb406650c5fe88dea6b131b52577c2d1fefba09b2ce959262087654fdfa1daf46176c964cf9e3e84f74ff0a5b17df95ac1d9f0aa771f100de6443a1929d7fed8a72da4e56e3ff18d0e80d0998b85ea2f22a3bdc9629c307600000101860b6676deb0d0a2cf17ab2a00c8c1b0a874305f8280e38d1b810f5594cfddebd6c709c867c5b8ae7f372ffaee66b74afdcf7afddcd34821abbcfdfca38ff8c3f04bf502b7286d70a2c3e8efdc612de8d8ac15f929e25304b8da4e8b659da8f30fd72b4b225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101070001080100010e2000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd010f04010000000110040a00000041145b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ff9e0653a9e0ff14d692f2228ff4e0029e13405f9b8549e3105080dd9e9aff64400e6e71508734fc4e30cbd9b477edce5a447240bd4a1d1eaeef195d7f7bc20ea035fc5e5f6d2531690e3ed8e66aa0a30ba5ba60d61cf2d8ee4b0d632816a12bfa4215c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b2847205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba5287c407fc04707365741108701101000000000007fc04707365741249200000000000011170d07579f4681725fb975f3ab1b61a9917e4ff4ea9d2785b95786b827edfc8d8d4684921df517c460832d528423c7c2e6fac87c2bf10bcaae27737a2c77d68fac807fc04707365741320515151515151515151515151515151515151515151515151515151515151515107fc047073657414430100013a87ec72139e78f20877f4a10da0900452ba29c39a54a31c58e3dadc33f75dd330bb3c1e80b90d92d2633159bb7cad57bb925542f8ed4b0914eee6392d78609f00010308204e00000000000007fc0470736574012108221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa60104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a5407fc047073657405630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf7907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad70507fc047073657408040000000007fc04707365740949200000000000004e2000a013fa31c5e0dbd228e0e591816daf441d44f9adc79e98b61254d31ecc97859e74fc7d66283eaebf6af5fcd34883275c001d99d800b04725d3ebe645bd264407fc04707365740a430100013ef52cd9932090f46de7016f500494bbc708f51a3cd1fd2ed4e37e5ba8a53fd268e9267754a7257f49ff424b3abbcd3dd7df49230b1bba6ded7ad0f5e750bc1d00010308983401000000000007fc0470736574012109e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c07fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1407fc047073657403210adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede600104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d07fc047073657405630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c499007fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf07fc047073657408040000000007fc04707365740949200000000000013498f2631ac777ac0e111382f020102b9824eb3afaa4b3d55acb7d557ecb2690a79fa3e0f339b221902b10c120487d64a9a79489d398f4b63ce6888bc849f0ebc80a07fc04707365740a43010001802eb9cf032901995db60d40b462ab40930bc331154b50dcd4f44c62c1ab3515638a4887ca2f32d699cc87fb327f7d247281ae6c3fb6126bf4f5ef7b01c9dab900010308983a00000000000007fc04707365740121080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd180104160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c107fc047073657404fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d07fc04707365740563020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa907fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc047073657407210326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa270107fc047073657408040000000007fc04707365740949200000000000003a9803d0d73499932e9da25917f5440607c07802061dea48fbb56bb7ca5bc53cd7a76082450d598768187be7fad7c53af22d6e53673b44be63bb1c386ac486e52ef407fc04707365740a430100012ce548138544786a4140aa9518587d67f222d9f2f250e9d14a22c07f18775a1e5232ba2b6cc031bdfc7fedee56c8d780428a718064528a2dc43fe48abdbd3cf600010308d8d600000000000007fc0470736574012108d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c07fc04707365740220515151515151515151515151515151515151515151515151515151515151515107fc047073657403210b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a0104225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460107fc047073657404fd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa07fc04707365740563020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285e07fc0470736574062102531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33707fc04707365740721024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e607fc047073657408040000000007fc0470736574094920000000000000d6d8c3d704c043f5944e018088ec92140c779d3c52e1d586c3e110910523411f7fb17b6b399bf8aaf09038a3c570dd2303a294cfb3378c61eed8225bc9456b197e7a07fc04707365740a430100018d7cea542817247b1b5ac5906828d3830f85c918c892e95811d41c8565811d6ef78beb5606469c5f9cc7f5ec3a2eca44c02d5fc27303eadb465be73d32d31b4800010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000", + "source": "fn main() {}", + "source_parent_tx_hex": "0200000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff0201499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000186a100160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c101515151515151515151515151515151515151515151515151515151515151515101000000000001117000160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c100000000", + "source_parent_txid": "9654a2e7cbe5fdd6ab0e3fd35caae776e75342011e2d46e5b50d74b4dd068043", + "test_only_secrets": { + "base_secret_key_hex": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "blinding_secret_key_hex": "0303030303030303030303030303030303030303030303030303030303030303", + "user_secret_key_hex": "0202020202020202020202020202020202020202020202020202020202020202" + }, + "transaction_hex": "02000000010200f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd00000000000a00000000f80279345efca11e48cb1c2e27cfe71d109171b8d90862fe5cef8728ccdedd01000000000a000000050ac0980bab8155c6668ac8989a24d70e7f33a40591e300b1a6464bdf73ec041aa608221bcc31d07e423758b85f621787cb8b916e61a21edfe9476bf3096feb09a51b020cbd68967bf8004c5338b845e3578d069b6b6c71e25a3bbf31d6671f3b9ad705160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c10adfd861fbfc219ffcaffca95151ea6d90d5fe84498d39d18b4ff18879dfdede6009e452e86e51bf85893c3905baa4d8653abc4e8f2fdaf5bca8e0f697b65c4dd45c0379eae69b96fe2bd9c81b4b719e9a83887725a37c3ebdb98021ac43da855f3dcf225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e5646010a564721af0e830dee9f7c336757846c73dde1e1d89aaabef51f1b8d560320cd18080996c7b6ad427d4c89837ab0d8fa40627329d13a334b6b8002ca136782d0c66a0326d8b85870da7c2e691693ebb7686c526704909581b4f88fd1efa02916fa2701160014ebc0ee0b2ab9e8277a600c251475e22a3241a1c10b57751f5bacfc840b9277c856faf8566358544dd7e47997442f3b469fec73fd0a08d1c567af0dc568a669be8a76fdb3757738695e17b524052a07b311673bc9257c024a526a43fe13724d57ae0c8051cf996459f1fd7e9a8d529bc59be3a03cb318e6225120547cb30dbf04154f158c9b061c65a802125db1a8b472d6accbe1550e0e56460101499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000003e80000f401000000000440f03d70340d0c5400e6d51e7501164e101f3318abd8ba53fcbca067986cdb86672846f849485a6afb30d68723cf5b829a5c7c01b137db3261f3f9181fb07f01cd40de2a749d4c8bcaf771c7023787defe929d1dc1a871327bc2c86cd6f6feabcd183a4a61eb4313c173020ca368b0f71b9352e7644bfa13933529d485fba01e33e746205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba528741c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b280000000440a7d435d10560718f371e808ec3e60c2efd919085f56e1e261297cb6f04f7eb488a432d40c56f6bd318acc773a766b9c26a266f2d1c40ffde785bd67e270358b6400e6e71508734fc4e30cbd9b477edce5a447240bd4a1d1eaeef195d7f7bc20ea035fc5e5f6d2531690e3ed8e66aa0a30ba5ba60d61cf2d8ee4b0d632816a12bfa46205b75857140effc27bbdd00aa4e29e41df72910dc89b99f288315785a6af749c9ac204d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766ba528741c450929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac047f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b2800630200031ccd9b00edd7398ba0a6e5a5aa340119b3e78e12cba3f5c3040d86b6cfdb36f7ff5be2ee5c47d00f80f5f0ab034a238e204801809657e979e4b43715123065aa36b87f5dab5b209191fca9f6ab95359c868c0cceaa5c7a89c9101de945afaf79fd4e10603300000000000000017b880e00ba918b0593af2a0e9db37c6577fc1eaca61fa0fee94b71f44e645d0c8d29c7bffeda1e8a3b9f227a25a7d00339d83ef2e336c270e5bd420cb7bb813f6f6d7549f28f6de4103ea87a49fbfacf3f592a24b5fe0499cf54c22d48fc2d29cea82dd43039036271e4177f425c2694dd22ecebda7b9893eefa2e74e95b93858b4ce8cbaf2e497d7c5cebcc1d5addf4b101374c75ab7b9168dc8560a7ce943704934c371001928164d742a94f650ccb4cfe3b9c566590b65a5fcab0119191e213d733ced9c2dd1879dfe8bbe9c8455c6fbe169f478c90553dad14955b953feea69ac0890ad7cf07eac7b38e233ef279a5fd1f1ca68a8f68c9dc722308301c7e5423a9ee8435dc676d9c2ae3db80fd39455839b055c633dedb86e789b0a15d41c9b56ec96db1854bcbde4e37e5e84283bbeafdc842a03387d28f9cd55a017e713d9e0b29b7041e984ee0598e28bec79048a40c5674ddd361476b2c94ac931fa092c28bc3397728a7dfdb9ba20872d93d0e0f14fc9ea835e417b3d68b2100bbbedc7c1abaf4e1ddeef926e2d01a055d40a348977a6ee156dde3383d2fc14faa243d3a4ea55746851013100a0aad8154dc6d3766b8ac99078601b65807c382fb6305fe6bf65d0c8e4d2c066fc80bca8187ad5c0f716e2236f9ae229c291711ac80674cbffb666b28c9a3895b97378cfb9ad70ae004ca2fda4affef3a725fb5c32fc64be5ad4269389d8144bf2104f0fc3f100ad7eb8e4f6fcd921564a0bb47fbd3110866be4cb95700154db3307d9a51c57adcb3c853909b2fbd48ec9bbb73cf968a42e3c0b311dd4d49a9e323d3bb1fd88d3e3390179b672facd55c47caa10c421d7c928f6e54e9f4b5c18d9ceea1780fbe75d40b19e2c4d567d714948f723ff5ed3a54c0000a48a95afb16f503705c0552091c1fb3305b56f9deb060fe5daaeccb4c982198846538892402996fedabdb02e3608e75a613e47e99557dc4e1f5f52ce577bb5438d6f70d9f92b7b57af0a7fc0751f2cc70cd45eec6fa63683abd80934ca0ba696f3cd232b6a2195d5a1a371ef0112aac228e446ed711be66a9984905b8611bee4e8bd92568f4abead790371fa075298db73dec4df37a437c9c7974c6adac2e0b59cc397a4c89010bcbddac484347b20229372d564215c671b38b98d54952ec8a6619a73ae16a2229249b8676de3e58f7b89e3005801ce9db1516c05cba23a96886969641cff75eb4d762f9edb9967e001d03ae8cb7cb9bf36ee38020cb0c7d6b148d6be4937c4cf8e24b68983d950d26e277901aa8304778c22e9d19931c44bd211903dc2f6057df77ff448640751c1d9ce777a953b0c9cc07e8df126e45656eb1dcdffca42fc14d7be733bfa64abb61cdf2f67f88d5690f0572e83ffde37460292a54c3c4de0dcd0d4bf8cd5b62ad78ac9845aaed81418934ac74a99a0989636c3c712de2476d84506056544085447db9c6224f8bea58cc99af724db6dacef94685d656094db9fc9c546c19ca4a340c5206639e569cb1981e7597244bdce6eb242bab916f04e63ab79244b7af81dc72411e88d128b5e92e6d638035706f7f8e34680c3c7d93a9aa72478d52f855cb6195390017864953a44c2af2618f9f4c415eaf9736951815477726d0ba35f10b0b10a7a06a137934948ce6ef595cde2d6f78d4769849e3f35534407197faaca1e4bd5d7dd5c9cb9ec87a44d4278f9fff2887e4d24d0b3485b093b3c929090bc8cb373ce795f5c4de903f9454ce0fc6ac52ed2c4519ba60ad97f94420e50f7760ededcdf51002af927fe8c27e1692f21a0757f5556291e35225beea4b2530060f873a435af17ea78436cc96f38680e37fcf87cd9ec92fb2e574bd4932003afb5ad32f2777d55c4b675b95a621828a49cbcc92df573e5185c097fd377764dd34478bcf46c269857e48523e18fc360435a7cb1b5d1aa07abbafac36965780de6240fb30ecea21c56bf8f1c620584fe20ac3b9804c0b822b877d0579509076cb3e63b25e8c23da37ddec10eff5c605a412eb88e06ea51b25422951386118dc7b2c51b359751ae25db272fa2441d31e330321d9781d9350e37f39f29cb006ce16aa460ee1007e8d87723d8dfa6b5f635fcb780f1a81004116182aa2b621bff312c1e3ea95dba0bb9bf1f4fd141eccbd940a89f0c89e3d6c18128427e9b32cf27fe31da9d5c840b65d88ca3508efeb5bebfe933528738746c813ad027b16c1f5d60a72a66d35ee2fb2e03c8e5410cfc5d0560c2d728585a2a4037bb6a2dcb03bb07759f97c49e2ec24c47f3980db3f7501555d8dd7a33395de08551bca7d260e26a0fe7903c21eeb759db786c3d6a725ae8bd6c3d3940eb8641f98950a2ad2a4713d4f7f1765808f79a0ad2f51263178aa18847837b78c233d4030877c1d9f5925b617a7bb308475245adc414488f7fb97bc91fc6cb4f26aa2160979cae53aae8193fb83781d0cc656a144e1cae9db04640268187ff2e4c9c928e4238599d7fdd7c1a718f3025efed1460ec3824b50df457f19ffa3a1d95b8e9162b9865e36e53f8b8e2983a87304746bf2a09a934a715af11f46e8c4a3e342c2e3f67f31aa4e9ca049e0863e65eec06a97239bb1c34b001540d1f85733959581bceb29ff4c12a3b1bb6a5a2b501b2d71f0d3ab99ef538ee91733671dea7358e6300d87ae65da3062d0172bd420470172bf87d04977752bf727abf87a336b794580d99bf4a3dbc85251bf8497c4c0de2b2c7df4e15ab6f60e9522d71033c746de6b8126f139bbaf9a73bd31681f071f681ce5db5c9b29f62af7dd7d349325774450a9647d67a17636c155974be632a2fd82d73c88bb31579aaaa9d57d93286d13cac25312992daa24cd09d8a10feb28c48193bc5b997126951b0fd2279ac92c042d8406c20f28ba1b0a6e8c15895a5ecf785c83d479e3ef2e98fe78ee237481ce801a32e51cc0801c3f0caaccf1d97948d26e76d7589432b6d6061c319f45c03e7c678202014d276e86a1325b2b7aa08d5fcca85f5c70faf6a01801435fd4a1623a0a44a7817d7a57b5c20cb3e404883af8df4d1238e4671848dd3db93d26922e2b37a7e93dcbc1e29f104d0dde9f5e2dfbe7bccd5180c4d138029b5121c273e63b517ae5c891ded623fe41086013447819867aa0143aa05b1e25ac776ae484b972438b8bf115c59795444198b0fe9e091f7a4d25a3b2201fd0f35eb62c8eadb291461f96421e825134107466669d2bcba75ae994ed47fc54fe4ffb4ad441ad61a301646d0aa0a143802f5878410341305a5828fb3d417cc8fbd3552a755e06222476c50ac066b93276f21ffcce8816e2678ac57c10036167de03c5be03124743e0b540340dac5b362ea2267bfaa7fbd7c2f63f37d91e5bf51c1cf8e4161aad66fae5163e537f5f57b0c0308567894ac362795a681f072f2c291c985d09b92795312205433a700bb659e2b4285799344ae22ba5400af0280fc453ef3ccf41c4606d7fab1397e2ce0903e674e958b50582a223bd4c733c9e22ed1aee2cdeabab52ed9b9f03458c888b78fee8ae20aa079bd65a755ffddc9200b383bd4b3b70f7bd8dfee0a9b3e8dd0790da8d95187c93734dceddc18d3d215b4dcf680c7e8037b829b15769990da186b4cb872cdd1dad967456c8371b3f90fa5ec586e55944046fbd7f66f583e3ecf454e4f490dcfbb2ec47989b4a06858f6153c2f88df15f3cca16bce3dc77a773115784459c8ef286a94e0a2eddb948fd33a1370a6c4798b111045bb08bcc2bfd56c1d0b5ddd85128a61488f59d6b15570db519994604d81bfc68735805af60902d407257930492eb94855278c2513715824af79fca5934af48ce13df37aea67e88b8056a863df10e3fa75f152272d93e171f97fe08c94276cc0732e8c94888402d155f70364be2e1ec1b1ad48ea87996120b61230e95fef383b2905831f50331a659878df4bd90bfdc68c744d75d0f955b2ab99e3669d5c316910c1d6cac4fe71c360e2c779090912c79855f2b0780c98d535ba7d24127111197015c05340234f4e9acea7a7531ddf77120f74e728d0e3ecad2a0cc20bd653c8999bc39460d46c07a60dff7b44750757b42293cc3806ff12dac2168ff7ad741cd2d2936fd32bff9e2a83925a0e3a65195cfd9a0daaedf75dbc7e1c10cba016a41271bc290546f482407d97c3beffa7bc32d2f15ac35f6d52cc8ef397a16022bd8cf3b98d0f8cb3a7a5f3b75e87a93a776093419b8b0468efdc0f57548dfe4e3597a53c13f4d937b3cf2682e4506f139d7f845076cad4263cbf54c69aa445d49b7b10bfb61534c86e6fe9eba3abb16bbb6adc5d8ff27611f3f5c0c6136a75218782c4c72c50b0e43e4d5141d651dbfe94edfe9aec4d47035f9b44dafd958d0a3be375426d9d59eb38238c230303a1a2877004699f10aef93ff20782cf3b03f59cc0199c6b3e1a3fe1739e3107c9c9fa15db9dc3c4786ab8ce58d65c12f5947614f4bb36ece989f74ad47c68846fd38d18446c84b4385322360ab3c32e8f845d8eb0cf8f29810c8b6466d2d1e77d1db103e7d5aa0f41b41e5b44aad5033926a2e39078773d5db3291f6ec557d5e8f8e01cd6cbd6dd52f2f788b08c4243240151d57865d61153947a107fea6626f4d19fe281521ed4b30e036f2a57490dc9ef2fe04c246fa45b15503466566cfcff389053a57fe8032500993da084599a406e4fe853bcaf903fa31d4d8177fd4d1b08cbc12a08e5eb39037eac9c92f0761bb83d3d83f6975004ed8a5e035d540e570d8a57ac02a536e050491f61bbc3e4c4d64c3468da1356d0a7596560132a8d859afed8b14f5f5553a00a8e13379f27ff8b8f1dc3b5f7ceccba019b67cfbd16d5612f2fe40ba7cf28a33392780946517960d037391674f6168cd73c19f02a28b0028a6223288bb7dca3bfc27a5fba8d99e838ccc5cacb510a3f2a713c475f18fb50fcd192ab1975c3147ccbbdddda706de4bb03725c34a5dcf1fbeb45dcdff85b75adc74627cd3dde993e0dccbbc840ecead82ffbf41296b16168fbb18693c47462a56d813f73cb5f6eda9f26c4ade551b2c9dce8b574c89799e5a8f1a8721c6c4c3fa6331b20cdeec54bc6b89e3f3f894a35041f790f497536d31ef888351ec0dc088bba069c06746c31afb9ab5eba417e6a434052d56f1a3ce30ddda866f484f8b75eca4bd409d3ee72a3f86755f9000e3a6efecd836318f1ffdd1e46a47fd1513072aa361e73717d868e992bd6d24963b66685e60e0cde6c75036bd0d4021ed1be9d93f0b23aca6bcb7364fe813d18d14a4193c5964e44eb9da75f109491822ec72823636e0a08d07e0b41726fcaac82215ae76c3adf61c534054adbebf0a8ad548c97fff746d07c32712b76e840b7efee80e88fa2aa503773ddbedadc8d457fb28273538cb51f21b9f5ea3a99dc239ae6c12e207ac42aea1f2d6aa0f2aad807a063c7a374fdf2ec6459e15db5997061e5f68f6c2634e8bf1b3bf9f6d7a5f3e5c94da28c7c7a1266203095199693d4fdc4b6b874add1397d5ab42d9ad56c9d09187b312c614934901c96a42c65387b295271bb20d0bd66f27c702f628296826e482e5bce44bc7158bf8116c74bc911042c3af8df40cf09551c74fabf13ae3b415c4ac42b2988e5794ec5b29134fb343b5642af01fba4c6dec1f2704b9747effb9eae7b94cbe9491284d57cfc75d9a171d896e003a11d0832effc591347b586c0cada9446741f7309e0fa8a22e999c2ebeb0d49f08e0166ae9a6433ca01e578a50daa9ac271b910c20bb85b38a56b0cd63cf580ae5472db0376614008e546a7f5a5b4473bfdf105f6188c3ce41b36a18b4a58fb1762309a59b0f619efae3b35cf96fab2053e032d2d76a54630200037d7b811db86d98ff97e40ae1090fa137836a69d56d2c4f880faeaddf518b716c86a4fe01bcb0c0d00499ce9dfc5ece53bef6661c01644417f5b9279a960ba5491d7e706a8584d8d5060ef823c013e79c1da3d7b9c8661781edf2d85df51c4990fd4e1060330000000000000001deca7601aeaa9f172a8913a6017fd274c69d692850358ef48bbd2f9bbd122bbed4d5f9853104484aa5348868a6270097c487280c811477268726838f3577b20f7a8151048f701b83e9cb10b0d6b39de98cbf3a2e374187375bb3b947de2aee6dca334bb04764c910464efbc0ca07a2219fc18a2959e90067b2e245ea803dc99c81c8f18f49acd550c9bcffd8637dfaaff3df5d9abc169d1e3161c272980b2bf5baa603620cc29127d43db5401fcd021817a73a66c3da9c99dfdb5274d766b67d51b98d4ff8d00dedc3dc8046dc13d9018500736a91c11b6d2e1d2af8f651cf8ccb4ca3a31f873b1e433d7729118ea2a8c497272651b5d173f87ef3fd5b2bb002e5f6e46d3590db6255bba64014c62fccbddfdb2d3b2e8a96f5b1bde07b6253bb3c500892b2ecb0cede4dd95c0a3f0e10f2f9544e2f7785bf900fd5750ec79f7c6a8e2fa60421228329b5cbe77656e2a6c052500df4572794fb1f6ff02618feecb6faacc35efc0c5507472324ba3a2a378afdb34e11dd4e1ccdc69c02071da5cb4df6106b22de209b2e349ed8bd79d82ac8b15ba121e41ddc5eb3c3b151dcbc1895e2c992df9b6fbf9fe70eeafa0ad946bee21088e85fbe519ed5b4ae02632c6b61267bd6d1a4c14deaebac1027397da220eed6d0d8a1c9039d09cd1941565888786e87d2ab789f38517728ebf506f73ac6e22d17e7a817d0b949198a29556a726fc650d0ab08704a99323c3daf79e946abe908b3ebac3522cf521a54fbc58c573093f12663848b1be83842205a8be8c6715260b6de4c183713b9675655214591c5ba69c285b07e695a21da1c0607437297720e79ff8556d506c06f4f92de51ca63e55a33ee0a3add532364411feeffb372413681bc2599c711035103f14177bd319f4a145b15b75aaa929dcfa9914da64e0d79b429b4b24b369880915d1744ab65cf12ddcae0e922bbd06fef600757b2949aa371ed1c5c770dc672a66a44f9916bf7f8e75c80dd7ba6a5a1754b04b4a1c700254c0739d2afd1ee5326a4cceee37668e294925a1e9aeaff051a6ed989c4a48272d4ac3de31bbb459b41ea270024fe8f19473a3a562b990703865e46899f044143eb7e64629ac0ee77beadbe3f50ffc118eb3fc4161f6b26a70b57bd02f50e2ef4a8131da7fb58159ed0ef76e14795d82c9377780324ee8830f485422bd33bd7cdd8f2b6f05150d249aa79a03939b20e635f0bd05a6326ffc41c2267949c30348ffa48c350f6a8fea65691eb01da5c8a3a53fc820e04ba87b03e3f4f6e76ce02d9bdad4b7d3db39859a47c5141a018857e3dbf872696013d2a435bafe2c05d6e2015b351310c89754e9b6c0ed9d05123f2dddbb262b1127c6d7b9d845ba5b0ac074222f714a12c5413ab4d537c70833433f1b4aa96fa9ad75ab53200c2cb27b9917b03129cbbc834a72d611c6b1c26397ee4508473f2ba22990e26c0c1a6cdce72536e43b58757d946bc37675cc8a037df94af9e43817d912c3248ab64a333acc31a7ca6c371bba76e72f7d00b10e0b5ac2ec6d4c0d8cc9ee42b006da6eaa847edaebd73533e236b729484e2af977018091a2510b0e9e3263e021cea0010497be2edf675d89e0579fa5f38d4056ad4bbdbda75cc3603c1f8659a8410a2c9dcd2137f06ac01cc2741f4eeda8f295b30536aa313f7ba58bd0aa620b3faebcd6a97f73621775a2ae0c55fb9b997c098e39189b4e2e4264dc4afdcacc75d17ab0b7c9bb96d9bdd96b75520c1d22356ecb0cd24ccb63182d501346bf686c25de29cc4aa19a1eeb924221a1e8014d6391d77bcccdd49663e484b7b298ba0886fd3864c3b049099828261b4d7acca3e3793c79889148dcf1697c471fca304043f9816521f6597844916b7bc34897b4f34cb70cca4532737492a21b95f2b7106e51f4e55e168641910a3501ccf31c3a489ea2fe6b5c5d923974f65ee185ee2aeb3c631825b8c7e609207104053a0693ef3aed61a342f3594761ff3ba2bbe9bd7dec90e1c99271e429d13324401a6847020796b3e4d1c5988d62df8f94bec6652166d0b5f8d5fd9f1cccc77daf42f95afd8d5e9d2d76753fd60a1bb412e8181b2759b0f50a714237211054634440394c549c324a8a66899b01f2e4ce71bd53b097ca1ba65bccf20870879d9a8ec7d3da4ddeb395d09651371310eb3d749845d80504d544a924fb59933d75ae84f90b43405ba7699817d1bea1caeafd4c5e98b956864d2a426118c8a009c50dd66e354b3d2ceabc7c17f4e256da7a6438bac4ea625e252bbe3c0804bf7a4c69c1bf39e13d43a6a1e422bd4f6930afe6016ac5383e7a62d93ac78588c84d7d97cec95b53dcf1e18c34329d8c2a611a7aeaad91d0ce4435a0f556638fb3867bf6e978b8efee047d7b415520bdb03ba86a4ad4e422a3d73aa1cadf077f98f2c4c1d317c8eab650aef23c3d8d28ea0716936caae731f72b4b73c1f31155a4365a607a5f60cf9f4dd6aceb5a94af15b706b95678850d7b0ef4619e86814a0e3d8c3a3860c4ea259b5e41b867cbd0145c218ddffc3a394a56e8104cb4f251c129d2640c9815df65ecab8a73a931df9172b984da5ffd1e5db1cfcdc58368dd585d1f4ec51b95f199ea87e5cebf4f23a249eb3b9775d20676c2232eee5739b2c341a86bdf16bdde2873f870af12c0315d2117a661d0d1c5dfb2dbfdf345c1ffcdc562f04e730d67ceb23c0ee931cd9afe7acdeb54fa0df7c78b15ef9bacd4b6f3ee9f62acbe3c5773b55268dc060d48428224e853e163d99469d38afee30ea5aa410c8783447f595dd69772a79b71fac1d3d03a48883ccb7211f0822da60a356f244524f0d22d27d7d782424d22e4390d05b507e2ccfe37bbde5bde0fdd621ea2be4380e8bc5017139dd950e4d2447dc2adacfe18a561b21c4a5a59a1646987fa0c1a5393271ad9a7129c86765fb5583ecb971b970e5b353900d56a6a8c534ee821112fe023e0d23eaac50f93280b914d97712258ff11152b1ac1b1246605357116900eb4b8378b95daad88d79ef121c72d66be19695c18e93a35892fe3d489fb3298b08c1c298b0adc20235b0aae2bd5207a63f17504119fc7b8a797e924a3dbaa7d642ec1c8888b996c3b9bd745267c30f825716b2b047550603940e341dc4d382d480eb6520e65106432348ca22ccd0e83ac70baa13717a8a536ffcf2204d45884ce24066a14bdd17432fbc22cbd3b9924548f4202a76fdfdcf1b5fba876f3fb02cf16b8182fd6044b88f5c3a496ed5908930dcb8ff3dd55a6c78a430ac03876002e70ab72dde441cc822d8dec0442f516ad0361864e0694cb3cb31376cb77dadfc046490f59b836b4b3be4f64044ab09b9215bce648cb42346572487f4ac4f6694b1d659b9a3957aaeefb2d2dd9f792ea95e3fbfec48d51bcf90a7e86d10a247b7e0b761651bf0ee27dd2240004d9f729b9c2da83eae70a849ef10ade5bd9d173355fc743d3fd17b4ee044a0e058403da26c0d78115a9780880237d813c0f316319ec4466fed32e93b9d959866b9919761d19662075ccc0e64d89b157bfdf916e5137b035f6f4641104da897c44361856e799cf3e9367a1ea0f28c657ae915a1f4554f4f45e6ad99c612db60cd52b8a69cdb1e444f94595af3eff05d6fa0854bcd6efcf91d7d47184f3cce212b6ae27f99d3c5e6f68e24583676d83f54d80399e0f6ccac10de3ff066695ca3b37d514d5293c05f0f10488eae558cc188074ea330eb47ac8bef9d8d53662b8c9bcac1bfb639a080d704f2e99151e29596d571d4f095d46d4b6a90a711d0d0d49833684c4c7f20ca84ac95aaa3d898ee93a56947843feb82d3e403eafedc7e98b3d6ead73667870095b607b19d2e0cd188fbde833f3c1a8ee87934b2885560fbebc55dfbc781c69ff837e3db748f28395d0138e255cda4ac1c72fbbccfc0edf05d7e7a870770ed15bea90b513dbecdc08d7539c4e424d7c063b616066dc57e22b629f62f3e705af22eebc2b97abeabffdfeae9b1414f64a4128f5e8c600780bcbbb851d3c5f19611932379cce9fad2ceaf5fe383a4a352532669ad3339cbfbd7827a2418bdffca5fcc34b3fc04da5bf9a8a6a5b95b075213e3ac9b3b27621be47a8c52970c48f4cd3bb599f2e03e9f96a02bc61f2a2a3a6e59ff763e4e32c39bbbd129e29ad49393e273844f0cf24042c73d94b46ad6bee82d4b41e1dea1c8d13417b35a5cfe4684674a38a65eea001cc547a54c6e36a7cc66f8ee4e3b22ba95f0bcb75bd71b9aca571eb98c3a964ae2642e44ec74ff2677487ce08c6ab5c178737691ac702311436346d54a18a4032edc64770552557a019eef0374a5b91ae4f4cf6010c82afcf2a3b968a1b8c9b06f7a72d3f4b04c3d36579c09c8c120a81443142233ffbf77447f911db6fdefa2ffe0b3854b220eb197de257ba272f4136bbc99dbb5dd1b65e2a5fccb37e2267842a8042626e1555b01b6ccdfeccb4622806bcd6d11b9b27adabc808132314c4f69daa09af5ab81bcc3d60ab4d3479811974f82646953171f6176ff5633703e901039ccdfd0abf37b4e971000f79ec4337d1942b55e3edc40d4cdd2ea4278fa98643e23d732e2a809f9c804c0fc19ae6fea256999950bc72ccb8b7b598aa17badbd9e5b1b374de2433af8aa03b92a4028ba590c667ad1c95a4ac180a9a654855caa286fd54e60d35b494a0c58b6cfc639133a59235aa48aefdc6c18298b7e59c940e6cc382bb21fc4ab8dab85c4243e1f1c0cac03f29b62ce057de3884f47272067a9a1591b7fcd805df741824e5d36921095e597bf54a47f68fbde65571998ce06638bfd4dfcc9e3edd2ee65d02fbec726e96b40639cd27444e7d5517d5980b72ea16b55d33c24ede8ff98158c2624e5209c0754c6eceac4454054a6bfec136821359e494e6c0e2e63f9b86e5939f6125e2c8bd82addbf8b762d54452d32290927d4f7bfe4a34a9a6bb665aa08a848e132c7aa877ff4b5ae687c813c00b241018d0443626ccd62bca8b83c327fbb964c0d18b3f46948b9e4c982ce72ce85b6b8e959a8a4e897ce66d140fe86cf51f46a2a1169f43043c6c55e743baf68cd9e071842385868c049925c611ac743fe2c69873054348ab0007c718629d53305a81ca0e840253178ba8f5f419c9465fe8c504ac96445c473c76784b145701cab25d86ca90972d1c327af72ea8b6449a50a0570025d250d44e34e27f8786b9ce3a079681480730cafe53789496eb4dfef4c8be0ba18ab34b795058b0051d283f47a2255e3a376ae490ea57aa3235fedc71d178bc25d59a8c12dd4a3febad8897e08a0cf6970f5635e1faa63d2f11f62c1a8fd580de903419868fbaf7a3008fb4805bba12cf3c94abf820739bbf7c6115fa6acc121bbe473cad3745b0dfd3fb5df9b0ad485c1f06aa84cc3679da1657693e70807f863103152cbdc936700388e5ea83cda3e7daf081018cbb4226139cd2012ecf7a1ec1c6e644bc61f96bdfaab2eb46b3fc1a166a1a56fd00a3aba46f50ae3497d97d96563e6e000dd56ec572e89bfeb594c95ae271241d33fcf6393450497ad6ffe406712efe893aa4cad567d979f5f140b9a39fa440606614f1f25097ee4bcbe3c0d9ca4c4b14b292c8dab5375d1e823cbd3cdf437e9b33f4bd0a1d48c6b34a07744cf2d168bde14256942a122ce67dd5c07b9edef571448136ad19c04f797a80c2455a284438b14415ed405f1b5d7ca705f117a0df8b15ec8f99faab87abf8e297a5f52e5052877046ad0ce3ca918f3ae416a0ddab6407f2364ea1b890eceedb9d1310a3fc40c1d45b4881fc2e778b339408c051339b36537fe18d42997d2471fce07810174ca5909971d99b9e494e1510303a0e1ed92240c6dbf52033a937d63020003044c93586ea4b0fcd7a356d98ca57addbc668217f6c660a41ca3520f2a6e522eac87b0aa496ad4596f070dccb4e213269dc05cba37713696e14decc196eca216ba3efa9bcd691817e2a9dc98b485812e75490ece6515d3c9aa08e60159210aa9fd4e1060330000000000000001044342017f0fd8357450e854300780e01740ccae6266c822442df2846f77fddfedc13be6a17e81baa66318e490917e13a9d924ee0b91ce62122bd6b642acda637381b1ef49e5fed494ddf29fce4d37d3d98b92f617ac5e261f2df44642945a45f5ee5745f794a70c0717a43cbf4bec07c84ad515bc83cbe56a49d453e46b2c4acc738bd3d5c416b767e7a77ee84ac849ad200d736da74c2786dcdc628e3873a1a5fdab5abc3624f5ee5635e3046e0838cf4381ec2e695cc755c0750ddd01375b3dfe852ad9f869c5740045e8e7767b3c9a695cd140421fc7c7e6054e8ef2fe5f19ac42eb29d74881699f5505e9f9e7b6878e8ed921d8924770d859813d93da94e5f49e5630fb06f6f85f4bbb542d6091df175e07d10060c4c4807fa6b73355385e206318c9a1dc7146b4a9767bbcc7c441b475fcc9d26c691991c3a7249d0f652443192fdc9c6d6d0f22a0a9c1e2432a64e2b66c6fc59c6d918c713c85a42eeab2e080a793be89ed03ffa5b3e62a2c83b9c0718566b2bfbeb29c9b0319636c869f344292c08e95dce05bf14a3ac9310d8d149f2795261d1ba38a0dcdfad325b512bd9317a47fa36f038c55068bf563ac1fafa6f305001366b0c79b02ae9075f2a7523e07865133a66f4370d97f7c0163e0bc3460e9b68338dfac3a90a5a31eb0cf57d5cd9d107bca72342ad73ce2a36fa6e6c4c08a29161cdc670a352603cce86ccc21c7101c4bfd4b9750762c1097b1dce4183b3b72d04d80cb98e3ac44afa6ddbe1eb336ef71d5b79f7e722ae758f77f6561d3fe978b979a14a973ef421e331d0204e1e6ea631b4d365c467d7469cc2010dc75f94e65f3ff888771245ce72a5b6b47da4746c4d5d0584c3f3b985975f95b3f540b29022092710a525872325daf4150778584130e89276b7d964fabf7b2ff7cee7a996107a3de1fca17ac5ad57cb3ba2b5623b15d29df4fdfe60ad80ed6dfe61d904bd26c7d881224a127b410f3121e4cb2140783fc3931bca50b36175809c5ce11bd8db7e61b2088c3b991e5753a51807509a8b8e5da31d17a024a0412bc76578122300d57028ddfa4a14ac1a8408ec6dbf3a35c50915a3f6af42c42f9c9446a1253058d1b7391c5de5c279df03af547ab1c70434479745c7c93bd15f49e02e36477263b25b0395ef41bb093b4810afca8ae40a95ee07a402503467e4fac8953da51de1133e69fe510b59925c157b4fdcd2c8943cb675daf1f73a6c71138b97412bc685b0078eaa67c6f12531b42913a7641076b5e95bc5d90167818de8fe41190bf14ea8f89a59b8f1a337212a9c006df2423fcdeba4258ab863e3fe39c83f23b09e26d060d13ac236fbca52fdcf2268192cf98df3d9b483c847d3ca4ef5a99f03ed90eccc0ea52bc3481c391d4d02cfafaebcdf37a8f6307b785d16ed3ff019241af11423349645a5d448e89869167f9c3be6f1ff2ec2e04677372ee3a4b133b7edc6a83ed3be589b1db97f1d0b5248946b7fb8f1bf9539cd4ee524b80690273fe8b4ef4d3fdae8bdcac337960a69350ba24a442a6926e6aaf10817578847df7a586155c8d74e070d8572f3ed3bccb11fe0a17809ec00608c914f8c8e355a81b36977cc1613c4d8c9640db58c42a92c6ebe72353247f2eae50295f1dd1b140db7b2a6542bbba0d0a6cbed68edc3b66a74053e25c0f452566ca2e1f3e7d41b8e69ca74c625f09cf779bb7e1f8fabaca2d4db69cdbc6d364a6bb5037d869522a2b81f537074b5c64aa3fdac6d7bcb2ece83d5d7c7b6d6d4c8260aba3e677b492ae315eecfb26533db8d4c0f3f7f28b1d11f9bfa5c43e7b548e4fd354c3bd0656ae4cf8d1ebfc1e5bb1e38bbb4e3a7f7ecaebc868edf9aad805f021ae3fc611faf33ea9c25f4b0222fde89bc823eb8dbfda7ac50d6e86ed7b73376239d894f40562d0f962df1ada4a56613b41f109b33f64540bf0c9a64f7bd938ed999d0eb86fcd7ae5539f2c39dfcb6aae9c1b2b323ddc6fcdb9d0becf7d3625a63962ca61590f50d18fe5be030c21527204649209a3263a56e64833f75577fdd5737652310bbaccc95e1cbb679559113c4068eb670f50508d6584f6f43a81982e22064d5a971351066f5040e452b8acecacb10e79f56c502e9c8d7220a8a0fc6961d7424ef47e354a339fe5a27f29f09640033e2c0dbaa72b5a5df7333f1914093b0a058e362afcc3c6dae696c8580f42a468887f39754974f48cb3430300f2fd6d23279c81e105de3d0724c95ab6527d11425f7846978593dc4ba2473e664a6ac7571c6610ba4823887750b6423ff6f115bc981b987b5e3a3f190fa04ee747fdf72b62d993b4a7fb2c64cae802b45805569e93f956b5306720c0d845fc204e5cda29d025ae32f1f2bddb7fe3cc62f787bd5c6eb9ee6b325187ed6b96dd24e8de1ab73352d7e20057fc8db5417f3cdd96b8bdb1799366e692b84ab539f6cc13367ec4842b7243eede22484554b5962bdf2c9472431fbbb8c97734e37750101a46f799ec81a8707c2e7e944473c70cf3c8d0cdc289a2df5a8632ceed717b665c9f1ef240bfdbf06c6b8855ca99e042cdcb868e8df5c4efc91d5b2f2e2cfce97ad2ab9347300819042fdd2910990e91e4386bc2f11435678b72bbab0c2cea5cdef0693a6aece0eee031ce83a55bf809e595ec73345541fccc91d714d9a2a327ec0813e950f6688202d7930704c8aa1532e3eaedb3d84368ddb211763074d4f9f37299ea4ebf7b201b5b812160041fdf7c47febd026060ff8bc49f7f0edb6583cfd94ae134d48ef9a309101fabedc3d2ed8206fc5fb38a04a27b37d3136b13dcea9a29cc0b5cd69dff2f9a42f7dd09f3d63cb19bc650cc0204dc599950c34e446184a640fa6082ed3c2bf203b572035981ce9b8c938cbb4af25fafb62b94988cad15d74e4080bca19dca8f0236f06cae83237555af21386ed9e5bd7e2e4890daa527821052e5e38179c5a273a0a601397d2af92b347cc56b0b6e3c37d9f07e943e7bb3f06833d14fec68a705ad8c7d6d4b0c40c4d23cfd2d95a28b277d542f17ffc7c2cea7c3605a2bd8cc7e5198ef400465095a9cb149a86181dd48ce8d5acdb68ca8919c28627cdd09c024c0bad270f68ae5ccbcd360fc1cff7cfdac50dc2de41af7066cf541aec367eacb986de7344a94983bbbb02699f696b0a64b4272889b811f60d4c642c3a244f89ee09e619460b6dc36308c5b697b6b0a7fc5d297386588af2d1d3c004993d0925fa15d26bfcaf5e8208b2a72a5c9f06fbd473d0dda4f27c673c071418c4d145744f98bc458dad6eeb502a0fb771eda35be1b04fed4d9014e566122f1687d624ecacd4e64e72ce8527c0c6868eb37adf553596827651589c5b8811ebbbbd4c007f632df1df59d2f90bf2a6602026af605d837ffd4cf3e690097c688d76c3d1bb810e901648f764cf87eadf1f7693e21ba8ec138be875e0299093e11f635cdd3a6da47ad053e8d3312d04053fb71930d0e34b545aff2957a71037cbca4918bdb7785dc636e4b1401d83fc84bf41681e3193ff70d16378b71be82fc578879cfc51fa032a4274b43b47fbddbbb2dd6044b6dda5d36309c0dcb32914befd7b0984333e33d53b81dec692878b049a18da6b53b788bbc81d17487b52c1551ce69f60a6e169d81dedff711bbc20b62712be01e8501877db182d02bcf6547f48d3b7694807ebbc33dd1d8ed8ca57d1ca193b6cf97a73f4cbc9be497aa1cbeaa716b764bc800c22d79c4a2e0b1dea4d5f3e2906298475edb7c6914ec844b0feeb1253313f4f06c798af49d2d32bb3ab094bbbf193f84c2c7dfe69af9ebe6fb880f50f8cfbc0bb1a9496838f67d77fb41b2c41a683bdffc503dca2ea34fa22795f9051da3901923568b81c3aa8bbe9186e5d120f90b126e645435aa74280b5c347fc1b210de1fab9c7516f6027f73ef2e3131c42258fa3342babb295f2fa01538c91eee66fd3796c8538c377adbba697d81b690d707af6b0ac84010b9e3b341e72ea7ff28ab01a8b4dc3237fec83fa384244bc582646aede615d63614f347560e1bfad3683e3d512db66809fab6861de649ccdc720a12c4be53009e480185bd3b418bc07c0e68021bd304ecbc4593ea269620ac6e2aaa63d0d143560363d86de0817d448f291532a7cdc10d8dd653987025d5c2ba2dc2f93ca210c7960c42269eaf18edf9b92fa16862037f074d941c722f6a247a2c6ee1572b7f6a99ac6e443c5fb9c71783ede253b096d589c237325fbe8d4dd9191d4c486bbd1f89850d4fde053761e0338effd047c4bb516ab98d1a66ac41ce0ee17fcb5efe4d21869f7d0e4df8c38089a0c9f1a03997be10eec1de68e2121ba79fa2aaba1051767433d9e938bd8dbb75c4358d3154378aced2c6b058596b40868412b773c78372623c83c4690c9fd93dea25fc9585b81757c98901d488141f7e9d7762b2e2de8ec58a2accbe68c41179f1cdb691ffa4fa3019b7d13486c264300b98b14bc425d9fe3d3b38d9a39caa33df750fa772d373b9280090a180d386699358642ff7cd65df51c5a1008aceaedb796798ab7935e933696513a9e39ec4981f2d22c36b9f5d532467051eb6a83d1e78f2784669039ef800e998171e4f1e65d1512619e05d1070c96615a538b19730b43ba8e81e726f730a2007cf10f744cd4fb7be2774e0f6198715f7fe974e5d79caa7f07ff1c160d95ffc1e35375459db53a913ad2cd563d553ce0af9e360d7f06559046532c52422d5803589174100cded5bc4e90185bced895f575bc1a7965f40f0dc690623be1c18376335ec093c7cb432daa0bc9fdde7535a84090d5a4e7e185a3e225081423bff73918990e310c422ae5fab163cbaba36c4d38de7f2109e56c562eb77cf6b1678b1d109597f587ec7778e29aa5e834f94ca3c11b76c463d082451cfed985e96f9ae50ff6558eea6c456144775d101e116866f1570a4c4f9902046988d53e35b89305e2b072f1a90ac8e0c595e1ac175045acc6ce4ecd94125ea9bbc724a3830da64aa0094f5a79a9447808afc384da92ff824540016501b48d319ce9a77d9dd17d9f98bbf608dc6216ec931dccde2dca5705fba6fc94485605a490f64046ee084c69d41b249dde6f21c89c9656b2f4fab63ea59abd09962335bd44021a9ec35e4b54a1246f7b8204fb029f53da2d24057afa36378a92f04a76cfaf04ba186a5c80c23a362e6cebd8035d705b071ee8b69c38d324a6c6a5fee36fd6c135fb3369c1d1743c706dd9b7523ae8605e779c0d02d7d7ea4b172a4fcd5ecfcf16c0ff5787742adfe187fd63574535312deef3d163a57cf894f592557f40b519dcb8ffee9acad749e2581c11ed9108587d675e154131db43becf75ce8efb2868ed73ada17e4d2fc96e7a7868e8f0ba5c261fdcc9ef0333aaf5d1b58ae675f17d98d0f52d0c370926e5825762e397eb346205c4e8de72fa7b7bce471b9845dc6dd703639fa93bf5266da60b2a40833404d238cb33555e655f92a28cdef75722263a6ad59506baf9f54e3cee712a9c4d53ff64b3e0a6368183e0fde617dcd62d8d3fe7f2ebb6980244ee4c781ecae1811107be8f06953dc0e84ff799f1851956224e53a898451f5176dd9f8ce96a11e3d33520c04de8c76e5af36df98bb4b0e9b3733fd03a1b5cb6e967f82de3bf37d07e8382656df5a1f4e58f4e93fd41cd9c57733ba8da84c94ec16a1c22fb51128d497936f2683d4d58a3f50e576f9aab2109a569b200066555a5bdf7aa52b441e7a7d90b26994cfbef3070b5e5eff519b5c78f0975af2d75c8925c06f4bbcf8943293246c026faf8a568dc801619fdb6bbfcdf5d05129d61547414680a11005f03bcb035d7a2f93dce76bbbe9da056cf6ce018b91625216145d63020003eb6e3809ef4b1e05c8476153859c90fe3aa19a0903074641fe1cf70fa0c1a14f9a1fe2a6a6adbe2b391b2aec382d0a361a0793c60c6b94c068c82c59ec382618623857a85cb89460f6c51fd0589d034d3b25274311a0bb8fb62da3a3f0eb285efd4e1060330000000000000001615cab0165560a22145779d4c77c12c328fb558aa7e6575856036cf0d74024654d728cfcd17f86c183365b718bb969da9523ee84dcbd8b2733b1ebf6c9cdd6f4bae778e537b69c7f06a5dd2bc461cb16407be6b8b783bc8befdee404db45be2e4ebd66a31209d60d9dcd284108162863b1eeec36c1f93bb033727d5139a3fda7551b0ee452483a42d8a2f4cb9e4f0fcc4e2280154f26d9c2518a0de46eec4695aa114c55b3c51fa47fff5149ded98a9c071547f8902ee3cd2c3ab6f489f03d2db5292a41d2512c63e83c5a7277080ef37bf0bd49cdf6225c8482bf831c441c900bf8f5c18004342844338347bd42f1f65d2b8bf3f68b51e5695596a7a09bd9cb4fa9171f3d1c64b38deb1de99f5536c2338db3d4e8517df3784e5ac61868e85f61ecd0049f3c228ca2cc51bac24a0039fd05290fb931aab7e88dbeb92b6c1f73f208bae9042d7f6ba98d040e2d8d503c201dfb1430bce81a3e2fd6db4d1f2ba87a1e4c07551523fa4ff8627752538232649766e402c9a6190123230da3c99ff50b3af42e53d885dc33f70f1cab43e808ef1a9185d0eb9d1522ad4a894f019dc2b64eb97242795539254349b00562abcfd28d39cea60731dbaaa9929b00bbd1e460298fd16f078bbe2f2b6a8f551adf766677542c73a993467481bb08162609679f1cb481a14bd86d65154c084c0b007d336cbffd0c375bae8e0d97fc9f4f46ef71d995506cad0dedc1b8217f7f090f372ea068c13615fc2f3057683e088d4419388f733bee1ecfa09091dcacbd6c6d9d26a911bbdd439ea26d5bcb378baa31220e2602ef72814de78d29c1653f5dfda27931892461c1127b634249141644f1c21f67198629f869cc31169d9823d955bdd608bbf48c551a81f391120c78c22eb04cab7f3bcb8f2d3a325c1719853b20c88bc0e4d74d5da72956a383556422e292b0b5c7f4556086f68e5902ef4729dbf6e348857b22e24214e1b34e4c1fc3ca0c330783229ae201c381ecd5e9bcd61d47436b4759a5de59179998b645b424328fcb8bf5f038d95f4a906ab3933bb0157d1d5e229b639d814fb2507c71939130e5acaf389e7f94e83913bba081968fb65e24ef66099be8a0ef78b208f4bc923f5a268fdc176501eb21486edfedbcbb32bdbbd88625b0513f9fdd3df771865b3a851b4d2710508555d7002e992573de2d8665231b75c747f8f92c3b9dc841810e73673fffac4f8881bdcffbcb3dd628f41a9df8ce391ed5e8ee39d0eac73c3c48ef8000cc22e4a2a5a83deeff1d3553be7f73c07c53435f7263f3f3ada6690e62c796d5e88176dab1795f7f38465a0b0e5523a67ba5f79b97bfd0ed485bbd4b0b9a6499a0d43a609193c56efdd74344ed1d41ac62e27dfdf848116bc5734c6a3f90e48ffaa5192acd6061d581b712d2cc831208d903f945c86ef430068a2030a5db96e262c46c494ce8b29330b4eac44cad62b186676556cff16f33c68dbaecef084dcd685c19d464efe819149a6159e22f5659c39c351e79ac5569477cb3b82cd0449fbecd30b1c0853640846e8dbeaca79fc2cd396ee74194e7901d8e58cbe035a1eef1b920eb1c013a1ac8a54e3e204435e3dee22d73e39623d230460e1a81954afeada31eb9c6fb0bef3b0ed2932b9c85f721b1a05b29a695ab802aeb0ba02ecbf22c82b274331af55359c41ee7c9d493918af87ec470bffefd4bd4eb96c81b2a37f5a7709a2f3015d936c404754bcedb958e44e49653f619438eee301fc52a94de50a71e7dfd74b323b24a3818ef5e52906a79a982034c46362b4345acf6e7f4f4345595a4d188c8c0fdc61e7bfaadfe059925461868b03e7186072186bb2fef9f3dc81f06df387334e4610be91b2689df3f9baa3bcdbdb9ba9e703f85797a1971e98695a5d136371a710e30bb2c9c3debf83db3e6d7afbd148148e2aaccb0c4143aad4fbd1e9fc67902ed11068342c8613b3ad3bc6490819a8edd85655cbd6408dfbbaf82cefaf01ea092f798ac78dba264a1cfaf35889e41b72c3bf7a8fe513c0f630306208a79e19a4da554a1e97178ffc773cc3d466947c95f443338ea6b5bd1fb621ab289c8613a03acfb6b0cc2e78c468f237a00b94874de291e6f83ac9f421f26b59ddf3f03842093f5abe6f24dd4c5d5357f15c6d7e8d08ef49527a858a1541517715aa1b27c1e7fb02cb2b081af84cfa99bf2ea5c651c651122746ed01192833d8c8f783dcdc227dd9cf95c45f204064b2c3befbac87c8c6a0a7408c0c59b6be60134b2dd299db9f99386892b878f98a9d72fb2a038128526b778e6c13aa1dd25b7defe383dfc1447c5108d07813e2fa969da3c149b2cce82cc75174609fada3a0ed6b733904faa1ad7fc3c950852bfb369d0e88ec70f1cf904666822ffc2ffa6dae6031eae2513a01a318bf78ae16b807e50e776bd545128c4a8d9f8e6c9dc2201adccb32dc4bcbd243d72362da9e02eded9dfcd572f7ec8b2db4e85e76424ad3d7c62d9475fbfff5b3e02609a870198555969af1d007d76645d315d4ae0f2a82e20294e08f642bb8d22bfd8c0058786d64df4d98c91f8ccbf300ccdaf6d40a4e008b24ec2cffb8614bff93c98856ef833f23dc7fdcff190f20f942fdb15c5556bbdaea3a73df84a2557b86750b592cec0e2d14027377279a2e192de5993c38b65f27a99fd80507b1c3c6184e79de5a8a727fd48fa1e715eff285c28e0516bcffcc86ec09106a42f8b8c5f93e46153c3b51791f5222c158f3f0beea385d50e07bc0a7b8db2b996720df0a93a1f4b93c4edbe758a3786f51a7f4570c878c92e5903ae991846d4567cb4da8714a500f0062ed6a664182825180767599a510ca5a417f09c9514bafda381f5bad0db0245c89da180f6722b4c5d60e899c8b226de881e8a96db173a9da44ae4ea6f886572cbb8cf6f0ad2e28d695f95613f23b10903f38f2d8fb48f25f18dc8b6c8bf93738bf55bb90706a6b3137762cb1d50d2545057eb4bf76725d7aecfef078809b09b35d95a5361b52f5420517033cd6d30279cb4ccfc90d37e59b56d3755c51590a34fec25e1836331687fea557e76bb7ef1d76795273cbca2f4ce6b3b280df19de25af892fd1c0a04419395afc2b5c1b33d6e8306f791a3c29cf59f08461a528fd3d51fb32e51ff4de4ea29276b70d189f0ac28c538a613207edb64f6d5b187d677409cf1b7f259f5e5b851ef3dc02fcfc6d07ded3ccc2b274cecf8a7e121ac27bcceb64217c8b3b636d14df11bcea71432e91ca7fd4639800b0f951844fb626f2e1043bec3739bb726a5ebb2c6b9c176982de1446b3e773b89e30458fba6e9f2b5e19a0213661efb01f26575e8434099366c5d5ec7c8a2ef6345385264d434581b0d0b850af32873564227472894995d7299e506ca8bb55663dcc84f5e4cea5df244941a4677492d8e3bcc52283ad1e268f1c1ebd4c201b0fe918228d65ca299df5dfc6de740ec9b1b3826d480a320fbd2b7fbb5bf771a68e69cd52b91dc4f81495d7d3d0ba7ec4d473d6f7b7e80d756acc14dffa99d44f6f2fc363417fbde0660c78e2fe22dc569893234f8a4546f95f23a1f71c185cb89d7e72bb946786b9e099b70b52a6b982e8da0c364bb89130e84f55dcb41e32ab577b9aea9d4b9eed4b34881736a5df888dfa12753923d691d1eee9f9126ebc34665f202cffd7769cad753c51185aa725e2ef26ecc4e31ca44fc0a27f858b1f2fab31ea6d9b8b94eff1b0790d5cdcaf88f3b8c567407128f1db2f5829f60a617f90f0ea3cc8960f2e6c9409d15fef84f4cf8a1711171745396127811a4e35934cbd4af8748ff101f8801ab99e69c81c9eb6f0e66c1e0d93ad0771fed07264c238d649a34d7ac481e9543c947d185ec9a3e399e371312218ab4d7f5260fae1d908a6554580fb54a0721d9b10c5b4db52fb3c505eff64803a51e6ff45cc24d559cdcc44ffcb8afdc337711648ffcd8746f937c28abd96df97f9c2c79a897078c99de97e167d262814675f5a537a63b7300fdccd8fb7043d2f7f3d7e467501d83b949a7b8445ecb6d3d30275dcf5845db8e0fbe60219c5d2667cb6de2f793a86b8b3f7013496bcb7151983f0cb039057bf4dc8d436a759cf134f9e1d9ef1c1846bc53f2851ee017338fea9530aa3d8bff21bd76d0c1c6588f0e322efd57cabf326d6355da5e64a43d7c139b220d39aa49a1d564abc957576b38474ae4206c6d318f39d96afd0641ea5882a07c6f8056f2e2a9627ada893cea7e2d47a3d3b64e6f388cdc29b017d7bd883b879b8008c4719b28079910cad92ca38b5afd3dcab8eb01c0d9cd0ccc1113770097a05e7fa1d2695dce2fe191b69bc78c4af4bd11662b5e77030b153eeb5a3362e0d5e1172fb2fadefbe129d9d78e393a29f067425dc4837e76939a52cce086e960917be94444ba61d4c35a99b3e52adc7a3ee8fd86c03f31456e29690ba8f7e42c3515f94922b5c535a0df971862923d821b46aebcaebe48063592bd6489f1ce1159bd156088e58aad6b33d558c0897dd961cfaa84e189543444c8298ae359e04086eb3c6b37546150c9b2ce59f17f9fcf999b68d87f8185d832dcecd3dbbb3928e9cb41d8727dcfc5ccbb1d6a0279d9142eb6142593438cd893153752ef2e3bbce2801721206ae71c2f4fdc1b1f8088175bd0f6b26651dcb601df1489dd7f467b5f85b5b90c8963cb835a86a13d523f77113f33bed4b0ee5a6bd3e9e8efa3c4e956c2052b2dc040f92efb7231dc79404e9f48df3c3a4822fcbebc1c8351109a9866d84cf9983186778fda16991f44b90302fee6a35e7ab3d220420072806bf211a263713ac13e12f6c9e521c4c3a82f2f209dc051d03f817bb910e30792f3e22e81930af2287b957ea0639ff548b400724520751ba5e400db76390c51db74adeabdcc99c4302f0690e099327a584f2a9667be5b54d021b9a779708a7a9dab52ee9e5589c01079da76e63d406cff82bec49bee794c24b7043ca965174d9756d02ffdbf8ce977e492806c6d674a7f1d0cce2f8688b3b44a3b6bbefc43cdcea2406acacfac748bbec8fda46833786df9f3aa5a3469ca04a201c5540881fc50fa71e71f83aedfec37d7db7ccba0c7a9489530cab91c9af47c9ff382afe1dbbc68e2951ba4b3f37d44e3ddf0268a711f439a72db7ac2c8ba69c0596b17e5e716b489bfc2158f1b19e644768e2069ecfed03a3ec7a8014dcd5946c9e80bbc9c23a86d9e72b9f251a99733de62af07f42ff4476eda57e778b9f83d457bac03089ddaf3f43d3a47632eb5af49759a7ed5acf2ad9b31f64cef635d5aebf0a4ab3cb47f8bf90ea49cb7af6936462dfa3e03435966faa26cf6e4a56caae7e01c68dba5d4005f2625f4f64907df46d5493640e73dc374cfb0e0e2fee67588c8c09c36e33c7326058206d48987dd85d1cc68e0177c3634741bcb515b5a5ae2a7e57c36cc960c012b2d698f6c844a50ef5118222e8ba41b00ad293c688a526089ac93c405bf81c70c3511ca9a1715b198a6f531e63e8b6ddd7f075fa14a5d4f2058841671c5e477ce6da5c5aa41aa2f01525064b8bf99d415cd6b615b40cae6cd65d7c9098aade7fc54af795d4d75f114aeb60df3afa7c222ca4e1aba970a1bb4624f6706b07915b962d17bac3fcb01dac133d906c12c2fcbd3826f1368c4b2c8ab0c4bfe8547f7725100e8fbee48faa0d2a11c19377f12af84343f41c64dd6d63c03c01db676c793b6749e27456d39ffd260da63eb734cfdc4c8ebc26ae3e3a9e12b3353f3f5d4e08ef3d3d59e4e7ce10fa6d13cb4d87b9f2d11ff179735fd87e61a3f6e68754d3bd6c93db88ff9ea5a01e747090c73f11ad966dd04caf659f8d032111aec99f692181471e62a46a615e1422a389baa0000", + "txid": "057681c60c12601f3038b60a7cc0a77ee38c2eaae2c2c1b6d644be338d6d1b55", + "user_leaf_hash_hex": "47f5c5724168f7ea601c865ac7109ca8ef143f88bff68e2dfc656548eca84b28", + "user_pubkey": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "user_signatures": [ + { + "input_index": 0, + "public_key_hex": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "signature_hex": "f03d70340d0c5400e6d51e7501164e101f3318abd8ba53fcbca067986cdb86672846f849485a6afb30d68723cf5b829a5c7c01b137db3261f3f9181fb07f01cd" + }, + { + "input_index": 1, + "public_key_hex": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "signature_hex": "a7d435d10560718f371e808ec3e60c2efd919085f56e1e261297cb6f04f7eb488a432d40c56f6bd318acc773a766b9c26a266f2d1c40ffde785bd67e270358b6" + } + ], + "warning": "PUBLIC TEST-ONLY KNOWN PRIVATE KEYS; NEVER FUND; parents are synthetic content evidence, NOT chain inclusion/consensus-validity/unspentness.", + "witness_base64": "" +} diff --git a/policy/tests/kernel.rs b/policy/tests/kernel.rs new file mode 100644 index 0000000..3516626 --- /dev/null +++ b/policy/tests/kernel.rs @@ -0,0 +1,1331 @@ +use std::collections::BTreeSet; + +use bitcoin::{ + Amount, Network, OutPoint, ScriptBuf, Sequence, TapSighashType, Transaction, TxIn, TxOut, + Witness, XOnlyPublicKey, absolute, + consensus::serialize, + hashes::{Hash, sha256}, + psbt::{self, Psbt}, + secp256k1::{Keypair, Message, Secp256k1, SecretKey}, + sighash::{Prevouts, SighashCache}, + taproot::{LeafVersion, TapLeafHash, TapNodeHash}, + transaction, +}; +use simplicity_unchained_policy::{ + BitValue, Builtin, Context, ContextSummary, Error, MAX_MONEY, MAX_REQUEST_BYTES, + MAX_SCRIPT_BYTES, PROFILE_NAME, bitcoin, cooperative_output, genesis_hash, manifest, + network_name, profile_hash, signing_commitment, tweak_public_key, +}; + +struct Fixture { + psbt: Psbt, + cmr: [u8; 32], + data: Vec, + network: Network, + base: XOnlyPublicKey, + user: XOnlyPublicKey, + recovery: TapNodeHash, +} + +fn key(seed: u8) -> XOnlyPublicKey { + Keypair::from_secret_key( + &Secp256k1::new(), + &SecretKey::from_slice(&[seed; 32]).unwrap(), + ) + .x_only_public_key() + .0 +} + +fn txin(outpoint: OutPoint, sequence: u32) -> TxIn { + TxIn { + previous_output: outpoint, + script_sig: ScriptBuf::new(), + sequence: Sequence(sequence), + witness: Witness::new(), + } +} + +fn output(value: u64, script: ScriptBuf) -> TxOut { + TxOut { + value: Amount::from_sat(value), + script_pubkey: script, + } +} + +impl Fixture { + fn new() -> Self { + let cmr = [0x11; 32]; + let data = b"policy-data".to_vec(); + let network = Network::Testnet4; + let base = key(1); + let user = key(2); + let recovery = TapNodeHash::from_byte_array([0x33; 32]); + let funding = cooperative_output(base, cmr, &data, network, user, recovery).unwrap(); + let parent = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: vec![txin( + OutPoint { + txid: bitcoin::Txid::from_byte_array([0x77; 32]), + vout: 1, + }, + u32::MAX, + )], + output: vec![ + output(60_000, funding.script_pubkey.clone()), + output(40_000, funding.script_pubkey.clone()), + ], + }; + let mut recipient = vec![0, 20]; + recipient.extend_from_slice(&[0x44; 20]); + let unsigned = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::from_consensus(1000), + input: vec![ + txin( + OutPoint { + txid: parent.compute_txid(), + vout: 0, + }, + 5, + ), + txin( + OutPoint { + txid: parent.compute_txid(), + vout: 1, + }, + 0x0040_0008, + ), + ], + output: vec![ + output(20_000, ScriptBuf::from_bytes(recipient)), + output(70_000, funding.script_pubkey), + output(0, ScriptBuf::from_bytes(vec![0x6a, 1, 0xff])), + ], + }; + let mut psbt = Psbt::from_unsigned_tx(unsigned).unwrap(); + for (index, input) in psbt.inputs.iter_mut().enumerate() { + input.witness_utxo = Some(parent.output[index].clone()); + input.non_witness_utxo = Some(parent.clone()); + } + Self { + psbt, + cmr, + data, + network, + base, + user, + recovery, + } + } + + fn context(&self, index: usize) -> Result { + Context::new( + &self.psbt, + index, + self.cmr, + &self.data, + self.network, + self.base, + self.user, + self.recovery, + ) + } + + fn parent(&self) -> Transaction { + self.psbt.inputs[0].non_witness_utxo.clone().unwrap() + } + + fn set_parent(&mut self, parent: Transaction) { + let txid = parent.compute_txid(); + for (txin, input) in self + .psbt + .unsigned_tx + .input + .iter_mut() + .zip(&mut self.psbt.inputs) + { + txin.previous_output.txid = txid; + input.witness_utxo = parent + .output + .get(txin.previous_output.vout as usize) + .cloned(); + input.non_witness_utxo = Some(parent.clone()); + } + } + + fn rebind(&mut self) { + let funded = cooperative_output( + self.base, + self.cmr, + &self.data, + self.network, + self.user, + self.recovery, + ) + .unwrap(); + let mut parent = self.parent(); + for output in &mut parent.output { + output.script_pubkey = funded.script_pubkey.clone(); + } + self.psbt.unsigned_tx.output[1].script_pubkey = funded.script_pubkey; + self.set_parent(parent); + } +} + +fn raw(bytes: &[u8]) -> BitValue { + BitValue { + bytes: bytes.to_vec(), + bit_len: bytes.len() * 8, + } +} + +fn uint(value: u64, bits: usize) -> BitValue { + assert!(matches!(bits, 8 | 16 | 32 | 64)); + raw(&value.to_be_bytes()[8 - bits / 8..]) +} + +fn join(a: &BitValue, b: &BitValue) -> BitValue { + let bits = a.bit_len + b.bit_len; + let mut bytes = vec![0; bits.div_ceil(8)]; + for (offset, value) in [(0, a), (a.bit_len, b)] { + for i in 0..value.bit_len { + if value.bit(i).unwrap() { + bytes[(offset + i) / 8] |= 0x80 >> ((offset + i) % 8); + } + } + } + BitValue { + bytes, + bit_len: bits, + } +} + +fn some(value: BitValue) -> BitValue { + join( + &BitValue { + bytes: vec![0x80], + bit_len: 1, + }, + &value, + ) +} + +fn none(bits: usize) -> BitValue { + BitValue { + bytes: vec![0; (bits + 1).div_ceil(8)], + bit_len: bits + 1, + } +} + +fn sha(data: &[u8]) -> [u8; 32] { + sha256::Hash::hash(data).to_byte_array() +} + +fn tagged(tag: &str, data: &[u8]) -> [u8; 32] { + let tag_hash = sha(tag.as_bytes()); + let mut preimage = Vec::new(); + preimage.extend_from_slice(&tag_hash); + preimage.extend_from_slice(&tag_hash); + preimage.extend_from_slice(data); + sha(&preimage) +} + +fn eval(context: &Context, jet: Builtin, source: BitValue) -> BitValue { + let result = context.evaluate(jet, &source).unwrap(); + result.validate(jet.target_bits()).unwrap(); + result +} + +fn evaluate_unit(context: &Context, jet: Builtin) -> BitValue { + eval(context, jet, BitValue::unit()) +} + +#[test] +fn all_55_reference_functions_have_exact_outputs() { + use Builtin::*; + let fixture = Fixture::new(); + let ctx = fixture.context(0).unwrap(); + let summary = ctx.summary(); + let hash_string = |s: &str| raw(&hex::decode(s).unwrap()); + let outpoint = join( + &raw(&ctx.tx().input[0].previous_output.txid.to_byte_array()), + &uint(0, 32), + ); + let script_hash = sha(ctx.prevouts()[0].script_pubkey.as_bytes()); + let mut cases = vec![ + (PolicyProfileHash, BitValue::unit(), raw(&profile_hash())), + (PolicyCmr, BitValue::unit(), raw(&fixture.cmr)), + ( + SigningCommitmentHash, + BitValue::unit(), + hash_string(&summary.signing_commitment), + ), + (RedeemScriptHash, BitValue::unit(), raw(&sha(&fixture.data))), + ( + NetworkGenesisHash, + BitValue::unit(), + raw(&genesis_hash(fixture.network).unwrap()), + ), + (CurrentIndex, BitValue::unit(), uint(0, 32)), + (Version, BitValue::unit(), uint(2, 32)), + (TxLockTime, BitValue::unit(), uint(1000, 32)), + (NumInputs, BitValue::unit(), uint(2, 32)), + (NumOutputs, BitValue::unit(), uint(3, 32)), + (InputPrevOutpoint, uint(0, 32), some(outpoint.clone())), + (InputValue, uint(0, 32), some(uint(60_000, 64))), + (InputScriptHash, uint(0, 32), some(raw(&script_hash))), + (InputScriptSize, uint(0, 32), some(uint(34, 32))), + ( + InputScriptByte, + join(&uint(0, 32), &uint(0, 32)), + some(uint(0x51, 8)), + ), + (InputSequence, uint(0, 32), some(uint(5, 32))), + (CurrentPrevOutpoint, BitValue::unit(), outpoint), + (CurrentValue, BitValue::unit(), uint(60_000, 64)), + (CurrentScriptHash, BitValue::unit(), raw(&script_hash)), + (CurrentSequence, BitValue::unit(), uint(5, 32)), + (OutputValue, uint(0, 32), some(uint(20_000, 64))), + ( + OutputScriptHash, + uint(0, 32), + some(raw(&sha(ctx.tx().output[0].script_pubkey.as_bytes()))), + ), + (OutputScriptSize, uint(0, 32), some(uint(22, 32))), + ( + OutputScriptByte, + join(&uint(0, 32), &uint(1, 32)), + some(uint(0x14, 8)), + ), + (OutputScriptClass, uint(0, 32), some(uint(3, 8))), + (TotalInputValue, BitValue::unit(), uint(100_000, 64)), + (TotalOutputValue, BitValue::unit(), uint(90_000, 64)), + (TxFee, BitValue::unit(), uint(10_000, 64)), + (InputValueForScript, raw(&script_hash), uint(100_000, 64)), + (OutputValueForScript, raw(&script_hash), uint(70_000, 64)), + (OutputCountForScript, raw(&script_hash), uint(1, 32)), + ( + ValueReturnedToCurrentScript, + BitValue::unit(), + uint(70_000, 64), + ), + (CurrentScriptDebit, BitValue::unit(), uint(30_000, 64)), + ( + IsRbfSignaled, + BitValue::unit(), + BitValue { + bytes: vec![0x80], + bit_len: 1, + }, + ), + ( + HasEffectiveLockTime, + BitValue::unit(), + BitValue { + bytes: vec![0x80], + bit_len: 1, + }, + ), + (InputLockBlocks, uint(0, 32), some(uint(5, 16))), + (InputLockIntervals, uint(1, 32), some(uint(8, 16))), + (CurrentLockBlocks, BitValue::unit(), some(uint(5, 16))), + (CurrentLockIntervals, BitValue::unit(), none(16)), + (CheckCsvBlocks, uint(5, 16), BitValue::unit()), + (CheckCltvHeight, uint(1000, 32), BitValue::unit()), + ( + UnsignedTxHash, + BitValue::unit(), + raw(&sha(&serialize(ctx.tx()))), + ), + ( + PrevoutsHash, + BitValue::unit(), + raw(&sha(&serialize(&ctx.prevouts().to_vec()))), + ), + ( + TxInputsHash, + BitValue::unit(), + raw(&sha(&serialize(&ctx.tx().input))), + ), + ( + TxOutputsHash, + BitValue::unit(), + raw(&sha(&serialize(&ctx.tx().output))), + ), + ( + CooperativeLeafHash, + BitValue::unit(), + raw(&ctx.cooperative_leaf_hash().to_byte_array()), + ), + ( + CooperativeSighashDefault, + BitValue::unit(), + raw(&ctx.sighash()), + ), + ( + CooperativeUserPubkey, + BitValue::unit(), + raw(&fixture.user.serialize()), + ), + ( + CooperativeServicePubkey, + BitValue::unit(), + raw(&ctx.service_pubkey().serialize()), + ), + ( + RecoveryLeafHash, + BitValue::unit(), + raw(&fixture.recovery.to_byte_array()), + ), + ( + PolicyApprovalHash, + BitValue::unit(), + raw(&ctx.policy_approval_hash()), + ), + ( + PolicyTransactionApprovalHash, + BitValue::unit(), + raw(&ctx.policy_transaction_approval_hash()), + ), + ( + TxVsizeLowerBound, + BitValue::unit(), + uint(serialize(ctx.tx()).len() as u64, 32), + ), + ]; + let mut covered = BTreeSet::new(); + for (jet, source, expected) in cases.drain(..) { + assert_eq!(eval(&ctx, jet, source), expected, "{jet}"); + covered.insert(jet.id()); + } + assert_eq!( + eval(&fixture.context(1).unwrap(), CheckCsvIntervals, uint(8, 16)), + BitValue::unit() + ); + covered.insert(CheckCsvIntervals.id()); + let mut timed = Fixture::new(); + timed.psbt.unsigned_tx.lock_time = absolute::LockTime::from_consensus(500_000_100); + assert_eq!( + eval( + &timed.context(0).unwrap(), + CheckCltvTime, + uint(500_000_000, 32) + ), + BitValue::unit() + ); + covered.insert(CheckCltvTime.id()); + assert_eq!(covered, (0..55).collect()); +} + +#[test] +fn indexed_missing_entries_and_bytes_are_canonical_none() { + use Builtin::*; + let fixture = Fixture::new(); + let ctx = fixture.context(0).unwrap(); + for jet in [ + InputPrevOutpoint, + InputValue, + InputScriptHash, + InputScriptSize, + InputSequence, + InputLockBlocks, + InputLockIntervals, + ] { + for missing in [2, u32::MAX] { + assert_eq!( + eval(&ctx, jet, uint(missing.into(), 32)), + none(jet.target_bits() - 1), + "{jet}" + ); + } + assert!(eval(&ctx, jet, uint(1, 32)).bit(0).unwrap() || jet == InputLockBlocks); + } + for jet in [ + OutputValue, + OutputScriptHash, + OutputScriptSize, + OutputScriptClass, + ] { + assert!(eval(&ctx, jet, uint(2, 32)).bit(0).unwrap()); + for missing in [3, u32::MAX] { + assert_eq!( + eval(&ctx, jet, uint(missing.into(), 32)), + none(jet.target_bits() - 1), + "{jet}" + ); + } + } + for (jet, index, script) in [ + ( + InputScriptByte, + 1, + ctx.prevouts()[1].script_pubkey.as_bytes(), + ), + ( + OutputScriptByte, + 0, + ctx.tx().output[0].script_pubkey.as_bytes(), + ), + ] { + let last = script.len() - 1; + assert_eq!( + eval(&ctx, jet, join(&uint(index, 32), &uint(last as u64, 32))), + some(uint(u64::from(script[last]), 8)), + ); + for (i, offset) in [ + (index, script.len() as u64), + (index, u32::MAX.into()), + (8, 0), + ] { + assert_eq!( + eval(&ctx, jet, join(&uint(i, 32), &uint(offset, 32))), + none(8) + ); + } + } +} + +#[test] +fn exact_source_shape_sum_tag_bool_and_trailing_padding() { + let fixture = Fixture::new(); + let ctx = fixture.context(0).unwrap(); + for jet in Builtin::ALL { + let invalid = BitValue { + bytes: vec![], + bit_len: jet.source_bits() + 1, + }; + assert_eq!( + ctx.evaluate(jet, &invalid), + Err(Error::SourceShape { + expected: jet.source_bits() + }) + ); + let invalid = BitValue { + bytes: vec![0; jet.source_bits().div_ceil(8) + 1], + bit_len: jet.source_bits(), + }; + assert_eq!( + ctx.evaluate(jet, &invalid), + Err(Error::SourceShape { + expected: jet.source_bits() + }) + ); + } + assert_eq!( + some(uint(0xab, 8)), + BitValue { + bytes: vec![0xd5, 0x80], + bit_len: 9 + } + ); + assert_eq!(none(256).bytes, vec![0; 33]); + assert_eq!(some(uint(1, 64)).bit_len, 65); + assert_eq!(some(join(&raw(&[0; 32]), &uint(1, 32))).bit_len, 289); + assert_eq!( + evaluate_unit(&ctx, Builtin::IsRbfSignaled), + BitValue { + bytes: vec![0x80], + bit_len: 1 + } + ); + assert_eq!( + BitValue::from_bytes(vec![0x81], 1), + Err(Error::SourceShape { expected: 1 }) + ); + assert!(BitValue::from_bytes(vec![0x80], 1).is_ok()); + assert_eq!( + BitValue::from_bytes(vec![0], 0), + Err(Error::SourceShape { expected: 0 }) + ); + assert_eq!(BitValue::unit().bit(0), None); +} + +#[test] +fn exact_script_patterns_and_empty_script_behavior() { + let scripts: Vec<(Vec, u8)> = vec![ + (vec![], 0), + ( + [vec![0x76, 0xa9, 0x14], vec![7; 20], vec![0x88, 0xac]].concat(), + 1, + ), + ([vec![0xa9, 0x14], vec![7; 20], vec![0x87]].concat(), 2), + ([vec![0, 0x14], vec![7; 20]].concat(), 3), + ([vec![0, 0x20], vec![7; 32]].concat(), 4), + ([vec![0x51, 0x20], vec![7; 32]].concat(), 5), + (vec![0x6a], 6), + (vec![0x6a, 0xff, 0xff], 6), + ([vec![0x52, 0x20], vec![7; 32]].concat(), 0), + ([vec![0x51, 0x14], vec![7; 20]].concat(), 0), + ([vec![0, 0x20], vec![7; 31]].concat(), 0), + (vec![0x4c, 1, 0x6a], 0), + ]; + for (script, class) in scripts { + let mut fixture = Fixture::new(); + fixture.psbt.unsigned_tx.output[0].script_pubkey = ScriptBuf::from_bytes(script.clone()); + let ctx = fixture.context(0).unwrap(); + assert_eq!( + eval(&ctx, Builtin::OutputScriptClass, uint(0, 32)), + some(uint(class.into(), 8)) + ); + assert_eq!( + eval(&ctx, Builtin::OutputScriptSize, uint(0, 32)), + some(uint(script.len() as u64, 32)) + ); + if script.is_empty() { + assert_eq!( + eval( + &ctx, + Builtin::OutputScriptByte, + join(&uint(0, 32), &uint(0, 32)) + ), + none(8) + ); + } + } +} + +#[test] +fn accounting_scans_all_funding_inputs_and_ignores_change_labels() { + let mut fixture = Fixture::new(); + fixture.psbt.outputs[0].unknown.insert( + psbt::raw::Key { + type_value: 0x70, + key: vec![], + }, + b"change".to_vec(), + ); + let ctx = fixture.context(0).unwrap(); + assert_eq!(ctx.summary().debit, 30_000); + assert_eq!(fixture.context(1).unwrap().summary().debit, 30_000); + for jet in [Builtin::InputValueForScript, Builtin::OutputValueForScript] { + assert_eq!(eval(&ctx, jet, raw(&[0; 32])), uint(0, 64)); + } + assert_eq!( + eval(&ctx, Builtin::OutputCountForScript, raw(&[0; 32])), + uint(0, 32) + ); + let script = ctx.prevouts()[0].script_pubkey.clone(); + fixture.psbt.unsigned_tx.output[0] = output(20_000, script); + let ctx = fixture.context(0).unwrap(); + assert_eq!( + evaluate_unit(&ctx, Builtin::ValueReturnedToCurrentScript), + uint(90_000, 64) + ); + assert_eq!( + evaluate_unit(&ctx, Builtin::CurrentScriptDebit), + uint(10_000, 64) + ); + assert_eq!( + eval( + &ctx, + Builtin::OutputCountForScript, + raw(&sha(ctx.prevouts()[0].script_pubkey.as_bytes())) + ), + uint(2, 32) + ); +} + +#[test] +fn proof_validation_rejects_missing_mismatch_conflict_and_duplicate() { + let mut f = Fixture::new(); + f.psbt.inputs[0].non_witness_utxo = None; + assert_eq!(f.context(0).unwrap_err(), Error::MissingParent(0)); + let mut f = Fixture::new(); + f.psbt.inputs[0].witness_utxo = None; + assert_eq!(f.context(0).unwrap_err(), Error::MissingWitnessUtxo(0)); + let mut f = Fixture::new(); + f.psbt.inputs[0] + .non_witness_utxo + .as_mut() + .unwrap() + .lock_time = absolute::LockTime::from_consensus(1); + assert_eq!(f.context(0).unwrap_err(), Error::ParentTxidMismatch(0)); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.input[0].previous_output.vout = u32::MAX; + assert_eq!(f.context(0).unwrap_err(), Error::ParentVout(0)); + let mut f = Fixture::new(); + f.psbt.inputs[0].witness_utxo.as_mut().unwrap().value = Amount::from_sat(1); + assert_eq!(f.context(0).unwrap_err(), Error::WitnessUtxoMismatch(0)); + let mut f = Fixture::new(); + f.psbt.inputs[0] + .witness_utxo + .as_mut() + .unwrap() + .script_pubkey = ScriptBuf::new(); + assert_eq!(f.context(0).unwrap_err(), Error::WitnessUtxoMismatch(0)); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.input[1].previous_output = f.psbt.unsigned_tx.input[0].previous_output; + assert_eq!(f.context(0).unwrap_err(), Error::DuplicateOutpoint(1)); + let mut f = Fixture::new(); + let mut parent = f.parent(); + parent.output[1].script_pubkey = ScriptBuf::new(); + f.set_parent(parent); + assert_eq!(f.context(0).unwrap_err(), Error::FundingScriptMismatch(1)); +} + +#[test] +fn reconstruction_binds_every_funding_input_and_all_public_parameters() { + for change in 0..6 { + let mut f = Fixture::new(); + match change { + 0 => f.cmr[0] ^= 1, + 1 => f.data.push(0), + 2 => f.network = Network::Testnet, + 3 => f.base = key(3), + 4 => f.user = key(3), + 5 => f.recovery = TapNodeHash::from_byte_array([0x34; 32]), + _ => unreachable!(), + } + assert_eq!(f.context(0).unwrap_err(), Error::FundingScriptMismatch(0)); + } + let f = Fixture::new(); + let ctx = f.context(0).unwrap(); + assert_eq!( + tweak_public_key(f.base, f.cmr, &f.data, f.network).unwrap(), + ctx.service_pubkey() + ); + assert_eq!(ctx.cooperative_script().len(), 70); + assert_eq!(ctx.control_block().serialize().len(), 65); + assert!(ctx.control_block().verify_taproot_commitment( + &Secp256k1::verification_only(), + XOnlyPublicKey::from_slice(&ctx.script_pubkey().as_bytes()[2..]).unwrap(), + ctx.cooperative_script(), + )); + assert_eq!( + ctx.cooperative_leaf_hash(), + TapLeafHash::from_script(ctx.cooperative_script(), LeafVersion::TapScript) + ); + let actual = SighashCache::new(ctx.tx()) + .taproot_script_spend_signature_hash( + 0, + &Prevouts::All(ctx.prevouts()), + ctx.cooperative_leaf_hash(), + TapSighashType::Default, + ) + .unwrap() + .to_byte_array(); + assert_eq!(ctx.sighash(), actual); + let secp = Secp256k1::new(); + let base_pair = Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[1; 32]).unwrap()); + use bitcoin::key::TapTweak; + let tweaked = base_pair.tap_tweak( + &secp, + Some(TapNodeHash::from_byte_array(ctx.signing_commitment())), + ); + assert_eq!( + tweaked.public_parts().0.to_x_only_public_key(), + ctx.service_pubkey() + ); + let sig = + secp.sign_schnorr_no_aux_rand(&Message::from_digest(ctx.sighash()), &tweaked.to_keypair()); + secp.verify_schnorr(&sig, &Message::from_digest(actual), &ctx.service_pubkey()) + .unwrap(); +} + +#[test] +fn context_is_immutable_and_does_not_keep_parent_or_metadata_copies() { + let mut f = Fixture::new(); + let ctx = f.context(0).unwrap(); + let before = ctx.summary(); + for input in &mut f.psbt.inputs { + input.non_witness_utxo = None; + input.witness_utxo = None; + } + f.psbt.unsigned_tx.output.clear(); + f.data.fill(0); + assert_eq!(ctx.summary(), before); + assert_eq!(before.provenance, "parent-txid-verified"); + assert!(!before.chain_status_verified); + assert_eq!( + serde_json::from_str::(&serde_json::to_string(&before).unwrap()).unwrap(), + before + ); +} + +#[test] +fn empty_script_data_and_exact_zero_return_are_not_missing_context() { + let mut f = Fixture::new(); + f.data.clear(); + f.rebind(); + f.psbt.unsigned_tx.output[1].script_pubkey = ScriptBuf::new(); + let ctx = f.context(0).unwrap(); + assert_eq!( + evaluate_unit(&ctx, Builtin::RedeemScriptHash), + raw(&sha(&[])) + ); + assert_eq!( + evaluate_unit(&ctx, Builtin::ValueReturnedToCurrentScript), + uint(0, 64) + ); + assert_eq!( + evaluate_unit(&ctx, Builtin::CurrentScriptDebit), + uint(100_000, 64) + ); + assert_ne!( + ctx.signing_commitment(), + tagged( + "SimplicityUnchained/SigningCommitment/v1", + &[f.cmr, sha(&[])].concat() + ) + ); +} + +#[test] +fn repeated_parents_are_content_verified_and_psbt_hints_cannot_override_context() { + let mut f = Fixture::new(); + let expected = f.context(0).unwrap().summary(); + // Witnesses do not enter txid, so this is a content-equivalent parent proof. + f.psbt.inputs[1].non_witness_utxo.as_mut().unwrap().input[0] + .witness + .push([1, 2, 3]); + f.psbt.inputs[0].tap_internal_key = Some(key(3)); + f.psbt.inputs[0].tap_merkle_root = Some(TapNodeHash::from_byte_array([0xff; 32])); + let control = f.context(0).unwrap().control_block().clone(); + f.psbt.inputs[0] + .tap_scripts + .insert(control, (ScriptBuf::new(), LeafVersion::TapScript)); + assert_eq!(f.context(0).unwrap().summary(), expected); +} + +#[test] +fn shape_version_map_unsigned_and_index_validation() { + for version in [i32::MIN, -1, 0, 1, 3, i32::MAX] { + let mut f = Fixture::new(); + f.psbt.unsigned_tx.version = transaction::Version(version); + assert_eq!(f.context(0).unwrap_err(), Error::InvalidVersion); + } + for inputs in [true, false] { + for count in [0, 9] { + let mut f = Fixture::new(); + if inputs { + f.psbt + .unsigned_tx + .input + .resize(count, f.psbt.unsigned_tx.input[0].clone()); + } else { + f.psbt + .unsigned_tx + .output + .resize(count, f.psbt.unsigned_tx.output[0].clone()); + } + assert_eq!(f.context(0).unwrap_err(), Error::InvalidShape); + } + } + for inputs in [true, false] { + let mut f = Fixture::new(); + if inputs { + f.psbt.inputs.pop(); + } else { + f.psbt.outputs.pop(); + } + assert_eq!(f.context(0).unwrap_err(), Error::MapCountMismatch); + } + let f = Fixture::new(); + for index in [2, usize::MAX] { + assert_eq!(f.context(index).unwrap_err(), Error::InputIndex(index)); + } + for witness in [true, false] { + let mut f = Fixture::new(); + if witness { + f.psbt.unsigned_tx.input[0].witness.push([0x50]); + } else { + f.psbt.unsigned_tx.input[0].script_sig = ScriptBuf::from_bytes(vec![1]); + } + assert_eq!(f.context(0).unwrap_err(), Error::NonUnsignedInput(0)); + } +} + +#[test] +fn zero_and_max_money_checked_totals_and_negative_fee() { + let mut f = Fixture::new(); + let mut parent = f.parent(); + parent.output[0].value = Amount::ZERO; + parent.output[1].value = Amount::ZERO; + f.set_parent(parent); + for output in &mut f.psbt.unsigned_tx.output { + output.value = Amount::ZERO; + } + assert_eq!(f.context(0).unwrap().summary().fee, 0); + assert_eq!(f.context(0).unwrap().summary().debit, 0); + let mut parent = f.parent(); + parent.output[0].value = Amount::from_sat(MAX_MONEY); + f.set_parent(parent); + f.psbt.unsigned_tx.output[1].value = Amount::from_sat(MAX_MONEY); + let ctx = f.context(0).unwrap(); + assert_eq!(ctx.summary().total_input_value, MAX_MONEY); + assert_eq!(ctx.summary().total_output_value, MAX_MONEY); + assert_eq!(ctx.summary().fee, 0); + assert_eq!(ctx.summary().debit, 0); + for invalid in [MAX_MONEY + 1, u64::MAX] { + let mut f = Fixture::new(); + let mut parent = f.parent(); + parent.output[0].value = Amount::from_sat(invalid); + f.set_parent(parent); + assert_eq!(f.context(0).unwrap_err(), Error::MoneyRange); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.output[0].value = Amount::from_sat(invalid); + assert_eq!(f.context(0).unwrap_err(), Error::MoneyRange); + } + let mut f = Fixture::new(); + let mut parent = f.parent(); + parent.output[0].value = Amount::from_sat(MAX_MONEY); + parent.output[1].value = Amount::from_sat(1); + f.set_parent(parent); + assert_eq!(f.context(0).unwrap_err(), Error::MoneyRange); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.output[0].value = Amount::from_sat(MAX_MONEY); + assert_eq!(f.context(0).unwrap_err(), Error::MoneyRange); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.output[0].value = Amount::from_sat(30_001); + assert_eq!(f.context(0).unwrap_err(), Error::Overspend); +} + +#[test] +fn maximum_shape_and_script_bounds_are_enforced_without_truncating() { + let mut f = Fixture::new(); + let mut parent = f.parent(); + parent.output.resize(8, parent.output[1].clone()); + for o in &mut parent.output { + o.value = Amount::from_sat(20_000); + } + f.psbt + .unsigned_tx + .input + .resize(8, f.psbt.unsigned_tx.input[0].clone()); + f.psbt.inputs.resize(8, psbt::Input::default()); + for (index, input) in f.psbt.unsigned_tx.input.iter_mut().enumerate() { + input.previous_output.vout = index as u32; + } + f.set_parent(parent); + f.psbt + .unsigned_tx + .output + .resize(8, output(1, ScriptBuf::new())); + f.psbt.outputs.resize(8, psbt::Output::default()); + for o in &mut f.psbt.unsigned_tx.output { + o.script_pubkey = ScriptBuf::from_bytes(vec![0x51; MAX_SCRIPT_BYTES]); + } + let ctx = f.context(7).unwrap(); + assert_eq!(evaluate_unit(&ctx, Builtin::NumInputs), uint(8, 32)); + assert_eq!(evaluate_unit(&ctx, Builtin::NumOutputs), uint(8, 32)); + assert_eq!( + eval( + &ctx, + Builtin::OutputScriptByte, + join(&uint(7, 32), &uint(9999, 32)) + ), + some(uint(0x51, 8)) + ); + assert_eq!( + eval(&ctx, Builtin::InputValue, uint(7, 32)), + some(uint(20_000, 64)) + ); + f.psbt.unsigned_tx.output[7] + .script_pubkey + .push_opcode(bitcoin::opcodes::all::OP_PUSHNUM_1); + assert_eq!(f.context(7).unwrap_err(), Error::OutputScriptTooLarge(7)); + let mut f = Fixture::new(); + f.data = vec![0x51; MAX_SCRIPT_BYTES]; + f.rebind(); + assert!(f.context(0).is_ok()); + f.data.push(0); + assert_eq!(f.context(0).unwrap_err(), Error::ScriptDataTooLarge); + assert_eq!( + signing_commitment(f.cmr, &f.data, f.network), + Err(Error::ScriptDataTooLarge) + ); + let mut f = Fixture::new(); + f.psbt.unsigned_tx.input.truncate(1); + f.psbt.inputs.truncate(1); + f.psbt.unsigned_tx.output.truncate(1); + f.psbt.outputs.truncate(1); + assert_eq!(f.context(0).unwrap().summary().input_count, 1); +} + +#[test] +fn exact_evidence_ceiling_and_giant_proof_or_metadata_rejection() { + let mut f = Fixture::new(); + let key = psbt::raw::Key { + type_value: 0x70, + key: vec![1], + }; + f.psbt + .unknown + .insert(key.clone(), vec![0; MAX_REQUEST_BYTES - 4096]); + let size = f.psbt.serialize().len(); + let len = f.psbt.unknown[&key].len() + MAX_REQUEST_BYTES - size; + f.psbt.unknown.get_mut(&key).unwrap().resize(len, 0); + assert_eq!(f.psbt.serialize().len(), MAX_REQUEST_BYTES); + assert!(f.context(0).is_ok()); + f.psbt.unknown.get_mut(&key).unwrap().push(0); + assert_eq!(f.context(0).unwrap_err(), Error::EvidenceTooLarge); + let mut f = Fixture::new(); + f.psbt.inputs[0] + .non_witness_utxo + .as_mut() + .unwrap() + .output + .push(output(0, ScriptBuf::from_bytes(vec![0; MAX_REQUEST_BYTES]))); + assert_eq!(f.context(0).unwrap_err(), Error::EvidenceTooLarge); +} + +#[test] +fn relative_lock_flags_units_reserved_bits_and_zero_delay() { + use Builtin::*; + for (sequence, blocks, intervals) in [ + (0, Some(0), None), + (0xffff, Some(0xffff), None), + (0x003f_0001, Some(1), None), + (0x0040_0000, None, Some(0)), + (0x0040_ffff, None, Some(0xffff)), + (0x7fff_0001, None, Some(1)), + (0x8000_0000, None, None), + (0x8040_ffff, None, None), + (u32::MAX, None, None), + ] { + let mut f = Fixture::new(); + f.psbt.unsigned_tx.input[0].sequence = Sequence(sequence); + let ctx = f.context(0).unwrap(); + assert_eq!( + evaluate_unit(&ctx, CurrentSequence), + uint(sequence.into(), 32) + ); + for (value, indexed, current, check) in [ + (blocks, InputLockBlocks, CurrentLockBlocks, CheckCsvBlocks), + ( + intervals, + InputLockIntervals, + CurrentLockIntervals, + CheckCsvIntervals, + ), + ] { + let expected = value.map_or_else(|| none(16), |v| some(uint(v, 16))); + assert_eq!(eval(&ctx, indexed, uint(0, 32)), expected); + assert_eq!(evaluate_unit(&ctx, current), expected); + for request in [0, 1, 0xffff] { + assert_eq!( + ctx.evaluate(check, &uint(request, 16)).is_ok(), + value.is_some_and(|delay| delay >= request), + "{check} seq={sequence:x} req={request}", + ); + } + } + } +} + +#[test] +fn absolute_locktime_unit_boundary_current_finality_and_global_signaling() { + use Builtin::*; + for actual in [0, 1, 499_999_999, 500_000_000, 500_000_001, u32::MAX] { + for sequence in [0, 0xffff_fffd, 0xffff_fffe, u32::MAX] { + let mut f = Fixture::new(); + f.psbt.unsigned_tx.lock_time = absolute::LockTime::from_consensus(actual); + for input in &mut f.psbt.unsigned_tx.input { + input.sequence = Sequence(sequence); + } + let ctx = f.context(0).unwrap(); + for request in [0, 1, 499_999_999, 500_000_000, 500_000_001, u32::MAX] { + for (jet, timed) in [(CheckCltvHeight, false), (CheckCltvTime, true)] { + let expected = (actual >= 500_000_000) == timed + && (request >= 500_000_000) == timed + && actual >= request + && sequence != u32::MAX; + assert_eq!( + ctx.evaluate(jet, &uint(request.into(), 32)).is_ok(), + expected + ); + } + } + assert_eq!( + evaluate_unit(&ctx, HasEffectiveLockTime).bit(0), + Some(actual != 0 && sequence != u32::MAX) + ); + assert_eq!( + evaluate_unit(&ctx, IsRbfSignaled).bit(0), + Some(sequence < 0xffff_fffe) + ); + } + } + let mut f = Fixture::new(); + f.psbt.unsigned_tx.input[1].sequence = Sequence::MAX; + let ctx = f.context(1).unwrap(); + assert_eq!(evaluate_unit(&ctx, HasEffectiveLockTime).bit(0), Some(true)); + assert_eq!( + ctx.evaluate(CheckCltvHeight, &uint(1000, 32)), + Err(Error::CheckFailed) + ); +} + +#[test] +fn network_default_sighash_and_no_final_witness_overrides() { + let f = Fixture::new(); + for network in [Network::Regtest, Network::Signet] { + assert_eq!(genesis_hash(network), Err(Error::UnsupportedNetwork)); + assert_eq!( + signing_commitment(f.cmr, &f.data, network), + Err(Error::UnsupportedNetwork) + ); + assert_eq!( + Context::new( + &f.psbt, 0, f.cmr, &f.data, network, f.base, f.user, f.recovery + ) + .unwrap_err(), + Error::UnsupportedNetwork + ); + } + let names = [ + (Network::Bitcoin, "bitcoin"), + (Network::Testnet, "testnet"), + (Network::Testnet4, "testnet4"), + ]; + for (network, name) in names { + assert_eq!(network_name(network).unwrap(), name); + let mut f = Fixture::new(); + f.network = network; + f.rebind(); + assert_eq!(f.context(0).unwrap().summary().network, name); + } + for hash_type in [1u32, 2, 3, 0x80, 0x81, 0xff, u32::MAX] { + let mut f = Fixture::new(); + f.psbt.inputs[1].sighash_type = Some(psbt::PsbtSighashType::from_u32(hash_type)); + assert_eq!(f.context(0).unwrap_err(), Error::NonDefaultSighash(1)); + } + let mut f = Fixture::new(); + f.psbt.inputs[0].sighash_type = Some(TapSighashType::Default.into()); + assert!(f.context(0).is_ok()); + f.psbt.inputs[0].final_script_witness = Some(Witness::from_slice(&[&[0x50]])); + assert_eq!(f.context(0).unwrap_err(), Error::FinalWitness(0)); + let mut f = Fixture::new(); + f.psbt.inputs[0].final_script_sig = Some(ScriptBuf::from_bytes(vec![0])); + assert_eq!(f.context(0).unwrap_err(), Error::FinalScriptSig(0)); + let mut f = Fixture::new(); + let signature = bitcoin::taproot::Signature { + signature: bitcoin::secp256k1::schnorr::Signature::from_slice(&[0; 64]).unwrap(), + sighash_type: TapSighashType::All, + }; + f.psbt.inputs[0].tap_key_sig = Some(signature); + assert_eq!(f.context(0).unwrap_err(), Error::NonDefaultSighash(0)); + f.psbt.inputs[0].tap_key_sig = None; + f.psbt.inputs[0].tap_script_sigs.insert( + ( + f.user, + TapLeafHash::from_script(&ScriptBuf::new(), LeafVersion::TapScript), + ), + signature, + ); + assert_eq!(f.context(0).unwrap_err(), Error::NonDefaultSighash(0)); +} + +#[test] +fn approval_domains_preimages_and_cross_input_reuse_are_explicit() { + let f = Fixture::new(); + let ctx0 = f.context(0).unwrap(); + let ctx1 = f.context(1).unwrap(); + assert_ne!(ctx0.sighash(), ctx1.sighash()); + assert_ne!(ctx0.policy_approval_hash(), ctx1.policy_approval_hash()); + assert_eq!( + ctx0.policy_transaction_approval_hash(), + ctx1.policy_transaction_approval_hash() + ); + let fields = [ + profile_hash(), + genesis_hash(f.network).unwrap(), + ctx0.signing_commitment(), + ctx0.unsigned_tx_hash(), + ctx0.prevouts_hash(), + ctx0.cooperative_leaf_hash().to_byte_array(), + f.recovery.to_byte_array(), + ]; + let j54 = tagged( + "SimplicityUnchained/PolicyTransactionApproval/v1", + &fields.concat(), + ); + assert_eq!(j54, ctx0.policy_transaction_approval_hash()); + for (index, ctx) in [(0u32, &ctx0), (1, &ctx1)] { + assert_eq!( + tagged( + "SimplicityUnchained/PolicyInputApproval/v1", + &[j54.as_slice(), &index.to_be_bytes(), &ctx.sighash(),].concat() + ), + ctx.policy_approval_hash(), + ); + } + let legacy_commitment = tagged( + "SimplicityUnchained/SigningCommitment/v1", + &[f.cmr, sha(&f.data)].concat(), + ); + assert_ne!(ctx0.signing_commitment(), legacy_commitment); + let legacy = tagged( + "SimplicityUnchained/BitcoinApproval/v1", + &[ + [2u8].as_slice(), + &0u32.to_be_bytes(), + &legacy_commitment, + &ctx0.unsigned_tx_hash(), + &ctx0.prevouts_hash(), + ] + .concat(), + ); + assert_eq!(ctx0.legacy_approval_hash(), legacy); + assert_ne!(legacy, ctx0.policy_approval_hash()); + assert_ne!(legacy, j54); + for change in 0..8 { + let mut changed = Fixture::new(); + match change { + 0 => changed.psbt.unsigned_tx.output[0].value = Amount::from_sat(20_001), + 1 => changed.psbt.unsigned_tx.output[0].script_pubkey = ScriptBuf::new(), + 2 => changed.psbt.unsigned_tx.input[0].sequence = Sequence(6), + 3 => changed.psbt.unsigned_tx.lock_time = absolute::LockTime::from_consensus(1001), + 4 => { + changed.data.push(0); + changed.rebind(); + } + 5 => { + changed.network = Network::Bitcoin; + changed.rebind(); + } + 6 => { + changed.cmr[0] ^= 1; + changed.rebind(); + } + 7 => { + let mut parent = changed.parent(); + parent.output[1].value = Amount::from_sat(40_001); + changed.set_parent(parent); + } + _ => unreachable!(), + } + let actual = changed.context(0).unwrap(); + assert_ne!( + actual.policy_approval_hash(), + ctx0.policy_approval_hash(), + "mutation{change}" + ); + assert_ne!( + actual.policy_transaction_approval_hash(), + j54, + "mutation{change}" + ); + assert_ne!(actual.sighash(), ctx0.sighash(), "mutation{change}"); + } +} + +#[test] +fn catalogue_manifest_types_legacy_identity_cost_and_prefix_freedom() { + use simplicity::jet::{Core, Jet}; + let metadata = manifest(); + assert_eq!(metadata.profile, PROFILE_NAME); + assert_eq!(metadata.status, "experimental"); + assert_eq!(metadata.jets.len(), 55); + assert_eq!(metadata.legacy_jets.len(), Core::ALL.len() + 3); + assert_eq!( + hex::encode(sha(&metadata.canonical_bytes())), + metadata.profile_hash + ); + assert_eq!(manifest(), metadata); + let decoded: simplicity_unchained_policy::Manifest = + serde_json::from_slice(&serde_json::to_vec(&metadata).unwrap()).unwrap(); + assert_eq!(decoded, metadata); + let mut identities = BTreeSet::new(); + for (i, jet) in Builtin::ALL.into_iter().enumerate() { + assert_eq!(jet.id() as usize, i); + assert_eq!(Builtin::from_id(i as u8), Some(jet)); + assert_eq!(jet.name().parse::().unwrap(), jet); + assert_eq!(jet.to_string(), jet.name()); + assert_eq!(jet.cost_milliweight(), 10_000); + assert_eq!(jet.source_bits(), jet.source_type().to_final().bit_width()); + assert_eq!(jet.target_bits(), jet.target_type().to_final().bit_width()); + assert_eq!(metadata.jets[i].encoding, format!("1{i:06b}")); + assert_eq!(metadata.jets[i].cmr, hex::encode(jet.cmr())); + assert_ne!(jet.cmr(), sha(jet.name().as_bytes())); + assert!(identities.insert(jet.cmr())); + assert!(jet.source_bits() + jet.target_bits() <= 321); + } + for reserved in 55..=255 { + assert_eq!(Builtin::from_id(reserved), None); + } + assert!("unknown_jet".parse::().is_err()); + for (i, jet) in Core::ALL.into_iter().enumerate() { + let row = &metadata.legacy_jets[i]; + assert_eq!(row.name, jet.to_string()); + assert_eq!(row.cmr, hex::encode(jet.cmr().to_byte_array())); + assert_eq!(row.cost_milliweight.to_string(), jet.cost().to_string()); + } + let tail = &metadata.legacy_jets[Core::ALL.len()..]; + assert_eq!( + tail.iter().map(|j| j.encoding.as_str()).collect::>(), + ["0111110", "01111110", "01111111"] + ); + assert_eq!( + tail.iter().map(|j| j.cost_milliweight).collect::>(), + [100, 100, 356] + ); + assert_eq!( + tail[0].cmr, + "dcccd2895922e75b018b0846e5cd4963808bbfd48b47234475607f90e70ee032" + ); + assert_eq!( + tail[1].cmr, + "27eab09068b035af619713335b73d2520ecc02090067c8fccabb4d72a655cdcb" + ); + assert_eq!( + tail[2].cmr, + "f0ed96ee8fbf1a69beb20deb3a7a9b748bdc18da284d15438debc7d4faeb9962" + ); + let encodings: Vec<_> = metadata + .jets + .iter() + .chain(&metadata.legacy_jets) + .map(|j| &j.encoding) + .collect(); + for (i, a) in encodings.iter().enumerate() { + for (j, b) in encodings.iter().enumerate() { + if i != j { + assert!(!a.starts_with(b.as_str()), "{a} prefixes{b}"); + } + } + } + let mut changed = metadata.clone(); + changed.body.jets[0].semantic_contract.push('!'); + assert_ne!(sha(&changed.canonical_bytes()), profile_hash()); + changed = metadata.clone(); + changed.body.limits.max_inputs -= 1; + assert_ne!(sha(&changed.canonical_bytes()), profile_hash()); +} + +#[test] +fn golden_manifest_and_transaction_vectors() { + let f = Fixture::new(); + let ctx = f.context(0).unwrap(); + let other = f.context(1).unwrap(); + let value = serde_json::json!({ + "profile_hash": hex::encode(profile_hash()), + "first_jet_identity": hex::encode(Builtin::PolicyProfileHash.cmr()), + "last_jet_identity": hex::encode(Builtin::TxVsizeLowerBound.cmr()), + "base_service_pubkey": f.base.to_string(), + "script_data_hex": hex::encode(&f.data), + "parent_tx_hex": hex::encode(serialize(&f.parent())), + "unsigned_tx_hex": hex::encode(serialize(ctx.tx())), + "prevouts_vector_hex": hex::encode(serialize(&ctx.prevouts().to_vec())), + "inputs_vector_hex": hex::encode(serialize(&ctx.tx().input)), + "outputs_vector_hex": hex::encode(serialize(&ctx.tx().output)), + "tx_inputs_hash": hex::encode(ctx.tx_inputs_hash()), + "tx_outputs_hash": hex::encode(ctx.tx_outputs_hash()), + "cooperative_script_hex": hex::encode(ctx.cooperative_script().as_bytes()), + "control_block_hex": hex::encode(ctx.control_block().serialize()), + "legacy_approval_hash": hex::encode(ctx.legacy_approval_hash()), + "input1_sighash": hex::encode(other.sighash()), + "input1_approval_hash": hex::encode(other.policy_approval_hash()), + "summary": ctx.summary(), + }); + let expected: serde_json::Value = + serde_json::from_str(include_str!("fixtures/golden.json")).unwrap(); + if std::env::var_os("POLICY_DUMP_VECTORS").is_some() { + println!("{}", serde_json::to_string_pretty(&value).unwrap()); + } + assert_eq!( + value, expected, + "frozen profile or transaction vectors changed" + ); +} diff --git a/policy/tests/liquid.rs b/policy/tests/liquid.rs new file mode 100644 index 0000000..3b80e92 --- /dev/null +++ b/policy/tests/liquid.rs @@ -0,0 +1,677 @@ +mod common { + include!("common/liquid.rs"); +} +use common::*; +use liquid::elements::{ + self, SchnorrSighashType, + encode::{deserialize, serialize}, + secp256k1_zkp::{Keypair, Message, Secp256k1}, + sighash::{Prevouts, SighashCache}, +}; +use simplicity::{ + BitIter, BitWriter, CommitNode, ConstructNode, jet::Jet, node::CoreConstructible, types, +}; +use simplicity_unchained_policy::{ + BitValue, + liquid::{self, Builtin, Context, PolicyJet}, +}; +use std::{io::Write, sync::Arc}; + +fn value(c: &Context, j: Builtin, input: &[u8]) -> BitValue { + c.evaluate( + j, + &BitValue::from_bytes(input.to_vec(), j.source_bits()).unwrap(), + ) + .unwrap() +} +fn u64_result(v: BitValue) -> u64 { + u64::from_be_bytes(v.bytes.try_into().unwrap()) +} + +#[test] +fn confidential_multi_asset_verified_signing_roundtrip() { + let mut f = Fixture::new(); + let summary = f.context(0).unwrap().summary(); + assert!( + summary + .inputs + .iter() + .all(|d| d.confidential_asset && d.confidential_value) + ); + assert!( + summary.outputs[..4] + .iter() + .all(|d| d.confidential_asset && d.confidential_value) + ); + let fee = summary + .assets + .iter() + .find(|a| a.asset_id == f.network.fee_asset().to_string()) + .unwrap(); + assert_eq!( + ( + fee.total_input_value, + fee.total_output_value, + fee.fee, + fee.returned_value, + fee.debit + ), + (100_000, 100_000, 1_000, 79_000, 21_000) + ); + let other = summary + .assets + .iter() + .find(|a| a.asset_id == f.other_asset.to_string()) + .unwrap(); + assert_eq!( + ( + other.total_input_value, + other.total_output_value, + other.fee, + other.debit + ), + (70_000, 70_000, 0, 15_000) + ); + assert!(!summary.chain_status_verified); + let out = &f.pset.inputs()[0].non_witness_utxo.as_ref().unwrap().output[0]; + let opening = + liquid::unblind_output(out, Some(&hex::encode(f.blinding_secret.secret_bytes()))).unwrap(); + assert_eq!(opening.value, 100_000); + assert!(liquid::unblind_output(out, Some(&hex::encode(f.user_secret.secret_bytes()))).is_err()); + for index in 0..2 { + let p = liquid::PreparedPolicy::new( + &f.program, &f.pset, index, &f.data, f.network, f.base, f.user, f.recovery, + ) + .unwrap(); + assert!(p.evaluate_default(&[]).unwrap() > 0); + } + f.sign_all(); + let c = f.context(0).unwrap(); + assert_eq!(c.summary(), summary); + let tx = liquid::finalize(&c, &f.pset).unwrap(); + assert!(tx.input.iter().all(|i| i.witness.script_witness.len() == 4)); + tx.verify_tx_amt_proofs(&Secp256k1::new(), c.prevouts()) + .unwrap(); + for index in 0..2 { + let digest = SighashCache::new(c.tx()) + .taproot_script_spend_signature_hash( + index, + &Prevouts::All(c.prevouts()), + c.cooperative().leaf_hash, + SchnorrSighashType::Default, + f.network.genesis_hash(), + ) + .unwrap(); + for ((key, _), sig) in &f.pset.inputs()[index].tap_script_sigs { + Secp256k1::verification_only() + .verify_schnorr(&sig.sig, &Message::from_digest(digest.to_byte_array()), key) + .unwrap(); + } + } + let roundtrip: elements::Transaction = deserialize(&serialize(&tx)).unwrap(); + assert_eq!(roundtrip, tx); +} + +#[test] +fn amounts_assets_and_disclosure_proof_tampering_rejected() { + let f = Fixture::new(); + for input in [true, false] { + for kind in 0..6 { + let mut p = f.pset.clone(); + if input { + let m = &mut p.inputs_mut()[0]; + match kind { + 0 => m.amount = Some(m.amount.unwrap() + 1), + 1 => m.asset = Some(f.other_asset), + 2 => m.blind_value_proof = None, + 3 => m.blind_asset_proof = None, + 4 => m.blind_value_proof = f.pset.inputs()[1].blind_value_proof.clone(), + _ => m.blind_asset_proof = f.pset.inputs()[1].blind_asset_proof.clone(), + } + } else { + let m = &mut p.outputs_mut()[0]; + match kind { + 0 => m.amount = Some(m.amount.unwrap() + 1), + 1 => m.asset = Some(f.other_asset), + 2 => m.blind_value_proof = None, + 3 => m.blind_asset_proof = None, + 4 => m.blind_value_proof = f.pset.outputs()[1].blind_value_proof.clone(), + _ => m.blind_asset_proof = f.pset.outputs()[2].blind_asset_proof.clone(), + } + } + assert!( + f.context_for(&p, 0).is_err(), + "input={input} mutation={kind}" + ); + } + } + let mut p = f.pset.clone(); + let mut proof = p.outputs()[0].blind_value_proof.as_ref().unwrap().to_vec(); + let last = proof.len() - 1; + proof[last] ^= 1; + if let Ok(proof) = elements::RangeProof::from_slice(&proof) { + p.outputs_mut()[0].blind_value_proof = Some(proof); + assert!(f.context_for(&p, 0).is_err()); + } +} + +#[test] +fn missing_parent_witness_and_original_transaction_proofs_rejected() { + let f = Fixture::new(); + for kind in 0..8 { + let mut p = f.pset.clone(); + match kind { + 0 => p.inputs_mut()[0].non_witness_utxo = None, + 1 => p.inputs_mut()[0].witness_utxo = None, + 2 => { + p.inputs_mut()[0] + .non_witness_utxo + .as_mut() + .unwrap() + .lock_time = elements::LockTime::from_consensus(1) + } + 3 => { + p.inputs_mut()[0].witness_utxo.as_mut().unwrap().value = + elements::confidential::Value::Explicit(100_000) + } + 4 => p.outputs_mut()[0].value_rangeproof = None, + 5 => p.outputs_mut()[0].asset_surjection_proof = None, + 6 => { + p.inputs_mut()[0].non_witness_utxo.as_mut().unwrap().output[0] + .witness + .rangeproof = elements::RangeProof::EMPTY + } + _ => { + p.inputs_mut()[0].non_witness_utxo.as_mut().unwrap().output[0] + .witness + .rangeproof = f.pset.inputs()[1].non_witness_utxo.as_ref().unwrap().output[1] + .witness + .rangeproof + .clone() + } + } + assert!(f.context_for(&p, 0).is_err(), "mutation={kind}"); + } +} + +#[test] +fn current_asset_projection_and_explicit_budgets_cannot_switch_assets() { + let f = Fixture::new(); + let a = f.context(0).unwrap(); + let b = f.context(1).unwrap(); + let fee = Builtin::Policy(simplicity_unchained_policy::Builtin::TxFee); + assert_eq!(u64_result(value(&a, fee, &[])), 1_000); + assert_eq!(u64_result(value(&b, fee, &[])), 1_000); + for c in [&a, &b] { + assert_eq!( + u64_result(value( + c, + Builtin::AssetCurrentScriptDebit, + &f.network.fee_asset().to_byte_array() + )), + 21_000 + ); + assert_eq!( + u64_result(value( + c, + Builtin::AssetCurrentScriptDebit, + &f.other_asset.to_byte_array() + )), + 15_000 + ); + assert_eq!( + u64_result(value( + c, + Builtin::AssetTotalInputValue, + &f.other_asset.to_byte_array() + )), + 70_000 + ); + } + let input_value = Builtin::Policy(simplicity_unchained_policy::Builtin::InputValue); + assert_eq!( + value(&a, input_value, &1u32.to_be_bytes()).bit(0), + Some(false) + ); + assert_eq!( + value(&b, input_value, &0u32.to_be_bytes()).bit(0), + Some(false) + ); + assert_ne!( + value(&a, Builtin::CurrentAsset, &[]), + value(&b, Builtin::CurrentAsset, &[]) + ); + assert_eq!( + value(&a, Builtin::InputValueAsset, &1u32.to_be_bytes()).bit(0), + Some(true) + ); +} + +#[test] +fn genesis_profile_program_script_and_approval_digests_are_bound() { + let f = Fixture::new(); + let a = f.context(0).unwrap().summary(); + let b = f.context(1).unwrap().summary(); + assert_ne!(a.policy_approval_hash, b.policy_approval_hash); + assert_eq!( + a.policy_transaction_approval_hash, + b.policy_transaction_approval_hash + ); + assert_ne!(a.cooperative_sighash_default, b.cooperative_sighash_default); + assert_ne!( + liquid::profile_hash(), + simplicity_unchained_policy::profile_hash() + ); + assert_ne!( + liquid::signing_commitment(f.cmr, &f.data, f.network).unwrap(), + simplicity_unchained_policy::signing_commitment( + f.cmr, + &f.data, + simplicity_unchained_policy::bitcoin::Network::Testnet4 + ) + .unwrap() + ); + assert!( + Context::new( + &f.pset, + 0, + f.cmr, + &f.data, + liquid::Network::Liquid, + f.base, + f.user, + f.recovery + ) + .is_err() + ); + assert!( + Context::new( + &f.pset, 0, [1; 32], &f.data, f.network, f.base, f.user, f.recovery + ) + .is_err() + ); + assert!( + Context::new( + &f.pset, 0, f.cmr, b"changed", f.network, f.base, f.user, f.recovery + ) + .is_err() + ); + assert_eq!( + hex::encode(f.network.genesis_hash().to_byte_array()), + "c1b16ae24f2423aea2ea34552292793b5b5e82999a1eed81d56aee528eda71a7" + ); + let wrong = SighashCache::new(&f.pset.extract_tx().unwrap()) + .taproot_script_spend_signature_hash( + 0, + &Prevouts::All(f.context(0).unwrap().prevouts()), + f.context(0).unwrap().cooperative().leaf_hash, + SchnorrSighashType::Default, + liquid::Network::Liquid.genesis_hash(), + ) + .unwrap(); + assert_ne!(wrong.to_byte_array(), f.context(0).unwrap().sighash()); +} + +#[test] +fn stale_invalid_unknown_and_missing_signatures_cannot_sign_or_finalize() { + let mut f = Fixture::new(); + f.sign_all(); + let c = f.context(0).unwrap(); + for kind in 0..4 { + let mut p = f.pset.clone(); + match kind { + 0 => { + p.inputs_mut()[0].tap_script_sigs.clear(); + } + 1 => { + p.inputs_mut()[0].tap_script_sigs = f.pset.inputs()[1].tap_script_sigs.clone(); + } + 2 => { + p.outputs_mut()[0].amount = Some(20_001); + } + _ => { + p.inputs_mut()[0].sequence = Some(elements::Sequence(11)); + } + } + assert!(liquid::finalize(&c, &p).is_err()); + assert!( + liquid::add_signature(&c, &mut p, f.user_secret, false).is_err(), + "mutation {kind}" + ); + } + let p = f.pset.clone(); + let key = Keypair::from_secret_key(&Secp256k1::new(), &f.user_secret); + let fake = Secp256k1::new().sign_schnorr_no_aux_rand(&Message::from_digest([9; 32]), &key); + let mut p = p; + p.inputs_mut()[0].tap_script_sigs.insert( + (f.user, c.cooperative().leaf_hash), + elements::SchnorrSig { + sig: fake, + hash_ty: SchnorrSighashType::Default, + }, + ); + assert!(f.context_for(&p, 0).is_err()); +} + +#[test] +fn every_liquid_jet_identity_type_encoding_and_vm_admission_are_separate() { + let mut cmrs = std::collections::BTreeSet::new(); + for j in Builtin::all() { + assert!(cmrs.insert(j.cmr())); + if let Builtin::Policy(old) = j { + assert_ne!(j.cmr(), old.cmr()); + } + let jet = PolicyJet::Builtin(j); + let mut bytes = Vec::new(); + let mut writer = BitWriter::new(&mut bytes as &mut dyn Write); + assert_eq!(jet.encode(&mut writer).unwrap(), 8); + writer.flush_all().unwrap(); + assert_eq!(PolicyJet::decode(&mut BitIter::from(bytes)).unwrap(), jet); + } + assert_eq!(cmrs.len(), 66); + let program = types::Context::with_context(|ctx| { + let profile = Arc::::jet( + &ctx, + &PolicyJet::Builtin(Builtin::Policy( + simplicity_unchained_policy::Builtin::PolicyProfileHash, + )), + ); + let unit = Arc::::unit(&ctx); + Arc::::comp(&profile, &unit) + .unwrap() + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + assert!( + CommitNode::decode::<_, simplicity::jet::Core>(BitIter::from(program.as_slice())).is_err() + ); + let f = Fixture::with_program(program); + let p = liquid::PreparedPolicy::new( + &f.program, &f.pset, 0, &f.data, f.network, f.base, f.user, f.recovery, + ) + .unwrap(); + assert!(p.evaluate_default(&[]).unwrap() > 10_000); +} + +#[test] +fn mixed_commitment_external_disclosures_are_verified_not_trusted() { + use elements::confidential::{Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor}; + use elements::secp256k1_zkp::{PublicKey, SecretKey, rand::rngs::OsRng}; + let f = Fixture::new(); + let secp = Secp256k1::new(); + let vbf = ValueBlindingFactor::from_slice(&[7; 32]).unwrap(); + let asset = f.network.fee_asset(); + let script = f.context(0).unwrap().cooperative().script_pubkey.clone(); + let blinded = |amount| { + let (value, nonce, proof) = Value::Explicit(amount) + .blind( + &secp, + vbf, + PublicKey::from_secret_key(&secp, &f.blinding_secret), + SecretKey::from_slice(&[8; 32]).unwrap(), + &script, + &elements::RangeProofMessage::new(asset, AssetBlindingFactor::zero()), + ) + .unwrap(); + elements::TxOut { + asset: Asset::Explicit(asset), + value, + nonce, + script_pubkey: script.clone(), + witness: elements::TxOutWitness { + rangeproof: proof, + surjection_proof: elements::SurjectionProof::EMPTY, + }, + } + }; + let input = blinded(10_000); + let output = blinded(9_000); + let parent = elements::Transaction { + version: 2, + lock_time: elements::LockTime::ZERO, + input: vec![elements::TxIn::default()], + output: vec![input.clone()], + }; + let tx = elements::Transaction { + version: 2, + lock_time: elements::LockTime::ZERO, + input: vec![elements::TxIn { + previous_output: elements::OutPoint::new(parent.txid(), 0), + ..Default::default() + }], + output: vec![ + output.clone(), + elements::TxOut { + asset: Asset::Explicit(asset), + value: Value::Explicit(1_000), + nonce: Nonce::Null, + script_pubkey: Default::default(), + witness: Default::default(), + }, + ], + }; + let mut p = elements::pset::PartiallySignedTransaction::from_tx(tx); + p.inputs_mut()[0].non_witness_utxo = Some(parent); + p.inputs_mut()[0].witness_utxo = Some(input.clone()); + p.inputs_mut()[0].amount = Some(10_000); + let generator = input.asset.into_asset_gen(&secp).unwrap(); + p.inputs_mut()[0].blind_value_proof = Some( + elements::RangeProof::blind_value_proof( + &mut OsRng, + &secp, + 10_000, + input.value.commitment().unwrap(), + generator, + vbf, + ) + .unwrap(), + ); + p.outputs_mut()[0].amount = Some(9_000); + p.outputs_mut()[0].blind_value_proof = Some( + elements::RangeProof::blind_value_proof( + &mut OsRng, + &secp, + 9_000, + output.value.commitment().unwrap(), + generator, + vbf, + ) + .unwrap(), + ); + assert!(f.context_for(&p, 0).is_ok()); + let mut missing = p.clone(); + missing.inputs_mut()[0].blind_value_proof = None; + assert!(f.context_for(&missing, 0).is_err()); + let mut wrong = p.clone(); + wrong.outputs_mut()[0].amount = Some(8_999); + assert!(f.context_for(&wrong, 0).is_err()); + assert!( + liquid::unblind_output(&input, Some(&hex::encode(f.blinding_secret.secret_bytes()))) + .is_err() + ); + let abf = AssetBlindingFactor::from_slice(&[9; 32]).unwrap(); + let other_mixed = elements::TxOut { + asset: Asset::new_confidential(&secp, asset, abf), + value: Value::Explicit(100), + nonce: Nonce::Null, + script_pubkey: script, + witness: Default::default(), + }; + let proof = + elements::SurjectionProof::blind_asset_proof(&mut OsRng, &secp, asset, abf).unwrap(); + assert!(liquid::verify_disclosure(&other_mixed, None, Some(asset), None, Some(&proof)).is_ok()); + assert!(liquid::verify_disclosure(&other_mixed, None, Some(asset), None, None).is_err()); + assert!( + liquid::verify_disclosure(&other_mixed, None, Some(f.other_asset), None, Some(&proof)) + .is_err() + ); +} + +#[test] +fn real_approval_witnesses_bind_the_entire_disclosed_transaction_and_profile() { + use simplicity::node::WitnessConstructible; + let secp = Secp256k1::new(); + let approver = Keypair::from_secret_key( + &secp, + &elements::secp256k1_zkp::SecretKey::from_slice(&[0x44; 32]).unwrap(), + ); + let program = types::Context::with_context(|ctx| { + let pk = Arc::::const_word( + &ctx, + simplicity::Word::u256(approver.x_only_public_key().0.serialize()), + ); + let digest = Arc::::jet( + &ctx, + &PolicyJet::Builtin(Builtin::Policy( + simplicity_unchained_policy::Builtin::PolicyTransactionApprovalHash, + )), + ); + let pk_digest = Arc::::pair(&pk, &digest).unwrap(); + let signature = Arc::::witness(&ctx, None); + let arguments = Arc::::pair(&pk_digest, &signature).unwrap(); + Arc::::comp( + &arguments, + &Arc::::jet( + &ctx, + &PolicyJet::Core(simplicity::jet::Core::Bip0340Verify), + ), + ) + .unwrap() + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + let f = Fixture::with_program(program.clone()); + let prepare = |f: &Fixture, index| { + liquid::PreparedPolicy::new( + &program, &f.pset, index, &f.data, f.network, f.base, f.user, f.recovery, + ) + .unwrap() + }; + let p = prepare(&f, 0); + let digest = hex::decode(&p.summary().policy_transaction_approval_hash) + .unwrap() + .try_into() + .unwrap(); + let sig = secp.sign_schnorr_no_aux_rand(&Message::from_digest(digest), &approver); + assert!(p.evaluate_default(sig.as_ref()).is_ok()); + assert!(prepare(&f, 1).evaluate_default(sig.as_ref()).is_ok()); + assert!(p.evaluate_default(&[]).is_err()); + let mut tampered = sig.as_ref().to_vec(); + tampered[1] ^= 1; + assert!(p.evaluate_default(&tampered).is_err()); + let mut extra = sig.as_ref().to_vec(); + extra.push(0); + assert!(p.evaluate_default(&extra).is_err()); + // A separately blinded, fully valid same-policy transaction cannot replay approval. + let other = Fixture::with_program(program); + assert!( + liquid::PreparedPolicy::new( + &other.program, + &other.pset, + 0, + &other.data, + other.network, + other.base, + other.user, + other.recovery + ) + .unwrap() + .evaluate_default(sig.as_ref()) + .is_err() + ); + let wrong = secp.sign_schnorr_no_aux_rand( + &Message::from_digest( + simplicity_unchained_policy::signing_commitment( + f.cmr, + &f.data, + simplicity_unchained_policy::bitcoin::Network::Testnet4, + ) + .unwrap(), + ), + &approver, + ); + assert!(p.evaluate_default(wrong.as_ref()).is_err()); +} + +#[test] +fn frozen_confidential_fixture_manifest_and_signatures_are_reproducible() { + let fixture: serde_json::Value = + serde_json::from_str(include_str!("fixtures/liquid-policy-v1.json")).unwrap(); + let f = Fixture::deterministic(); + assert_eq!(fixture["pset_hex"], hex::encode(serialize(&f.pset))); + assert_eq!( + fixture["source_parent_tx_hex"], + hex::encode(serialize(&f.source_parent)) + ); + assert_eq!( + hex::encode(liquid::profile_hash()), + "6097ac61cbd923269edaaed8cf14691cc8ba756e45949f7478f7f907f42d8ce2" + ); + assert_eq!( + hex::encode(simplicity_unchained_policy::profile_hash()), + "0890429432ce3f797ae26e43ca6d47bdea6cd9369b32a48722a8b6385addd0c7" + ); + let signed = + deserialize(&hex::decode(fixture["fully_signed_pset_hex"].as_str().unwrap()).unwrap()) + .unwrap(); + for index in 0..2 { + let context = f.context_for(&signed, index).unwrap(); + assert_eq!( + fixture["contexts"][index], + serde_json::to_value(context.summary()).unwrap() + ); + } + + let tx = liquid::finalize(&f.context_for(&signed, 0).unwrap(), &signed).unwrap(); + assert_eq!(fixture["transaction_hex"], hex::encode(serialize(&tx))); + for (_, encoded) in fixture["negative_psets"].as_object().unwrap() { + if let Ok(p) = deserialize(&hex::decode(encoded.as_str().unwrap()).unwrap()) { + assert!(f.context_for(&p, 0).is_err()); + } + } + let mut manifest = liquid::manifest(); + manifest.as_object_mut().unwrap().remove("profile_hash"); + assert_eq!( + elements::hashes::sha256::Hash::hash(&serde_json::to_vec(&manifest).unwrap()) + .to_byte_array(), + liquid::profile_hash() + ); +} + +#[test] +fn bitcoin_profile_funding_script_is_not_reinterpreted_as_liquid_contract() { + use simplicity_unchained_policy::bitcoin::hashes::Hash as _; + let f = Fixture::new(); + let btc = simplicity_unchained_policy::bitcoin::XOnlyPublicKey::from_slice(&f.base.serialize()) + .unwrap(); + let user = + simplicity_unchained_policy::bitcoin::XOnlyPublicKey::from_slice(&f.user.serialize()) + .unwrap(); + let recovery = simplicity_unchained_policy::bitcoin::TapNodeHash::from_byte_array( + f.recovery.to_byte_array(), + ); + let old = simplicity_unchained_policy::cooperative_output( + btc, + f.cmr, + &f.data, + simplicity_unchained_policy::bitcoin::Network::Testnet4, + user, + recovery, + ) + .unwrap(); + let mut p = f.pset.clone(); + let mut parent = p.inputs()[0].non_witness_utxo.as_ref().unwrap().clone(); + parent.output[0].script_pubkey = elements::Script::from(old.script_pubkey.as_bytes().to_vec()); + for (index, map) in p.inputs_mut().iter_mut().enumerate() { + map.previous_txid = parent.txid(); + map.witness_utxo = Some(parent.output[index].clone()); + map.non_witness_utxo = Some(parent.clone()); + } + let error = f.context_for(&p, 0).unwrap_err().to_string(); + assert!( + error.contains("funding script/profile/network mismatch"), + "{error}" + ); +} diff --git a/service/Cargo.toml b/service/Cargo.toml index fa7f9cd..a4b6a28 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -16,10 +16,10 @@ tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "sync", " toml = "0.9.10" tracing = "0.1" tracing-subscriber = "0.3.0" -validator = { version = "0.20.0", features = ["derive"] } simplicity-lang = { workspace = true } core = { path = "../core" } +simplicity-unchained-policy = { path = "../policy" } [dev-dependencies] http-body = "1" diff --git a/service/README.md b/service/README.md index 69b3ad8..3559b0d 100644 --- a/service/README.md +++ b/service/README.md @@ -1,10 +1,11 @@ # Simplicity Unchained Service -Simplicity Unchained Service is a web service that provides endpoints to interact with the Simplicity Unchained oracle. It currently supports a 2-of-2 multisig side effect triggered by the execution of Simplicity programs. +Simplicity Unchained Service evaluates committed policies and co-signs Bitcoin and Liquid transactions. It exposes `bitcoin-policy-v1` and `liquid-policy-v1` through separate API paths. ## Starting the Service -First provision a configuration with a unique, persistent signing key: +For a new service, provision a configuration with a unique, persistent signing +key. Reuse the existing private configuration when restarting a funded service. ```bash umask 077 @@ -17,7 +18,7 @@ Set `service.private_key` in the local `config.toml` to a newly generated generator, for example `openssl rand -hex 32`, and edit the file locally without putting the key in command-line arguments. Keep the file private and backed up; do not commit it or put it in a Docker build context. The example's empty key -deliberately cannot start the service. +cannot start the service. Then start the service: @@ -25,13 +26,14 @@ Then start the service: cargo run --quiet -- start ``` -You can optionally specify a custom configuration file using the `--config` flag: +You can specify a custom configuration file using `--config`: ```bash cargo run --quiet -- start --config path/to/your/config.toml ``` -By default, service configuration looks for a `config.toml` file in the current directory. +By default, the service looks for `config.toml` in the current directory. Invalid +configuration exits with a nonzero status. ## Configuration @@ -39,14 +41,17 @@ By default, service configuration looks for a `config.toml` file in the current [service] # Port on which the service will run port = 30431 -# Required: replace locally with a unique, persistent 32-byte private key. +# Required: provision a unique, persistent 32-byte private key. private_key = "" -# Network to get metadata from: liquid or liquidtestnet -elements_network = "liquidtestnet" -# Bitcoin network for signature computation: bitcoin, testnet, testnet4 +# Bitcoin network: bitcoin, testnet, testnet4 bitcoin_network = "testnet4" +# Liquid network: liquid or liquidtestnet (default) +liquid_network = "liquidtestnet" ``` +`bitcoin_network` is required. `liquid_network` is optional and defaults to +Liquid Testnet. Requests must match the configured network for their profile. + ### Docker The image contains the service binary but no active configuration or signing key. @@ -63,17 +68,12 @@ docker run --rm -p 30431:30431 \ Run these commands from the repository root. The host file must exist and be readable by the container's service user (UID/GID 1000), without making the key world-readable. Adjust the published port if you change `service.port`. -Starting the image without provisioning fails closed with a nonzero exit. ## Endpoints -The service exposes the following endpoints: - ### 1. Version Endpoint -`GET /simplicity-unchained/version`: Returns the current version of the service. - -**Success Response**: +`GET /simplicity-unchained/version` returns the service package version: ```json { @@ -81,159 +81,106 @@ The service exposes the following endpoints: } ``` -### 2. Sign PSBT Endpoint - -`POST /simplicity-unchained/sign/psbt`: Accepts a Simplicity program and its inputs, executes it, and if successful, co-signs a 2-of-2 multisig Bitcoin transaction. - -For per-spend authorization, the program must verify an approver signature over -`jet::tx_approval_hash()`. The runner derives this digest from the PSBT, all -prevouts, input index, configured Bitcoin network, and program/script commitment. -Use the [local approval workflow](../cli/README.md#bitcoin-transaction-approvals) -before requesting the signature. All inputs need `witness_utxo`; the approver -must verify those prevouts independently. - -A fixed-message signature, hash preimage, or other static condition is still -replayable across transactions. Merely providing a PSBT or using the new runner -does not convert such a policy into per-spend approval. Existing static contracts -need a new program/address to adopt the new condition. See the -[digest definition and compatibility rules](../core/README.md#bitcoin-per-spend-approvals). - -**Request Body**: +### 2. Policy Endpoints -```json -{ - "psbt_hex": "70736274ff...", - "input_index": 0, - "program": "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA", - "user_pubkey": "02...", - "user_leaf_hash_hex": "a1b2c3...", - "witness": null, - "redeem_script_hex": null -} -``` - -**Request Fields**: +Bitcoin operations use `/simplicity-unchained/policy/bitcoin`; Liquid operations +use `/simplicity-unchained/policy/liquid`. -- `psbt_hex`: The PSBT (Partially Signed Bitcoin Transaction) encoded as a hexadecimal string -- `input_index`: The index of the transaction input to sign (must be between 0 and 65535) -- `program`: The Simplicity program to execute for validation before signing -- `user_pubkey`: The user's compressed public key in hexadecimal format -- `user_leaf_hash_hex`: The user leaf hash in hexadecimal format, used to reconstruct the taproot tree -- `witness`: The witness data required by the Simplicity program (optional) -- `redeem_script_hex`: Script bytes exposed to script-reading jets (optional; null or omitted means empty). Must exactly match the script supplied to `/tweak` before funding. +| Method | Path suffix | Operation | +|---|---|---| +| GET | `/capabilities` | Return the profile fingerprint, manifest, network, genesis hash, base public key and limits. | +| POST | `/tweak` | Derive the policy-bound cooperative key before funding. | +| POST | `/prepare` | Verify transaction evidence and return the context without requiring approval witnesses. | +| POST | `/evaluate` | Evaluate the witnessed policy without signing. | +| POST | `/sign` | Prepare and evaluate again, then co-sign the selected input. | -**Success Response**: +The [CLI](../cli/README.md) checks the service manifest, network and known base +key, reconstructs the context locally and verifies returned signatures. -```json -{ - "psbt_hex": "70736274ff...", - "signature_hex": "3045022100...", - "public_key_hex": "02...", - "input_index": 0, - "partial_sigs_count": 1 -} -``` +**Program binding** -**Error Response**: +All POST operations require these fields. This example is for Bitcoin: ```json { - "error": "Validation failed: ..." + "profile_hash": "", + "network": "testnet4", + "program": "", + "redeem_script_hex": "" } ``` -### 3. Sign PSET Endpoint - -`POST /simplicity-unchained/sign/pset`: Accepts a Simplicity program and its inputs, executes it, and if successful, co-signs a 2-of-2 multisig transaction. -**Request Body**: - -```json -{ - "pset_hex": "70736574ff...", - "input_index": 0, - "program": "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA", - "user_pubkey": "02...", - "user_leaf_hash_hex": "a1b2c3...", - "witness": null, - "redeem_script_hex": null -} -``` +For Liquid, use its fingerprint, compiled program and configured network. +`redeem_script_hex` is required; `""` commits zero script-data bytes. -**Request Fields**: +`/tweak` returns `profile`, `profile_hash`, `network`, `genesis_hash`, `cmr`, +`signing_commitment`, `public_key_hex` and `base_public_key_hex`. +`public_key_hex` is the 32-byte x-only cooperative leaf key, not the Taproot +internal key. The commitment binds the profile, genesis hash, CMR and script +data; see the [kernel specification](../policy/README.md). -- `pset_hex`: The PSET (Partially Signed Elements Transaction) encoded as a hexadecimal string -- `input_index`: The index of the transaction input to sign (must be between 0 and 65535) -- `program`: The Simplicity program to execute for validation before signing -- `user_pubkey`: The user's compressed public key in hexadecimal format -- `user_leaf_hash_hex`: The user leaf hash in hexadecimal format, used to reconstruct the taproot tree -- `witness`: The witness data required by the Simplicity program (optional) -- `redeem_script_hex`: Script bytes exposed to script-reading jets (optional; null or omitted means empty). Must exactly match the script supplied to `/tweak` before funding. +**Transaction requests** -**Success Response**: +`prepare`, `evaluate` and `sign` add these fields to the binding: ```json { - "pset_hex": "70736574ff...", - "signature_hex": "3045022100...", - "public_key_hex": "02...", + "psbt_hex": "", "input_index": 0, - "partial_sigs_count": 1 -} -``` - -**Error Response**: - -```json -{ - "error": "Validation failed: ..." -} -``` - -### 4. Tweak Endpoint - -`POST /simplicity-unchained/tweak`: Accepts a Simplicity program and script data, computes its CMR (commitment Merkle root), and returns the public key tweaked with a commitment to both. - -### **Note** - -The tweaked key is used in the cooperative multisig leaf, not as the P2TR internal key. - -**Request Body**: - -```json -{ - "program": "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA", - "jet_env": "elements", - "redeem_script_hex": null -} -``` - -**Request Fields**: - -- `program`: The Simplicity program to compute the CMR from (must be a non-empty string) -- `jet_env`: The jet environment to use, either `elements` or `bitcoin` (optional, defaults to `elements`) -- `redeem_script_hex`: Exact script bytes to commit to and later expose to script-reading jets (optional; null, omitted, or `""` means empty). - -**Success Response**: - -```json -{ - "cmr_hex": "a9b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1", - "tweaked_public_key_hex": "02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" + "user_pubkey": "", + "user_leaf_hash_hex": "<32-byte raw recovery leaf hash>", + "witness": "" } ``` -**Response Fields**: - -- `cmr_hex`: The 32-byte commitment Merkle root (CMR) of the program, encoded as a 64-character hexadecimal string -- `tweaked_public_key_hex`: The 33-byte compressed tweaked public key, encoded as a 66-character hexadecimal string - -**Error Response**: - -```json -{ - "error": "Failed to parse program: ..." -} -``` +Liquid uses `pset_hex` instead of `psbt_hex`. `/prepare` does not require +`witness`; `/evaluate` and `/sign` require it, with `""` for a witness-free +program. Unknown fields are rejected. + +Every input requires a full `non_witness_utxo` parent and matching +`witness_utxo`. The service verifies parent txids and referenced outputs; it +does not fetch parents. The Bitcoin CLI can populate missing evidence using an +explicit Esplora resolver before sending the request. + +Both profiles accept version 2, one to eight same-contract P2TR inputs and one +to eight outputs, with SIGHASH_DEFAULT script-path signing. Duplicates, +conflicting evidence, invalid accounting and finalized input witnesses are +rejected. Liquid also verifies asset/value disclosures, range and surjection +proofs, and commitment balance. See the [Liquid profile](../policy/LIQUID.md) for +required PSET fields and per-asset rules. + +**Responses** + +- `/prepare`: `{"context": ...}`. +- `/evaluate`: `{"accepted":true,"context":...,"execution_cost_milliweight":...}`. +- `/sign`: `psbt_hex` (or `pset_hex`), `signature_hex`, `public_key_hex`, + `input_index`, `context` and `execution_cost_milliweight`. + +The context includes verified prevouts, output scripts, accounting, the signing +digest and approval hashes. J53 `policy_approval_hash` binds the selected input; +J54 `policy_transaction_approval_hash` can approve all same-contract inputs of +one transaction. Approvers review the context locally and sign decoded 32-byte +digests directly with BIP-340, without another SHA256. + +The service does not finalize or broadcast. Add the user's signature with +`policy user-sign` or `liquid-policy user-sign`, repeat for every input, then +finalize locally. + +### Limits and Errors + +Requests are limited to 2 MiB, two concurrent CPU workers, a five-second response +deadline and 10,000,000 milliweight per evaluation. A timed-out worker retains +its slot until it exits. Busy requests return 503; timeouts return 504. + +Invalid fields, context, proofs or policy execution return non-success HTTP +responses. Operation errors contain `error` and `code`; request-decoding +errors can return framework-generated responses. + +Parent verification establishes content, not confirmation, chain membership, +unspentness or maturity. Bitcoin context reports +`provenance: "parent-txid-verified"`; Liquid provenance also lists the verified +disclosure and transaction proofs. Both report `chain_status_verified: false`. +Check live chain status separately. ## Licence diff --git a/service/config.example.toml b/service/config.example.toml index 89ba48a..1b5d639 100644 --- a/service/config.example.toml +++ b/service/config.example.toml @@ -3,5 +3,4 @@ port = 30431 # Required: provision a unique 32-byte secp256k1 private key as 64 hex characters. # This deliberately empty example cannot start the signer. private_key = "" -elements_network = "liquidtestnet" bitcoin_network = "testnet4" diff --git a/service/src/config.rs b/service/src/config.rs index 1bcdfa9..3bee532 100644 --- a/service/src/config.rs +++ b/service/src/config.rs @@ -18,8 +18,13 @@ pub struct Config { pub struct ServiceConfig { pub port: u16, pub private_key: String, - pub elements_network: String, pub bitcoin_network: String, + #[serde(default = "default_liquid_network")] + pub liquid_network: String, +} + +fn default_liquid_network() -> String { + "liquidtestnet".into() } impl Config { diff --git a/service/src/handlers/liquid_policy.rs b/service/src/handlers/liquid_policy.rs new file mode 100644 index 0000000..e5cd7fd --- /dev/null +++ b/service/src/handlers/liquid_policy.rs @@ -0,0 +1,218 @@ +//! Strict new Liquid profile endpoints. Never accept local unblinding secrets. +use super::SignerState; +use crate::work::{MAX_BODY_BYTES, WorkPermit}; +use axum::{ + Json, + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use base64::{Engine, engine::general_purpose::STANDARD}; +use serde::Deserialize; +use serde_json::{Value, json}; +use simplicity_unchained_policy::liquid::{ + self as policy, + elements::{ + encode::{deserialize, serialize}, + pset::PartiallySignedTransaction as Pset, + secp256k1_zkp::{Keypair, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}, + taproot::TapNodeHash, + }, +}; +use std::str::FromStr; + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TweakRequest { + pub profile_hash: String, + pub network: String, + pub program: String, + pub redeem_script_hex: String, +} +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TransactionRequest { + pub profile_hash: String, + pub network: String, + pub program: String, + pub redeem_script_hex: String, + pub pset_hex: String, + pub input_index: usize, + pub user_pubkey: String, + pub user_leaf_hash_hex: String, + pub witness: Option, +} +type Result = std::result::Result; +fn bounded(value: &str) -> Result<()> { + if value.len() <= MAX_BODY_BYTES { + Ok(()) + } else { + Err("encoded field exceeds request limit".into()) + } +} +fn hex_bytes(value: &str) -> Result> { + bounded(value)?; + hex::decode(value).map_err(|e| e.to_string()) +} +fn base64(value: &str) -> Result> { + bounded(value)?; + STANDARD.decode(value).map_err(|e| e.to_string()) +} +fn key(value: &str) -> Result { + match value.len() { + 64 => XOnlyPublicKey::from_str(value).map_err(|e| e.to_string()), + 66 => PublicKey::from_str(value) + .map(|k| k.x_only_public_key().0) + .map_err(|e| e.to_string()), + _ => Err("expected compressed/x-only public key".into()), + } +} +fn base_key(state: &SignerState) -> XOnlyPublicKey { + Keypair::from_secret_key( + &Secp256k1::new(), + &SecretKey::from_slice(&state.secret_key.secret_bytes()).expect("valid secret"), + ) + .x_only_public_key() + .0 +} +fn binding( + state: &SignerState, + fingerprint: &str, + network: &str, + program: &str, + data: &str, +) -> Result<(Vec, Vec)> { + if fingerprint != hex::encode(policy::profile_hash()) { + return Err("liquid-policy-v1 fingerprint mismatch".into()); + } + if network != state.liquid_network.name() { + return Err("configured Liquid network mismatch".into()); + } + Ok((base64(program)?, hex_bytes(data)?)) +} +fn prepare_request( + state: &SignerState, + r: &TransactionRequest, +) -> Result<(Pset, policy::PreparedPolicy)> { + let (program, data) = binding( + state, + &r.profile_hash, + &r.network, + &r.program, + &r.redeem_script_hex, + )?; + let pset: Pset = deserialize(&hex_bytes(&r.pset_hex)?).map_err(|e| e.to_string())?; + let recovery: [u8; 32] = hex_bytes(&r.user_leaf_hash_hex)? + .try_into() + .map_err(|_| "recovery hash must be 32 bytes")?; + let prepared = policy::PreparedPolicy::new( + &program, + &pset, + r.input_index, + &data, + state.liquid_network, + base_key(state), + key(&r.user_pubkey)?, + TapNodeHash::from_byte_array(recovery), + ) + .map_err(|e| e.to_string())?; + Ok((pset, prepared)) +} +pub(crate) fn execute( + state: &SignerState, + r: &TransactionRequest, + operation: &str, +) -> Result { + let (mut pset, prepared) = prepare_request(state, r)?; + if operation == "prepare" { + return Ok(json!({"context":prepared.summary()})); + } + let witness = base64( + r.witness + .as_ref() + .ok_or("witness is required; use empty string for unit witness")?, + )?; + let cost = prepared + .evaluate_default(&witness) + .map_err(|e| e.to_string())?; + if operation == "evaluate" { + return Ok( + json!({"accepted":true,"context":prepared.summary(),"execution_cost_milliweight":cost}), + ); + } + if operation != "sign" { + return Err("unsupported Liquid policy operation".into()); + } + let secp = Secp256k1::new(); + let pair = Keypair::from_secret_key( + &secp, + &SecretKey::from_slice(&state.secret_key.secret_bytes()).expect("valid secret"), + ); + let tweak = policy::service_tweak( + pair.x_only_public_key().0, + prepared.cmr().to_byte_array(), + prepared.context().script_data(), + state.liquid_network, + ) + .map_err(|e| e.to_string())?; + let pair = pair + .add_xonly_tweak(&secp, &tweak) + .map_err(|e| e.to_string())?; + let sig = policy::add_signature(prepared.context(), &mut pset, pair.secret_key(), true) + .map_err(|e| e.to_string())?; + Ok( + json!({"pset_hex":hex::encode(serialize(&pset)),"signature_hex":hex::encode(sig.sig.as_ref()),"public_key_hex":prepared.context().cooperative().service_pubkey.to_string(),"input_index":r.input_index,"context":prepared.summary(),"execution_cost_milliweight":cost}), + ) +} +async fn work( + permit: WorkPermit, + job: impl FnOnce() -> Result + Send + 'static, +) -> Response { + match permit.run(job).await { + Ok(Ok(v)) => Json(v).into_response(), + Ok(Err(e)) => ( + StatusCode::UNPROCESSABLE_ENTITY, + Json(json!({"error":e,"code":"invalid_liquid_policy"})), + ) + .into_response(), + Err(e) => e.into_response(), + } +} +pub async fn capabilities(State(state): State, permit: WorkPermit) -> Response { + work(permit,move || Ok(json!({"profile":policy::PROFILE_NAME,"profile_hash":hex::encode(policy::profile_hash()),"network":state.liquid_network.name(),"genesis_hash":hex::encode(state.liquid_network.genesis_hash().to_byte_array()),"base_public_key_hex":base_key(&state).to_string(),"manifest":policy::manifest(),"limits":{"max_body_bytes":MAX_BODY_BYTES,"max_blocking_jobs":crate::work::MAX_BLOCKING_JOBS},"provenance":"verified PSET exact asset/value disclosure proofs; parent content, NOT chain state"}))).await +} +pub async fn tweak( + State(state): State, + permit: WorkPermit, + Json(r): Json, +) -> Response { + work(permit,move || { + let (program,data) = binding(&state,&r.profile_hash,&r.network,&r.program,&r.redeem_script_hex)?; + let cmr = policy::policy_cmr(&program).map_err(|e| e.to_string())?.to_byte_array(); + let base = base_key(&state); + Ok(json!({"profile":policy::PROFILE_NAME,"profile_hash":hex::encode(policy::profile_hash()),"network":state.liquid_network.name(),"genesis_hash":hex::encode(state.liquid_network.genesis_hash().to_byte_array()),"cmr":hex::encode(cmr),"signing_commitment":hex::encode(policy::signing_commitment(cmr,&data,state.liquid_network).map_err(|e| e.to_string())?),"public_key_hex":policy::tweak_public_key(base,cmr,&data,state.liquid_network).map_err(|e| e.to_string())?.to_string(),"base_public_key_hex":base.to_string()})) + }).await +} +pub async fn prepare( + State(state): State, + permit: WorkPermit, + Json(r): Json, +) -> Response { + work(permit, move || execute(&state, &r, "prepare")).await +} +pub async fn evaluate( + State(state): State, + permit: WorkPermit, + Json(r): Json, +) -> Response { + work(permit, move || execute(&state, &r, "evaluate")).await +} +pub async fn sign( + State(state): State, + permit: WorkPermit, + Json(r): Json, +) -> Response { + work(permit, move || execute(&state, &r, "sign")).await +} +#[cfg(test)] +mod tests; diff --git a/service/src/handlers/liquid_policy/tests.rs b/service/src/handlers/liquid_policy/tests.rs new file mode 100644 index 0000000..55eb211 --- /dev/null +++ b/service/src/handlers/liquid_policy/tests.rs @@ -0,0 +1,157 @@ +#[allow(dead_code)] +mod common { + include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../policy/tests/common/liquid.rs" + )); +} +use super::*; +use axum::{ + body::Body, + http::{Request, StatusCode}, +}; +use common::*; +use policy::elements::{ + encode::{deserialize, serialize}, + pset::PartiallySignedTransaction, +}; +use tower::ServiceExt; + +fn state(f: &Fixture) -> SignerState { + SignerState::new( + &hex::encode(f.base_secret.secret_bytes()), + simplicity_unchained_core::BitcoinNetwork::Testnet4, + ) + .unwrap() +} +fn transaction(f: &Fixture, index: usize) -> Value { + json!({"profile_hash":hex::encode(policy::profile_hash()),"network":f.network.name(),"program":STANDARD.encode(&f.program),"redeem_script_hex":hex::encode(&f.data),"pset_hex":hex::encode(serialize(&f.pset)),"input_index":index,"user_pubkey":f.user.to_string(),"user_leaf_hash_hex":hex::encode(f.recovery.to_byte_array()),"witness":""}) +} +async fn request(state: &SignerState, method: &str, op: &str, body: Value) -> (StatusCode, Value) { + let response = super::super::routes(state.clone()) + .oneshot( + Request::builder() + .method(method) + .uri(format!("/simplicity-unchained/policy/liquid/{op}")) + .header("content-type", "application/json") + .body(Body::from(body.to_string())) + .unwrap(), + ) + .await + .unwrap(); + let status = response.status(); + let bytes = axum::body::to_bytes(response.into_body(), MAX_BODY_BYTES) + .await + .unwrap(); + (status, serde_json::from_slice(&bytes).unwrap()) +} +#[tokio::test] +async fn confidential_multi_asset_http_prepare_evaluate_sign_user_finalize_roundtrip() { + let mut f = Fixture::new(); + let s = state(&f); + let (status, caps) = request(&s, "GET", "capabilities", Value::Null).await; + assert_eq!(status, StatusCode::OK); + assert_eq!(caps["network"], "liquidtestnet"); + assert_eq!(caps["manifest"], policy::manifest()); + let binding = json!({"profile_hash":hex::encode(policy::profile_hash()),"network":"liquidtestnet","program":STANDARD.encode(&f.program),"redeem_script_hex":hex::encode(&f.data)}); + let (status, tweak) = request(&s, "POST", "tweak", binding).await; + assert_eq!(status, StatusCode::OK); + assert_eq!( + tweak["public_key_hex"], + f.context(0) + .unwrap() + .cooperative() + .service_pubkey + .to_string() + ); + for index in 0..2 { + let local = f.context(index).unwrap(); + let expected = serde_json::to_value(local.summary()).unwrap(); + let (status, prepared) = request(&s, "POST", "prepare", transaction(&f, index)).await; + assert_eq!(status, StatusCode::OK); + assert_eq!(prepared["context"], expected); + let (status, evaluated) = request(&s, "POST", "evaluate", transaction(&f, index)).await; + assert_eq!(status, StatusCode::OK); + assert_eq!(evaluated["context"], expected); + assert_eq!(evaluated["accepted"], true); + let (status, signed) = request(&s, "POST", "sign", transaction(&f, index)).await; + assert_eq!(status, StatusCode::OK); + assert_eq!(signed["context"], expected); + let mut pset: PartiallySignedTransaction = + deserialize(&hex::decode(signed["pset_hex"].as_str().unwrap()).unwrap()).unwrap(); + local.revalidate(&pset).unwrap(); + let actual = pset.inputs()[index] + .tap_script_sigs + .get(&( + local.cooperative().service_pubkey, + local.cooperative().leaf_hash, + )) + .unwrap(); + assert_eq!(signed["signature_hex"], hex::encode(actual.sig.as_ref())); + policy::add_signature(&local, &mut pset, f.user_secret, false).unwrap(); + f.pset = pset; + } + let context = f.context(0).unwrap(); + let tx = policy::finalize(&context, &f.pset).unwrap(); + assert!(tx.input.iter().all(|i| i.witness.script_witness.len() == 4)); + tx.verify_tx_amt_proofs(&Secp256k1::new(), context.prevouts()) + .unwrap(); +} +#[tokio::test] +async fn http_sign_rejects_tampered_labels_missing_proofs_evidence_and_replays() { + let f = Fixture::new(); + let s = state(&f); + for mutation in 0..9 { + let mut pset = f.pset.clone(); + match mutation { + 0 => pset.inputs_mut()[0].amount = Some(99_999), + 1 => pset.inputs_mut()[0].asset = Some(f.other_asset), + 2 => pset.inputs_mut()[0].blind_value_proof = None, + 3 => pset.outputs_mut()[0].blind_asset_proof = None, + 4 => pset.inputs_mut()[0].non_witness_utxo = None, + 5 => pset.outputs_mut()[0].amount = Some(20_001), + 6 => pset.outputs_mut()[0].value_rangeproof = None, + 7 => pset.outputs_mut()[0].asset_surjection_proof = None, + _ => { + pset.inputs_mut()[0].blind_asset_proof = + f.pset.inputs()[1].blind_asset_proof.clone() + } + } + let mut body = transaction(&f, 0); + body["pset_hex"] = hex::encode(serialize(&pset)).into(); + let (status, body) = request(&s, "POST", "sign", body).await; + assert_eq!( + status, + StatusCode::UNPROCESSABLE_ENTITY, + "mutation {mutation}: {body}" + ); + assert!(body.get("signature_hex").is_none()); + } + for (field, bad) in [ + ("network", json!("liquid")), + ( + "profile_hash", + json!(hex::encode(simplicity_unchained_policy::profile_hash())), + ), + ("witness", Value::Null), + ] { + let mut body = transaction(&f, 0); + body[field] = bad; + assert_eq!( + request(&s, "POST", "sign", body).await.0, + StatusCode::UNPROCESSABLE_ENTITY + ); + } + let mut body = transaction(&f, 0); + body["blinding_secret_key_hex"] = json!("forbidden"); + let response = super::super::routes(s) + .oneshot( + Request::post("/simplicity-unchained/policy/liquid/sign") + .header("content-type", "application/json") + .body(Body::from(body.to_string())) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); +} diff --git a/service/src/handlers/mod.rs b/service/src/handlers/mod.rs index 2f4651a..9b0e4e7 100644 --- a/service/src/handlers/mod.rs +++ b/service/src/handlers/mod.rs @@ -1,6 +1,5 @@ -pub mod sign_psbt; -pub mod sign_pset; -pub mod tweak; +pub mod liquid_policy; +pub mod policy; use std::sync::Arc; @@ -11,16 +10,13 @@ use axum::{ response::IntoResponse, routing::{get, post}, }; - use serde::Serialize; - use serde_json::json; - use simplicity::elements::secp256k1_zkp::{All, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; +use simplicity_unchained_core::BitcoinNetwork; +use tokio::sync::Semaphore; use crate::work::{MAX_BLOCKING_JOBS, MAX_BODY_BYTES}; -use simplicity_unchained_core::{BitcoinNetwork, ElementsNetwork}; -use tokio::sync::Semaphore; // Public identity of the disclosed demo key; also reject its negation (same Taproot identity). const DISCLOSED_DEMO_KEY: &str = "3523982d58e94be3b735731593f8225043880d53727235b566c515d24a0f7baf"; @@ -36,23 +32,52 @@ fn validate_signing_identity(public_key: XOnlyPublicKey) -> Result<(), String> { } async fn version() -> impl IntoResponse { - Json(json!({ - "version": env!("CARGO_PKG_VERSION"), - })) + Json(json!({ "version": env!("CARGO_PKG_VERSION") })) } pub fn routes(signer_state: SignerState) -> Router { Router::new() + .route( + "/simplicity-unchained/policy/liquid/capabilities", + get(liquid_policy::capabilities), + ) + .route( + "/simplicity-unchained/policy/liquid/tweak", + post(liquid_policy::tweak), + ) + .route( + "/simplicity-unchained/policy/liquid/prepare", + post(liquid_policy::prepare), + ) + .route( + "/simplicity-unchained/policy/liquid/evaluate", + post(liquid_policy::evaluate), + ) + .route( + "/simplicity-unchained/policy/liquid/sign", + post(liquid_policy::sign), + ) .route("/simplicity-unchained/version", get(version)) .route( - "/simplicity-unchained/sign/psbt", - post(sign_psbt::sign_psbt), + "/simplicity-unchained/policy/bitcoin/capabilities", + get(policy::capabilities), ) .route( - "/simplicity-unchained/sign/pset", - post(sign_pset::sign_pset), + "/simplicity-unchained/policy/bitcoin/tweak", + post(policy::tweak), + ) + .route( + "/simplicity-unchained/policy/bitcoin/prepare", + post(policy::prepare), + ) + .route( + "/simplicity-unchained/policy/bitcoin/evaluate", + post(policy::evaluate), + ) + .route( + "/simplicity-unchained/policy/bitcoin/sign", + post(policy::sign), ) - .route("/simplicity-unchained/tweak", post(tweak::get_tweaked_key)) .route_layer(middleware::from_fn(crate::work::request_deadline)) .layer(DefaultBodyLimit::max(MAX_BODY_BYTES)) .with_state(signer_state) @@ -62,35 +87,28 @@ pub fn routes(signer_state: SignerState) -> Router { pub struct SignerState { pub secret_key: SecretKey, pub secp: Arc>, - pub elements_network: ElementsNetwork, pub bitcoin_network: BitcoinNetwork, + pub liquid_network: simplicity_unchained_policy::liquid::Network, pub work_slots: Arc, } impl SignerState { - pub fn new( - secret_key_hex: &str, - elements_network: ElementsNetwork, - bitcoin_network: BitcoinNetwork, - ) -> Result { + pub fn new(secret_key_hex: &str, bitcoin_network: BitcoinNetwork) -> Result { let secret_key_bytes = hex::decode(secret_key_hex).map_err(|e| format!("Invalid private key hex: {}", e))?; - let secret_key = SecretKey::from_slice(&secret_key_bytes) .map_err(|e| format!("Invalid private key: {}", e))?; - let secp = Secp256k1::new(); validate_signing_identity( PublicKey::from_secret_key(&secp, &secret_key) .x_only_public_key() .0, )?; - Ok(Self { secret_key, secp: Arc::new(secp), - elements_network, bitcoin_network, + liquid_network: Default::default(), work_slots: Arc::new(Semaphore::new(MAX_BLOCKING_JOBS)), }) } @@ -102,271 +120,4 @@ pub struct ErrorResponse { } #[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - use axum::{ - body::Body, - http::{Request, StatusCode}, - response::Response, - }; - use base64::{Engine as _, engine::general_purpose::STANDARD}; - use simplicity::{ - ConstructNode, Word, - bitcoin::PublicKey, - elements::secp256k1_zkp::{Keypair, Message}, - jet::Jet, - node::{CoreConstructible, WitnessConstructible}, - types, - }; - use tower::ServiceExt; - - fn test_state() -> SignerState { - SignerState::new( - &hex::encode([0xcd; 32]), - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ) - .unwrap() - } - - #[test] - fn disclosed_demo_identity_is_rejected_for_both_parities() { - for prefix in ["02", "03"] { - let public_key = simplicity::elements::secp256k1_zkp::PublicKey::from_str(&format!( - "{}{}", - prefix, DISCLOSED_DEMO_KEY - )) - .unwrap(); - let result = validate_signing_identity(public_key.x_only_public_key().0); - assert!( - result - .unwrap_err() - .contains("publicly disclosed demo signing key") - ); - } - assert!( - SignerState::new("", ElementsNetwork::LiquidTestnet, BitcoinNetwork::Testnet,).is_err() - ); - assert!( - SignerState::new( - &hex::encode([0; 32]), - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ) - .is_err() - ); - } - - pub(super) async fn post_json( - state: &SignerState, - path: &str, - body: serde_json::Value, - ) -> Response { - routes(state.clone()) - .oneshot( - Request::post(path) - .header("content-type", "application/json") - .body(Body::from(body.to_string())) - .unwrap(), - ) - .await - .unwrap() - } - - pub(super) fn expensive_program() -> String { - let bytes = types::Context::with_context(|ctx| { - let mut node = Arc::::unit(&ctx); - for _ in 0..20 { - node = Arc::::comp(&node, &node).unwrap(); - } - node.finalize_types().unwrap().to_vec_without_witness() - }); - assert!(bytes.len() < 256); - STANDARD.encode(bytes) - } - - pub(super) fn script_key_program( - get_pubkey: J, - verify: J, - secret_key: &SecretKey, - ) -> (String, String) { - let program = types::Context::with_context(|ctx| { - let index = Arc::::const_word(&ctx, Word::u8(1)); - let key_jet = Arc::::jet(&ctx, &get_pubkey); - let key = Arc::::comp(&index, &key_jet).unwrap(); - let msg = Arc::::const_word(&ctx, Word::u256([0x42; 32])); - let key_msg = Arc::::pair(&key, &msg).unwrap(); - let signature = Arc::::witness(&ctx, None); - let args = Arc::::pair(&key_msg, &signature).unwrap(); - let verify_jet = Arc::::jet(&ctx, &verify); - Arc::::comp(&args, &verify_jet) - .unwrap() - .finalize_types() - .unwrap() - .to_vec_without_witness() - }); - let secp = Secp256k1::new(); - let signature = secp.sign_schnorr( - &Message::from_digest([0x42; 32]), - &Keypair::from_secret_key(&secp, secret_key), - ); - ( - STANDARD.encode(program), - STANDARD.encode(signature.as_ref()), - ) - } - - pub(super) fn key_script(state: &SignerState, secret_key: &SecretKey) -> String { - let key = - simplicity::bitcoin::secp256k1::PublicKey::from_secret_key(&*state.secp, secret_key); - format!("5221{}", hex::encode(key.serialize())) - } - - pub(super) async fn tweaked_public_key( - state: &SignerState, - program: &str, - script: Option<&str>, - jet_env: tweak::JetEnvKind, - ) -> PublicKey { - let response = post_json( - state, - "/simplicity-unchained/tweak", - json!({ - "program": program, - "jet_env": match jet_env { - tweak::JetEnvKind::Bitcoin => "bitcoin", - tweak::JetEnvKind::Elements => "elements", - }, - "redeem_script_hex": script, - }), - ) - .await; - assert_eq!(response.status(), axum::http::StatusCode::OK); - let body = axum::body::to_bytes(response.into_body(), usize::MAX) - .await - .unwrap(); - let result: serde_json::Value = serde_json::from_slice(&body).unwrap(); - PublicKey::from_str(result["tweaked_public_key_hex"].as_str().unwrap()).unwrap() - } - - #[tokio::test] - async fn cpu_admission_is_shared_and_precedes_json_extraction() { - let state = test_state(); - let slots = [ - Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), - Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), - ]; - let app = routes(state.clone()); - for path in [ - "/simplicity-unchained/sign/psbt", - "/simplicity-unchained/sign/pset", - "/simplicity-unchained/tweak", - ] { - let response = app - .clone() - .oneshot(Request::post(path).body(Body::empty()).unwrap()) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE); - } - let response = app - .oneshot( - Request::get("/simplicity-unchained/version") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::OK); - drop(slots); - let response = post_json( - &state, - "/simplicity-unchained/tweak", - json!({ - "program": "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA", - }), - ) - .await; - assert_eq!(response.status(), StatusCode::OK); - assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); - } - - #[tokio::test] - async fn request_body_limit_has_exact_boundary() { - let state = test_state(); - let mut body = json!({ - "program": "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA", - }) - .to_string(); - body.extend(std::iter::repeat_n(' ', MAX_BODY_BYTES - body.len())); - for (extra, status) in [(0, StatusCode::OK), (1, StatusCode::PAYLOAD_TOO_LARGE)] { - let response = routes(state.clone()) - .oneshot( - Request::post("/simplicity-unchained/tweak") - .header("content-type", "application/json") - .body(Body::from(format!("{}{}", body, " ".repeat(extra)))) - .unwrap(), - ) - .await - .unwrap(); - assert_eq!(response.status(), status); - assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); - } - } - - #[tokio::test] - async fn malformed_requests_release_admission_slots() { - let state = test_state(); - for path in [ - "/simplicity-unchained/sign/psbt", - "/simplicity-unchained/sign/pset", - "/simplicity-unchained/tweak", - ] { - let response = post_json(&state, path, json!({})).await; - assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); - assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); - } - } - - #[tokio::test] - async fn stalled_body_hits_request_deadline_and_releases_slot() { - use axum::body::{Bytes, HttpBody}; - use std::{ - convert::Infallible, - pin::Pin, - task::{Context, Poll}, - time::Duration, - }; - - struct StalledBody; - impl HttpBody for StalledBody { - type Data = Bytes; - type Error = Infallible; - - fn poll_frame( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>>> { - Poll::Pending - } - } - - let state = test_state(); - let response = tokio::time::timeout( - Duration::from_secs(6), - routes(state.clone()).oneshot( - Request::post("/simplicity-unchained/tweak") - .header("content-type", "application/json") - .body(Body::new(StalledBody)) - .unwrap(), - ), - ) - .await - .unwrap() - .unwrap(); - assert_eq!(response.status(), StatusCode::GATEWAY_TIMEOUT); - assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); - } -} +mod tests; diff --git a/service/src/handlers/policy.rs b/service/src/handlers/policy.rs new file mode 100644 index 0000000..cf2b4a7 --- /dev/null +++ b/service/src/handlers/policy.rs @@ -0,0 +1,505 @@ +use std::str::FromStr; + +use axum::{ + Json, + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use serde::{Deserialize, Serialize}; +use simplicity::bitcoin::{ + self, + hashes::Hash, + key::{TapTweak, UntweakedKeypair}, + psbt::Psbt, + secp256k1::{Message, XOnlyPublicKey}, + taproot::{LeafVersion, TapNodeHash}, +}; +use simplicity_unchained_core::policy::{PolicyError, PreparedPolicy, policy_cmr, prepare_policy}; +use simplicity_unchained_policy::{self as policy, ContextSummary, Limits, Manifest, PROFILE_NAME}; + +use super::SignerState; +use crate::work::{MAX_BLOCKING_JOBS, MAX_BODY_BYTES, WorkPermit}; + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TweakRequest { + pub profile_hash: String, + pub network: String, + pub program: String, + pub redeem_script_hex: String, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TransactionRequest { + pub profile_hash: String, + pub network: String, + pub program: String, + pub redeem_script_hex: String, + pub psbt_hex: String, + pub input_index: usize, + pub user_pubkey: String, + pub user_leaf_hash_hex: String, + pub witness: Option, +} + +#[derive(Debug, Serialize)] +pub struct TweakResponse { + pub profile: &'static str, + pub profile_hash: String, + pub network: &'static str, + pub genesis_hash: String, + pub cmr: String, + pub signing_commitment: String, + pub public_key_hex: String, + pub base_public_key_hex: String, +} + +#[derive(Debug, Serialize)] +struct CapabilitiesResponse { + profile: &'static str, + profile_hash: String, + network: &'static str, + genesis_hash: String, + base_public_key_hex: String, + manifest: Manifest, + limits: ServiceLimits, + provenance: &'static str, +} + +#[derive(Debug, Serialize)] +struct ServiceLimits { + #[serde(flatten)] + profile: Limits, + max_body_bytes: usize, + max_blocking_jobs: usize, +} + +#[derive(Debug, Serialize)] +struct PrepareResponse { + context: ContextSummary, +} + +#[derive(Debug, Serialize)] +struct EvaluateResponse { + accepted: bool, + context: ContextSummary, + execution_cost_milliweight: u32, +} + +#[derive(Debug, Serialize)] +struct SignResponse { + psbt_hex: String, + signature_hex: String, + public_key_hex: String, + input_index: usize, + context: ContextSummary, + execution_cost_milliweight: u32, +} + +#[derive(Debug)] +struct ApiError { + status: StatusCode, + code: &'static str, + message: String, +} + +impl ApiError { + fn invalid(field: &'static str, error: impl std::fmt::Display) -> Self { + Self { + status: StatusCode::BAD_REQUEST, + code: "invalid_request", + message: format!("{field}: {error}"), + } + } + + fn internal(error: impl std::fmt::Display) -> Self { + log::error!("Policy operation failed: {error}"); + Self { + status: StatusCode::INTERNAL_SERVER_ERROR, + code: "internal_error", + message: "Policy operation failed".to_owned(), + } + } + + fn context(error: policy::Error) -> Self { + let limited = matches!( + error, + policy::Error::EvidenceTooLarge + | policy::Error::ScriptDataTooLarge + | policy::Error::OutputScriptTooLarge(_) + ); + Self { + status: if limited { + StatusCode::PAYLOAD_TOO_LARGE + } else { + StatusCode::UNPROCESSABLE_ENTITY + }, + code: if limited { + "profile_limit" + } else { + "invalid_context" + }, + message: error.to_string(), + } + } + + fn execution(error: PolicyError) -> Self { + let (status, code) = match &error { + PolicyError::Decode(_) => (StatusCode::BAD_REQUEST, "invalid_program_or_witness"), + PolicyError::Context(_) => (StatusCode::UNPROCESSABLE_ENTITY, "invalid_context"), + PolicyError::CommitmentMismatch => (StatusCode::BAD_REQUEST, "commitment_mismatch"), + PolicyError::PolicyRejected(_) => (StatusCode::UNPROCESSABLE_ENTITY, "policy_rejected"), + PolicyError::ExecutionCostExceeded { .. } | PolicyError::MemoryLimit(_) => { + (StatusCode::UNPROCESSABLE_ENTITY, "execution_limit") + } + PolicyError::Vm(_) => return Self::internal(error), + }; + if let PolicyError::Context(error) = error { + return Self::context(error); + } + Self { + status, + code, + message: error.to_string(), + } + } +} + +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + #[derive(Serialize)] + struct ErrorBody { + error: String, + code: &'static str, + } + log::warn!("[{}] Policy {}: {}", self.status, self.code, self.message); + ( + self.status, + Json(ErrorBody { + error: self.message, + code: self.code, + }), + ) + .into_response() + } +} + +fn configured_network(state: &SignerState) -> (bitcoin::Network, &'static str) { + match state.bitcoin_network { + simplicity_unchained_core::BitcoinNetwork::Mainnet => { + (bitcoin::Network::Bitcoin, "bitcoin") + } + simplicity_unchained_core::BitcoinNetwork::Testnet => { + (bitcoin::Network::Testnet, "testnet") + } + simplicity_unchained_core::BitcoinNetwork::Testnet4 => { + (bitcoin::Network::Testnet4, "testnet4") + } + } +} + +fn decode_hex(field: &'static str, value: &str) -> Result, ApiError> { + if value.len() > MAX_BODY_BYTES { + return Err(ApiError::invalid( + field, + "encoded data exceeds request limit", + )); + } + hex::decode(value).map_err(|error| ApiError::invalid(field, error)) +} + +fn decode_base64(field: &'static str, value: &str) -> Result, ApiError> { + if value.len() > MAX_BODY_BYTES { + return Err(ApiError::invalid( + field, + "encoded data exceeds request limit", + )); + } + STANDARD + .decode(value) + .map_err(|error| ApiError::invalid(field, error)) +} + +fn decode_user_key(value: &str) -> Result { + if value.len() == 64 { + XOnlyPublicKey::from_str(value).map_err(|error| ApiError::invalid("user_pubkey", error)) + } else { + bitcoin::secp256k1::PublicKey::from_str(value) + .map(|key| key.x_only_public_key().0) + .map_err(|error| ApiError::invalid("user_pubkey", error)) + } +} + +fn decode_recovery_hash(value: &str) -> Result { + let bytes = decode_hex("user_leaf_hash_hex", value)?; + bitcoin::TapNodeHash::from_slice(&bytes) + .map_err(|error| ApiError::invalid("user_leaf_hash_hex", error)) +} + +struct Binding { + program: Vec, + script: Vec, + network: bitcoin::Network, +} + +fn decode_binding( + state: &SignerState, + profile_hash: &str, + network: &str, + program: &str, + script: &str, +) -> Result { + if decode_hex("profile_hash", profile_hash)? != policy::profile_hash() { + return Err(ApiError::invalid( + "profile_hash", + "unsupported profile fingerprint", + )); + } + let (configured, name) = configured_network(state); + if network != name { + return Err(ApiError::invalid( + "network", + format!("service is configured for {name}"), + )); + } + let program = decode_base64("program", program)?; + if program.is_empty() { + return Err(ApiError::invalid("program", "empty program")); + } + let script = decode_hex("redeem_script_hex", script)?; + if script.len() > policy::MAX_SCRIPT_BYTES { + return Err(ApiError::context(policy::Error::ScriptDataTooLarge)); + } + Ok(Binding { + program, + script, + network: configured, + }) +} + +fn base_public_key(state: &SignerState) -> XOnlyPublicKey { + UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key) + .x_only_public_key() + .0 +} + +fn prepare_request( + state: &SignerState, + request: &TransactionRequest, +) -> Result<(Psbt, PreparedPolicy), ApiError> { + let binding = decode_binding( + state, + &request.profile_hash, + &request.network, + &request.program, + &request.redeem_script_hex, + )?; + let bytes = decode_hex("psbt_hex", &request.psbt_hex)?; + let psbt = Psbt::deserialize(&bytes).map_err(|error| ApiError::invalid("psbt_hex", error))?; + let prepared = prepare_policy( + &binding.program, + &psbt, + request.input_index, + &binding.script, + binding.network, + base_public_key(state), + decode_user_key(&request.user_pubkey)?, + decode_recovery_hash(&request.user_leaf_hash_hex)?, + ) + .map_err(ApiError::execution)?; + Ok((psbt, prepared)) +} + +fn witness(request: &TransactionRequest) -> Result, ApiError> { + let value = request.witness.as_deref().ok_or_else(|| { + ApiError::invalid("witness", "required; use an empty string for zero bits") + })?; + decode_base64("witness", value) +} + +fn capabilities_internal(state: &SignerState) -> Result { + let (network, name) = configured_network(state); + let manifest = policy::manifest(); + Ok(CapabilitiesResponse { + profile: PROFILE_NAME, + profile_hash: hex::encode(policy::profile_hash()), + network: name, + genesis_hash: hex::encode(policy::genesis_hash(network).map_err(ApiError::context)?), + base_public_key_hex: base_public_key(state).to_string(), + limits: ServiceLimits { + profile: manifest.limits.clone(), + max_body_bytes: MAX_BODY_BYTES, + max_blocking_jobs: MAX_BLOCKING_JOBS, + }, + manifest, + provenance: "parent-txid-verified", + }) +} + +fn tweak_internal(state: &SignerState, request: TweakRequest) -> Result { + let binding = decode_binding( + state, + &request.profile_hash, + &request.network, + &request.program, + &request.redeem_script_hex, + )?; + let cmr = policy_cmr(&binding.program).map_err(ApiError::execution)?; + let key = base_public_key(state); + Ok(TweakResponse { + profile: PROFILE_NAME, + profile_hash: hex::encode(policy::profile_hash()), + network: configured_network(state).1, + genesis_hash: hex::encode( + policy::genesis_hash(binding.network).map_err(ApiError::context)?, + ), + cmr: hex::encode(cmr.to_byte_array()), + signing_commitment: hex::encode( + policy::signing_commitment(cmr.to_byte_array(), &binding.script, binding.network) + .map_err(ApiError::context)?, + ), + public_key_hex: policy::tweak_public_key( + key, + cmr.to_byte_array(), + &binding.script, + binding.network, + ) + .map_err(ApiError::context)? + .to_string(), + base_public_key_hex: key.to_string(), + }) +} + +fn prepare_internal( + state: &SignerState, + request: TransactionRequest, +) -> Result { + let (_, prepared) = prepare_request(state, &request)?; + Ok(PrepareResponse { + context: prepared.summary(), + }) +} + +fn evaluate_internal( + state: &SignerState, + request: TransactionRequest, +) -> Result { + let (_, prepared) = prepare_request(state, &request)?; + let evaluation = prepared + .evaluate_default(&witness(&request)?) + .map_err(ApiError::execution)?; + Ok(EvaluateResponse { + accepted: true, + context: evaluation.context, + execution_cost_milliweight: evaluation.cost_milliweight, + }) +} + +fn sign_internal( + state: &SignerState, + request: TransactionRequest, +) -> Result { + let (mut psbt, prepared) = prepare_request(state, &request)?; + let evaluation = prepared + .evaluate_default(&witness(&request)?) + .map_err(ApiError::execution)?; + let context = prepared.context(); + let keypair = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key).tap_tweak( + &*state.secp, + Some(TapNodeHash::from_byte_array(context.signing_commitment())), + ); + let (public_key, _) = keypair.public_parts(); + let public_key = public_key.to_x_only_public_key(); + if public_key != context.service_pubkey() { + return Err(ApiError::internal("policy public/private tweak mismatch")); + } + let message = Message::from_digest(context.sighash()); + let signature = state.secp.sign_schnorr(&message, &keypair.to_keypair()); + state + .secp + .verify_schnorr(&signature, &message, &public_key) + .map_err(ApiError::internal)?; + let input = &mut psbt.inputs[request.input_index]; + input.tap_script_sigs.insert( + (public_key, context.cooperative_leaf_hash()), + bitcoin::taproot::Signature { + signature, + sighash_type: bitcoin::TapSighashType::Default, + }, + ); + input.tap_scripts.insert( + context.control_block().clone(), + (context.cooperative_script().clone(), LeafVersion::TapScript), + ); + Ok(SignResponse { + psbt_hex: hex::encode(psbt.serialize()), + signature_hex: hex::encode(signature.as_ref()), + public_key_hex: public_key.to_string(), + input_index: request.input_index, + context: evaluation.context, + execution_cost_milliweight: evaluation.cost_milliweight, + }) +} + +async fn work_response( + permit: WorkPermit, + operation: &'static str, + job: impl FnOnce() -> Result + Send + 'static, +) -> Response { + match permit.run(job).await { + Ok(Ok(response)) => { + log::info!("[200] Policy {operation} successful"); + Json(response).into_response() + } + Ok(Err(error)) => error.into_response(), + Err(error) => error.into_response(), + } +} + +pub async fn capabilities(State(state): State, permit: WorkPermit) -> Response { + work_response(permit, "capabilities", move || { + capabilities_internal(&state) + }) + .await +} + +pub async fn tweak( + State(state): State, + permit: WorkPermit, + Json(request): Json, +) -> Response { + work_response(permit, "tweak", move || tweak_internal(&state, request)).await +} + +pub async fn prepare( + State(state): State, + permit: WorkPermit, + Json(request): Json, +) -> Response { + work_response(permit, "prepare", move || prepare_internal(&state, request)).await +} + +pub async fn evaluate( + State(state): State, + permit: WorkPermit, + Json(request): Json, +) -> Response { + work_response(permit, "evaluate", move || { + evaluate_internal(&state, request) + }) + .await +} + +pub async fn sign( + State(state): State, + permit: WorkPermit, + Json(request): Json, +) -> Response { + work_response(permit, "sign", move || sign_internal(&state, request)).await +} + +#[cfg(test)] +mod tests; diff --git a/service/src/handlers/policy/tests.rs b/service/src/handlers/policy/tests.rs new file mode 100644 index 0000000..07ccb3c --- /dev/null +++ b/service/src/handlers/policy/tests.rs @@ -0,0 +1,435 @@ +use std::sync::Arc; + +use axum::{ + body::Body, + http::{Request, StatusCode}, + response::Response, +}; +use serde_json::{Value, json}; +use simplicity::{ + ConstructNode, Word, + bitcoin::{ + Amount, OutPoint, Sequence, TapNodeHash, Transaction, TxIn, TxOut, + absolute::LockTime, + key::UntweakedKeypair, + psbt::Psbt, + script::ScriptBuf, + secp256k1::{Message, Secp256k1, SecretKey}, + taproot::{LeafVersion, TaprootBuilder}, + transaction::Version, + }, + node::{CoreConstructible, WitnessConstructible}, + types, +}; +use simplicity_unchained_core::{ + BitcoinNetwork, + jets::bitcoin::CoreExtension, + jets::policy::PolicyJet, + utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc}, +}; +use tower::ServiceExt; + +use super::*; +use crate::handlers::routes; + +const PREFIX: &str = "/simplicity-unchained/policy/bitcoin"; + +fn state() -> SignerState { + SignerState::new(&hex::encode([0xcd; 32]), BitcoinNetwork::Testnet).unwrap() +} + +fn unit_program() -> String { + let bytes = types::Context::with_context(|ctx| { + Arc::::unit(&ctx) + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + STANDARD.encode(bytes) +} + +fn approval_program() -> (String, UntweakedKeypair) { + let secp = Secp256k1::new(); + let key = + UntweakedKeypair::from_secret_key(&secp, &SecretKey::from_slice(&[0x44; 32]).unwrap()); + let bytes = types::Context::with_context(|ctx| { + let public_key = Arc::::const_word( + &ctx, + Word::u256(key.x_only_public_key().0.serialize()), + ); + let hash = Arc::::jet( + &ctx, + &PolicyJet::Builtin(policy::Builtin::PolicyTransactionApprovalHash), + ); + let key_msg = Arc::::pair(&public_key, &hash).unwrap(); + let signature = Arc::::witness(&ctx, None); + let args = Arc::::pair(&key_msg, &signature).unwrap(); + let verify = Arc::::jet( + &ctx, + &PolicyJet::Legacy(CoreExtension::Core(simplicity::jet::Core::Bip0340Verify)), + ); + Arc::::comp(&args, &verify) + .unwrap() + .finalize_types() + .unwrap() + .to_vec_without_witness() + }); + (STANDARD.encode(bytes), key) +} + +async fn request(state: &SignerState, method: &str, operation: &str, body: Value) -> Response { + routes(state.clone()) + .oneshot( + Request::builder() + .method(method) + .uri(format!("{PREFIX}/{operation}")) + .header("content-type", "application/json") + .body(Body::from(body.to_string())) + .unwrap(), + ) + .await + .unwrap() +} + +async fn body(response: Response) -> Value { + let bytes = axum::body::to_bytes(response.into_body(), MAX_BODY_BYTES) + .await + .unwrap(); + serde_json::from_slice(&bytes).unwrap() +} + +async fn capabilities_body(state: &SignerState) -> Value { + let response = request(state, "GET", "capabilities", Value::Null).await; + assert_eq!(response.status(), StatusCode::OK); + body(response).await +} + +async fn fixture(state: &SignerState, program: &str) -> (Psbt, Value) { + let capabilities = capabilities_body(state).await; + let binding = json!({ + "profile_hash": capabilities["profile_hash"], + "network": "testnet", + "program": program, + "redeem_script_hex": "", + }); + let response = request(state, "POST", "tweak", binding.clone()).await; + assert_eq!(response.status(), StatusCode::OK); + let tweak = body(response).await; + let service_key = XOnlyPublicKey::from_str(tweak["public_key_hex"].as_str().unwrap()).unwrap(); + let secp = Secp256k1::new(); + let user = + UntweakedKeypair::from_secret_key(&secp, &SecretKey::from_slice(&[0x33; 32]).unwrap()); + let user_key = user.public_key(); + let recovery_hash = TapNodeHash::from_byte_array([0x42; 32]); + let leaf = p2tr_multisig_leaf_btc( + &service_key + .public_key(bitcoin::secp256k1::Parity::Even) + .into(), + &user_key.into(), + ); + let spend = TaprootBuilder::new() + .add_leaf(1, leaf) + .unwrap() + .add_hidden_node(1, recovery_hash) + .unwrap() + .finalize(&secp, *UNSPENDABLE_KEY_P2TR) + .unwrap(); + // This parent is content evidence only, deliberately not a mined transaction. + let parent = Transaction { + version: Version::TWO, + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::null(), + ..TxIn::default() + }], + output: vec![TxOut { + value: Amount::from_sat(100_000), + script_pubkey: ScriptBuf::new_p2tr_tweaked(spend.output_key()), + }], + }; + let tx = Transaction { + version: Version::TWO, + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::new(parent.compute_txid(), 0), + sequence: Sequence::from_height(144), + ..TxIn::default() + }], + output: vec![TxOut { + value: Amount::from_sat(90_000), + script_pubkey: ScriptBuf::new_p2tr(&secp, user.x_only_public_key().0, None), + }], + }; + let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); + psbt.inputs[0].witness_utxo = Some(parent.output[0].clone()); + psbt.inputs[0].non_witness_utxo = Some(parent); + let mut payload = binding; + payload["psbt_hex"] = json!(hex::encode(psbt.serialize())); + payload["input_index"] = json!(0); + payload["user_pubkey"] = json!(user_key.to_string()); + payload["user_leaf_hash_hex"] = json!(hex::encode(recovery_hash.to_byte_array())); + payload["witness"] = json!(""); + (psbt, payload) +} + +#[tokio::test] +async fn capabilities_are_profile_and_network_specific() { + let capabilities = capabilities_body(&state()).await; + assert_eq!(capabilities["profile"], "bitcoin-policy-v1"); + assert_eq!(capabilities["network"], "testnet"); + assert_eq!(capabilities["profile_hash"].as_str().unwrap().len(), 64); + assert_eq!( + capabilities["base_public_key_hex"].as_str().unwrap().len(), + 64 + ); + assert_eq!(capabilities["provenance"], "parent-txid-verified"); + assert_eq!( + capabilities["genesis_hash"], + hex::encode( + bitcoin::blockdata::constants::genesis_block(bitcoin::Network::Testnet) + .block_hash() + .to_byte_array() + ) + ); +} + +#[tokio::test] +async fn prepare_evaluate_and_sign_use_the_exact_same_context() { + let state = state(); + let (original, payload) = fixture(&state, &unit_program()).await; + let response = request(&state, "POST", "prepare", payload.clone()).await; + assert_eq!(response.status(), StatusCode::OK); + let prepared = body(response).await; + assert_eq!(prepared["context"]["fee"], 10_000); + assert_eq!(prepared["context"]["debit"], 100_000); + assert_eq!(prepared["context"]["chain_status_verified"], false); + assert_eq!(prepared["context"]["provenance"], "parent-txid-verified"); + let response = request(&state, "POST", "evaluate", payload.clone()).await; + assert_eq!(response.status(), StatusCode::OK); + let evaluation = body(response).await; + assert_eq!(evaluation["accepted"], true); + assert_eq!(evaluation["context"], prepared["context"]); + let response = request(&state, "POST", "sign", payload).await; + assert_eq!(response.status(), StatusCode::OK); + let signed = body(response).await; + assert_eq!(signed["context"], prepared["context"]); + let result = + Psbt::deserialize(&hex::decode(signed["psbt_hex"].as_str().unwrap()).unwrap()).unwrap(); + assert_eq!(result.unsigned_tx, original.unsigned_tx); + assert_eq!( + result.inputs[0].witness_utxo, + original.inputs[0].witness_utxo + ); + assert_eq!( + result.inputs[0].non_witness_utxo, + original.inputs[0].non_witness_utxo + ); + let key = XOnlyPublicKey::from_str(signed["public_key_hex"].as_str().unwrap()).unwrap(); + let digest: [u8; 32] = hex::decode( + signed["context"]["cooperative_sighash_default"] + .as_str() + .unwrap(), + ) + .unwrap() + .try_into() + .unwrap(); + let signature = bitcoin::secp256k1::schnorr::Signature::from_slice( + &hex::decode(signed["signature_hex"].as_str().unwrap()).unwrap(), + ) + .unwrap(); + state + .secp + .verify_schnorr(&signature, &Message::from_digest(digest), &key) + .unwrap(); + assert_eq!(result.inputs[0].tap_script_sigs.len(), 1); + assert_eq!( + result.inputs[0] + .tap_script_sigs + .values() + .next() + .unwrap() + .signature, + signature + ); + let (control, (script, version)) = result.inputs[0].tap_scripts.iter().next().unwrap(); + assert_eq!(*version, LeafVersion::TapScript); + assert_eq!( + hex::encode( + bitcoin::sighash::ScriptPath::with_defaults(script) + .leaf_hash() + .to_byte_array() + ), + signed["context"]["cooperative_leaf_hash"] + ); + let output_key = XOnlyPublicKey::from_slice( + &original.inputs[0] + .witness_utxo + .as_ref() + .unwrap() + .script_pubkey + .as_bytes()[2..], + ) + .unwrap(); + assert!(control.verify_taproot_commitment(&*state.secp, output_key, script)); +} + +#[tokio::test] +async fn missing_or_inconsistent_parent_evidence_never_signs() { + let state = state(); + let (mut psbt, mut payload) = fixture(&state, &unit_program()).await; + psbt.inputs[0].non_witness_utxo = None; + payload["psbt_hex"] = json!(hex::encode(psbt.serialize())); + let response = request(&state, "POST", "sign", payload.clone()).await; + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); + let error = body(response).await; + assert_eq!(error["code"], "invalid_context"); + assert!(error.get("signature_hex").is_none()); + let (mut psbt, _) = fixture(&state, &unit_program()).await; + psbt.inputs[0].witness_utxo.as_mut().unwrap().value = Amount::from_sat(100_001); + payload["psbt_hex"] = json!(hex::encode(psbt.serialize())); + let response = request(&state, "POST", "sign", payload).await; + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); + assert!(body(response).await.get("signature_hex").is_none()); +} + +#[tokio::test] +async fn mismatched_profile_and_network_reject_before_execution() { + let state = state(); + let (_, payload) = fixture(&state, &unit_program()).await; + for (field, value) in [ + ("profile_hash", json!(hex::encode([0u8; 32]))), + ("network", json!("testnet4")), + ] { + let mut modified = payload.clone(); + modified[field] = value; + modified["program"] = json!("invalid-base64"); + for operation in ["prepare", "evaluate", "sign"] { + let response = request(&state, "POST", operation, modified.clone()).await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let error = body(response).await; + assert!(error["error"].as_str().unwrap().contains(field)); + assert!(error.get("signature_hex").is_none()); + } + } +} + +#[tokio::test] +async fn profile_key_is_isolated_even_for_a_jet_free_program() { + let state = state(); + let capabilities = capabilities_body(&state).await; + let program = unit_program(); + let new_response = request( + &state, + "POST", + "tweak", + json!({ + "profile_hash": capabilities["profile_hash"], + "network": "testnet", + "program": program, + "redeem_script_hex": "", + }), + ) + .await; + assert_eq!(new_response.status(), StatusCode::OK); + let policy_key = body(new_response).await; + let bytes = STANDARD.decode(program).unwrap(); + let cmr = simplicity_unchained_core::policy::policy_cmr(&bytes).unwrap(); + let key = UntweakedKeypair::from_secret_key(&state.secp, &state.secret_key) + .tap_tweak( + &state.secp, + Some(TapNodeHash::from_byte_array( + simplicity_unchained_core::utils::signing_commitment(cmr, &[]), + )), + ) + .to_keypair(); + assert_ne!( + policy_key["public_key_hex"], + key.x_only_public_key().0.to_string() + ); +} + +#[tokio::test] +async fn policy_requests_have_the_same_exact_body_limit() { + let state = state(); + let capabilities = capabilities_body(&state).await; + let mut payload = json!({ + "profile_hash": capabilities["profile_hash"], + "network": "testnet", + "program": unit_program(), + "redeem_script_hex": "", + }) + .to_string(); + payload.extend(std::iter::repeat_n(' ', MAX_BODY_BYTES - payload.len())); + for (extra, expected) in [(0, StatusCode::OK), (1, StatusCode::PAYLOAD_TOO_LARGE)] { + let response = routes(state.clone()) + .oneshot( + Request::post(format!("{PREFIX}/tweak")) + .header("content-type", "application/json") + .body(Body::from(format!("{payload}{}", " ".repeat(extra)))) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), expected); + } +} + +#[tokio::test] +async fn transaction_approval_prepares_without_a_signature_and_rejects_mutation() { + let state = state(); + let (program, approver) = approval_program(); + let (mut psbt, mut payload) = fixture(&state, &program).await; + payload.as_object_mut().unwrap().remove("witness"); + let response = request(&state, "POST", "prepare", payload.clone()).await; + assert_eq!(response.status(), StatusCode::OK); + let prepared = body(response).await; + let digest: [u8; 32] = hex::decode( + prepared["context"]["policy_transaction_approval_hash"] + .as_str() + .unwrap(), + ) + .unwrap() + .try_into() + .unwrap(); + let approval = state + .secp + .sign_schnorr(&Message::from_digest(digest), &approver); + payload["witness"] = json!(STANDARD.encode(approval.as_ref())); + let response = request(&state, "POST", "sign", payload.clone()).await; + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(body(response).await["context"], prepared["context"]); + psbt.unsigned_tx.output[0].value = Amount::from_sat(89_999); + payload["psbt_hex"] = json!(hex::encode(psbt.serialize())); + for operation in ["evaluate", "sign"] { + let response = request(&state, "POST", operation, payload.clone()).await; + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); + let error = body(response).await; + assert_eq!(error["code"], "policy_rejected"); + assert!(error.get("signature_hex").is_none()); + } +} + +#[tokio::test] +async fn removed_legacy_signing_endpoint_cannot_sign_new_profile_outputs() { + let state = state(); + let (_, payload) = fixture(&state, &unit_program()).await; + let response = + crate::handlers::tests::post_json(&state, "/simplicity-unchained/sign/psbt", payload).await; + assert_eq!(response.status(), StatusCode::NOT_FOUND); +} + +#[tokio::test] +async fn over_cost_policy_rejects_without_a_success_shaped_signature() { + let state = state(); + let program = crate::handlers::tests::expensive_program(); + let (_, payload) = fixture(&state, &program).await; + let response = request(&state, "POST", "sign", payload).await; + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); + let error = body(response).await; + assert_eq!(error["code"], "execution_limit"); + assert!(error.get("signature_hex").is_none()); + assert_eq!( + state.work_slots.available_permits(), + crate::work::MAX_BLOCKING_JOBS + ); +} diff --git a/service/src/handlers/sign_psbt.rs b/service/src/handlers/sign_psbt.rs deleted file mode 100644 index 22e5a59..0000000 --- a/service/src/handlers/sign_psbt.rs +++ /dev/null @@ -1,909 +0,0 @@ -use std::str::FromStr; - -use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; - -use simplicity::bitcoin::key::{TapTweak, TweakedKeypair, UntweakedKeypair}; -use simplicity::bitcoin::sighash::{Prevouts, ScriptPath}; -use simplicity::bitcoin::taproot::{LeafVersion, TaprootBuilder}; -use simplicity::bitcoin::{self, TapSighashType}; -use simplicity::bitcoin::{TapNodeHash, Transaction, TxOut}; - -use simplicity::bitcoin::{hashes::Hash, psbt::Psbt, sighash::SighashCache}; - -use simplicity::elements::secp256k1_zkp::Message; - -use serde::{Deserialize, Serialize}; - -use simplicity_unchained_core::utils::{ - UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_btc, signing_commitment, -}; -use validator::Validate; - -use simplicity_unchained_core::runner::SimplicityRunner; - -use crate::handlers::ErrorResponse; -use crate::validation; -use crate::work::WorkPermit; - -use super::SignerState; - -#[derive(Debug, Deserialize, Validate)] -pub struct SignPsbtRequest { - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub psbt_hex: String, - - #[validate(range(min = 0, max = { - u16::MAX as usize - }))] - pub input_index: usize, - - #[validate(length(min = 1))] - pub program: String, - - pub witness: Option, - - #[validate(custom(function = "validation::validate_redeem_script"))] - pub redeem_script_hex: Option, - - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub user_pubkey: String, - - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub user_leaf_hash_hex: String, -} - -#[derive(Debug, Serialize)] -pub struct SignPsbtResponse { - pub psbt_hex: String, - pub signature_hex: String, - pub public_key_hex: String, - pub input_index: usize, - pub partial_sigs_count: usize, -} - -pub async fn sign_psbt( - State(state): State, - permit: WorkPermit, - Json(request): Json, -) -> impl IntoResponse { - let result = permit - .run(move || { - request - .validate() - .map_err(|e| format!("Validation failed: {}", e))?; - sign_psbt_internal(&state, request) - }) - .await; - match result { - Ok(Ok(response)) => { - log::info!( - "[200] Sign PSBT successful: Tweaked Public Key {}", - response.public_key_hex - ); - - (StatusCode::OK, Json(response)).into_response() - } - Ok(Err(e)) => { - log::error!("[400] Sign PSBT error: {}", e); - - (StatusCode::BAD_REQUEST, Json(ErrorResponse { error: e })).into_response() - } - Err(e) => e.into_response(), - } -} - -fn sign_psbt_internal( - state: &SignerState, - request: SignPsbtRequest, -) -> Result { - let psbt_bytes = - hex::decode(&request.psbt_hex).map_err(|e| format!("Failed to decode PSBT hex: {}", e))?; - - let mut psbt: Psbt = - Psbt::deserialize(&psbt_bytes).map_err(|e| format!("Failed to deserialize PSBT: {}", e))?; - - if request.input_index >= psbt.inputs.len() { - return Err(format!( - "Input index {} out of bounds (PSBT has {} inputs)", - request.input_index, - psbt.inputs.len() - )); - } - - let user_pk = bitcoin::secp256k1::PublicKey::from_str(&request.user_pubkey) - .map_err(|e| format!("Failed to deserialize user pubkey: {}", e))?; - - let user_leaf_hash = { - let bytes = hex::decode(&request.user_leaf_hash_hex) - .map_err(|e| format!("Failed to decode user leaf hash hex: {}", e))?; - - TapNodeHash::from_slice(&bytes).map_err(|e| e.to_string())? - }; - - let redeem_script = validation::decode_redeem_script(request.redeem_script_hex.as_deref())?; - - let cmr = SimplicityRunner::execute_bitcoin( - &request.program, - request.witness.as_deref(), - request.input_index, - &psbt, - redeem_script.clone(), - state.bitcoin_network, - ) - .map_err(|e| format!("Simplicity execution failed: {}", e))?; - - let untweaked_keypair = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key); - let tweaked_keypair = untweaked_keypair.tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(signing_commitment( - cmr, - redeem_script.as_bytes(), - ))), - ); - - let tx = psbt - .clone() - .extract_tx() - .map_err(|e| format!("Failed to extract transaction: {}", e))?; - - let (sig_bytes, partial_sigs_count) = { - let sig = sign_p2tr( - state, - &mut psbt, - &tx, - &tweaked_keypair, - &user_pk, - user_leaf_hash, - request.input_index, - )?; - let count = psbt.inputs[request.input_index].partial_sigs.len(); - (sig, count) - }; - - let public_key_hex = String::new(); - - Ok(SignPsbtResponse { - psbt_hex: hex::encode(psbt.serialize()), - signature_hex: hex::encode(&sig_bytes), - public_key_hex, - input_index: request.input_index, - partial_sigs_count, - }) -} - -fn sign_p2tr( - state: &SignerState, - psbt: &mut Psbt, - tx: &Transaction, - cosigner_tweaked: &TweakedKeypair, - user_pubkey: &bitcoin::secp256k1::PublicKey, - user_leaf_hash: TapNodeHash, - input_index: usize, -) -> Result, String> { - let secp = &*state.secp; - - let (cosigner_tweaked_xonly, parity) = cosigner_tweaked.public_parts(); - let cosigner_full_pk = cosigner_tweaked_xonly - .as_x_only_public_key() - .public_key(parity) - .into(); - let user_full_pk = (*user_pubkey).into(); - - let multisig_leaf = p2tr_multisig_leaf_btc(&cosigner_full_pk, &user_full_pk); - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf.clone()) - .map_err(|e| format!("Failed to add multisig leaf: {}", e))? - .add_hidden_node(1, user_leaf_hash) - .map_err(|e| format!("Failed to add recovery leaf: {}", e))? - .finalize(secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| "Failed to finalize taproot".to_string())?; - - let control_block = spend_info - .control_block(&(multisig_leaf.clone(), LeafVersion::TapScript)) - .ok_or_else(|| "Failed to get control block".to_string())?; - - let prevouts: Vec = psbt - .inputs - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| "Missing witness_utxo".to_string()) - }) - .collect::>()?; - - if prevouts[input_index].script_pubkey - != bitcoin::ScriptBuf::new_p2tr_tweaked(spend_info.output_key()) - { - return Err( - "Spent output does not match the program, redeem script, user key and recovery leaf" - .to_string(), - ); - } - - let mut sighash_cache = SighashCache::new(tx); - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &Prevouts::All(&prevouts), - ScriptPath::with_defaults(&multisig_leaf), - TapSighashType::Default, - ) - .map_err(|e| format!("Failed to compute script-path sighash: {}", e))?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let signature = secp.sign_schnorr(&msg, &cosigner_tweaked.to_keypair()); - - let tap_sig = bitcoin::taproot::Signature { - signature, - sighash_type: TapSighashType::Default, - }; - - let leaf_hash = ScriptPath::with_defaults(&multisig_leaf).leaf_hash(); - psbt.inputs[input_index].tap_script_sigs.insert( - (cosigner_tweaked_xonly.to_x_only_public_key(), leaf_hash), - tap_sig, - ); - - psbt.inputs[input_index] - .tap_scripts - .insert(control_block, (multisig_leaf, LeafVersion::TapScript)); - - Ok(signature.as_ref().to_vec()) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use super::*; - use simplicity::bitcoin::{self}; - use simplicity::bitcoin::{ - NetworkKind, OutPoint, PrivateKey, Transaction, TxIn, TxOut, Txid, psbt::Psbt, - script::ScriptBuf, taproot::LeafVersion, - }; - use simplicity::elements::{secp256k1_zkp::Secp256k1, secp256k1_zkp::SecretKey}; - use simplicity_unchained_core::BitcoinNetwork; - use simplicity_unchained_core::program::Program; - use std::str::FromStr; - - use simplicity_unchained_core::utils::generate_p2tr_address_btc; - use simplicity_unchained_core::{ElementsNetwork, jets::bitcoin::CoreExtension}; - - const PROGRAM: &str = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA"; - - fn create_test_signer_state() -> SignerState { - let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("valid secret key"); - SignerState { - secret_key, - secp: Arc::new(Secp256k1::new()), - elements_network: ElementsNetwork::LiquidTestnet, - bitcoin_network: BitcoinNetwork::Testnet, - work_slots: Arc::new(tokio::sync::Semaphore::new(crate::work::MAX_BLOCKING_JOBS)), - } - } - - fn create_test_transaction() -> Transaction { - let prev_txid = - Txid::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - .unwrap(); - - Transaction { - version: simplicity::bitcoin::transaction::Version::TWO, - lock_time: simplicity::bitcoin::absolute::LockTime::ZERO, - input: vec![TxIn { - previous_output: OutPoint { - txid: prev_txid, - vout: 0, - }, - script_sig: ScriptBuf::new(), - sequence: simplicity::bitcoin::Sequence::MAX, - witness: Default::default(), - }], - output: vec![TxOut { - value: simplicity::bitcoin::Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), - }], - } - } - - fn create_test_psbt(tx: Transaction, script_pubkey: ScriptBuf) -> Psbt { - let mut psbt = Psbt::from_unsigned_tx(tx).unwrap(); - psbt.inputs[0].witness_utxo = Some(TxOut { - value: simplicity::bitcoin::Amount::from_sat(100_000), - script_pubkey, - }); - psbt - } - - /// Build a P2TR address + user leaf hash for use in tests. - fn create_test_p2tr_setup( - state: &SignerState, - user_secret_key: &SecretKey, - ) -> (bitcoin::Address, TapNodeHash, bitcoin::PublicKey) { - let cmr = Program::::from_str(PROGRAM, None) - .unwrap() - .cmr(); - let tweaked = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key).tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(signing_commitment(cmr, &[]))), - ); - let (key, parity) = tweaked.public_parts(); - let cosigner_pubkey = - bitcoin::PublicKey::new(key.to_x_only_public_key().public_key(parity)); - let user_pubkey = bitcoin::PublicKey::from_private_key( - &*state.secp, - &PrivateKey { - compressed: true, - network: NetworkKind::Test, - inner: *user_secret_key, - }, - ); - - let user_leaf_script = bitcoin::script::Builder::new() - .push_opcode(bitcoin::opcodes::OP_TRUE) - .into_script(); - let user_leaf_hash = TapNodeHash::from_script(&user_leaf_script, LeafVersion::TapScript); - - let (address, _) = generate_p2tr_address_btc( - &cosigner_pubkey, - &user_pubkey, - user_leaf_hash, - bitcoin::Network::Testnet, - ) - .expect("failed to generate p2tr address"); - - (address, user_leaf_hash, user_pubkey) - } - - fn create_test_sign_request( - psbt_hex: String, - user_pubkey: &bitcoin::PublicKey, - user_leaf_hash: TapNodeHash, - ) -> SignPsbtRequest { - SignPsbtRequest { - psbt_hex, - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - } - } - - #[test] - fn test_sign_psbt_internal_success() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, user_leaf_hash, user_pubkey) = - create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let psbt = create_test_psbt(tx, address.script_pubkey()); - let psbt_hex = hex::encode(psbt.serialize()); - - let request = create_test_sign_request(psbt_hex, &user_pubkey, user_leaf_hash); - let result = sign_psbt_internal(&state, request); - assert!(result.is_ok()); - - let response = result.unwrap(); - assert!(!response.psbt_hex.is_empty()); - assert!(!response.signature_hex.is_empty()); - assert_eq!(response.input_index, 0); - - // Schnorr sig is always 64 bytes - let sig_bytes = hex::decode(&response.signature_hex).unwrap(); - assert_eq!(sig_bytes.len(), 64); - - let signed_psbt = Psbt::deserialize(&hex::decode(&response.psbt_hex).unwrap()).unwrap(); - assert!(!signed_psbt.inputs[0].tap_script_sigs.is_empty()); - } - - #[tokio::test] - async fn high_cost_program_is_rejected_by_signing_endpoint() { - use super::super::tests::{expensive_program, post_json}; - let state = create_test_signer_state(); - let user_secret = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (address, recovery, user) = create_test_p2tr_setup(&state, &user_secret); - let psbt = create_test_psbt(create_test_transaction(), address.script_pubkey()); - let response = tokio::time::timeout( - std::time::Duration::from_secs(1), - post_json( - &state, - "/simplicity-unchained/sign/psbt", - serde_json::json!({ - "psbt_hex": hex::encode(psbt.serialize()), - "input_index": 0, - "program": expensive_program(), - "witness": "", - "user_pubkey": hex::encode(user.to_bytes()), - "user_leaf_hash_hex": hex::encode(recovery.to_byte_array()), - }), - ), - ) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - let body = axum::body::to_bytes(response.into_body(), usize::MAX) - .await - .unwrap(); - let body: serde_json::Value = serde_json::from_slice(&body).unwrap(); - assert!( - body["error"] - .as_str() - .unwrap() - .contains("exceeds maximum 10000000 milliweight") - ); - } - - #[test] - fn test_sign_psbt_internal_invalid_hex() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (_, user_leaf_hash, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let request = SignPsbtRequest { - psbt_hex: "invalid_hex!!!".to_string(), - input_index: 0, - program: PROGRAM.to_string(), - witness: None, - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_psbt_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Failed to decode PSBT hex")); - } - - #[test] - fn test_sign_psbt_internal_invalid_psbt_data() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (_, user_leaf_hash, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let request = SignPsbtRequest { - psbt_hex: hex::encode(b"not a valid psbt"), - input_index: 0, - program: PROGRAM.to_string(), - witness: None, - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_psbt_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Failed to deserialize PSBT")); - } - - #[test] - fn test_sign_psbt_internal_input_index_out_of_bounds() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, user_leaf_hash, user_pubkey) = - create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let psbt = create_test_psbt(tx, address.script_pubkey()); - let psbt_hex = hex::encode(psbt.serialize()); - - let request = SignPsbtRequest { - psbt_hex, - input_index: 99, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_psbt_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Input index 99 out of bounds")); - } - - #[test] - fn test_sign_psbt_internal_invalid_leaf_hash() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, _, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let psbt = create_test_psbt(tx, address.script_pubkey()); - let psbt_hex = hex::encode(psbt.serialize()); - - let request = SignPsbtRequest { - psbt_hex, - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: "deadbeef".to_string(), // wrong length - }; - - let result = sign_psbt_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("invalid slice length")); - } - - #[test] - fn test_sign_psbt_internal_signature_verification() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, user_leaf_hash, user_pubkey) = - create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let psbt = create_test_psbt(tx.clone(), address.script_pubkey()); - let psbt_hex = hex::encode(psbt.serialize()); - - let request = create_test_sign_request(psbt_hex, &user_pubkey, user_leaf_hash); - let result = sign_psbt_internal(&state, request).unwrap(); - - let signed_psbt = Psbt::deserialize(&hex::decode(&result.psbt_hex).unwrap()).unwrap(); - - // Exactly one tap_script_sig added - assert_eq!(signed_psbt.inputs[0].tap_script_sigs.len(), 1); - - // Derive the expected cosigner xonly pubkey - let program = Program::::from_str(PROGRAM, Some("")).unwrap(); - let cmr = program.commit_prog().cmr(); - let untweaked = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key); - let tweaked = untweaked.tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(signing_commitment(cmr, &[]))), - ); - let (cosigner_xonly, _) = tweaked.public_parts(); - - // Verify the sig is keyed to the cosigner xonly pubkey - let has_cosigner_sig = signed_psbt.inputs[0] - .tap_script_sigs - .keys() - .any(|(xonly, _)| xonly == &cosigner_xonly.to_x_only_public_key()); - assert!(has_cosigner_sig); - } - - #[tokio::test] - async fn test_script_key_substitution_is_rejected() { - use super::super::tests::{key_script, script_key_program, tweaked_public_key}; - use super::super::tweak::JetEnvKind; - use simplicity::jet::Core; - - let state = create_test_signer_state(); - let guardian = SecretKey::from_slice(&[0x11; 32]).unwrap(); - let attacker = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (program, witness) = script_key_program( - CoreExtension::GetPubkeyFromScript, - CoreExtension::Core(Core::Bip0340Verify), - &guardian, - ); - let (attacker_program, attacker_witness) = script_key_program( - CoreExtension::GetPubkeyFromScript, - CoreExtension::Core(Core::Bip0340Verify), - &attacker, - ); - assert_eq!(program, attacker_program); - let script = key_script(&state, &guardian); - let attacker_script = key_script(&state, &attacker); - let cosigner = - tweaked_public_key(&state, &program, Some(&script), JetEnvKind::Bitcoin).await; - let (_, recovery, user) = create_test_p2tr_setup(&state, &attacker); - let (address, spend_info) = - generate_p2tr_address_btc(&cosigner, &user, recovery, bitcoin::Network::Testnet) - .unwrap(); - let tx = create_test_transaction(); - let psbt = create_test_psbt(tx.clone(), address.script_pubkey()); - let request = |script: Option<&str>, witness: &str| { - let mut request = - create_test_sign_request(hex::encode(psbt.serialize()), &user, recovery); - request.program = program.clone(); - request.witness = Some(witness.to_string()); - request.redeem_script_hex = script.map(str::to_string); - request - }; - let result = sign_psbt_internal(&state, request(Some(&script), &witness)).unwrap(); - let signed = Psbt::deserialize(&hex::decode(result.psbt_hex).unwrap()).unwrap(); - let ((signing_key, _), signature) = - signed.inputs[0].tap_script_sigs.first_key_value().unwrap(); - assert_eq!(*signing_key, cosigner.inner.x_only_public_key().0); - let (control, (leaf, _)) = signed.inputs[0].tap_scripts.first_key_value().unwrap(); - assert!(control.verify_taproot_commitment( - &*state.secp, - spend_info.output_key().to_x_only_public_key(), - leaf, - )); - let sighash = SighashCache::new(&tx) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[psbt.inputs[0].witness_utxo.clone().unwrap()]), - ScriptPath::with_defaults(leaf), - TapSighashType::Default, - ) - .unwrap(); - state - .secp - .verify_schnorr( - &signature.signature, - &Message::from_digest(sighash.to_byte_array()), - signing_key, - ) - .unwrap(); - assert!( - sign_psbt_internal(&state, request(Some(&script), &attacker_witness)) - .unwrap_err() - .contains("Simplicity execution failed") - ); - assert!( - sign_psbt_internal(&state, request(Some(&attacker_script), &attacker_witness)) - .unwrap_err() - .contains("Spent output does not match") - ); - let changed_opcode = format!("51{}", &script[2..]); - assert!( - sign_psbt_internal(&state, request(Some(&changed_opcode), &witness)) - .unwrap_err() - .contains("Spent output does not match") - ); - assert!(sign_psbt_internal(&state, request(None, &attacker_witness)).is_err()); - // A signature over a fixed message remains a reusable policy, by design. - let mut changed_tx = psbt.clone(); - changed_tx.unsigned_tx.output[0].value = bitcoin::Amount::from_sat(40_000); - let mut static_replay = request(Some(&script), &witness); - static_replay.psbt_hex = hex::encode(changed_tx.serialize()); - assert!(sign_psbt_internal(&state, static_replay).is_ok()); - } - - #[tokio::test] - async fn transaction_approval_cannot_be_replayed_for_a_different_spend() { - use super::super::tests::{post_json, tweaked_public_key}; - use super::super::tweak::JetEnvKind; - use base64::{Engine as _, engine::general_purpose::STANDARD}; - use simplicity::{ - ConstructNode, Word, - jet::Core, - node::{CoreConstructible, WitnessConstructible}, - types, - }; - use simplicity_unchained_core::runner::bitcoin_execution_environment; - - let state = create_test_signer_state(); - let guardian = SecretKey::from_slice(&[0x11; 32]).unwrap(); - let guardian_pair = UntweakedKeypair::from_secret_key(&*state.secp, &guardian); - let guardian_key = guardian_pair.x_only_public_key().0; - let program = types::Context::with_context(|ctx| { - let key = Arc::::const_word(&ctx, Word::u256(guardian_key.serialize())); - let hash = Arc::::jet(&ctx, &CoreExtension::TxApprovalHash); - let key_msg = Arc::::pair(&key, &hash).unwrap(); - let signature = Arc::::witness(&ctx, None); - let args = Arc::::pair(&key_msg, &signature).unwrap(); - let verify = Arc::::jet(&ctx, &CoreExtension::Core(Core::Bip0340Verify)); - STANDARD.encode( - Arc::::comp(&args, &verify) - .unwrap() - .finalize_types() - .unwrap() - .to_vec_without_witness(), - ) - }); - let cmr = Program::::from_str(&program, None) - .unwrap() - .cmr(); - let user_secret = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (_, recovery, user) = create_test_p2tr_setup(&state, &user_secret); - let cosigner = tweaked_public_key(&state, &program, None, JetEnvKind::Bitcoin).await; - let (address, spend_info) = - generate_p2tr_address_btc(&cosigner, &user, recovery, bitcoin::Network::Testnet) - .unwrap(); - let psbt = create_test_psbt(create_test_transaction(), address.script_pubkey()); - let approval = |psbt: &Psbt| { - let digest = bitcoin_execution_environment( - psbt, - 0, - cmr, - &simplicity::elements::Script::new(), - state.bitcoin_network, - ) - .unwrap() - .approval_hash; - STANDARD.encode( - state - .secp - .sign_schnorr(&Message::from_digest(digest), &guardian_pair) - .as_ref(), - ) - }; - let witness = approval(&psbt); - let body = |psbt: &Psbt, witness: &str| { - serde_json::json!({ - "psbt_hex": hex::encode(psbt.serialize()), - "input_index": 0, - "program": program, - "witness": witness, - "user_pubkey": hex::encode(user.to_bytes()), - "user_leaf_hash_hex": hex::encode(recovery.to_byte_array()), - }) - }; - let response = post_json( - &state, - "/simplicity-unchained/sign/psbt", - body(&psbt, &witness), - ) - .await; - assert_eq!(response.status(), StatusCode::OK); - let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) - .await - .unwrap(); - let result: serde_json::Value = serde_json::from_slice(&bytes).unwrap(); - let signed = - Psbt::deserialize(&hex::decode(result["psbt_hex"].as_str().unwrap()).unwrap()).unwrap(); - let ((signing_key, _), signature) = - signed.inputs[0].tap_script_sigs.first_key_value().unwrap(); - let (control, (leaf, _)) = signed.inputs[0].tap_scripts.first_key_value().unwrap(); - assert!(control.verify_taproot_commitment( - &*state.secp, - spend_info.output_key().to_x_only_public_key(), - leaf, - )); - let sighash = SighashCache::new(&psbt.unsigned_tx) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[psbt.inputs[0].witness_utxo.clone().unwrap()]), - ScriptPath::with_defaults(leaf), - TapSighashType::Default, - ) - .unwrap(); - state - .secp - .verify_schnorr( - &signature.signature, - &Message::from_digest(sighash.to_byte_array()), - signing_key, - ) - .unwrap(); - - let mut changed_output = psbt.clone(); - changed_output.unsigned_tx.output[0].value = bitcoin::Amount::from_sat(40_000); - let mut changed_locktime = psbt.clone(); - changed_locktime.unsigned_tx.lock_time = bitcoin::absolute::LockTime::from_consensus(1); - let mut changed_prevout = psbt.clone(); - changed_prevout.inputs[0] - .witness_utxo - .as_mut() - .unwrap() - .value = bitcoin::Amount::from_sat(99_999); - for changed in [&changed_output, &changed_locktime, &changed_prevout] { - let response = post_json( - &state, - "/simplicity-unchained/sign/psbt", - body(changed, &witness), - ) - .await; - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) - .await - .unwrap(); - let result: serde_json::Value = serde_json::from_slice(&bytes).unwrap(); - assert!( - result["error"] - .as_str() - .unwrap() - .contains("Simplicity execution failed") - ); - } - let mut other_network = state.clone(); - other_network.bitcoin_network = BitcoinNetwork::Testnet4; - let response = post_json( - &other_network, - "/simplicity-unchained/sign/psbt", - body(&psbt, &witness), - ) - .await; - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - let fresh_witness = approval(&changed_output); - let response = post_json( - &state, - "/simplicity-unchained/sign/psbt", - body(&changed_output, &fresh_witness), - ) - .await; - assert_eq!(response.status(), StatusCode::OK); - } - - #[test] - fn test_legacy_cmr_only_output_is_rejected() { - let state = create_test_signer_state(); - let user_secret = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (_, recovery, user) = create_test_p2tr_setup(&state, &user_secret); - let cmr = Program::::from_str(PROGRAM, None) - .unwrap() - .cmr(); - let legacy = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key).tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(cmr.to_byte_array())), - ); - let (key, parity) = legacy.public_parts(); - let cosigner = bitcoin::PublicKey::new(key.to_x_only_public_key().public_key(parity)); - let (address, _) = - generate_p2tr_address_btc(&cosigner, &user, recovery, bitcoin::Network::Testnet) - .unwrap(); - let psbt = create_test_psbt(create_test_transaction(), address.script_pubkey()); - let request = create_test_sign_request(hex::encode(psbt.serialize()), &user, recovery); - assert!( - sign_psbt_internal(&state, request) - .unwrap_err() - .contains("Spent output does not match") - ); - } - - #[test] - fn test_sign_psbt_request_validation() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (_, user_leaf_hash, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let leaf_hash_hex = hex::encode(user_leaf_hash.to_byte_array()); - let user_pubkey_hex = hex::encode(user_pubkey.to_bytes()); - - // Valid request - let valid_request = SignPsbtRequest { - psbt_hex: "70736274ff".to_string(), - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: user_pubkey_hex.clone(), - redeem_script_hex: None, - user_leaf_hash_hex: leaf_hash_hex.clone(), - }; - assert!(valid_request.validate().is_ok()); - - // Empty psbt_hex should fail - let invalid_request = SignPsbtRequest { - psbt_hex: "".to_string(), - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: user_pubkey_hex, - redeem_script_hex: None, - user_leaf_hash_hex: leaf_hash_hex, - }; - assert!(invalid_request.validate().is_err()); - } - - #[test] - fn test_signer_state_new_valid_key() { - let secret_key_hex = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"; - let result = SignerState::new( - secret_key_hex, - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_ok()); - } - - #[test] - fn test_signer_state_new_invalid_hex() { - let result = SignerState::new( - "not_valid_hex", - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Invalid private key hex")); - } - - #[test] - fn test_signer_state_new_invalid_key_length() { - let result = SignerState::new( - "cdcdcd", - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Invalid private key")); - } -} diff --git a/service/src/handlers/sign_pset.rs b/service/src/handlers/sign_pset.rs deleted file mode 100644 index 0c53006..0000000 --- a/service/src/handlers/sign_pset.rs +++ /dev/null @@ -1,745 +0,0 @@ -use std::str::FromStr; - -use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; - -use simplicity::{ - bitcoin::{self}, - elements::{ - self, SchnorrSig, SchnorrSighashType, Transaction, TxOut, - encode::Encodable, - schnorr::{TapTweak, TweakedKeypair, UntweakedKeypair}, - sighash::{Prevouts, ScriptPath}, - taproot::{LeafVersion, TapNodeHash, TaprootBuilder}, - }, -}; - -use elements::{ - encode::deserialize, pset::PartiallySignedTransaction, secp256k1_zkp::Message, - sighash::SighashCache, -}; -use serde::{Deserialize, Serialize}; - -use simplicity_unchained_core::{ - runner::SimplicityRunner, - utils::{UNSPENDABLE_KEY_P2TR, p2tr_multisig_leaf_elements, signing_commitment}, -}; -use validator::Validate; - -use crate::handlers::ErrorResponse; -use crate::validation; -use crate::work::WorkPermit; - -use super::SignerState; - -#[derive(Debug, Deserialize, Validate)] -pub struct SignPsetRequest { - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub pset_hex: String, - - // TODO(ivanlele): it looks bad, but cargo check yells warning which we can't silence with #[allow], so leave it as is for now - #[validate(range(min = 0, max = { - u16::MAX as usize - }))] - pub input_index: usize, - - #[validate(length(min = 1))] - pub program: String, - - pub witness: Option, - - #[validate(custom(function = "validation::validate_redeem_script"))] - pub redeem_script_hex: Option, - - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub user_pubkey: String, - - #[validate(length(min = 1), custom(function = "validation::validate_hex"))] - pub user_leaf_hash_hex: String, -} - -#[derive(Debug, Serialize)] -pub struct SignPsetResponse { - pub pset_hex: String, - pub signature_hex: String, - pub public_key_hex: String, - pub input_index: usize, - pub partial_sigs_count: usize, -} - -pub async fn sign_pset( - State(state): State, - permit: WorkPermit, - Json(request): Json, -) -> impl IntoResponse { - let result = permit - .run(move || { - request - .validate() - .map_err(|e| format!("Validation failed: {}", e))?; - sign_pset_internal(&state, request) - }) - .await; - match result { - Ok(Ok(response)) => { - log::info!( - "[200] Sign PSET successful: Tweaked Public Key {}", - response.public_key_hex - ); - - (StatusCode::OK, Json(response)).into_response() - } - Ok(Err(e)) => { - log::error!("[400] Sign PSET error: {}", e); - - (StatusCode::BAD_REQUEST, Json(ErrorResponse { error: e })).into_response() - } - Err(e) => e.into_response(), - } -} -fn sign_pset_internal( - state: &SignerState, - request: SignPsetRequest, -) -> Result { - let pset_bytes = - hex::decode(&request.pset_hex).map_err(|e| format!("Failed to decode PSET hex: {}", e))?; - - let mut pset: PartiallySignedTransaction = - deserialize(&pset_bytes).map_err(|e| format!("Failed to deserialize PSET: {}", e))?; - - if request.input_index >= pset.inputs().len() { - return Err(format!( - "Input index {} out of bounds (PSET has {} inputs)", - request.input_index, - pset.inputs().len() - )); - } - - let user_pk = bitcoin::secp256k1::PublicKey::from_str(&request.user_pubkey) - .map_err(|e| format!("Failed to deserialize user pubkey: {}", e))?; - - let user_leaf_hash = { - let bytes = hex::decode(&request.user_leaf_hash_hex) - .map_err(|e| format!("Failed to decode user leaf hash hex: {}", e))?; - - let bytes: [u8; 32] = bytes - .as_slice() - .try_into() - .map_err(|_| format!("invalid slice length {} (expected 32)", bytes.len()))?; - elements::hashes::sha256::Hash::from_byte_array(bytes) - }; - - let redeem_script = validation::decode_redeem_script(request.redeem_script_hex.as_deref())?; - - let cmr = SimplicityRunner::execute_elements( - &request.program, - request.witness.as_deref(), - request.input_index, - &pset, - redeem_script.clone(), - state.elements_network, - ) - .map_err(|e| format!("Simplicity execution failed: {}", e))?; - - let untweaked_keypair = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key); - let tweaked_keypair = untweaked_keypair.tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(signing_commitment( - cmr, - redeem_script.as_bytes(), - ))), - ); - - let tx = pset - .clone() - .extract_tx() - .map_err(|e| format!("Failed to extract transaction: {}", e))?; - - let (sig_bytes, partial_sigs_count) = { - let sig = sign_p2tr( - state, - &mut pset, - &tx, - &tweaked_keypair, - &user_pk, - user_leaf_hash, - request.input_index, - )?; - let count = pset.inputs()[request.input_index].partial_sigs.len(); - (sig, count) - }; - - let public_key_hex = String::new(); - - let mut pset_bytes = Vec::new(); - pset.consensus_encode(&mut pset_bytes) - .map_err(|e| format!("Failed to encode PSET: {}", e))?; - - Ok(SignPsetResponse { - pset_hex: hex::encode(pset_bytes), - signature_hex: hex::encode(&sig_bytes), - public_key_hex, - input_index: request.input_index, - partial_sigs_count, - }) -} - -fn sign_p2tr( - state: &SignerState, - pset: &mut PartiallySignedTransaction, - tx: &Transaction, - cosigner_tweaked: &TweakedKeypair, - user_pubkey: &bitcoin::secp256k1::PublicKey, - user_leaf_hash: elements::hashes::sha256::Hash, - input_index: usize, -) -> Result, String> { - let secp = &*state.secp; - - let (cosigner_tweaked_xonly, parity) = cosigner_tweaked.public_parts(); - let cosigner_full_pk = cosigner_tweaked_xonly.as_inner().public_key(parity).into(); - let user_full_pk = (*user_pubkey).into(); - - let multisig_leaf = p2tr_multisig_leaf_elements(&cosigner_full_pk, &user_full_pk); - - let spend_info = TaprootBuilder::new() - .add_leaf(1, multisig_leaf.clone()) - .map_err(|e| format!("Failed to add multisig leaf: {}", e))? - .add_hidden( - 1, - TapNodeHash::from_byte_array(user_leaf_hash.to_byte_array()), - ) - .map_err(|e| format!("Failed to add recovery leaf: {}", e))? - .finalize(secp, *UNSPENDABLE_KEY_P2TR) - .map_err(|_| "Failed to finalize taproot".to_string())?; - - let control_block = spend_info - .control_block(&(multisig_leaf.clone(), LeafVersion::default())) // TAPROOT_LEAF_TAPSCRIPT - .ok_or_else(|| "Failed to get control block".to_string())?; - - let prevouts: Vec = pset - .inputs() - .iter() - .map(|i| { - i.witness_utxo - .clone() - .ok_or_else(|| "Missing witness_utxo".to_string()) - }) - .collect::>()?; - - if prevouts[input_index].script_pubkey - != elements::Script::new_v1_p2tr_tweaked(spend_info.output_key()) - { - return Err( - "Spent output does not match the program, redeem script, user key and recovery leaf" - .to_string(), - ); - } - - let mut sighash_cache = SighashCache::new(tx); - let sighash = sighash_cache - .taproot_script_spend_signature_hash( - input_index, - &Prevouts::All(&prevouts), - ScriptPath::with_defaults(&multisig_leaf), - SchnorrSighashType::Default, - state.elements_network.genesis_hash(), - ) - .map_err(|e| format!("Failed to compute script-path sighash: {}", e))?; - - let msg = Message::from_digest(sighash.to_byte_array()); - let signature = secp.sign_schnorr(&msg, &cosigner_tweaked.to_inner()); - - let tap_sig = SchnorrSig { - sig: signature, - hash_ty: SchnorrSighashType::Default, - }; - - let leaf_hash = ScriptPath::with_defaults(&multisig_leaf).leaf_hash(); - pset.inputs_mut()[input_index] - .tap_script_sigs - .insert((cosigner_tweaked_xonly.into_inner(), leaf_hash), tap_sig); - - pset.inputs_mut()[input_index] - .tap_scripts - .insert(control_block, (multisig_leaf, LeafVersion::default())); // TAPROOT_LEAF_TAPSCRIPT - - Ok(signature.as_ref().to_vec()) -} -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use super::*; - use bitcoin::PublicKey; - use simplicity::{ - bitcoin::{NetworkKind, PrivateKey}, - elements::{ - OutPoint, Transaction, TxIn, TxOut, Txid, - pset::PartiallySignedTransaction as Pset, - secp256k1_zkp::{Secp256k1, SecretKey}, - taproot::LeafVersion, - }, - }; - use simplicity_unchained_core::{ - BitcoinNetwork, ElementsNetwork, jets::elements::ElementsExtension, program::Program, - utils::generate_p2tr_address_elements, - }; - use std::str::FromStr; - - const PROGRAM: &str = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA"; - - fn create_test_signer_state() -> SignerState { - let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("valid secret key"); - SignerState { - secret_key, - secp: Arc::new(Secp256k1::new()), - elements_network: ElementsNetwork::LiquidTestnet, - bitcoin_network: BitcoinNetwork::Testnet, - work_slots: Arc::new(tokio::sync::Semaphore::new(crate::work::MAX_BLOCKING_JOBS)), - } - } - - fn create_test_transaction() -> Transaction { - let prev_txid = - Txid::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - .expect("valid txid"); - - Transaction { - version: 2, - lock_time: elements::LockTime::ZERO, - input: vec![TxIn { - previous_output: OutPoint { - txid: prev_txid, - vout: 0, - }, - script_sig: elements::script::Script::new(), - sequence: elements::Sequence::MAX, - witness: Default::default(), - is_pegin: false, - asset_issuance: Default::default(), - }], - output: vec![TxOut::default()], - } - } - - fn create_test_pset( - tx: Transaction, - script_pubkey: elements::script::Script, - value: elements::confidential::Value, - ) -> Pset { - let mut pset = Pset::from_tx(tx); - pset.inputs_mut()[0].witness_utxo = Some(TxOut { - asset: elements::confidential::Asset::Explicit(elements::issuance::AssetId::default()), - value, - nonce: elements::confidential::Nonce::Null, - script_pubkey, - witness: Default::default(), - }); - for output in pset.outputs_mut() { - if output.amount.is_none() { - output.amount = Some(90_000); - } - if output.asset.is_none() { - output.asset = Some(elements::issuance::AssetId::default()); - } - } - pset - } - - fn create_test_p2tr_setup( - state: &SignerState, - user_secret_key: &SecretKey, - ) -> ( - elements::Address, - elements::hashes::sha256::Hash, - bitcoin::PublicKey, - ) { - let cmr = Program::::from_str(PROGRAM, None) - .unwrap() - .cmr(); - let tweaked = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key).tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(signing_commitment(cmr, &[]))), - ); - let (key, parity) = tweaked.public_parts(); - let cosigner_pubkey = PublicKey::new(key.into_inner().public_key(parity)); - let user_pubkey = PublicKey::from_private_key( - &*state.secp, - &PrivateKey { - compressed: true, - network: NetworkKind::Test, - inner: *user_secret_key, - }, - ); - - let user_leaf_script = elements::script::Builder::new() - .push_opcode(elements::opcodes::OP_TRUE) - .into_script(); - - let user_leaf_hash = elements::hashes::sha256::Hash::from_byte_array( - elements::taproot::TapLeafHash::from_script(&user_leaf_script, LeafVersion::default()) - .to_byte_array(), - ); - - let (address, _) = generate_p2tr_address_elements( - &cosigner_pubkey, - &user_pubkey, - user_leaf_hash, - &elements::AddressParams::LIQUID_TESTNET, - ) - .expect("failed to generate p2tr address"); - - (address, user_leaf_hash, user_pubkey) - } - - fn create_test_sign_request( - pset_hex: String, - user_pubkey: &PublicKey, - user_leaf_hash: elements::hashes::sha256::Hash, - ) -> SignPsetRequest { - SignPsetRequest { - pset_hex, - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - } - } - - #[test] - fn test_sign_pset_internal_success() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, user_leaf_hash, user_pubkey) = - create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let value = elements::confidential::Value::Explicit(100_000); - let pset = create_test_pset(tx, address.script_pubkey(), value); - - let mut pset_bytes = Vec::new(); - elements::encode::Encodable::consensus_encode(&pset, &mut pset_bytes).unwrap(); - let pset_hex = hex::encode(pset_bytes); - - let request = create_test_sign_request(pset_hex, &user_pubkey, user_leaf_hash); - let result = sign_pset_internal(&state, request); - assert!(result.is_ok()); - - let response = result.unwrap(); - assert!(!response.pset_hex.is_empty()); - assert!(!response.signature_hex.is_empty()); - assert_eq!(response.input_index, 0); - - let sig_bytes = hex::decode(&response.signature_hex).unwrap(); - assert_eq!(sig_bytes.len(), 64); - } - - #[tokio::test] - async fn high_cost_program_is_rejected_by_signing_endpoint() { - use super::super::tests::{expensive_program, post_json}; - let state = create_test_signer_state(); - let user_secret = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (address, recovery, user) = create_test_p2tr_setup(&state, &user_secret); - let pset = create_test_pset( - create_test_transaction(), - address.script_pubkey(), - elements::confidential::Value::Explicit(100_000), - ); - let response = tokio::time::timeout( - std::time::Duration::from_secs(1), - post_json( - &state, - "/simplicity-unchained/sign/pset", - serde_json::json!({ - "pset_hex": hex::encode(elements::encode::serialize(&pset)), - "input_index": 0, - "program": expensive_program(), - "witness": "", - "user_pubkey": hex::encode(user.to_bytes()), - "user_leaf_hash_hex": hex::encode(recovery.to_byte_array()), - }), - ), - ) - .await - .unwrap(); - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - let body = axum::body::to_bytes(response.into_body(), usize::MAX) - .await - .unwrap(); - let body: serde_json::Value = serde_json::from_slice(&body).unwrap(); - assert!( - body["error"] - .as_str() - .unwrap() - .contains("exceeds maximum 10000000 milliweight") - ); - } - - #[test] - fn test_sign_pset_internal_invalid_hex() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (_, user_leaf_hash, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let request = SignPsetRequest { - pset_hex: "invalid_hex!!!".to_string(), - input_index: 0, - program: PROGRAM.to_string(), - witness: None, - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_pset_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Failed to decode PSET hex")); - } - - #[tokio::test] - async fn test_script_key_substitution_is_rejected() { - use super::super::tests::{key_script, script_key_program, tweaked_public_key}; - use super::super::tweak::JetEnvKind; - use simplicity::jet::Elements; - - let state = create_test_signer_state(); - let guardian = SecretKey::from_slice(&[0x11; 32]).unwrap(); - let attacker = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (program, witness) = script_key_program( - ElementsExtension::GetPubkeyFromScript, - ElementsExtension::Elements(Elements::Bip0340Verify), - &guardian, - ); - let (attacker_program, attacker_witness) = script_key_program( - ElementsExtension::GetPubkeyFromScript, - ElementsExtension::Elements(Elements::Bip0340Verify), - &attacker, - ); - assert_eq!(program, attacker_program); - let script = key_script(&state, &guardian); - let attacker_script = key_script(&state, &attacker); - let cosigner = - tweaked_public_key(&state, &program, Some(&script), JetEnvKind::Elements).await; - let (_, recovery, user) = create_test_p2tr_setup(&state, &attacker); - let (address, spend_info) = generate_p2tr_address_elements( - &cosigner, - &user, - recovery, - &elements::AddressParams::LIQUID_TESTNET, - ) - .unwrap(); - let tx = create_test_transaction(); - let pset = create_test_pset( - tx, - address.script_pubkey(), - elements::confidential::Value::Explicit(100_000), - ); - let request = |script: Option<&str>, witness: &str| { - let mut request = create_test_sign_request( - hex::encode(elements::encode::serialize(&pset)), - &user, - recovery, - ); - request.program = program.clone(); - request.witness = Some(witness.to_string()); - request.redeem_script_hex = script.map(str::to_string); - request - }; - let result = sign_pset_internal(&state, request(Some(&script), &witness)).unwrap(); - let signed: Pset = deserialize(&hex::decode(result.pset_hex).unwrap()).unwrap(); - let ((signing_key, _), signature) = signed.inputs()[0] - .tap_script_sigs - .first_key_value() - .unwrap(); - assert_eq!(*signing_key, cosigner.inner.x_only_public_key().0); - let (control, (leaf, _)) = signed.inputs()[0].tap_scripts.first_key_value().unwrap(); - assert!(control.verify_taproot_commitment(&*state.secp, &spend_info.output_key(), leaf,)); - let tx = pset.extract_tx().unwrap(); - let sighash = SighashCache::new(&tx) - .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[pset.inputs()[0].witness_utxo.clone().unwrap()]), - ScriptPath::with_defaults(leaf), - SchnorrSighashType::Default, - state.elements_network.genesis_hash(), - ) - .unwrap(); - state - .secp - .verify_schnorr( - &signature.sig, - &Message::from_digest(sighash.to_byte_array()), - signing_key, - ) - .unwrap(); - assert!( - sign_pset_internal(&state, request(Some(&script), &attacker_witness)) - .unwrap_err() - .contains("Simplicity execution failed") - ); - assert!( - sign_pset_internal(&state, request(Some(&attacker_script), &attacker_witness)) - .unwrap_err() - .contains("Spent output does not match") - ); - let changed_opcode = format!("51{}", &script[2..]); - assert!( - sign_pset_internal(&state, request(Some(&changed_opcode), &witness)) - .unwrap_err() - .contains("Spent output does not match") - ); - assert!(sign_pset_internal(&state, request(None, &attacker_witness)).is_err()); - } - - #[test] - fn test_legacy_cmr_only_output_is_rejected() { - let state = create_test_signer_state(); - let user_secret = SecretKey::from_slice(&[0xab; 32]).unwrap(); - let (_, recovery, user) = create_test_p2tr_setup(&state, &user_secret); - let cmr = Program::::from_str(PROGRAM, None) - .unwrap() - .cmr(); - let legacy = UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key).tap_tweak( - &*state.secp, - Some(TapNodeHash::from_byte_array(cmr.to_byte_array())), - ); - let (key, parity) = legacy.public_parts(); - let cosigner = PublicKey::new(key.into_inner().public_key(parity)); - let (address, _) = generate_p2tr_address_elements( - &cosigner, - &user, - recovery, - &elements::AddressParams::LIQUID_TESTNET, - ) - .unwrap(); - let pset = create_test_pset( - create_test_transaction(), - address.script_pubkey(), - elements::confidential::Value::Explicit(100_000), - ); - let request = create_test_sign_request( - hex::encode(elements::encode::serialize(&pset)), - &user, - recovery, - ); - assert!( - sign_pset_internal(&state, request) - .unwrap_err() - .contains("Spent output does not match") - ); - } - - #[test] - fn test_sign_pset_internal_invalid_pset_data() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (_, user_leaf_hash, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let request = SignPsetRequest { - pset_hex: hex::encode(b"not a valid pset"), - input_index: 0, - program: PROGRAM.to_string(), - witness: None, - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_pset_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Failed to deserialize PSET")); - } - - #[test] - fn test_sign_pset_internal_input_index_out_of_bounds() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, user_leaf_hash, user_pubkey) = - create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let value = elements::confidential::Value::Explicit(100_000); - let pset = create_test_pset(tx, address.script_pubkey(), value); - - let mut pset_bytes = Vec::new(); - elements::encode::Encodable::consensus_encode(&pset, &mut pset_bytes).unwrap(); - let pset_hex = hex::encode(pset_bytes); - - let request = SignPsetRequest { - pset_hex, - input_index: 99, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: hex::encode(user_leaf_hash.to_byte_array()), - }; - - let result = sign_pset_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("out of bounds")); - } - - #[test] - fn test_sign_pset_internal_invalid_leaf_hash() { - let state = create_test_signer_state(); - let user_secret_key = SecretKey::from_slice(&[0xab; 32]).expect("valid secret key"); - let (address, _, user_pubkey) = create_test_p2tr_setup(&state, &user_secret_key); - - let tx = create_test_transaction(); - let value = elements::confidential::Value::Explicit(100_000); - let pset = create_test_pset(tx, address.script_pubkey(), value); - - let mut pset_bytes = Vec::new(); - elements::encode::Encodable::consensus_encode(&pset, &mut pset_bytes).unwrap(); - let pset_hex = hex::encode(pset_bytes); - - let request = SignPsetRequest { - pset_hex, - input_index: 0, - program: PROGRAM.to_string(), - witness: Some("".to_string()), - user_pubkey: hex::encode(user_pubkey.to_bytes()), - redeem_script_hex: None, - user_leaf_hash_hex: "deadbeef".to_string(), // wrong length - }; - - let result = sign_pset_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("invalid slice length")); - } - - #[test] - fn test_signer_state_new_valid_key() { - let secret_key_hex = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"; - let result = SignerState::new( - secret_key_hex, - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_ok()); - } - - #[test] - fn test_signer_state_new_invalid_hex() { - let result = SignerState::new( - "not_valid_hex", - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Invalid private key hex")); - } - - #[test] - fn test_signer_state_new_invalid_key_length() { - let result = SignerState::new( - "cdcdcd", - ElementsNetwork::LiquidTestnet, - BitcoinNetwork::Testnet, - ); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Invalid private key")); - } -} diff --git a/service/src/handlers/tests.rs b/service/src/handlers/tests.rs new file mode 100644 index 0000000..3ff5f7b --- /dev/null +++ b/service/src/handlers/tests.rs @@ -0,0 +1,181 @@ +use std::str::FromStr; + +use super::*; +use axum::{ + body::Body, + http::{Request, StatusCode}, + response::Response, +}; +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use simplicity::{ConstructNode, node::CoreConstructible, types}; +use tower::ServiceExt; + +fn test_state() -> SignerState { + SignerState::new(&hex::encode([0xcd; 32]), BitcoinNetwork::Testnet4).unwrap() +} + +#[test] +fn disclosed_demo_identity_is_rejected_for_both_parities() { + for prefix in ["02", "03"] { + let public_key = PublicKey::from_str(&format!("{prefix}{DISCLOSED_DEMO_KEY}")).unwrap(); + assert!( + validate_signing_identity(public_key.x_only_public_key().0) + .unwrap_err() + .contains("publicly disclosed demo signing key") + ); + } + assert!(SignerState::new("", BitcoinNetwork::Testnet4).is_err()); + assert!(SignerState::new(&hex::encode([0; 32]), BitcoinNetwork::Testnet4).is_err()); +} + +pub(super) async fn post_json( + state: &SignerState, + path: &str, + body: serde_json::Value, +) -> Response { + routes(state.clone()) + .oneshot( + Request::post(path) + .header("content-type", "application/json") + .body(Body::from(body.to_string())) + .unwrap(), + ) + .await + .unwrap() +} + +pub(super) fn expensive_program() -> String { + let bytes = types::Context::with_context(|ctx| { + let mut node = Arc::::unit(&ctx); + for _ in 0..20 { + node = Arc::::comp(&node, &node).unwrap(); + } + node.finalize_types().unwrap().to_vec_without_witness() + }); + assert!(bytes.len() < 256); + STANDARD.encode(bytes) +} + +const POLICY_POST_PATHS: [&str; 4] = [ + "/simplicity-unchained/policy/bitcoin/tweak", + "/simplicity-unchained/policy/bitcoin/prepare", + "/simplicity-unchained/policy/bitcoin/evaluate", + "/simplicity-unchained/policy/bitcoin/sign", +]; + +#[tokio::test] +async fn legacy_routes_are_not_available_even_when_workers_are_busy() { + let state = test_state(); + let slots = [ + Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), + Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), + ]; + for path in [ + "/simplicity-unchained/tweak", + "/simplicity-unchained/sign/psbt", + "/simplicity-unchained/sign/pset", + ] { + for method in ["GET", "POST"] { + let response = routes(state.clone()) + .oneshot( + Request::builder() + .method(method) + .uri(path) + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::NOT_FOUND, "{method} {path}"); + } + } + drop(slots); +} + +#[tokio::test] +async fn cpu_admission_is_shared_and_precedes_json_extraction() { + let state = test_state(); + let slots = [ + Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), + Arc::clone(&state.work_slots).acquire_owned().await.unwrap(), + ]; + for path in POLICY_POST_PATHS { + let response = routes(state.clone()) + .oneshot(Request::post(path).body(Body::empty()).unwrap()) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE); + } + for (path, expected) in [ + ( + "/simplicity-unchained/policy/bitcoin/capabilities", + StatusCode::SERVICE_UNAVAILABLE, + ), + ("/simplicity-unchained/version", StatusCode::OK), + ] { + let response = routes(state.clone()) + .oneshot(Request::get(path).body(Body::empty()).unwrap()) + .await + .unwrap(); + assert_eq!(response.status(), expected); + } + drop(slots); + let response = routes(state.clone()) + .oneshot( + Request::get("/simplicity-unchained/policy/bitcoin/capabilities") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); +} + +#[tokio::test] +async fn malformed_requests_release_admission_slots() { + let state = test_state(); + for path in POLICY_POST_PATHS { + let response = post_json(&state, path, json!({})).await; + assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); + assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); + } +} + +#[tokio::test] +async fn stalled_body_hits_request_deadline_and_releases_slot() { + use axum::body::{Bytes, HttpBody}; + use std::{ + convert::Infallible, + pin::Pin, + task::{Context, Poll}, + time::Duration, + }; + + struct StalledBody; + impl HttpBody for StalledBody { + type Data = Bytes; + type Error = Infallible; + fn poll_frame( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + Poll::Pending + } + } + let state = test_state(); + let response = tokio::time::timeout( + Duration::from_secs(6), + routes(state.clone()).oneshot( + Request::post("/simplicity-unchained/policy/bitcoin/tweak") + .header("content-type", "application/json") + .body(Body::new(StalledBody)) + .unwrap(), + ), + ) + .await + .unwrap() + .unwrap(); + assert_eq!(response.status(), StatusCode::GATEWAY_TIMEOUT); + assert_eq!(state.work_slots.available_permits(), MAX_BLOCKING_JOBS); +} diff --git a/service/src/handlers/tweak.rs b/service/src/handlers/tweak.rs deleted file mode 100644 index 7c53fe8..0000000 --- a/service/src/handlers/tweak.rs +++ /dev/null @@ -1,293 +0,0 @@ -use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; - -use simplicity::{ - bitcoin::{self, secp256k1}, - elements::{self, schnorr::TapTweak}, -}; - -use bitcoin::hashes::Hash; -use elements::bitcoin::PublicKey; -use serde::{Deserialize, Serialize}; -use validator::Validate; - -use simplicity_unchained_core::jets::bitcoin::CoreExtension; -use simplicity_unchained_core::jets::elements::ElementsExtension; -use simplicity_unchained_core::program::Program; -use simplicity_unchained_core::utils::signing_commitment; - -use crate::handlers::{ErrorResponse, SignerState}; -use crate::validation; -use crate::work::WorkPermit; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum JetEnvKind { - Elements, - Bitcoin, -} - -#[derive(Debug, Deserialize, Validate)] -pub struct TweakRequest { - #[validate(length(min = 1))] - pub program: String, - pub jet_env: Option, - #[validate(custom(function = "validation::validate_redeem_script"))] - pub redeem_script_hex: Option, -} - -#[derive(Debug, Serialize)] -pub struct TweakResponse { - pub cmr_hex: String, - pub tweaked_public_key_hex: String, -} - -pub async fn get_tweaked_key( - State(state): State, - permit: WorkPermit, - Json(request): Json, -) -> impl IntoResponse { - let result = permit - .run(move || { - request - .validate() - .map_err(|e| format!("Validation failed: {}", e))?; - get_tweaked_key_internal(&state, request) - }) - .await; - match result { - Ok(Ok(response)) => { - log::info!( - "[200] Tweak successful: CMR {}, Tweaked Public Key {}", - response.cmr_hex, - response.tweaked_public_key_hex - ); - - (StatusCode::OK, Json(response)).into_response() - } - Ok(Err(e)) => { - log::error!("[400] Tweak error: {}", e); - - (StatusCode::BAD_REQUEST, Json(ErrorResponse { error: e })).into_response() - } - Err(e) => e.into_response(), - } -} - -fn get_tweaked_key_internal( - state: &SignerState, - request: TweakRequest, -) -> Result { - match &request.jet_env { - Some(JetEnvKind::Bitcoin) => get_tweaked_key_bitcoin(state, request), - _ => get_tweaked_key_elements(state, request), - } -} - -fn get_tweaked_key_bitcoin( - state: &SignerState, - request: TweakRequest, -) -> Result { - // Parse Simplicity program and get CMR from commitment - let cmr = { - let program = Program::::from_str(&request.program, None) - .map_err(|e| format!("Failed to parse program: {}", e))?; - - program.commit_prog().cmr() - }; - - let redeem_script = validation::decode_redeem_script(request.redeem_script_hex.as_deref())?; - let untweaked_keypair = - bitcoin::key::UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key); - - let tweaked_keypair = bitcoin::key::TapTweak::tap_tweak( - untweaked_keypair, - &*state.secp, - Some(bitcoin::taproot::TapNodeHash::from_byte_array( - signing_commitment(cmr, redeem_script.as_bytes()), - )), - ); - - let (tweaked_public_key, tweaked_parity) = tweaked_keypair.public_parts(); - let public_key = PublicKey::new(secp256k1::PublicKey::from_x_only_public_key( - tweaked_public_key.to_x_only_public_key(), - tweaked_parity, - )); - - Ok(TweakResponse { - cmr_hex: hex::encode(cmr.to_byte_array()), - tweaked_public_key_hex: hex::encode(public_key.to_bytes()), - }) -} - -fn get_tweaked_key_elements( - state: &SignerState, - request: TweakRequest, -) -> Result { - // Parse Simplicity program and get CMR from commitment - let cmr = { - let program = Program::::from_str(&request.program, None) - .map_err(|e| format!("Failed to parse program: {}", e))?; - - program.commit_prog().cmr() - }; - - let redeem_script = validation::decode_redeem_script(request.redeem_script_hex.as_deref())?; - let untweaked_keypair = - elements::schnorr::UntweakedKeypair::from_secret_key(&*state.secp, &state.secret_key); - - let tweaked_keypair = untweaked_keypair.tap_tweak( - &*state.secp, - Some(elements::taproot::TapNodeHash::from_byte_array( - signing_commitment(cmr, redeem_script.as_bytes()), - )), - ); - - let (tweaked_public_key, tweaked_parity) = tweaked_keypair.public_parts(); - let public_key = PublicKey::new(secp256k1::PublicKey::from_x_only_public_key( - tweaked_public_key.into_inner(), - tweaked_parity, - )); - - Ok(TweakResponse { - cmr_hex: hex::encode(cmr.to_byte_array()), - tweaked_public_key_hex: hex::encode(public_key.to_bytes()), - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use elements::secp256k1_zkp::{Secp256k1, SecretKey}; - use std::sync::Arc; - - use simplicity_unchained_core::{BitcoinNetwork, ElementsNetwork}; - - fn create_test_signer_state() -> SignerState { - let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("valid secret key"); - SignerState { - secret_key, - secp: Arc::new(Secp256k1::new()), - elements_network: ElementsNetwork::LiquidTestnet, - bitcoin_network: BitcoinNetwork::Testnet, - work_slots: Arc::new(tokio::sync::Semaphore::new(crate::work::MAX_BLOCKING_JOBS)), - } - } - - #[test] - fn test_get_tweaked_key_internal_success() { - let state = create_test_signer_state(); - - let request = TweakRequest { - program: "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA".to_string(), - jet_env: None, - redeem_script_hex: None, - }; - - let result = get_tweaked_key_internal(&state, request); - assert!(result.is_ok()); - - let response = result.unwrap(); - assert!(!response.cmr_hex.is_empty()); - assert!(!response.tweaked_public_key_hex.is_empty()); - - // Verify CMR is 32 bytes (64 hex chars) - assert_eq!(response.cmr_hex.len(), 64); - - // Verify public key is 33 bytes (66 hex chars) for compressed key - assert_eq!(response.tweaked_public_key_hex.len(), 66); - } - - #[test] - fn test_get_tweaked_key_internal_invalid_program() { - let state = create_test_signer_state(); - - let request = TweakRequest { - program: "invalid_program".to_string(), - jet_env: None, - redeem_script_hex: None, - }; - - let result = get_tweaked_key_internal(&state, request); - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Failed to parse program")); - } - - #[test] - fn test_tweak_request_validation() { - // Valid request - let valid_request = TweakRequest { - program: "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA".to_string(), - jet_env: None, - redeem_script_hex: None, - }; - assert!(valid_request.validate().is_ok()); - - // Empty program should fail - let invalid_request = TweakRequest { - program: "".to_string(), - jet_env: None, - redeem_script_hex: None, - }; - assert!(invalid_request.validate().is_err()); - } - - #[test] - fn test_consistent_cmr_and_key_with_sign() { - // This test ensures the tweak endpoint produces the same CMR and key - // as the sign endpoint would use internally - let state = create_test_signer_state(); - - let program = "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA".to_string(); - - let request = TweakRequest { - program: program.clone(), - jet_env: None, - redeem_script_hex: None, - }; - - let result = get_tweaked_key_internal(&state, request).unwrap(); - - // Decode and verify the CMR - let cmr_bytes = hex::decode(&result.cmr_hex).unwrap(); - assert_eq!(cmr_bytes.len(), 32); - - // Decode and verify the public key - let pubkey_bytes = hex::decode(&result.tweaked_public_key_hex).unwrap(); - assert_eq!(pubkey_bytes.len(), 33); // Compressed public key - } - - #[test] - fn test_tweak_binds_script_in_both_environments() { - let state = create_test_signer_state(); - for bitcoin in [false, true] { - let request = |script: Option<&str>| TweakRequest { - program: "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA".to_string(), - jet_env: Some(if bitcoin { - JetEnvKind::Bitcoin - } else { - JetEnvKind::Elements - }), - redeem_script_hex: script.map(str::to_string), - }; - let empty = get_tweaked_key_internal(&state, request(None)).unwrap(); - let explicit_empty = get_tweaked_key_internal(&state, request(Some(""))).unwrap(); - assert_eq!( - empty.tweaked_public_key_hex, - explicit_empty.tweaked_public_key_hex - ); - let first = get_tweaked_key_internal(&state, request(Some("52"))).unwrap(); - let second = get_tweaked_key_internal(&state, request(Some("51"))).unwrap(); - assert_eq!(first.cmr_hex, second.cmr_hex); - assert_eq!(first.cmr_hex, empty.cmr_hex); - assert_ne!(first.tweaked_public_key_hex, second.tweaked_public_key_hex); - assert_ne!(first.tweaked_public_key_hex, empty.tweaked_public_key_hex); - assert!(request(Some("21")).validate().is_err()); - assert!(request(Some("zz")).validate().is_err()); - assert!( - get_tweaked_key_internal(&state, request(Some("zz"))) - .unwrap_err() - .contains("Failed to decode redeem script hex") - ); - } - } -} diff --git a/service/src/main.rs b/service/src/main.rs index 0caa8db..2babce9 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -1,13 +1,12 @@ mod cli; mod config; mod handlers; -mod validation; mod work; use cli::{Cli, Commands}; use config::Config; -use simplicity_unchained_core::{BitcoinNetwork, ElementsNetwork}; +use simplicity_unchained_core::BitcoinNetwork; use std::path::PathBuf; use std::process::ExitCode; use std::str::FromStr; @@ -43,18 +42,16 @@ async fn start(config_path: PathBuf) -> Result<(), String> { let config = Config::from_file(config_path).map_err(|e| format!("Failed to load config: {}", e))?; - let elements_network = ElementsNetwork::from_str(&config.service.elements_network) - .map_err(|e| format!("Failed to parse Elements network: {}", e))?; - let bitcoin_network = BitcoinNetwork::from_str(&config.service.bitcoin_network) .map_err(|e| format!("Failed to parse Bitcoin network: {}", e))?; - let signer_state = SignerState::new( - &config.service.private_key, - elements_network, - bitcoin_network, - ) - .map_err(|e| format!("Failed to initialize signer state: {}", e))?; + let mut signer_state = SignerState::new(&config.service.private_key, bitcoin_network) + .map_err(|e| format!("Failed to initialize signer state: {}", e))?; + signer_state.liquid_network = config + .service + .liquid_network + .parse() + .map_err(|e| format!("Failed to parse Liquid policy network: {e}"))?; let app = Router::new().merge(handlers::routes(signer_state)); diff --git a/service/src/validation.rs b/service/src/validation.rs deleted file mode 100644 index 3b0cd29..0000000 --- a/service/src/validation.rs +++ /dev/null @@ -1,34 +0,0 @@ -use simplicity::bitcoin; -use validator::ValidationError; - -pub fn validate_hex(hex: &str) -> Result<(), ValidationError> { - if hex.trim().is_empty() { - return Err(ValidationError::new("hex_empty")); - } - if !hex.len().is_multiple_of(2) { - return Err(ValidationError::new("hex_odd_length")); - } - if !hex.chars().all(|c| c.is_ascii_hexdigit()) { - return Err(ValidationError::new("hex_invalid_chars")); - } - Ok(()) -} -pub fn validate_redeem_script(value: &str) -> Result<(), ValidationError> { - let bytes = hex::decode(value).map_err(|_| ValidationError::new("invalid hex"))?; - - let script = bitcoin::script::ScriptBuf::from(bytes); - for instruction in script.instructions() { - instruction.map_err(|_| ValidationError::new("invalid script encoding"))?; - } - - Ok(()) -} - -pub fn decode_redeem_script(value: Option<&str>) -> Result { - let bytes = value - .map(hex::decode) - .transpose() - .map_err(|e| format!("Failed to decode redeem script hex: {}", e))? - .unwrap_or_default(); - Ok(simplicity::elements::Script::from(bytes)) -} diff --git a/service/tests/startup.rs b/service/tests/startup.rs index fcaccdc..bdefd37 100644 --- a/service/tests/startup.rs +++ b/service/tests/startup.rs @@ -69,7 +69,7 @@ impl Drop for ServiceProcess { fn configuration(key: &str, port: u16) -> String { format!( "[service]\nport = {port}\nprivate_key = \"{key}\"\n\ - elements_network = \"liquidtestnet\"\nbitcoin_network = \"testnet4\"\n" + bitcoin_network = \"testnet4\"\n" ) } @@ -79,9 +79,13 @@ fn write_configuration(directory: &Path, contents: &str) -> NamedTempFile { file } +fn test_directory() -> TempDir { + TempDir::new_in(std::env::current_dir().unwrap()).unwrap() +} + #[test] fn missing_configuration_exits_unsuccessfully() { - let directory = TempDir::new().unwrap(); + let directory = test_directory(); ServiceProcess::spawn(directory.path(), None).assert_failure("Failed to load config"); ServiceProcess::spawn( directory.path(), @@ -94,7 +98,7 @@ fn missing_configuration_exits_unsuccessfully() { #[test] fn missing_empty_and_invalid_keys_fail_closed() { - let directory = TempDir::new().unwrap(); + let directory = test_directory(); for (key, error) in [ ("".to_string(), "Invalid private key"), ("not-hex".to_string(), "Invalid private key hex"), @@ -106,7 +110,7 @@ fn missing_empty_and_invalid_keys_fail_closed() { } let file = write_configuration( directory.path(), - "[service]\nport = 0\nelements_network = \"liquidtestnet\"\nbitcoin_network = \"testnet4\"\n", + "[service]\nport = 0\nbitcoin_network = \"testnet4\"\n", ); ServiceProcess::spawn(directory.path(), Some(file.path())) .assert_failure("failed to parse config"); @@ -114,7 +118,7 @@ fn missing_empty_and_invalid_keys_fail_closed() { #[test] fn malformed_configuration_does_not_log_private_key_contents() { - let directory = TempDir::new().unwrap(); + let directory = test_directory(); let marker = "must-not-appear-in-logs"; let file = write_configuration( directory.path(), @@ -129,25 +133,33 @@ fn malformed_configuration_does_not_log_private_key_contents() { } #[test] -fn invalid_networks_exit_unsuccessfully() { - let directory = TempDir::new().unwrap(); +fn invalid_bitcoin_network_exits_unsuccessfully() { + let directory = test_directory(); let (secret_key, _) = generate_keypair(); let config = configuration(&hex::encode(secret_key.secret_bytes()), 0); - for (network, error) in [ - ("liquidtestnet", "Failed to parse Elements network"), - ("testnet4", "Failed to parse Bitcoin network"), - ] { - let file = write_configuration( - directory.path(), - &config.replace(network, "invalid-network"), - ); - ServiceProcess::spawn(directory.path(), Some(file.path())).assert_failure(error); - } + let file = write_configuration( + directory.path(), + &config.replace("testnet4", "invalid-network"), + ); + ServiceProcess::spawn(directory.path(), Some(file.path())) + .assert_failure("Failed to parse Bitcoin network"); +} + +#[test] +fn legacy_elements_field_cannot_replace_explicit_bitcoin_network() { + let directory = test_directory(); + let config = format!( + "{}elements_network = \"liquidtestnet\"\n", + configuration(&hex::encode([0xcd; 32]), 0).replace("bitcoin_network = \"testnet4\"\n", ""), + ); + let file = write_configuration(directory.path(), &config); + ServiceProcess::spawn(directory.path(), Some(file.path())) + .assert_failure("failed to parse config"); } #[test] fn occupied_port_exits_unsuccessfully() { - let directory = TempDir::new().unwrap(); + let directory = test_directory(); let listener = TcpListener::bind("0.0.0.0:0").unwrap(); let (secret_key, _) = generate_keypair(); let file = write_configuration( @@ -162,13 +174,16 @@ fn occupied_port_exits_unsuccessfully() { #[test] fn explicit_read_only_configuration_starts_and_serves_requests() { - let directory = TempDir::new().unwrap(); + let directory = test_directory(); let reservation = TcpListener::bind("127.0.0.1:0").unwrap(); let address = reservation.local_addr().unwrap(); let (secret_key, _) = generate_keypair(); let file = write_configuration( directory.path(), - &configuration(&hex::encode(secret_key.secret_bytes()), address.port()), + &format!( + "{}elements_network = \"liquidtestnet\"\n", + configuration(&hex::encode(secret_key.secret_bytes()), address.port()), + ), ); let mut permissions = fs::metadata(file.path()).unwrap().permissions(); permissions.set_readonly(true); @@ -203,7 +218,7 @@ fn explicit_read_only_configuration_starts_and_serves_requests() { fn shipped_example_has_no_usable_key() { let example: toml::Value = toml::from_str(include_str!("../config.example.toml")).unwrap(); assert_eq!(example["service"]["private_key"].as_str(), Some("")); - let directory = TempDir::new().unwrap(); + let directory = test_directory(); let file = write_configuration(directory.path(), include_str!("../config.example.toml")); ServiceProcess::spawn(directory.path(), Some(file.path())) .assert_failure("Invalid private key");