diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5af4ce3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,120 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.39.0 + override: true + + # cargo fmt does not build the code, and running it in a fresh clone of + # the codebase will fail because the protobuf code has not been generated. + - name: cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --all + + # Ensure all code has been formatted with rustfmt + - run: rustup component add rustfmt + - name: Check formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check --color always + + # Build benchmarks to prevent bitrot + - name: Build benchmarks + uses: actions-rs/cargo@v1 + with: + command: build + args: --all --benches + + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.39.0 + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build tests + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --all --tests + - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release --all + - name: Run slow tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release --all -- --ignored + + codecov: + name: Code coverage + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + # Use stable for this to ensure that cargo-tarpaulin can be built. + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install cargo-tarpaulin + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-tarpaulin + - name: Generate coverage report + uses: actions-rs/cargo@v1 + with: + command: tarpaulin + args: --release --timeout 600 --out Xml --packages "zcash_client_backend,zcash_primitives,zcash_proofs" + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1.0.3 + with: + token: ${{secrets.CODECOV_TOKEN}} + + doc-links: + name: Nightly lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + + # Ensure intra-documentation links all resolve correctly + # Requires #![deny(intra_doc_link_resolution_failure)] in crates. + - name: Check intra-doc links + uses: actions-rs/cargo@v1 + with: + command: doc + args: --all --document-private-items diff --git a/.gitignore b/.gitignore index eb5a316..fa8d85a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +Cargo.lock target diff --git a/.travis.yml b/.travis.yml index 4f444f7..c72494c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.36.0 + - 1.39.0 cache: cargo @@ -11,3 +11,9 @@ script: - cargo build --verbose --release --all - cargo fmt --all -- --check - cargo test --verbose --release --all + - cargo test --verbose --release --all -- --ignored + +before_cache: + - rm -rf "$TRAVIS_HOME/.cargo/registry/src" + - cargo install cargo-update || echo "cargo-update already installed" + - cargo install-update -a # update outdated cached binaries diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 61d0316..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,839 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aes" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bech32" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bellman" -version = "0.1.0" -dependencies = [ - "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "group 0.1.0", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.14.2", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit-vec" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2b_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "blake2s_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bs58" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "c2-chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "constant_time_eq" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "directories" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ff" -version = "0.4.0" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff_derive 0.3.0", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ff_derive" -version = "0.3.0" -dependencies = [ - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fpe" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "group" -version = "0.1.0" -dependencies = [ - "ff 0.4.0", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.62" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "librustzcash" -version = "0.1.0" -dependencies = [ - "bellman 0.1.0", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.14.2", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_primitives 0.0.0", - "zcash_proofs 0.0.0", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-bigint" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pairing" -version = "0.14.2" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "group 0.1.0", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pkg-config" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ppv-lite86" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro-hack" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "protobuf" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "protobuf-codegen" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "protobuf-codegen-pure" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ripemd160" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rusqlite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "secp256k1" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "subtle" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tempfile" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vcpkg" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zcash_client_backend" -version = "0.0.0" -dependencies = [ - "bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.14.2", - "protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen-pure 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_primitives 0.0.0", -] - -[[package]] -name = "zcash_client_sqlite" -version = "0.0.0" -dependencies = [ - "bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "pairing 0.14.2", - "protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_client_backend 0.0.0", - "zcash_primitives 0.0.0", - "zcash_proofs 0.0.0", -] - -[[package]] -name = "zcash_primitives" -version = "0.0.0" -dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.14.2", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zcash_proofs" -version = "0.0.0" -dependencies = [ - "bellman 0.1.0", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.4.0", - "pairing 0.14.2", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_primitives 0.0.0", -] - -[metadata] -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2" -"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" -"checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c95ee6bba9d950218b6cc910cf62bc9e0a171d0f4537e3627b0f54d08549b188" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "a61c7bce55cd2fae6ec8cb935ebd76256c2959a1f95790f6118a441c2cd5b406" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" -"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" -"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" -"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -"checksum fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" -"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" -"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b" -"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8aefcec9f142b524d98fc81d07827743be89dd6586a1ba6ab21fa66a500b3fa5" -"checksum protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31539be8028d6b9e8e1b3b7c74e2fa3555302e27b2cc20dbaee6ffba648f75e2" -"checksum protobuf-codegen-pure 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00993dc5fbbfcf9d8a005f6b6c29fd29fd6d86deba3ae3f41fd20c624c414616" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" -"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" -"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" -"checksum secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0344a794ff109f85547039536028e12f313178ac1545e49fdf16a530d900a7b" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082" -"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" -"checksum wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd5442abcac6525a045cc8c795aedb60da7a2e5e89c7bf18a0d5357849bb23c7" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 0eba4cb..dd595b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,13 @@ members = [ "bellman", "ff", "group", - "librustzcash", "pairing", "zcash_client_backend", - "zcash_client_sqlite", + "zcash_history", "zcash_primitives", "zcash_proofs", + "jubjub", + "bls12_381", ] [profile.release] diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml index 70521e7..b056b6c 100644 --- a/bellman/Cargo.toml +++ b/bellman/Cargo.toml @@ -1,28 +1,30 @@ [package] authors = ["Sean Bowe "] description = "zk-SNARK library" -documentation = "https://github.com/ebfull/bellman" +readme = "README.md" homepage = "https://github.com/ebfull/bellman" license = "MIT/Apache-2.0" name = "bellman" repository = "https://github.com/ebfull/bellman" -version = "0.1.0" +version = "0.6.0" +edition = "2018" [dependencies] bit-vec = "0.4.4" blake2s_simd = "0.5" -ff = { path = "../ff" } +ff = { version = "0.6", path = "../ff" } futures = "0.1" futures-cpupool = { version = "0.1", optional = true } -group = { path = "../group" } +group = { version = "0.6", path = "../group" } num_cpus = { version = "1", optional = true } -crossbeam = { version = "0.3", optional = true } -pairing = { path = "../pairing", optional = true } +crossbeam = { version = "0.7", optional = true } +pairing = { version = "0.16", path = "../pairing", optional = true } rand_core = "0.5" byteorder = "1" +subtle = "2.2.1" [dev-dependencies] -hex-literal = "0.1" +hex-literal = "0.2" rand = "0.7" rand_xorshift = "0.2" sha2 = "0.8" @@ -36,3 +38,6 @@ default = ["groth16", "multicore"] name = "mimc" path = "tests/mimc.rs" required-features = ["groth16"] + +[badges] +maintenance = { status = "actively-developed" } diff --git a/bellman/README.md b/bellman/README.md index 659a81c..d64dd9c 100644 --- a/bellman/README.md +++ b/bellman/README.md @@ -1,12 +1,23 @@ # bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # -This is a research project being built for [Zcash](https://z.cash/). +`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits +and primitive structures, as well as basic gadget implementations such as +booleans and number abstractions. + +## Roadmap + +`bellman` is being refactored into a generic proving library. Currently it is +pairing-specific, and different types of proving systems need to be implemented +as sub-modules. After the refactor, `bellman` will be generic using the `ff` and +`group` crates, while specific proving systems will be separate crates that pull +in the dependencies they require. ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 808d2af..be97c20 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -1,17 +1,19 @@ -//! This module contains an `EvaluationDomain` abstraction for -//! performing various kinds of polynomial arithmetic on top of -//! the scalar field. +//! This module contains an [`EvaluationDomain`] abstraction for performing +//! various kinds of polynomial arithmetic on top of the scalar field. //! -//! In pairing-based SNARKs like Groth16, we need to calculate -//! a quotient polynomial over a target polynomial with roots -//! at distinct points associated with each constraint of the -//! constraint system. In order to be efficient, we choose these -//! roots to be the powers of a 2^n root of unity in the field. -//! This allows us to perform polynomial operations in O(n) -//! by performing an O(n log n) FFT over such a domain. +//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient +//! polynomial over a target polynomial with roots at distinct points associated +//! with each constraint of the constraint system. In order to be efficient, we +//! choose these roots to be the powers of a 2n root of unity in the +//! field. This allows us to perform polynomial operations in O(n) by performing +//! an O(n log n) FFT over such a domain. +//! +//! [`EvaluationDomain`]: crate::domain::EvaluationDomain +//! [Groth16]: https://eprint.iacr.org/2016/260 use ff::{Field, PrimeField, ScalarEngine}; use group::CurveProjective; +use std::ops::{AddAssign, MulAssign, SubAssign}; use super::SynthesisError; @@ -26,15 +28,19 @@ pub struct EvaluationDomain> { minv: E::Fr, } -impl> EvaluationDomain { - pub fn as_ref(&self) -> &[G] { +impl> AsRef<[G]> for EvaluationDomain { + fn as_ref(&self) -> &[G] { &self.coeffs } +} - pub fn as_mut(&mut self) -> &mut [G] { +impl> AsMut<[G]> for EvaluationDomain { + fn as_mut(&mut self) -> &mut [G] { &mut self.coeffs } +} +impl> EvaluationDomain { pub fn into_coeffs(self) -> Vec { self.coeffs } @@ -57,21 +63,21 @@ impl> EvaluationDomain { // Compute omega, the 2^exp primitive root of unity let mut omega = E::Fr::root_of_unity(); for _ in exp..E::Fr::S { - omega.square(); + omega = omega.square(); } // Extend the coeffs vector with zeroes if necessary coeffs.resize(m, G::group_zero()); Ok(EvaluationDomain { - coeffs: coeffs, - exp: exp, - omega: omega, - omegainv: omega.inverse().unwrap(), - geninv: E::Fr::multiplicative_generator().inverse().unwrap(), + coeffs, + exp, + omega, + omegainv: omega.invert().unwrap(), + geninv: E::Fr::multiplicative_generator().invert().unwrap(), minv: E::Fr::from_str(&format!("{}", m)) .unwrap() - .inverse() + .invert() .unwrap(), }) } @@ -87,7 +93,7 @@ impl> EvaluationDomain { let minv = self.minv; for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move || { + scope.spawn(move |_scope| { for v in v { v.group_mul_assign(&minv); } @@ -99,8 +105,8 @@ impl> EvaluationDomain { pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) { worker.scope(self.coeffs.len(), |scope, chunk| { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { - scope.spawn(move || { - let mut u = g.pow(&[(i * chunk) as u64]); + scope.spawn(move |_scope| { + let mut u = g.pow_vartime(&[(i * chunk) as u64]); for v in v.iter_mut() { v.group_mul_assign(&u); u.mul_assign(&g); @@ -125,7 +131,7 @@ impl> EvaluationDomain { /// This evaluates t(tau) for this domain, which is /// tau^m - 1 for these radix-2 domains. pub fn z(&self, tau: &E::Fr) -> E::Fr { - let mut tmp = tau.pow(&[self.coeffs.len() as u64]); + let mut tmp = tau.pow_vartime(&[self.coeffs.len() as u64]); tmp.sub_assign(&E::Fr::one()); tmp @@ -135,14 +141,11 @@ impl> EvaluationDomain { /// evaluation domain, so we must perform division over /// a coset. pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { - let i = self - .z(&E::Fr::multiplicative_generator()) - .inverse() - .unwrap(); + let i = self.z(&E::Fr::multiplicative_generator()).invert().unwrap(); worker.scope(self.coeffs.len(), |scope, chunk| { for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move || { + scope.spawn(move |_scope| { for v in v { v.group_mul_assign(&i); } @@ -161,7 +164,7 @@ impl> EvaluationDomain { .chunks_mut(chunk) .zip(other.coeffs.chunks(chunk)) { - scope.spawn(move || { + scope.spawn(move |_scope| { for (a, b) in a.iter_mut().zip(b.iter()) { a.group_mul_assign(&b.0); } @@ -180,7 +183,7 @@ impl> EvaluationDomain { .chunks_mut(chunk) .zip(other.coeffs.chunks(chunk)) { - scope.spawn(move || { + scope.spawn(move |_scope| { for (a, b) in a.iter_mut().zip(b.iter()) { a.group_sub_assign(&b); } @@ -218,7 +221,7 @@ impl Group for Point { Point(G::zero()) } fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by.into_repr()); + self.0.mul_assign(by.to_repr()); } fn group_add_assign(&mut self, other: &Self) { self.0.add_assign(&other.0); @@ -291,7 +294,7 @@ fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u let mut m = 1; for _ in 0..log_n { - let w_m = omega.pow(&[(n / (2 * m)) as u64]); + let w_m = omega.pow_vartime(&[u64::from(n / (2 * m))]); let mut k = 0; while k < n { @@ -325,24 +328,24 @@ fn parallel_fft>( let num_cpus = 1 << log_cpus; let log_new_n = log_n - log_cpus; let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; - let new_omega = omega.pow(&[num_cpus as u64]); + let new_omega = omega.pow_vartime(&[num_cpus as u64]); worker.scope(0, |scope, _| { let a = &*a; for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move || { + scope.spawn(move |_scope| { // Shuffle into a sub-FFT - let omega_j = omega.pow(&[j as u64]); - let omega_step = omega.pow(&[(j as u64) << log_new_n]); + let omega_j = omega.pow_vartime(&[j as u64]); + let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]); let mut elt = E::Fr::one(); - for i in 0..(1 << log_new_n) { + for (i, tmp) in tmp.iter_mut().enumerate() { for s in 0..num_cpus { let idx = (i + (s << log_new_n)) % (1 << log_n); let mut t = a[idx]; t.group_mul_assign(&elt); - tmp[i].group_add_assign(&t); + tmp.group_add_assign(&t); elt.mul_assign(&omega_step); } elt.mul_assign(&omega_j); @@ -359,7 +362,7 @@ fn parallel_fft>( let tmp = &tmp; for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move || { + scope.spawn(move |_scope| { let mut idx = idx * chunk; let mask = (1 << log_cpus) - 1; for a in a { diff --git a/bellman/src/gadgets.rs b/bellman/src/gadgets.rs index cf366df..b0ce734 100644 --- a/bellman/src/gadgets.rs +++ b/bellman/src/gadgets.rs @@ -1,3 +1,5 @@ +//! Self-contained sub-circuit implementations for various primitives. + pub mod test; pub mod blake2s; diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs index c5cee23..79c42e4 100644 --- a/bellman/src/gadgets/blake2s.rs +++ b/bellman/src/gadgets/blake2s.rs @@ -1,12 +1,10 @@ -use pairing::Engine; +//! The [BLAKE2s] hash function with personalization support. +//! +//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693 +use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32}; use crate::{ConstraintSystem, SynthesisError}; - -use super::boolean::Boolean; - -use super::uint32::UInt32; - -use super::multieq::MultiEq; +use ff::ScalarEngine; /* 2.1. Parameters @@ -81,7 +79,7 @@ const SIGMA: [[usize; 16]; 10] = [ END FUNCTION. */ -fn mixing_g, M>( +fn mixing_g, M>( mut cs: M, v: &mut [UInt32], a: usize, @@ -166,7 +164,7 @@ where END FUNCTION. */ -fn blake2s_compression>( +fn blake2s_compression>( mut cs: CS, h: &mut [UInt32], m: &[UInt32], @@ -339,7 +337,7 @@ fn blake2s_compression>( END FUNCTION. */ -pub fn blake2s>( +pub fn blake2s>( mut cs: CS, input: &[Boolean], personalization: &[u8], @@ -382,7 +380,7 @@ pub fn blake2s>( blocks.push(this_block); } - if blocks.len() == 0 { + if blocks.is_empty() { blocks.push((0..16).map(|_| UInt32::constant(0)).collect()); } @@ -404,12 +402,13 @@ pub fn blake2s>( )?; } - Ok(h.iter().flat_map(|b| b.into_bits()).collect()) + Ok(h.into_iter().flat_map(|b| b.into_bits()).collect()) } #[cfg(test)] mod test { use blake2s_simd::Params as Blake2sParams; + use hex_literal::hex; use pairing::bls12_381::Bls12; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -433,7 +432,7 @@ mod test { let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); let mut out = out.into_iter(); - for b in expected.into_iter() { + for b in expected.iter() { for i in 0..8 { let c = out.next().unwrap().get_value().unwrap(); @@ -554,4 +553,145 @@ mod test { } } } + + #[test] + fn test_blake2s_256_vars() { + let data: Vec = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); + assert_eq!(data.len(), 256); + + let mut cs = TestConstraintSystem::::new(); + + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in 0..8 { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push( + AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) + .unwrap() + .into(), + ); + } + } + + let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + + assert!(cs.is_satisfied()); + + let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e"); + + let mut out = r.into_iter(); + for b in expected.iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_blake2s_700_vars() { + let data: Vec = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); + assert_eq!(data.len(), 700); + + let mut cs = TestConstraintSystem::::new(); + + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in 0..8 { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push( + AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) + .unwrap() + .into(), + ); + } + } + + let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + + assert!(cs.is_satisfied()); + + let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a"); + + let mut out = r.into_iter(); + for b in expected.iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_blake2s_test_vectors() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + + let expecteds = [ + hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"), + hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"), + ]; + for i in 0..2 { + let mut h = Blake2sParams::new() + .hash_length(32) + .personal(b"12345678") + .to_state(); + let input_len = 1024; + let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); + + h.update(&data); + + let hash_result = h.finalize(); + + let mut cs = TestConstraintSystem::::new(); + + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in 0..8 { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push( + AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) + .unwrap() + .into(), + ); + } + } + + let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + + assert!(cs.is_satisfied()); + + let mut s = hash_result + .as_ref() + .iter() + .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); + + for b in r { + match b { + Boolean::Is(b) => { + assert!(s.next().unwrap() == b.get_value().unwrap()); + } + Boolean::Not(b) => { + assert!(s.next().unwrap() != b.get_value().unwrap()); + } + Boolean::Constant(b) => { + assert!(input_len == 0); + assert!(s.next().unwrap() == b); + } + } + } + + assert_eq!(expecteds[i], hash_result.as_bytes()); + } + } } diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs index 414b290..b521e7b 100644 --- a/bellman/src/gadgets/boolean.rs +++ b/bellman/src/gadgets/boolean.rs @@ -1,5 +1,6 @@ -use ff::{BitIterator, Field, PrimeField}; -use pairing::Engine; +//! Gadgets for allocating bits in the circuit and performing boolean logic. + +use ff::{BitIterator, Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; @@ -31,7 +32,7 @@ impl AllocatedBit { must_be_false: &AllocatedBit, ) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let var = cs.alloc( @@ -60,7 +61,7 @@ impl AllocatedBit { Ok(AllocatedBit { variable: var, - value: value, + value, }) } @@ -68,7 +69,7 @@ impl AllocatedBit { /// boolean value. pub fn alloc(mut cs: CS, value: Option) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let var = cs.alloc( @@ -93,7 +94,7 @@ impl AllocatedBit { Ok(AllocatedBit { variable: var, - value: value, + value, }) } @@ -101,7 +102,7 @@ impl AllocatedBit { /// an `AllocatedBit`. pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let mut result_value = None; @@ -153,7 +154,7 @@ impl AllocatedBit { /// an `AllocatedBit`. pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let mut result_value = None; @@ -191,7 +192,7 @@ impl AllocatedBit { /// Calculates `a AND (NOT b)`. pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let mut result_value = None; @@ -229,7 +230,7 @@ impl AllocatedBit { /// Calculates `(NOT a) AND (NOT b)`. pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let mut result_value = None; @@ -265,7 +266,7 @@ impl AllocatedBit { } } -pub fn u64_into_boolean_vec_le>( +pub fn u64_into_boolean_vec_le>( mut cs: CS, value: Option, ) -> Result, SynthesisError> { @@ -296,28 +297,28 @@ pub fn u64_into_boolean_vec_le>( Ok(bits) } -pub fn field_into_boolean_vec_le, F: PrimeField>( +pub fn field_into_boolean_vec_le, F: PrimeField>( cs: CS, value: Option, ) -> Result, SynthesisError> { let v = field_into_allocated_bits_le::(cs, value)?; - Ok(v.into_iter().map(|e| Boolean::from(e)).collect()) + Ok(v.into_iter().map(Boolean::from).collect()) } -pub fn field_into_allocated_bits_le, F: PrimeField>( +pub fn field_into_allocated_bits_le, F: PrimeField>( mut cs: CS, value: Option, ) -> Result, SynthesisError> { // Deconstruct in big-endian bit order let values = match value { Some(ref value) => { - let mut field_char = BitIterator::new(F::char()); + let mut field_char = BitIterator::::new(F::char()); let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); let mut found_one = false; - for b in BitIterator::new(value.into_repr()) { + for b in BitIterator::::new(value.to_repr()) { // Skip leading bits found_one |= field_char.next().unwrap(); if !found_one { @@ -367,7 +368,7 @@ impl Boolean { pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { match (a, b) { @@ -412,24 +413,24 @@ impl Boolean { } pub fn get_value(&self) -> Option { - match self { - &Boolean::Constant(c) => Some(c), - &Boolean::Is(ref v) => v.get_value(), - &Boolean::Not(ref v) => v.get_value().map(|b| !b), + match *self { + Boolean::Constant(c) => Some(c), + Boolean::Is(ref v) => v.get_value(), + Boolean::Not(ref v) => v.get_value().map(|b| !b), } } - pub fn lc(&self, one: Variable, coeff: E::Fr) -> LinearCombination { - match self { - &Boolean::Constant(c) => { + pub fn lc(&self, one: Variable, coeff: E::Fr) -> LinearCombination { + match *self { + Boolean::Constant(c) => { if c { LinearCombination::::zero() + (coeff, one) } else { LinearCombination::::zero() } } - &Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), - &Boolean::Not(ref v) => { + Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), + Boolean::Not(ref v) => { LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) } } @@ -442,17 +443,17 @@ impl Boolean { /// Return a negated interpretation of this boolean. pub fn not(&self) -> Self { - match self { - &Boolean::Constant(c) => Boolean::Constant(!c), - &Boolean::Is(ref v) => Boolean::Not(v.clone()), - &Boolean::Not(ref v) => Boolean::Is(v.clone()), + match *self { + Boolean::Constant(c) => Boolean::Constant(!c), + Boolean::Is(ref v) => Boolean::Not(v.clone()), + Boolean::Not(ref v) => Boolean::Is(v.clone()), } } /// Perform XOR over two boolean operands pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { match (a, b) { @@ -474,7 +475,7 @@ impl Boolean { /// Perform AND over two boolean operands pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { match (a, b) { @@ -508,7 +509,7 @@ impl Boolean { c: &'a Self, ) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { @@ -615,7 +616,7 @@ impl Boolean { c: &'a Self, ) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { @@ -1740,4 +1741,72 @@ mod test { } } } + + #[test] + fn test_alloc_conditionally() { + { + let mut cs = TestConstraintSystem::::new(); + let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); + + let value = None; + // if value is none, fail with SynthesisError + let is_err = AllocatedBit::alloc_conditionally( + cs.namespace(|| "alloc_conditionally"), + value, + &b, + ) + .is_err(); + assert!(is_err); + } + + { + // since value is true, b must be false, so it should succeed + let mut cs = TestConstraintSystem::::new(); + + let value = Some(true); + let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); + let allocated_value = AllocatedBit::alloc_conditionally( + cs.namespace(|| "alloc_conditionally"), + value, + &b, + ) + .unwrap(); + + assert_eq!(allocated_value.get_value().unwrap(), true); + assert!(cs.is_satisfied()); + } + + { + // since value is true, b must be false, so it should fail + let mut cs = TestConstraintSystem::::new(); + + let value = Some(true); + let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); + AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b) + .unwrap(); + + assert!(!cs.is_satisfied()); + } + + { + // since value is false, we don't care about the value of the bit + + let value = Some(false); + //check with false bit + let mut cs = TestConstraintSystem::::new(); + let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); + AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) + .unwrap(); + + assert!(cs.is_satisfied()); + + //check with true bit + let mut cs = TestConstraintSystem::::new(); + let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); + AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) + .unwrap(); + + assert!(cs.is_satisfied()); + } + } } diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs index bbb1da6..bde86e2 100644 --- a/bellman/src/gadgets/lookup.rs +++ b/bellman/src/gadgets/lookup.rs @@ -1,5 +1,7 @@ -use ff::Field; -use pairing::Engine; +//! Window table lookup gadgets. + +use ff::{Field, ScalarEngine}; +use std::ops::{AddAssign, Neg}; use super::boolean::Boolean; use super::num::{AllocatedNum, Num}; @@ -7,15 +9,14 @@ use super::*; use crate::ConstraintSystem; // Synthesize the constants for each base pattern. -fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) +fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) where I: IntoIterator, { assert_eq!(assignment.len(), 1 << window_size); for (i, constant) in constants.into_iter().enumerate() { - let mut cur = assignment[i]; - cur.negate(); + let mut cur = assignment[i].neg(); cur.add_assign(constant); assignment[i] = cur; for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) { @@ -28,7 +29,7 @@ where /// Performs a 3-bit window table lookup. `bits` is in /// little-endian order. -pub fn lookup3_xy( +pub fn lookup3_xy( mut cs: CS, bits: &[Boolean], coords: &[(E::Fr, E::Fr)], @@ -118,7 +119,7 @@ where /// Performs a 3-bit window table lookup, where /// one of the bits is a sign bit. -pub fn lookup3_xy_with_conditional_negation( +pub fn lookup3_xy_with_conditional_negation( mut cs: CS, bits: &[Boolean], coords: &[(E::Fr, E::Fr)], @@ -149,7 +150,7 @@ where let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { let mut tmp = coords[*i.get()?].1; if *bits[2].get_value().get()? { - tmp.negate(); + tmp = tmp.neg(); } Ok(tmp) })?; @@ -279,7 +280,7 @@ mod test { assert_eq!(res.0.get_value().unwrap(), points[index].0); let mut tmp = points[index].1; if c_val { - tmp.negate() + tmp = tmp.neg() } assert_eq!(res.1.get_value().unwrap(), tmp); } diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs index 510802d..37b2d94 100644 --- a/bellman/src/gadgets/multieq.rs +++ b/bellman/src/gadgets/multieq.rs @@ -1,9 +1,8 @@ -use ff::{Field, PrimeField}; -use pairing::Engine; +use ff::{Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; -pub struct MultiEq> { +pub struct MultiEq> { cs: CS, ops: usize, bits_used: usize, @@ -11,10 +10,10 @@ pub struct MultiEq> { rhs: LinearCombination, } -impl> MultiEq { +impl> MultiEq { pub fn new(cs: CS) -> Self { MultiEq { - cs: cs, + cs, ops: 0, bits_used: 0, lhs: LinearCombination::zero(), @@ -51,14 +50,16 @@ impl> MultiEq { assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits)); - let coeff = E::Fr::from_str("2").unwrap().pow(&[self.bits_used as u64]); + let coeff = E::Fr::from_str("2") + .unwrap() + .pow_vartime(&[self.bits_used as u64]); self.lhs = self.lhs.clone() + (coeff, lhs); self.rhs = self.rhs.clone() + (coeff, rhs); self.bits_used += num_bits; } } -impl> Drop for MultiEq { +impl> Drop for MultiEq { fn drop(&mut self) { if self.bits_used > 0 { self.accumulate(); @@ -66,7 +67,7 @@ impl> Drop for MultiEq { } } -impl> ConstraintSystem for MultiEq { +impl> ConstraintSystem for MultiEq { type Root = Self; fn one() -> Variable { diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs index 34df7cd..8ee6a15 100644 --- a/bellman/src/gadgets/multipack.rs +++ b/bellman/src/gadgets/multipack.rs @@ -1,15 +1,17 @@ +//! Helpers for packing vectors of bits into scalar field elements. + use super::boolean::Boolean; use super::num::Num; use super::Assignment; use crate::{ConstraintSystem, SynthesisError}; -use ff::{Field, PrimeField}; -use pairing::Engine; +use ff::{Field, PrimeField, ScalarEngine}; +use std::ops::AddAssign; /// Takes a sequence of booleans and exposes them as compact /// public inputs pub fn pack_into_inputs(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() { @@ -18,7 +20,7 @@ where for bit in bits { num = num.add_bool_with_coeff(CS::one(), bit, coeff); - coeff.double(); + coeff = coeff.double(); } let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?; @@ -49,7 +51,7 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec { .collect() } -pub fn compute_multipacking(bits: &[bool]) -> Vec { +pub fn compute_multipacking(bits: &[bool]) -> Vec { let mut result = vec![]; for bits in bits.chunks(E::Fr::CAPACITY as usize) { @@ -61,7 +63,7 @@ pub fn compute_multipacking(bits: &[bool]) -> Vec { cur.add_assign(&coeff); } - coeff.double(); + coeff = coeff.double(); } result.push(cur); diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index 84843c1..236689d 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -1,5 +1,7 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; -use pairing::Engine; +//! Gadgets representing numbers in the scalar field of the underlying curve. + +use ff::{BitIterator, Field, PrimeField, ScalarEngine}; +use std::ops::{AddAssign, MulAssign}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; @@ -7,12 +9,12 @@ use super::Assignment; use super::boolean::{self, AllocatedBit, Boolean}; -pub struct AllocatedNum { +pub struct AllocatedNum { value: Option, variable: Variable, } -impl Clone for AllocatedNum { +impl Clone for AllocatedNum { fn clone(&self) -> Self { AllocatedNum { value: self.value, @@ -21,7 +23,7 @@ impl Clone for AllocatedNum { } } -impl AllocatedNum { +impl AllocatedNum { pub fn alloc(mut cs: CS, value: F) -> Result where CS: ConstraintSystem, @@ -66,7 +68,7 @@ impl AllocatedNum { /// order, requiring that the representation /// strictly exists "in the field" (i.e., a /// congruency is not allowed.) - pub fn into_bits_le_strict(&self, mut cs: CS) -> Result, SynthesisError> + pub fn to_bits_le_strict(&self, mut cs: CS) -> Result, SynthesisError> where CS: ConstraintSystem, { @@ -75,10 +77,10 @@ impl AllocatedNum { v: &[AllocatedBit], ) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { - assert!(v.len() > 0); + assert!(!v.is_empty()); // Let's keep this simple for now and just AND them all // manually @@ -101,9 +103,8 @@ impl AllocatedNum { // We want to ensure that the bit representation of a is // less than or equal to r - 1. - let mut a = self.value.map(|e| BitIterator::new(e.into_repr())); - let mut b = E::Fr::char(); - b.sub_noborrow(&1.into()); + let mut a = self.value.map(|e| BitIterator::::new(e.to_repr())); + let b = (-E::Fr::one()).to_repr(); let mut result = vec![]; @@ -113,7 +114,7 @@ impl AllocatedNum { let mut found_one = false; let mut i = 0; - for b in BitIterator::new(b) { + for b in BitIterator::::new(b) { let a_bit = a.as_mut().map(|e| e.next().unwrap()); // Skip over unset bits at the beginning @@ -132,7 +133,7 @@ impl AllocatedNum { current_run.push(a_bit.clone()); result.push(a_bit); } else { - if current_run.len() > 0 { + if !current_run.is_empty() { // This is the start of a run of zeros, but we need // to k-ary AND against `last_run` first. @@ -175,7 +176,7 @@ impl AllocatedNum { for bit in result.iter().rev() { lc = lc + (coeff, bit.get_variable()); - coeff.double(); + coeff = coeff.double(); } lc = lc - self.variable; @@ -183,13 +184,13 @@ impl AllocatedNum { cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); // Convert into booleans, and reverse for little-endian bit order - Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) + Ok(result.into_iter().map(Boolean::from).rev().collect()) } /// Convert the allocated number into its little-endian representation. /// Note that this does not strongly enforce that the commitment is /// "in the field." - pub fn into_bits_le(&self, mut cs: CS) -> Result, SynthesisError> + pub fn to_bits_le(&self, mut cs: CS) -> Result, SynthesisError> where CS: ConstraintSystem, { @@ -201,14 +202,14 @@ impl AllocatedNum { for bit in bits.iter() { lc = lc + (coeff, bit.get_variable()); - coeff.double(); + coeff = coeff.double(); } lc = lc - self.variable; cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); - Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) + Ok(bits.into_iter().map(Boolean::from).collect()) } pub fn mul(&self, mut cs: CS, other: &Self) -> Result @@ -238,7 +239,7 @@ impl AllocatedNum { ); Ok(AllocatedNum { - value: value, + value, variable: var, }) } @@ -252,8 +253,7 @@ impl AllocatedNum { let var = cs.alloc( || "squared num", || { - let mut tmp = *self.value.get()?; - tmp.square(); + let tmp = self.value.get()?.square(); value = Some(tmp); @@ -270,7 +270,7 @@ impl AllocatedNum { ); Ok(AllocatedNum { - value: value, + value, variable: var, }) } @@ -287,7 +287,7 @@ impl AllocatedNum { if tmp.is_zero() { Err(SynthesisError::DivisionByZero) } else { - Ok(tmp.inverse().unwrap()) + Ok(tmp.invert().unwrap()) } }, )?; @@ -359,12 +359,12 @@ impl AllocatedNum { } } -pub struct Num { +pub struct Num { value: Option, lc: LinearCombination, } -impl From> for Num { +impl From> for Num { fn from(num: AllocatedNum) -> Num { Num { value: num.value, @@ -373,7 +373,7 @@ impl From> for Num { } } -impl Num { +impl Num { pub fn zero() -> Self { Num { value: Some(E::Fr::zero()), @@ -415,6 +415,7 @@ mod test { use pairing::bls12_381::{Bls12, Fr}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; + use std::ops::{Neg, SubAssign}; use super::{AllocatedNum, Boolean}; use crate::gadgets::test::*; @@ -516,13 +517,12 @@ mod test { #[test] fn test_into_bits_strict() { - let mut negone = Fr::one(); - negone.negate(); + let negone = Fr::one().neg(); let mut cs = TestConstraintSystem::::new(); let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); - n.into_bits_le_strict(&mut cs).unwrap(); + n.to_bits_le_strict(&mut cs).unwrap(); assert!(cs.is_satisfied()); @@ -550,14 +550,14 @@ mod test { let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); let bits = if i % 2 == 0 { - n.into_bits_le(&mut cs).unwrap() + n.to_bits_le(&mut cs).unwrap() } else { - n.into_bits_le_strict(&mut cs).unwrap() + n.to_bits_le_strict(&mut cs).unwrap() }; assert!(cs.is_satisfied()); - for (b, a) in BitIterator::new(r.into_repr()) + for (b, a) in BitIterator::::new(r.to_repr()) .skip(1) .zip(bits.iter().rev()) { diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs index cb057f8..1be898e 100644 --- a/bellman/src/gadgets/sha256.rs +++ b/bellman/src/gadgets/sha256.rs @@ -1,9 +1,15 @@ +//! Circuits for the [SHA-256] hash function and its internal compression +//! function. +//! +//! [SHA-256]: https://tools.ietf.org/html/rfc6234 + use super::boolean::Boolean; use super::multieq::MultiEq; use super::uint32::UInt32; use crate::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::ScalarEngine; +#[allow(clippy::unreadable_literal)] const ROUND_CONSTANTS: [u32; 64] = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, @@ -15,6 +21,7 @@ const ROUND_CONSTANTS: [u32; 64] = [ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ]; +#[allow(clippy::unreadable_literal)] const IV: [u32; 8] = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]; @@ -24,7 +31,7 @@ pub fn sha256_block_no_padding( input: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { assert_eq!(input.len(), 512); @@ -39,7 +46,7 @@ where pub fn sha256(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { assert!(input.len() % 8 == 0); @@ -76,7 +83,7 @@ fn sha256_compression_function( current_hash_value: &[UInt32], ) -> Result, SynthesisError> where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { assert_eq!(input.len(), 512); @@ -123,14 +130,14 @@ where impl Maybe { fn compute(self, cs: M, others: &[UInt32]) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, M: ConstraintSystem>, { Ok(match self { Maybe::Concrete(ref v) => return Ok(v.clone()), Maybe::Deferred(mut v) => { - v.extend(others.into_iter().cloned()); + v.extend(others.iter().cloned()); UInt32::addmany(cs, &v)? } }) @@ -266,6 +273,7 @@ mod test { use super::*; use crate::gadgets::boolean::AllocatedBit; use crate::gadgets::test::TestConstraintSystem; + use hex_literal::hex; use pairing::bls12_381::Bls12; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -286,7 +294,7 @@ mod test { let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); let mut out = out_bits.into_iter(); - for b in expected.into_iter() { + for b in expected.iter() { for i in (0..8).rev() { let c = out.next().unwrap().get_value().unwrap(); diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs index 58ba040..be7214e 100644 --- a/bellman/src/gadgets/test/mod.rs +++ b/bellman/src/gadgets/test/mod.rs @@ -1,10 +1,12 @@ -use ff::{Field, PrimeField, PrimeFieldRepr}; -use pairing::Engine; +//! Helpers for testing circuit implementations. + +use ff::{Endianness, Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use std::collections::HashMap; use std::fmt::Write; +use std::ops::{AddAssign, MulAssign, Neg}; use byteorder::{BigEndian, ByteOrder}; use std::cmp::Ordering; @@ -20,7 +22,7 @@ enum NamedObject { } /// Constraint system for testing purposes. -pub struct TestConstraintSystem { +pub struct TestConstraintSystem { named_objects: HashMap, current_namespace: Vec, constraints: Vec<( @@ -62,11 +64,11 @@ impl Ord for OrderedVariable { } } -fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap { +fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap { let mut map = BTreeMap::new(); for &(var, coeff) in terms { map.entry(OrderedVariable(var)) - .or_insert(E::Fr::zero()) + .or_insert_with(E::Fr::zero) .add_assign(&coeff); } @@ -85,7 +87,7 @@ fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { +fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { let map = proc_lc::(terms); let mut buf = [0u8; 9 + 32]; @@ -104,13 +106,16 @@ fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { } } - coeff.into_repr().write_be(&mut buf[9..]).unwrap(); + let mut coeff_repr = coeff.to_repr(); + ::ReprEndianness::toggle_little_endian(&mut coeff_repr); + let coeff_be: Vec<_> = coeff_repr.as_ref().iter().cloned().rev().collect(); + buf[9..].copy_from_slice(&coeff_be[..]); h.update(&buf); } } -fn eval_lc( +fn eval_lc( terms: &[(Variable, E::Fr)], inputs: &[(E::Fr, String)], aux: &[(E::Fr, String)], @@ -130,7 +135,7 @@ fn eval_lc( acc } -impl TestConstraintSystem { +impl TestConstraintSystem { pub fn new() -> TestConstraintSystem { let mut map = HashMap::new(); map.insert( @@ -150,14 +155,10 @@ impl TestConstraintSystem { pub fn pretty_print(&self) -> String { let mut s = String::new(); - let negone = { - let mut tmp = E::Fr::one(); - tmp.negate(); - tmp - }; + let negone = E::Fr::one().neg(); let powers_of_two = (0..E::Fr::NUM_BITS) - .map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64])) + .map(|i| E::Fr::from_str("2").unwrap().pow_vartime(&[u64::from(i)])) .collect::>(); let pp = |s: &mut String, lc: &LinearCombination| { @@ -286,7 +287,7 @@ impl TestConstraintSystem { } } - return true; + true } pub fn num_inputs(&self) -> usize { @@ -344,7 +345,7 @@ fn compute_path(ns: &[String], this: String) -> String { name } -impl ConstraintSystem for TestConstraintSystem { +impl ConstraintSystem for TestConstraintSystem { type Root = Self; fn alloc(&mut self, annotation: A, f: F) -> Result diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs index 4c0d376..16bb651 100644 --- a/bellman/src/gadgets/uint32.rs +++ b/bellman/src/gadgets/uint32.rs @@ -1,5 +1,9 @@ -use ff::{Field, PrimeField}; -use pairing::Engine; +//! Circuit representation of a [`u32`], with helpers for the [`sha256`] +//! gadgets. +//! +//! [`sha256`]: crate::gadgets::sha256 + +use ff::{Field, PrimeField, ScalarEngine}; use crate::{ConstraintSystem, LinearCombination, SynthesisError}; @@ -33,7 +37,7 @@ impl UInt32 { } UInt32 { - bits: bits, + bits, value: Some(value), } } @@ -41,7 +45,7 @@ impl UInt32 { /// Allocate a `UInt32` in the constraint system pub fn alloc(mut cs: CS, value: Option) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let values = match value { @@ -69,14 +73,13 @@ impl UInt32 { }) .collect::, SynthesisError>>()?; - Ok(UInt32 { - bits: bits, - value: value, - }) + Ok(UInt32 { bits, value }) } - pub fn into_bits_be(&self) -> Vec { - self.bits.iter().rev().cloned().collect() + pub fn into_bits_be(self) -> Vec { + let mut ret = self.bits; + ret.reverse(); + ret } pub fn from_bits_be(bits: &[Boolean]) -> Self { @@ -98,14 +101,14 @@ impl UInt32 { } UInt32 { - value: value, + value, bits: bits.iter().rev().cloned().collect(), } } /// Turns this `UInt32` into its little-endian byte order representation. - pub fn into_bits(&self) -> Vec { - self.bits.clone() + pub fn into_bits(self) -> Vec { + self.bits } /// Converts a little-endian byte order representation of bits into a @@ -119,20 +122,20 @@ impl UInt32 { for b in new_bits.iter().rev() { value.as_mut().map(|v| *v <<= 1); - match b { - &Boolean::Constant(b) => { + match *b { + Boolean::Constant(b) => { if b { value.as_mut().map(|v| *v |= 1); } } - &Boolean::Is(ref b) => match b.get_value() { + Boolean::Is(ref b) => match b.get_value() { Some(true) => { value.as_mut().map(|v| *v |= 1); } Some(false) => {} None => value = None, }, - &Boolean::Not(ref b) => match b.get_value() { + Boolean::Not(ref b) => match b.get_value() { Some(false) => { value.as_mut().map(|v| *v |= 1); } @@ -143,7 +146,7 @@ impl UInt32 { } UInt32 { - value: value, + value, bits: new_bits, } } @@ -195,7 +198,7 @@ impl UInt32 { circuit_fn: U, ) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, F: Fn(u32, u32, u32) -> u32, U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, @@ -215,7 +218,7 @@ impl UInt32 { .collect::>()?; Ok(UInt32 { - bits: bits, + bits, value: new_value, }) } @@ -224,7 +227,7 @@ impl UInt32 { /// during SHA256. pub fn sha256_maj(cs: CS, a: &Self, b: &Self, c: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { Self::triop( @@ -241,7 +244,7 @@ impl UInt32 { /// during SHA256. pub fn sha256_ch(cs: CS, a: &Self, b: &Self, c: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { Self::triop( @@ -257,7 +260,7 @@ impl UInt32 { /// XOR this `UInt32` with another `UInt32` pub fn xor(&self, mut cs: CS, other: &Self) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, { let new_value = match (self.value, other.value) { @@ -274,7 +277,7 @@ impl UInt32 { .collect::>()?; Ok(UInt32 { - bits: bits, + bits, value: new_value, }) } @@ -282,7 +285,7 @@ impl UInt32 { /// Perform modular addition of several `UInt32` objects. pub fn addmany(mut cs: M, operands: &[Self]) -> Result where - E: Engine, + E: ScalarEngine, CS: ConstraintSystem, M: ConstraintSystem>, { @@ -294,7 +297,7 @@ impl UInt32 { // Compute the maximum value of the sum so we allocate enough bits for // the result - let mut max_value = (operands.len() as u64) * (u32::max_value() as u64); + let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); // Keep track of the resulting value let mut result_value = Some(0u64); @@ -310,7 +313,7 @@ impl UInt32 { // Accumulate the value match op.value { Some(val) => { - result_value.as_mut().map(|v| *v += val as u64); + result_value.as_mut().map(|v| *v += u64::from(val)); } None => { // If any of our operands have unknown value, we won't @@ -327,7 +330,7 @@ impl UInt32 { all_constants &= bit.is_constant(); - coeff.double(); + coeff = coeff.double(); } } @@ -365,7 +368,7 @@ impl UInt32 { max_value >>= 1; i += 1; - coeff.double(); + coeff = coeff.double(); } // Enforce equality between the sum and result @@ -401,15 +404,15 @@ mod test { ]); for _ in 0..1000 { - let mut v = (0..32) + let v = (0..32) .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .collect::>(); let b = UInt32::from_bits_be(&v); for (i, bit) in b.bits.iter().enumerate() { - match bit { - &Boolean::Constant(bit) => { + match *bit { + Boolean::Constant(bit) => { assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); } _ => unreachable!(), @@ -436,15 +439,15 @@ mod test { ]); for _ in 0..1000 { - let mut v = (0..32) + let v = (0..32) .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .collect::>(); let b = UInt32::from_bits(&v); for (i, bit) in b.bits.iter().enumerate() { - match bit { - &Boolean::Constant(bit) => { + match *bit { + Boolean::Constant(bit) => { assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); } _ => unreachable!(), @@ -491,14 +494,14 @@ mod test { assert!(r.value == Some(expected)); for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { + match *b { + Boolean::Is(ref b) => { assert!(b.get_value().unwrap() == (expected & 1 == 1)); } - &Boolean::Not(ref b) => { + Boolean::Not(ref b) => { assert!(!b.get_value().unwrap() == (expected & 1 == 1)); } - &Boolean::Constant(b) => { + Boolean::Constant(b) => { assert!(b == (expected & 1 == 1)); } } @@ -538,10 +541,10 @@ mod test { assert!(r.value == Some(expected)); for b in r.bits.iter() { - match b { - &Boolean::Is(_) => panic!(), - &Boolean::Not(_) => panic!(), - &Boolean::Constant(b) => { + match *b { + Boolean::Is(_) => panic!(), + Boolean::Not(_) => panic!(), + Boolean::Constant(b) => { assert!(b == (expected & 1 == 1)); } } @@ -576,8 +579,7 @@ mod test { let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); let r = { let mut cs = MultiEq::new(&mut cs); - let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap(); - r + UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() }; assert!(cs.is_satisfied()); @@ -585,14 +587,14 @@ mod test { assert!(r.value == Some(expected)); for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { + match *b { + Boolean::Is(ref b) => { assert!(b.get_value().unwrap() == (expected & 1 == 1)); } - &Boolean::Not(ref b) => { + Boolean::Not(ref b) => { assert!(!b.get_value().unwrap() == (expected & 1 == 1)); } - &Boolean::Constant(_) => unreachable!(), + Boolean::Constant(_) => unreachable!(), } expected >>= 1; @@ -628,8 +630,8 @@ mod test { let mut tmp = num; for b in &b.bits { - match b { - &Boolean::Constant(b) => { + match *b { + Boolean::Constant(b) => { assert_eq!(b, tmp & 1 == 1); } _ => unreachable!(), diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 596464f..02efc21 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -1,18 +1,18 @@ use rand_core::RngCore; - +use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::{Field, PrimeField}; +use ff::Field; use group::{CurveAffine, CurveProjective, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; -use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; +use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; -use domain::{EvaluationDomain, Scalar}; +use crate::domain::{EvaluationDomain, Scalar}; -use multicore::Worker; +use crate::multicore::Worker; /// Generates a random common reference string for /// a circuit. @@ -215,8 +215,22 @@ where assembly.num_inputs + assembly.num_aux }); - let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; - let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + let gamma_inverse = { + let inverse = gamma.invert(); + if bool::from(inverse.is_some()) { + Ok(inverse.unwrap()) + } else { + Err(SynthesisError::UnexpectedIdentity) + } + }?; + let delta_inverse = { + let inverse = delta.invert(); + if bool::from(inverse.is_some()) { + Ok(inverse.unwrap()) + } else { + Err(SynthesisError::UnexpectedIdentity) + } + }?; let worker = Worker::new(); @@ -227,8 +241,8 @@ where let powers_of_tau = powers_of_tau.as_mut(); worker.scope(powers_of_tau.len(), |scope, chunk| { for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { - scope.spawn(move || { - let mut current_tau_power = tau.pow(&[(i * chunk) as u64]); + scope.spawn(move |_scope| { + let mut current_tau_power = tau.pow_vartime(&[(i * chunk) as u64]); for p in powers_of_tau { p.0 = current_tau_power; @@ -251,7 +265,7 @@ where { let mut g1_wnaf = g1_wnaf.shared(); - scope.spawn(move || { + scope.spawn(move |_scope| { // Set values of the H query to g1^{(tau^i * t(tau)) / delta} for (h, p) in h.iter_mut().zip(p.iter()) { // Compute final exponent @@ -259,7 +273,7 @@ where exp.mul_assign(&coeff); // Exponentiate - *h = g1_wnaf.scalar(exp.into_repr()); + *h = g1_wnaf.scalar(&exp); } // Batch normalize @@ -330,7 +344,7 @@ where let mut g1_wnaf = g1_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared(); - scope.spawn(move || { + scope.spawn(move |_scope| { for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a .iter_mut() .zip(b_g1.iter_mut()) @@ -362,14 +376,14 @@ where // Compute A query (in G1) if !at.is_zero() { - *a = g1_wnaf.scalar(at.into_repr()); + *a = g1_wnaf.scalar(&at); } // Compute B query (in G1/G2) if !bt.is_zero() { - let bt_repr = bt.into_repr(); - *b_g1 = g1_wnaf.scalar(bt_repr); - *b_g2 = g2_wnaf.scalar(bt_repr); + (); + *b_g1 = g1_wnaf.scalar(&bt); + *b_g2 = g2_wnaf.scalar(&bt); } at.mul_assign(&beta); @@ -380,7 +394,7 @@ where e.add_assign(&ct); e.mul_assign(inv); - *ext = g1_wnaf.scalar(e.into_repr()); + *ext = g1_wnaf.scalar(&e); } // Batch normalize @@ -451,7 +465,7 @@ where }; Ok(Parameters { - vk: vk, + vk, h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 85b7952..6f5af85 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -1,10 +1,14 @@ +//! The [Groth16] proving system. +//! +//! [Groth16]: https://eprint.iacr.org/2016/260 + use group::{CurveAffine, EncodedPoint}; use pairing::{Engine, PairingCurveAffine}; -use SynthesisError; +use crate::SynthesisError; +use crate::multiexp::SourceBuilder; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use multiexp::SourceBuilder; use std::io::{self, Read, Write}; use std::sync::Arc; @@ -90,7 +94,7 @@ impl Proof { } })?; - Ok(Proof { a: a, b: b, c: c }) + Ok(Proof { a, b, c }) } } @@ -208,13 +212,13 @@ impl VerifyingKey { } Ok(VerifyingKey { - alpha_g1: alpha_g1, - beta_g1: beta_g1, - beta_g2: beta_g2, - gamma_g2: gamma_g2, - delta_g1: delta_g1, - delta_g2: delta_g2, - ic: ic, + alpha_g1, + beta_g1, + beta_g2, + gamma_g2, + delta_g1, + delta_g2, + ic, }) } } @@ -376,7 +380,7 @@ impl Parameters { } Ok(Parameters { - vk: vk, + vk, h: Arc::new(h), l: Arc::new(l), a: Arc::new(a), @@ -465,11 +469,12 @@ impl<'a, E: Engine> ParameterSource for &'a Parameters { #[cfg(test)] mod test_with_bls12_381 { use super::*; - use {Circuit, ConstraintSystem, SynthesisError}; + use crate::{Circuit, ConstraintSystem, SynthesisError}; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; use rand::thread_rng; + use std::ops::MulAssign; #[test] fn serialization() { diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index a502ac3..34abbb4 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -1,22 +1,22 @@ use rand_core::RngCore; - +use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use futures::Future; -use ff::{Field, PrimeField}; +use ff::Field; use group::{CurveAffine, CurveProjective}; use pairing::Engine; use super::{ParameterSource, Proof}; -use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; +use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; -use domain::{EvaluationDomain, Scalar}; +use crate::domain::{EvaluationDomain, Scalar}; -use multiexp::{multiexp, DensityTracker, FullDensity}; +use crate::multiexp::{multiexp, DensityTracker, FullDensity}; -use multicore::Worker; +use crate::multicore::Worker; fn eval( lc: &LinearCombination, @@ -229,26 +229,14 @@ where let a_len = a.len() - 1; a.truncate(a_len); // TODO: parallelize if it's even helpful - let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); + let a = Arc::new(a.into_iter().map(|s| s.0).collect::>()); multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; // TODO: parallelize if it's even helpful - let input_assignment = Arc::new( - prover - .input_assignment - .into_iter() - .map(|s| s.into_repr()) - .collect::>(), - ); - let aux_assignment = Arc::new( - prover - .aux_assignment - .into_iter() - .map(|s| s.into_repr()) - .collect::>(), - ); + let input_assignment = Arc::new(prover.input_assignment); + let aux_assignment = Arc::new(prover.aux_assignment); let l = multiexp( &worker, @@ -314,34 +302,34 @@ where } let mut g_a = vk.delta_g1.mul(r); - g_a.add_assign_mixed(&vk.alpha_g1); + AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1); let mut g_b = vk.delta_g2.mul(s); - g_b.add_assign_mixed(&vk.beta_g2); + AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2); let mut g_c; { let mut rs = r; rs.mul_assign(&s); g_c = vk.delta_g1.mul(rs); - g_c.add_assign(&vk.alpha_g1.mul(s)); - g_c.add_assign(&vk.beta_g1.mul(r)); + AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.alpha_g1.mul(s)); + AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.beta_g1.mul(r)); } let mut a_answer = a_inputs.wait()?; - a_answer.add_assign(&a_aux.wait()?); - g_a.add_assign(&a_answer); + AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); + AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer); a_answer.mul_assign(s); - g_c.add_assign(&a_answer); + AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer); - let mut b1_answer = b_g1_inputs.wait()?; - b1_answer.add_assign(&b_g1_aux.wait()?); + let mut b1_answer: E::G1 = b_g1_inputs.wait()?; + AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?); let mut b2_answer = b_g2_inputs.wait()?; - b2_answer.add_assign(&b_g2_aux.wait()?); + AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?); - g_b.add_assign(&b2_answer); + AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer); b1_answer.mul_assign(r); - g_c.add_assign(&b1_answer); - g_c.add_assign(&h.wait()?); - g_c.add_assign(&l.wait()?); + AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer); + AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?); + AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); Ok(Proof { a: g_a.into_affine(), diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 4c5874d..fccf5b0 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,28 +1,172 @@ -use ff::{ - Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, - SqrtField, -}; +use ff::{Field, PrimeField, ScalarEngine}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; use rand_core::RngCore; -use std::cmp::Ordering; use std::fmt; use std::num::Wrapping; +use std::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; const MODULUS_R: Wrapping = Wrapping(64513); #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Fr(Wrapping); +impl Default for Fr { + fn default() -> Self { + ::zero() + } +} + +impl ConstantTimeEq for Fr { + fn ct_eq(&self, other: &Fr) -> Choice { + (self.0).0.ct_eq(&(other.0).0) + } +} + impl fmt::Display for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", (self.0).0) } } +impl From for Fr { + fn from(v: u64) -> Fr { + Fr(Wrapping((v % MODULUS_R.0 as u64) as u32)) + } +} + +impl ConditionallySelectable for Fr { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fr(Wrapping(u32::conditional_select( + &(a.0).0, + &(b.0).0, + choice, + ))) + } +} + +impl Neg for Fr { + type Output = Self; + + fn neg(mut self) -> Self { + if !::is_zero(&self) { + self.0 = MODULUS_R - self.0; + } + self + } +} + +impl<'r> Add<&'r Fr> for Fr { + type Output = Self; + + fn add(self, other: &Self) -> Self { + let mut ret = self; + AddAssign::add_assign(&mut ret, other); + ret + } +} + +impl Add for Fr { + type Output = Self; + + fn add(self, other: Self) -> Self { + self + &other + } +} + +impl<'r> AddAssign<&'r Fr> for Fr { + fn add_assign(&mut self, other: &Self) { + self.0 = (self.0 + other.0) % MODULUS_R; + } +} + +impl AddAssign for Fr { + fn add_assign(&mut self, other: Self) { + AddAssign::add_assign(self, &other); + } +} + +impl<'r> Sub<&'r Fr> for Fr { + type Output = Self; + + fn sub(self, other: &Self) -> Self { + let mut ret = self; + SubAssign::sub_assign(&mut ret, other); + ret + } +} + +impl Sub for Fr { + type Output = Self; + + fn sub(self, other: Self) -> Self { + self - &other + } +} + +impl<'r> SubAssign<&'r Fr> for Fr { + fn sub_assign(&mut self, other: &Self) { + self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; + } +} + +impl SubAssign for Fr { + fn sub_assign(&mut self, other: Self) { + SubAssign::sub_assign(self, &other); + } +} + +impl<'r> Mul<&'r Fr> for Fr { + type Output = Self; + + fn mul(self, other: &Self) -> Self { + let mut ret = self; + MulAssign::mul_assign(&mut ret, other); + ret + } +} + +impl Mul for Fr { + type Output = Self; + + fn mul(self, other: Self) -> Self { + self * &other + } +} + +impl<'r> MulAssign<&'r Fr> for Fr { + fn mul_assign(&mut self, other: &Self) { + self.0 = (self.0 * other.0) % MODULUS_R; + } +} + +impl MulAssign for Fr { + fn mul_assign(&mut self, other: Self) { + MulAssign::mul_assign(self, &other); + } +} + +impl BitAnd for Fr { + type Output = u64; + + fn bitand(self, rhs: u64) -> u64 { + (self.0).0 as u64 & rhs + } +} + +impl Shr for Fr { + type Output = Fr; + + fn shr(mut self, rhs: u32) -> Fr { + self.0 = Wrapping((self.0).0 >> rhs); + self + } +} + impl Field for Fr { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fr(Wrapping(rng.next_u32()) % MODULUS_R) } @@ -38,204 +182,119 @@ impl Field for Fr { (self.0).0 == 0 } - fn square(&mut self) { - self.0 = (self.0 * self.0) % MODULUS_R; + fn square(&self) -> Self { + Fr((self.0 * self.0) % MODULUS_R) } - fn double(&mut self) { - self.0 = (self.0 << 1) % MODULUS_R; + fn double(&self) -> Self { + Fr((self.0 << 1) % MODULUS_R) } - fn negate(&mut self) { - if !::is_zero(self) { - self.0 = MODULUS_R - self.0; - } - } - - fn add_assign(&mut self, other: &Self) { - self.0 = (self.0 + other.0) % MODULUS_R; - } - - fn sub_assign(&mut self, other: &Self) { - self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; - } - - fn mul_assign(&mut self, other: &Self) { - self.0 = (self.0 * other.0) % MODULUS_R; - } - - fn inverse(&self) -> Option { + fn invert(&self) -> CtOption { if ::is_zero(self) { - None + CtOption::new(::zero(), Choice::from(0)) } else { - Some(self.pow(&[(MODULUS_R.0 as u64) - 2])) + CtOption::new( + self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]), + Choice::from(1), + ) } } - fn frobenius_map(&mut self, _: usize) { - // identity - } -} - -impl SqrtField for Fr { - fn legendre(&self) -> LegendreSymbol { - // s = self^((r - 1) // 2) - let s = self.pow([32256]); - if s == ::zero() { - LegendreSymbol::Zero - } else if s == ::one() { - LegendreSymbol::QuadraticResidue - } else { - LegendreSymbol::QuadraticNonResidue - } - } - - fn sqrt(&self) -> Option { + fn sqrt(&self) -> CtOption { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - match self.legendre() { - LegendreSymbol::Zero => Some(*self), - LegendreSymbol::QuadraticNonResidue => None, - LegendreSymbol::QuadraticResidue => { - let mut c = Fr::root_of_unity(); - // r = self^((t + 1) // 2) - let mut r = self.pow([32]); - // t = self^t - let mut t = self.pow([63]); - let mut m = Fr::S; + let mut c = Fr::root_of_unity(); + // r = self^((t + 1) // 2) + let mut r = self.pow_vartime([32u64]); + // t = self^t + let mut t = self.pow_vartime([63u64]); + let mut m = Fr::S; - while t != ::one() { - let mut i = 1; - { - let mut t2i = t; - t2i.square(); - loop { - if t2i == ::one() { - break; - } - t2i.square(); - i += 1; - } + while t != ::one() { + let mut i = 1; + { + let mut t2i = t.square(); + loop { + if t2i == ::one() { + break; } - - for _ in 0..(m - i - 1) { - c.square(); - } - ::mul_assign(&mut r, &c); - c.square(); - ::mul_assign(&mut t, &c); - m = i; + t2i = t2i.square(); + i += 1; } - - Some(r) } + + for _ in 0..(m - i - 1) { + c = c.square(); + } + MulAssign::mul_assign(&mut r, &c); + c = c.square(); + MulAssign::mul_assign(&mut t, &c); + m = i; } + + CtOption::new(r, (r * r).ct_eq(self)) } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FrRepr([u64; 1]); - -impl Ord for FrRepr { - fn cmp(&self, other: &FrRepr) -> Ordering { - (self.0)[0].cmp(&(other.0)[0]) - } -} - -impl PartialOrd for FrRepr { - fn partial_cmp(&self, other: &FrRepr) -> Option { - Some(self.cmp(other)) - } -} - -impl fmt::Display for FrRepr { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", (self.0)[0]) - } -} - -impl From for FrRepr { - fn from(v: u64) -> FrRepr { - FrRepr([v]) - } -} +pub struct FrRepr([u8; 8]); impl From for FrRepr { fn from(v: Fr) -> FrRepr { - FrRepr([(v.0).0 as u64]) + FrRepr::from(&v) } } -impl AsMut<[u64]> for FrRepr { - fn as_mut(&mut self) -> &mut [u64] { +impl<'a> From<&'a Fr> for FrRepr { + fn from(v: &'a Fr) -> FrRepr { + FrRepr(((v.0).0 as u64).to_le_bytes()) + } +} + +impl AsMut<[u8]> for FrRepr { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0[..] } } -impl AsRef<[u64]> for FrRepr { - fn as_ref(&self) -> &[u64] { +impl AsRef<[u8]> for FrRepr { + fn as_ref(&self) -> &[u8] { &self.0[..] } } impl Default for FrRepr { fn default() -> FrRepr { - FrRepr::from(0u64) - } -} - -impl PrimeFieldRepr for FrRepr { - fn sub_noborrow(&mut self, other: &Self) { - self.0[0] = self.0[0].wrapping_sub(other.0[0]); - } - fn add_nocarry(&mut self, other: &Self) { - self.0[0] = self.0[0].wrapping_add(other.0[0]); - } - fn num_bits(&self) -> u32 { - 64 - self.0[0].leading_zeros() - } - fn is_zero(&self) -> bool { - self.0[0] == 0 - } - fn is_odd(&self) -> bool { - !self.is_even() - } - fn is_even(&self) -> bool { - self.0[0] % 2 == 0 - } - fn div2(&mut self) { - self.shr(1) - } - fn shr(&mut self, amt: u32) { - self.0[0] >>= amt; - } - fn mul2(&mut self) { - self.shl(1) - } - fn shl(&mut self, amt: u32) { - self.0[0] <<= amt; + FrRepr([0; 8]) } } impl PrimeField for Fr { type Repr = FrRepr; + type ReprEndianness = byteorder::LittleEndian; const NUM_BITS: u32 = 16; const CAPACITY: u32 = 15; const S: u32 = 10; - fn from_repr(repr: FrRepr) -> Result { - if repr.0[0] >= (MODULUS_R.0 as u64) { - Err(PrimeFieldDecodingError::NotInField(format!("{}", repr))) + fn from_repr(repr: FrRepr) -> Option { + let v = u64::from_le_bytes(repr.0); + if v >= (MODULUS_R.0 as u64) { + None } else { - Ok(Fr(Wrapping(repr.0[0] as u32))) + Some(Fr(Wrapping(v as u32))) } } - fn into_repr(&self) -> FrRepr { + fn to_repr(&self) -> FrRepr { FrRepr::from(*self) } + fn is_odd(&self) -> bool { + (self.0).0 % 2 != 0 + } + fn char() -> FrRepr { Fr(MODULUS_R).into() } @@ -280,16 +339,16 @@ impl Engine for DummyEngine { for &(a, b) in i { let mut tmp = *a; - ::mul_assign(&mut tmp, b); - ::add_assign(&mut acc, &tmp); + MulAssign::mul_assign(&mut tmp, b); + AddAssign::add_assign(&mut acc, &tmp); } acc } /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(this: &Self::Fqk) -> Option { - Some(*this) + fn final_exponentiation(this: &Self::Fqk) -> CtOption { + CtOption::new(*this, Choice::from(1)) } } @@ -299,7 +358,7 @@ impl CurveProjective for Fr { type Scalar = Fr; type Engine = DummyEngine; - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { ::random(rng) } @@ -322,32 +381,20 @@ impl CurveProjective for Fr { } fn double(&mut self) { - ::double(self); - } - - fn add_assign(&mut self, other: &Self) { - ::add_assign(self, other); - } - - fn add_assign_mixed(&mut self, other: &Self) { - ::add_assign(self, other); - } - - fn negate(&mut self) { - ::negate(self); + self.0 = ::double(self).0; } fn mul_assign::Repr>>(&mut self, other: S) { let tmp = Fr::from_repr(other.into()).unwrap(); - ::mul_assign(self, &tmp); + MulAssign::mul_assign(self, &tmp); } fn into_affine(&self) -> Fr { *self } - fn recommended_wnaf_for_scalar(_: ::Repr) -> usize { + fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { 3 } @@ -415,15 +462,11 @@ impl CurveAffine for Fr { ::is_zero(self) } - fn negate(&mut self) { - ::negate(self); - } - fn mul::Repr>>(&self, other: S) -> Self::Projective { let mut res = *self; let tmp = Fr::from_repr(other.into()).unwrap(); - ::mul_assign(&mut res, &tmp); + MulAssign::mul_assign(&mut res, &tmp); res } diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index e6a36e4..276738c 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -5,8 +5,9 @@ mod dummy_engine; use self::dummy_engine::*; use std::marker::PhantomData; +use std::ops::{AddAssign, MulAssign, SubAssign}; -use {Circuit, ConstraintSystem, SynthesisError}; +use crate::{Circuit, ConstraintSystem, SynthesisError}; use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; @@ -126,22 +127,22 @@ fn test_xordemo() { let mut root_of_unity = Fr::root_of_unity(); // We expect this to be a 2^10 root of unity - assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10])); + assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 10])); // Let's turn it into a 2^3 root of unity. - root_of_unity = root_of_unity.pow(&[1 << 7]); - assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3])); + root_of_unity = root_of_unity.pow_vartime(&[1u64 << 7]); + assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1u64 << 3])); assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity); // Let's compute all the points in our evaluation domain. let mut points = Vec::with_capacity(8); - for i in 0..8 { - points.push(root_of_unity.pow(&[i])); + for i in 0u64..8 { + points.push(root_of_unity.pow_vartime(&[i])); } // Let's compute t(tau) = (tau - p_0)(tau - p_1)... // = tau^8 - 1 - let mut t_at_tau = tau.pow(&[8]); + let mut t_at_tau = tau.pow_vartime(&[8u64]); t_at_tau.sub_assign(&Fr::one()); { let mut tmp = Fr::one(); @@ -155,8 +156,8 @@ fn test_xordemo() { // We expect our H query to be 7 elements of the form... // {tau^i t(tau) / delta} - let delta_inverse = delta.inverse().unwrap(); - let gamma_inverse = gamma.inverse().unwrap(); + let delta_inverse = delta.invert().unwrap(); + let gamma_inverse = gamma.invert().unwrap(); { let mut coeff = delta_inverse; coeff.mul_assign(&t_at_tau); diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 926955a..0c89101 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,16 +1,15 @@ use ff::PrimeField; use group::{CurveAffine, CurveProjective}; use pairing::{Engine, PairingCurveAffine}; +use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; -use SynthesisError; +use crate::SynthesisError; pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { - let mut gamma = vk.gamma_g2; - gamma.negate(); - let mut delta = vk.delta_g2; - delta.negate(); + let gamma = vk.gamma_g2.neg(); + let delta = vk.delta_g2.neg(); PreparedVerifyingKey { alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), @@ -32,7 +31,7 @@ pub fn verify_proof<'a, E: Engine>( let mut acc = pvk.ic[0].into_projective(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { - acc.add_assign(&b.mul(i.into_repr())); + AddAssign::<&E::G1>::add_assign(&mut acc, &b.mul(i.to_repr())); } // The original verification equation is: @@ -49,7 +48,7 @@ pub fn verify_proof<'a, E: Engine>( (&acc.into_affine().prepare(), &pvk.neg_gamma_g2), (&proof.c.prepare(), &pvk.neg_delta_g2), ] - .into_iter(), + .iter(), )) .unwrap() == pvk.alpha_g1_beta_g2) diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs index 96400c9..1e48b0c 100644 --- a/bellman/src/lib.rs +++ b/bellman/src/lib.rs @@ -1,33 +1,139 @@ -extern crate ff; -extern crate group; -#[cfg(feature = "pairing")] -extern crate pairing; -extern crate rand_core; +//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit +//! traits and and primitive structures, as well as basic gadget implementations +//! such as booleans and number abstractions. +//! +//! # Example circuit +//! +//! Say we want to write a circuit that proves we know the preimage to some hash +//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a +//! fixed length known in advance (because the circuit parameters will depend on +//! it), but can otherwise have any value. We take the following strategy: +//! +//! - Witness each bit of the preimage. +//! - Compute `hash = SHA-256d(preimage)` inside the circuit. +//! - Expose `hash` as a public input using multiscalar packing. +//! +//! ``` +//! use bellman::{ +//! gadgets::{ +//! boolean::{AllocatedBit, Boolean}, +//! multipack, +//! sha256::sha256, +//! }, +//! groth16, Circuit, ConstraintSystem, SynthesisError, +//! }; +//! use pairing::{bls12_381::Bls12, Engine}; +//! use rand::rngs::OsRng; +//! use sha2::{Digest, Sha256}; +//! +//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order. +//! fn sha256d>( +//! mut cs: CS, +//! data: &[Boolean], +//! ) -> Result, SynthesisError> { +//! // Flip endianness of each input byte +//! let input: Vec<_> = data +//! .chunks(8) +//! .map(|c| c.iter().rev()) +//! .flatten() +//! .cloned() +//! .collect(); +//! +//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?; +//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?; +//! +//! // Flip endianness of each output byte +//! Ok(res +//! .chunks(8) +//! .map(|c| c.iter().rev()) +//! .flatten() +//! .cloned() +//! .collect()) +//! } +//! +//! struct MyCircuit { +//! /// The input to SHA-256d we are proving that we know. Set to `None` when we +//! /// are verifying a proof (and do not have the witness data). +//! preimage: Option<[u8; 80]>, +//! } +//! +//! impl Circuit for MyCircuit { +//! fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +//! // Compute the values for the bits of the preimage. If we are verifying a proof, +//! // we still need to create the same constraints, so we return an equivalent-size +//! // Vec of None (indicating that the value of each bit is unknown). +//! let bit_values = if let Some(preimage) = self.preimage { +//! preimage +//! .into_iter() +//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)) +//! .flatten() +//! .map(|b| Some(b)) +//! .collect() +//! } else { +//! vec![None; 80 * 8] +//! }; +//! assert_eq!(bit_values.len(), 80 * 8); +//! +//! // Witness the bits of the preimage. +//! let preimage_bits = bit_values +//! .into_iter() +//! .enumerate() +//! // Allocate each bit. +//! .map(|(i, b)| { +//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b) +//! }) +//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget). +//! .map(|b| b.map(Boolean::from)) +//! .collect::, _>>()?; +//! +//! // Compute hash = SHA-256d(preimage). +//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?; +//! +//! // Expose the vector of 32 boolean variables as compact public inputs. +//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash) +//! } +//! } +//! +//! // Create parameters for our circuit. In a production deployment these would +//! // be generated securely using a multiparty computation. +//! let params = { +//! let c = MyCircuit { preimage: None }; +//! groth16::generate_random_parameters::(c, &mut OsRng).unwrap() +//! }; +//! +//! // Prepare the verification key (for proof verification). +//! let pvk = groth16::prepare_verifying_key(¶ms.vk); +//! +//! // Pick a preimage and compute its hash. +//! let preimage = [42; 80]; +//! let hash = Sha256::digest(&Sha256::digest(&preimage)); +//! +//! // Create an instance of our circuit (with the preimage as a witness). +//! let c = MyCircuit { +//! preimage: Some(preimage), +//! }; +//! +//! // Create a Groth16 proof with our parameters. +//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap(); +//! +//! // Pack the hash as inputs for proof verification. +//! let hash_bits = multipack::bytes_to_bits_le(&hash); +//! let inputs = multipack::compute_multipacking::(&hash_bits); +//! +//! // Check the proof! +//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap()); +//! ``` +//! +//! # Roadmap +//! +//! `bellman` is being refactored into a generic proving library. Currently it +//! is pairing-specific, and different types of proving systems need to be +//! implemented as sub-modules. After the refactor, `bellman` will be generic +//! using the [`ff`] and [`group`] crates, while specific proving systems will +//! be separate crates that pull in the dependencies they require. -extern crate bit_vec; -extern crate blake2s_simd; -extern crate byteorder; -extern crate futures; - -#[cfg(feature = "multicore")] -extern crate crossbeam; -#[cfg(feature = "multicore")] -extern crate futures_cpupool; -#[cfg(feature = "multicore")] -extern crate num_cpus; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -#[cfg(test)] -extern crate rand; - -#[cfg(test)] -extern crate rand_xorshift; - -#[cfg(test)] -extern crate sha2; +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] pub mod domain; pub mod gadgets; @@ -42,7 +148,7 @@ use std::error::Error; use std::fmt; use std::io; use std::marker::PhantomData; -use std::ops::{Add, Sub}; +use std::ops::{Add, MulAssign, Neg, Sub}; /// Computations are expressed in terms of arithmetic circuits, in particular /// rank-1 quadratic constraint systems. The `Circuit` trait represents a @@ -109,10 +215,9 @@ impl Add<(E::Fr, Variable)> for LinearCombination { impl Sub<(E::Fr, Variable)> for LinearCombination { type Output = LinearCombination; - fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { - coeff.negate(); - - self + (coeff, var) + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { + self + (coeff.neg(), var) } } @@ -230,8 +335,8 @@ impl Error for SynthesisError { } impl fmt::Display for SynthesisError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - if let &SynthesisError::IoError(ref e) = self { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + if let SynthesisError::IoError(ref e) = *self { write!(f, "I/O error: ")?; e.fmt(f) } else { @@ -296,7 +401,7 @@ pub trait ConstraintSystem: Sized { fn get_root(&mut self) -> &mut Self::Root; /// Begin a namespace for this constraint system. - fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root> + fn namespace(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root> where NR: Into, N: FnOnce() -> NR, @@ -309,7 +414,7 @@ pub trait ConstraintSystem: Sized { /// This is a "namespaced" constraint system which borrows a constraint system (pushing /// a namespace context) and, when dropped, pops out of the namespace context. -pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData); +pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem>(&'a mut CS, PhantomData); impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { type Root = CS::Root; diff --git a/bellman/src/multicore.rs b/bellman/src/multicore.rs index e8b2dae..ba69b5f 100644 --- a/bellman/src/multicore.rs +++ b/bellman/src/multicore.rs @@ -1,12 +1,13 @@ -//! This is an interface for dealing with the kinds of -//! parallel computations involved in bellman. It's -//! currently just a thin wrapper around CpuPool and -//! crossbeam but may be extended in the future to -//! allow for various parallelism strategies. +//! An interface for dealing with the kinds of parallel computations involved in +//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and +//! [`crossbeam`] but may be extended in the future to allow for various +//! parallelism strategies. +//! +//! [`CpuPool`]: futures_cpupool::CpuPool #[cfg(feature = "multicore")] mod implementation { - use crossbeam::{self, Scope}; + use crossbeam::{self, thread::Scope}; use futures::{Future, IntoFuture, Poll}; use futures_cpupool::{CpuFuture, CpuPool}; use num_cpus; @@ -23,7 +24,7 @@ mod implementation { // CPUs configured. pub(crate) fn new_with_cpus(cpus: usize) -> Worker { Worker { - cpus: cpus, + cpus, pool: CpuPool::new(cpus), } } @@ -59,7 +60,9 @@ mod implementation { elements / self.cpus }; + // TODO: Handle case where threads fail crossbeam::scope(|scope| f(scope, chunk_size)) + .expect("Threads aren't allowed to fail yet") } } @@ -152,8 +155,8 @@ mod implementation { pub struct DummyScope; impl DummyScope { - pub fn spawn(&self, f: F) { - f(); + pub fn spawn(&self, f: F) { + f(self); } } } diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index fabb978..deed9fa 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -1,10 +1,11 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use ff::{Endianness, Field, PrimeField, ScalarEngine}; use futures::Future; use group::{CurveAffine, CurveProjective}; use std::io; use std::iter; +use std::ops::AddAssign; use std::sync::Arc; use super::SynthesisError; @@ -18,16 +19,24 @@ pub trait SourceBuilder: Send + Sync + 'static + Clone { /// A source of bases, like an iterator. pub trait Source { - /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_mixed( - &mut self, - to: &mut ::Projective, - ) -> Result<(), SynthesisError>; + fn next(&mut self) -> Result<&G, SynthesisError>; /// Skips `amt` elements from the source, avoiding deserialization. fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } +pub trait AddAssignFromSource: CurveProjective { + /// Parses the element from the source. Fails if the point is at infinity. + fn add_assign_from_source::Affine>>( + &mut self, + source: &mut S, + ) -> Result<(), SynthesisError> { + AddAssign::<&::Affine>::add_assign(self, source.next()?); + Ok(()) + } +} +impl AddAssignFromSource for G where G: CurveProjective {} + impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); @@ -37,10 +46,7 @@ impl SourceBuilder for (Arc>, usize) { } impl Source for (Arc>, usize) { - fn add_assign_mixed( - &mut self, - to: &mut ::Projective, - ) -> Result<(), SynthesisError> { + fn next(&mut self) -> Result<&G, SynthesisError> { if self.0.len() <= self.1 { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, @@ -53,11 +59,10 @@ impl Source for (Arc>, usize) { return Err(SynthesisError::UnexpectedIdentity); } - to.add_assign_mixed(&self.0[self.1]); - + let ret = &self.0[self.1]; self.1 += 1; - Ok(()) + Ok(ret) } fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { @@ -149,16 +154,16 @@ fn multiexp_inner( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, + exponents: Arc::Fr>>, mut skip: u32, c: u32, handle_trivial: bool, -) -> Box::Projective, Error = SynthesisError>> +) -> Box> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveAffine, - S: SourceBuilder, + G: CurveProjective, + S: SourceBuilder<::Affine>, { // Perform this region of the multiexp let this = { @@ -168,35 +173,44 @@ where pool.compute(move || { // Accumulate the result - let mut acc = G::Projective::zero(); + let mut acc = G::zero(); // Build a source for the bases let mut bases = bases.new(); // Create space for the buckets - let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; + let mut buckets = vec![G::zero(); (1 << c) - 1]; - let zero = ::Fr::zero().into_repr(); - let one = ::Fr::one().into_repr(); + let one = ::Fr::one(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { if density { - if exp == zero { + if exp.is_zero() { bases.skip(1)?; } else if exp == one { if handle_trivial { - bases.add_assign_mixed(&mut acc)?; + acc.add_assign_from_source(&mut bases)?; } else { bases.skip(1)?; } } else { - let mut exp = exp; - exp.shr(skip); - let exp = exp.as_ref()[0] % (1 << c); + let mut exp = exp.to_repr(); + <::Fr as PrimeField>::ReprEndianness::toggle_little_endian(&mut exp); + + let exp = exp + .as_ref() + .into_iter() + .map(|b| (0..8).map(move |i| (b >> i) & 1u8)) + .flatten() + .skip(skip as usize) + .take(c as usize) + .enumerate() + .fold(0u64, |acc, (i, b)| acc + ((b as u64) << i)); if exp != 0 { - bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?; + (&mut buckets[(exp - 1) as usize]) + .add_assign_from_source(&mut bases)?; } else { bases.skip(1)?; } @@ -208,7 +222,7 @@ where // e.g. 3a + 2b + 1c = a + // (a) + b + // ((a) + b) + c - let mut running_sum = G::Projective::zero(); + let mut running_sum = G::zero(); for exp in buckets.into_iter().rev() { running_sum.add_assign(&exp); acc.add_assign(&running_sum); @@ -236,7 +250,7 @@ where c, false, )) - .map(move |(this, mut higher)| { + .map(move |(this, mut higher): (_, G)| { for _ in 0..c { higher.double(); } @@ -255,13 +269,13 @@ pub fn multiexp( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, -) -> Box::Projective, Error = SynthesisError>> + exponents: Arc::Fr>>, +) -> Box> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveAffine, - S: SourceBuilder, + G: CurveProjective, + S: SourceBuilder<::Affine>, { let c = if exponents.len() < 32 { 3u32 @@ -282,16 +296,16 @@ where #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { - fn naive_multiexp( - bases: Arc>, - exponents: Arc::Repr>>, - ) -> G::Projective { + fn naive_multiexp( + bases: Arc::Affine>>, + exponents: Arc>, + ) -> G { assert_eq!(bases.len(), exponents.len()); - let mut acc = G::Projective::zero(); + let mut acc = G::zero(); for (base, exp) in bases.iter().zip(exponents.iter()) { - acc.add_assign(&base.mul(*exp)); + AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.to_repr())); } acc @@ -305,7 +319,7 @@ fn test_with_bls12() { let rng = &mut rand::thread_rng(); let v = Arc::new( (0..SAMPLES) - .map(|_| ::Fr::random(rng).into_repr()) + .map(|_| ::Fr::random(rng)) .collect::>(), ); let g = Arc::new( @@ -314,7 +328,7 @@ fn test_with_bls12() { .collect::>(), ); - let naive = naive_multiexp(g.clone(), v.clone()); + let naive: ::G1 = naive_multiexp(g.clone(), v.clone()); let pool = Worker::new(); diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs index 18aaece..a1de0f1 100644 --- a/bellman/tests/mimc.rs +++ b/bellman/tests/mimc.rs @@ -1,8 +1,3 @@ -extern crate bellman; -extern crate ff; -extern crate pairing; -extern crate rand; - // For randomness (during paramgen and proof generation) use rand::thread_rng; @@ -12,6 +7,7 @@ use std::time::{Duration, Instant}; // Bring in some tools for using pairing-friendly curves use ff::{Field, ScalarEngine}; use pairing::Engine; +use std::ops::{AddAssign, MulAssign}; // We're going to use the BLS12-381 pairing-friendly elliptic curve. use pairing::bls12_381::Bls12; @@ -45,8 +41,7 @@ fn mimc(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr { for i in 0..MIMC_ROUNDS { let mut tmp1 = xl; tmp1.add_assign(&constants[i]); - let mut tmp2 = tmp1; - tmp2.square(); + let mut tmp2 = tmp1.square(); tmp2.mul_assign(&tmp1); tmp2.add_assign(&xr); xr = xl; @@ -90,12 +85,11 @@ impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { let cs = &mut cs.namespace(|| format!("round {}", i)); // tmp = (xL + Ci)^2 - let mut tmp_value = xl_value.map(|mut e| { + let tmp_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); - e.square(); - e + e.square() }); - let mut tmp = cs.alloc( + let tmp = cs.alloc( || "tmp", || tmp_value.ok_or(SynthesisError::AssignmentMissing), )?; @@ -110,14 +104,14 @@ impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { // new_xL = xR + (xL + Ci)^3 // new_xL = xR + tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci) - let mut new_xl_value = xl_value.map(|mut e| { + let new_xl_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.mul_assign(&tmp_value.unwrap()); e.add_assign(&xr_value.unwrap()); e }); - let mut new_xl = if i == (MIMC_ROUNDS - 1) { + let new_xl = if i == (MIMC_ROUNDS - 1) { // This is the last round, xL is our image and so // we allocate a public input. cs.alloc_input( diff --git a/bls12_381/.github/workflows/ci.yml b/bls12_381/.github/workflows/ci.yml new file mode 100644 index 0000000..39066db --- /dev/null +++ b/bls12_381/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + + # Ensure all code has been formatted with rustfmt + - run: rustup component add rustfmt + - name: Check formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check --color always + + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build tests + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --tests + - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release + + no-std: + name: Check no-std compatibility + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - run: rustup target add thumbv6m-none-eabi + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings + + doc-links: + name: Nightly lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + + # Ensure intra-documentation links all resolve correctly + # Requires #![deny(intra_doc_link_resolution_failure)] in crate. + - name: Check intra-doc links + uses: actions-rs/cargo@v1 + with: + command: doc + args: --document-private-items diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore new file mode 100644 index 0000000..2f88dba --- /dev/null +++ b/bls12_381/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock \ No newline at end of file diff --git a/bls12_381/COPYRIGHT b/bls12_381/COPYRIGHT new file mode 100644 index 0000000..7764b86 --- /dev/null +++ b/bls12_381/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "bls12_381" library are retained by their contributors. No +copyright assignment is required to contribute to the "bls12_381" library. + +The "bls12_381" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml new file mode 100644 index 0000000..6e77fb5 --- /dev/null +++ b/bls12_381/Cargo.toml @@ -0,0 +1,32 @@ +[package] +authors = ["Sean Bowe "] +description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction" +documentation = "https://docs.rs/bls12_381/" +homepage = "https://github.com/zkcrypto/bls12_381" +license = "MIT/Apache-2.0" +name = "bls12_381" +repository = "https://github.com/zkcrypto/bls12_381" +version = "0.1.0" +edition = "2018" + +[package.metadata.docs.rs] +rustdoc-args = [ "--html-in-header", "katex-header.html" ] + +[dev-dependencies] +criterion = "0.3" + +[[bench]] +name = "groups" +harness = false +required-features = ["groups"] + +[dependencies.subtle] +version = "2.2.1" +default-features = false + +[features] +default = ["groups", "pairings", "alloc"] +groups = [] +pairings = ["groups"] +alloc = [] +nightly = ["subtle/nightly"] diff --git a/bls12_381/LICENSE-APACHE b/bls12_381/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/bls12_381/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/bls12_381/LICENSE-MIT b/bls12_381/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/bls12_381/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/bls12_381/README.md b/bls12_381/README.md new file mode 100644 index 0000000..ba61f30 --- /dev/null +++ b/bls12_381/README.md @@ -0,0 +1,63 @@ +# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) # + +This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction. + +* **This implementation has not been reviewed or audited. Use at your own risk.** +* This implementation targets Rust `1.36` or later. +* This implementation does not require the Rust standard library. +* All operations are constant time unless explicitly noted. + +## Features + +* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT. +* `pairings` (on by default): Enables some APIs for performing pairings. +* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. +* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. + +## [Documentation](https://docs.rs/bls12_381) + +## Curve Description + +BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with... + +* z = `-0xd201000000010000` +* p = (z - 1)2(z4 - z2 + 1) / 3 + z + * = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` +* q = z4 - z2 + 1 + * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` + +... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extention field Fp2 is defined as Fp(u) / (u2 + 1). + +BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. + +### Curve Security + +Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. + +In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. + +There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) + +### Alternative Curves + +Applications may wish to exchange pairing performance and/or G2 performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe. + +## Acknowledgements + +Please see `Cargo.toml` for a list of primary authors of this codebase. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/bls12_381/RELEASES.md b/bls12_381/RELEASES.md new file mode 100644 index 0000000..69afd52 --- /dev/null +++ b/bls12_381/RELEASES.md @@ -0,0 +1,3 @@ +# 0.1.0 + +Initial release. diff --git a/bls12_381/benches/groups.rs b/bls12_381/benches/groups.rs new file mode 100644 index 0000000..87c80d0 --- /dev/null +++ b/bls12_381/benches/groups.rs @@ -0,0 +1,170 @@ +#[macro_use] +extern crate criterion; + +extern crate bls12_381; +use bls12_381::*; + +use criterion::{black_box, Criterion}; + +fn criterion_benchmark(c: &mut Criterion) { + // Pairings + { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + c.bench_function("full pairing", move |b| { + b.iter(|| pairing(black_box(&g), black_box(&h))) + }); + c.bench_function("G2 preparation for pairing", move |b| { + b.iter(|| G2Prepared::from(h)) + }); + let prep = G2Prepared::from(h); + c.bench_function("miller loop for pairing", move |b| { + b.iter(|| multi_miller_loop(&[(&g, &prep)])) + }); + let prep = G2Prepared::from(h); + let r = multi_miller_loop(&[(&g, &prep)]); + c.bench_function("final exponentiation for pairing", move |b| { + b.iter(|| r.final_exponentiation()) + }); + } + // G1Affine + { + let name = "G1Affine"; + let a = G1Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + let compressed = [0u8; 48]; + let uncompressed = [0u8; 96]; + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); + c.bench_function( + &format!("{} deserialize compressed point", name), + move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))), + ); + c.bench_function( + &format!("{} deserialize uncompressed point", name), + move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))), + ); + } + + // G1Projective + { + let name = "G1Projective"; + let a = G1Projective::generator(); + let a_affine = G1Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + + const N: usize = 10000; + let v = vec![G1Projective::generator(); N]; + let mut q = vec![G1Affine::identity(); N]; + + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} to affine", name), move |b| { + b.iter(|| G1Affine::from(black_box(a))) + }); + c.bench_function(&format!("{} doubling", name), move |b| { + b.iter(|| black_box(a).double()) + }); + c.bench_function(&format!("{} addition", name), move |b| { + b.iter(|| black_box(a).add(&a)) + }); + c.bench_function(&format!("{} mixed addition", name), move |b| { + b.iter(|| black_box(a).add_mixed(&a_affine)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { + b.iter(|| { + G1Projective::batch_normalize(black_box(&v), black_box(&mut q)); + black_box(&q)[0] + }) + }); + } + + // G2Affine + { + let name = "G2Affine"; + let a = G2Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + let compressed = [0u8; 96]; + let uncompressed = [0u8; 192]; + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); + c.bench_function( + &format!("{} deserialize compressed point", name), + move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))), + ); + c.bench_function( + &format!("{} deserialize uncompressed point", name), + move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))), + ); + } + + // G2Projective + { + let name = "G2Projective"; + let a = G2Projective::generator(); + let a_affine = G2Affine::generator(); + let s = Scalar::from_raw([1, 2, 3, 4]); + + const N: usize = 10000; + let v = vec![G2Projective::generator(); N]; + let mut q = vec![G2Affine::identity(); N]; + + c.bench_function(&format!("{} check on curve", name), move |b| { + b.iter(|| black_box(a).is_on_curve()) + }); + c.bench_function(&format!("{} check equality", name), move |b| { + b.iter(|| black_box(a) == black_box(a)) + }); + c.bench_function(&format!("{} to affine", name), move |b| { + b.iter(|| G2Affine::from(black_box(a))) + }); + c.bench_function(&format!("{} doubling", name), move |b| { + b.iter(|| black_box(a).double()) + }); + c.bench_function(&format!("{} addition", name), move |b| { + b.iter(|| black_box(a).add(&a)) + }); + c.bench_function(&format!("{} mixed addition", name), move |b| { + b.iter(|| black_box(a).add_mixed(&a_affine)) + }); + c.bench_function(&format!("{} scalar multiplication", name), move |b| { + b.iter(|| black_box(a) * black_box(s)) + }); + c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { + b.iter(|| { + G2Projective::batch_normalize(black_box(&v), black_box(&mut q)); + black_box(&q)[0] + }) + }); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/bls12_381/katex-header.html b/bls12_381/katex-header.html new file mode 100644 index 0000000..98e8590 --- /dev/null +++ b/bls12_381/katex-header.html @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/bls12_381/rust-toolchain b/bls12_381/rust-toolchain new file mode 100644 index 0000000..d70132e --- /dev/null +++ b/bls12_381/rust-toolchain @@ -0,0 +1 @@ +1.36.0 \ No newline at end of file diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs new file mode 100644 index 0000000..28aa24b --- /dev/null +++ b/bls12_381/src/fp.rs @@ -0,0 +1,866 @@ +//! This module provides an implementation of the BLS12-381 base field `GF(p)` +//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` + +use core::convert::TryFrom; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::util::{adc, mac, sbb}; + +// The internal representation of this type is six 64-bit unsigned +// integers in little-endian order. `Fp` values are always in +// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. +#[derive(Copy, Clone)] +pub struct Fp([u64; 6]); + +impl fmt::Debug for Fp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl Default for Fp { + fn default() -> Self { + Fp::zero() + } +} + +impl ConstantTimeEq for Fp { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + & self.0[4].ct_eq(&other.0[4]) + & self.0[5].ct_eq(&other.0[5]) + } +} + +impl Eq for Fp {} +impl PartialEq for Fp { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Fp { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + u64::conditional_select(&a.0[4], &b.0[4], choice), + u64::conditional_select(&a.0[5], &b.0[5], choice), + ]) + } +} + +/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +const MODULUS: [u64; 6] = [ + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, +]; + +/// INV = -(p^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x89f3_fffc_fffc_fffd; + +/// R = 2^384 mod p +const R: Fp = Fp([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, +]); + +/// R2 = 2^(384*2) mod p +const R2: Fp = Fp([ + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 0x1198_8fe5_92ca_e3aa, +]); + +impl<'a> Neg for &'a Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + self.neg() + } +} + +impl Neg for Fp { + type Output = Fp; + + #[inline] + fn neg(self) -> Fp { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn sub(self, rhs: &'b Fp) -> Fp { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn add(self, rhs: &'b Fp) -> Fp { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp> for &'a Fp { + type Output = Fp; + + #[inline] + fn mul(self, rhs: &'b Fp) -> Fp { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp, Fp); +impl_binops_multiplicative!(Fp, Fp); + +impl Fp { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Fp { + Fp([0, 0, 0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Fp { + R + } + + pub fn is_zero(&self) -> Choice { + self.ct_eq(&Fp::zero()) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into an `Fp`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { + let mut tmp = Fp([0, 0, 0, 0, 0, 0]); + + tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); + tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); + let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); + let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Fp` into a byte representation in + /// big-endian byte order. + pub fn to_bytes(&self) -> [u8; 48] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let mut res = [0; 48]; + res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes()); + res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes()); + res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes()); + res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes()); + res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes()); + res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes()); + + res + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + pub fn lexicographically_largest(&self) -> Choice { + // This can be determined by checking to see if the element is + // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 + // and there is no underflow, then the element must be larger than + // (p - 1) // 2. + + // First, because self is in Montgomery form we need to reduce it + let tmp = Fp::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, + ); + + let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0); + let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow); + let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow); + let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow); + let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow); + let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow); + + // If the element was smaller, the subtraction will underflow + // producing a borrow value of 0xffff...ffff, otherwise it will + // be zero. We create a Choice representing true if there was + // overflow (and so this element is not lexicographically larger + // than its negation) and then negate it. + + !Choice::from((borrow as u8) & 1) + } + + /// Constructs an element of `Fp` without checking that it is + /// canonical. + pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp { + Fp(v) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } + + #[inline] + pub fn sqrt(&self) -> CtOption { + // We use Shank's method, as p = 3 (mod 4). This means + // we only need to exponentiate by (p+1)/4. This only + // works for elements that are actually quadratic residue, + // so we check that we got the correct result at the end. + + let sqrt = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + CtOption::new(sqrt, sqrt.square().ct_eq(self)) + } + + #[inline] + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // Exponentiate by p - 2 + let t = self.pow_vartime(&[ + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, + ]); + + CtOption::new(t, !self.is_zero()) + } + + #[inline] + const fn subtract_p(&self) -> Fp { + let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0); + let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow); + let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow); + let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow); + let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow); + let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask! + let r0 = (self.0[0] & borrow) | (r0 & !borrow); + let r1 = (self.0[1] & borrow) | (r1 & !borrow); + let r2 = (self.0[2] & borrow) | (r2 & !borrow); + let r3 = (self.0[3] & borrow) | (r3 & !borrow); + let r4 = (self.0[4] & borrow) | (r4 & !borrow); + let r5 = (self.0[5] & borrow) | (r5 & !borrow); + + Fp([r0, r1, r2, r3, r4, r5]) + } + + #[inline] + pub const fn add(&self, rhs: &Fp) -> Fp { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, carry) = adc(self.0[3], rhs.0[3], carry); + let (d4, carry) = adc(self.0[4], rhs.0[4], carry); + let (d5, _) = adc(self.0[5], rhs.0[5], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fp([d0, d1, d2, d3, d4, d5])).subtract_p() + } + + #[inline] + pub const fn neg(&self) -> Fp { + let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow); + let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow); + let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow); + let (d5, _) = sbb(MODULUS[5], self.0[5], borrow); + + // Let's use a mask if `self` was zero, which would mean + // the result of the subtraction is p. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0) + as u64) + .wrapping_sub(1); + + Fp([ + d0 & mask, + d1 & mask, + d2 & mask, + d3 & mask, + d4 & mask, + d5 & mask, + ]) + } + + #[inline] + pub const fn sub(&self, rhs: &Fp) -> Fp { + (&rhs.neg()).add(self) + } + + #[inline(always)] + const fn montgomery_reduce( + t0: u64, + t1: u64, + t2: u64, + t3: u64, + t4: u64, + t5: u64, + t6: u64, + t7: u64, + t8: u64, + t9: u64, + t10: u64, + t11: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = t0.wrapping_mul(INV); + let (_, carry) = mac(t0, k, MODULUS[0], 0); + let (r1, carry) = mac(t1, k, MODULUS[1], carry); + let (r2, carry) = mac(t2, k, MODULUS[2], carry); + let (r3, carry) = mac(t3, k, MODULUS[3], carry); + let (r4, carry) = mac(t4, k, MODULUS[4], carry); + let (r5, carry) = mac(t5, k, MODULUS[5], carry); + let (r6, r7) = adc(t6, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS[0], 0); + let (r2, carry) = mac(r2, k, MODULUS[1], carry); + let (r3, carry) = mac(r3, k, MODULUS[2], carry); + let (r4, carry) = mac(r4, k, MODULUS[3], carry); + let (r5, carry) = mac(r5, k, MODULUS[4], carry); + let (r6, carry) = mac(r6, k, MODULUS[5], carry); + let (r7, r8) = adc(t7, r7, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS[0], 0); + let (r3, carry) = mac(r3, k, MODULUS[1], carry); + let (r4, carry) = mac(r4, k, MODULUS[2], carry); + let (r5, carry) = mac(r5, k, MODULUS[3], carry); + let (r6, carry) = mac(r6, k, MODULUS[4], carry); + let (r7, carry) = mac(r7, k, MODULUS[5], carry); + let (r8, r9) = adc(t8, r8, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS[0], 0); + let (r4, carry) = mac(r4, k, MODULUS[1], carry); + let (r5, carry) = mac(r5, k, MODULUS[2], carry); + let (r6, carry) = mac(r6, k, MODULUS[3], carry); + let (r7, carry) = mac(r7, k, MODULUS[4], carry); + let (r8, carry) = mac(r8, k, MODULUS[5], carry); + let (r9, r10) = adc(t9, r9, carry); + + let k = r4.wrapping_mul(INV); + let (_, carry) = mac(r4, k, MODULUS[0], 0); + let (r5, carry) = mac(r5, k, MODULUS[1], carry); + let (r6, carry) = mac(r6, k, MODULUS[2], carry); + let (r7, carry) = mac(r7, k, MODULUS[3], carry); + let (r8, carry) = mac(r8, k, MODULUS[4], carry); + let (r9, carry) = mac(r9, k, MODULUS[5], carry); + let (r10, r11) = adc(t10, r10, carry); + + let k = r5.wrapping_mul(INV); + let (_, carry) = mac(r5, k, MODULUS[0], 0); + let (r6, carry) = mac(r6, k, MODULUS[1], carry); + let (r7, carry) = mac(r7, k, MODULUS[2], carry); + let (r8, carry) = mac(r8, k, MODULUS[3], carry); + let (r9, carry) = mac(r9, k, MODULUS[4], carry); + let (r10, carry) = mac(r10, k, MODULUS[5], carry); + let (r11, _) = adc(t11, r11, carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fp([r6, r7, r8, r9, r10, r11])).subtract_p() + } + + #[inline] + pub const fn mul(&self, rhs: &Fp) -> Fp { + let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry); + let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry); + let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry); + + let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0); + let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry); + let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry); + let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry); + + let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0); + let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry); + let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry); + let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry); + let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry); + + let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0); + let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry); + let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry); + let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry); + let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry); + let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry); + + let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0); + let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry); + let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry); + let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry); + let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry); + let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry); + + let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0); + let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry); + let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry); + let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry); + let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry); + let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Self { + let (t1, carry) = mac(0, self.0[0], self.0[1], 0); + let (t2, carry) = mac(0, self.0[0], self.0[2], carry); + let (t3, carry) = mac(0, self.0[0], self.0[3], carry); + let (t4, carry) = mac(0, self.0[0], self.0[4], carry); + let (t5, t6) = mac(0, self.0[0], self.0[5], carry); + + let (t3, carry) = mac(t3, self.0[1], self.0[2], 0); + let (t4, carry) = mac(t4, self.0[1], self.0[3], carry); + let (t5, carry) = mac(t5, self.0[1], self.0[4], carry); + let (t6, t7) = mac(t6, self.0[1], self.0[5], carry); + + let (t5, carry) = mac(t5, self.0[2], self.0[3], 0); + let (t6, carry) = mac(t6, self.0[2], self.0[4], carry); + let (t7, t8) = mac(t7, self.0[2], self.0[5], carry); + + let (t7, carry) = mac(t7, self.0[3], self.0[4], 0); + let (t8, t9) = mac(t8, self.0[3], self.0[5], carry); + + let (t9, t10) = mac(t9, self.0[4], self.0[5], 0); + + let t11 = t10 >> 63; + let t10 = (t10 << 1) | (t9 >> 63); + let t9 = (t9 << 1) | (t8 >> 63); + let t8 = (t8 << 1) | (t7 >> 63); + let t7 = (t7 << 1) | (t6 >> 63); + let t6 = (t6 << 1) | (t5 >> 63); + let t5 = (t5 << 1) | (t4 >> 63); + let t4 = (t4 << 1) | (t3 >> 63); + let t3 = (t3 << 1) | (t2 >> 63); + let t2 = (t2 << 1) | (t1 >> 63); + let t1 = t1 << 1; + + let (t0, carry) = mac(0, self.0[0], self.0[0], 0); + let (t1, carry) = adc(t1, 0, carry); + let (t2, carry) = mac(t2, self.0[1], self.0[1], carry); + let (t3, carry) = adc(t3, 0, carry); + let (t4, carry) = mac(t4, self.0[2], self.0[2], carry); + let (t5, carry) = adc(t5, 0, carry); + let (t6, carry) = mac(t6, self.0[3], self.0[3], carry); + let (t7, carry) = adc(t7, 0, carry); + let (t8, carry) = mac(t8, self.0[4], self.0[4], carry); + let (t9, carry) = adc(t9, 0, carry); + let (t10, carry) = mac(t10, self.0[5], self.0[5], carry); + let (t11, _) = adc(t11, 0, carry); + + Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } +} + +#[test] +fn test_conditional_selection() { + let a = Fp([1, 2, 3, 4, 5, 6]); + let b = Fp([7, 8, 9, 10, 11, 12]); + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp, b: &Fp) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); + + assert_eq!(eq, ct_eq.unwrap_u8() == 1); + + eq + } + + assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + + assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); + assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); +} + +#[test] +fn test_squaring() { + let a = Fp([ + 0xd215_d276_8e83_191b, + 0x5085_d80f_8fb2_8261, + 0xce9a_032d_df39_3a56, + 0x3e9c_4fff_2ca0_c4bb, + 0x6436_b6f7_f4d9_5dfb, + 0x1060_6628_ad4a_4d90, + ]); + let b = Fp([ + 0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560, + ]); + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp([ + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb, + ]); + let b = Fp([ + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0, + ]); + let c = Fp([ + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4, + ]); + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x3934_42cc_b58b_b327, + 0x1092_685f_3bd5_47e3, + 0x3382_252c_ab6a_c4c9, + 0xf946_94cb_7688_7f55, + 0x4b21_5e90_93a5_e071, + 0x0d56_e30f_34f5_f853, + ]); + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091, + ]); + let c = Fp([ + 0x6d8d_33e6_3b43_4d3d, + 0xeb12_82fd_b766_dd39, + 0x8534_7bb6_f133_d6d5, + 0xa21d_aa5a_9892_f727, + 0x3b25_6cfb_3ad8_ae23, + 0x155d_7199_de7f_8464, + ]); + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]); + let b = Fp([ + 0x669e_44a6_8798_2a79, + 0xa0d9_8a50_37b5_ed71, + 0x0ad5_822f_2861_a854, + 0x96c5_2bf1_ebf7_5781, + 0x87f8_41f0_5c0c_658c, + 0x08a6_e795_afc5_283e, + ]); + + assert_eq!(-a, b); +} + +#[test] +fn test_debug() { + assert_eq!( + format!( + "{:?}", + Fp([ + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b, + ]) + ), + "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" + ); +} + +#[test] +fn test_from_bytes() { + let mut a = Fp([ + 0xdc90_6d9b_e3f9_5dc8, + 0x8755_caf7_4596_91a1, + 0xcff1_a7f4_e958_3ab3, + 0x9b43_821f_849e_2284, + 0xf575_54f3_a297_4f3f, + 0x085d_bea8_4ed4_7f79, + ]); + + for _ in 0..100 { + a = a.square(); + let tmp = a.to_bytes(); + let b = Fp::from_bytes(&tmp).unwrap(); + + assert_eq!(a, b); + } + + assert_eq!( + -Fp::one(), + Fp::from_bytes(&[ + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .unwrap() + ); + + assert!( + Fp::from_bytes(&[ + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1); +} + +#[test] +fn test_sqrt() { + // a = 4 + let a = Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]); + + assert_eq!( + // sqrt(4) = -2 + -a.sqrt().unwrap(), + // 2 + Fp::from_raw_unchecked([ + 0x3213_0000_0006_554f, + 0xb93c_0018_d6c4_0005, + 0x5760_5e0d_b0dd_bb51, + 0x8b25_6521_ed1f_9bcb, + 0x6cf2_8d79_0162_2c03, + 0x11eb_ab9d_bb81_e28c, + ]) + ); +} + +#[test] +fn test_inversion() { + let a = Fp([ + 0x43b4_3a50_78ac_2076, + 0x1ce0_7630_46f8_962b, + 0x724a_5276_486d_735c, + 0x6f05_c2a6_282d_48fd, + 0x2095_bd5b_b4ca_9331, + 0x03b3_5b38_94b0_f7da, + ]); + let b = Fp([ + 0x69ec_d704_0952_148f, + 0x985c_cc20_2219_0f55, + 0xe19b_ba36_a9ad_2f41, + 0x19bb_16c9_5219_dbd8, + 0x14dc_acfd_fb47_8693, + 0x115f_f58a_fff9_a8e1, + ]); + + assert_eq!(a.invert().unwrap(), b); + assert!(Fp::zero().invert().is_none().unwrap_u8() == 1); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp::zero().lexicographically_largest())); + assert!(!bool::from(Fp::one().lexicographically_largest())); + assert!(!bool::from( + Fp::from_raw_unchecked([ + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596, + ]) + .lexicographically_largest() + )); + assert!(bool::from( + Fp::from_raw_unchecked([ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ]) + .lexicographically_largest() + )); +} diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs new file mode 100644 index 0000000..735f91e --- /dev/null +++ b/bls12_381/src/fp12.rs @@ -0,0 +1,635 @@ +use crate::fp::*; +use crate::fp2::*; +use crate::fp6::*; + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$. +pub struct Fp12 { + pub c0: Fp6, + pub c1: Fp6, +} + +impl From for Fp12 { + fn from(f: Fp) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp2) -> Fp12 { + Fp12 { + c0: Fp6::from(f), + c1: Fp6::zero(), + } + } +} + +impl From for Fp12 { + fn from(f: Fp6) -> Fp12 { + Fp12 { + c0: f, + c1: Fp6::zero(), + } + } +} + +impl PartialEq for Fp12 { + fn eq(&self, other: &Fp12) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp12 {} +impl Clone for Fp12 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp12 { + fn default() -> Self { + Fp12::zero() + } +} + +impl fmt::Debug for Fp12 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*w", self.c0, self.c1) + } +} + +impl ConditionallySelectable for Fp12 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp12 { + c0: Fp6::conditional_select(&a.c0, &b.c0, choice), + c1: Fp6::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl ConstantTimeEq for Fp12 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Fp12 { + #[inline] + pub fn zero() -> Self { + Fp12 { + c0: Fp6::zero(), + c1: Fp6::zero(), + } + } + + #[inline] + pub fn one() -> Self { + Fp12 { + c0: Fp6::one(), + c1: Fp6::zero(), + } + } + + pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { + let aa = self.c0.mul_by_01(c0, c1); + let bb = self.c1.mul_by_1(c4); + let o = c1 + c4; + let c1 = self.c1 + self.c0; + let c1 = c1.mul_by_01(c0, &o); + let c1 = c1 - aa - bb; + let c0 = bb; + let c0 = c0.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp12 { + c0: self.c0, + c1: -self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 6) + let c1 = c1 + * Fp6::from(Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0708_9552_b319_d465, + 0xc669_5f92_b50a_8313, + 0x97e8_3ccc_d117_228f, + 0xa35b_aeca_b2dc_29ee, + 0x1ce3_93ea_5daa_ce4d, + 0x08f2_220f_b0fb_66eb, + ]), + c1: Fp::from_raw_unchecked([ + 0xb2f6_6aad_4ce5_d646, + 0x5842_a06b_fc49_7cec, + 0xcf48_95d4_2599_d394, + 0xc11b_9cba_40a8_e8d0, + 0x2e38_13cb_e5a0_de89, + 0x110e_efda_8884_7faf, + ]), + }); + + Fp12 { c0, c1 } + } + + #[inline] + pub fn square(&self) -> Self { + let ab = self.c0 * self.c1; + let c0c1 = self.c0 + self.c1; + let c0 = self.c1.mul_by_nonresidue(); + let c0 = c0 + self.c0; + let c0 = c0 * c0c1; + let c0 = c0 - ab; + let c1 = ab + ab; + let c0 = c0 - ab.mul_by_nonresidue(); + + Fp12 { c0, c1 } + } + + pub fn invert(&self) -> CtOption { + (self.c0.square() - self.c1.square().mul_by_nonresidue()) + .invert() + .map(|t| Fp12 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } +} + +impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn mul(self, other: &'b Fp12) -> Self::Output { + let aa = self.c0 * other.c0; + let bb = self.c1 * other.c1; + let o = other.c0 + other.c1; + let c1 = self.c1 + self.c0; + let c1 = c1 * o; + let c1 = c1 - aa; + let c1 = c1 - bb; + let c0 = bb.mul_by_nonresidue(); + let c0 = c0 + aa; + + Fp12 { c0, c1 } + } +} + +impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn add(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + } + } +} + +impl<'a> Neg for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + Fp12 { + c0: -self.c0, + c1: -self.c1, + } + } +} + +impl Neg for Fp12 { + type Output = Fp12; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { + type Output = Fp12; + + #[inline] + fn sub(self, rhs: &'b Fp12) -> Self::Output { + Fp12 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + } + } +} + +impl_binops_additive!(Fp12, Fp12); +impl_binops_multiplicative!(Fp12, Fp12); + +#[test] +fn test_arithmetic() { + use crate::fp::*; + use crate::fp2::*; + + let a = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + }; + + let b = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d272_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e348, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd2_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a117_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }, + }; + + let c = Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_71b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0x7791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_133c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_40e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_1744_c040, + ]), + }, + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d3_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_1040, + ]), + }, + }, + }; + + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + let a = a.square().invert().unwrap().square() + c; + let b = b.square().invert().unwrap().square() + a; + let c = c.square().invert().unwrap().square() + b; + + assert_eq!(a.square(), a * a); + assert_eq!(b.square(), b * b); + assert_eq!(c.square(), c * c); + + assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); + + assert_eq!( + a.invert().unwrap() * b.invert().unwrap(), + (a * b).invert().unwrap() + ); + assert_eq!(a.invert().unwrap() * a, Fp12::one()); + + assert!(a != a.frobenius_map()); + assert_eq!( + a, + a.frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + ); +} diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs new file mode 100644 index 0000000..3890d31 --- /dev/null +++ b/bls12_381/src/fp2.rs @@ -0,0 +1,868 @@ +//! This module implements arithmetic over the quadratic extension field Fp2. + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; + +#[derive(Copy, Clone)] +pub struct Fp2 { + pub c0: Fp, + pub c1: Fp, +} + +impl fmt::Debug for Fp2 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + {:?}*u", self.c0, self.c1) + } +} + +impl Default for Fp2 { + fn default() -> Self { + Fp2::zero() + } +} + +impl From for Fp2 { + fn from(f: Fp) -> Fp2 { + Fp2 { + c0: f, + c1: Fp::zero(), + } + } +} + +impl ConstantTimeEq for Fp2 { + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} + +impl Eq for Fp2 {} +impl PartialEq for Fp2 { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Fp2 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp2 { + c0: Fp::conditional_select(&a.c0, &b.c0, choice), + c1: Fp::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl<'a> Neg for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + self.neg() + } +} + +impl Neg for Fp2 { + type Output = Fp2; + + #[inline] + fn neg(self) -> Fp2 { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn sub(self, rhs: &'b Fp2) -> Fp2 { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn add(self, rhs: &'b Fp2) -> Fp2 { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 { + type Output = Fp2; + + #[inline] + fn mul(self, rhs: &'b Fp2) -> Fp2 { + self.mul(rhs) + } +} + +impl_binops_additive!(Fp2, Fp2); +impl_binops_multiplicative!(Fp2, Fp2); + +impl Fp2 { + #[inline] + pub const fn zero() -> Fp2 { + Fp2 { + c0: Fp::zero(), + c1: Fp::zero(), + } + } + + #[inline] + pub const fn one() -> Fp2 { + Fp2 { + c0: Fp::one(), + c1: Fp::zero(), + } + } + + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + self.conjugate() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Fp2 { + c0: self.c0, + c1: -self.c1, + } + } + + #[inline(always)] + pub fn mul_by_nonresidue(&self) -> Fp2 { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u + + Fp2 { + c0: self.c0 - self.c1, + c1: self.c0 + self.c1, + } + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + #[inline] + pub fn lexicographically_largest(&self) -> Choice { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. + + self.c1.lexicographically_largest() + | (self.c1.is_zero() & self.c0.lexicographically_largest()) + } + + pub const fn square(&self) -> Fp2 { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + let a = (&self.c0).add(&self.c1); + let b = (&self.c0).sub(&self.c1); + let c = (&self.c0).add(&self.c0); + + Fp2 { + c0: (&a).mul(&b), + c1: (&c).mul(&self.c1), + } + } + + pub const fn mul(&self, rhs: &Fp2) -> Fp2 { + // Karatsuba multiplication: + // + // v0 = a0 * b0 + // v1 = a1 * b1 + // c0 = v0 + \beta * v1 + // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula. (Also, since we always + // subtract v1, we can compute v1 = -a1 * b1.) + // + // v0 = a0 * b0 + // v1 = (-a1) * b1 + // c0 = v0 + v1 + // c1 = (a0 + a1) * (b0 + b1) - v0 + v1 + + let v0 = (&self.c0).mul(&rhs.c0); + let v1 = (&(&self.c1).neg()).mul(&rhs.c1); + let c0 = (&v0).add(&v1); + let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1)); + let c1 = (&c1).sub(&v0); + let c1 = (&c1).add(&v1); + + Fp2 { c0, c1 } + } + + pub const fn add(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).add(&rhs.c0), + c1: (&self.c1).add(&rhs.c1), + } + } + + pub const fn sub(&self, rhs: &Fp2) -> Fp2 { + Fp2 { + c0: (&self.c0).sub(&rhs.c0), + c1: (&self.c1).sub(&rhs.c1), + } + } + + pub const fn neg(&self) -> Fp2 { + Fp2 { + c0: (&self.c0).neg(), + c1: (&self.c1).neg(), + } + } + + pub fn sqrt(&self) -> CtOption { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + CtOption::new( + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(&Fp2::one()).neg()), + ) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + ]) * x0, + Choice::from(1), + ) + }) + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) + }) + } + + /// Computes the multiplicative inverse of this field + /// element, returning None in the case that this element + /// is zero. + pub fn invert(&self) -> CtOption { + // We wish to find the multiplicative inverse of a nonzero + // element a + bu in Fp2. We leverage an identity + // + // (a + bu)(a - bu) = a^2 + b^2 + // + // which holds because u^2 = -1. This can be rewritten as + // + // (a + bu)(a - bu)/(a^2 + b^2) = 1 + // + // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). + // This gives that (a - bu)/(a^2 + b^2) is the inverse + // of (a + bu). Importantly, this can be computing using + // only a single inversion in Fp. + + (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } + + /// Although this is labeled "vartime", it is only + /// variable time with respect to the exponent. It + /// is also not exposed in the public API. + pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res *= self; + } + } + } + res + } +} + +#[test] +fn test_conditional_selection() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), + c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), + }; + + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_equality() { + fn is_equal(a: &Fp2, b: &Fp2) -> bool { + let eq = a == b; + let ct_eq = a.ct_eq(&b); + + assert_eq!(eq, ct_eq.unwrap_u8() == 1); + + eq + } + + assert!(is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); + + assert!(!is_equal( + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), + }, + &Fp2 { + c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), + c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), + } + )); +} + +#[test] +fn test_squaring() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + + assert_eq!(a.square(), b); +} + +#[test] +fn test_multiplication() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + ]), + c1: Fp::from_raw_unchecked([ + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + ]), + }; + + assert_eq!(a * b, c); +} + +#[test] +fn test_addition() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + ]), + c1: Fp::from_raw_unchecked([ + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + ]), + }; + + assert_eq!(a + b, c); +} + +#[test] +fn test_subtraction() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + ]), + c1: Fp::from_raw_unchecked([ + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + ]), + }; + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + ]), + c1: Fp::from_raw_unchecked([ + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + ]), + }; + + assert_eq!(a - b, c); +} + +#[test] +fn test_negation() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + ]), + c1: Fp::from_raw_unchecked([ + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + ]), + }; + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + ]), + c1: Fp::from_raw_unchecked([ + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + ]), + }; + + assert_eq!(-a, b); +} + +#[test] +fn test_sqrt() { + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x2bee_d146_27d7_f9e9, + 0xb661_4e06_660e_5dce, + 0x06c4_cc7c_2f91_d42c, + 0x996d_7847_4b7a_63cc, + 0xebae_bc4c_820d_574e, + 0x1886_5e12_d93f_d845, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d82_8664_baf4_f566, + 0xd17e_6639_96ec_7339, + 0x679e_ad55_cb40_78d0, + 0xfe3b_2260_e001_ec28, + 0x3059_93d0_43d9_1b68, + 0x0626_f03c_0489_b72d, + ]), + }; + + assert_eq!(a.sqrt().unwrap().square(), a); + + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6631_0000_0010_5545, + 0x2114_0040_0eec_000d, + 0x3fa7_af30_c820_e316, + 0xc52a_8b8d_6387_695d, + 0x9fb4_e61d_1e83_eac5, + 0x005c_b922_afe8_4dc7, + ]), + c1: Fp::zero(), + }; + + assert_eq!(b.sqrt().unwrap().square(), b); + + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + let c = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x44f6_0000_0051_ffae, + 0x86b8_0141_9948_0043, + 0xd715_9952_f1f3_794a, + 0x755d_6e3d_fe1f_fc12, + 0xd36c_d6db_5547_e905, + 0x02f8_c8ec_bf18_67bb, + ]), + c1: Fp::zero(), + }; + + assert_eq!(c.sqrt().unwrap().square(), c); + + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0xc5fa_1bc8_fd00_d7f6, + 0x3830_ca45_4606_003b, + 0x2b28_7f11_04b1_02da, + 0xa7fb_30f2_8230_f23e, + 0x339c_db9e_e953_dbf0, + 0x0d78_ec51_d989_fc57, + ]), + c1: Fp::from_raw_unchecked([ + 0x27ec_4898_cf87_f613, + 0x9de1_394e_1abb_05a5, + 0x0947_f85d_c170_fc14, + 0x586f_bc69_6b61_14b7, + 0x2b34_75a4_077d_7169, + 0x13e1_c895_cc4b_6c22, + ]) + } + .sqrt() + .is_none() + )); +} + +#[test] +fn test_inversion() { + let a = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + }; + + let b = Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0581_a133_3d4f_48a6, + 0x5824_2f6e_f074_8500, + 0x0292_c955_349e_6da5, + 0xba37_721d_dd95_fcd0, + 0x70d1_6790_3aa5_dfc5, + 0x1189_5e11_8b58_a9d5, + ]), + c1: Fp::from_raw_unchecked([ + 0x0eda_09d2_d7a8_5d17, + 0x8808_e137_a7d1_a2cf, + 0x43ae_2625_c1ff_21db, + 0xf85a_c9fd_f7a7_4c64, + 0x8fcc_dda5_b8da_9738, + 0x08e8_4f0c_b32c_d17d, + ]), + }; + + assert_eq!(a.invert().unwrap(), b); + + assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1); +} + +#[test] +fn test_lexicographic_largest() { + assert!(!bool::from(Fp2::zero().lexicographically_largest())); + assert!(!bool::from(Fp2::one().lexicographically_largest())); + assert!(bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: -Fp::from_raw_unchecked([ + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + ]), + } + .lexicographically_largest() + )); + assert!(!bool::from( + Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); + assert!(bool::from( + Fp2 { + c0: -Fp::from_raw_unchecked([ + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + ]), + c1: Fp::zero(), + } + .lexicographically_largest() + )); +} diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs new file mode 100644 index 0000000..3f310dc --- /dev/null +++ b/bls12_381/src/fp6.rs @@ -0,0 +1,504 @@ +use crate::fp::*; +use crate::fp2::*; + +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. +pub struct Fp6 { + pub c0: Fp2, + pub c1: Fp2, + pub c2: Fp2, +} + +impl From for Fp6 { + fn from(f: Fp) -> Fp6 { + Fp6 { + c0: Fp2::from(f), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl From for Fp6 { + fn from(f: Fp2) -> Fp6 { + Fp6 { + c0: f, + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } +} + +impl PartialEq for Fp6 { + fn eq(&self, other: &Fp6) -> bool { + self.ct_eq(other).into() + } +} + +impl Copy for Fp6 {} +impl Clone for Fp6 { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Default for Fp6 { + fn default() -> Self { + Fp6::zero() + } +} + +impl fmt::Debug for Fp6 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2) + } +} + +impl ConditionallySelectable for Fp6 { + #[inline(always)] + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fp6 { + c0: Fp2::conditional_select(&a.c0, &b.c0, choice), + c1: Fp2::conditional_select(&a.c1, &b.c1, choice), + c2: Fp2::conditional_select(&a.c2, &b.c2, choice), + } + } +} + +impl ConstantTimeEq for Fp6 { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) + } +} + +impl Fp6 { + #[inline] + pub fn zero() -> Self { + Fp6 { + c0: Fp2::zero(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + #[inline] + pub fn one() -> Self { + Fp6 { + c0: Fp2::one(), + c1: Fp2::zero(), + c2: Fp2::zero(), + } + } + + pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue(); + + let t2 = (self.c0 + self.c1) * c1 - b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: b_b, + } + } + + pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 { + let a_a = self.c0 * c0; + let b_b = self.c1 * c1; + + let t1 = (self.c1 + self.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue() + a_a; + + let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; + + let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } + + /// Multiply by quadratic nonresidue v. + pub fn mul_by_nonresidue(&self) -> Self { + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 + + Fp6 { + c0: self.c2.mul_by_nonresidue(), + c1: self.c0, + c2: self.c1, + } + } + + /// Raises this element to p. + #[inline(always)] + pub fn frobenius_map(&self) -> Self { + let c0 = self.c0.frobenius_map(); + let c1 = self.c1.frobenius_map(); + let c2 = self.c2.frobenius_map(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + let c1 = c1 + * Fp2 { + c0: Fp::zero(), + c1: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + }; + + // c2 = c2 * (u + 1)^((2p - 2) / 3) + let c2 = c2 + * Fp2 { + c0: Fp::from_raw_unchecked([ + 0x890d_c9e4_8675_45c3, + 0x2af3_2253_3285_a5d5, + 0x5088_0866_309b_7e2c, + 0xa20d_1b8c_7e88_1024, + 0x14e4_f04f_e2db_9068, + 0x14e5_6d3f_1564_853a, + ]), + c1: Fp::zero(), + }; + + Fp6 { c0, c1, c2 } + } + + #[inline(always)] + pub fn is_zero(&self) -> Choice { + self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero() + } + + #[inline] + pub fn square(&self) -> Self { + let s0 = self.c0.square(); + let ab = self.c0 * self.c1; + let s1 = ab + ab; + let s2 = (self.c0 - self.c1 + self.c2).square(); + let bc = self.c1 * self.c2; + let s3 = bc + bc; + let s4 = self.c2.square(); + + Fp6 { + c0: s3.mul_by_nonresidue() + s0, + c1: s4.mul_by_nonresidue() + s1, + c2: s1 + s2 + s3 - s0 - s4, + } + } + + #[inline] + pub fn invert(&self) -> CtOption { + let c0 = (self.c1 * self.c2).mul_by_nonresidue(); + let c0 = self.c0.square() - c0; + + let c1 = self.c2.square().mul_by_nonresidue(); + let c1 = c1 - (self.c0 * self.c1); + + let c2 = self.c1.square(); + let c2 = c2 - (self.c0 * self.c2); + + let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue(); + let tmp = tmp + (self.c0 * c0); + + tmp.invert().map(|t| Fp6 { + c0: t * c0, + c1: t * c1, + c2: t * c2, + }) + } +} + +impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn mul(self, other: &'b Fp6) -> Self::Output { + let aa = self.c0 * other.c0; + let bb = self.c1 * other.c1; + let cc = self.c2 * other.c2; + + let t1 = other.c1 + other.c2; + let tmp = self.c1 + self.c2; + let t1 = t1 * tmp; + let t1 = t1 - bb; + let t1 = t1 - cc; + let t1 = t1.mul_by_nonresidue(); + let t1 = t1 + aa; + + let t3 = other.c0 + other.c2; + let tmp = self.c0 + self.c2; + let t3 = t3 * tmp; + let t3 = t3 - aa; + let t3 = t3 + bb; + let t3 = t3 - cc; + + let t2 = other.c0 + other.c1; + let tmp = self.c0 + self.c1; + let t2 = t2 * tmp; + let t2 = t2 - aa; + let t2 = t2 - bb; + let cc = cc.mul_by_nonresidue(); + let t2 = t2 + cc; + + Fp6 { + c0: t1, + c1: t2, + c2: t3, + } + } +} + +impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn add(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 + rhs.c0, + c1: self.c1 + rhs.c1, + c2: self.c2 + rhs.c2, + } + } +} + +impl<'a> Neg for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + Fp6 { + c0: -self.c0, + c1: -self.c1, + c2: -self.c2, + } + } +} + +impl Neg for Fp6 { + type Output = Fp6; + + #[inline] + fn neg(self) -> Self::Output { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { + type Output = Fp6; + + #[inline] + fn sub(self, rhs: &'b Fp6) -> Self::Output { + Fp6 { + c0: self.c0 - rhs.c0, + c1: self.c1 - rhs.c1, + c2: self.c2 - rhs.c2, + } + } +} + +impl_binops_additive!(Fp6, Fp6); +impl_binops_multiplicative!(Fp6, Fp6); + +#[test] +fn test_arithmetic() { + use crate::fp::*; + + let a = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d, + ]), + c1: Fp::from_raw_unchecked([ + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8, + ]), + c1: Fp::from_raw_unchecked([ + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6, + ]), + c1: Fp::from_raw_unchecked([ + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040, + ]), + }, + }; + + let b = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247, + ]), + c1: Fp::from_raw_unchecked([ + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba, + ]), + c1: Fp::from_raw_unchecked([ + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b, + ]), + c1: Fp::from_raw_unchecked([ + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32, + ]), + }, + }; + + let c = Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x6934_cb98_b176_82ef, + 0xfa45_10ea_194e_1d67, + 0xff51_313d_2405_877e, + 0xd0cd_efcc_2e8d_0ca5, + 0x7bea_1ad8_3da0_106b, + 0x0c8e_97e6_1845_be39, + ]), + c1: Fp::from_raw_unchecked([ + 0x4779_cb98_b18d_82d8, + 0xb5e9_1144_4daa_1d7a, + 0x2f28_6bda_a653_2fc2, + 0xbca6_94f6_8bae_ff0f, + 0x3d75_e6b8_1a3a_7a5d, + 0x0a44_c3c4_98cc_96a3, + ]), + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8b6f_cb98_b18a_2d86, + 0xe8a1_1137_3af2_1d77, + 0x3710_a624_493c_cd2b, + 0xa94f_8828_0ee1_ba89, + 0x2c8a_73d6_bb2f_3ac7, + 0x0e4f_76ea_d7cb_98aa, + ]), + c1: Fp::from_raw_unchecked([ + 0xcf65_cb98_b186_d834, + 0x1b59_112a_283a_1d74, + 0x3ef8_e06d_ec26_6a95, + 0x95f8_7b59_9214_7603, + 0x1b9f_00f5_5c23_fb31, + 0x125a_2a11_16ca_9ab1, + ]), + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x135b_cb98_b183_82e2, + 0x4e11_111d_1582_1d72, + 0x46e1_1ab7_8f10_07fe, + 0x82a1_6e8b_1547_317d, + 0x0ab3_8e13_fd18_bb9b, + 0x1664_dd37_55c9_9cb8, + ]), + c1: Fp::from_raw_unchecked([ + 0xce65_cb98_b131_8334, + 0xc759_0fdb_7c3a_1d2e, + 0x6fcb_8164_9d1c_8eb3, + 0x0d44_004d_1727_356a, + 0x3746_b738_a7d0_d296, + 0x136c_144a_96b1_34fc, + ]), + }, + }; + + assert_eq!(a.square(), a * a); + assert_eq!(b.square(), b * b); + assert_eq!(c.square(), c * c); + + assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); + + assert_eq!( + a.invert().unwrap() * b.invert().unwrap(), + (a * b).invert().unwrap() + ); + assert_eq!(a.invert().unwrap() * a, Fp6::one()); +} diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs new file mode 100644 index 0000000..5469fd6 --- /dev/null +++ b/bls12_381/src/g1.rs @@ -0,0 +1,1347 @@ +//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; +use crate::Scalar; + +/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[derive(Copy, Clone, Debug)] +pub struct G1Affine { + pub(crate) x: Fp, + pub(crate) y: Fp, + infinity: Choice, +} + +impl Default for G1Affine { + fn default() -> G1Affine { + G1Affine::identity() + } +} + +impl<'a> From<&'a G1Projective> for G1Affine { + fn from(p: &'a G1Projective) -> G1Affine { + let zinv = p.z.invert().unwrap_or(Fp::zero()); + let zinv2 = zinv.square(); + let x = p.x * zinv2; + let zinv3 = zinv2 * zinv; + let y = p.y * zinv3; + + let tmp = G1Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) + } +} + +impl From for G1Affine { + fn from(p: G1Projective) -> G1Affine { + G1Affine::from(&p) + } +} + +impl ConstantTimeEq for G1Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + | ((!self.infinity) + & (!other.infinity) + & self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y)) + } +} + +impl ConditionallySelectable for G1Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G1Affine { + x: Fp::conditional_select(&a.x, &b.x, choice), + y: Fp::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl Eq for G1Affine {} +impl PartialEq for G1Affine { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G1Affine { + type Output = G1Affine; + + #[inline] + fn neg(self) -> G1Affine { + G1Affine { + x: self.x, + y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity), + infinity: self.infinity, + } + } +} + +impl Neg for G1Affine { + type Output = G1Affine; + + #[inline] + fn neg(self) -> G1Affine { + -&self + } +} + +impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Projective) -> G1Projective { + rhs.add_mixed(self) + } +} + +impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Affine) -> G1Projective { + self.add_mixed(rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Projective) -> G1Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Affine) -> G1Projective { + self + (-rhs) + } +} + +impl_binops_additive!(G1Projective, G1Affine); +impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); + +const B: Fp = Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, +]); + +impl G1Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Affine { + G1Affine { + x: Fp::zero(), + y: Fp::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Affine { + G1Affine { + x: Fp::from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_compressed(&self) -> [u8; 48] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes(); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_uncompressed(&self) -> [u8; 96] { + let mut res = [0; 96]; + + res[0..48].copy_from_slice( + &Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..], + ); + res[48..96].copy_from_slice( + &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..], + ); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption { + Self::from_uncompressed_unchecked(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + let y = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + x.and_then(|x| { + y.and_then(|y| { + // Create a point representing this value + let p = G1Affine::conditional_select( + &G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G1Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_compressed(bytes: &[u8; 48]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let x = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + + x.and_then(|x| { + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G1Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G1Affine { + x, + y, + infinity: infinity_flag_set, + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G1Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4 + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. +#[derive(Copy, Clone, Debug)] +pub struct G1Projective { + x: Fp, + y: Fp, + z: Fp, +} + +impl<'a> From<&'a G1Affine> for G1Projective { + fn from(p: &'a G1Affine) -> G1Projective { + G1Projective { + x: p.x, + y: p.y, + z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), + } + } +} + +impl From for G1Projective { + fn from(p: G1Affine) -> G1Projective { + G1Projective::from(&p) + } +} + +impl ConstantTimeEq for G1Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? + + let z = other.z.square(); + let x1 = self.x * z; + let z = z * other.z; + let y1 = self.y * z; + let z = self.z.square(); + let x2 = other.x * z; + let z = z * self.z; + let y2 = other.y * z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } +} + +impl ConditionallySelectable for G1Projective { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G1Projective { + x: Fp::conditional_select(&a.x, &b.x, choice), + y: Fp::conditional_select(&a.y, &b.y, choice), + z: Fp::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl Eq for G1Projective {} +impl PartialEq for G1Projective { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn neg(self) -> G1Projective { + G1Projective { + x: self.x, + y: -self.y, + z: self.z, + } + } +} + +impl Neg for G1Projective { + type Output = G1Projective; + + #[inline] + fn neg(self) -> G1Projective { + -&self + } +} + +impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn add(self, rhs: &'b G1Projective) -> G1Projective { + self.add(rhs) + } +} + +impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective { + type Output = G1Projective; + + #[inline] + fn sub(self, rhs: &'b G1Projective) -> G1Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective { + type Output = G1Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + self.multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine { + type Output = G1Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + G1Projective::from(self).multiply(&other.to_bytes()) + } +} + +impl_binops_additive!(G1Projective, G1Projective); +impl_binops_multiplicative!(G1Projective, Scalar); +impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); + +impl G1Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Projective { + G1Projective { + x: Fp::zero(), + y: Fp::one(), + z: Fp::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Projective { + G1Projective { + x: Fp::from_raw_unchecked([ + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a, + ]), + z: Fp::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G1Projective { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // There are no points of order 2. + + let a = self.x.square(); + let b = self.y.square(); + let c = b.square(); + let d = self.x + b; + let d = d.square(); + let d = d - a - c; + let d = d + d; + let e = a + a + a; + let f = e.square(); + let z3 = self.z * self.y; + let z3 = z3 + z3; + let x3 = f - (d + d); + let c = c + c; + let c = c + c; + let c = c + c; + let y3 = e * (d - x3) - c; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G1Projective) -> G1Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G1Projective::conditional_select(self, rhs, f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let z = rhs.z.square(); + let u1 = self.x * z; + let z = z * rhs.z; + let s1 = self.y * z; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let u1 = self.x; + let s1 = self.y; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z; + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G1Projective { + x: x3, + y: y3, + z: z3, + }; + + G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + fn multiply(&self, by: &[u8; 32]) -> G1Projective { + let mut acc = G1Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G1Projective::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + + /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This + /// function will panic if `p.len() != q.len()`. + pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fp::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of `G1Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = Choice::from(0u8); + + *q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 - X^3 = 4(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G1Affine::identity().is_on_curve())); + assert!(bool::from(G1Affine::generator().is_on_curve())); + assert!(bool::from(G1Projective::identity().is_on_curve())); + assert!(bool::from(G1Projective::generator().is_on_curve())); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let gen = G1Affine::generator(); + let mut test = G1Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_affine_point_equality() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_point_equality() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let mut c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} + +#[test] +fn test_conditionally_select_affine() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} + +#[test] +fn test_conditionally_select_projective() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G1Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_projective_to_affine() { + let a = G1Projective::generator(); + let b = G1Projective::identity(); + + assert!(bool::from(G1Affine::from(a).is_on_curve())); + assert!(!bool::from(G1Affine::from(a).is_identity())); + assert!(bool::from(G1Affine::from(b).is_on_curve())); + assert!(bool::from(G1Affine::from(b).is_identity())); + + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + let c = G1Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G1Affine::from(c), G1Affine::generator()); +} + +#[test] +fn test_affine_to_projective() { + let a = G1Affine::generator(); + let b = G1Affine::identity(); + + assert!(bool::from(G1Projective::from(a).is_on_curve())); + assert!(!bool::from(G1Projective::from(a).is_identity())); + assert!(bool::from(G1Projective::from(b).is_on_curve())); + assert!(bool::from(G1Projective::from(b).is_identity())); +} + +#[test] +fn test_doubling() { + { + let tmp = G1Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + } + { + let tmp = G1Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + + assert_eq!( + G1Affine::from(tmp), + G1Affine { + x: Fp::from_raw_unchecked([ + 0x53e9_78ce_58a9_ba3c, + 0x3ea0_583c_4f3d_65f9, + 0x4d20_bb47_f001_2960, + 0xa54c_664a_e5b2_b5d9, + 0x26b5_52a3_9d7e_b21f, + 0x0008_895d_26e6_8785, + ]), + y: Fp::from_raw_unchecked([ + 0x7011_0b32_9829_3940, + 0xda33_c539_3f1f_6afc, + 0xb86e_dfd1_6a5a_a785, + 0xaec6_d1c9_e7b1_c895, + 0x25cf_c2b5_22d1_1720, + 0x0636_1c83_f8d0_9b15, + ]), + infinity: Choice::from(0u8) + } + ); + } +} + +#[test] +fn test_projective_addition() { + { + let a = G1Projective::identity(); + let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + d += G1Projective::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_mixed_addition() { + { + let a = G1Affine::identity(); + let b = G1Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Affine::identity(); + let mut b = G1Projective::generator(); + { + let z = Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]); + + b = G1Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G1Projective::generator()); + } + { + let a = G1Projective::generator().double().double(); // 4P + let b = G1Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G1Projective::generator(); + for _ in 0..5 { + d += G1Affine::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]); + let beta = beta.square(); + let a = G1Projective::generator().double().double(); + let b = G1Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G1Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G1Affine::from(c), + G1Affine::from(G1Projective { + x: Fp::from_raw_unchecked([ + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6, + ]), + y: Fp::from_raw_unchecked([ + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + ]), + z: Fp::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_negation_and_subtraction() { + let a = G1Projective::generator().double(); + assert_eq!(a + (-a), G1Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G1Affine::generator(); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); + assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G1Projective::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G1Affine::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!(G1Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G1Affine { + x: Fp::from_raw_unchecked([ + 0x0aba_f895_b97e_43c8, + 0xba4c_6432_eb9b_61b0, + 0x1250_6f52_adfe_307f, + 0x7502_8c34_3933_6b72, + 0x8474_4f05_b8e9_bd71, + 0x113d_554f_b095_54f7, + ]), + y: Fp::from_raw_unchecked([ + 0x73e9_0e88_f5cf_01c0, + 0x3700_7b65_dd31_97e2, + 0x5cf9_a199_2f0d_7c78, + 0x4f83_c10b_9eb3_330d, + 0xf6a6_3f6f_07f6_0961, + 0x0c53_b5b9_7e63_4df3, + ]), + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G1Affine::identity().is_torsion_free())); + assert!(bool::from(G1Affine::generator().is_torsion_free())); +} + +#[test] +fn test_batch_normalize() { + let a = G1Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G1Projective::identity() + } + if b_identity { + v[1] = G1Projective::identity() + } + if c_identity { + v[2] = G1Projective::identity() + } + + let mut t = [ + G1Affine::identity(), + G1Affine::identity(), + G1Affine::identity(), + ]; + let expected = [ + G1Affine::from(v[0]), + G1Affine::from(v[1]), + G1Affine::from(v[2]), + ]; + + G1Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } +} diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs new file mode 100644 index 0000000..d3f505b --- /dev/null +++ b/bls12_381/src/g2.rs @@ -0,0 +1,1595 @@ +//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::fp::Fp; +use crate::fp2::Fp2; +use crate::Scalar; + +/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. +/// It is ideal to keep elements in this representation to reduce memory usage and +/// improve performance through the use of mixed curve model arithmetic. +/// +/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an +/// "unchecked" API was misused. +#[derive(Copy, Clone, Debug)] +pub struct G2Affine { + pub(crate) x: Fp2, + pub(crate) y: Fp2, + infinity: Choice, +} + +impl Default for G2Affine { + fn default() -> G2Affine { + G2Affine::identity() + } +} + +impl<'a> From<&'a G2Projective> for G2Affine { + fn from(p: &'a G2Projective) -> G2Affine { + let zinv = p.z.invert().unwrap_or(Fp2::zero()); + let zinv2 = zinv.square(); + let x = p.x * zinv2; + let zinv3 = zinv2 * zinv; + let y = p.y * zinv3; + + let tmp = G2Affine { + x, + y, + infinity: Choice::from(0u8), + }; + + G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) + } +} + +impl From for G2Affine { + fn from(p: G2Projective) -> G2Affine { + G2Affine::from(&p) + } +} + +impl ConstantTimeEq for G2Affine { + fn ct_eq(&self, other: &Self) -> Choice { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal + + (self.infinity & other.infinity) + | ((!self.infinity) + & (!other.infinity) + & self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y)) + } +} + +impl ConditionallySelectable for G2Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Affine { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl Eq for G2Affine {} +impl PartialEq for G2Affine { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + G2Affine { + x: self.x, + y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), + infinity: self.infinity, + } + } +} + +impl Neg for G2Affine { + type Output = G2Affine; + + #[inline] + fn neg(self) -> G2Affine { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + rhs.add_mixed(self) + } +} + +impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Affine) -> G2Projective { + self.add_mixed(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Affine) -> G2Projective { + self + (-rhs) + } +} + +impl_binops_additive!(G2Projective, G2Affine); +impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); + +const B: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e, + ]), +}; + +impl G2Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Affine { + G2Affine { + x: Fp2::zero(), + y: Fp2::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Affine { + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, + ]), + }, + infinity: Choice::from(0u8), + } + } + + /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_compressed(&self) -> [u8; 96] { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + + let mut res = [0; 96]; + + (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); + (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); + + // This point is in compressed form, so we set the most significant bit. + res[0] |= 1u8 << 7; + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= u8::conditional_select( + &0u8, + &(1u8 << 5), + (!self.infinity) & self.y.lexicographically_largest(), + ); + + res + } + + /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn to_uncompressed(&self) -> [u8; 192] { + let mut res = [0; 192]; + + let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); + let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); + + res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); + res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); + res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); + res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); + + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); + + res + } + + /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption { + Self::from_uncompressed_unchecked(bytes) + .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is on the curve and not checking if it is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. + pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + // Attempt to obtain the y-coordinate + let yc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[96..144]); + + Fp::from_bytes(&tmp) + }; + let yc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[144..192]); + + Fp::from_bytes(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + yc1.and_then(|yc1| { + yc0.and_then(|yc0| { + let x = Fp2 { + c0: xc0, + c1: xc1 + }; + let y = Fp2 { + c0: yc0, + c1: yc1 + }; + + // Create a point representing this value + let p = G2Affine::conditional_select( + &G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + &G2Affine::identity(), + infinity_flag_set, + ); + + CtOption::new( + p, + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set), + ) + }) + }) + }) + }) + } + + /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) + /// for details about how group elements are serialized. + pub fn from_compressed(bytes: &[u8; 96]) -> CtOption { + // We already know the point is on the curve because this is established + // by the y-coordinate recovery procedure in from_compressed_unchecked(). + + Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) + } + + /// Attempts to deserialize an uncompressed element, not checking if the + /// element is in the correct subgroup. + /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, + /// API invariants may be broken.** Please consider using `from_compressed()` instead. + pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption { + // Obtain the three flags from the start of the byte sequence + let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); + let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); + let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); + + // Attempt to obtain the x-coordinate + let xc1 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[0..48]); + + // Mask away the flag bits + tmp[0] &= 0b0001_1111; + + Fp::from_bytes(&tmp) + }; + let xc0 = { + let mut tmp = [0; 48]; + tmp.copy_from_slice(&bytes[48..96]); + + Fp::from_bytes(&tmp) + }; + + xc1.and_then(|xc1| { + xc0.and_then(|xc0| { + let x = Fp2 { c0: xc0, c1: xc1 }; + + // If the infinity flag is set, return the value assuming + // the x-coordinate is zero and the sort bit is not set. + // + // Otherwise, return a recovered point (assuming the correct + // y-coordinate can be found) so long as the infinity flag + // was not set. + CtOption::new( + G2Affine::identity(), + infinity_flag_set & // Infinity flag should be set + compression_flag_set & // Compression flag should be set + (!sort_flag_set) & // Sort flag should not be set + x.is_zero(), // The x-coordinate should be zero + ) + .or_else(|| { + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + ((x.square() * x) + B).sqrt().and_then(|y| { + // Switch to the correct y-coordinate if necessary. + let y = Fp2::conditional_select( + &y, + &-y, + y.lexicographically_largest() ^ sort_flag_set, + ); + + CtOption::new( + G2Affine { + x, + y, + infinity: infinity_flag_set, + }, + (!infinity_flag_set) & // Infinity flag should not be set + compression_flag_set, // Compression flag should be set + ) + }) + }) + }) + }) + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.infinity + } + + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G2Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // y^2 - x^3 ?= 4(u + 1) + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + +/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. +#[derive(Copy, Clone, Debug)] +pub struct G2Projective { + pub(crate) x: Fp2, + pub(crate) y: Fp2, + pub(crate) z: Fp2, +} + +impl<'a> From<&'a G2Affine> for G2Projective { + fn from(p: &'a G2Affine) -> G2Projective { + G2Projective { + x: p.x, + y: p.y, + z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), + } + } +} + +impl From for G2Projective { + fn from(p: G2Affine) -> G2Projective { + G2Projective::from(&p) + } +} + +impl ConstantTimeEq for G2Projective { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? + + let z = other.z.square(); + let x1 = self.x * z; + let z = z * other.z; + let y1 = self.y * z; + let z = self.z.square(); + let x2 = other.x * z; + let z = z * self.z; + let y2 = other.y * z; + + let self_is_zero = self.z.is_zero(); + let other_is_zero = other.z.is_zero(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } +} + +impl ConditionallySelectable for G2Projective { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + G2Projective { + x: Fp2::conditional_select(&a.x, &b.x, choice), + y: Fp2::conditional_select(&a.y, &b.y, choice), + z: Fp2::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl Eq for G2Projective {} +impl PartialEq for G2Projective { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl<'a> Neg for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + G2Projective { + x: self.x, + y: -self.y, + z: self.z, + } + } +} + +impl Neg for G2Projective { + type Output = G2Projective; + + #[inline] + fn neg(self) -> G2Projective { + -&self + } +} + +impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn add(self, rhs: &'b G2Projective) -> G2Projective { + self.add(rhs) + } +} + +impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { + type Output = G2Projective; + + #[inline] + fn sub(self, rhs: &'b G2Projective) -> G2Projective { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + self.multiply(&other.to_bytes()) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { + type Output = G2Projective; + + fn mul(self, other: &'b Scalar) -> Self::Output { + G2Projective::from(self).multiply(&other.to_bytes()) + } +} + +impl_binops_additive!(G2Projective, G2Projective); +impl_binops_multiplicative!(G2Projective, Scalar); +impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); + +impl G2Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Projective { + G2Projective { + x: Fp2::zero(), + y: Fp2::one(), + z: Fp2::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Projective { + G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, + ]), + }, + z: Fp2::one(), + } + } + + /// Computes the doubling of this point. + pub fn double(&self) -> G2Projective { + // http://www.hyperelliptic.org/EFD/g2p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // There are no points of order 2. + + let a = self.x.square(); + let b = self.y.square(); + let c = b.square(); + let d = self.x + b; + let d = d.square(); + let d = d - a - c; + let d = d + d; + let e = a + a + a; + let f = e.square(); + let z3 = self.z * self.y; + let z3 = z3 + z3; + let x3 = f - (d + d); + let c = c + c; + let c = c + c; + let c = c + c; + let y3 = e * (d - x3) - c; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) + } + + /// Adds this point to another point. + pub fn add(&self, rhs: &G2Projective) -> G2Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G2Projective::conditional_select(self, rhs, f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let z = rhs.z.square(); + let u1 = self.x * z; + let z = z * rhs.z; + let s1 = self.y * z; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp2::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + /// Adds this point to another point in the affine model. + pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { + // This Jacobian point addition technique is based on the implementation in libsecp256k1, + // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. + + // If self is the identity, return rhs. Otherwise, return self. The other cases will be + // predicated on neither self nor rhs being the identity. + let f1 = self.is_identity(); + let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); + let f2 = rhs.is_identity(); + + // If neither are the identity but x1 = x2 and y1 != y2, then return the identity + let u1 = self.x; + let s1 = self.y; + let z = self.z.square(); + let u2 = rhs.x * z; + let z = z * self.z; + let s2 = rhs.y * z; + let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); + let res = + G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); + + let t = u1 + u2; + let m = s1 + s2; + let rr = t.square(); + let m_alt = -u2; + let tt = u1 * m_alt; + let rr = rr + tt; + + // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. + // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. + let degenerate = m.is_zero() & rr.is_zero(); + let rr_alt = s1 + s1; + let m_alt = m_alt + u1; + let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); + let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); + + let n = m_alt.square(); + let q = n * t; + + let n = n.square(); + let n = Fp2::conditional_select(&n, &m, degenerate); + let t = rr_alt.square(); + let z3 = m_alt * self.z; + let z3 = z3 + z3; + let q = -q; + let t = t + q; + let x3 = t; + let t = t + t; + let t = t + q; + let t = t * rr_alt; + let t = t + n; + let y3 = -t; + let x3 = x3 + x3; + let x3 = x3 + x3; + let y3 = y3 + y3; + let y3 = y3 + y3; + + let tmp = G2Projective { + x: x3, + y: y3, + z: z3, + }; + + G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) + } + + fn multiply(&self, by: &[u8; 32]) -> G2Projective { + let mut acc = G2Projective::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G2Projective::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + + /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This + /// function will panic if `p.len() != q.len()`. + pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { + assert_eq!(p.len(), q.len()); + + let mut acc = Fp2::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of `G2Affine` to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); + + // Set the coordinates to the correct value + let tmp2 = tmp.square(); + let tmp3 = tmp2 * tmp; + + q.x = p.x * tmp2; + q.y = p.y * tmp3; + q.infinity = Choice::from(0u8); + + *q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip); + } + } + + /// Returns true if this element is the identity (the point at infinity). + #[inline] + pub fn is_identity(&self) -> Choice { + self.z.is_zero() + } + + /// Returns true if this point is on the curve. This should always return + /// true unless an "unchecked" API was used. + pub fn is_on_curve(&self) -> Choice { + // Y^2 - X^3 = 4(u + 1)(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G2Affine::identity().is_on_curve())); + assert!(bool::from(G2Affine::generator().is_on_curve())); + assert!(bool::from(G2Projective::identity().is_on_curve())); + assert!(bool::from(G2Projective::generator().is_on_curve())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let gen = G2Affine::generator(); + let mut test = G2Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_affine_point_equality() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_point_equality() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let mut c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + assert!(bool::from(c.is_on_curve())); + + assert!(a == c); + assert!(b != c); + assert!(c == a); + assert!(c != b); + + c.y = -c.y; + assert!(bool::from(c.is_on_curve())); + + assert!(a != c); + assert!(b != c); + assert!(c != a); + assert!(c != b); + + c.y = -c.y; + c.x = z; + assert!(!bool::from(c.is_on_curve())); + assert!(a != b); + assert!(a != c); + assert!(b != c); +} + +#[test] +fn test_conditionally_select_affine() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); + assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); +} + +#[test] +fn test_conditionally_select_projective() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(0u8)), + a + ); + assert_eq!( + G2Projective::conditional_select(&a, &b, Choice::from(1u8)), + b + ); +} + +#[test] +fn test_projective_to_affine() { + let a = G2Projective::generator(); + let b = G2Projective::identity(); + + assert!(bool::from(G2Affine::from(a).is_on_curve())); + assert!(!bool::from(G2Affine::from(a).is_identity())); + assert!(bool::from(G2Affine::from(b).is_on_curve())); + assert!(bool::from(G2Affine::from(b).is_identity())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + let c = G2Projective { + x: a.x * (z.square()), + y: a.y * (z.square() * z), + z, + }; + + assert_eq!(G2Affine::from(c), G2Affine::generator()); +} + +#[test] +fn test_affine_to_projective() { + let a = G2Affine::generator(); + let b = G2Affine::identity(); + + assert!(bool::from(G2Projective::from(a).is_on_curve())); + assert!(!bool::from(G2Projective::from(a).is_identity())); + assert!(bool::from(G2Projective::from(b).is_on_curve())); + assert!(bool::from(G2Projective::from(b).is_identity())); +} + +#[test] +fn test_doubling() { + { + let tmp = G2Projective::identity().double(); + assert!(bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + } + { + let tmp = G2Projective::generator().double(); + assert!(!bool::from(tmp.is_identity())); + assert!(bool::from(tmp.is_on_curve())); + + assert_eq!( + G2Affine::from(tmp), + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xe9d9_e2da_9620_f98b, + 0x54f1_1993_46b9_7f36, + 0x3db3_b820_376b_ed27, + 0xcfdb_31c9_b0b6_4f4c, + 0x41d7_c127_8635_4493, + 0x0571_0794_c255_c064, + ]), + c1: Fp::from_raw_unchecked([ + 0xd6c1_d3ca_6ea0_d06e, + 0xda0c_bd90_5595_489f, + 0x4f53_52d4_3479_221d, + 0x8ade_5d73_6f8c_97e0, + 0x48cc_8433_925e_f70e, + 0x08d7_ea71_ea91_ef81, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x15ba_26eb_4b0d_186f, + 0x0d08_6d64_b7e9_e01e, + 0xc8b8_48dd_652f_4c78, + 0xeecf_46a6_123b_ae4f, + 0x255e_8dd8_b6dc_812a, + 0x1641_42af_21dc_f93f, + ]), + c1: Fp::from_raw_unchecked([ + 0xf9b4_a1a8_9598_4db4, + 0xd417_b114_cccf_f748, + 0x6856_301f_c89f_086e, + 0x41c7_7787_8931_e3da, + 0x3556_b155_066a_2105, + 0x00ac_f7d3_25cb_89cf, + ]), + }, + infinity: Choice::from(0u8) + } + ); + } +} + +#[test] +fn test_projective_addition() { + { + let a = G2Projective::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d += G2Projective::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +fn test_mixed_addition() { + { + let a = G2Affine::identity(); + let b = G2Projective::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = a + b; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Affine::identity(); + let mut b = G2Projective::generator(); + { + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844, + ]), + }; + + b = G2Projective { + x: b.x * (z.square()), + y: b.y * (z.square() * z), + z, + }; + } + let c = b + a; + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(c == G2Projective::generator()); + } + { + let a = G2Projective::generator().double().double(); // 4P + let b = G2Projective::generator().double(); // 2P + let c = a + b; + + let mut d = G2Projective::generator(); + for _ in 0..5 { + d += G2Affine::generator(); + } + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + assert_eq!(c, d); + } + + // Degenerate case + { + let beta = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741, + ]), + c1: Fp::zero(), + }; + let beta = beta.square(); + let a = G2Projective::generator().double().double(); + let b = G2Projective { + x: a.x * beta, + y: -a.y, + z: a.z, + }; + let a = G2Affine::from(a); + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(b.is_on_curve())); + + let c = a + b; + assert_eq!( + G2Affine::from(c), + G2Affine::from(G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2, + ]), + c1: Fp::from_raw_unchecked([ + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952, + ]) + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3, + ]), + c1: Fp::from_raw_unchecked([ + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe, + ]) + }, + z: Fp2::one() + }) + ); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + } +} + +#[test] +#[allow(clippy::eq_op)] +fn test_projective_negation_and_subtraction() { + let a = G2Projective::generator().double(); + assert_eq!(a + (-a), G2Projective::identity()); + assert_eq!(a + (-a), a - a); +} + +#[test] +fn test_affine_negation_and_subtraction() { + let a = G2Affine::generator(); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); + assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); +} + +#[test] +fn test_projective_scalar_multiplication() { + let g = G2Projective::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!((g * a) * b, g * c); +} + +#[test] +fn test_affine_scalar_multiplication() { + let g = G2Affine::generator(); + let a = Scalar::from_raw([ + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2, + ]); + let b = Scalar::from_raw([ + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20, + ]); + let c = a * b; + + assert_eq!(G2Affine::from(g * a) * b, g * c); +} + +#[test] +fn test_is_torsion_free() { + let a = G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x89f5_50c8_13db_6431, + 0xa50b_e8c4_56cd_8a1a, + 0xa45b_3741_14ca_e851, + 0xbb61_90f5_bf7f_ff63, + 0x970c_a02c_3ba8_0bc7, + 0x02b8_5d24_e840_fbac, + ]), + c1: Fp::from_raw_unchecked([ + 0x6888_bc53_d707_16dc, + 0x3dea_6b41_1768_2d70, + 0xd8f5_f930_500c_a354, + 0x6b5e_cb65_56f5_c155, + 0xc96b_ef04_3477_8ab0, + 0x0508_1505_5150_06ad, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3cf1_ea0d_434b_0f40, + 0x1a0d_c610_e603_e333, + 0x7f89_9561_60c7_2fa0, + 0x25ee_03de_cf64_31c5, + 0xeee8_e206_ec0f_e137, + 0x0975_92b2_26df_ef28, + ]), + c1: Fp::from_raw_unchecked([ + 0x71e8_bb5f_2924_7367, + 0xa5fe_049e_2118_31ce, + 0x0ce6_b354_502a_3896, + 0x93b0_1200_0997_314e, + 0x6759_f3b6_aa5b_42ac, + 0x1569_44c4_dfe9_2bbb, + ]), + }, + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G2Affine::identity().is_torsion_free())); + assert!(bool::from(G2Affine::generator().is_torsion_free())); +} + +#[test] +fn test_batch_normalize() { + let a = G2Projective::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = G2Projective::identity() + } + if b_identity { + v[1] = G2Projective::identity() + } + if c_identity { + v[2] = G2Projective::identity() + } + + let mut t = [ + G2Affine::identity(), + G2Affine::identity(), + G2Affine::identity(), + ]; + let expected = [ + G2Affine::from(v[0]), + G2Affine::from(v[1]), + G2Affine::from(v[2]), + ]; + + G2Projective::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } +} diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs new file mode 100644 index 0000000..d5b4d51 --- /dev/null +++ b/bls12_381/src/lib.rs @@ -0,0 +1,80 @@ +//! # `bls12_381` +//! +//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic +//! curve construction. +//! +//! * **This implementation has not been reviewed or audited. Use at your own risk.** +//! * This implementation targets Rust `1.36` or later. +//! * This implementation does not require the Rust standard library. +//! * All operations are constant time unless explicitly noted. + +#![no_std] +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(unsafe_code)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::many_single_char_names)] +// This lint is described at +// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl +// In our library, some of the arithmetic involving extension fields will necessarily +// involve various binary operators, and so this lint is triggered unnecessarily. +#![allow(clippy::suspicious_arithmetic_impl)] + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(test)] +#[macro_use] +extern crate std; + +#[cfg(test)] +#[cfg(feature = "groups")] +mod tests; + +#[macro_use] +mod util; + +/// Notes about how the BLS12-381 elliptic curve is designed, specified +/// and implemented by this library. +pub mod notes { + pub mod design; + pub mod serialization; +} + +mod scalar; + +pub use scalar::Scalar; + +#[cfg(feature = "groups")] +mod fp; +#[cfg(feature = "groups")] +mod fp2; +#[cfg(feature = "groups")] +mod g1; +#[cfg(feature = "groups")] +mod g2; + +#[cfg(feature = "groups")] +pub use g1::{G1Affine, G1Projective}; +#[cfg(feature = "groups")] +pub use g2::{G2Affine, G2Projective}; + +#[cfg(feature = "groups")] +mod fp12; +#[cfg(feature = "groups")] +mod fp6; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +const BLS_X: u64 = 0xd201_0000_0001_0000; +const BLS_X_IS_NEGATIVE: bool = true; + +#[cfg(feature = "pairings")] +mod pairings; + +#[cfg(feature = "pairings")] +pub use pairings::{pairing, Gt, MillerLoopResult}; + +#[cfg(all(feature = "pairings", feature = "alloc"))] +pub use pairings::{multi_miller_loop, G2Prepared}; diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs new file mode 100644 index 0000000..d245260 --- /dev/null +++ b/bls12_381/src/notes/design.rs @@ -0,0 +1,63 @@ +//! # Design of BLS12-381 +//! ## Fixed Generators +//! +//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is +//! safe to use in a cryptographic protocol, we specify some simple, fixed generators. +//! +//! In order to derive these generators, we select the lexicographically smallest +//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate, +//! and then scale the resulting point by the cofactor, such that the result is not the +//! identity. This results in the following fixed generators: +//! +//! 1. $\mathbb{G}_1$ +//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$ +//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$ +//! 2. $\mathbb{G}_2$ +//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$ +//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$ +//! +//! This can be derived using the following sage script: +//! +//! ```norun +//! param = -0xd201000000010000 +//! def r(x): +//! return (x**4) - (x**2) + 1 +//! def q(x): +//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x +//! def g1_h(x): +//! return ((x-1)**2) // 3 +//! def g2_h(x): +//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 +//! q = q(param) +//! r = r(param) +//! Fq = GF(q) +//! ec = EllipticCurve(Fq, [0, 4]) +//! def psqrt(v): +//! assert(not v.is_zero()) +//! a = sqrt(v) +//! b = -a +//! if a < b: +//! return a +//! else: +//! return b +//! for x in range(0,100): +//! rhs = Fq(x)^3 + 4 +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec(x, y) * g1_h(param) +//! if (not p.is_zero()) and (p * r).is_zero(): +//! print "g1 generator: %s" % p +//! break +//! Fqx. = PolynomialRing(Fq, 'j') +//! Fq2. = GF(q^2, modulus=j^2 + 1) +//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) +//! assert(ec2.order() == (r * g2_h(param))) +//! for x in range(0,100): +//! rhs = (Fq2(x))^3 + (4 * (1 + i)) +//! if rhs.is_square(): +//! y = psqrt(rhs) +//! p = ec2(Fq2(x), y) * g2_h(param) +//! if (not p.is_zero()) and (p * r).is_zero(): +//! print "g2 generator: %s" % p +//! break +//! ``` diff --git a/bls12_381/src/notes/serialization.rs b/bls12_381/src/notes/serialization.rs new file mode 100644 index 0000000..ded752e --- /dev/null +++ b/bls12_381/src/notes/serialization.rs @@ -0,0 +1,29 @@ +//! # BLS12-381 serialization +//! +//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48 +//! bytes in this form. +//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that +//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the +//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$. +//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form. +//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The +//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates. +//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed +//! form (the x-coordinate followed by the y-coordinate) or in compressed form +//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in +//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$ +//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed +//! form. +//! +//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$ +//! encoding should be masked away before the coordinate(s) are interpreted. +//! These bits are used to unambiguously represent the underlying element: +//! * The most significant bit, when set, indicates that the point is in +//! compressed form. Otherwise, the point is in uncompressed form. +//! * The second-most significant bit indicates that the point is at infinity. +//! If this bit is set, the remaining bits of the group element's encoding +//! should be set to zero. +//! * The third-most significant bit is set if (and only if) this point is in +//! compressed form _and_ it is not the point at infinity _and_ its +//! y-coordinate is the lexicographically largest of the two associated with +//! the encoded x-coordinate. diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs new file mode 100644 index 0000000..ef7180a --- /dev/null +++ b/bls12_381/src/pairings.rs @@ -0,0 +1,648 @@ +use crate::fp12::Fp12; +use crate::fp2::Fp2; +use crate::fp6::Fp6; +use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +/// Represents results of a Miller loop, one of the most expensive portions +/// of the pairing function. `MillerLoopResult`s cannot be compared with each +/// other until `.final_exponentiation()` is called, which is also expensive. +#[derive(Copy, Clone, Debug)] +pub struct MillerLoopResult(pub(crate) Fp12); + +impl ConditionallySelectable for MillerLoopResult { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl MillerLoopResult { + /// This performs a "final exponentiation" routine to convert the result + /// of a Miller loop into an element of `Gt` with help of efficient squaring + /// operation in the so-called `cyclotomic subgroup` of `Fq6` so that + /// it can be compared with other elements of `Gt`. + pub fn final_exponentiation(&self) -> Gt { + #[must_use] + fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) { + let t0 = a.square(); + let t1 = b.square(); + let mut t2 = t1.mul_by_nonresidue(); + let c0 = t2 + t0; + t2 = a + b; + t2 = t2.square(); + t2 -= t0; + let c1 = t2 - t1; + + (c0, c1) + } + // Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography + // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions + // https://eprint.iacr.org/2009/565.pdf + #[must_use] + fn cyclotomic_square(f: Fp12) -> Fp12 { + let mut z0 = f.c0.c0; + let mut z4 = f.c0.c1; + let mut z3 = f.c0.c2; + let mut z2 = f.c1.c0; + let mut z1 = f.c1.c1; + let mut z5 = f.c1.c2; + + let (t0, t1) = fp4_square(z0, z1); + + // For A + z0 = t0 - z0; + z0 = z0 + z0 + t0; + + z1 = t1 + z1; + z1 = z1 + z1 + t1; + + let (mut t0, t1) = fp4_square(z2, z3); + let (t2, t3) = fp4_square(z4, z5); + + // For C + z4 = t0 - z4; + z4 = z4 + z4 + t0; + + z5 = t1 + z5; + z5 = z5 + z5 + t1; + + // For B + t0 = t3.mul_by_nonresidue(); + z2 = t0 + z2; + z2 = z2 + z2 + t0; + + z3 = t2 - z3; + z3 = z3 + z3 + t2; + + Fp12 { + c0: Fp6 { + c0: z0, + c1: z4, + c2: z3, + }, + c1: Fp6 { + c0: z2, + c1: z1, + c2: z5, + }, + } + } + #[must_use] + fn cycolotomic_exp(f: Fp12) -> Fp12 { + let x = BLS_X; + let mut tmp = Fp12::one(); + let mut found_one = false; + for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) { + if found_one { + tmp = cyclotomic_square(tmp) + } else { + found_one = i; + } + + if i { + tmp *= f; + } + } + + tmp.conjugate() + } + + let mut f = self.0; + let mut t0 = f + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map() + .frobenius_map(); + Gt(f.invert() + .map(|mut t1| { + let mut t2 = t0 * t1; + t1 = t2; + t2 = t2.frobenius_map().frobenius_map(); + t2 *= t1; + t1 = cyclotomic_square(t2).conjugate(); + let mut t3 = cycolotomic_exp(t2); + let mut t4 = cyclotomic_square(t3); + let mut t5 = t1 * t3; + t1 = cycolotomic_exp(t5); + t0 = cycolotomic_exp(t1); + let mut t6 = cycolotomic_exp(t0); + t6 *= t4; + t4 = cycolotomic_exp(t6); + t5 = t5.conjugate(); + t4 *= t5 * t2; + t5 = t2.conjugate(); + t1 *= t2; + t1 = t1.frobenius_map().frobenius_map().frobenius_map(); + t6 *= t5; + t6 = t6.frobenius_map(); + t3 *= t0; + t3 = t3.frobenius_map().frobenius_map(); + t3 *= t1; + t3 *= t6; + f = t3 * t4; + + f + }) + // We unwrap() because `MillerLoopResult` can only be constructed + // by a function within this crate, and we uphold the invariant + // that the enclosed value is nonzero. + .unwrap()) + } +} + +impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult { + type Output = MillerLoopResult; + + #[inline] + fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult { + MillerLoopResult(self.0 * rhs.0) + } +} + +impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult); + +/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with +/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$. +/// +/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to +/// keep code and abstractions consistent. +#[derive(Copy, Clone, Debug)] +pub struct Gt(pub(crate) Fp12); + +impl ConstantTimeEq for Gt { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } +} + +impl ConditionallySelectable for Gt { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Gt(Fp12::conditional_select(&a.0, &b.0, choice)) + } +} + +impl Eq for Gt {} +impl PartialEq for Gt { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } +} + +impl Gt { + /// Returns the group identity, which is $1$. + pub fn identity() -> Gt { + Gt(Fp12::one()) + } + + /// Doubles this group element. + pub fn double(&self) -> Gt { + Gt(self.0.square()) + } +} + +impl<'a> Neg for &'a Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + // The element is unitary, so we just conjugate. + Gt(self.0.conjugate()) + } +} + +impl Neg for Gt { + type Output = Gt; + + #[inline] + fn neg(self) -> Gt { + -&self + } +} + +impl<'a, 'b> Add<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn add(self, rhs: &'b Gt) -> Gt { + Gt(self.0 * rhs.0) + } +} + +impl<'a, 'b> Sub<&'b Gt> for &'a Gt { + type Output = Gt; + + #[inline] + fn sub(self, rhs: &'b Gt) -> Gt { + self + (-rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { + type Output = Gt; + + fn mul(self, other: &'b Scalar) -> Self::Output { + let mut acc = Gt::identity(); + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + for bit in other + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = Gt::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl_binops_additive!(Gt, Gt); +impl_binops_multiplicative!(Gt, Scalar); + +#[cfg(feature = "alloc")] +#[derive(Clone, Debug)] +/// This structure contains cached computations pertaining to a $\mathbb{G}_2$ +/// element as part of the pairing function (specifically, the Miller loop) and +/// so should be computed whenever a $\mathbb{G}_2$ element is being used in +/// multiple pairings or is otherwise known in advance. This should be used in +/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop) +/// function provided by this crate. +/// +/// Requires the `alloc` and `pairing` crate features to be enabled. +pub struct G2Prepared { + infinity: Choice, + coeffs: Vec<(Fp2, Fp2, Fp2)>, +} + +#[cfg(feature = "alloc")] +impl From for G2Prepared { + fn from(q: G2Affine) -> G2Prepared { + struct Adder { + cur: G2Projective, + base: G2Affine, + coeffs: Vec<(Fp2, Fp2, Fp2)>, + } + + impl MillerLoopDriver for Adder { + type Output = (); + + fn doubling_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + self.coeffs.push(coeffs); + } + fn addition_step(&mut self, _: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + self.coeffs.push(coeffs); + } + fn square_output(_: Self::Output) -> Self::Output {} + fn conjugate(_: Self::Output) -> Self::Output {} + fn one() -> Self::Output {} + } + + let is_identity = q.is_identity(); + let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + coeffs: Vec::with_capacity(68), + }; + + miller_loop(&mut adder); + + assert_eq!(adder.coeffs.len(), 68); + + G2Prepared { + infinity: is_identity, + coeffs: adder.coeffs, + } + } +} + +#[cfg(feature = "alloc")] +/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms +/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ +/// +/// Requires the `alloc` and `pairing` crate features to be enabled. +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult { + struct Adder<'a, 'b, 'c> { + terms: &'c [(&'a G1Affine, &'b G2Prepared)], + index: usize, + } + + impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> { + type Output = Fp12; + + fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn addition_step(&mut self, mut f: Self::Output) -> Self::Output { + let index = self.index; + for term in self.terms { + let either_identity = term.0.is_identity() | term.1.infinity; + + let new_f = ell(f, &term.1.coeffs[index], term.0); + f = Fp12::conditional_select(&new_f, &f, either_identity); + } + self.index += 1; + + f + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let mut adder = Adder { terms, index: 0 }; + + let tmp = miller_loop(&mut adder); + + MillerLoopResult(tmp) +} + +/// Invoke the pairing function without the use of precomputation and other optimizations. +pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { + struct Adder { + cur: G2Projective, + base: G2Affine, + p: G1Affine, + } + + impl MillerLoopDriver for Adder { + type Output = Fp12; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = doubling_step(&mut self.cur); + ell(f, &coeffs, &self.p) + } + fn addition_step(&mut self, f: Self::Output) -> Self::Output { + let coeffs = addition_step(&mut self.cur, &self.base); + ell(f, &coeffs, &self.p) + } + fn square_output(f: Self::Output) -> Self::Output { + f.square() + } + fn conjugate(f: Self::Output) -> Self::Output { + f.conjugate() + } + fn one() -> Self::Output { + Fp12::one() + } + } + + let either_identity = p.is_identity() | q.is_identity(); + let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity); + let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity); + + let mut adder = Adder { + cur: G2Projective::from(q), + base: q, + p, + }; + + let tmp = miller_loop(&mut adder); + let tmp = MillerLoopResult(Fp12::conditional_select( + &tmp, + &Fp12::one(), + either_identity, + )); + tmp.final_exponentiation() +} + +trait MillerLoopDriver { + type Output; + + fn doubling_step(&mut self, f: Self::Output) -> Self::Output; + fn addition_step(&mut self, f: Self::Output) -> Self::Output; + fn square_output(f: Self::Output) -> Self::Output; + fn conjugate(f: Self::Output) -> Self::Output; + fn one() -> Self::Output; +} + +/// This is a "generic" implementation of the Miller loop to avoid duplicating code +/// structure elsewhere; instead, we'll write concrete instantiations of +/// `MillerLoopDriver` for whatever purposes we need (such as caching modes). +fn miller_loop(driver: &mut D) -> D::Output { + let mut f = D::one(); + + let mut found_one = false; + for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) { + if !found_one { + found_one = i; + continue; + } + + f = driver.doubling_step(f); + + if i { + f = driver.addition_step(f); + } + + f = D::square_output(f); + } + + f = driver.doubling_step(f); + + if BLS_X_IS_NEGATIVE { + f = D::conjugate(f); + } + + f +} + +fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + + c0.c0 *= p.y; + c0.c1 *= p.y; + + c1.c0 *= p.x; + c1.c1 *= p.x; + + f.mul_by_014(&coeffs.2, &c1, &c0) +} + +fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + let tmp0 = r.x.square(); + let tmp1 = r.y.square(); + let tmp2 = tmp1.square(); + let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; + let tmp3 = tmp3 + tmp3; + let tmp4 = tmp0 + tmp0 + tmp0; + let tmp6 = r.x + tmp4; + let tmp5 = tmp4.square(); + let zsquared = r.z.square(); + r.x = tmp5 - tmp3 - tmp3; + r.z = (r.z + r.y).square() - tmp1 - zsquared; + r.y = (tmp3 - r.x) * tmp4; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + let tmp2 = tmp2 + tmp2; + r.y -= tmp2; + let tmp3 = tmp4 * zsquared; + let tmp3 = tmp3 + tmp3; + let tmp3 = -tmp3; + let tmp6 = tmp6.square() - tmp0 - tmp5; + let tmp1 = tmp1 + tmp1; + let tmp1 = tmp1 + tmp1; + let tmp6 = tmp6 - tmp1; + let tmp0 = r.z * zsquared; + let tmp0 = tmp0 + tmp0; + + (tmp0, tmp3, tmp6) +} + +fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + let zsquared = r.z.square(); + let ysquared = q.y.square(); + let t0 = zsquared * q.x; + let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; + let t2 = t0 - r.x; + let t3 = t2.square(); + let t4 = t3 + t3; + let t4 = t4 + t4; + let t5 = t4 * t2; + let t6 = t1 - r.y - r.y; + let t9 = t6 * q.x; + let t7 = t4 * r.x; + r.x = t6.square() - t5 - t7 - t7; + r.z = (r.z + t2).square() - zsquared - t3; + let t10 = q.y + r.z; + let t8 = (t7 - r.x) * t6; + let t0 = r.y * t5; + let t0 = t0 + t0; + r.y = t8 - t0; + let t10 = t10.square() - ysquared; + let ztsquared = r.z.square(); + let t10 = t10 - ztsquared; + let t9 = t9 + t9 - t10; + let t10 = r.z + r.z; + let t6 = -t6; + let t1 = t6 + t6; + + (t10, t1, t9) +} + +#[test] +fn test_bilinearity() { + use crate::Scalar; + + let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); + let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); + let c = a * b; + + let g = G1Affine::from(G1Affine::generator() * a); + let h = G2Affine::from(G2Affine::generator() * b); + let p = pairing(&g, &h); + + assert!(p != Gt::identity()); + + let expected = G1Affine::from(G1Affine::generator() * c); + + assert_eq!(p, pairing(&expected, &G2Affine::generator())); + assert_eq!( + p, + pairing(&G1Affine::generator(), &G2Affine::generator()) * c + ); +} + +#[test] +fn test_unitary() { + let g = G1Affine::generator(); + let h = G2Affine::generator(); + let p = -pairing(&g, &h); + let q = pairing(&g, &-h); + let r = pairing(&-g, &h); + + assert_eq!(p, q); + assert_eq!(q, r); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_multi_miller_loop() { + let a1 = G1Affine::generator(); + let b1 = G2Affine::generator(); + + let a2 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), + ); + let b2 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), + ); + + let a3 = G1Affine::identity(); + let b3 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), + ); + + let a4 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), + ); + let b4 = G2Affine::identity(); + + let a5 = G1Affine::from( + G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(), + ); + let b5 = G2Affine::from( + G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(), + ); + + let b1_prepared = G2Prepared::from(b1); + let b2_prepared = G2Prepared::from(b2); + let b3_prepared = G2Prepared::from(b3); + let b4_prepared = G2Prepared::from(b4); + let b5_prepared = G2Prepared::from(b5); + + let expected = pairing(&a1, &b1) + + pairing(&a2, &b2) + + pairing(&a3, &b3) + + pairing(&a4, &b4) + + pairing(&a5, &b5); + + let test = multi_miller_loop(&[ + (&a1, &b1_prepared), + (&a2, &b2_prepared), + (&a3, &b3_prepared), + (&a4, &b4_prepared), + (&a5, &b5_prepared), + ]) + .final_exponentiation(); + + assert_eq!(expected, test); +} diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs new file mode 100644 index 0000000..91f88a4 --- /dev/null +++ b/bls12_381/src/scalar.rs @@ -0,0 +1,1076 @@ +//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ +//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` + +use core::convert::TryFrom; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::util::{adc, mac, sbb}; + +/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic +/// curve construction. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. `Scalar` values are always in +// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. +#[derive(Clone, Copy, Eq)] +pub struct Scalar(pub(crate) [u64; 4]); + +impl fmt::Debug for Scalar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter().rev() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl From for Scalar { + fn from(val: u64) -> Scalar { + Scalar([val, 0, 0, 0]) * R2 + } +} + +impl ConstantTimeEq for Scalar { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + } +} + +impl PartialEq for Scalar { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Scalar { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Scalar([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) + } +} + +/// Constant representing the modulus +/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +const MODULUS: Scalar = Scalar([ + 0xffff_ffff_0000_0001, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, +]); + +impl<'a> Neg for &'a Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + self.neg() + } +} + +impl Neg for Scalar { + type Output = Scalar; + + #[inline] + fn neg(self) -> Scalar { + -&self + } +} + +impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn sub(self, rhs: &'b Scalar) -> Scalar { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn add(self, rhs: &'b Scalar) -> Scalar { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { + type Output = Scalar; + + #[inline] + fn mul(self, rhs: &'b Scalar) -> Scalar { + self.mul(rhs) + } +} + +impl_binops_additive!(Scalar, Scalar); +impl_binops_multiplicative!(Scalar, Scalar); + +/// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xffff_fffe_ffff_ffff; + +/// R = 2^256 mod q +const R: Scalar = Scalar([ + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, +]); + +/// R^2 = 2^512 mod q +const R2: Scalar = Scalar([ + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, +]); + +/// R^3 = 2^768 mod q +const R3: Scalar = Scalar([ + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9, +]); + +const S: u32 = 32; + +/// GENERATOR^t where t * 2^s + 1 = q +/// with t odd. In other words, this +/// is a 2^s root of unity. +/// +/// `GENERATOR = 7 mod q` is a generator +/// of the q - 1 order multiplicative +/// subgroup. +const ROOT_OF_UNITY: Scalar = Scalar([ + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b, +]); + +impl Default for Scalar { + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl Scalar { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Scalar { + Scalar([0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Scalar { + R + } + + /// Doubles this field element. + #[inline] + pub const fn double(&self) -> Scalar { + // TODO: This can be achieved more efficiently with a bitshift. + self.add(self) + } + + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `Scalar`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = Scalar([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); + tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); + tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); + tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Scalar` into a byte representation in + /// little-endian byte order. + pub fn to_bytes(&self) -> [u8; 32] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + /// Converts a 512-bit little endian integer into + /// a `Scalar` by reducing by the modulus. + pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { + Scalar::from_u512([ + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), + u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), + ]) + } + + fn from_u512(limbs: [u64; 8]) -> Scalar { + // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits + // with the higher bits multiplied by 2^256. Thus, we perform two reductions + // + // 1. the lower bits are multiplied by R^2, as normal + // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 + // + // and computing their sum in the field. It remains to see that arbitrary 256-bit + // numbers can be placed into Montgomery form safely using the reduction. The + // reduction works so long as the product is less than R=2^256 multipled by + // the modulus. This holds because for any `c` smaller than the modulus, we have + // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the + // reduction always works so long as `c` is in the field; in this case it is either the + // constant `R2` or `R3`. + let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); + let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } + + /// Converts from an integer represented in little endian + /// into its (congruent) `Scalar` representation. + pub const fn from_raw(val: [u64; 4]) -> Self { + (&Scalar(val)).mul(&R2) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Scalar { + let (r1, carry) = mac(0, self.0[0], self.0[1], 0); + let (r2, carry) = mac(0, self.0[0], self.0[2], carry); + let (r3, r4) = mac(0, self.0[0], self.0[3], carry); + + let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); + let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); + + let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let (r0, carry) = mac(0, self.0[0], self.0[0], 0); + let (r1, carry) = adc(0, r1, carry); + let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); + let (r3, carry) = adc(0, r3, carry); + let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); + let (r5, carry) = adc(0, r5, carry); + let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); + let (r7, _) = adc(0, r7, carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Computes the square root of this element, if it exists. + pub fn sqrt(&self) -> CtOption { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + let w = self.pow_vartime(&[ + 0x7fff_2dff_7fff_ffff, + 0x04d0_ec02_a9de_d201, + 0x94ce_bea4_199c_ec04, + 0x0000_0000_39f6_d3a9, + ]); + + let mut v = S; + let mut x = self * w; + let mut b = x * w; + + // Initialize z as the 2^S root of unity. + let mut z = ROOT_OF_UNITY; + + for max_v in (1..=S).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: Choice = 1.into(); + + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&Scalar::one()); + let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = Scalar::conditional_select(&z, &new_z, j_less_than_v); + } + + let result = x * z; + x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); + z = z.square(); + b *= z; + v = k; + } + + CtOption::new( + x, + (x * x).ct_eq(self), // Only return Some if it's the square root. + ) + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + pub fn pow(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + let mut tmp = res; + tmp *= self; + res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); + } + } + res + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + /// + /// **This operation is variable time with respect + /// to the exponent.** If the exponent is fixed, + /// this operation is effectively constant time. + pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res.mul_assign(self); + } + } + } + res + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + pub fn invert(&self) -> CtOption { + #[inline(always)] + fn square_assign_multi(n: &mut Scalar, num_times: usize) { + for _ in 0..num_times { + *n = n.square(); + } + } + // found using https://github.com/kwantam/addchain + let mut t0 = self.square(); + let mut t1 = t0 * self; + let mut t16 = t0.square(); + let mut t6 = t16.square(); + let mut t5 = t6 * t0; + t0 = t6 * t16; + let mut t12 = t5 * t16; + let mut t2 = t6.square(); + let mut t7 = t5 * t6; + let mut t15 = t0 * t5; + let mut t17 = t12.square(); + t1 *= t17; + let mut t3 = t7 * t2; + let t8 = t1 * t17; + let t4 = t8 * t2; + let t9 = t8 * t7; + t7 = t4 * t5; + let t11 = t4 * t17; + t5 = t9 * t17; + let t14 = t7 * t15; + let t13 = t11 * t12; + t12 = t11 * t17; + t15 *= &t12; + t16 *= &t15; + t3 *= &t16; + t17 *= &t3; + t0 *= &t17; + t6 *= &t0; + t2 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t17; + square_assign_multi(&mut t0, 9); + t0 *= &t16; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 9); + t0 *= &t15; + square_assign_multi(&mut t0, 7); + t0 *= &t14; + square_assign_multi(&mut t0, 7); + t0 *= &t13; + square_assign_multi(&mut t0, 10); + t0 *= &t12; + square_assign_multi(&mut t0, 9); + t0 *= &t11; + square_assign_multi(&mut t0, 8); + t0 *= &t8; + square_assign_multi(&mut t0, 8); + t0 *= self; + square_assign_multi(&mut t0, 14); + t0 *= &t9; + square_assign_multi(&mut t0, 10); + t0 *= &t8; + square_assign_multi(&mut t0, 15); + t0 *= &t7; + square_assign_multi(&mut t0, 10); + t0 *= &t6; + square_assign_multi(&mut t0, 8); + t0 *= &t5; + square_assign_multi(&mut t0, 16); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 7); + t0 *= &t4; + square_assign_multi(&mut t0, 9); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t3; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 8); + t0 *= &t2; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + square_assign_multi(&mut t0, 5); + t0 *= &t1; + + CtOption::new(t0, !self.ct_eq(&Self::zero())) + } + + #[inline(always)] + const fn montgomery_reduce( + r0: u64, + r1: u64, + r2: u64, + r3: u64, + r4: u64, + r5: u64, + r6: u64, + r7: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let (_, carry) = mac(r0, k, MODULUS.0[0], 0); + let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); + let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); + let (r4, carry2) = adc(r4, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS.0[0], 0); + let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); + let (r5, carry2) = adc(r5, carry2, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS.0[0], 0); + let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); + let (r6, carry2) = adc(r6, carry2, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS.0[0], 0); + let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); + let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); + let (r7, _) = adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) + } + + /// Multiplies `rhs` by `self`, returning the result. + #[inline] + pub const fn mul(&self, rhs: &Self) -> Self { + // Schoolbook multiplication + + let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); + + let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); + let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); + let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); + let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); + + let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); + let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); + let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); + let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); + + let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); + let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); + let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); + let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); + + Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Subtracts `rhs` from `self`, returning the result. + #[inline] + pub const fn sub(&self, rhs: &Self) -> Self { + let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); + let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); + let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); + let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. + let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); + let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); + let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); + let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); + + Scalar([d0, d1, d2, d3]) + } + + /// Adds `rhs` to `self`, returning the result. + #[inline] + pub const fn add(&self, rhs: &Self) -> Self { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, _) = adc(self.0[3], rhs.0[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) + } + + /// Negates `self`. + #[inline] + pub const fn neg(&self) -> Self { + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); + let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); + + Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) + } +} + +impl<'a> From<&'a Scalar> for [u8; 32] { + fn from(value: &'a Scalar) -> [u8; 32] { + value.to_bytes() + } +} + +#[test] +fn test_inv() { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 + + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(MODULUS.0[0]); + } + inv = inv.wrapping_neg(); + + assert_eq!(inv, INV); +} + +#[cfg(feature = "std")] +#[test] +fn test_debug() { + assert_eq!( + format!("{:?}", Scalar::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Scalar::one()), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + format!("{:?}", R2), + "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" + ); +} + +#[test] +fn test_equality() { + assert_eq!(Scalar::zero(), Scalar::zero()); + assert_eq!(Scalar::one(), Scalar::one()); + assert_eq!(R2, R2); + + assert!(Scalar::zero() != Scalar::one()); + assert!(Scalar::one() != R2); +} + +#[test] +fn test_to_bytes() { + assert_eq!( + Scalar::zero().to_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + Scalar::one().to_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + R2.to_bytes(), + [ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ] + ); + + assert_eq!( + (-&Scalar::one()).to_bytes(), + [ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ] + ); +} + +#[test] +fn test_from_bytes() { + assert_eq!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Scalar::zero() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Scalar::one() + ); + + assert_eq!( + Scalar::from_bytes(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!( + Scalar::from_bytes(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_some() + .unwrap_u8() + == 1 + ); + + // modulus is invalid + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + // Anything larger than the modulus is invalid + assert!( + Scalar::from_bytes(&[ + 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + assert!( + Scalar::from_bytes(&[ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + ]) + .is_none() + .unwrap_u8() + == 1 + ); +} + +#[test] +fn test_from_u512_zero() { + assert_eq!( + Scalar::zero(), + Scalar::from_u512([ + MODULUS.0[0], + MODULUS.0[1], + MODULUS.0[2], + MODULUS.0[3], + 0, + 0, + 0, + 0 + ]) + ); +} + +#[test] +fn test_from_u512_r() { + assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_from_u512_r2() { + assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); +} + +#[test] +fn test_from_u512_max() { + let max_u64 = 0xffff_ffff_ffff_ffff; + assert_eq!( + R3 - R, + Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) + ); +} + +#[test] +fn test_from_bytes_wide_r2() { + assert_eq!( + R2, + Scalar::from_bytes_wide(&[ + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_negative_one() { + assert_eq!( + -&Scalar::one(), + Scalar::from_bytes_wide(&[ + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_maximum() { + assert_eq!( + Scalar([ + 0xc62c_1805_439b_73b1, + 0xc2b9_551e_8ced_218e, + 0xda44_ec81_daf9_a422, + 0x5605_aa60_1c16_2e79, + ]), + Scalar::from_bytes_wide(&[0xff; 64]) + ); +} + +#[test] +fn test_zero() { + assert_eq!(Scalar::zero(), -&Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); + assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); +} + +#[cfg(test)] +const LARGEST: Scalar = Scalar([ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, +]); + +#[test] +fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Scalar([ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]) + ); + + let mut tmp = LARGEST; + tmp += &Scalar([1, 0, 0, 0]); + + assert_eq!(tmp, Scalar::zero()); +} + +#[test] +fn test_negation() { + let tmp = -&LARGEST; + + assert_eq!(tmp, Scalar([1, 0, 0, 0])); + + let tmp = -&Scalar::zero(); + assert_eq!(tmp, Scalar::zero()); + let tmp = -&Scalar([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); +} + +#[test] +fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Scalar::zero()); + + let mut tmp = Scalar::zero(); + tmp -= &LARGEST; + + let mut tmp2 = MODULUS; + tmp2 -= &LARGEST; + + assert_eq!(tmp, tmp2); +} + +#[test] +fn test_multiplication() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp *= &cur; + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_squaring() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp = tmp.square(); + + let mut tmp2 = Scalar::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_inversion() { + assert_eq!(Scalar::zero().invert().is_none().unwrap_u8(), 1); + assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); + assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Scalar::one()); + + tmp.add_assign(&R2); + } +} + +#[test] +fn test_invert_is_pow() { + let q_minus_2 = [ + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]; + + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; + + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&q_minus_2); + r3 = r3.pow(&q_minus_2); + + assert_eq!(r1, r2); + assert_eq!(r2, r3); + // Add R so we check something different next time around + r1.add_assign(&R); + r2 = r1; + r3 = r1; + } +} + +#[test] +fn test_sqrt() { + { + assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); + } + + let mut square = Scalar([ + 0x46cd_85a5_f273_077e, + 0x1d30_c47d_d68f_c735, + 0x77f6_56f6_0bec_a0eb, + 0x494a_a01b_df32_468d, + ]); + + let mut none_count = 0; + + for _ in 0..100 { + let square_root = square.sqrt(); + if square_root.is_none().unwrap_u8() == 1 { + none_count += 1; + } else { + assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + } + square -= Scalar::one(); + } + + assert_eq!(49, none_count); +} + +#[test] +fn test_from_raw() { + assert_eq!( + Scalar::from_raw([ + 0x0001_ffff_fffd, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f, + ]), + Scalar::from_raw([0xffff_ffff_ffff_ffff; 4]) + ); + + assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); + + assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); +} + +#[test] +fn test_double() { + let a = Scalar::from_raw([ + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562, + ]); + + assert_eq!(a.double(), a + a); +} diff --git a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..ea8cd67 Binary files /dev/null and b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..86abfba Binary files /dev/null and b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat new file mode 100644 index 0000000..a40bbe2 Binary files /dev/null and b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000..92e4bc5 Binary files /dev/null and b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat differ diff --git a/bls12_381/src/tests/mod.rs b/bls12_381/src/tests/mod.rs new file mode 100644 index 0000000..d6ab86d --- /dev/null +++ b/bls12_381/src/tests/mod.rs @@ -0,0 +1,230 @@ +use super::*; + +macro_rules! test_vectors { + ($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => { + let mut e = $projective::identity(); + + let mut v = vec![]; + { + let mut expected = $expected; + for _ in 0..1000 { + let e_affine = $affine::from(e); + let encoded = e_affine.$serialize(); + v.extend_from_slice(&encoded[..]); + + let mut decoded = encoded; + let len_of_encoding = decoded.len(); + (&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]); + expected = &expected[len_of_encoding..]; + let decoded = $affine::$deserialize(&decoded).unwrap(); + assert_eq!(e_affine, decoded); + + e = &e + &$projective::generator(); + } + } + + assert_eq!(&v[..], $expected); + }; +} + +#[test] +fn g1_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_uncompressed, + from_uncompressed, + bytes + ); +} + +#[test] +fn g1_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); + test_vectors!( + G1Projective, + G1Affine, + to_compressed, + from_compressed, + bytes + ); +} + +#[test] +fn g2_uncompressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_uncompressed, + from_uncompressed, + bytes + ); +} + +#[test] +fn g2_compressed_valid_test_vectors() { + let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); + test_vectors!( + G2Projective, + G2Affine, + to_compressed, + from_compressed, + bytes + ); +} + +#[test] +fn test_pairing_result_against_relic() { + /* + Sent to me from Diego Aranha (author of RELIC library): + 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 + 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F + 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 + 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F + 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 + 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 + 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D + 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A + 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 + 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 + 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF + 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 + */ + + let a = G1Affine::generator(); + let b = G2Affine::generator(); + + use super::fp::Fp; + use super::fp12::Fp12; + use super::fp2::Fp2; + use super::fp6::Fp6; + + let res = pairing(&a, &b); + + let prep = G2Prepared::from(b); + + assert_eq!( + res, + multi_miller_loop(&[(&a, &prep)]).final_exponentiation() + ); + + assert_eq!( + res.0, + Fp12 { + c0: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825, + ]), + c1: Fp::from_raw_unchecked([ + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e, + ]), + c1: Fp::from_raw_unchecked([ + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889, + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f, + ]), + c1: Fp::from_raw_unchecked([ + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339, + ]) + } + }, + c1: Fp6 { + c0: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae, + ]), + c1: Fp::from_raw_unchecked([ + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc, + ]) + }, + c1: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa, + ]), + c1: Fp::from_raw_unchecked([ + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567, + ]) + }, + c2: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2, + ]), + c1: Fp::from_raw_unchecked([ + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242, + ]) + } + } + } + ); +} diff --git a/bls12_381/src/util.rs b/bls12_381/src/util.rs new file mode 100644 index 0000000..bd25dd0 --- /dev/null +++ b/bls12_381/src/util.rs @@ -0,0 +1,174 @@ +/// Compute a + b + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + (b as u128) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +#[inline(always)] +pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c) + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +macro_rules! impl_add_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Add<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: &'b $rhs) -> $output { + &self + rhs + } + } + + impl<'a> Add<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + self + &rhs + } + } + + impl Add<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + &self + &rhs + } + } + }; +} + +macro_rules! impl_sub_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Sub<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: &'b $rhs) -> $output { + &self - rhs + } + } + + impl<'a> Sub<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + self - &rhs + } + } + + impl Sub<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + &self - &rhs + } + } + }; +} + +macro_rules! impl_binops_additive_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl_add_binop_specify_output!($lhs, $rhs, $output); + impl_sub_binop_specify_output!($lhs, $rhs, $output); + }; +} + +macro_rules! impl_binops_multiplicative_mixed { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Mul<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: &'b $rhs) -> $output { + &self * rhs + } + } + + impl<'a> Mul<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + self * &rhs + } + } + + impl Mul<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + &self * &rhs + } + } + }; +} + +macro_rules! impl_binops_additive { + ($lhs:ident, $rhs:ident) => { + impl_binops_additive_specify_output!($lhs, $rhs, $lhs); + + impl SubAssign<$rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: $rhs) { + *self = &*self - &rhs; + } + } + + impl AddAssign<$rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: $rhs) { + *self = &*self + &rhs; + } + } + + impl<'b> SubAssign<&'b $rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: &'b $rhs) { + *self = &*self - rhs; + } + } + + impl<'b> AddAssign<&'b $rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: &'b $rhs) { + *self = &*self + rhs; + } + } + }; +} + +macro_rules! impl_binops_multiplicative { + ($lhs:ident, $rhs:ident) => { + impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); + + impl MulAssign<$rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: $rhs) { + *self = &*self * &rhs; + } + } + + impl<'b> MulAssign<&'b $rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: &'b $rhs) { + *self = &*self * rhs; + } + } + }; +} diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 212f6c4..01cc6c6 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -1,18 +1,25 @@ [package] name = "ff" -version = "0.4.0" +version = "0.6.0" authors = ["Sean Bowe "] description = "Library for building and interfacing with finite fields" +readme = "README.md" documentation = "https://docs.rs/ff/" homepage = "https://github.com/ebfull/ff" license = "MIT/Apache-2.0" repository = "https://github.com/ebfull/ff" +edition = "2018" [dependencies] -byteorder = "1" -ff_derive = { version = "0.3.0", path = "ff_derive", optional = true } -rand_core = "0.5" +byteorder = { version = "1", default-features = false } +ff_derive = { version = "0.6", path = "ff_derive", optional = true } +rand_core = { version = "0.5", default-features = false } +subtle = { version = "2.2.1", default-features = false, features = ["i128"] } [features] -default = [] +default = ["std"] derive = ["ff_derive"] +std = [] + +[badges] +maintenance = { status = "actively-developed" } diff --git a/ff/README.md b/ff/README.md index 3efef94..57ef693 100644 --- a/ff/README.md +++ b/ff/README.md @@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`: ```toml [dependencies] -ff = "0.4" +ff = "0.5" ``` -The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more. +The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. +See the **[documentation](https://docs.rs/ff/)** for more. ### #![derive(PrimeField)] -If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue. +If you need an implementation of a prime field, this library also provides a procedural +macro that will expand into an efficient implementation of a prime field when supplied +with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is +also quadratic nonresidue. First, enable the `derive` crate feature: @@ -41,13 +45,16 @@ extern crate ff; struct Fp(FpRepr); ``` -And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement `SqrtField` if supported. The library implements `FpRepr` itself and derives `PrimeFieldRepr` for it. +And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement +`SqrtField` if supported. The library implements `FpRepr` itself and derives +`PrimeFieldRepr` for it. ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml index 914e392..4adf28b 100644 --- a/ff/ff_derive/Cargo.toml +++ b/ff/ff_derive/Cargo.toml @@ -1,20 +1,28 @@ [package] name = "ff_derive" -version = "0.3.0" -authors = ["Sean Bowe "] +version = "0.6.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", +] description = "Procedural macro library used to build custom prime field implementations" documentation = "https://docs.rs/ff/" homepage = "https://github.com/ebfull/ff" license = "MIT/Apache-2.0" repository = "https://github.com/ebfull/ff" +edition = "2018" [lib] proc-macro = true [dependencies] +addchain = "0.1" num-bigint = "0.2" num-traits = "0.2" num-integer = "0.1" -proc-macro2 = "0.4" -quote = "0.6" -syn = "0.14" +proc-macro2 = "1" +quote = "1" +syn = "1" + +[badges] +maintenance = { status = "passively-maintained" } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 5f8e642..f04ecfa 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -2,29 +2,135 @@ extern crate proc_macro; extern crate proc_macro2; -extern crate syn; -#[macro_use] -extern crate quote; - -extern crate num_bigint; -extern crate num_integer; -extern crate num_traits; use num_bigint::BigUint; use num_integer::Integer; use num_traits::{One, ToPrimitive, Zero}; +use quote::quote; use quote::TokenStreamExt; +use std::iter; use std::str::FromStr; -#[proc_macro_derive(PrimeField, attributes(PrimeFieldModulus, PrimeFieldGenerator))] +mod pow_fixed; + +enum ReprEndianness { + Big, + Little, +} + +impl FromStr for ReprEndianness { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "big" => Ok(ReprEndianness::Big), + "little" => Ok(ReprEndianness::Little), + _ => Err(()), + } + } +} + +impl ReprEndianness { + fn repr_endianness(&self) -> proc_macro2::TokenStream { + match self { + ReprEndianness::Big => quote! {::byteorder::BigEndian}, + ReprEndianness::Little => quote! {::byteorder::LittleEndian}, + } + } + + fn modulus_repr(&self, modulus: &BigUint, bytes: usize) -> Vec { + match self { + ReprEndianness::Big => { + let buf = modulus.to_bytes_be(); + iter::repeat(0) + .take(bytes - buf.len()) + .chain(buf.into_iter()) + .collect() + } + ReprEndianness::Little => { + let mut buf = modulus.to_bytes_le(); + buf.extend(iter::repeat(0).take(bytes - buf.len())); + buf + } + } + } + + fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { + let read_repr = match self { + ReprEndianness::Big => quote! { + ::byteorder::BigEndian::read_u64_into(r.as_ref(), &mut inner[..]); + inner.reverse(); + }, + ReprEndianness::Little => quote! { + ::byteorder::LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); + }, + }; + + quote! { + use ::byteorder::ByteOrder; + + let r = { + let mut inner = [0u64; #limbs]; + #read_repr + #name(inner) + }; + + if r.is_valid() { + Some(r * R2) + } else { + None + } + } + } + + fn to_repr( + &self, + repr: &syn::Ident, + mont_reduce_self_params: &proc_macro2::TokenStream, + limbs: usize, + ) -> proc_macro2::TokenStream { + let bytes = limbs * 8; + + let write_repr = match self { + ReprEndianness::Big => quote! { + r.0.reverse(); + ::byteorder::BigEndian::write_u64_into(&r.0, &mut repr[..]); + }, + ReprEndianness::Little => quote! { + ::byteorder::LittleEndian::write_u64_into(&r.0, &mut repr[..]); + }, + }; + + quote! { + use ::byteorder::ByteOrder; + + let mut r = *self; + r.mont_reduce( + #mont_reduce_self_params + ); + + let mut repr = [0u8; #bytes]; + #write_repr + #repr(repr) + } + } + + fn iter_be(&self) -> proc_macro2::TokenStream { + match self { + ReprEndianness::Big => quote! {self.0.iter()}, + ReprEndianness::Little => quote! {self.0.iter().rev()}, + } + } +} + +#[proc_macro_derive( + PrimeField, + attributes(PrimeFieldModulus, PrimeFieldGenerator, PrimeFieldReprEndianness) +)] pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Parse the type definition let ast: syn::DeriveInput = syn::parse(input).unwrap(); - // The struct we're deriving for is a wrapper around a "Repr" type we must construct. - let repr_ident = fetch_wrapped_ident(&ast.data) - .expect("PrimeField derive only operates over tuple structs of a single item"); - // We're given the modulus p of the prime field let modulus: BigUint = fetch_attr("PrimeFieldModulus", &ast.attrs) .expect("Please supply a PrimeFieldModulus attribute") @@ -33,11 +139,18 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // We may be provided with a generator of p - 1 order. It is required that this generator be quadratic // nonresidue. + // TODO: Compute this ourselves. let generator: BigUint = fetch_attr("PrimeFieldGenerator", &ast.attrs) .expect("Please supply a PrimeFieldGenerator attribute") .parse() .expect("PrimeFieldGenerator should be a number"); + // Field element representations may be in little-endian or big-endian. + let endianness = fetch_attr("PrimeFieldReprEndianness", &ast.attrs) + .expect("Please supply a PrimeFieldReprEndianness attribute") + .parse() + .expect("PrimeFieldReprEndianness should be 'big' or 'little'"); + // The arithmetic in this library only works if the modulus*2 is smaller than the backing // representation. Compute the number of limbs we need. let mut limbs = 1; @@ -46,55 +159,166 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut cur = BigUint::one() << 64; // always 64-bit limbs for now while cur < mod2 { limbs += 1; - cur = cur << 64; + cur <<= 64; } } + // The struct we're deriving for must be a wrapper around `pub [u64; limbs]`. + if let Some(err) = validate_struct(&ast, limbs) { + return err.into(); + } + + // Generate the identifier for the "Repr" type we must construct. + let repr_ident = syn::Ident::new( + &format!("{}Repr", ast.ident), + proc_macro2::Span::call_site(), + ); + let mut gen = proc_macro2::TokenStream::new(); - let (constants_impl, sqrt_impl) = - prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator); + let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( + &ast.ident, + &repr_ident, + &modulus, + &endianness, + limbs, + generator, + ); gen.extend(constants_impl); - gen.extend(prime_field_repr_impl(&repr_ident, limbs)); - gen.extend(prime_field_impl(&ast.ident, &repr_ident, limbs)); - gen.extend(sqrt_impl); + gen.extend(prime_field_repr_impl(&repr_ident, &endianness, limbs * 8)); + gen.extend(prime_field_impl( + &ast.ident, + &repr_ident, + &modulus, + &endianness, + limbs, + sqrt_impl, + )); // Return the generated impl gen.into() } -/// Fetches the ident being wrapped by the type we're deriving. -fn fetch_wrapped_ident(body: &syn::Data) -> Option { - match body { - &syn::Data::Struct(ref variant_data) => match variant_data.fields { - syn::Fields::Unnamed(ref fields) => { - if fields.unnamed.len() == 1 { - match fields.unnamed[0].ty { - syn::Type::Path(ref path) => { - if path.path.segments.len() == 1 { - return Some(path.path.segments[0].ident.clone()); - } - } - _ => {} - } - } - } - _ => {} - }, - _ => {} +/// Checks that `body` contains `pub [u64; limbs]`. +fn validate_struct(ast: &syn::DeriveInput, limbs: usize) -> Option { + // The body should be a struct. + let variant_data = match &ast.data { + syn::Data::Struct(x) => x, + _ => { + return Some( + syn::Error::new_spanned(ast, "PrimeField derive only works for structs.") + .to_compile_error(), + ) + } }; + // The struct should contain a single unnamed field. + let fields = match &variant_data.fields { + syn::Fields::Unnamed(x) if x.unnamed.len() == 1 => x, + _ => { + return Some( + syn::Error::new_spanned( + &ast.ident, + format!( + "The struct must contain an array of limbs. Change this to `{}([u64; {}])`", + ast.ident, limbs, + ), + ) + .to_compile_error(), + ) + } + }; + let field = &fields.unnamed[0]; + + // The field should be an array. + let arr = match &field.ty { + syn::Type::Array(x) => x, + _ => { + return Some( + syn::Error::new_spanned( + field, + format!( + "The inner field must be an array of limbs. Change this to `[u64; {}]`", + limbs, + ), + ) + .to_compile_error(), + ) + } + }; + + // The array's element type should be `u64`. + if match arr.elem.as_ref() { + syn::Type::Path(path) => path + .path + .get_ident() + .map(|x| x.to_string() != "u64") + .unwrap_or(true), + _ => true, + } { + return Some( + syn::Error::new_spanned( + arr, + format!( + "PrimeField derive requires 64-bit limbs. Change this to `[u64; {}]", + limbs + ), + ) + .to_compile_error(), + ); + } + + // The array's length should be a literal int equal to `limbs`. + let lit_int = match match &arr.len { + syn::Expr::Lit(expr_lit) => match &expr_lit.lit { + syn::Lit::Int(lit_int) => Some(lit_int), + _ => None, + }, + _ => None, + } { + Some(x) => x, + _ => { + return Some( + syn::Error::new_spanned( + arr, + format!("To derive PrimeField, change this to `[u64; {}]`.", limbs), + ) + .to_compile_error(), + ) + } + }; + if lit_int.base10_digits() != limbs.to_string() { + return Some( + syn::Error::new_spanned( + lit_int, + format!("The given modulus requires {} limbs.", limbs), + ) + .to_compile_error(), + ); + } + + // The field should not be public. + match &field.vis { + syn::Visibility::Inherited => (), + _ => { + return Some( + syn::Error::new_spanned(&field.vis, "Field must not be public.").to_compile_error(), + ) + } + } + + // Valid! None } /// Fetch an attribute string from the derived struct. fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { for attr in attrs { - if let Some(meta) = attr.interpret_meta() { + if let Ok(meta) = attr.parse_meta() { match meta { syn::Meta::NameValue(nv) => { - if nv.ident.to_string() == name { + if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) { match nv.lit { syn::Lit::Str(ref s) => return Some(s.value()), _ => { @@ -113,204 +337,79 @@ fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { None } -// Implement PrimeFieldRepr for the wrapped ident `repr` with `limbs` limbs. -fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { +// Implement the wrapped ident `repr` with `bytes` bytes. +fn prime_field_repr_impl( + repr: &syn::Ident, + endianness: &ReprEndianness, + bytes: usize, +) -> proc_macro2::TokenStream { + let repr_iter_be = endianness.iter_be(); + quote! { - #[derive(Copy, Clone, PartialEq, Eq, Default)] - pub struct #repr(pub [u64; #limbs]); + #[derive(Copy, Clone)] + pub struct #repr(pub [u8; #bytes]); - impl ::std::fmt::Debug for #repr + impl ::subtle::ConstantTimeEq for #repr { + fn ct_eq(&self, other: &#repr) -> ::subtle::Choice { + self.0 + .iter() + .zip(other.0.iter()) + .map(|(a, b)| a.ct_eq(b)) + .fold(1.into(), |acc, x| acc & x) + } + } + + impl ::core::cmp::PartialEq for #repr { + fn eq(&self, other: &#repr) -> bool { + use ::subtle::ConstantTimeEq; + self.ct_eq(other).into() + } + } + + impl ::core::cmp::Eq for #repr { } + + impl ::core::default::Default for #repr { + fn default() -> #repr { + #repr([0u8; #bytes]) + } + } + + impl ::core::fmt::Debug for #repr { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - try!(write!(f, "0x")); - for i in self.0.iter().rev() { - try!(write!(f, "{:016x}", *i)); + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "0x")?; + for i in #repr_iter_be { + write!(f, "{:02x}", *i)?; } Ok(()) } } - impl ::std::fmt::Display for #repr { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - try!(write!(f, "0x")); - for i in self.0.iter().rev() { - try!(write!(f, "{:016x}", *i)); + impl ::core::fmt::Display for #repr { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "0x")?; + for i in #repr_iter_be { + write!(f, "{:02x}", *i)?; } Ok(()) } } - impl AsRef<[u64]> for #repr { + impl AsRef<[u8]> for #repr { #[inline(always)] - fn as_ref(&self) -> &[u64] { + fn as_ref(&self) -> &[u8] { &self.0 } } - impl AsMut<[u64]> for #repr { + impl AsMut<[u8]> for #repr { #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } - - impl From for #repr { - #[inline(always)] - fn from(val: u64) -> #repr { - use std::default::Default; - - let mut repr = Self::default(); - repr.0[0] = val; - repr - } - } - - impl Ord for #repr { - #[inline(always)] - fn cmp(&self, other: &#repr) -> ::std::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::std::cmp::Ordering::Less - } else if a > b { - return ::std::cmp::Ordering::Greater - } - } - - ::std::cmp::Ordering::Equal - } - } - - impl PartialOrd for #repr { - #[inline(always)] - fn partial_cmp(&self, other: &#repr) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::ff::PrimeFieldRepr for #repr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (#limbs as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &#repr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &#repr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::sbb(*a, *b, &mut borrow); - } - } - } } } @@ -321,7 +420,7 @@ fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec { while v > BigUint::zero() { ret.push((&v % &m).to_u64().unwrap()); - v = v >> 64; + v >>= 64; } while ret.len() < limbs { @@ -343,7 +442,7 @@ fn biguint_num_bits(mut v: BigUint) -> u32 { let mut bits = 0; while v != BigUint::zero() { - v = v >> 1; + v >>= 1; bits += 1; } @@ -389,7 +488,8 @@ fn test_exp() { fn prime_field_constants_and_sqrt( name: &syn::Ident, repr: &syn::Ident, - modulus: BigUint, + modulus: &BigUint, + endianness: &ReprEndianness, limbs: usize, generator: BigUint, ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { @@ -402,129 +502,106 @@ fn prime_field_constants_and_sqrt( let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone()); // Compute R = 2**(64 * limbs) mod m - let r = (BigUint::one() << (limbs * 64)) % &modulus; + let r = (BigUint::one() << (limbs * 64)) % modulus; // modulus - 1 = 2^s * t let mut s: u32 = 0; - let mut t = &modulus - BigUint::from_str("1").unwrap(); + let mut t = modulus - BigUint::from_str("1").unwrap(); while t.is_even() { - t = t >> 1; + t >>= 1; s += 1; } // Compute 2^s root of unity given the generator - let root_of_unity = biguint_to_u64_vec( - (exp(generator.clone(), &t, &modulus) * &r) % &modulus, - limbs, - ); - let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs); - - let mod_minus_1_over_2 = - biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs); - let legendre_impl = quote! { - fn legendre(&self) -> ::ff::LegendreSymbol { - // s = self^((modulus - 1) // 2) - let s = self.pow(#mod_minus_1_over_2); - if s == Self::zero() { - ::ff::LegendreSymbol::Zero - } else if s == Self::one() { - ::ff::LegendreSymbol::QuadraticResidue - } else { - ::ff::LegendreSymbol::QuadraticNonResidue - } - } - }; + let root_of_unity = + biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs); + let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs); let sqrt_impl = - if (&modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { - let mod_minus_3_over_4 = - biguint_to_u64_vec((&modulus - BigUint::from_str("3").unwrap()) >> 2, limbs); - - // Compute -R as (m - r) - let rneg = biguint_to_u64_vec(&modulus - &r, limbs); + if (modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { + // Addition chain for (r + 1) // 4 + let mod_plus_1_over_4 = pow_fixed::generate( + "e! {self}, + (modulus + BigUint::from_str("1").unwrap()) >> 2, + ); quote! { - impl ::ff::SqrtField for #name { - #legendre_impl + use ::subtle::ConstantTimeEq; - fn sqrt(&self) -> Option { - // Shank's algorithm for q mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + // Because r = 3 (mod 4) + // sqrt can be done with only one exponentiation, + // via the computation of self^((r + 1) // 4) (mod r) + let sqrt = { + #mod_plus_1_over_4 + }; - let mut a1 = self.pow(#mod_minus_3_over_4); - - let mut a0 = a1; - a0.square(); - a0.mul_assign(self); - - if a0.0 == #repr(#rneg) { - None - } else { - a1.mul_assign(self); - Some(a1) - } - } - } + ::subtle::CtOption::new( + sqrt, + (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. + ) } - } else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs); - let t = biguint_to_u64_vec(t.clone(), limbs); + } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { + // Addition chain for (t - 1) // 2 + let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); quote! { - impl ::ff::SqrtField for #name { - #legendre_impl + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + use ::subtle::{ConditionallySelectable, ConstantTimeEq}; - fn sqrt(&self) -> Option { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + // w = self^((t - 1) // 2) + let w = { + #t_minus_1_over_2 + }; - match self.legendre() { - ::ff::LegendreSymbol::Zero => Some(*self), - ::ff::LegendreSymbol::QuadraticNonResidue => None, - ::ff::LegendreSymbol::QuadraticResidue => { - let mut c = #name(ROOT_OF_UNITY); - let mut r = self.pow(#t_plus_1_over_2); - let mut t = self.pow(#t); - let mut m = S; + let mut v = S; + let mut x = *self * &w; + let mut b = x * &w; - while t != Self::one() { - let mut i = 1; - { - let mut t2i = t; - t2i.square(); - loop { - if t2i == Self::one() { - break; - } - t2i.square(); - i += 1; - } - } + // Initialize z as the 2^S root of unity. + let mut z = ROOT_OF_UNITY; - for _ in 0..(m - i - 1) { - c.square(); - } - r.mul_assign(&c); - c.square(); - t.mul_assign(&c); - m = i; - } + for max_v in (1..=S).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: ::subtle::Choice = 1.into(); - Some(r) - } - } + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&#name::one()); + let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = #name::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = #name::conditional_select(&z, &new_z, j_less_than_v); } + + let result = x * &z; + x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); + z = z.square(); + b *= &z; + v = k; } + + ::subtle::CtOption::new( + x, + (x * &x).ct_eq(self), // Only return Some if it's the square root. + ) } } else { - quote! {} + syn::Error::new_spanned( + &name, + "ff_derive can't generate a square root function for this field.", + ) + .to_compile_error() }; // Compute R^2 mod m - let r2 = biguint_to_u64_vec((&r * &r) % &modulus, limbs); + let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); let r = biguint_to_u64_vec(r, limbs); - let modulus = biguint_to_real_u64_vec(modulus, limbs); + let modulus_repr = endianness.modulus_repr(modulus, limbs * 8); + let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 let mut inv = 1u64; @@ -537,7 +614,10 @@ fn prime_field_constants_and_sqrt( ( quote! { /// This is the modulus m of the prime field - const MODULUS: #repr = #repr([#(#modulus,)*]); + const MODULUS: #repr = #repr([#(#modulus_repr,)*]); + + /// This is the modulus m of the prime field in limb form + const MODULUS_LIMBS: #name = #name([#(#modulus,)*]); /// The number of bits needed to represent the modulus. const MODULUS_BITS: u32 = #modulus_num_bits; @@ -547,23 +627,23 @@ fn prime_field_constants_and_sqrt( const REPR_SHAVE_BITS: u32 = #repr_shave_bits; /// 2^{limbs*64} mod m - const R: #repr = #repr(#r); + const R: #name = #name(#r); /// 2^{limbs*64*2} mod m - const R2: #repr = #repr(#r2); + const R2: #name = #name(#r2); /// -(m^{-1} mod m) mod m const INV: u64 = #inv; /// Multiplicative generator of `MODULUS` - 1 order, also quadratic /// nonresidue. - const GENERATOR: #repr = #repr(#generator); + const GENERATOR: #name = #name(#generator); /// 2^s * t = MODULUS - 1 with t odd const S: u32 = #s; /// 2^s root of unity computed by GENERATOR^t - const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); + const ROOT_OF_UNITY: #name = #name(#root_of_unity); }, sqrt_impl, ) @@ -573,7 +653,10 @@ fn prime_field_constants_and_sqrt( fn prime_field_impl( name: &syn::Ident, repr: &syn::Ident, + modulus: &BigUint, + endianness: &ReprEndianness, limbs: usize, + sqrt_impl: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { // Returns r{n} as an ident. fn get_temp(n: usize) -> syn::Ident { @@ -604,14 +687,14 @@ fn prime_field_impl( gen.extend(quote! { let k = #temp.wrapping_mul(INV); let mut carry = 0; - ::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry); + ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[0], &mut carry); }); } for j in 1..limbs { let temp = get_temp(i + j); gen.extend(quote! { - #temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry); + #temp = ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[#j], &mut carry); }); } @@ -638,7 +721,7 @@ fn prime_field_impl( let temp = get_temp(limbs + i); gen.extend(quote! { - (self.0).0[#i] = #temp; + self.0[#i] = #temp; }); } @@ -657,11 +740,11 @@ fn prime_field_impl( let temp = get_temp(i + j); if i == 0 { gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry); + let #temp = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#j], &mut carry); }); } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#a.0).0[#j], &mut carry); + gen.extend(quote! { + let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #a.0[#j], &mut carry); }); } } @@ -701,11 +784,11 @@ fn prime_field_impl( let temp1 = get_temp(i * 2 + 1); if i == 0 { gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); + let #temp0 = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#i], &mut carry); }); } else { - gen.extend(quote!{ - let #temp0 = ::ff::mac_with_carry(#temp0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); + gen.extend(quote! { + let #temp0 = ::ff::mac_with_carry(#temp0, #a.0[#i], #a.0[#i], &mut carry); }); } @@ -716,12 +799,14 @@ fn prime_field_impl( let mut mont_calling = proc_macro2::TokenStream::new(); mont_calling.append_separated( - (0..(limbs * 2)).map(|i| get_temp(i)), + (0..(limbs * 2)).map(get_temp), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), ); gen.extend(quote! { - self.mont_reduce(#mont_calling); + let mut ret = *self; + ret.mont_reduce(#mont_calling); + ret }); gen @@ -744,11 +829,11 @@ fn prime_field_impl( if i == 0 { gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry); + let #temp = ::ff::mac_with_carry(0, #a.0[#i], #b.0[#j], &mut carry); }); } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#b.0).0[#j], &mut carry); + gen.extend(quote! { + let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #b.0[#j], &mut carry); }); } } @@ -762,7 +847,7 @@ fn prime_field_impl( let mut mont_calling = proc_macro2::TokenStream::new(); mont_calling.append_separated( - (0..(limbs * 2)).map(|i| get_temp(i)), + (0..(limbs * 2)).map(get_temp), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), ); @@ -773,96 +858,316 @@ fn prime_field_impl( gen } + /// Generates an implementation of multiplicative inversion within the target prime + /// field. + fn inv_impl( + a: proc_macro2::TokenStream, + name: &syn::Ident, + modulus: &BigUint, + ) -> proc_macro2::TokenStream { + // Addition chain for p - 2 + let mod_minus_2 = pow_fixed::generate(&a, modulus - BigUint::from(2u64)); + + quote! { + use ::subtle::ConstantTimeEq; + + // By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then: + // a^-1 ≡ a^(phi(p) - 1) mod p + // + // `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and + // thus: + // a^-1 ≡ a^(p - 2) mod p + let inv = { + #mod_minus_2 + }; + + ::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero())) + } + } + let squaring_impl = sqr_impl(quote! {self}, limbs); let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs); + let invert_impl = inv_impl(quote! {self}, name, modulus); let montgomery_impl = mont_impl(limbs); - // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... - let mut into_repr_params = proc_macro2::TokenStream::new(); - into_repr_params.append_separated( - (0..limbs) - .map(|i| quote! { (self.0).0[#i] }) - .chain((0..limbs).map(|_| quote! {0})), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), + // self.0[0].ct_eq(&other.0[0]) & self.0[1].ct_eq(&other.0[1]) & ... + let mut ct_eq_impl = proc_macro2::TokenStream::new(); + ct_eq_impl.append_separated( + (0..limbs).map(|i| quote! { self.0[#i].ct_eq(&other.0[#i]) }), + proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), ); + fn mont_reduce_params(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { + // a.0[0], a.0[1], ..., 0, 0, 0, 0, ... + let mut mont_reduce_params = proc_macro2::TokenStream::new(); + mont_reduce_params.append_separated( + (0..limbs) + .map(|i| quote! { #a.0[#i] }) + .chain((0..limbs).map(|_| quote! {0})), + proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), + ); + mont_reduce_params + } + + let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); + let mont_reduce_other_params = mont_reduce_params(quote! {other}, limbs); + + let repr_endianness = endianness.repr_endianness(); + let from_repr_impl = endianness.from_repr(name, limbs); + let to_repr_impl = endianness.to_repr(repr, &mont_reduce_self_params, limbs); + let top_limb_index = limbs - 1; quote! { - impl ::std::marker::Copy for #name { } + impl ::core::marker::Copy for #name { } - impl ::std::clone::Clone for #name { + impl ::core::clone::Clone for #name { fn clone(&self) -> #name { *self } } - impl ::std::cmp::PartialEq for #name { - fn eq(&self, other: &#name) -> bool { - self.0 == other.0 + impl ::core::default::Default for #name { + fn default() -> #name { + #name::zero() } } - impl ::std::cmp::Eq for #name { } + impl ::subtle::ConstantTimeEq for #name { + fn ct_eq(&self, other: &#name) -> ::subtle::Choice { + self.to_repr().ct_eq(&other.to_repr()) + } + } - impl ::std::fmt::Debug for #name + impl ::core::cmp::PartialEq for #name { + fn eq(&self, other: &#name) -> bool { + use ::subtle::ConstantTimeEq; + self.ct_eq(other).into() + } + } + + impl ::core::cmp::Eq for #name { } + + impl ::core::fmt::Debug for #name { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}({:?})", stringify!(#name), self.into_repr()) + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({:?})", stringify!(#name), self.to_repr()) } } /// Elements are ordered lexicographically. impl Ord for #name { #[inline(always)] - fn cmp(&self, other: &#name) -> ::std::cmp::Ordering { - self.into_repr().cmp(&other.into_repr()) + fn cmp(&self, other: &#name) -> ::core::cmp::Ordering { + let mut a = *self; + a.mont_reduce( + #mont_reduce_self_params + ); + + let mut b = *other; + b.mont_reduce( + #mont_reduce_other_params + ); + + a.cmp_native(&b) } } impl PartialOrd for #name { #[inline(always)] - fn partial_cmp(&self, other: &#name) -> Option<::std::cmp::Ordering> { + fn partial_cmp(&self, other: &#name) -> Option<::core::cmp::Ordering> { Some(self.cmp(other)) } } - impl ::std::fmt::Display for #name { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}({})", stringify!(#name), self.into_repr()) + impl ::core::fmt::Display for #name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{}({})", stringify!(#name), self.to_repr()) + } + } + + impl From for #name { + #[inline(always)] + fn from(val: u64) -> #name { + let mut raw = [0u64; #limbs]; + raw[0] = val; + #name(raw) * R2 } } impl From<#name> for #repr { fn from(e: #name) -> #repr { - e.into_repr() + e.to_repr() + } + } + + impl<'a> From<&'a #name> for #repr { + fn from(e: &'a #name) -> #repr { + e.to_repr() + } + } + + impl ::subtle::ConditionallySelectable for #name { + fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name { + let mut res = [0u64; #limbs]; + for i in 0..#limbs { + res[i] = u64::conditional_select(&a.0[i], &b.0[i], choice); + } + #name(res) + } + } + + impl ::core::ops::Neg for #name { + type Output = #name; + + #[inline] + fn neg(self) -> #name { + let mut ret = self; + if !ret.is_zero() { + let mut tmp = MODULUS_LIMBS; + tmp.sub_noborrow(&ret); + ret = tmp; + } + ret + } + } + + impl<'r> ::core::ops::Add<&'r #name> for #name { + type Output = #name; + + #[inline] + fn add(self, other: &#name) -> #name { + let mut ret = self; + ret.add_assign(other); + ret + } + } + + impl ::core::ops::Add for #name { + type Output = #name; + + #[inline] + fn add(self, other: #name) -> Self { + self + &other + } + } + + impl<'r> ::core::ops::AddAssign<&'r #name> for #name { + #[inline] + fn add_assign(&mut self, other: &#name) { + // This cannot exceed the backing capacity. + self.add_nocarry(other); + + // However, it may need to be reduced. + self.reduce(); + } + } + + impl ::core::ops::AddAssign for #name { + #[inline] + fn add_assign(&mut self, other: #name) { + self.add_assign(&other); + } + } + + impl<'r> ::core::ops::Sub<&'r #name> for #name { + type Output = #name; + + #[inline] + fn sub(self, other: &#name) -> Self { + let mut ret = self; + ret.sub_assign(other); + ret + } + } + + impl ::core::ops::Sub for #name { + type Output = #name; + + #[inline] + fn sub(self, other: #name) -> Self { + self - &other + } + } + + impl<'r> ::core::ops::SubAssign<&'r #name> for #name { + #[inline] + fn sub_assign(&mut self, other: &#name) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.cmp_native(self) == ::core::cmp::Ordering::Greater { + self.add_nocarry(&MODULUS_LIMBS); + } + + self.sub_noborrow(other); + } + } + + impl ::core::ops::SubAssign for #name { + #[inline] + fn sub_assign(&mut self, other: #name) { + self.sub_assign(&other); + } + } + + impl<'r> ::core::ops::Mul<&'r #name> for #name { + type Output = #name; + + #[inline] + fn mul(self, other: &#name) -> Self { + let mut ret = self; + ret.mul_assign(other); + ret + } + } + + impl ::core::ops::Mul for #name { + type Output = #name; + + #[inline] + fn mul(self, other: #name) -> Self { + self * &other + } + } + + impl<'r> ::core::ops::MulAssign<&'r #name> for #name { + #[inline] + fn mul_assign(&mut self, other: &#name) + { + #multiply_impl + } + } + + impl ::core::ops::MulAssign for #name { + #[inline] + fn mul_assign(&mut self, other: #name) + { + self.mul_assign(&other); } } impl ::ff::PrimeField for #name { type Repr = #repr; + type ReprEndianness = #repr_endianness; - fn from_repr(r: #repr) -> Result<#name, PrimeFieldDecodingError> { - let mut r = #name(r); - if r.is_valid() { - r.mul_assign(&#name(R2)); - - Ok(r) - } else { - Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) - } + fn from_repr(r: #repr) -> Option<#name> { + #from_repr_impl } - fn into_repr(&self) -> #repr { + fn to_repr(&self) -> #repr { + #to_repr_impl + } + + #[inline(always)] + fn is_odd(&self) -> bool { let mut r = *self; r.mont_reduce( - #into_repr_params + #mont_reduce_self_params ); - r.0 + r.0[0] & 1 == 1 } - fn char() -> #repr { + fn char() -> Self::Repr { MODULUS } @@ -871,26 +1176,26 @@ fn prime_field_impl( const CAPACITY: u32 = Self::NUM_BITS - 1; fn multiplicative_generator() -> Self { - #name(GENERATOR) + GENERATOR } const S: u32 = S; fn root_of_unity() -> Self { - #name(ROOT_OF_UNITY) + ROOT_OF_UNITY } } impl ::ff::Field for #name { /// Computes a uniformly random element using rejection sampling. - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let mut tmp = { let mut repr = [0u64; #limbs]; for i in 0..#limbs { repr[i] = rng.next_u64(); } - #name(#repr(repr)) + #name(repr) }; // Mask away the unused most-significant bits. @@ -904,135 +1209,94 @@ fn prime_field_impl( #[inline] fn zero() -> Self { - #name(#repr::from(0)) + #name([0; #limbs]) } #[inline] fn one() -> Self { - #name(R) + R } #[inline] fn is_zero(&self) -> bool { - self.0.is_zero() + self.0.iter().all(|&e| e == 0) } #[inline] - fn add_assign(&mut self, other: &#name) { + fn double(&self) -> Self { + let mut ret = *self; + // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); + let mut last = 0; + for i in &mut ret.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } // However, it may need to be reduced. - self.reduce(); + ret.reduce(); + + ret + } + + fn invert(&self) -> ::subtle::CtOption { + #invert_impl } #[inline] - fn double(&mut self) { - // This cannot exceed the backing capacity. - self.0.mul2(); - - // However, it may need to be reduced. - self.reduce(); - } - - #[inline] - fn sub_assign(&mut self, other: &#name) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); - } - - self.0.sub_noborrow(&other.0); - } - - #[inline] - fn negate(&mut self) { - if !self.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&self.0); - self.0 = tmp; - } - } - - fn inverse(&self) -> Option { - if self.is_zero() { - None - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - let one = #repr::from(1); - - let mut u = self.0; - let mut v = MODULUS; - let mut b = #name(R2); // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != one && v != one { - while u.is_even() { - u.div2(); - - if b.0.is_even() { - b.0.div2(); - } else { - b.0.add_nocarry(&MODULUS); - b.0.div2(); - } - } - - while v.is_even() { - v.div2(); - - if c.0.is_even() { - c.0.div2(); - } else { - c.0.add_nocarry(&MODULUS); - c.0.div2(); - } - } - - if v < u { - u.sub_noborrow(&v); - b.sub_assign(&c); - } else { - v.sub_noborrow(&u); - c.sub_assign(&b); - } - } - - if u == one { - Some(b) - } else { - Some(c) - } - } - } - - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - - #[inline] - fn mul_assign(&mut self, other: &#name) - { - #multiply_impl - } - - #[inline] - fn square(&mut self) + fn square(&self) -> Self { #squaring_impl } + + fn sqrt(&self) -> ::subtle::CtOption { + #sqrt_impl + } } impl #name { + /// Compares two elements in native representation. This is only used + /// internally. + #[inline(always)] + fn cmp_native(&self, other: &#name) -> ::core::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::core::cmp::Ordering::Less + } else if a > b { + return ::core::cmp::Ordering::Greater + } + } + + ::core::cmp::Ordering::Equal + } + /// Determines if the element is really in the field. This is only used /// internally. #[inline(always)] fn is_valid(&self) -> bool { - self.0 < MODULUS + // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use + // this internal function to eliminate the cycle. + self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &#name) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::ff::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &#name) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::ff::sbb(*a, *b, &mut borrow); + } } /// Subtracts the modulus from this element if this element is not in the @@ -1040,7 +1304,7 @@ fn prime_field_impl( #[inline(always)] fn reduce(&mut self) { if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); + self.sub_noborrow(&MODULUS_LIMBS); } } diff --git a/ff/ff_derive/src/pow_fixed.rs b/ff/ff_derive/src/pow_fixed.rs new file mode 100644 index 0000000..1d2b37a --- /dev/null +++ b/ff/ff_derive/src/pow_fixed.rs @@ -0,0 +1,56 @@ +//! Fixed-exponent variable-base exponentiation using addition chains. + +use addchain::{build_addition_chain, Step}; +use num_bigint::BigUint; +use quote::quote; +use syn::Ident; + +/// Returns t{n} as an ident. +fn get_temp(n: usize) -> Ident { + Ident::new(&format!("t{}", n), proc_macro2::Span::call_site()) +} + +pub(crate) fn generate( + base: &proc_macro2::TokenStream, + exponent: BigUint, +) -> proc_macro2::TokenStream { + let steps = build_addition_chain(exponent); + + let mut gen = proc_macro2::TokenStream::new(); + + // First entry in chain is one, i.e. the base. + let start = get_temp(0); + gen.extend(quote! { + let #start = #base; + }); + + let mut tmps = vec![start]; + for (i, step) in steps.into_iter().enumerate() { + let out = get_temp(i + 1); + + gen.extend(match step { + Step::Double { index } => { + let val = &tmps[index]; + quote! { + let #out = #val.square(); + } + } + Step::Add { left, right } => { + let left = &tmps[left]; + let right = &tmps[right]; + quote! { + let #out = #left * #right; + } + } + }); + + tmps.push(out.clone()); + } + + let end = tmps.last().expect("have last"); + gen.extend(quote! { + #end + }); + + gen +} diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 482dc46..16e0bec 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -1,26 +1,56 @@ +//! This crate provides traits for working with finite fields. + +// Catch documentation errors caused by code changes. +#![no_std] +#![deny(intra_doc_link_resolution_failure)] #![allow(unused_imports)] -extern crate byteorder; -extern crate rand_core; - -#[cfg(feature = "derive")] +#[cfg(feature = "std")] #[macro_use] -extern crate ff_derive; +extern crate std; #[cfg(feature = "derive")] pub use ff_derive::*; +use byteorder::ByteOrder; +use core::convert::TryFrom; +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Add, AddAssign, BitAnd, Mul, MulAssign, Neg, Shr, Sub, SubAssign}; use rand_core::RngCore; -use std::error::Error; -use std::fmt; +#[cfg(feature = "std")] use std::io::{self, Read, Write}; +use subtle::{ConditionallySelectable, CtOption}; /// This trait represents an element of a field. pub trait Field: - Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + Sized + + Eq + + Copy + + Clone + + Default + + Send + + Sync + + fmt::Debug + + fmt::Display + + 'static + + ConditionallySelectable + + Add + + Sub + + Mul + + Neg + + for<'a> Add<&'a Self, Output = Self> + + for<'a> Mul<&'a Self, Output = Self> + + for<'a> Sub<&'a Self, Output = Self> + + MulAssign + + AddAssign + + SubAssign + + for<'a> MulAssign<&'a Self> + + for<'a> AddAssign<&'a Self> + + for<'a> SubAssign<&'a Self> { /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self; /// Returns the zero element of the field, the additive identity. fn zero() -> Self; @@ -32,46 +62,35 @@ pub trait Field: fn is_zero(&self) -> bool; /// Squares this element. - fn square(&mut self); + #[must_use] + fn square(&self) -> Self; /// Doubles this element. - fn double(&mut self); + #[must_use] + fn double(&self) -> Self; - /// Negates this element. - fn negate(&mut self); + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + fn invert(&self) -> CtOption; - /// Adds another element to this element. - fn add_assign(&mut self, other: &Self); + /// Returns the square root of the field element, if it is + /// quadratic residue. + fn sqrt(&self) -> CtOption; - /// Subtracts another element from this element. - fn sub_assign(&mut self, other: &Self); - - /// Multiplies another element by this element. - fn mul_assign(&mut self, other: &Self); - - /// Computes the multiplicative inverse of this element, if nonzero. - fn inverse(&self) -> Option; - - /// Exponentiates this element by a power of the base prime modulus via - /// the Frobenius automorphism. - fn frobenius_map(&mut self, power: usize); - - /// Exponentiates this element by a number represented with `u64` limbs, - /// least significant digit first. - fn pow>(&self, exp: S) -> Self { + /// Exponentiates `self` by `exp`, where `exp` is a little-endian order + /// integer exponent. + /// + /// **This operation is variable time with respect to the exponent.** If the + /// exponent is fixed, this operation is effectively constant time. + fn pow_vartime>(&self, exp: S) -> Self { let mut res = Self::one(); + for e in exp.as_ref().iter().rev() { + for i in (0..64).rev() { + res = res.square(); - let mut found_one = false; - - for i in BitIterator::new(exp) { - if found_one { - res.square(); - } else { - found_one = i; - } - - if i { - res.mul_assign(self); + if ((*e >> i) & 1) == 1 { + res.mul_assign(self); + } } } @@ -79,151 +98,35 @@ pub trait Field: } } -/// This trait represents an element of a field that has a square root operation described for it. -pub trait SqrtField: Field { - /// Returns the Legendre symbol of the field element. - fn legendre(&self) -> LegendreSymbol; - - /// Returns the square root of the field element, if it is - /// quadratic residue. - fn sqrt(&self) -> Option; +/// Helper trait for converting the binary representation of a prime field element into a +/// specific endianness. This is useful when you need to act on the bit representation +/// of an element generically, as the native binary representation of a prime field is +/// field-dependent. +pub trait Endianness: ByteOrder { + /// Converts the provided representation between native and little-endian. + fn toggle_little_endian>(t: &mut T); } -/// This trait represents a wrapper around a biginteger which can encode any element of a particular -/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit -/// first. -pub trait PrimeFieldRepr: - Sized - + Copy - + Clone - + Eq - + Ord - + Send - + Sync - + Default - + fmt::Debug - + fmt::Display - + 'static - + AsRef<[u64]> - + AsMut<[u64]> - + From -{ - /// Subtract another represetation from this one. - fn sub_noborrow(&mut self, other: &Self); - - /// Add another representation to this one. - fn add_nocarry(&mut self, other: &Self); - - /// Compute the number of bits needed to encode this number. Always a - /// multiple of 64. - fn num_bits(&self) -> u32; - - /// Returns true iff this number is zero. - fn is_zero(&self) -> bool; - - /// Returns true iff this number is odd. - fn is_odd(&self) -> bool; - - /// Returns true iff this number is even. - fn is_even(&self) -> bool; - - /// Performs a rightwise bitshift of this number, effectively dividing - /// it by 2. - fn div2(&mut self); - - /// Performs a rightwise bitshift of this number by some amount. - fn shr(&mut self, amt: u32); - - /// Performs a leftwise bitshift of this number, effectively multiplying - /// it by 2. Overflow is ignored. - fn mul2(&mut self); - - /// Performs a leftwise bitshift of this number by some amount. - fn shl(&mut self, amt: u32); - - /// Writes this `PrimeFieldRepr` as a big endian integer. - fn write_be(&self, mut writer: W) -> io::Result<()> { - use byteorder::{BigEndian, WriteBytesExt}; - - for digit in self.as_ref().iter().rev() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a big endian integer into this representation. - fn read_be(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{BigEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut().rev() { - *digit = reader.read_u64::()?; - } - - Ok(()) - } - - /// Writes this `PrimeFieldRepr` as a little endian integer. - fn write_le(&self, mut writer: W) -> io::Result<()> { - use byteorder::{LittleEndian, WriteBytesExt}; - - for digit in self.as_ref().iter() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a little endian integer into this representation. - fn read_le(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{LittleEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut() { - *digit = reader.read_u64::()?; - } - - Ok(()) +impl Endianness for byteorder::BigEndian { + fn toggle_little_endian>(t: &mut T) { + t.as_mut().reverse(); } } -#[derive(Debug, PartialEq)] -pub enum LegendreSymbol { - Zero = 0, - QuadraticResidue = 1, - QuadraticNonResidue = -1, -} - -/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a -/// `PrimeField` element. -#[derive(Debug)] -pub enum PrimeFieldDecodingError { - /// The encoded value is not in the field - NotInField(String), -} - -impl Error for PrimeFieldDecodingError { - fn description(&self) -> &str { - match *self { - PrimeFieldDecodingError::NotInField(..) => "not an element of the field", - } - } -} - -impl fmt::Display for PrimeFieldDecodingError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - PrimeFieldDecodingError::NotInField(ref repr) => { - write!(f, "{} is not an element of the field", repr) - } - } +impl Endianness for byteorder::LittleEndian { + fn toggle_little_endian>(_: &mut T) { + // No-op } } /// This represents an element of a prime field. -pub trait PrimeField: Field { - /// The prime field can be converted back and forth into this biginteger +pub trait PrimeField: Field + From { + /// The prime field can be converted back and forth into this binary /// representation. - type Repr: PrimeFieldRepr + From; + type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + for<'r> From<&'r Self>; + + /// This indicates the endianness of [`PrimeField::Repr`]. + type ReprEndianness: Endianness; /// Interpret a string of numbers as a (congruent) prime field element. /// Does not accept unnecessary leading zeroes or a blank string. @@ -238,7 +141,7 @@ pub trait PrimeField: Field { let mut res = Self::zero(); - let ten = Self::from_repr(Self::Repr::from(10)).unwrap(); + let ten = Self::from(10); let mut first_digit = true; @@ -254,7 +157,7 @@ pub trait PrimeField: Field { } res.mul_assign(&ten); - res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap()); + res.add_assign(&Self::from(u64::from(c))); } None => { return None; @@ -265,12 +168,29 @@ pub trait PrimeField: Field { Some(res) } - /// Convert this prime field element into a biginteger representation. - fn from_repr(Self::Repr) -> Result; + /// Attempts to convert a byte representation of a field element into an element of + /// this prime field, failing if the input is not canonical (is not smaller than the + /// field's modulus). + /// + /// The byte representation is interpreted with the endianness defined by + /// [`PrimeField::ReprEndianness`]. + fn from_repr(_: Self::Repr) -> Option; - /// Convert a biginteger representation into a prime field element, if - /// the number is an element of the field. - fn into_repr(&self) -> Self::Repr; + /// Converts an element of the prime field into the standard byte representation for + /// this field. + /// + /// The endianness of the byte representation is defined by + /// [`PrimeField::ReprEndianness`]. + fn to_repr(&self) -> Self::Repr; + + /// Returns true iff this element is odd. + fn is_odd(&self) -> bool; + + /// Returns true iff this element is even. + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } /// Returns the field characteristic; the modulus. fn char() -> Self::Repr; @@ -298,24 +218,29 @@ pub trait PrimeField: Field { /// pairing-friendly curve) can be defined in a subtrait. pub trait ScalarEngine: Sized + 'static + Clone { /// This is the scalar field of the engine's groups. - type Fr: PrimeField + SqrtField; + type Fr: PrimeField; } #[derive(Debug)] -pub struct BitIterator { +pub struct BitIterator> { t: E, n: usize, + _limb: PhantomData, } -impl> BitIterator { +impl> BitIterator { pub fn new(t: E) -> Self { let n = t.as_ref().len() * 64; - BitIterator { t, n } + BitIterator { + t, + n, + _limb: PhantomData::default(), + } } } -impl> Iterator for BitIterator { +impl> Iterator for BitIterator { type Item = bool; fn next(&mut self) -> Option { @@ -331,9 +256,37 @@ impl> Iterator for BitIterator { } } +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 8; + + BitIterator { + t, + n, + _limb: PhantomData::default(), + } + } +} + +impl> Iterator for BitIterator { + type Item = bool; + + fn next(&mut self) -> Option { + if self.n == 0 { + None + } else { + self.n -= 1; + let part = self.n / 8; + let bit = self.n - (8 * part); + + Some(self.t.as_ref()[part] & (1 << bit) > 0) + } + } +} + #[test] fn test_bit_iterator() { - let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]); + let mut a = BitIterator::::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]); let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001"; for e in expected.chars() { @@ -344,11 +297,11 @@ fn test_bit_iterator() { let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001"; - let mut a = BitIterator::new([ - 0x429d5f3ac3a3b759, - 0xb10f4c66768b1c92, - 0x92368b6d16ecd3b4, - 0xa57ea85ae8775219, + let mut a = BitIterator::::new([ + 0x429d_5f3a_c3a3_b759, + 0xb10f_4c66_768b_1c92, + 0x9236_8b6d_16ec_d3b4, + 0xa57e_a85a_e877_5219, ]); for e in expected.chars() { diff --git a/group/Cargo.toml b/group/Cargo.toml index 7d2d531..b68c2fe 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -1,18 +1,24 @@ [package] name = "group" -version = "0.1.0" +version = "0.6.0" authors = [ "Sean Bowe ", "Jack Grigg ", ] +readme = "README.md" license = "MIT/Apache-2.0" description = "Elliptic curve group traits and utilities" documentation = "https://docs.rs/group/" homepage = "https://github.com/ebfull/group" repository = "https://github.com/ebfull/group" +edition = "2018" [dependencies] -ff = { path = "../ff" } +byteorder = { version = "1", default-features = false } +ff = { version = "0.6", path = "../ff" } rand = "0.7" rand_xorshift = "0.2" + +[badges] +maintenance = { status = "actively-developed" } diff --git a/group/README.md b/group/README.md index 7fadc0b..5c2398b 100644 --- a/group/README.md +++ b/group/README.md @@ -1,10 +1,13 @@ # group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # +`group` is a crate for working with groups over elliptic curves. + ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/group/src/lib.rs b/group/src/lib.rs index 448c5a3..a330d14 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -1,29 +1,58 @@ -extern crate ff; -extern crate rand; -extern crate rand_xorshift; +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] -use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; +use ff::{Field, PrimeField, ScalarEngine}; use rand::RngCore; use std::error::Error; use std::fmt; +use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; pub mod tests; mod wnaf; pub use self::wnaf::Wnaf; +/// A helper trait for types implementing group addition. +pub trait CurveOps: + Add + Sub + AddAssign + SubAssign +{ +} + +impl CurveOps for T where + T: Add + Sub + AddAssign + SubAssign +{ +} + +/// A helper trait for references implementing group addition. +pub trait CurveOpsOwned: for<'r> CurveOps<&'r Rhs, Output> {} +impl CurveOpsOwned for T where T: for<'r> CurveOps<&'r Rhs, Output> {} + /// Projective representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. pub trait CurveProjective: - PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + PartialEq + + Eq + + Sized + + Copy + + Clone + + Send + + Sync + + fmt::Debug + + fmt::Display + + 'static + + Neg + + CurveOps + + CurveOpsOwned + + CurveOps<::Affine> + + CurveOpsOwned<::Affine> { type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; + type Scalar: PrimeField; + type Base: Field; type Affine: CurveAffine; /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self; /// Returns the additive identity. fn zero() -> Self; @@ -45,22 +74,6 @@ pub trait CurveProjective: /// Doubles this element. fn double(&mut self); - /// Adds another element to this element. - fn add_assign(&mut self, other: &Self); - - /// Subtracts another element from this element. - fn sub_assign(&mut self, other: &Self) { - let mut tmp = *other; - tmp.negate(); - self.add_assign(&tmp); - } - - /// Adds an affine element to this element. - fn add_assign_mixed(&mut self, other: &Self::Affine); - - /// Negates this element. - fn negate(&mut self); - /// Performs scalar multiplication of this element. fn mul_assign::Repr>>(&mut self, other: S); @@ -69,7 +82,7 @@ pub trait CurveProjective: /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize; + fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> usize; /// Recommends a wNAF window size given the number of scalars you intend to multiply /// a base by. Always returns a number between 2 and 22, inclusive. @@ -79,11 +92,21 @@ pub trait CurveProjective: /// Affine representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. pub trait CurveAffine: - Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static + Copy + + Clone + + Sized + + Send + + Sync + + fmt::Debug + + fmt::Display + + PartialEq + + Eq + + 'static + + Neg { type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; + type Scalar: PrimeField; + type Base: Field; type Projective: CurveProjective; type Uncompressed: EncodedPoint; type Compressed: EncodedPoint; @@ -98,9 +121,6 @@ pub trait CurveAffine: /// additive identity. fn is_zero(&self) -> bool; - /// Negates this element. - fn negate(&mut self); - /// Performs scalar multiplication of this element with mixed addition. fn mul::Repr>>(&self, other: S) -> Self::Projective; @@ -158,7 +178,7 @@ pub enum GroupDecodingError { /// The element is not part of the r-order subgroup. NotInSubgroup, /// One of the coordinates could not be decoded - CoordinateDecodingError(&'static str, PrimeFieldDecodingError), + CoordinateDecodingError(&'static str), /// The compression mode of the encoded element was not as expected UnexpectedCompressionMode, /// The encoding contained bits that should not have been set @@ -180,10 +200,10 @@ impl Error for GroupDecodingError { } impl fmt::Display for GroupDecodingError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - GroupDecodingError::CoordinateDecodingError(description, ref err) => { - write!(f, "{} decoding error: {}", description, err) + GroupDecodingError::CoordinateDecodingError(description) => { + write!(f, "{} decoding error", description) } _ => write!(f, "{}", self.description()), } diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 2b58b6c..75fc46f 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -1,8 +1,9 @@ use ff::{Field, PrimeField}; use rand::SeedableRng; use rand_xorshift::XorShiftRng; +use std::ops::Neg; -use {CurveAffine, CurveProjective, EncodedPoint}; +use crate::{CurveAffine, CurveProjective, EncodedPoint}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ @@ -12,8 +13,7 @@ pub fn curve_tests() { // Negation edge case with zero. { - let mut z = G::zero(); - z.negate(); + let z = G::zero().neg(); assert!(z.is_zero()); } @@ -30,19 +30,19 @@ pub fn curve_tests() { let rcopy = r; r.add_assign(&G::zero()); assert_eq!(r, rcopy); - r.add_assign_mixed(&G::Affine::zero()); + r.add_assign(&G::Affine::zero()); assert_eq!(r, rcopy); let mut z = G::zero(); z.add_assign(&G::zero()); assert!(z.is_zero()); - z.add_assign_mixed(&G::Affine::zero()); + z.add_assign(&G::Affine::zero()); assert!(z.is_zero()); let mut z2 = z; z2.add_assign(&r); - z.add_assign_mixed(&r.into_affine()); + z.add_assign(&r.into_affine()); assert_eq!(z, z2); assert_eq!(z, r); @@ -67,11 +67,11 @@ pub fn curve_tests() { random_negation_tests::(); random_transformation_tests::(); random_wnaf_tests::(); - random_encoding_tests::(); + random_encoding_tests::(); } fn random_wnaf_tests() { - use wnaf::*; + use crate::wnaf::*; let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -85,12 +85,12 @@ fn random_wnaf_tests() { for w in 2..14 { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); wnaf_table(&mut table, g, w); - wnaf_form(&mut wnaf, s, w); + wnaf_form(&mut wnaf, s.to_repr(), w); let g2 = wnaf_exp(&table, &wnaf); assert_eq!(g1, g2); @@ -103,17 +103,17 @@ fn random_wnaf_tests() { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); let g2 = { let mut wnaf = Wnaf::new(); - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g3 = { let mut wnaf = Wnaf::new(); - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g4 = { let mut wnaf = Wnaf::new(); @@ -121,11 +121,11 @@ fn random_wnaf_tests() { only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g5 = { let mut wnaf = Wnaf::new(); - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); @@ -137,40 +137,40 @@ fn random_wnaf_tests() { { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g7 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g8 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } let mut shared = wnaf.base(g, 1).shared(); only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g9 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); @@ -199,8 +199,7 @@ fn random_negation_tests() { let r = G::random(&mut rng); let s = G::Scalar::random(&mut rng); - let mut sneg = s; - sneg.negate(); + let sneg = s.neg(); let mut t1 = r; t1.mul_assign(s); @@ -213,11 +212,10 @@ fn random_negation_tests() { assert!(t3.is_zero()); let mut t4 = t1; - t4.add_assign_mixed(&t2.into_affine()); + t4.add_assign(&t2.into_affine()); assert!(t4.is_zero()); - t1.negate(); - assert_eq!(t1, t2); + assert_eq!(t1.neg(), t2); } } @@ -244,7 +242,7 @@ fn random_doubling_tests() { tmp2.add_assign(&b); let mut tmp3 = a; - tmp3.add_assign_mixed(&b.into_affine()); + tmp3.add_assign(&b.into_affine()); assert_eq!(tmp1, tmp2); assert_eq!(tmp1, tmp3); @@ -306,7 +304,7 @@ fn random_addition_tests() { aplusa.add_assign(&a); let mut aplusamixed = a; - aplusamixed.add_assign_mixed(&a.into_affine()); + aplusamixed.add_assign(&a.into_affine()); let mut adouble = a; adouble.double(); @@ -336,18 +334,18 @@ fn random_addition_tests() { // (a + b) + c tmp[3] = a_affine.into_projective(); - tmp[3].add_assign_mixed(&b_affine); - tmp[3].add_assign_mixed(&c_affine); + tmp[3].add_assign(&b_affine); + tmp[3].add_assign(&c_affine); // a + (b + c) tmp[4] = b_affine.into_projective(); - tmp[4].add_assign_mixed(&c_affine); - tmp[4].add_assign_mixed(&a_affine); + tmp[4].add_assign(&c_affine); + tmp[4].add_assign(&a_affine); // (a + c) + b tmp[5] = a_affine.into_projective(); - tmp[5].add_assign_mixed(&c_affine); - tmp[5].add_assign_mixed(&b_affine); + tmp[5].add_assign(&c_affine); + tmp[5].add_assign(&b_affine); // Comparisons for i in 0..6 { @@ -413,24 +411,24 @@ fn random_transformation_tests() { } } -fn random_encoding_tests() { +fn random_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); assert_eq!( - G::zero().into_uncompressed().into_affine().unwrap(), - G::zero() + G::Affine::zero().into_uncompressed().into_affine().unwrap(), + G::Affine::zero() ); assert_eq!( - G::zero().into_compressed().into_affine().unwrap(), - G::zero() + G::Affine::zero().into_compressed().into_affine().unwrap(), + G::Affine::zero() ); for _ in 0..1000 { - let mut r = G::Projective::random(&mut rng).into_affine(); + let mut r = G::random(&mut rng).into_affine(); let uncompressed = r.into_uncompressed(); let de_uncompressed = uncompressed.into_affine().unwrap(); @@ -440,7 +438,7 @@ fn random_encoding_tests() { let de_compressed = compressed.into_affine().unwrap(); assert_eq!(de_compressed, r); - r.negate(); + r = r.neg(); let compressed = r.into_compressed(); let de_compressed = compressed.into_affine().unwrap(); diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 381cd10..57f780d 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -1,4 +1,6 @@ -use ff::{PrimeField, PrimeFieldRepr}; +use byteorder::{ByteOrder, LittleEndian}; +use ff::PrimeField; +use std::iter; use super::CurveProjective; @@ -16,31 +18,60 @@ pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, wi } } -/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. -pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { +/// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian +/// scalar. +pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { + // Required by the NAF definition + debug_assert!(window >= 2); + // Required so that the NAF digits fit in i64 + debug_assert!(window <= 64); + wnaf.truncate(0); - while !c.is_zero() { - let mut u; - if c.is_odd() { - u = (c.as_ref()[0] % (1 << (window + 1))) as i64; + let bit_len = c.as_ref().len() * 8; + let u64_len = (bit_len + 1) / 64; - if u > (1 << window) { - u -= 1 << (window + 1); - } + let mut c_u64 = vec![0u64; u64_len + 1]; + LittleEndian::read_u64_into(c.as_ref(), &mut c_u64[0..u64_len]); - if u > 0 { - c.sub_noborrow(&S::from(u as u64)); - } else { - c.add_nocarry(&S::from((-u) as u64)); - } + let width = 1u64 << window; + let window_mask = width - 1; + + let mut pos = 0; + let mut carry = 0; + while pos < bit_len { + // Construct a buffer of bits of the scalar, starting at bit `pos` + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let bit_buf = if bit_idx + window < 64 { + // This window's bits are contained in a single u64 + c_u64[u64_idx] >> bit_idx } else { - u = 0; + // Combine the current u64's bits with the bits from the next u64 + (c_u64[u64_idx] >> bit_idx) | (c_u64[u64_idx + 1] << (64 - bit_idx)) + }; + + // Add the carry into the current window + let window_val = carry + (bit_buf & window_mask); + + if window_val & 1 == 0 { + // If the window value is even, preserve the carry and emit 0. + // Why is the carry preserved? + // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 + // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 + wnaf.push(0); + pos += 1; + } else { + wnaf.push(if window_val < width / 2 { + carry = 0; + window_val as i64 + } else { + carry = 1; + (window_val as i64).wrapping_sub(width as i64) + }); + wnaf.extend(iter::repeat(0).take(window - 1)); + pos += window; } - - wnaf.push(u); - - c.div2(); } } @@ -112,13 +143,13 @@ impl Wnaf<(), Vec, Vec> { /// exponentiations with `.base(..)`. pub fn scalar( &mut self, - scalar: <::Scalar as PrimeField>::Repr, + scalar: &::Scalar, ) -> Wnaf, &[i64]> { // Compute the appropriate window size for the scalar. - let window_size = G::recommended_wnaf_for_scalar(scalar); + let window_size = G::recommended_wnaf_for_scalar(&scalar); // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar, window_size); + wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); // Return a Wnaf object that mutably borrows the base storage location, but // immutably borrows the computed wNAF form scalar location. @@ -168,14 +199,11 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar( - &mut self, - scalar: <::Scalar as PrimeField>::Repr, - ) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } } diff --git a/jubjub/.github/workflows/ci.yml b/jubjub/.github/workflows/ci.yml new file mode 100644 index 0000000..5d0efb3 --- /dev/null +++ b/jubjub/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI checks + +on: [push, pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + + # Ensure all code has been formatted with rustfmt + - run: rustup component add rustfmt + - name: Check formatting + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -- --check --color always + + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build tests + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --tests + - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release + + no-std: + name: Check no-std compatibility + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.36.0 + override: true + - run: rustup target add thumbv6m-none-eabi + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --target thumbv6m-none-eabi --no-default-features + + doc-links: + name: Nightly lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + + # Ensure intra-documentation links all resolve correctly + # Requires #![deny(intra_doc_link_resolution_failure)] in crate. + - name: Check intra-doc links + uses: actions-rs/cargo@v1 + with: + command: doc + args: --document-private-items diff --git a/jubjub/.gitignore b/jubjub/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/jubjub/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/jubjub/COPYRIGHT b/jubjub/COPYRIGHT new file mode 100644 index 0000000..aaca1cc --- /dev/null +++ b/jubjub/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "jubjub" library are retained by their contributors. No +copyright assignment is required to contribute to the "jubjub" library. + +The "jubjub" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml new file mode 100644 index 0000000..31221ef --- /dev/null +++ b/jubjub/Cargo.toml @@ -0,0 +1,49 @@ +[package] +authors = [ + "Sean Bowe ", + "Eirik Ogilvie-Wigley ", + "Jack Grigg ", +] +description = "Implementation of the Jubjub elliptic curve group" +documentation = "https://docs.rs/jubjub/" +homepage = "https://github.com/zkcrypto/jubjub" +license = "MIT/Apache-2.0" +name = "jubjub" +repository = "https://github.com/zkcrypto/jubjub" +version = "0.3.0" +edition = "2018" + +[dependencies.bls12_381] +path = "../bls12_381" +version = "0.1" +default-features = false + +[dependencies.subtle] +version = "^2.2.1" +default-features = false + +[dev-dependencies] +criterion = "0.3" + +[dev-dependencies.rand_core] +version = "0.5" +default-features = false + +[dev-dependencies.rand_xorshift] +version = "0.2" +default-features = false + +[features] +default = [] + +[[bench]] +name = "fq_bench" +harness = false + +[[bench]] +name = "fr_bench" +harness = false + +[[bench]] +name = "point_bench" +harness = false diff --git a/jubjub/LICENSE-APACHE b/jubjub/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/jubjub/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/jubjub/LICENSE-MIT b/jubjub/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/jubjub/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/jubjub/README.md b/jubjub/README.md new file mode 100644 index 0000000..da5bd53 --- /dev/null +++ b/jubjub/README.md @@ -0,0 +1,53 @@ +# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) # + + + +This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields. + +* **This implementation has not been reviewed or audited. Use at your own risk.** +* This implementation targets Rust `1.36` or later. +* All operations are constant time unless explicitly noted. +* This implementation does not require the Rust standard library. + +## [Documentation](https://docs.rs/jubjub) + +## Curve Description + +Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`. + +``` +q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 +d = -(10240/10241) +``` + +The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction. + +Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519. + +Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity. + +## Acknowledgements + +Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image. + +Please see `Cargo.toml` for a list of primary authors of this codebase. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/jubjub/RELEASES.md b/jubjub/RELEASES.md new file mode 100644 index 0000000..45db61c --- /dev/null +++ b/jubjub/RELEASES.md @@ -0,0 +1,24 @@ +# 0.3.0 + +This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export. + +* The `Fq` and `Fr` field types now have better constant function support for various operations and constructors. +* We no longer depend on the `byteorder` crate. +* We've bumped our `rand_core` dev-dependency up to 0.5. +* We've removed the `std` and `nightly` features. +* We've bumped our dependency of `subtle` up to `^2.2.1`. + +# 0.2.0 + +This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes. + +* Implemented `Mul` for `AffineNielsPoint` and `ExtendedNielsPoint` +* Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics. +* Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint` +* Removed `CtOption` and replaced it with `CtOption` from `subtle` crate. +* Modified receivers of some methods to reduce stack usage +* Changed various `into_bytes` methods into `to_bytes` + +# 0.1.0 + +Initial release. diff --git a/jubjub/benches/fq_bench.rs b/jubjub/benches/fq_bench.rs new file mode 100644 index 0000000..65eceaf --- /dev/null +++ b/jubjub/benches/fq_bench.rs @@ -0,0 +1,58 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use jubjub::*; + +fn bench_add_assign(c: &mut Criterion) { + let mut n = Fq::one(); + let neg_one = -Fq::one(); + c.bench_function("Fq add_assign", |b| { + b.iter(move || { + n += &neg_one; + }) + }); +} + +fn bench_sub_assign(c: &mut Criterion) { + let mut n = Fq::one(); + let neg_one = -Fq::one(); + c.bench_function("Fq sub_assign", |b| { + b.iter(move || { + n -= &neg_one; + }) + }); +} + +fn bench_mul_assign(c: &mut Criterion) { + let mut n = Fq::one(); + let neg_one = -Fq::one(); + c.bench_function("Fq mul_assign", |b| { + b.iter(move || { + n *= &neg_one; + }) + }); +} + +fn bench_square(c: &mut Criterion) { + let n = Fq::one(); + c.bench_function("Fq square", |b| b.iter(move || n.square())); +} + +fn bench_invert(c: &mut Criterion) { + let n = Fq::one(); + c.bench_function("Fq invert", |b| b.iter(move || n.invert())); +} + +fn bench_sqrt(c: &mut Criterion) { + let n = Fq::one().double().double(); + c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt())); +} + +criterion_group!( + benches, + bench_add_assign, + bench_sub_assign, + bench_mul_assign, + bench_square, + bench_invert, + bench_sqrt, +); +criterion_main!(benches); diff --git a/jubjub/benches/fr_bench.rs b/jubjub/benches/fr_bench.rs new file mode 100644 index 0000000..8dc9ce2 --- /dev/null +++ b/jubjub/benches/fr_bench.rs @@ -0,0 +1,58 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use jubjub::*; + +fn bench_add_assign(c: &mut Criterion) { + let mut n = Fr::one(); + let neg_one = -Fr::one(); + c.bench_function("Fr add_assign", |b| { + b.iter(move || { + n += &neg_one; + }) + }); +} + +fn bench_sub_assign(c: &mut Criterion) { + let mut n = Fr::one(); + let neg_one = -Fr::one(); + c.bench_function("Fr sub_assign", |b| { + b.iter(move || { + n -= &neg_one; + }) + }); +} + +fn bench_mul_assign(c: &mut Criterion) { + let mut n = Fr::one(); + let neg_one = -Fr::one(); + c.bench_function("Fr mul_assign", |b| { + b.iter(move || { + n *= &neg_one; + }) + }); +} + +fn bench_square(c: &mut Criterion) { + let n = Fr::one(); + c.bench_function("Fr square", |b| b.iter(move || n.square())); +} + +fn bench_invert(c: &mut Criterion) { + let n = Fr::one(); + c.bench_function("Fr invert", |b| b.iter(move || n.invert())); +} + +fn bench_sqrt(c: &mut Criterion) { + let n = Fr::one().double().double(); + c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt())); +} + +criterion_group!( + benches, + bench_add_assign, + bench_sub_assign, + bench_mul_assign, + bench_square, + bench_invert, + bench_sqrt, +); +criterion_main!(benches); diff --git a/jubjub/benches/point_bench.rs b/jubjub/benches/point_bench.rs new file mode 100644 index 0000000..1659ea5 --- /dev/null +++ b/jubjub/benches/point_bench.rs @@ -0,0 +1,73 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use jubjub::*; + +// Non-Niels + +fn bench_point_doubling(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + c.bench_function("Jubjub point doubling", |bencher| { + bencher.iter(move || a.double()) + }); +} + +fn bench_point_addition(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = -ExtendedPoint::identity(); + c.bench_function("Jubjub point addition", |bencher| { + bencher.iter(move || a + b) + }); +} + +fn bench_point_subtraction(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = -ExtendedPoint::identity(); + c.bench_function("Jubjub point subtraction", |bencher| { + bencher.iter(move || a + b) + }); +} + +// Niels + +fn bench_cached_point_addition(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = ExtendedPoint::identity().to_niels(); + c.bench_function("Jubjub cached point addition", |bencher| { + bencher.iter(move || a + b) + }); +} + +fn bench_cached_point_subtraction(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = ExtendedPoint::identity().to_niels(); + c.bench_function("Jubjub cached point subtraction", |bencher| { + bencher.iter(move || a + b) + }); +} + +fn bench_cached_affine_point_addition(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = AffinePoint::identity().to_niels(); + c.bench_function("Jubjub cached affine point addition", |bencher| { + bencher.iter(move || a + b) + }); +} + +fn bench_cached_affine_point_subtraction(c: &mut Criterion) { + let a = ExtendedPoint::identity(); + let b = AffinePoint::identity().to_niels(); + c.bench_function("Jubjub cached affine point subtraction", |bencher| { + bencher.iter(move || a + b) + }); +} + +criterion_group!( + benches, + bench_point_doubling, + bench_point_addition, + bench_point_subtraction, + bench_cached_point_addition, + bench_cached_point_subtraction, + bench_cached_affine_point_addition, + bench_cached_affine_point_subtraction, +); +criterion_main!(benches); diff --git a/jubjub/doc/derive/.gitignore b/jubjub/doc/derive/.gitignore new file mode 100644 index 0000000..7c974cf --- /dev/null +++ b/jubjub/doc/derive/.gitignore @@ -0,0 +1 @@ +*.sage.py diff --git a/jubjub/doc/derive/derive.sage b/jubjub/doc/derive/derive.sage new file mode 100644 index 0000000..c0c5310 --- /dev/null +++ b/jubjub/doc/derive/derive.sage @@ -0,0 +1,32 @@ +q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +Fq = GF(q) + +# We wish to find a Montgomery curve with B = 1 and A the smallest such +# that (A - 2) / 4 is a small integer. +def get_A(n): + return (n * 4) + 2 + +# A = 2 is invalid (singular curve), so we start at i = 1 (A = 6) +i = 1 + +while True: + A = Fq(get_A(i)) + i = i + 1 + + # We also want that A^2 - 4 is nonsquare. + if ((A^2) - 4).is_square(): + continue + + ec = EllipticCurve(Fq, [0, A, 0, 1, 0]) + o = ec.order() + + if (o % 8 == 0): + o = o // 8 + if is_prime(o): + twist = ec.quadratic_twist() + otwist = twist.order() + if (otwist % 4 == 0): + otwist = otwist // 4 + if is_prime(otwist): + print "A = %s" % A + exit(0) diff --git a/jubjub/doc/evidence/.gitignore b/jubjub/doc/evidence/.gitignore new file mode 100644 index 0000000..9a0d287 --- /dev/null +++ b/jubjub/doc/evidence/.gitignore @@ -0,0 +1,102 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + diff --git a/jubjub/doc/evidence/LICENSE b/jubjub/doc/evidence/LICENSE new file mode 100644 index 0000000..9e18163 --- /dev/null +++ b/jubjub/doc/evidence/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 The Zcash developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/jubjub/doc/evidence/README.md b/jubjub/doc/evidence/README.md new file mode 100644 index 0000000..26b2e36 --- /dev/null +++ b/jubjub/doc/evidence/README.md @@ -0,0 +1,28 @@ +Jubjub supporting evidence +-------------------------- + +This repository contains supporting evidence that the twisted Edwards curve +-x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over +GF(52435875175126190479447740508185965837690552500527637822603658699938581184513), +[also called "Jubjub"](https://z.cash/technology/jubjub.html), +satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html). + +The script ``verify.sage`` is based on +[this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html), +modified + +* to support twisted Edwards curves; +* to generate a file 'primes' containing the primes needed for primality proofs, + if it is not already present; +* to change the directory in which Pocklington proof files are generated + (``proof/`` rather than ``../../../proof``), and to create that directory + if it does not exist. + +Prerequisites: + +* apt-get install sagemath +* pip install sortedcontainers + +Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results. + +Note that the "rigidity" criterion cannot be checked automatically. diff --git a/jubjub/doc/evidence/a b/jubjub/doc/evidence/a new file mode 100644 index 0000000..3a2e3f4 --- /dev/null +++ b/jubjub/doc/evidence/a @@ -0,0 +1 @@ +-1 diff --git a/jubjub/doc/evidence/d b/jubjub/doc/evidence/d new file mode 100644 index 0000000..767309a --- /dev/null +++ b/jubjub/doc/evidence/d @@ -0,0 +1 @@ +19257038036680949359750312669786877991949435402254120286184196891950884077233 diff --git a/jubjub/doc/evidence/l b/jubjub/doc/evidence/l new file mode 100644 index 0000000..83f92d5 --- /dev/null +++ b/jubjub/doc/evidence/l @@ -0,0 +1 @@ +6554484396890773809930967563523245729705921265872317281365359162392183254199 diff --git a/jubjub/doc/evidence/p b/jubjub/doc/evidence/p new file mode 100644 index 0000000..1dc0557 --- /dev/null +++ b/jubjub/doc/evidence/p @@ -0,0 +1 @@ +52435875175126190479447740508185965837690552500527637822603658699938581184513 diff --git a/jubjub/doc/evidence/rigid b/jubjub/doc/evidence/rigid new file mode 100644 index 0000000..e560e40 --- /dev/null +++ b/jubjub/doc/evidence/rigid @@ -0,0 +1 @@ +fully rigid diff --git a/jubjub/doc/evidence/run.sh b/jubjub/doc/evidence/run.sh new file mode 100644 index 0000000..817f2fa --- /dev/null +++ b/jubjub/doc/evidence/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh +sage verify.sage . +grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' + diff --git a/jubjub/doc/evidence/shape b/jubjub/doc/evidence/shape new file mode 100644 index 0000000..796f74d --- /dev/null +++ b/jubjub/doc/evidence/shape @@ -0,0 +1 @@ +tedwards diff --git a/jubjub/doc/evidence/verify.sage b/jubjub/doc/evidence/verify.sage new file mode 100644 index 0000000..1717c0b --- /dev/null +++ b/jubjub/doc/evidence/verify.sage @@ -0,0 +1,444 @@ +import os +import sys +from errno import ENOENT, EEXIST +from sortedcontainers import SortedSet + + +def readfile(fn): + fd = open(fn,'r') + r = fd.read() + fd.close() + return r + +def writefile(fn,s): + fd = open(fn,'w') + fd.write(s) + fd.close() + +def expand2(n): + s = "" + + while n != 0: + j = 16 + while 2**j < abs(n): j += 1 + if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1 + + if abs(abs(n) - 2**j) > 2**(j - 10): + if n > 0: + if s != "": s += " + " + s += str(n) + else: + s += " - " + str(-n) + n = 0 + elif n > 0: + if s != "": s += " + " + s += "2^" + str(j) + n -= 2**j + else: + s += " - 2^" + str(j) + n += 2**j + + return s + +def requirement(fn,istrue): + writefile(fn,str(istrue) + '\n') + return istrue + +def verify(): + try: + os.mkdir('proof') + except OSError as e: + if e.errno != EEXIST: raise + + try: + s = set(map(Integer, readfile('primes').split())) + except IOError, e: + if e.errno != ENOENT: raise + s = set() + + needtofactor = SortedSet() + V = SortedSet() # distinct verified primes + verify_primes(V, s, needtofactor) + verify_pass(V, needtofactor) + + old = V + needtofactor.update(V) + while len(needtofactor) > len(old): + k = len(needtofactor) - len(old) + sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's')) + sys.stdout.flush() + for x in needtofactor: + if x not in old: + for (y, z) in factor(x): + s.add(y) + sys.stdout.write('.') + sys.stdout.flush() + + print('') + + old = needtofactor.copy() + verify_primes(V, s, needtofactor) + + writefile('primes', '\n'.join(map(str, s)) + '\n') + writefile('verify-primes', '\n' + + ''.join(('2\n' if v == 2 else + '%s\n' % (v,v)) for v in V) + + '\n') + + verify_pass(V, needtofactor) + + +def verify_primes(V, s, needtofactor): + for n in sorted(s): + if not n.is_prime() or n in V: continue + needtofactor.add(n-1) + if n == 2: + V.add(n) + continue + for trybase in primes(2,10000): + base = Integers(n)(trybase) + if base^(n-1) != 1: continue + proof = 'Primality proof for n = %s:\n' % n + proof += '

Take b = %s.\n' % base + proof += '

b^(n-1) mod n = 1.\n' + f = factor(1) + for v in reversed(V): + if f.prod()^2 <= n: + if n % v == 1: + u = base^((n-1)/v)-1 + if u.is_unit(): + if v == 2: + proof += '

2 is prime.\n' + else: + proof += '

%s is prime.\n' % (v,v) + proof += '
b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u) + f *= factor(v)^(n-1).valuation(v) + if f.prod()^2 <= n: continue + if n % f.prod() != 1: continue + proof += '

(%s) divides n-1.\n' % f + proof += '

(%s)^2 > n.\n' % f + proof += "

n is prime by Pocklington's theorem.\n" + proof += '\n' + writefile('proof/%s.html' % n,proof) + V.add(n) + break + + +def verify_pass(V, needtofactor): + p = Integer(readfile('p')) + k = GF(p) + kz. = k[] + l = Integer(readfile('l')) + x0 = Integer(readfile('x0')) + y0 = Integer(readfile('y0')) + x1 = Integer(readfile('x1')) + y1 = Integer(readfile('y1')) + shape = readfile('shape').strip() + rigid = readfile('rigid').strip() + + safefield = True + safeeq = True + safebase = True + saferho = True + safetransfer = True + safedisc = True + saferigid = True + safeladder = True + safetwist = True + safecomplete = True + safeind = True + + pstatus = 'Unverified' + if not p.is_prime(): pstatus = 'False' + needtofactor.add(p) + if p in V: pstatus = 'True' + if pstatus != 'True': safefield = False + writefile('verify-pisprime',pstatus + '\n') + + pstatus = 'Unverified' + if not l.is_prime(): pstatus = 'False' + needtofactor.add(l) + if l in V: pstatus = 'True' + if pstatus != 'True': safebase = False + writefile('verify-lisprime',pstatus + '\n') + + writefile('expand2-p','= %s\n' % expand2(p)) + writefile('expand2-l','
= %s\n' % expand2(l)) + + writefile('hex-p',hex(p) + '\n') + writefile('hex-l',hex(l) + '\n') + writefile('hex-x0',hex(x0) + '\n') + writefile('hex-x1',hex(x1) + '\n') + writefile('hex-y0',hex(y0) + '\n') + writefile('hex-y1',hex(y1) + '\n') + + gcdlpis1 = gcd(l,p) == 1 + safetransfer &= requirement('verify-gcdlp1',gcdlpis1) + + writefile('verify-movsafe','Unverified\n') + writefile('verify-embeddingdegree','Unverified\n') + if gcdlpis1 and l.is_prime(): + u = Integers(l)(p) + d = l-1 + needtofactor.add(d) + for v in V: + while d % v == 0: d /= v + if d == 1: + d = l-1 + for v in V: + while d % v == 0: + if u^(d/v) != 1: break + d /= v + safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100) + writefile('verify-embeddingdegree','%s
= (l-1)/%s\n' % (d,(l-1)/d)) + + t = p+1-l*round((p+1)/l) + if l^2 > 16*p: + writefile('verify-trace','%s\n' % t) + f = factor(1) + d = (p+1-t)/l + needtofactor.add(d) + for v in V: + while d % v == 0: + d //= v + f *= factor(v) + writefile('verify-cofactor','%s\n' % f) + else: + writefile('verify-trace','Unverified\n') + writefile('verify-cofactor','Unverified\n') + + D = t^2-4*p + needtofactor.add(D) + for v in V: + while D % v^2 == 0: D /= v^2 + if prod([v for v in V if D % v == 0]) != -D: + writefile('verify-disc','Unverified\n') + writefile('verify-discisbig','Unverified\n') + safedisc = False + else: + f = -prod([factor(v) for v in V if D % v == 0]) + if D % 4 != 1: + D *= 4 + f = factor(4) * f + Dbits = (log(-D)/log(2)).numerical_approx() + writefile('verify-disc','%s
= %s
≈ -2^%.1f\n' % (D,f,Dbits)) + safedisc &= requirement('verify-discisbig',D < -2^100) + + pi4 = 0.78539816339744830961566084581987572105 + rho = log(pi4*l)/log(4) + writefile('verify-rho','%.1f\n' % rho) + saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100) + + twistl = 'Unverified' + d = p+1+t + needtofactor.add(d) + for v in V: + while d % v == 0: d /= v + if d == 1: + d = p+1+t + for v in V: + if d % v == 0: + if twistl == 'Unverified' or v > twistl: twistl = v + + writefile('verify-twistl','%s\n' % twistl) + writefile('verify-twistembeddingdegree','Unverified\n') + writefile('verify-twistmovsafe','Unverified\n') + if twistl == 'Unverified': + writefile('hex-twistl','Unverified\n') + writefile('expand2-twistl','Unverified\n') + writefile('verify-twistcofactor','Unverified\n') + writefile('verify-gcdtwistlp1','Unverified\n') + writefile('verify-twistrho','Unverified\n') + safetwist = False + else: + writefile('hex-twistl',hex(twistl) + '\n') + writefile('expand2-twistl','
= %s\n' % expand2(twistl)) + f = factor(1) + d = (p+1+t)/twistl + needtofactor.add(d) + for v in V: + while d % v == 0: + d //= v + f *= factor(v) + writefile('verify-twistcofactor','%s\n' % f) + gcdtwistlpis1 = gcd(twistl,p) == 1 + safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1) + + movsafe = 'Unverified' + embeddingdegree = 'Unverified' + if gcdtwistlpis1 and twistl.is_prime(): + u = Integers(twistl)(p) + d = twistl-1 + needtofactor.add(d) + for v in V: + while d % v == 0: d /= v + if d == 1: + d = twistl-1 + for v in V: + while d % v == 0: + if u^(d/v) != 1: break + d /= v + safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100) + writefile('verify-twistembeddingdegree',"%s
= (l'-1)/%s\n" % (d,(twistl-1)/d)) + + rho = log(pi4*twistl)/log(4) + writefile('verify-twistrho','%.1f\n' % rho) + safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100) + + precomp = 0 + joint = l + needtofactor.add(p+1-t) + needtofactor.add(p+1+t) + for v in V: + d1 = p+1-t + d2 = p+1+t + while d1 % v == 0 or d2 % v == 0: + if d1 % v == 0: d1 //= v + if d2 % v == 0: d2 //= v + # best case for attack: cyclic; each power is usable + # also assume that kangaroo is as efficient as rho + if v + sqrt(pi4*joint/v) < sqrt(pi4*joint): + precomp += v + joint /= v + + rho = log(precomp + sqrt(pi4 * joint))/log(2) + writefile('verify-jointrho','%.1f\n' % rho) + safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100) + + + x0 = k(x0) + y0 = k(y0) + x1 = k(x1) + y1 = k(y1) + + if shape in ('edwards', 'tedwards'): + d = Integer(readfile('d')) + a = 1 + if shape == 'tedwards': + a = Integer(readfile('a')) + + writefile('verify-shape','Twisted Edwards\n') + writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d)) + if a == 1: + writefile('verify-shape','Edwards\n') + writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d) + + a = k(a) + d = k(d) + elliptic = a*d*(a-d) + level0 = a*x0^2+y0^2-1-d*x0^2*y0^2 + level1 = a*x1^2+y1^2-1-d*x1^2*y1^2 + + if shape == 'montgomery': + writefile('verify-shape','Montgomery\n') + A = Integer(readfile('A')) + B = Integer(readfile('B')) + equation = '%sy^2 = x^3%+dx^2+x' % (B,A) + if B == 1: + equation = 'y^2 = x^3%+dx^2+x' % A + writefile('verify-equation',equation + '\n') + + A = k(A) + B = k(B) + elliptic = B*(A^2-4) + level0 = B*y0^2-x0^3-A*x0^2-x0 + level1 = B*y1^2-x1^3-A*x1^2-x1 + + if shape == 'shortw': + writefile('verify-shape','short Weierstrass\n') + a = Integer(readfile('a')) + b = Integer(readfile('b')) + writefile('verify-equation','y^2 = x^3%+dx%+d\n' % (a,b)) + + a = k(a) + b = k(b) + elliptic = 4*a^3+27*b^2 + level0 = y0^2-x0^3-a*x0-b + level1 = y1^2-x1^3-a*x1-b + + writefile('verify-elliptic',str(elliptic) + '\n') + safeeq &= requirement('verify-iselliptic',elliptic != 0) + safebase &= requirement('verify-isoncurve0',level0 == 0) + safebase &= requirement('verify-isoncurve1',level1 == 0) + + if shape in ('edwards', 'tedwards'): + A = 2*(a+d)/(a-d) + B = 4/(a-d) + x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0 + x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1 + shape = 'montgomery' + + if shape == 'montgomery': + a = (3-A^2)/(3*B^2) + b = (2*A^3-9*A)/(27*B^3) + x0,y0 = (x0+A/3)/B,y0/B + x1,y1 = (x1+A/3)/B,y1/B + shape = 'shortw' + + try: + E = EllipticCurve([a,b]) + numorder2 = 0 + numorder4 = 0 + for P in E(0).division_points(4): + if P != 0 and 2*P == 0: + numorder2 += 1 + if 2*P != 0 and 4*P == 0: + numorder4 += 1 + writefile('verify-numorder2',str(numorder2) + '\n') + writefile('verify-numorder4',str(numorder4) + '\n') + completesingle = False + completemulti = False + if numorder4 == 2 and numorder2 == 1: + # complete edwards form, and montgomery with unique point of order 2 + completesingle = True + completemulti = True + # should extend this to allow complete twisted hessian + safecomplete &= requirement('verify-completesingle',completesingle) + safecomplete &= requirement('verify-completemulti',completemulti) + safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0) + writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n') + writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n') + except: + writefile('verify-numorder2','Unverified\n') + writefile('verify-numorder4','Unverified\n') + writefile('verify-ltimesbase1','Unverified\n') + writefile('verify-cofactorbase01','Unverified\n') + safecomplete = False + + montladder = False + for r,e in (z^3+a*z+b).roots(): + if (3*r^2+a).is_square(): + montladder = True + safeladder &= requirement('verify-montladder',montladder) + + indistinguishability = False + elligator2 = False + if (p+1-t) % 2 == 0: + if b != 0: + indistinguishability = True + elligator2 = True + safeind &= requirement('verify-indistinguishability',indistinguishability) + writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2]) + + saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid') + + safecurve = True + safecurve &= requirement('verify-safefield',safefield) + safecurve &= requirement('verify-safeeq',safeeq) + safecurve &= requirement('verify-safebase',safebase) + safecurve &= requirement('verify-saferho',saferho) + safecurve &= requirement('verify-safetransfer',safetransfer) + safecurve &= requirement('verify-safedisc',safedisc) + safecurve &= requirement('verify-saferigid',saferigid) + safecurve &= requirement('verify-safeladder',safeladder) + safecurve &= requirement('verify-safetwist',safetwist) + safecurve &= requirement('verify-safecomplete',safecomplete) + safecurve &= requirement('verify-safeind',safeind) + requirement('verify-safecurve',safecurve) + +originaldir = os.open('.',os.O_RDONLY) +for i in range(1,len(sys.argv)): + os.fchdir(originaldir) + os.chdir(sys.argv[i]) + verify() + diff --git a/jubjub/doc/evidence/x0 b/jubjub/doc/evidence/x0 new file mode 100644 index 0000000..3b2097a --- /dev/null +++ b/jubjub/doc/evidence/x0 @@ -0,0 +1 @@ +11076627216317271660298050606127911965867021807910416450833192264015104452986 diff --git a/jubjub/doc/evidence/x1 b/jubjub/doc/evidence/x1 new file mode 100644 index 0000000..c8c8fc3 --- /dev/null +++ b/jubjub/doc/evidence/x1 @@ -0,0 +1 @@ +8076246640662884909881801758704306714034609987455869804520522091855516602923 diff --git a/jubjub/doc/evidence/y0 b/jubjub/doc/evidence/y0 new file mode 100644 index 0000000..b47cd27 --- /dev/null +++ b/jubjub/doc/evidence/y0 @@ -0,0 +1 @@ +44412834903739585386157632289020980010620626017712148233229312325549216099227 diff --git a/jubjub/doc/evidence/y1 b/jubjub/doc/evidence/y1 new file mode 100644 index 0000000..a46479f --- /dev/null +++ b/jubjub/doc/evidence/y1 @@ -0,0 +1 @@ +13262374693698910701929044844600465831413122818447359594527400194675274060458 diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs new file mode 100644 index 0000000..e469530 --- /dev/null +++ b/jubjub/src/fr.rs @@ -0,0 +1,1024 @@ +//! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ +//! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` + +use core::convert::TryInto; +use core::fmt; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::util::{adc, mac, sbb}; + +/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic +/// curve construction. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. Elements of Fr are always in +// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. +#[derive(Clone, Copy, Eq)] +pub struct Fr(pub(crate) [u64; 4]); + +impl fmt::Debug for Fr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tmp = self.to_bytes(); + write!(f, "0x")?; + for &b in tmp.iter().rev() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl From for Fr { + fn from(val: u64) -> Fr { + Fr([val, 0, 0, 0]) * R2 + } +} + +impl ConstantTimeEq for Fr { + fn ct_eq(&self, other: &Self) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + } +} + +impl PartialEq for Fr { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for Fr { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fr([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) + } +} + +/// Constant representing the modulus +/// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 +pub const MODULUS: Fr = Fr([ + 0xd097_0e5e_d6f7_2cb7, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, +]); + +impl<'a> Neg for &'a Fr { + type Output = Fr; + + #[inline] + fn neg(self) -> Fr { + self.neg() + } +} + +impl Neg for Fr { + type Output = Fr; + + #[inline] + fn neg(self) -> Fr { + -&self + } +} + +impl<'a, 'b> Sub<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn sub(self, rhs: &'b Fr) -> Fr { + self.sub(rhs) + } +} + +impl<'a, 'b> Add<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn add(self, rhs: &'b Fr) -> Fr { + self.add(rhs) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a Fr { + type Output = Fr; + + #[inline] + fn mul(self, rhs: &'b Fr) -> Fr { + // Schoolbook multiplication + + self.mul(rhs) + } +} + +impl_binops_additive!(Fr, Fr); +impl_binops_multiplicative!(Fr, Fr); + +/// INV = -(r^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x1ba3_a358_ef78_8ef9; + +/// R = 2^256 mod r +const R: Fr = Fr([ + 0x25f8_0bb3_b996_07d9, + 0xf315_d62f_66b6_e750, + 0x9325_14ee_eb88_14f4, + 0x09a6_fc6f_4791_55c6, +]); + +/// R^2 = 2^512 mod r +const R2: Fr = Fr([ + 0x6771_9aa4_95e5_7731, + 0x51b0_cef0_9ce3_fc26, + 0x69da_b7fa_c026_e9a5, + 0x04f6_547b_8d12_7688, +]); + +/// R^2 = 2^768 mod r +const R3: Fr = Fr([ + 0xe0d6_c656_3d83_0544, + 0x323e_3883_598d_0f85, + 0xf0fe_a300_4c2e_2ba8, + 0x0587_4f84_9467_37ec, +]); + +impl Default for Fr { + fn default() -> Self { + Self::zero() + } +} + +impl Fr { + /// Returns zero, the additive identity. + #[inline] + pub const fn zero() -> Fr { + Fr([0, 0, 0, 0]) + } + + /// Returns one, the multiplicative identity. + #[inline] + pub const fn one() -> Fr { + R + } + + /// Doubles this field element. + #[inline] + pub const fn double(&self) -> Fr { + self.add(self) + } + + /// Attempts to convert a little-endian byte representation of + /// a field element into an element of `Fr`, failing if the input + /// is not canonical (is not smaller than r). + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + let mut tmp = Fr([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); + tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); + tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); + tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + /// Converts an element of `Fr` into a byte representation in + /// little-endian byte order. + pub fn to_bytes(&self) -> [u8; 32] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fr::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + /// Converts a 512-bit little endian integer into + /// an element of Fr by reducing modulo r. + pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { + Fr::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) + } + + fn from_u512(limbs: [u64; 8]) -> Fr { + // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits + // with the higher bits multiplied by 2^256. Thus, we perform two reductions + // + // 1. the lower bits are multiplied by R^2, as normal + // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 + // + // and computing their sum in the field. It remains to see that arbitrary 256-bit + // numbers can be placed into Montgomery form safely using the reduction. The + // reduction works so long as the product is less than R=2^256 multipled by + // the modulus. This holds because for any `c` smaller than the modulus, we have + // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the + // reduction always works so long as `c` is in the field; in this case it is either the + // constant `R2` or `R3`. + let d0 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); + let d1 = Fr([limbs[4], limbs[5], limbs[6], limbs[7]]); + // Convert to Montgomery form + d0 * R2 + d1 * R3 + } + + /// Converts from an integer represented in little endian + /// into its (congruent) `Fr` representation. + pub const fn from_raw(val: [u64; 4]) -> Self { + (&Fr(val)).mul(&R2) + } + + /// Squares this element. + #[inline] + pub const fn square(&self) -> Fr { + let (r1, carry) = mac(0, self.0[0], self.0[1], 0); + let (r2, carry) = mac(0, self.0[0], self.0[2], carry); + let (r3, r4) = mac(0, self.0[0], self.0[3], carry); + + let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); + let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); + + let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let (r0, carry) = mac(0, self.0[0], self.0[0], 0); + let (r1, carry) = adc(0, r1, carry); + let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); + let (r3, carry) = adc(0, r3, carry); + let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); + let (r5, carry) = adc(0, r5, carry); + let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); + let (r7, _) = adc(0, r7, carry); + + Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Computes the square root of this element, if it exists. + pub fn sqrt(&self) -> CtOption { + // Because r = 3 (mod 4) + // sqrt can be done with only one exponentiation, + // via the computation of self^((r + 1) // 4) (mod r) + let sqrt = self.pow_vartime(&[ + 0xb425_c397_b5bd_cb2e, + 0x299a_0824_f332_0420, + 0x4199_cec0_404d_0ec0, + 0x039f_6d3a_994c_ebea, + ]); + + CtOption::new( + sqrt, + (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. + ) + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + pub fn pow(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + let mut tmp = res; + tmp.mul_assign(self); + res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); + } + } + res + } + + /// Exponentiates `self` by `by`, where `by` is a + /// little-endian order integer exponent. + /// + /// **This operation is variable time with respect + /// to the exponent.** If the exponent is fixed, + /// this operation is effectively constant time. + pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..64).rev() { + res = res.square(); + + if ((*e >> i) & 1) == 1 { + res.mul_assign(self); + } + } + } + res + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + pub fn invert(&self) -> CtOption { + #[inline(always)] + fn square_assign_multi(n: &mut Fr, num_times: usize) { + for _ in 0..num_times { + *n = n.square(); + } + } + // found using https://github.com/kwantam/addchain + let mut t1 = self.square(); + let mut t0 = t1.square(); + let mut t3 = t0 * t1; + let t6 = t3 * self; + let t7 = t6 * t1; + let t12 = t7 * t3; + let t13 = t12 * t0; + let t16 = t12 * t3; + let t2 = t13 * t3; + let t15 = t16 * t3; + let t19 = t2 * t0; + let t9 = t15 * t3; + let t18 = t9 * t3; + let t14 = t18 * t1; + let t4 = t18 * t0; + let t8 = t18 * t3; + let t17 = t14 * t3; + let t11 = t8 * t3; + t1 = t17 * t3; + let t5 = t11 * t3; + t3 = t5 * t0; + t0 = t5.square(); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t8); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t19); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t14); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t18); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t17); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t16); + square_assign_multi(&mut t0, 3); + t0.mul_assign(self); + square_assign_multi(&mut t0, 11); + t0.mul_assign(&t11); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t5); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t15); + square_assign_multi(&mut t0, 8); + t0.mul_assign(self); + square_assign_multi(&mut t0, 12); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t9); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t15); + square_assign_multi(&mut t0, 14); + t0.mul_assign(&t14); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t13); + square_assign_multi(&mut t0, 2); + t0.mul_assign(self); + square_assign_multi(&mut t0, 6); + t0.mul_assign(self); + square_assign_multi(&mut t0, 9); + t0.mul_assign(&t7); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t12); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t11); + square_assign_multi(&mut t0, 3); + t0.mul_assign(self); + square_assign_multi(&mut t0, 12); + t0.mul_assign(&t9); + square_assign_multi(&mut t0, 11); + t0.mul_assign(&t8); + square_assign_multi(&mut t0, 8); + t0.mul_assign(&t7); + square_assign_multi(&mut t0, 4); + t0.mul_assign(&t6); + square_assign_multi(&mut t0, 10); + t0.mul_assign(&t5); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t4); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t3); + square_assign_multi(&mut t0, 5); + t0.mul_assign(&t2); + square_assign_multi(&mut t0, 6); + t0.mul_assign(&t2); + square_assign_multi(&mut t0, 7); + t0.mul_assign(&t1); + + CtOption::new(t0, !self.ct_eq(&Self::zero())) + } + + #[inline] + #[allow(clippy::too_many_arguments)] + const fn montgomery_reduce( + r0: u64, + r1: u64, + r2: u64, + r3: u64, + r4: u64, + r5: u64, + r6: u64, + r7: u64, + ) -> Self { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let (_, carry) = mac(r0, k, MODULUS.0[0], 0); + let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); + let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); + let (r4, carry2) = adc(r4, 0, carry); + + let k = r1.wrapping_mul(INV); + let (_, carry) = mac(r1, k, MODULUS.0[0], 0); + let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); + let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); + let (r5, carry2) = adc(r5, carry2, carry); + + let k = r2.wrapping_mul(INV); + let (_, carry) = mac(r2, k, MODULUS.0[0], 0); + let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); + let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); + let (r6, carry2) = adc(r6, carry2, carry); + + let k = r3.wrapping_mul(INV); + let (_, carry) = mac(r3, k, MODULUS.0[0], 0); + let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); + let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); + let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); + let (r7, _) = adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + (&Fr([r4, r5, r6, r7])).sub(&MODULUS) + } + + /// Multiplies this element by another element + #[inline] + pub const fn mul(&self, rhs: &Self) -> Self { + // Schoolbook multiplication + + let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); + let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); + let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); + let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); + + let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); + let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); + let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); + let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); + + let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); + let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); + let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); + let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); + + let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); + let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); + let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); + let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); + + Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) + } + + /// Subtracts another element from this element. + #[inline] + pub const fn sub(&self, rhs: &Self) -> Self { + let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); + let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); + let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); + let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); + + // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. + let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); + let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); + let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); + let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); + + Fr([d0, d1, d2, d3]) + } + + /// Adds this element to another element. + #[inline] + pub const fn add(&self, rhs: &Self) -> Self { + let (d0, carry) = adc(self.0[0], rhs.0[0], 0); + let (d1, carry) = adc(self.0[1], rhs.0[1], carry); + let (d2, carry) = adc(self.0[2], rhs.0[2], carry); + let (d3, _) = adc(self.0[3], rhs.0[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (&Fr([d0, d1, d2, d3])).sub(&MODULUS) + } + + /// Negates this element. + #[inline] + pub const fn neg(&self) -> Self { + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); + let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); + let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); + let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); + + Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) + } +} + +impl<'a> From<&'a Fr> for [u8; 32] { + fn from(value: &'a Fr) -> [u8; 32] { + value.to_bytes() + } +} + +#[test] +fn test_inv() { + // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 + + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(MODULUS.0[0]); + } + inv = inv.wrapping_neg(); + + assert_eq!(inv, INV); +} + +#[test] +fn test_debug() { + assert_eq!( + format!("{:?}", Fr::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Fr::one()), + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + assert_eq!( + format!("{:?}", R2), + "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" + ); +} + +#[test] +fn test_equality() { + assert_eq!(Fr::zero(), Fr::zero()); + assert_eq!(Fr::one(), Fr::one()); + assert_eq!(R2, R2); + + assert!(Fr::zero() != Fr::one()); + assert!(Fr::one() != R2); +} + +#[test] +fn test_to_bytes() { + assert_eq!( + Fr::zero().to_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + Fr::one().to_bytes(), + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ] + ); + + assert_eq!( + R2.to_bytes(), + [ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 + ] + ); + + assert_eq!( + (-&Fr::one()).to_bytes(), + [ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ] + ); +} + +#[test] +fn test_from_bytes() { + assert_eq!( + Fr::from_bytes(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Fr::zero() + ); + + assert_eq!( + Fr::from_bytes(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ]) + .unwrap(), + Fr::one() + ); + + assert_eq!( + Fr::from_bytes(&[ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 + ]) + .unwrap(), + R2 + ); + + // -1 should work + assert!( + Fr::from_bytes(&[ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_some() + .unwrap_u8() + == 1 + ); + + // modulus is invalid + assert!( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + // Anything larger than the modulus is invalid + assert!( + Fr::from_bytes(&[ + 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + assert!( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 + ]) + .is_none() + .unwrap_u8() + == 1 + ); + + assert!( + Fr::from_bytes(&[ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 + ]) + .is_none() + .unwrap_u8() + == 1 + ); +} + +#[test] +fn test_from_u512_zero() { + assert_eq!( + Fr::zero(), + Fr::from_u512([ + MODULUS.0[0], + MODULUS.0[1], + MODULUS.0[2], + MODULUS.0[3], + 0, + 0, + 0, + 0 + ]) + ); +} + +#[test] +fn test_from_u512_r() { + assert_eq!(R, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_from_u512_r2() { + assert_eq!(R2, Fr::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); +} + +#[test] +fn test_from_u512_max() { + let max_u64 = 0xffff_ffff_ffff_ffff; + assert_eq!( + R3 - R, + Fr::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) + ); +} + +#[test] +fn test_from_bytes_wide_r2() { + assert_eq!( + R2, + Fr::from_bytes_wide(&[ + 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, + 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_negative_one() { + assert_eq!( + -&Fr::one(), + Fr::from_bytes_wide(&[ + 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, + 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + ); +} + +#[test] +fn test_from_bytes_wide_maximum() { + assert_eq!( + Fr([ + 0x8b75_c901_5ae4_2a22, + 0xe590_82e7_bf9e_38b8, + 0x6440_c912_61da_51b3, + 0x0a5e_07ff_b209_91cf, + ]), + Fr::from_bytes_wide(&[0xff; 64]) + ); +} + +#[test] +fn test_zero() { + assert_eq!(Fr::zero(), -&Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); + assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); +} + +#[cfg(test)] +const LARGEST: Fr = Fr([ + 0xd097_0e5e_d6f7_2cb6, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, +]); + +#[test] +fn test_addition() { + let mut tmp = LARGEST; + tmp += &LARGEST; + + assert_eq!( + tmp, + Fr([ + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9 + ]) + ); + + let mut tmp = LARGEST; + tmp += &Fr([1, 0, 0, 0]); + + assert_eq!(tmp, Fr::zero()); +} + +#[test] +fn test_negation() { + let tmp = -&LARGEST; + + assert_eq!(tmp, Fr([1, 0, 0, 0])); + + let tmp = -&Fr::zero(); + assert_eq!(tmp, Fr::zero()); + let tmp = -&Fr([1, 0, 0, 0]); + assert_eq!(tmp, LARGEST); +} + +#[test] +fn test_subtraction() { + let mut tmp = LARGEST; + tmp -= &LARGEST; + + assert_eq!(tmp, Fr::zero()); + + let mut tmp = Fr::zero(); + tmp -= &LARGEST; + + let mut tmp2 = MODULUS; + tmp2 -= &LARGEST; + + assert_eq!(tmp, tmp2); +} + +#[test] +fn test_multiplication() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp *= &cur; + + let mut tmp2 = Fr::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_squaring() { + let mut cur = LARGEST; + + for _ in 0..100 { + let mut tmp = cur; + tmp = tmp.square(); + + let mut tmp2 = Fr::zero(); + for b in cur + .to_bytes() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) + { + let tmp3 = tmp2; + tmp2.add_assign(&tmp3); + + if b { + tmp2.add_assign(&cur); + } + } + + assert_eq!(tmp, tmp2); + + cur.add_assign(&LARGEST); + } +} + +#[test] +fn test_inversion() { + assert_eq!(Fr::zero().invert().is_none().unwrap_u8(), 1); + assert_eq!(Fr::one().invert().unwrap(), Fr::one()); + assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); + + let mut tmp = R2; + + for _ in 0..100 { + let mut tmp2 = tmp.invert().unwrap(); + tmp2.mul_assign(&tmp); + + assert_eq!(tmp2, Fr::one()); + + tmp.add_assign(&R2); + } +} + +#[test] +fn test_invert_is_pow() { + let r_minus_2 = [ + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]; + + let mut r1 = R; + let mut r2 = R; + let mut r3 = R; + + for _ in 0..100 { + r1 = r1.invert().unwrap(); + r2 = r2.pow_vartime(&r_minus_2); + r3 = r3.pow(&r_minus_2); + + assert_eq!(r1, r2); + assert_eq!(r2, r3); + // Add R so we check something different next time around + r1.add_assign(&R); + r2 = r1; + r3 = r1; + } +} + +#[test] +fn test_sqrt() { + let mut square = Fr([ + // r - 2 + 0xd097_0e5e_d6f7_2cb5, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]); + + let mut none_count = 0; + + for _ in 0..100 { + let square_root = square.sqrt(); + if square_root.is_none().unwrap_u8() == 1 { + none_count += 1; + } else { + assert_eq!(square_root.unwrap() * square_root.unwrap(), square); + } + square -= Fr::one(); + } + + assert_eq!(47, none_count); +} + +#[test] +fn test_from_raw() { + assert_eq!( + Fr::from_raw([ + 0x25f8_0bb3_b996_07d8, + 0xf315_d62f_66b6_e750, + 0x9325_14ee_eb88_14f4, + 0x09a6_fc6f_4791_55c6, + ]), + Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) + ); + + assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); + + assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); +} diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs new file mode 100644 index 0000000..09cb97e --- /dev/null +++ b/jubjub/src/lib.rs @@ -0,0 +1,1328 @@ +//! This crate provides an implementation of the **Jubjub** elliptic curve and its associated +//! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. +//! +//! # API +//! +//! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic +//! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points +//! * `Fq`, which is the base field of Jubjub +//! * `Fr`, which is the scalar field of Jubjub +//! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. +//! +//! # Constant Time +//! +//! All operations are constant time unless explicitly noted; these functions will contain +//! "vartime" in their name and they will be documented as variable time. +//! +//! This crate uses the `subtle` crate to perform constant-time operations. + +#![no_std] +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![deny(unsafe_code)] +// This lint is described at +// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl +// In our library, some of the arithmetic will necessarily involve various binary +// operators, and so this lint is triggered unnecessarily. +#![allow(clippy::suspicious_arithmetic_impl)] + +#[cfg(test)] +#[macro_use] +extern crate std; + +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +#[macro_use] +mod util; + +mod fr; +pub use bls12_381::Scalar as Fq; +pub use fr::Fr; + +const FR_MODULUS_BYTES: [u8; 32] = [ + 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, + 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, +]; + +/// This represents a Jubjub point in the affine `(u, v)` +/// coordinates. +#[derive(Clone, Copy, Debug)] +pub struct AffinePoint { + u: Fq, + v: Fq, +} + +impl Neg for AffinePoint { + type Output = AffinePoint; + + /// This computes the negation of a point `P = (u, v)` + /// as `-P = (-u, v)`. + #[inline] + fn neg(self) -> AffinePoint { + AffinePoint { + u: -self.u, + v: self.v, + } + } +} + +impl ConstantTimeEq for AffinePoint { + fn ct_eq(&self, other: &Self) -> Choice { + self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) + } +} + +impl PartialEq for AffinePoint { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl ConditionallySelectable for AffinePoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffinePoint { + u: Fq::conditional_select(&a.u, &b.u, choice), + v: Fq::conditional_select(&a.v, &b.v, choice), + } + } +} + +/// This represents an extended point `(U, V, Z, T1, T2)` +/// with `Z` nonzero, corresponding to the affine point +/// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. +/// +/// You can do the following things with a point in this +/// form: +/// +/// * Convert it into a point in the affine form. +/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. +/// * Double it using `double()`. +/// * Compare it with another extended point using `PartialEq` or `ct_eq()`. +#[derive(Clone, Copy, Debug)] +pub struct ExtendedPoint { + u: Fq, + v: Fq, + z: Fq, + t1: Fq, + t2: Fq, +} + +impl ConstantTimeEq for ExtendedPoint { + fn ct_eq(&self, other: &Self) -> Choice { + // (u/z, v/z) = (u'/z', v'/z') is implied by + // (uz'z = u'z'z) and + // (vz'z = v'z'z) + // as z and z' are always nonzero. + + (self.u * other.z).ct_eq(&(other.u * self.z)) + & (self.v * other.z).ct_eq(&(other.v * self.z)) + } +} + +impl ConditionallySelectable for ExtendedPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtendedPoint { + u: Fq::conditional_select(&a.u, &b.u, choice), + v: Fq::conditional_select(&a.v, &b.v, choice), + z: Fq::conditional_select(&a.z, &b.z, choice), + t1: Fq::conditional_select(&a.t1, &b.t1, choice), + t2: Fq::conditional_select(&a.t2, &b.t2, choice), + } + } +} + +impl PartialEq for ExtendedPoint { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).unwrap_u8() == 1 + } +} + +impl Neg for ExtendedPoint { + type Output = ExtendedPoint; + + /// Computes the negation of a point `P = (U, V, Z, T)` + /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` + /// is made without loss of generality. + #[inline] + fn neg(self) -> ExtendedPoint { + ExtendedPoint { + u: -self.u, + v: self.v, + z: self.z, + t1: -self.t1, + t2: self.t2, + } + } +} + +impl From for ExtendedPoint { + /// Constructs an extended point (with `Z = 1`) from + /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. + fn from(affine: AffinePoint) -> ExtendedPoint { + ExtendedPoint { + u: affine.u, + v: affine.v, + z: Fq::one(), + t1: affine.u, + t2: affine.v, + } + } +} + +impl<'a> From<&'a ExtendedPoint> for AffinePoint { + /// Constructs an affine point from an extended point + /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` + /// as Z is always nonzero. **This requires a field inversion + /// and so it is recommended to perform these in a batch + /// using [`batch_normalize`](crate::batch_normalize) instead.** + fn from(extended: &'a ExtendedPoint) -> AffinePoint { + // Z coordinate is always nonzero, so this is + // its inverse. + let zinv = extended.z.invert().unwrap(); + + AffinePoint { + u: extended.u * zinv, + v: extended.v * zinv, + } + } +} + +impl From for AffinePoint { + fn from(extended: ExtendedPoint) -> AffinePoint { + AffinePoint::from(&extended) + } +} + +/// This is a pre-processed version of an affine point `(u, v)` +/// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an +/// [`ExtendedPoint`](crate::ExtendedPoint). +#[derive(Clone, Copy, Debug)] +pub struct AffineNielsPoint { + v_plus_u: Fq, + v_minus_u: Fq, + t2d: Fq, +} + +impl AffineNielsPoint { + /// Constructs this point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + AffineNielsPoint { + v_plus_u: Fq::one(), + v_minus_u: Fq::one(), + t2d: Fq::zero(), + } + } + + #[inline] + fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { + let zero = AffineNielsPoint::identity(); + + let mut acc = ExtendedPoint::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading four bits because they're always + // unset for Fr. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(4) + { + acc = acc.double(); + acc += AffineNielsPoint::conditional_select(&zero, &self, bit); + } + + acc + } + + /// Multiplies this point by the specific little-endian bit pattern in the + /// given byte array, ignoring the highest four bits. + pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { + self.multiply(by) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); + +impl ConditionallySelectable for AffineNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffineNielsPoint { + v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), + v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), + t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), + } + } +} + +/// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` +/// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. +#[derive(Clone, Copy, Debug)] +pub struct ExtendedNielsPoint { + v_plus_u: Fq, + v_minus_u: Fq, + z: Fq, + t2d: Fq, +} + +impl ConditionallySelectable for ExtendedNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtendedNielsPoint { + v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), + v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), + z: Fq::conditional_select(&a.z, &b.z, choice), + t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), + } + } +} + +impl ExtendedNielsPoint { + /// Constructs this point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + ExtendedNielsPoint { + v_plus_u: Fq::one(), + v_minus_u: Fq::one(), + z: Fq::one(), + t2d: Fq::zero(), + } + } + + #[inline] + fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { + let zero = ExtendedNielsPoint::identity(); + + let mut acc = ExtendedPoint::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading four bits because they're always + // unset for Fr. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(4) + { + acc = acc.double(); + acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit); + } + + acc + } + + /// Multiplies this point by the specific little-endian bit pattern in the + /// given byte array, ignoring the highest four bits. + pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { + self.multiply(by) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); + +// `d = -(10240/10241)` +const EDWARDS_D: Fq = Fq::from_raw([ + 0x0106_5fd6_d634_3eb1, + 0x292d_7f6d_3757_9d26, + 0xf5fd_9207_e6bd_7fd4, + 0x2a93_18e7_4bfa_2b48, +]); + +// `2*d` +const EDWARDS_D2: Fq = Fq::from_raw([ + 0x020c_bfad_ac68_7d62, + 0x525a_feda_6eaf_3a4c, + 0xebfb_240f_cd7a_ffa8, + 0x5526_31ce_97f4_5691, +]); + +impl AffinePoint { + /// Constructs the neutral element `(0, 1)`. + pub const fn identity() -> Self { + AffinePoint { + u: Fq::zero(), + v: Fq::one(), + } + } + + /// Multiplies this point by the cofactor, producing an + /// `ExtendedPoint` + pub fn mul_by_cofactor(&self) -> ExtendedPoint { + ExtendedPoint::from(*self).mul_by_cofactor() + } + + /// Determines if this point is of small order. + pub fn is_small_order(&self) -> Choice { + ExtendedPoint::from(*self).is_small_order() + } + + /// Determines if this point is torsion free and so is + /// in the prime order subgroup. + pub fn is_torsion_free(&self) -> Choice { + ExtendedPoint::from(*self).is_torsion_free() + } + + /// Determines if this point is prime order, or in other words that + /// the smallest scalar multiplied by this point that produces the + /// identity is `r`. This is equivalent to checking that the point + /// is both torsion free and not the identity. + pub fn is_prime_order(&self) -> Choice { + let extended = ExtendedPoint::from(*self); + extended.is_torsion_free() & (!extended.is_identity()) + } + + /// Converts this element into its byte representation. + pub fn to_bytes(&self) -> [u8; 32] { + let mut tmp = self.v.to_bytes(); + let u = self.u.to_bytes(); + + // Encode the sign of the u-coordinate in the most + // significant bit. + tmp[31] |= u[0] << 7; + + tmp + } + + /// Attempts to interpret a byte representation of an + /// affine point, failing if the element is not on + /// the curve or non-canonical. + pub fn from_bytes(mut b: [u8; 32]) -> CtOption { + // Grab the sign bit from the representation + let sign = b[31] >> 7; + + // Mask away the sign bit + b[31] &= 0b0111_1111; + + // Interpret what remains as the v-coordinate + Fq::from_bytes(&b).and_then(|v| { + // -u^2 + v^2 = 1 + d.u^2.v^2 + // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) + // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) + // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) + // u^2 (1 + d.v^2) = v^2 - 1 (factor) + // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) + // We know that (1 + d.v^2) is nonzero for all v: + // (1 + d.v^2) = 0 + // d.v^2 = -1 + // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square + + let v2 = v.square(); + + ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .and_then(|u| { + // Fix the sign of `u` if necessary + let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); + let u_negated = -u; + let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); + + CtOption::new(AffinePoint { u: final_u, v }, Choice::from(1u8)) + }) + }) + } + + /// Returns the `u`-coordinate of this point. + pub fn get_u(&self) -> Fq { + self.u + } + + /// Returns the `v`-coordinate of this point. + pub fn get_v(&self) -> Fq { + self.v + } + + /// Performs a pre-processing step that produces an `AffineNielsPoint` + /// for use in multiple additions. + pub const fn to_niels(&self) -> AffineNielsPoint { + AffineNielsPoint { + v_plus_u: Fq::add(&self.v, &self.u), + v_minus_u: Fq::sub(&self.v, &self.u), + t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), + } + } + + /// Constructs an AffinePoint given `u` and `v` without checking + /// that the point is on the curve. + pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { + AffinePoint { u, v } + } + + /// This is only for debugging purposes and not + /// exposed in the public API. Checks that this + /// point is on the curve. + #[cfg(test)] + fn is_on_curve_vartime(&self) -> bool { + let u2 = self.u.square(); + let v2 = self.v.square(); + + v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 + } +} + +impl ExtendedPoint { + /// Constructs an extended point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + ExtendedPoint { + u: Fq::zero(), + v: Fq::one(), + z: Fq::one(), + t1: Fq::zero(), + t2: Fq::zero(), + } + } + + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + // If this point is the identity, then + // u = 0 * z = 0 + // and v = 1 * z = z + self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) + } + + /// Determines if this point is of small order. + pub fn is_small_order(&self) -> Choice { + // We only need to perform two doublings, since the 2-torsion + // points are (0, 1) and (0, -1), and so we only need to check + // that the u-coordinate of the result is zero to see if the + // point is small order. + self.double().double().u.ct_eq(&Fq::zero()) + } + + /// Determines if this point is torsion free and so is contained + /// in the prime order subgroup. + pub fn is_torsion_free(&self) -> Choice { + self.multiply(&FR_MODULUS_BYTES).is_identity() + } + + /// Determines if this point is prime order, or in other words that + /// the smallest scalar multiplied by this point that produces the + /// identity is `r`. This is equivalent to checking that the point + /// is both torsion free and not the identity. + pub fn is_prime_order(&self) -> Choice { + self.is_torsion_free() & (!self.is_identity()) + } + + /// Multiplies this element by the cofactor `8`. + pub fn mul_by_cofactor(&self) -> ExtendedPoint { + self.double().double().double() + } + + /// Performs a pre-processing step that produces an `ExtendedNielsPoint` + /// for use in multiple additions. + pub fn to_niels(&self) -> ExtendedNielsPoint { + ExtendedNielsPoint { + v_plus_u: self.v + self.u, + v_minus_u: self.v - self.u, + z: self.z, + t2d: self.t1 * self.t2 * EDWARDS_D2, + } + } + + /// Computes the doubling of a point more efficiently than a point can + /// be added to itself. + pub fn double(&self) -> ExtendedPoint { + // Doubling is more efficient (three multiplications, four squarings) + // when we work within the projective coordinate space (U:Z, V:Z). We + // rely on the most efficient formula, "dbl-2008-bbjlp", as described + // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. + // + // See + // for more information. + // + // We differ from the literature in that we use (u, v) rather than + // (x, y) coordinates. We also have the constant `a = -1` implied. Let + // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) + // as follows: + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // H = 2 * z^2 + // J = F - H + // U = (B - C - D) * J + // V = F * (- C - D) + // Z = F * J + // + // If we compute K = D + C, we can rewrite this: + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // K = D + C + // H = 2 * z^2 + // J = F - H + // U = (B - K) * J + // V = F * (-K) + // Z = F * J + // + // In order to avoid the unnecessary negation of K, + // we will negate J, transforming the result into + // an equivalent point with a negated z-coordinate. + // + // B = (u + v)^2 + // C = u^2 + // D = v^2 + // F = D - C + // K = D + C + // H = 2 * z^2 + // J = H - F + // U = (B - K) * J + // V = F * K + // Z = F * J + // + // Let us rename some variables to simplify: + // + // UV2 = (u + v)^2 + // UU = u^2 + // VV = v^2 + // VVmUU = VV - UU + // VVpUU = VV + UU + // ZZ2 = 2 * z^2 + // J = ZZ2 - VVmUU + // U = (UV2 - VVpUU) * J + // V = VVmUU * VVpUU + // Z = VVmUU * J + // + // We wish to obtain two factors of T = UV/Z. + // + // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) + // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU + // = (UV2 - VVpUU) * VVpUU + // + // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. + + let uu = self.u.square(); + let vv = self.v.square(); + let zz2 = self.z.square().double(); + let uv2 = (self.u + self.v).square(); + let vv_plus_uu = vv + uu; + let vv_minus_uu = vv - uu; + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: uv2 - vv_plus_uu, + v: vv_plus_uu, + z: vv_minus_uu, + t: zz2 - vv_minus_uu, + } + .into_extended() + } + + #[inline] + fn multiply(self, by: &[u8; 32]) -> Self { + self.to_niels().multiply(by) + } + + /// This is only for debugging purposes and not + /// exposed in the public API. Checks that this + /// point is on the curve. + #[cfg(test)] + fn is_on_curve_vartime(&self) -> bool { + let affine = AffinePoint::from(*self); + + self.z != Fq::zero() + && affine.is_on_curve_vartime() + && (affine.u * affine.v * self.z == self.t1 * self.t2) + } +} + +impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + fn mul(self, other: &'b Fr) -> ExtendedPoint { + self.multiply(&other.to_bytes()) + } +} + +impl_binops_multiplicative!(ExtendedPoint, Fr); + +impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { + // We perform addition in the extended coordinates. Here we use + // a formula presented by Hisil, Wong, Carter and Dawson in + // "Twisted Edward Curves Revisited" which only requires 8M. + // + // A = (V1 - U1) * (V2 - U2) + // B = (V1 + U1) * (V2 + U2) + // C = 2d * T1 * T2 + // D = 2 * Z1 * Z2 + // E = B - A + // F = D - C + // G = D + C + // H = B + A + // U3 = E * F + // Y3 = G * H + // Z3 = F * G + // T3 = E * H + + let a = (self.v - self.u) * other.v_minus_u; + let b = (self.v + self.u) * other.v_plus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = (self.z * other.z).double(); + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: b - a, + v: b + a, + z: d + c, + t: d - c, + } + .into_extended() + } +} + +impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { + let a = (self.v - self.u) * other.v_plus_u; + let b = (self.v + self.u) * other.v_minus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = (self.z * other.z).double(); + + CompletedPoint { + u: b - a, + v: b + a, + z: d - c, + t: d + c, + } + .into_extended() + } +} + +impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); + +impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { + // This is identical to the addition formula for `ExtendedNielsPoint`, + // except we can assume that `other.z` is one, so that we perform + // 7 multiplications. + + let a = (self.v - self.u) * other.v_minus_u; + let b = (self.v + self.u) * other.v_plus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = self.z.double(); + + // The remaining arithmetic is exactly the process of converting + // from a completed point to an extended point. + CompletedPoint { + u: b - a, + v: b + a, + z: d + c, + t: d - c, + } + .into_extended() + } +} + +impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { + let a = (self.v - self.u) * other.v_plus_u; + let b = (self.v + self.u) * other.v_minus_u; + let c = self.t1 * self.t2 * other.t2d; + let d = self.z.double(); + + CompletedPoint { + u: b - a, + v: b + a, + z: d - c, + t: d + c, + } + .into_extended() + } +} + +impl_binops_additive!(ExtendedPoint, AffineNielsPoint); + +impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { + self + other.to_niels() + } +} + +impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { + self - other.to_niels() + } +} + +impl_binops_additive!(ExtendedPoint, ExtendedPoint); + +impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn add(self, other: &'b AffinePoint) -> ExtendedPoint { + self + other.to_niels() + } +} + +impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + #[inline] + fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { + self - other.to_niels() + } +} + +impl_binops_additive!(ExtendedPoint, AffinePoint); + +/// This is a "completed" point produced during a point doubling or +/// addition routine. These points exist in the `(U:Z, V:T)` model +/// of the curve. This is not exposed in the API because it is +/// an implementation detail. +struct CompletedPoint { + u: Fq, + v: Fq, + z: Fq, + t: Fq, +} + +impl CompletedPoint { + /// This converts a completed point into an extended point by + /// homogenizing: + /// + /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) + /// + /// The resulting T coordinate is utvz/zt = uv, and so + /// T1 = u, T2 = v, without loss of generality. + #[inline] + fn into_extended(self) -> ExtendedPoint { + ExtendedPoint { + u: self.u * self.t, + v: self.v * self.z, + z: self.z * self.t, + t1: self.u, + t2: self.v, + } + } +} + +impl Default for AffinePoint { + /// Returns the identity. + fn default() -> AffinePoint { + AffinePoint::identity() + } +} + +impl Default for ExtendedPoint { + /// Returns the identity. + fn default() -> ExtendedPoint { + ExtendedPoint::identity() + } +} + +/// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using +/// only a single inversion for the entire batch. This normalization results in +/// all of the points having a Z-coordinate of one. Further, an iterator is +/// returned which can be used to obtain `AffinePoint`s for each element in the +/// slice. +/// +/// This costs 5 multiplications per element, and a field inversion. +pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator + 'a { + let mut acc = Fq::one(); + for p in v.iter_mut() { + // We use the `t1` field of `ExtendedPoint` to store the product + // of previous z-coordinates seen. + p.t1 = acc; + acc *= &p.z; + } + + // This is the inverse, as all z-coordinates are nonzero. + acc = acc.invert().unwrap(); + + for p in v.iter_mut().rev() { + let mut q = *p; + + // Compute tmp = 1/z + let tmp = q.t1 * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc *= &q.z; + + // Set the coordinates to the correct value + q.u *= &tmp; // Multiply by 1/z + q.v *= &tmp; // Multiply by 1/z + q.z = Fq::one(); // z-coordinate is now one + q.t1 = q.u; + q.t2 = q.v; + + *p = q; + } + + // All extended points are now normalized, but the type + // doesn't encode this fact. Let us offer affine points + // to the caller. + + v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) +} + +#[test] +fn test_is_on_curve_var() { + assert!(AffinePoint::identity().is_on_curve_vartime()); +} + +#[test] +fn test_d_is_non_quadratic_residue() { + assert!(EDWARDS_D.sqrt().is_none().unwrap_u8() == 1); + assert!((-EDWARDS_D).sqrt().is_none().unwrap_u8() == 1); + assert!((-EDWARDS_D).invert().unwrap().sqrt().is_none().unwrap_u8() == 1); +} + +#[test] +fn test_affine_niels_point_identity() { + assert_eq!( + AffineNielsPoint::identity().v_plus_u, + AffinePoint::identity().to_niels().v_plus_u + ); + assert_eq!( + AffineNielsPoint::identity().v_minus_u, + AffinePoint::identity().to_niels().v_minus_u + ); + assert_eq!( + AffineNielsPoint::identity().t2d, + AffinePoint::identity().to_niels().t2d + ); +} + +#[test] +fn test_extended_niels_point_identity() { + assert_eq!( + ExtendedNielsPoint::identity().v_plus_u, + ExtendedPoint::identity().to_niels().v_plus_u + ); + assert_eq!( + ExtendedNielsPoint::identity().v_minus_u, + ExtendedPoint::identity().to_niels().v_minus_u + ); + assert_eq!( + ExtendedNielsPoint::identity().z, + ExtendedPoint::identity().to_niels().z + ); + assert_eq!( + ExtendedNielsPoint::identity().t2d, + ExtendedPoint::identity().to_niels().t2d + ); +} + +#[test] +fn test_assoc() { + let p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + assert!(p.is_on_curve_vartime()); + + assert_eq!( + (p * Fr::from(1000u64)) * Fr::from(3938u64), + p * (Fr::from(1000u64) * Fr::from(3938u64)), + ); +} + +#[test] +fn test_batch_normalize() { + let mut p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + + let mut v = vec![]; + for _ in 0..10 { + v.push(p); + p = p.double(); + } + + for p in &v { + assert!(p.is_on_curve_vartime()); + } + + let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); + let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); + for i in 0..10 { + assert!(expected[i] == result1[i]); + assert!(v[i].is_on_curve_vartime()); + assert!(AffinePoint::from(v[i]) == expected[i]); + } + let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); + for i in 0..10 { + assert!(expected[i] == result2[i]); + assert!(v[i].is_on_curve_vartime()); + assert!(AffinePoint::from(v[i]) == expected[i]); + } +} + +#[cfg(test)] +const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xe4b3_d35d_f1a7_adfe, + 0xcaf5_5d1b_29bf_81af, + 0x8b0f_03dd_d60a_8187, + 0x62ed_cbb8_bf37_87c8, + ]), + Fq::from_raw([0xb, 0x0, 0x0, 0x0]), +); + +#[cfg(test)] +const EIGHT_TORSION: [AffinePoint; 8] = [ + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xd92e_6a79_2720_0d43, + 0x7aa4_1ac4_3dae_8582, + 0xeaaa_e086_a166_18d1, + 0x71d4_df38_ba9e_7973, + ]), + Fq::from_raw([ + 0xff0d_2068_eff4_96dd, + 0x9106_ee90_f384_a4a1, + 0x16a1_3035_ad4d_7266, + 0x4958_bdb2_1966_982e, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xfffe_ffff_0000_0001, + 0x67ba_a400_89fb_5bfe, + 0xa5e8_0b39_939e_d334, + 0x73ed_a753_299d_7d47, + ]), + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0xd92e_6a79_2720_0d43, + 0x7aa4_1ac4_3dae_8582, + 0xeaaa_e086_a166_18d1, + 0x71d4_df38_ba9e_7973, + ]), + Fq::from_raw([ + 0x00f2_df96_100b_6924, + 0xc2b6_b572_0c79_b75d, + 0x1c98_a7d2_5c54_659e, + 0x2a94_e9a1_1036_e51a, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + Fq::from_raw([ + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x26d1_9585_d8df_f2be, + 0xd919_893e_c24f_d67c, + 0x488e_f781_683b_bf33, + 0x0218_c81a_6eff_03d4, + ]), + Fq::from_raw([ + 0x00f2_df96_100b_6924, + 0xc2b6_b572_0c79_b75d, + 0x1c98_a7d2_5c54_659e, + 0x2a94_e9a1_1036_e51a, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x0001_0000_0000_0000, + 0xec03_0002_7603_0000, + 0x8d51_ccce_7603_04d0, + 0x0, + ]), + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([ + 0x26d1_9585_d8df_f2be, + 0xd919_893e_c24f_d67c, + 0x488e_f781_683b_bf33, + 0x0218_c81a_6eff_03d4, + ]), + Fq::from_raw([ + 0xff0d_2068_eff4_96dd, + 0x9106_ee90_f384_a4a1, + 0x16a1_3035_ad4d_7266, + 0x4958_bdb2_1966_982e, + ]), + ), + AffinePoint::from_raw_unchecked( + Fq::from_raw([0x0, 0x0, 0x0, 0x0]), + Fq::from_raw([0x1, 0x0, 0x0, 0x0]), + ), +]; + +#[test] +fn find_eight_torsion() { + let g = ExtendedPoint::from(FULL_GENERATOR); + assert!(g.is_small_order().unwrap_u8() == 0); + let g = g.multiply(&FR_MODULUS_BYTES); + assert!(g.is_small_order().unwrap_u8() == 1); + + let mut cur = g; + + for (i, point) in EIGHT_TORSION.iter().enumerate() { + let tmp = AffinePoint::from(cur); + if &tmp != point { + panic!("{}th torsion point should be {:?}", i, tmp); + } + + cur += &g; + } +} + +#[test] +fn find_curve_generator() { + let mut trial_bytes = [0; 32]; + for _ in 0..255 { + let a = AffinePoint::from_bytes(trial_bytes); + if a.is_some().unwrap_u8() == 1 { + let a = a.unwrap(); + assert!(a.is_on_curve_vartime()); + let b = ExtendedPoint::from(a); + let b = b.multiply(&FR_MODULUS_BYTES); + assert!(b.is_small_order().unwrap_u8() == 1); + let b = b.double(); + assert!(b.is_small_order().unwrap_u8() == 1); + let b = b.double(); + assert!(b.is_small_order().unwrap_u8() == 1); + if b.is_identity().unwrap_u8() == 0 { + let b = b.double(); + assert!(b.is_small_order().unwrap_u8() == 1); + assert!(b.is_identity().unwrap_u8() == 1); + assert_eq!(FULL_GENERATOR, a); + assert!(a.mul_by_cofactor().is_torsion_free().unwrap_u8() == 1); + return; + } + } + + trial_bytes[0] += 1; + } + + panic!("should have found a generator of the curve"); +} + +#[test] +fn test_small_order() { + for point in EIGHT_TORSION.iter() { + assert!(point.is_small_order().unwrap_u8() == 1); + } +} + +#[test] +fn test_is_identity() { + let a = EIGHT_TORSION[0].mul_by_cofactor(); + let b = EIGHT_TORSION[1].mul_by_cofactor(); + + assert_eq!(a.u, b.u); + assert_eq!(a.v, a.z); + assert_eq!(b.v, b.z); + assert!(a.v != b.v); + assert!(a.z != b.z); + + assert!(a.is_identity().unwrap_u8() == 1); + assert!(b.is_identity().unwrap_u8() == 1); + + for point in EIGHT_TORSION.iter() { + assert!(point.mul_by_cofactor().is_identity().unwrap_u8() == 1); + } +} + +#[test] +fn test_mul_consistency() { + let a = Fr([ + 0x21e6_1211_d993_4f2e, + 0xa52c_058a_693c_3e07, + 0x9ccb_77bf_b12d_6360, + 0x07df_2470_ec94_398e, + ]); + let b = Fr([ + 0x0333_6d1c_be19_dbe0, + 0x0153_618f_6156_a536, + 0x2604_c9e1_fc3c_6b15, + 0x04ae_581c_eb02_8720, + ]); + let c = Fr([ + 0xd7ab_f5bb_2468_3f4c, + 0x9d77_12cc_274b_7c03, + 0x9732_93db_9683_789f, + 0x0b67_7e29_380a_97a7, + ]); + assert_eq!(a * b, c); + let p = ExtendedPoint::from(AffinePoint { + u: Fq::from_raw([ + 0x81c5_71e5_d883_cfb0, + 0x049f_7a68_6f14_7029, + 0xf539_c860_bc3e_a21f, + 0x4284_715b_7ccc_8162, + ]), + v: Fq::from_raw([ + 0xbf09_6275_684b_b8ca, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + assert_eq!(p * c, (p * a) * b); + + // Test Mul implemented on ExtendedNielsPoint + assert_eq!(p * c, (p.to_niels() * a) * b); + assert_eq!(p.to_niels() * c, (p * a) * b); + assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); + + // Test Mul implemented on AffineNielsPoint + let p_affine_niels = AffinePoint::from(p).to_niels(); + assert_eq!(p * c, (p_affine_niels * a) * b); + assert_eq!(p_affine_niels * c, (p * a) * b); + assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); +} + +#[test] +fn test_serialization_consistency() { + let gen = FULL_GENERATOR.mul_by_cofactor(); + let mut p = gen; + + let v = vec![ + [ + 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, + 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, + ], + [ + 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, + 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, + ], + [ + 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, + 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, + ], + [ + 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, + 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, + ], + [ + 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, + 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, + ], + [ + 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, + 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, + ], + [ + 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, + 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, + ], + [ + 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, + 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, + ], + [ + 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, + 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, + ], + [ + 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, + 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, + ], + [ + 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, + 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, + ], + [ + 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, + 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, + ], + [ + 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, + 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, + ], + [ + 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, + 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, + ], + [ + 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, + 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, + ], + [ + 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, + 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, + ], + ]; + + for expected_serialized in v { + assert!(p.is_on_curve_vartime()); + let affine = AffinePoint::from(p); + let serialized = affine.to_bytes(); + let deserialized = AffinePoint::from_bytes(serialized).unwrap(); + assert_eq!(affine, deserialized); + assert_eq!(expected_serialized, serialized); + p += gen; + } +} diff --git a/jubjub/src/util.rs b/jubjub/src/util.rs new file mode 100644 index 0000000..bd25dd0 --- /dev/null +++ b/jubjub/src/util.rs @@ -0,0 +1,174 @@ +/// Compute a + b + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + (b as u128) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +#[inline(always)] +pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c) + carry, returning the result and the new carry over. +#[inline(always)] +pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +macro_rules! impl_add_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Add<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: &'b $rhs) -> $output { + &self + rhs + } + } + + impl<'a> Add<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + self + &rhs + } + } + + impl Add<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + &self + &rhs + } + } + }; +} + +macro_rules! impl_sub_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Sub<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: &'b $rhs) -> $output { + &self - rhs + } + } + + impl<'a> Sub<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + self - &rhs + } + } + + impl Sub<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + &self - &rhs + } + } + }; +} + +macro_rules! impl_binops_additive_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl_add_binop_specify_output!($lhs, $rhs, $output); + impl_sub_binop_specify_output!($lhs, $rhs, $output); + }; +} + +macro_rules! impl_binops_multiplicative_mixed { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> Mul<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: &'b $rhs) -> $output { + &self * rhs + } + } + + impl<'a> Mul<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + self * &rhs + } + } + + impl Mul<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + &self * &rhs + } + } + }; +} + +macro_rules! impl_binops_additive { + ($lhs:ident, $rhs:ident) => { + impl_binops_additive_specify_output!($lhs, $rhs, $lhs); + + impl SubAssign<$rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: $rhs) { + *self = &*self - &rhs; + } + } + + impl AddAssign<$rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: $rhs) { + *self = &*self + &rhs; + } + } + + impl<'b> SubAssign<&'b $rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: &'b $rhs) { + *self = &*self - rhs; + } + } + + impl<'b> AddAssign<&'b $rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: &'b $rhs) { + *self = &*self + rhs; + } + } + }; +} + +macro_rules! impl_binops_multiplicative { + ($lhs:ident, $rhs:ident) => { + impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); + + impl MulAssign<$rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: $rhs) { + *self = &*self * &rhs; + } + } + + impl<'b> MulAssign<&'b $rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: &'b $rhs) { + *self = &*self * rhs; + } + } + }; +} diff --git a/jubjub/tests/common.rs b/jubjub/tests/common.rs new file mode 100644 index 0000000..a4535ed --- /dev/null +++ b/jubjub/tests/common.rs @@ -0,0 +1,29 @@ +use jubjub::*; +use rand_core::{RngCore, SeedableRng}; +use rand_xorshift::XorShiftRng; + +pub const NUM_BLACK_BOX_CHECKS: u32 = 2000; + +pub fn new_rng() -> XorShiftRng { + XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) +} + +pub trait MyRandom { + fn new_random(rng: &mut T) -> Self; +} + +impl MyRandom for Fq { + fn new_random(rng: &mut T) -> Self { + let mut random_bytes = [0u8; 64]; + rng.fill_bytes(&mut random_bytes); + Fq::from_bytes_wide(&random_bytes) + } +} + +impl MyRandom for Fr { + fn new_random(rng: &mut T) -> Self { + let mut random_bytes = [0u8; 64]; + rng.fill_bytes(&mut random_bytes); + Fr::from_bytes_wide(&random_bytes) + } +} diff --git a/jubjub/tests/fq_blackbox.rs b/jubjub/tests/fq_blackbox.rs new file mode 100644 index 0000000..a823c9b --- /dev/null +++ b/jubjub/tests/fq_blackbox.rs @@ -0,0 +1,120 @@ +mod common; + +use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; +use jubjub::*; + +#[test] +fn test_to_and_from_bytes() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap()); + } +} + +#[test] +fn test_additive_associativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + let b = Fq::new_random(&mut rng); + let c = Fq::new_random(&mut rng); + assert_eq!((a + b) + c, a + (b + c)) + } +} + +#[test] +fn test_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + assert_eq!(a, a + Fq::zero()); + assert_eq!(a, Fq::zero() + a); + } +} + +#[test] +fn test_subtract_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + assert_eq!(a, a - Fq::zero()); + assert_eq!(a, Fq::zero() - -&a); + } +} + +#[test] +fn test_additive_inverse() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + let a_neg = -&a; + assert_eq!(Fq::zero(), a + a_neg); + assert_eq!(Fq::zero(), a_neg + a); + } +} + +#[test] +fn test_additive_commutativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + let b = Fq::new_random(&mut rng); + assert_eq!(a + b, b + a); + } +} + +#[test] +fn test_multiplicative_associativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + let b = Fq::new_random(&mut rng); + let c = Fq::new_random(&mut rng); + assert_eq!((a * b) * c, a * (b * c)) + } +} + +#[test] +fn test_multiplicative_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + assert_eq!(a, a * Fq::one()); + assert_eq!(a, Fq::one() * a); + } +} + +#[test] +fn test_multiplicative_inverse() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + if a == Fq::zero() { + continue; + } + let a_inv = a.invert().unwrap(); + assert_eq!(Fq::one(), a * a_inv); + assert_eq!(Fq::one(), a_inv * a); + } +} + +#[test] +fn test_multiplicative_commutativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + let b = Fq::new_random(&mut rng); + assert_eq!(a * b, b * a); + } +} + +#[test] +fn test_multiply_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fq::new_random(&mut rng); + assert_eq!(Fq::zero(), Fq::zero() * a); + assert_eq!(Fq::zero(), a * Fq::zero()); + } +} diff --git a/jubjub/tests/fr_blackbox.rs b/jubjub/tests/fr_blackbox.rs new file mode 100644 index 0000000..6e36d0f --- /dev/null +++ b/jubjub/tests/fr_blackbox.rs @@ -0,0 +1,120 @@ +mod common; + +use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; +use jubjub::*; + +#[test] +fn test_to_and_from_bytes() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap()); + } +} + +#[test] +fn test_additive_associativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + let b = Fr::new_random(&mut rng); + let c = Fr::new_random(&mut rng); + assert_eq!((a + b) + c, a + (b + c)) + } +} + +#[test] +fn test_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + assert_eq!(a, a + Fr::zero()); + assert_eq!(a, Fr::zero() + a); + } +} + +#[test] +fn test_subtract_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + assert_eq!(a, a - Fr::zero()); + assert_eq!(a, Fr::zero() - -&a); + } +} + +#[test] +fn test_additive_inverse() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + let a_neg = -&a; + assert_eq!(Fr::zero(), a + a_neg); + assert_eq!(Fr::zero(), a_neg + a); + } +} + +#[test] +fn test_additive_commutativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + let b = Fr::new_random(&mut rng); + assert_eq!(a + b, b + a); + } +} + +#[test] +fn test_multiplicative_associativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + let b = Fr::new_random(&mut rng); + let c = Fr::new_random(&mut rng); + assert_eq!((a * b) * c, a * (b * c)) + } +} + +#[test] +fn test_multiplicative_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + assert_eq!(a, a * Fr::one()); + assert_eq!(a, Fr::one() * a); + } +} + +#[test] +fn test_multiplicative_inverse() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + if a == Fr::zero() { + continue; + } + let a_inv = a.invert().unwrap(); + assert_eq!(Fr::one(), a * a_inv); + assert_eq!(Fr::one(), a_inv * a); + } +} + +#[test] +fn test_multiplicative_commutativity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + let b = Fr::new_random(&mut rng); + assert_eq!(a * b, b * a); + } +} + +#[test] +fn test_multiply_additive_identity() { + let mut rng = new_rng(); + for _ in 0..NUM_BLACK_BOX_CHECKS { + let a = Fr::new_random(&mut rng); + assert_eq!(Fr::zero(), Fr::zero() * a); + assert_eq!(Fr::zero(), a * Fr::zero()); + } +} diff --git a/librustzcash/Cargo.toml b/librustzcash/Cargo.toml deleted file mode 100644 index 3256d14..0000000 --- a/librustzcash/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "librustzcash" -version = "0.1.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", - "Jay Graber ", - "Simon Liu " - ] - -[lib] -name = "rustzcash" -path = "src/rustzcash.rs" -crate-type = ["staticlib"] - -[dependencies] -bellman = { path = "../bellman" } -blake2b_simd = "0.5" -blake2s_simd = "0.5" -ff = { path = "../ff" } -libc = "0.2" -pairing = { path = "../pairing" } -lazy_static = "1" -byteorder = "1" -rand_core = "0.5" -rand_os = "0.2" -zcash_primitives = { path = "../zcash_primitives" } -zcash_proofs = { path = "../zcash_proofs" } diff --git a/librustzcash/README.md b/librustzcash/README.md index c21ca8e..25b9631 100644 --- a/librustzcash/README.md +++ b/librustzcash/README.md @@ -1,20 +1,3 @@ # librustzcash -This repository contains librustzcash, a static library for Zcash code assets written in Rust. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. - +This crate has been moved into https://github.com/zcash/zcash. diff --git a/librustzcash/include/librustzcash.h b/librustzcash/include/librustzcash.h deleted file mode 100644 index 4efb544..0000000 --- a/librustzcash/include/librustzcash.h +++ /dev/null @@ -1,313 +0,0 @@ -#ifndef LIBRUSTZCASH_INCLUDE_H_ -#define LIBRUSTZCASH_INCLUDE_H_ - -#include - -extern "C" { -#ifdef WIN32 - typedef uint16_t codeunit; -#else - typedef uint8_t codeunit; -#endif - - void librustzcash_to_scalar(const unsigned char *input, unsigned char *result); - - void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result); - - void librustzcash_nsk_to_nk(const unsigned char *nsk, unsigned char *result); - - void librustzcash_crh_ivk(const unsigned char *ak, const unsigned char *nk, unsigned char *result); - - bool librustzcash_check_diversifier(const unsigned char *diversifier); - - bool librustzcash_ivk_to_pkd(const unsigned char *ivk, const unsigned char *diversifier, unsigned char *result); - - /// Loads the zk-SNARK parameters into memory and saves - /// paths as necessary. Only called once. - void librustzcash_init_zksnark_params( - const codeunit* spend_path, - size_t spend_path_len, - const char* spend_hash, - const codeunit* output_path, - size_t output_path_len, - const char* output_hash, - const codeunit* sprout_path, - size_t sprout_path_len, - const char* sprout_hash - ); - - /// Validates the provided Equihash solution against - /// the given parameters, input and nonce. - bool librustzcash_eh_isvalid( - uint32_t n, - uint32_t k, - const unsigned char* input, - size_t input_len, - const unsigned char* nonce, - size_t nonce_len, - const unsigned char* soln, - size_t soln_len - ); - - /// Writes the "uncommitted" note value for empty leaves - /// of the merkle tree. `result` must be a valid pointer - /// to 32 bytes which will be written. - void librustzcash_tree_uncommitted( - unsigned char *result - ); - - /// Computes a merkle tree hash for a given depth. - /// The `depth` parameter should not be larger than - /// 62. - /// - /// `a` and `b` each must be of length 32, and must each - /// be scalars of BLS12-381. - /// - /// The result of the merkle tree hash is placed in - /// `result`, which must also be of length 32. - void librustzcash_merkle_hash( - size_t depth, - const unsigned char *a, - const unsigned char *b, - unsigned char *result - ); - - /// Computes the signature for each Spend description, given the key - /// `ask`, the re-randomization `ar`, the 32-byte sighash `sighash`, - /// and an output `result` buffer of 64-bytes for the signature. - /// - /// This function will fail if the provided `ask` or `ar` are invalid. - bool librustzcash_sapling_spend_sig( - const unsigned char *ask, - const unsigned char *ar, - const unsigned char *sighash, - unsigned char *result - ); - - /// Creates a Sapling proving context. Please free this when you're done. - void * librustzcash_sapling_proving_ctx_init(); - - /// This function (using the proving context) constructs a Spend proof - /// given the necessary witness information. It outputs `cv` (the value - /// commitment) and `rk` (so that you don't have to compute it) along - /// with the proof. - bool librustzcash_sapling_spend_proof( - void *ctx, - const unsigned char *ak, - const unsigned char *nsk, - const unsigned char *diversifier, - const unsigned char *rcm, - const unsigned char *ar, - const uint64_t value, - const unsigned char *anchor, - const unsigned char *witness, - unsigned char *cv, - unsigned char *rk, - unsigned char *zkproof - ); - - /// This function (using the proving context) constructs an Output - /// proof given the necessary witness information. It outputs `cv` - /// and the `zkproof`. - bool librustzcash_sapling_output_proof( - void *ctx, - const unsigned char *esk, - const unsigned char *diversifier, - const unsigned char *pk_d, - const unsigned char *rcm, - const uint64_t value, - unsigned char *cv, - unsigned char *zkproof - ); - - /// This function (using the proving context) constructs a binding - /// signature. You must provide the intended valueBalance so that - /// we can internally check consistency. - bool librustzcash_sapling_binding_sig( - const void *ctx, - int64_t valueBalance, - const unsigned char *sighash, - unsigned char *result - ); - - /// Frees a Sapling proving context returned from - /// `librustzcash_sapling_proving_ctx_init`. - void librustzcash_sapling_proving_ctx_free(void *); - - /// Creates a Sapling verification context. Please free this - /// when you're done. - void * librustzcash_sapling_verification_ctx_init(); - - /// Check the validity of a Sapling Spend description, - /// accumulating the value commitment into the context. - bool librustzcash_sapling_check_spend( - void *ctx, - const unsigned char *cv, - const unsigned char *anchor, - const unsigned char *nullifier, - const unsigned char *rk, - const unsigned char *zkproof, - const unsigned char *spendAuthSig, - const unsigned char *sighashValue - ); - - /// Check the validity of a Sapling Output description, - /// accumulating the value commitment into the context. - bool librustzcash_sapling_check_output( - void *ctx, - const unsigned char *cv, - const unsigned char *cm, - const unsigned char *ephemeralKey, - const unsigned char *zkproof - ); - - /// Finally checks the validity of the entire Sapling - /// transaction given valueBalance and the binding signature. - bool librustzcash_sapling_final_check( - void *ctx, - int64_t valueBalance, - const unsigned char *bindingSig, - const unsigned char *sighashValue - ); - - /// Frees a Sapling verification context returned from - /// `librustzcash_sapling_verification_ctx_init`. - void librustzcash_sapling_verification_ctx_free(void *); - - /// Compute a Sapling nullifier. - /// - /// The `diversifier` parameter must be 11 bytes in length. - /// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32. - /// The result is also of length 32 and placed in `result`. - /// Returns false if the diversifier or pk_d is not valid - bool librustzcash_sapling_compute_nf( - const unsigned char *diversifier, - const unsigned char *pk_d, - const uint64_t value, - const unsigned char *r, - const unsigned char *ak, - const unsigned char *nk, - const uint64_t position, - unsigned char *result - ); - - /// Compute a Sapling commitment. - /// - /// The `diversifier` parameter must be 11 bytes in length. - /// The `pk_d` and `r` parameters must be of length 32. - /// The result is also of length 32 and placed in `result`. - /// Returns false if the diversifier or pk_d is not valid - bool librustzcash_sapling_compute_cm( - const unsigned char *diversifier, - const unsigned char *pk_d, - const uint64_t value, - const unsigned char *r, - unsigned char *result - ); - - /// Compute [sk] [8] P for some 32-byte - /// point P, and 32-byte Fs. If P or sk - /// are invalid, returns false. Otherwise, - /// the result is written to the 32-byte - /// `result` buffer. - bool librustzcash_sapling_ka_agree( - const unsigned char *p, - const unsigned char *sk, - unsigned char *result - ); - - /// Compute g_d = GH(diversifier) and returns - /// false if the diversifier is invalid. - /// Computes [esk] g_d and writes the result - /// to the 32-byte `result` buffer. Returns - /// false if `esk` is not a valid scalar. - bool librustzcash_sapling_ka_derivepublic( - const unsigned char *diversifier, - const unsigned char *esk, - unsigned char *result - ); - - /// Generate uniformly random scalar in Jubjub. - /// The result is of length 32. - void librustzcash_sapling_generate_r( - unsigned char *result - ); - - /// Sprout JoinSplit proof generation. - void librustzcash_sprout_prove( - unsigned char *proof_out, - - const unsigned char *phi, - const unsigned char *rt, - const unsigned char *h_sig, - - const unsigned char *in_sk1, - uint64_t in_value1, - const unsigned char *in_rho1, - const unsigned char *in_r1, - const unsigned char *in_auth1, - - const unsigned char *in_sk2, - uint64_t in_value2, - const unsigned char *in_rho2, - const unsigned char *in_r2, - const unsigned char *in_auth2, - - const unsigned char *out_pk1, - uint64_t out_value1, - const unsigned char *out_r1, - - const unsigned char *out_pk2, - uint64_t out_value2, - const unsigned char *out_r2, - - uint64_t vpub_old, - uint64_t vpub_new - ); - - /// Sprout JoinSplit proof verification. - bool librustzcash_sprout_verify( - const unsigned char *proof, - const unsigned char *rt, - const unsigned char *h_sig, - const unsigned char *mac1, - const unsigned char *mac2, - const unsigned char *nf1, - const unsigned char *nf2, - const unsigned char *cm1, - const unsigned char *cm2, - uint64_t vpub_old, - uint64_t vpub_new - ); - - /// Derive the master ExtendedSpendingKey from a seed. - void librustzcash_zip32_xsk_master( - const unsigned char *seed, - size_t seedlen, - unsigned char *xsk_master - ); - - /// Derive a child ExtendedSpendingKey from a parent. - void librustzcash_zip32_xsk_derive( - const unsigned char *xsk_parent, - uint32_t i, - unsigned char *xsk_i - ); - - /// Derive a child ExtendedFullViewingKey from a parent. - bool librustzcash_zip32_xfvk_derive( - const unsigned char *xfvk_parent, - uint32_t i, - unsigned char *xfvk_i - ); - - /// Derive a PaymentAddress from an ExtendedFullViewingKey. - bool librustzcash_zip32_xfvk_address( - const unsigned char *xfvk, - const unsigned char *j, - unsigned char *j_ret, - unsigned char *addr_ret - ); -} - -#endif // LIBRUSTZCASH_INCLUDE_H_ diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs deleted file mode 100644 index 0697272..0000000 --- a/librustzcash/src/rustzcash.rs +++ /dev/null @@ -1,1241 +0,0 @@ -extern crate bellman; -extern crate blake2b_simd; -extern crate blake2s_simd; -extern crate byteorder; -extern crate ff; -extern crate libc; -extern crate pairing; -extern crate rand_core; -extern crate rand_os; -extern crate zcash_primitives; -extern crate zcash_proofs; - -extern crate lazy_static; - -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; - -use zcash_primitives::{ - constants::CRH_IVK_PERSONALIZATION, - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, - }, -}; - -use zcash_proofs::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH; -use zcash_proofs::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; - -use bellman::gadgets::multipack; -use bellman::groth16::{ - create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof, -}; - -use blake2s_simd::Params as Blake2sParams; - -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - -use rand_core::RngCore; -use rand_os::OsRng; -use std::io::BufReader; - -use libc::{c_char, c_uchar, size_t}; -use std::ffi::CStr; -use std::fs::File; -use std::path::{Path, PathBuf}; -use std::slice; - -#[cfg(not(target_os = "windows"))] -use std::ffi::OsStr; -#[cfg(not(target_os = "windows"))] -use std::os::unix::ffi::OsStrExt; - -#[cfg(target_os = "windows")] -use std::ffi::OsString; -#[cfg(target_os = "windows")] -use std::os::windows::ffi::OsStringExt; - -use zcash_primitives::{ - merkle_tree::CommitmentTreeWitness, - note_encryption::sapling_ka_agree, - primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey}, - redjubjub::{self, Signature}, - sapling::{merkle_hash, spend_sig}, - transaction::components::Amount, - zip32, JUBJUB, -}; -use zcash_proofs::{ - load_parameters, - sapling::{SaplingProvingContext, SaplingVerificationContext}, -}; - -pub mod equihash; - -#[cfg(test)] -mod tests; - -static mut SAPLING_SPEND_VK: Option> = None; -static mut SAPLING_OUTPUT_VK: Option> = None; -static mut SPROUT_GROTH16_VK: Option> = None; - -static mut SAPLING_SPEND_PARAMS: Option> = None; -static mut SAPLING_OUTPUT_PARAMS: Option> = None; -static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; - -/// Writes an FrRepr to [u8] of length 32 -fn write_le(f: FrRepr, to: &mut [u8]) { - assert_eq!(to.len(), 32); - - f.write_le(to).expect("length is 32 bytes"); -} - -/// Reads an FrRepr from a [u8] of length 32. -/// This will panic (abort) if length provided is -/// not correct. -fn read_le(from: &[u8]) -> FrRepr { - assert_eq!(from.len(), 32); - - let mut f = FrRepr::default(); - f.read_le(from).expect("length is 32 bytes"); - - f -} - -/// Reads an FsRepr from [u8] of length 32 -/// This will panic (abort) if length provided is -/// not correct -fn read_fs(from: &[u8]) -> FsRepr { - assert_eq!(from.len(), 32); - - let mut f = <::Fs as PrimeField>::Repr::default(); - f.read_le(from).expect("length is 32 bytes"); - - f -} - -/// Reads an FsRepr from [u8] of length 32 -/// and multiplies it by the given base. -/// This will panic (abort) if length provided is -/// not correct -fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point { - let f = read_fs(from); - - JUBJUB.generator(p_g).mul(f, &JUBJUB) -} - -#[cfg(not(target_os = "windows"))] -#[no_mangle] -pub extern "system" fn librustzcash_init_zksnark_params( - spend_path: *const u8, - spend_path_len: usize, - spend_hash: *const c_char, - output_path: *const u8, - output_path_len: usize, - output_hash: *const c_char, - sprout_path: *const u8, - sprout_path_len: usize, - sprout_hash: *const c_char, -) { - let spend_path = Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(spend_path, spend_path_len) - })); - let output_path = Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(output_path, output_path_len) - })); - let sprout_path = Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(sprout_path, sprout_path_len) - })); - - init_zksnark_params( - spend_path, - spend_hash, - output_path, - output_hash, - sprout_path, - sprout_hash, - ) -} - -#[cfg(target_os = "windows")] -#[no_mangle] -pub extern "system" fn librustzcash_init_zksnark_params( - spend_path: *const u16, - spend_path_len: usize, - spend_hash: *const c_char, - output_path: *const u16, - output_path_len: usize, - output_hash: *const c_char, - sprout_path: *const u16, - sprout_path_len: usize, - sprout_hash: *const c_char, -) { - let spend_path = - OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }); - let output_path = - OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }); - let sprout_path = - OsString::from_wide(unsafe { slice::from_raw_parts(sprout_path, sprout_path_len) }); - - init_zksnark_params( - Path::new(&spend_path), - spend_hash, - Path::new(&output_path), - output_hash, - Path::new(&sprout_path), - sprout_hash, - ) -} - -fn init_zksnark_params( - spend_path: &Path, - spend_hash: *const c_char, - output_path: &Path, - output_hash: *const c_char, - sprout_path: &Path, - sprout_hash: *const c_char, -) { - // Initialize jubjub parameters here - lazy_static::initialize(&JUBJUB); - - let spend_hash = unsafe { CStr::from_ptr(spend_hash) } - .to_str() - .expect("hash should be a valid string"); - - let output_hash = unsafe { CStr::from_ptr(output_hash) } - .to_str() - .expect("hash should be a valid string"); - - let sprout_hash = unsafe { CStr::from_ptr(sprout_hash) } - .to_str() - .expect("hash should be a valid string"); - - // Load params - let (spend_params, spend_vk, output_params, output_vk, sprout_vk) = load_parameters( - spend_path, - spend_hash, - output_path, - output_hash, - Some(sprout_path), - Some(sprout_hash), - ); - - // Caller is responsible for calling this function once, so - // these global mutations are safe. - unsafe { - SAPLING_SPEND_PARAMS = Some(spend_params); - SAPLING_OUTPUT_PARAMS = Some(output_params); - SPROUT_GROTH16_PARAMS_PATH = Some(sprout_path.to_owned()); - - SAPLING_SPEND_VK = Some(spend_vk); - SAPLING_OUTPUT_VK = Some(output_vk); - SPROUT_GROTH16_VK = Some(sprout_vk.unwrap()); - } -} - -#[no_mangle] -pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { - let tmp = Note::::uncommitted().into_repr(); - - // Should be okay, caller is responsible for ensuring the pointer - // is a valid pointer to 32 bytes that can be mutated. - let result = unsafe { &mut *result }; - - write_le(tmp, &mut result[..]); -} - -#[no_mangle] -pub extern "system" fn librustzcash_merkle_hash( - depth: size_t, - a: *const [c_uchar; 32], - b: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let a_repr = read_le(unsafe { &(&*a)[..] }); - - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let b_repr = read_le(unsafe { &(&*b)[..] }); - - let tmp = merkle_hash(depth, &a_repr, &b_repr); - - // Should be okay, caller is responsible for ensuring the pointer - // is a valid pointer to 32 bytes that can be mutated. - let result = unsafe { &mut *result }; - - write_le(tmp, &mut result[..]); -} - -#[no_mangle] // ToScalar -pub extern "system" fn librustzcash_to_scalar( - input: *const [c_uchar; 64], - result: *mut [c_uchar; 32], -) { - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let scalar = ::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr(); - - let result = unsafe { &mut *result }; - - scalar - .write_le(&mut result[..]) - .expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_ask_to_ak( - ask: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - let ask = unsafe { &*ask }; - let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator); - - let result = unsafe { &mut *result }; - - ak.write(&mut result[..]).expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_nsk_to_nk( - nsk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - let nsk = unsafe { &*nsk }; - let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey); - - let result = unsafe { &mut *result }; - - nk.write(&mut result[..]).expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_crh_ivk( - ak: *const [c_uchar; 32], - nk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - let ak = unsafe { &*ak }; - let nk = unsafe { &*nk }; - - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(CRH_IVK_PERSONALIZATION) - .to_state(); - h.update(ak); - h.update(nk); - let mut h = h.finalize().as_ref().to_vec(); - - // Drop the last five bits, so it can be interpreted as a scalar. - h[31] &= 0b0000_0111; - - let result = unsafe { &mut *result }; - - result.copy_from_slice(&h); -} - -#[no_mangle] -pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { - let diversifier = Diversifier(unsafe { *diversifier }); - diversifier.g_d::(&JUBJUB).is_some() -} - -#[no_mangle] -pub extern "system" fn librustzcash_ivk_to_pkd( - ivk: *const [c_uchar; 32], - diversifier: *const [c_uchar; 11], - result: *mut [c_uchar; 32], -) -> bool { - let ivk = read_fs(unsafe { &*ivk }); - let diversifier = Diversifier(unsafe { *diversifier }); - if let Some(g_d) = diversifier.g_d::(&JUBJUB) { - let pk_d = g_d.mul(ivk, &JUBJUB); - - let result = unsafe { &mut *result }; - - pk_d.write(&mut result[..]).expect("length is 32 bytes"); - - true - } else { - false - } -} - -/// Test generation of commitment randomness -#[test] -fn test_gen_r() { - let mut r1 = [0u8; 32]; - let mut r2 = [0u8; 32]; - - // Verify different r values are generated - librustzcash_sapling_generate_r(&mut r1); - librustzcash_sapling_generate_r(&mut r2); - assert_ne!(r1, r2); - - // Verify r values are valid in the field - let mut repr = FsRepr::default(); - repr.read_le(&r1[..]).expect("length is not 32 bytes"); - let _ = Fs::from_repr(repr).unwrap(); - repr.read_le(&r2[..]).expect("length is not 32 bytes"); - let _ = Fs::from_repr(repr).unwrap(); -} - -/// Return 32 byte random scalar, uniformly. -#[no_mangle] -pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { - // create random 64 byte buffer - let mut rng = OsRng; - let mut buffer = [0u8; 64]; - rng.fill_bytes(&mut buffer); - - // reduce to uniform value - let r = ::Fs::to_uniform(&buffer[..]); - let result = unsafe { &mut *result }; - r.into_repr() - .write_le(&mut result[..]) - .expect("result must be 32 bytes"); -} - -// Private utility function to get Note from C parameters -fn priv_get_note( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], -) -> Result, ()> { - let diversifier = Diversifier(unsafe { *diversifier }); - let g_d = match diversifier.g_d::(&JUBJUB) { - Some(g_d) => g_d, - None => return Err(()), - }; - - let pk_d = match edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return Err(()), - }; - - let pk_d = match pk_d.as_prime_order(&JUBJUB) { - Some(pk_d) => pk_d, - None => return Err(()), - }; - - // Deserialize randomness - let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) { - Ok(r) => r, - Err(_) => return Err(()), - }; - - let note = Note { - value, - g_d, - pk_d, - r, - }; - - Ok(note) -} - -/// Compute Sapling note nullifier. -#[no_mangle] -pub extern "system" fn librustzcash_sapling_compute_nf( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], - ak: *const [c_uchar; 32], - nk: *const [c_uchar; 32], - position: u64, - result: *mut [c_uchar; 32], -) -> bool { - let note = match priv_get_note(diversifier, pk_d, value, r) { - Ok(p) => p, - Err(_) => return false, - }; - - let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - let ak = match ak.as_prime_order(&JUBJUB) { - Some(ak) => ak, - None => return false, - }; - - let nk = match edwards::Point::::read(&(unsafe { &*nk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - let nk = match nk.as_prime_order(&JUBJUB) { - Some(nk) => nk, - None => return false, - }; - - let vk = ViewingKey { ak, nk }; - let nf = note.nf(&vk, position, &JUBJUB); - let result = unsafe { &mut *result }; - result.copy_from_slice(&nf); - - true -} - -/// Compute Sapling note commitment. -#[no_mangle] -pub extern "system" fn librustzcash_sapling_compute_cm( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - let note = match priv_get_note(diversifier, pk_d, value, r) { - Ok(p) => p, - Err(_) => return false, - }; - - let result = unsafe { &mut *result }; - write_le(note.cm(&JUBJUB).into_repr(), &mut result[..]); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_ka_agree( - p: *const [c_uchar; 32], - sk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - // Deserialize p - let p = match edwards::Point::::read(&(unsafe { &*p })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize sk - let sk = match Fs::from_repr(read_fs(&(unsafe { &*sk })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // Compute key agreement - let ka = sapling_ka_agree(&sk, &p); - - // Produce result - let result = unsafe { &mut *result }; - ka.write(&mut result[..]).expect("length is not 32 bytes"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_ka_derivepublic( - diversifier: *const [c_uchar; 11], - esk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - let diversifier = Diversifier(unsafe { *diversifier }); - - // Compute g_d from the diversifier - let g_d = match diversifier.g_d::(&JUBJUB) { - Some(g) => g, - None => return false, - }; - - // Deserialize esk - let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - let p = g_d.mul(esk, &JUBJUB); - - let result = unsafe { &mut *result }; - p.write(&mut result[..]).expect("length is not 32 bytes"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_eh_isvalid( - n: u32, - k: u32, - input: *const c_uchar, - input_len: size_t, - nonce: *const c_uchar, - nonce_len: size_t, - soln: *const c_uchar, - soln_len: size_t, -) -> bool { - if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) { - return false; - } - let rs_input = unsafe { slice::from_raw_parts(input, input_len) }; - let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) }; - let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) }; - equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_verification_ctx_init( -) -> *mut SaplingVerificationContext { - let ctx = Box::new(SaplingVerificationContext::new()); - - Box::into_raw(ctx) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_verification_ctx_free( - ctx: *mut SaplingVerificationContext, -) { - drop(unsafe { Box::from_raw(ctx) }); -} - -const GROTH_PROOF_SIZE: usize = 48 // π_A - + 96 // π_B - + 48; // π_C - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_check_spend( - ctx: *mut SaplingVerificationContext, - cv: *const [c_uchar; 32], - anchor: *const [c_uchar; 32], - nullifier: *const [c_uchar; 32], - rk: *const [c_uchar; 32], - zkproof: *const [c_uchar; GROTH_PROOF_SIZE], - spend_auth_sig: *const [c_uchar; 64], - sighash_value: *const [c_uchar; 32], -) -> bool { - // Deserialize the value commitment - let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the anchor, which should be an element - // of Fr. - let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) { - Ok(a) => a, - Err(_) => return false, - }; - - // Deserialize rk - let rk = match redjubjub::PublicKey::::read(&(unsafe { &*rk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the signature - let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; - - // Deserialize the proof - let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - unsafe { &mut *ctx }.check_spend( - cv, - anchor, - unsafe { &*nullifier }, - rk, - unsafe { &*sighash_value }, - spend_auth_sig, - zkproof, - unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - &JUBJUB, - ) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_check_output( - ctx: *mut SaplingVerificationContext, - cv: *const [c_uchar; 32], - cm: *const [c_uchar; 32], - epk: *const [c_uchar; 32], - zkproof: *const [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Deserialize the value commitment - let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the commitment, which should be an element - // of Fr. - let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) { - Ok(a) => a, - Err(_) => return false, - }; - - // Deserialize the ephemeral key - let epk = match edwards::Point::::read(&(unsafe { &*epk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the proof - let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - unsafe { &mut *ctx }.check_output( - cv, - cm, - epk, - zkproof, - unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), - &JUBJUB, - ) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_final_check( - ctx: *mut SaplingVerificationContext, - value_balance: i64, - binding_sig: *const [c_uchar; 64], - sighash_value: *const [c_uchar; 32], -) -> bool { - let value_balance = match Amount::from_i64(value_balance) { - Ok(vb) => vb, - Err(()) => return false, - }; - - // Deserialize the signature - let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; - - unsafe { &*ctx }.final_check( - value_balance, - unsafe { &*sighash_value }, - binding_sig, - &JUBJUB, - ) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sprout_prove( - proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], - - phi: *const [c_uchar; 32], - rt: *const [c_uchar; 32], - h_sig: *const [c_uchar; 32], - - // First input - in_sk1: *const [c_uchar; 32], - in_value1: u64, - in_rho1: *const [c_uchar; 32], - in_r1: *const [c_uchar; 32], - in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], - - // Second input - in_sk2: *const [c_uchar; 32], - in_value2: u64, - in_rho2: *const [c_uchar; 32], - in_r2: *const [c_uchar; 32], - in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], - - // First output - out_pk1: *const [c_uchar; 32], - out_value1: u64, - out_r1: *const [c_uchar; 32], - - // Second output - out_pk2: *const [c_uchar; 32], - out_value2: u64, - out_r2: *const [c_uchar; 32], - - // Public value - vpub_old: u64, - vpub_new: u64, -) { - let phi = unsafe { *phi }; - let rt = unsafe { *rt }; - let h_sig = unsafe { *h_sig }; - let in_sk1 = unsafe { *in_sk1 }; - let in_rho1 = unsafe { *in_rho1 }; - let in_r1 = unsafe { *in_r1 }; - let in_auth1 = unsafe { *in_auth1 }; - let in_sk2 = unsafe { *in_sk2 }; - let in_rho2 = unsafe { *in_rho2 }; - let in_r2 = unsafe { *in_r2 }; - let in_auth2 = unsafe { *in_auth2 }; - let out_pk1 = unsafe { *out_pk1 }; - let out_r1 = unsafe { *out_r1 }; - let out_pk2 = unsafe { *out_pk2 }; - let out_r2 = unsafe { *out_r2 }; - - let mut inputs = Vec::with_capacity(2); - { - let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| { - let value = Some(value); - let rho = Some(sprout::UniqueRandomness(rho)); - let r = Some(sprout::CommitmentRandomness(r)); - let a_sk = Some(sprout::SpendingKey(sk)); - - // skip the first byte - assert_eq!(auth[0], SPROUT_TREE_DEPTH as u8); - auth = &auth[1..]; - - let mut auth_path = [None; SPROUT_TREE_DEPTH]; - for i in (0..SPROUT_TREE_DEPTH).rev() { - // skip length of inner vector - assert_eq!(auth[0], 32); - auth = &auth[1..]; - - let mut sibling = [0u8; 32]; - sibling.copy_from_slice(&auth[0..32]); - auth = &auth[32..]; - - auth_path[i] = Some((sibling, false)); - } - - let mut position = auth - .read_u64::() - .expect("should have had index at the end"); - - for i in 0..SPROUT_TREE_DEPTH { - auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); - - position >>= 1; - } - - inputs.push(sprout::JSInput { - value: value, - a_sk: a_sk, - rho: rho, - r: r, - auth_path: auth_path, - }); - }; - - handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]); - handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]); - } - - let mut outputs = Vec::with_capacity(2); - { - let mut handle_output = |a_pk, value, r| { - outputs.push(sprout::JSOutput { - value: Some(value), - a_pk: Some(sprout::PayingKey(a_pk)), - r: Some(sprout::CommitmentRandomness(r)), - }); - }; - - handle_output(out_pk1, out_value1, out_r1); - handle_output(out_pk2, out_value2, out_r2); - } - - let js = sprout::JoinSplit { - vpub_old: Some(vpub_old), - vpub_new: Some(vpub_new), - h_sig: Some(h_sig), - phi: Some(phi), - inputs: inputs, - outputs: outputs, - rt: Some(rt), - }; - - // Load parameters from disk - let sprout_fs = File::open( - unsafe { &SPROUT_GROTH16_PARAMS_PATH } - .as_ref() - .expect("parameters should have been initialized"), - ) - .expect("couldn't load Sprout groth16 parameters file"); - - let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); - - let params = Parameters::::read(&mut sprout_fs, false) - .expect("couldn't deserialize Sprout JoinSplit parameters file"); - - drop(sprout_fs); - - // Initialize secure RNG - let mut rng = OsRng; - - let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail"); - - proof - .write(&mut (unsafe { &mut *proof_out })[..]) - .expect("should be able to serialize a proof"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_sprout_verify( - proof: *const [c_uchar; GROTH_PROOF_SIZE], - rt: *const [c_uchar; 32], - h_sig: *const [c_uchar; 32], - mac1: *const [c_uchar; 32], - mac2: *const [c_uchar; 32], - nf1: *const [c_uchar; 32], - nf2: *const [c_uchar; 32], - cm1: *const [c_uchar; 32], - cm2: *const [c_uchar; 32], - vpub_old: u64, - vpub_new: u64, -) -> bool { - // Prepare the public input for the verifier - let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); - public_input.extend(unsafe { &(&*rt)[..] }); - public_input.extend(unsafe { &(&*h_sig)[..] }); - public_input.extend(unsafe { &(&*nf1)[..] }); - public_input.extend(unsafe { &(&*mac1)[..] }); - public_input.extend(unsafe { &(&*nf2)[..] }); - public_input.extend(unsafe { &(&*mac2)[..] }); - public_input.extend(unsafe { &(&*cm1)[..] }); - public_input.extend(unsafe { &(&*cm2)[..] }); - public_input.write_u64::(vpub_old).unwrap(); - public_input.write_u64::(vpub_new).unwrap(); - - let public_input = multipack::bytes_to_bits(&public_input); - let public_input = multipack::compute_multipacking::(&public_input); - - let proof = match Proof::read(unsafe { &(&*proof)[..] }) { - Ok(p) => p, - Err(_) => return false, - }; - - // Verify the proof - match verify_proof( - unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), - &proof, - &public_input[..], - ) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_output_proof( - ctx: *mut SaplingProvingContext, - esk: *const [c_uchar; 32], - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - rcm: *const [c_uchar; 32], - value: u64, - cv: *mut [c_uchar; 32], - zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Grab `esk`, which the caller should have constructed for the DH key exchange. - let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // Grab the diversifier from the caller. - let diversifier = Diversifier(unsafe { *diversifier }); - - // Grab pk_d from the caller. - let pk_d = match edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // pk_d should be prime order. - let pk_d = match pk_d.as_prime_order(&JUBJUB) { - Some(p) => p, - None => return false, - }; - - // Construct a payment address - let payment_address = PaymentAddress { - pk_d: pk_d, - diversifier: diversifier, - }; - - // The caller provides the commitment randomness for the output note - let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // Create proof - let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof( - esk, - payment_address, - rcm, - value, - unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(), - &JUBJUB, - ); - - // Write the proof out to the caller - proof - .write(&mut (unsafe { &mut *zkproof })[..]) - .expect("should be able to serialize a proof"); - - // Write the value commitment to the caller - value_commitment - .write(&mut (unsafe { &mut *cv })[..]) - .expect("should be able to serialize rcv"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_spend_sig( - ask: *const [c_uchar; 32], - ar: *const [c_uchar; 32], - sighash: *const [c_uchar; 32], - result: *mut [c_uchar; 64], -) -> bool { - // The caller provides the re-randomization of `ak`. - let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // The caller provides `ask`, the spend authorizing key. - let ask = match redjubjub::PrivateKey::::read(&(unsafe { &*ask })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - // Initialize secure RNG - let mut rng = OsRng; - - // Do the signing - let sig = spend_sig(ask, ar, unsafe { &*sighash }, &mut rng, &JUBJUB); - - // Write out the signature - sig.write(&mut (unsafe { &mut *result })[..]) - .expect("result should be 64 bytes"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_binding_sig( - ctx: *const SaplingProvingContext, - value_balance: i64, - sighash: *const [c_uchar; 32], - result: *mut [c_uchar; 64], -) -> bool { - let value_balance = match Amount::from_i64(value_balance) { - Ok(vb) => vb, - Err(()) => return false, - }; - - // Sign - let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) { - Ok(s) => s, - Err(_) => return false, - }; - - // Write out signature - sig.write(&mut (unsafe { &mut *result })[..]) - .expect("result should be 64 bytes"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_spend_proof( - ctx: *mut SaplingProvingContext, - ak: *const [c_uchar; 32], - nsk: *const [c_uchar; 32], - diversifier: *const [c_uchar; 11], - rcm: *const [c_uchar; 32], - ar: *const [c_uchar; 32], - value: u64, - anchor: *const [c_uchar; 32], - witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], - cv: *mut [c_uchar; 32], - rk_out: *mut [c_uchar; 32], - zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Grab `ak` from the caller, which should be a point. - let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // `ak` should be prime order. - let ak = match ak.as_prime_order(&JUBJUB) { - Some(p) => p, - None => return false, - }; - - // Grab `nsk` from the caller - let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // Construct the proof generation key - let proof_generation_key = ProofGenerationKey { - ak: ak.clone(), - nsk, - }; - - // Grab the diversifier from the caller - let diversifier = Diversifier(unsafe { *diversifier }); - - // The caller chooses the note randomness - let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // The caller also chooses the re-randomization of ak - let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { - Ok(p) => p, - Err(_) => return false, - }; - - // We need to compute the anchor of the Spend. - let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) { - Ok(p) => p, - Err(_) => return false, - }; - - // The witness contains the incremental tree witness information, in a - // weird serialized format. - let witness = match CommitmentTreeWitness::from_slice(unsafe { &(&*witness)[..] }) { - Ok(w) => w, - Err(_) => return false, - }; - - // Create proof - let (proof, value_commitment, rk) = unsafe { &mut *ctx } - .spend_proof( - proof_generation_key, - diversifier, - rcm, - ar, - value, - anchor, - witness, - unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(), - unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - &JUBJUB, - ) - .expect("proving should not fail"); - - // Write value commitment to caller - value_commitment - .write(&mut unsafe { &mut *cv }[..]) - .expect("should be able to serialize cv"); - - // Write proof out to caller - proof - .write(&mut (unsafe { &mut *zkproof })[..]) - .expect("should be able to serialize a proof"); - - // Write out `rk` to the caller - rk.write(&mut unsafe { &mut *rk_out }[..]) - .expect("should be able to write to rk_out"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { - let ctx = Box::new(SaplingProvingContext::new()); - - Box::into_raw(ctx) -} - -#[no_mangle] -pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { - drop(unsafe { Box::from_raw(ctx) }); -} - -#[no_mangle] -pub extern "system" fn librustzcash_zip32_xsk_master( - seed: *const c_uchar, - seedlen: size_t, - xsk_master: *mut [c_uchar; 169], -) { - let seed = unsafe { std::slice::from_raw_parts(seed, seedlen) }; - - let xsk = zip32::ExtendedSpendingKey::master(seed); - - xsk.write(&mut (unsafe { &mut *xsk_master })[..]) - .expect("should be able to serialize an ExtendedSpendingKey"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_zip32_xsk_derive( - xsk_parent: *const [c_uchar; 169], - i: u32, - xsk_i: *mut [c_uchar; 169], -) { - let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..]) - .expect("valid ExtendedSpendingKey"); - let i = zip32::ChildIndex::from_index(i); - - let xsk = xsk_parent.derive_child(i); - - xsk.write(&mut (unsafe { &mut *xsk_i })[..]) - .expect("should be able to serialize an ExtendedSpendingKey"); -} - -#[no_mangle] -pub extern "system" fn librustzcash_zip32_xfvk_derive( - xfvk_parent: *const [c_uchar; 169], - i: u32, - xfvk_i: *mut [c_uchar; 169], -) -> bool { - let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..]) - .expect("valid ExtendedFullViewingKey"); - let i = zip32::ChildIndex::from_index(i); - - let xfvk = match xfvk_parent.derive_child(i) { - Ok(xfvk) => xfvk, - Err(_) => return false, - }; - - xfvk.write(&mut (unsafe { &mut *xfvk_i })[..]) - .expect("should be able to serialize an ExtendedFullViewingKey"); - - true -} - -#[no_mangle] -pub extern "system" fn librustzcash_zip32_xfvk_address( - xfvk: *const [c_uchar; 169], - j: *const [c_uchar; 11], - j_ret: *mut [c_uchar; 11], - addr_ret: *mut [c_uchar; 43], -) -> bool { - let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) - .expect("valid ExtendedFullViewingKey"); - let j = zip32::DiversifierIndex(unsafe { *j }); - - let addr = match xfvk.address(j) { - Ok(addr) => addr, - Err(_) => return false, - }; - - let j_ret = unsafe { &mut *j_ret }; - let addr_ret = unsafe { &mut *addr_ret }; - - j_ret.copy_from_slice(&(addr.0).0); - addr_ret - .get_mut(..11) - .unwrap() - .copy_from_slice(&addr.1.diversifier.0); - addr.1 - .pk_d - .write(addr_ret.get_mut(11..).unwrap()) - .expect("should be able to serialize a PaymentAddress"); - - true -} diff --git a/librustzcash/src/tests/key_agreement.rs b/librustzcash/src/tests/key_agreement.rs deleted file mode 100644 index ab1cc84..0000000 --- a/librustzcash/src/tests/key_agreement.rs +++ /dev/null @@ -1,77 +0,0 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use rand_core::RngCore; -use rand_os::OsRng; -use zcash_primitives::jubjub::{edwards, JubjubBls12}; -use zcash_primitives::primitives::{Diversifier, ViewingKey}; - -use { - librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, - librustzcash_sapling_ka_derivepublic, -}; - -#[test] -fn test_key_agreement() { - let params = JubjubBls12::new(); - let mut rng = OsRng; - - // Create random viewing key - let vk = ViewingKey:: { - ak: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), - nk: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), - }; - - // Create a random address with the viewing key - let addr = loop { - let mut d = [0; 11]; - rng.fill_bytes(&mut d); - match vk.into_payment_address(Diversifier(d), ¶ms) { - Some(a) => break a, - None => {} - } - }; - - // Grab ivk from our viewing key in serialized form - let ivk = vk.ivk(); - let mut ivk_serialized = [0u8; 32]; - ivk.into_repr().write_le(&mut ivk_serialized[..]).unwrap(); - - // Create random esk - let mut esk = [0u8; 32]; - librustzcash_sapling_generate_r(&mut esk); - - // The sender will create a shared secret with the recipient - // by multiplying the pk_d from their address with the esk - // we randomly generated - let mut shared_secret_sender = [0u8; 32]; - - // Serialize pk_d for the call to librustzcash_sapling_ka_agree - let mut addr_pk_d = [0u8; 32]; - addr.pk_d.write(&mut addr_pk_d[..]).unwrap(); - - assert!(librustzcash_sapling_ka_agree( - &addr_pk_d, - &esk, - &mut shared_secret_sender - )); - - // Create epk for the recipient, placed in the transaction. Computed - // using the diversifier and esk. - let mut epk = [0u8; 32]; - assert!(librustzcash_sapling_ka_derivepublic( - &addr.diversifier.0, - &esk, - &mut epk - )); - - // Create sharedSecret with ephemeral key - let mut shared_secret_recipient = [0u8; 32]; - assert!(librustzcash_sapling_ka_agree( - &epk, - &ivk_serialized, - &mut shared_secret_recipient - )); - - assert!(!shared_secret_sender.iter().all(|&v| v == 0)); - assert_eq!(shared_secret_sender, shared_secret_recipient); -} diff --git a/librustzcash/src/tests/key_components.rs b/librustzcash/src/tests/key_components.rs deleted file mode 100644 index 99d3f52..0000000 --- a/librustzcash/src/tests/key_components.rs +++ /dev/null @@ -1,731 +0,0 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use zcash_primitives::{ - jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, - primitives::{Diversifier, ProofGenerationKey}, -}; - -use super::JUBJUB; - -use { - librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, - librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, -}; - -#[test] -fn key_components() { - #![allow(dead_code)] - struct TestVector { - sk: [u8; 32], - ask: [u8; 32], - nsk: [u8; 32], - ovk: [u8; 32], - ak: [u8; 32], - nk: [u8; 32], - ivk: [u8; 32], - default_d: [u8; 11], - default_pk_d: [u8; 32], - note_v: u64, - note_r: [u8; 32], - note_cm: [u8; 32], - note_pos: u64, - note_nf: [u8; 32], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py - let test_vectors = vec![ - TestVector { - sk: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - ask: [ - 0x85, 0x48, 0xa1, 0x4a, 0x47, 0x3e, 0xa5, 0x47, 0xaa, 0x23, 0x78, 0x40, 0x20, 0x44, - 0xf8, 0x18, 0xcf, 0x19, 0x11, 0xcf, 0x5d, 0xd2, 0x05, 0x4f, 0x67, 0x83, 0x45, 0xf0, - 0x0d, 0x0e, 0x88, 0x06, - ], - nsk: [ - 0x30, 0x11, 0x4e, 0xa0, 0xdd, 0x0b, 0xb6, 0x1c, 0xf0, 0xea, 0xea, 0xb6, 0xec, 0x33, - 0x31, 0xf5, 0x81, 0xb0, 0x42, 0x5e, 0x27, 0x33, 0x85, 0x01, 0x26, 0x2d, 0x7e, 0xac, - 0x74, 0x5e, 0x6e, 0x05, - ], - ovk: [ - 0x98, 0xd1, 0x69, 0x13, 0xd9, 0x9b, 0x04, 0x17, 0x7c, 0xab, 0xa4, 0x4f, 0x6e, 0x4d, - 0x22, 0x4e, 0x03, 0xb5, 0xac, 0x03, 0x1d, 0x7c, 0xe4, 0x5e, 0x86, 0x51, 0x38, 0xe1, - 0xb9, 0x96, 0xd6, 0x3b, - ], - ak: [ - 0xf3, 0x44, 0xec, 0x38, 0x0f, 0xe1, 0x27, 0x3e, 0x30, 0x98, 0xc2, 0x58, 0x8c, 0x5d, - 0x3a, 0x79, 0x1f, 0xd7, 0xba, 0x95, 0x80, 0x32, 0x76, 0x07, 0x77, 0xfd, 0x0e, 0xfa, - 0x8e, 0xf1, 0x16, 0x20, - ], - nk: [ - 0xf7, 0xcf, 0x9e, 0x77, 0xf2, 0xe5, 0x86, 0x83, 0x38, 0x3c, 0x15, 0x19, 0xac, 0x7b, - 0x06, 0x2d, 0x30, 0x04, 0x0e, 0x27, 0xa7, 0x25, 0xfb, 0x88, 0xfb, 0x19, 0xa9, 0x78, - 0xbd, 0x3f, 0xd6, 0xba, - ], - ivk: [ - 0xb7, 0x0b, 0x7c, 0xd0, 0xed, 0x03, 0xcb, 0xdf, 0xd7, 0xad, 0xa9, 0x50, 0x2e, 0xe2, - 0x45, 0xb1, 0x3e, 0x56, 0x9d, 0x54, 0xa5, 0x71, 0x9d, 0x2d, 0xaa, 0x0f, 0x5f, 0x14, - 0x51, 0x47, 0x92, 0x04, - ], - default_d: [ - 0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39, - ], - default_pk_d: [ - 0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67, - 0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8, - 0x41, 0xae, 0x74, 0x15, - ], - note_v: 0, - note_r: [ - 0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89, - 0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - note_cm: [ - 0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0, - 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, - 0xdd, 0x07, 0x64, 0x39, - ], - note_pos: 0, - note_nf: [ - 0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86, - 0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27, - 0x47, 0xab, 0x40, 0x63, - ], - }, - TestVector { - sk: [ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, - ], - ask: [ - 0xc9, 0x43, 0x56, 0x29, 0xbf, 0x8b, 0xff, 0xe5, 0x5e, 0x73, 0x35, 0xec, 0x07, 0x77, - 0x18, 0xba, 0x60, 0xba, 0x28, 0xd7, 0xac, 0x37, 0x94, 0xb7, 0x4f, 0x51, 0x2c, 0x31, - 0xaf, 0x0a, 0x53, 0x04, - ], - nsk: [ - 0x11, 0xac, 0xc2, 0xea, 0xd0, 0x7b, 0x5f, 0x00, 0x8c, 0x1f, 0x0f, 0x09, 0x0c, 0xc8, - 0xdd, 0xf3, 0x35, 0x23, 0x6f, 0xf4, 0xb2, 0x53, 0xc6, 0x49, 0x56, 0x95, 0xe9, 0xd6, - 0x39, 0xda, 0xcd, 0x08, - ], - ovk: [ - 0x3b, 0x94, 0x62, 0x10, 0xce, 0x6d, 0x1b, 0x16, 0x92, 0xd7, 0x39, 0x2a, 0xc8, 0x4a, - 0x8b, 0xc8, 0xf0, 0x3b, 0x72, 0x72, 0x3c, 0x7d, 0x36, 0x72, 0x1b, 0x80, 0x9a, 0x79, - 0xc9, 0xd6, 0xe4, 0x5b, - ], - ak: [ - 0x82, 0xff, 0x5e, 0xff, 0xc5, 0x27, 0xae, 0x84, 0x02, 0x0b, 0xf2, 0xd3, 0x52, 0x01, - 0xc1, 0x02, 0x19, 0x13, 0x19, 0x47, 0xff, 0x4b, 0x96, 0xf8, 0x81, 0xa4, 0x5f, 0x2e, - 0x8a, 0xe3, 0x05, 0x18, - ], - nk: [ - 0xc4, 0x53, 0x4d, 0x84, 0x8b, 0xb9, 0x18, 0xcf, 0x4a, 0x7f, 0x8b, 0x98, 0x74, 0x0a, - 0xb3, 0xcc, 0xee, 0x58, 0x67, 0x95, 0xff, 0x4d, 0xf6, 0x45, 0x47, 0xa8, 0x88, 0x8a, - 0x6c, 0x74, 0x15, 0xd2, - ], - ivk: [ - 0xc5, 0x18, 0x38, 0x44, 0x66, 0xb2, 0x69, 0x88, 0xb5, 0x10, 0x90, 0x67, 0x41, 0x8d, - 0x19, 0x2d, 0x9d, 0x6b, 0xd0, 0xd9, 0x23, 0x22, 0x05, 0xd7, 0x74, 0x18, 0xc2, 0x40, - 0xfc, 0x68, 0xa4, 0x06, - ], - default_d: [ - 0xae, 0xf1, 0x80, 0xf6, 0xe3, 0x4e, 0x35, 0x4b, 0x88, 0x8f, 0x81, - ], - default_pk_d: [ - 0xa6, 0xb1, 0x3e, 0xa3, 0x36, 0xdd, 0xb7, 0xa6, 0x7b, 0xb0, 0x9a, 0x0e, 0x68, 0xe9, - 0xd3, 0xcf, 0xb3, 0x92, 0x10, 0x83, 0x1e, 0xa3, 0xa2, 0x96, 0xba, 0x09, 0xa9, 0x22, - 0x06, 0x0f, 0xd3, 0x8b, - ], - note_v: 12227227834928555328, - note_r: [ - 0x47, 0x8b, 0xa0, 0xee, 0x6e, 0x1a, 0x75, 0xb6, 0x00, 0x03, 0x6f, 0x26, 0xf1, 0x8b, - 0x70, 0x15, 0xab, 0x55, 0x6b, 0xed, 0xdf, 0x8b, 0x96, 0x02, 0x38, 0x86, 0x9f, 0x89, - 0xdd, 0x80, 0x4e, 0x06, - ], - note_cm: [ - 0xb5, 0x78, 0x93, 0x50, 0x0b, 0xfb, 0x85, 0xdf, 0x2e, 0x8b, 0x01, 0xac, 0x45, 0x2f, - 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, - 0xca, 0xd4, 0x69, 0x50, - ], - note_pos: 763714296, - note_nf: [ - 0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2, - 0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17, - 0x4c, 0x2e, 0x9d, 0x93, - ], - }, - TestVector { - sk: [ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, - ], - ask: [ - 0xee, 0x1c, 0x3d, 0x7e, 0xfe, 0x0a, 0x78, 0x06, 0x3d, 0x6a, 0xf3, 0xd9, 0xd8, 0x12, - 0x12, 0xaf, 0x47, 0xb7, 0xc1, 0xb7, 0x61, 0xf8, 0x5c, 0xcb, 0x06, 0x6f, 0xc1, 0x1a, - 0x6a, 0x42, 0x17, 0x03, - ], - nsk: [ - 0x1d, 0x3b, 0x71, 0x37, 0x55, 0xd7, 0x48, 0x75, 0xe8, 0xea, 0x38, 0xfd, 0x16, 0x6e, - 0x76, 0xc6, 0x2a, 0x42, 0x50, 0x21, 0x6e, 0x6b, 0xbf, 0xe4, 0x8a, 0x5e, 0x2e, 0xab, - 0xad, 0x11, 0x7f, 0x0b, - ], - ovk: [ - 0x8b, 0xf4, 0x39, 0x0e, 0x28, 0xdd, 0xc9, 0x5b, 0x83, 0x02, 0xc3, 0x81, 0xd5, 0x81, - 0x0b, 0x84, 0xba, 0x8e, 0x60, 0x96, 0xe5, 0xa7, 0x68, 0x22, 0x77, 0x4f, 0xd4, 0x9f, - 0x49, 0x1e, 0x8f, 0x49, - ], - ak: [ - 0xab, 0x83, 0x57, 0x4e, 0xb5, 0xde, 0x85, 0x9a, 0x0a, 0xb8, 0x62, 0x9d, 0xec, 0x34, - 0xc7, 0xbe, 0xe8, 0xc3, 0xfc, 0x74, 0xdf, 0xa0, 0xb1, 0x9a, 0x3a, 0x74, 0x68, 0xd1, - 0x5d, 0xca, 0x64, 0xc6, - ], - nk: [ - 0x95, 0xd5, 0x80, 0x53, 0xe0, 0x59, 0x2e, 0x4a, 0x16, 0x9c, 0xc0, 0xb7, 0x92, 0x8a, - 0xaa, 0xc3, 0xde, 0x24, 0xef, 0x15, 0x31, 0xaa, 0x9e, 0xb6, 0xf4, 0xab, 0x93, 0x91, - 0x4d, 0xa8, 0xa0, 0x6e, - ], - ivk: [ - 0x47, 0x1c, 0x24, 0xa3, 0xdc, 0x87, 0x30, 0xe7, 0x50, 0x36, 0xc0, 0xa9, 0x5f, 0x3e, - 0x2f, 0x7d, 0xd1, 0xbe, 0x6f, 0xb9, 0x3a, 0xd2, 0x95, 0x92, 0x20, 0x3d, 0xef, 0x30, - 0x41, 0x95, 0x45, 0x05, - ], - default_d: [ - 0x75, 0x99, 0xf0, 0xbf, 0x9b, 0x57, 0xcd, 0x2d, 0xc2, 0x99, 0xb6, - ], - default_pk_d: [ - 0x66, 0x14, 0x17, 0x39, 0x51, 0x4b, 0x28, 0xf0, 0x5d, 0xef, 0x8a, 0x18, 0xee, 0xee, - 0x5e, 0xed, 0x4d, 0x44, 0xc6, 0x22, 0x5c, 0x3c, 0x65, 0xd8, 0x8d, 0xd9, 0x90, 0x77, - 0x08, 0x01, 0x2f, 0x5a, - ], - note_v: 6007711596147559040, - note_r: [ - 0x14, 0x7c, 0xf2, 0xb5, 0x1b, 0x4c, 0x7c, 0x63, 0xcb, 0x77, 0xb9, 0x9e, 0x8b, 0x78, - 0x3e, 0x5b, 0x51, 0x11, 0xdb, 0x0a, 0x7c, 0xa0, 0x4d, 0x6c, 0x01, 0x4a, 0x1d, 0x7d, - 0xa8, 0x3b, 0xae, 0x0a, - ], - note_cm: [ - 0xdb, 0x85, 0xa7, 0x0a, 0x98, 0x43, 0x7f, 0x73, 0x16, 0x7f, 0xc3, 0x32, 0xd5, 0xb7, - 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, - 0x4e, 0x55, 0xf1, 0x51, - ], - note_pos: 1527428592, - note_nf: [ - 0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f, - 0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f, - 0x59, 0xae, 0x1d, 0x14, - ], - }, - TestVector { - sk: [ - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, - ], - ask: [ - 0x00, 0xc3, 0xa1, 0xe1, 0xca, 0x8f, 0x4e, 0x04, 0x80, 0xee, 0x1e, 0xe9, 0x0c, 0xa7, - 0x51, 0x78, 0x79, 0xd3, 0xfc, 0x5c, 0x81, 0x5c, 0x09, 0x03, 0xe5, 0xee, 0xbc, 0x94, - 0xbb, 0x80, 0x95, 0x03, - ], - nsk: [ - 0xe6, 0x62, 0x85, 0xa5, 0xe9, 0xb6, 0x5e, 0x15, 0x7a, 0xd2, 0xfc, 0xd5, 0x43, 0xda, - 0xd9, 0x8c, 0x67, 0xa5, 0x8a, 0xbd, 0xf2, 0x87, 0xe0, 0x55, 0x06, 0xbd, 0x1c, 0x2e, - 0x59, 0xb0, 0x72, 0x0b, - ], - ovk: [ - 0x14, 0x76, 0x78, 0xe0, 0x55, 0x3b, 0x97, 0x82, 0x93, 0x47, 0x64, 0x7c, 0x5b, 0xc7, - 0xda, 0xb4, 0xcc, 0x22, 0x02, 0xb5, 0x4e, 0xc2, 0x9f, 0xd3, 0x1a, 0x3d, 0xe6, 0xbe, - 0x08, 0x25, 0xfc, 0x5e, - ], - ak: [ - 0x3c, 0x9c, 0xde, 0x7e, 0x5d, 0x0d, 0x38, 0xa8, 0x61, 0x0f, 0xaa, 0xdb, 0xcf, 0x4c, - 0x34, 0x3f, 0x5d, 0x3c, 0xfa, 0x31, 0x55, 0xa5, 0xb9, 0x46, 0x61, 0xa6, 0x75, 0x3e, - 0x96, 0xe8, 0x84, 0xea, - ], - nk: [ - 0xb7, 0x7d, 0x36, 0xf5, 0x08, 0x94, 0x1d, 0xbd, 0x61, 0xcf, 0xd0, 0xf1, 0x59, 0xee, - 0x05, 0xcf, 0xaa, 0x78, 0xa2, 0x6c, 0x94, 0x92, 0x90, 0x38, 0x06, 0xd8, 0x3b, 0x59, - 0x8d, 0x3c, 0x1c, 0x2a, - ], - ivk: [ - 0x63, 0x6a, 0xa9, 0x64, 0xbf, 0xc2, 0x3c, 0xe4, 0xb1, 0xfc, 0xf7, 0xdf, 0xc9, 0x91, - 0x79, 0xdd, 0xc4, 0x06, 0xff, 0x55, 0x40, 0x0c, 0x92, 0x95, 0xac, 0xfc, 0x14, 0xf0, - 0x31, 0xc7, 0x26, 0x00, - ], - default_d: [ - 0x1b, 0x81, 0x61, 0x4f, 0x1d, 0xad, 0xea, 0x0f, 0x8d, 0x0a, 0x58, - ], - default_pk_d: [ - 0x25, 0xeb, 0x55, 0xfc, 0xcf, 0x76, 0x1f, 0xc6, 0x4e, 0x85, 0xa5, 0x88, 0xef, 0xe6, - 0xea, 0xd7, 0x83, 0x2f, 0xb1, 0xf0, 0xf7, 0xa8, 0x31, 0x65, 0x89, 0x5b, 0xdf, 0xf9, - 0x42, 0x92, 0x5f, 0x5c, - ], - note_v: 18234939431076114368, - note_r: [ - 0x34, 0xa4, 0xb2, 0xa9, 0x14, 0x4f, 0xf5, 0xea, 0x54, 0xef, 0xee, 0x87, 0xcf, 0x90, - 0x1b, 0x5b, 0xed, 0x5e, 0x35, 0xd2, 0x1f, 0xbb, 0xd7, 0x88, 0xd5, 0xbd, 0x9d, 0x83, - 0x3e, 0x11, 0x28, 0x04, - ], - note_cm: [ - 0xe0, 0x8c, 0xe4, 0x82, 0xb3, 0xa8, 0xfb, 0x3b, 0x35, 0xcc, 0xdb, 0xe3, 0x43, 0x37, - 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, - 0x60, 0xd1, 0x9b, 0x6c, - ], - note_pos: 2291142888, - note_nf: [ - 0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56, - 0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75, - 0x8b, 0xec, 0x47, 0xa1, - ], - }, - TestVector { - sk: [ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, - ], - ask: [ - 0x82, 0x36, 0xd1, 0x9d, 0x32, 0x05, 0xd8, 0x55, 0x43, 0xa0, 0x68, 0x11, 0x34, 0x3f, - 0x82, 0x7b, 0x65, 0x63, 0x77, 0x0a, 0x49, 0xaa, 0x4d, 0x0c, 0xa0, 0x08, 0x18, 0x05, - 0xd4, 0xc8, 0xea, 0x0d, - ], - nsk: [ - 0x7e, 0xc1, 0xef, 0x0b, 0xed, 0x82, 0x71, 0x82, 0x72, 0xf0, 0xf4, 0x4f, 0x01, 0x7c, - 0x48, 0x41, 0x74, 0x51, 0x3d, 0x66, 0x1d, 0xd1, 0x68, 0xaf, 0x02, 0xd2, 0x09, 0x2a, - 0x1d, 0x8a, 0x05, 0x07, - ], - ovk: [ - 0x1b, 0x6e, 0x75, 0xec, 0xe3, 0xac, 0xe8, 0xdb, 0xa6, 0xa5, 0x41, 0x0d, 0x9a, 0xd4, - 0x75, 0x56, 0x68, 0xe4, 0xb3, 0x95, 0x85, 0xd6, 0x35, 0xec, 0x1d, 0xa7, 0xc8, 0xdc, - 0xfd, 0x5f, 0xc4, 0xed, - ], - ak: [ - 0x55, 0xe8, 0x83, 0x89, 0xbb, 0x7e, 0x41, 0xde, 0x13, 0x0c, 0xfa, 0x51, 0xa8, 0x71, - 0x5f, 0xde, 0x01, 0xff, 0x9c, 0x68, 0x76, 0x64, 0x7f, 0x01, 0x75, 0xad, 0x34, 0xf0, - 0x58, 0xdd, 0xe0, 0x1a, - ], - nk: [ - 0x72, 0x5d, 0x4a, 0xd6, 0xa1, 0x50, 0x21, 0xcd, 0x1c, 0x48, 0xc5, 0xee, 0x19, 0xde, - 0x6c, 0x1e, 0x76, 0x8a, 0x2c, 0xc0, 0xa9, 0xa7, 0x30, 0xa0, 0x1b, 0xb2, 0x1c, 0x95, - 0xe3, 0xd9, 0xe4, 0x3c, - ], - ivk: [ - 0x67, 0xfa, 0x2b, 0xf7, 0xc6, 0x7d, 0x46, 0x58, 0x24, 0x3c, 0x31, 0x7c, 0x0c, 0xb4, - 0x1f, 0xd3, 0x20, 0x64, 0xdf, 0xd3, 0x70, 0x9f, 0xe0, 0xdc, 0xb7, 0x24, 0xf1, 0x4b, - 0xb0, 0x1a, 0x1d, 0x04, - ], - default_d: [ - 0xfc, 0xfb, 0x68, 0xa4, 0x0d, 0x4b, 0xc6, 0xa0, 0x4b, 0x09, 0xc4, - ], - default_pk_d: [ - 0x8b, 0x2a, 0x33, 0x7f, 0x03, 0x62, 0x2c, 0x24, 0xff, 0x38, 0x1d, 0x4c, 0x54, 0x6f, - 0x69, 0x77, 0xf9, 0x05, 0x22, 0xe9, 0x2f, 0xde, 0x44, 0xc9, 0xd1, 0xbb, 0x09, 0x97, - 0x14, 0xb9, 0xdb, 0x2b, - ], - note_v: 12015423192295118080, - note_r: [ - 0xe5, 0x57, 0x85, 0x13, 0x55, 0x74, 0x7c, 0x09, 0xac, 0x59, 0x01, 0x3c, 0xbd, 0xe8, - 0x59, 0x80, 0x96, 0x4e, 0xc1, 0x84, 0x4d, 0x9c, 0x69, 0x67, 0xca, 0x0c, 0x02, 0x9c, - 0x84, 0x57, 0xbb, 0x04, - ], - note_cm: [ - 0xbd, 0xc8, 0x54, 0xbf, 0x3e, 0x7b, 0x00, 0x82, 0x1f, 0x3b, 0x8b, 0x85, 0x23, 0x8c, - 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, - 0xc7, 0x1b, 0x7f, 0x36, - ], - note_pos: 3054857184, - note_nf: [ - 0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f, - 0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03, - 0x01, 0xbf, 0x3d, 0x13, - ], - }, - TestVector { - sk: [ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, - ], - ask: [ - 0xea, 0xe6, 0x88, 0x4d, 0x76, 0x4a, 0x05, 0x40, 0x61, 0xa8, 0xf1, 0xc0, 0x07, 0x6c, - 0x62, 0x4d, 0xcb, 0x73, 0x87, 0x89, 0xf7, 0xad, 0x1e, 0x74, 0x08, 0xe3, 0x1f, 0x24, - 0xdf, 0xc8, 0x26, 0x07, - ], - nsk: [ - 0xfb, 0xe6, 0x10, 0xf4, 0x2a, 0x41, 0x74, 0x9f, 0x9b, 0x6e, 0x6e, 0x4a, 0x54, 0xb5, - 0xa3, 0x2e, 0xbf, 0xe8, 0xf4, 0x38, 0x00, 0x88, 0x1b, 0xa6, 0xcd, 0x13, 0xed, 0x0b, - 0x05, 0x29, 0x46, 0x01, - ], - ovk: [ - 0xc6, 0xbc, 0x1f, 0x39, 0xf0, 0xd7, 0x86, 0x31, 0x4c, 0xb2, 0x0b, 0xf9, 0xab, 0x22, - 0x85, 0x40, 0x91, 0x35, 0x55, 0xf9, 0x70, 0x69, 0x6b, 0x6d, 0x7c, 0x77, 0xbb, 0x33, - 0x23, 0x28, 0x37, 0x2a, - ], - ak: [ - 0xe6, 0x82, 0x76, 0x59, 0x14, 0xe3, 0x86, 0x4c, 0x33, 0x9e, 0x57, 0x82, 0xb8, 0x55, - 0xc0, 0xfd, 0xf4, 0x0e, 0x0d, 0xfc, 0xed, 0xb9, 0xe7, 0xb4, 0x7b, 0xc9, 0x4b, 0x90, - 0xb3, 0xa4, 0xc9, 0x88, - ], - nk: [ - 0x82, 0x25, 0x6b, 0x95, 0x62, 0x3c, 0x67, 0x02, 0x4b, 0x44, 0x24, 0xd9, 0x14, 0x00, - 0xa3, 0x70, 0xe7, 0xac, 0x8e, 0x4d, 0x15, 0x48, 0x2a, 0x37, 0x59, 0xe0, 0x0d, 0x21, - 0x97, 0x49, 0xda, 0xee, - ], - ivk: [ - 0xea, 0x3f, 0x1d, 0x80, 0xe4, 0x30, 0x7c, 0xa7, 0x3b, 0x9f, 0x37, 0x80, 0x1f, 0x91, - 0xfb, 0xa8, 0x10, 0xcc, 0x41, 0xd2, 0x79, 0xfc, 0x29, 0xf5, 0x64, 0x23, 0x56, 0x54, - 0xa2, 0x17, 0x8e, 0x03, - ], - default_d: [ - 0xeb, 0x51, 0x98, 0x82, 0xad, 0x1e, 0x5c, 0xc6, 0x54, 0xcd, 0x59, - ], - default_pk_d: [ - 0x6b, 0x27, 0xda, 0xcc, 0xb5, 0xa8, 0x20, 0x7f, 0x53, 0x2d, 0x10, 0xca, 0x23, 0x8f, - 0x97, 0x86, 0x64, 0x8a, 0x11, 0xb5, 0x96, 0x6e, 0x51, 0xa2, 0xf7, 0xd8, 0x9e, 0x15, - 0xd2, 0x9b, 0x8f, 0xdf, - ], - note_v: 5795906953514121792, - note_r: [ - 0x68, 0xf0, 0x61, 0x04, 0x60, 0x6b, 0x0c, 0x54, 0x49, 0x84, 0x5f, 0xf4, 0xc6, 0x5f, - 0x73, 0xe9, 0x0f, 0x45, 0xef, 0x5a, 0x43, 0xc9, 0xd7, 0x4c, 0xb2, 0xc8, 0x5c, 0xf5, - 0x6c, 0x94, 0xc0, 0x02, - ], - note_cm: [ - 0xe8, 0x26, 0x7d, 0x30, 0xac, 0x11, 0xc1, 0x00, 0xbc, 0x7a, 0x0f, 0xdf, 0x91, 0xf7, - 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, - 0xde, 0x1a, 0x5b, 0x4c, - ], - note_pos: 3818571480, - note_nf: [ - 0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2, - 0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20, - 0xb6, 0xc9, 0xa7, 0xe1, - ], - }, - TestVector { - sk: [ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, - ], - ask: [ - 0xe8, 0xf8, 0x16, 0xb4, 0xbc, 0x08, 0xa7, 0xe5, 0x66, 0x75, 0x0c, 0xc2, 0x8a, 0xfe, - 0x82, 0xa4, 0xce, 0xa9, 0xc2, 0xbe, 0xf2, 0x44, 0xfa, 0x4b, 0x13, 0xc4, 0x73, 0x9b, - 0x28, 0x07, 0x4c, 0x0d, - ], - nsk: [ - 0x32, 0x61, 0x5b, 0x13, 0x7f, 0x28, 0x01, 0xed, 0x44, 0x6e, 0x48, 0x78, 0x1a, 0xb0, - 0x63, 0x45, 0x72, 0xe1, 0x8c, 0xfb, 0x06, 0x93, 0x72, 0x1b, 0x88, 0x03, 0xc0, 0x5b, - 0x82, 0x27, 0xd1, 0x07, - ], - ovk: [ - 0xf6, 0x2c, 0x05, 0xe8, 0x48, 0xa8, 0x73, 0xef, 0x88, 0x5e, 0x12, 0xb0, 0x8c, 0x5e, - 0x7c, 0xa2, 0xf3, 0x24, 0x24, 0xba, 0xcc, 0x75, 0x4c, 0xb6, 0x97, 0x50, 0x44, 0x4d, - 0x35, 0x5f, 0x51, 0x06, - ], - ak: [ - 0xff, 0x27, 0xdb, 0x07, 0x51, 0x94, 0x5d, 0x3e, 0xe4, 0xbe, 0x9c, 0xf1, 0x5c, 0x2e, - 0xa2, 0x11, 0xb2, 0x4b, 0x16, 0x4d, 0x5f, 0x2d, 0x7d, 0xdf, 0xf5, 0xe4, 0xa0, 0x70, - 0x8f, 0x10, 0xb9, 0x5e, - ], - nk: [ - 0x94, 0x38, 0x85, 0x95, 0x9d, 0x4e, 0xf8, 0xa9, 0xcf, 0xca, 0x07, 0xc4, 0x57, 0xf0, - 0x9e, 0xc7, 0x4b, 0x96, 0xf9, 0x93, 0xd8, 0xe0, 0xfa, 0x32, 0xb1, 0x9c, 0x03, 0xe3, - 0xb0, 0x7a, 0x42, 0x0f, - ], - ivk: [ - 0xb5, 0xc5, 0x89, 0x49, 0x43, 0x95, 0x69, 0x33, 0xc0, 0xe5, 0xc1, 0x2d, 0x31, 0x1f, - 0xc1, 0x2c, 0xba, 0x58, 0x35, 0x4b, 0x5c, 0x38, 0x9e, 0xdc, 0x03, 0xda, 0x55, 0x08, - 0x4f, 0x74, 0xc2, 0x05, - ], - default_d: [ - 0xbe, 0xbb, 0x0f, 0xb4, 0x6b, 0x8a, 0xaf, 0xf8, 0x90, 0x40, 0xf6, - ], - default_pk_d: [ - 0xd1, 0x1d, 0xa0, 0x1f, 0x0b, 0x43, 0xbd, 0xd5, 0x28, 0x8d, 0x32, 0x38, 0x5b, 0x87, - 0x71, 0xd2, 0x23, 0x49, 0x3c, 0x69, 0x80, 0x25, 0x44, 0x04, 0x3f, 0x77, 0xcf, 0x1d, - 0x71, 0xc1, 0xcb, 0x8c, - ], - note_v: 18023134788442677120, - note_r: [ - 0x49, 0xf9, 0x0b, 0x47, 0xfd, 0x52, 0xfe, 0xe7, 0xc1, 0xc8, 0x1f, 0x0d, 0xcb, 0x5b, - 0x74, 0xc3, 0xfb, 0x9b, 0x3e, 0x03, 0x97, 0x6f, 0x8b, 0x75, 0x24, 0xea, 0xba, 0xd0, - 0x08, 0x89, 0x21, 0x07, - ], - note_cm: [ - 0x57, 0x2b, 0xa2, 0x05, 0x25, 0xb0, 0xac, 0x4d, 0x6d, 0xc0, 0x1a, 0xc2, 0xea, 0x10, - 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, - 0x78, 0x3a, 0x1e, 0x55, - ], - note_pos: 287318480, - note_nf: [ - 0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae, - 0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4, - 0x07, 0xd5, 0x1e, 0x11, - ], - }, - TestVector { - sk: [ - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, - ], - ask: [ - 0x74, 0xb4, 0x4a, 0x37, 0xf1, 0x50, 0x23, 0xc0, 0x60, 0x42, 0x7e, 0x1d, 0xae, 0xa3, - 0xf6, 0x43, 0x12, 0xdd, 0x8f, 0xeb, 0x7b, 0x2c, 0xed, 0xf0, 0xdd, 0x55, 0x44, 0x49, - 0x3f, 0x87, 0x2c, 0x06, - ], - nsk: [ - 0x07, 0x5c, 0x35, 0xdb, 0x8b, 0x1b, 0x25, 0x75, 0x42, 0x23, 0xec, 0xee, 0x34, 0xab, - 0x73, 0x0d, 0xdd, 0xd1, 0xf1, 0x4a, 0x6a, 0x54, 0xf4, 0xc6, 0xf4, 0x68, 0x45, 0x3c, - 0x3c, 0x6e, 0xd6, 0x0b, - ], - ovk: [ - 0xe9, 0xe0, 0xdc, 0x1e, 0xd3, 0x11, 0xda, 0xed, 0x64, 0xbd, 0x74, 0xda, 0x5d, 0x94, - 0xfe, 0x88, 0xa6, 0xea, 0x41, 0x4b, 0x73, 0x12, 0xde, 0x3d, 0x2a, 0x78, 0xf6, 0x46, - 0x32, 0xbb, 0xe3, 0x73, - ], - ak: [ - 0x28, 0x3f, 0x9a, 0xaf, 0xa9, 0xbc, 0xb3, 0xe6, 0xce, 0x17, 0xe6, 0x32, 0x12, 0x63, - 0x4c, 0xb3, 0xee, 0x55, 0x0c, 0x47, 0x6b, 0x67, 0x6b, 0xd3, 0x56, 0xa6, 0xdf, 0x8a, - 0xdf, 0x51, 0xd2, 0x5e, - ], - nk: [ - 0xdc, 0x4c, 0x67, 0xb1, 0x0d, 0x4b, 0x0a, 0x21, 0x8d, 0xc6, 0xe1, 0x48, 0x70, 0x66, - 0x74, 0x0a, 0x40, 0x93, 0x17, 0x86, 0x6c, 0x32, 0xe6, 0x64, 0xb5, 0x0e, 0x39, 0x7a, - 0xa8, 0x03, 0x89, 0xd4, - ], - ivk: [ - 0x87, 0x16, 0xc8, 0x28, 0x80, 0xe1, 0x36, 0x83, 0xe1, 0xbb, 0x05, 0x9d, 0xd0, 0x6c, - 0x80, 0xc9, 0x01, 0x34, 0xa9, 0x6d, 0x5a, 0xfc, 0xa8, 0xaa, 0xc2, 0xbb, 0xf6, 0x8b, - 0xb0, 0x5f, 0x84, 0x02, - ], - default_d: [ - 0xad, 0x6e, 0x2e, 0x18, 0x5a, 0x31, 0x00, 0xe3, 0xa6, 0xa8, 0xb3, - ], - default_pk_d: [ - 0x32, 0xcb, 0x28, 0x06, 0xb8, 0x82, 0xf1, 0x36, 0x8b, 0x0d, 0x4a, 0x89, 0x8f, 0x72, - 0xc4, 0xc8, 0xf7, 0x28, 0x13, 0x2c, 0xc1, 0x24, 0x56, 0x94, 0x6e, 0x7f, 0x4c, 0xb0, - 0xfb, 0x05, 0x8d, 0xa9, - ], - note_v: 11803618549661680832, - note_r: [ - 0x51, 0x65, 0xaf, 0xf2, 0x2d, 0xd4, 0xed, 0x56, 0xb4, 0xd8, 0x1d, 0x1f, 0x17, 0x1c, - 0xc3, 0xd6, 0x43, 0x2f, 0xed, 0x1b, 0xeb, 0xf2, 0x0a, 0x7b, 0xea, 0xb1, 0x2d, 0xb1, - 0x42, 0xf9, 0x4a, 0x0c, - ], - note_cm: [ - 0xab, 0x7f, 0xc5, 0x66, 0x87, 0x3c, 0xcd, 0xe6, 0x71, 0xf5, 0x98, 0x27, 0x67, 0x85, - 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, - 0xf7, 0x8c, 0x2b, 0x23, - ], - note_pos: 1051032776, - note_nf: [ - 0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f, - 0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc, - 0x8b, 0xb6, 0x98, 0x90, - ], - }, - TestVector { - sk: [ - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, - ], - ask: [ - 0x03, 0x9d, 0xd9, 0x3d, 0xf3, 0x11, 0xff, 0x8f, 0xba, 0xb3, 0xfe, 0x23, 0x02, 0x19, - 0xcd, 0x42, 0xac, 0x87, 0x94, 0x84, 0xf3, 0x0b, 0x90, 0x3a, 0x3c, 0x1e, 0x67, 0xcc, - 0xca, 0x5a, 0x7b, 0x0d, - ], - nsk: [ - 0x04, 0x9f, 0xa1, 0x4f, 0x48, 0x6c, 0x75, 0xb9, 0xfa, 0xd7, 0xe3, 0xb6, 0x73, 0xa4, - 0x43, 0xdd, 0x07, 0x4e, 0xaa, 0x96, 0xed, 0xcb, 0x2a, 0x53, 0xea, 0xaa, 0xbd, 0xaf, - 0x70, 0xff, 0xbb, 0x08, - ], - ovk: [ - 0x14, 0x7d, 0xd1, 0x1d, 0x77, 0xeb, 0xa1, 0xb1, 0x63, 0x6f, 0xd6, 0x19, 0x0c, 0x62, - 0xb9, 0xa5, 0xd0, 0x48, 0x1b, 0xee, 0x7e, 0x91, 0x7f, 0xab, 0x02, 0xe2, 0x18, 0x58, - 0x06, 0x3a, 0xb5, 0x04, - ], - ak: [ - 0x36, 0x40, 0x48, 0xee, 0xdb, 0xe8, 0xca, 0x20, 0x5e, 0xb7, 0xe7, 0xba, 0x0a, 0x90, - 0x12, 0x16, 0x6c, 0x7c, 0x7b, 0xd9, 0xeb, 0x22, 0x8e, 0x08, 0x48, 0x14, 0x48, 0xc4, - 0x88, 0xaa, 0x21, 0xd2, - ], - nk: [ - 0xed, 0x60, 0xaf, 0x1c, 0xe7, 0xdf, 0x38, 0x07, 0x0d, 0x38, 0x51, 0x43, 0x2a, 0x96, - 0x48, 0x0d, 0xb0, 0xb4, 0x17, 0xc3, 0x68, 0x2a, 0x1d, 0x68, 0xe3, 0xe8, 0x93, 0x34, - 0x23, 0x5c, 0x0b, 0xdf, - ], - ivk: [ - 0x99, 0xc9, 0xb4, 0xb8, 0x4f, 0x4b, 0x4e, 0x35, 0x0f, 0x78, 0x7d, 0x1c, 0xf7, 0x05, - 0x1d, 0x50, 0xec, 0xc3, 0x4b, 0x1a, 0x5b, 0x20, 0xd2, 0xd2, 0x13, 0x9b, 0x4a, 0xf1, - 0xf1, 0x60, 0xe0, 0x01, - ], - default_d: [ - 0x21, 0xc9, 0x0e, 0x1c, 0x65, 0x8b, 0x3e, 0xfe, 0x86, 0xaf, 0x58, - ], - default_pk_d: [ - 0x9e, 0x64, 0x17, 0x4b, 0x4a, 0xb9, 0x81, 0x40, 0x5c, 0x32, 0x3b, 0x5e, 0x12, 0x47, - 0x59, 0x45, 0xa4, 0x6d, 0x4f, 0xed, 0xf8, 0x06, 0x08, 0x28, 0x04, 0x1c, 0xd2, 0x0e, - 0x62, 0xfd, 0x2c, 0xef, - ], - note_v: 5584102310880684544, - note_r: [ - 0x8c, 0x3e, 0x56, 0x44, 0x9d, 0xc8, 0x63, 0x54, 0xd3, 0x3b, 0x02, 0x5e, 0xf2, 0x79, - 0x34, 0x60, 0xbc, 0xb1, 0x69, 0xf3, 0x32, 0x4e, 0x4a, 0x6b, 0x64, 0xba, 0xa6, 0x08, - 0x32, 0x31, 0x57, 0x04, - ], - note_cm: [ - 0x7b, 0x48, 0xa8, 0x37, 0x5d, 0x3e, 0xbd, 0x56, 0xbc, 0x64, 0x9b, 0xb5, 0xb5, 0x24, - 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, - 0x8f, 0xea, 0x4d, 0x04, - ], - note_pos: 1814747072, - note_nf: [ - 0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20, - 0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe, - 0xb1, 0x7c, 0xd6, 0x20, - ], - }, - TestVector { - sk: [ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, - ], - ask: [ - 0xeb, 0xbb, 0x40, 0xa9, 0x80, 0xba, 0x3b, 0x88, 0x60, 0x94, 0x8d, 0x01, 0x1e, 0x1b, - 0xfb, 0x4a, 0xff, 0xe1, 0x6c, 0x65, 0x2e, 0x90, 0xe9, 0x82, 0x58, 0x30, 0x2f, 0x44, - 0x64, 0xc9, 0x1e, 0x0c, - ], - nsk: [ - 0x68, 0x43, 0x1b, 0x19, 0x91, 0x04, 0x21, 0x52, 0x00, 0xb9, 0x5e, 0xe5, 0xcb, 0x71, - 0xbf, 0x8b, 0x88, 0x3a, 0x3e, 0x95, 0xb7, 0x98, 0x9c, 0xad, 0x19, 0x70, 0x63, 0x14, - 0x1e, 0xbb, 0xfd, 0x00, - ], - ovk: [ - 0x57, 0x34, 0x67, 0xa7, 0xb3, 0x0e, 0xad, 0x6c, 0xcc, 0x50, 0x47, 0x44, 0xca, 0x9e, - 0x1a, 0x28, 0x1a, 0x0d, 0x1a, 0x08, 0x73, 0x8b, 0x06, 0xa0, 0x68, 0x4f, 0xea, 0xcd, - 0x1e, 0x9d, 0x12, 0x6d, - ], - ak: [ - 0x71, 0xc3, 0x52, 0x3e, 0xec, 0xa3, 0x53, 0x11, 0xfb, 0xd5, 0xd7, 0xe7, 0xd7, 0x0b, - 0x70, 0x9d, 0x6c, 0x35, 0xa2, 0x4f, 0x26, 0x2b, 0x34, 0xbf, 0x64, 0x05, 0x9b, 0xf2, - 0xc0, 0x2e, 0x0b, 0xa8, - ], - nk: [ - 0x62, 0x44, 0x00, 0x10, 0x3b, 0x65, 0x69, 0xb7, 0x35, 0x8f, 0xe8, 0x0f, 0x6f, 0x6c, - 0xad, 0x43, 0x25, 0xde, 0xfd, 0xa9, 0xd9, 0x49, 0x9c, 0x2b, 0x8f, 0x88, 0x6a, 0x62, - 0x69, 0xa2, 0xaa, 0x52, - ], - ivk: [ - 0xdb, 0x95, 0xea, 0x8b, 0xd9, 0xf9, 0x3d, 0x41, 0xb5, 0xab, 0x2b, 0xeb, 0xc9, 0x1a, - 0x38, 0xed, 0xd5, 0x27, 0x08, 0x3e, 0x2a, 0x6e, 0xf9, 0xf3, 0xc2, 0x97, 0x02, 0xd5, - 0xff, 0x89, 0xed, 0x00, - ], - default_d: [ - 0x23, 0x3c, 0x4a, 0xb8, 0x86, 0xa5, 0x5e, 0x3b, 0xa3, 0x74, 0xc0, - ], - default_pk_d: [ - 0xb6, 0x8e, 0x9e, 0xe0, 0xc0, 0x67, 0x8d, 0x7b, 0x30, 0x36, 0x93, 0x1c, 0x83, 0x1a, - 0x25, 0x25, 0x5f, 0x7e, 0xe4, 0x87, 0x38, 0x5a, 0x30, 0x31, 0x6e, 0x15, 0xf6, 0x48, - 0x2b, 0x87, 0x4f, 0xda, - ], - note_v: 17811330145809239872, - note_r: [ - 0x6e, 0xbb, 0xed, 0x74, 0x36, 0x19, 0xa2, 0x56, 0xf9, 0xad, 0x2e, 0x85, 0x88, 0x0c, - 0xfa, 0xa9, 0x09, 0x8a, 0x5f, 0xdb, 0x16, 0x29, 0x99, 0x0d, 0x9a, 0x7d, 0x3b, 0xb9, - 0x3f, 0xc9, 0x00, 0x03, - ], - note_cm: [ - 0xd3, 0x76, 0xa7, 0xbe, 0xe8, 0xce, 0x67, 0xf4, 0xef, 0xde, 0x56, 0xaa, 0x77, 0xcf, - 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, - 0xe4, 0x1d, 0xeb, 0x37, - ], - note_pos: 2578461368, - note_nf: [ - 0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe, - 0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71, - 0x0d, 0x93, 0xc9, 0xe9, - ], - }, - ]; - - for tv in test_vectors { - let mut ask_repr = FsRepr::default(); - let mut nsk_repr = FsRepr::default(); - ask_repr.read_le(&tv.ask[..]).unwrap(); - nsk_repr.read_le(&tv.nsk[..]).unwrap(); - let nsk = ::Fs::from_repr(nsk_repr).unwrap(); - - let ak = JUBJUB - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(ask_repr.clone(), &JUBJUB); - { - let mut vec = Vec::new(); - ak.write(&mut vec).unwrap(); - assert_eq!(&vec, &tv.ak); - } - { - let mut ak = [0u8; 32]; - librustzcash_ask_to_ak(&tv.ask, &mut ak); - assert_eq!(&ak, &tv.ak); - } - - let pgk = ProofGenerationKey { ak, nsk }; - let fvk = pgk.into_viewing_key(&JUBJUB); - { - let mut vec = Vec::new(); - fvk.nk.write(&mut vec).unwrap(); - assert_eq!(&vec, &tv.nk); - } - { - let mut nk = [0u8; 32]; - librustzcash_nsk_to_nk(&tv.nsk, &mut nk); - assert_eq!(&nk, &tv.nk); - } - - { - let mut vec = Vec::new(); - fvk.ivk().into_repr().write_le(&mut vec).unwrap(); - assert_eq!(&vec, &tv.ivk); - } - { - let mut ivk = [0u8; 32]; - librustzcash_crh_ivk(&tv.ak, &tv.nk, &mut ivk); - assert_eq!(&ivk, &tv.ivk); - } - - let diversifier = Diversifier(tv.default_d); - assert!(librustzcash_check_diversifier(&tv.default_d)); - - let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap(); - { - let mut vec = Vec::new(); - addr.pk_d.write(&mut vec).unwrap(); - assert_eq!(&vec, &tv.default_pk_d); - } - { - let mut default_pk_d = [0u8; 32]; - librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d); - assert_eq!(&default_pk_d, &tv.default_pk_d); - } - - let mut note_r_repr = FsRepr::default(); - note_r_repr.read_le(&tv.note_r[..]).unwrap(); - let note_r = ::Fs::from_repr(note_r_repr).unwrap(); - let note = addr.create_note(tv.note_v, note_r, &JUBJUB).unwrap(); - { - let mut vec = Vec::new(); - note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); - assert_eq!(&vec, &tv.note_cm); - } - - assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf); - } -} diff --git a/librustzcash/src/tests/mod.rs b/librustzcash/src/tests/mod.rs deleted file mode 100644 index dba7387..0000000 --- a/librustzcash/src/tests/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -use zcash_primitives::jubjub::{FixedGenerators, JubjubParams}; - -use super::JUBJUB; - -mod key_agreement; -mod key_components; -mod notes; -mod signatures; - -#[test] -fn sapling_generators() { - struct SaplingGenerators { - skb: [u8; 32], - pkb: [u8; 32], - npb: [u8; 32], - wprb: [u8; 32], - vcvb: [u8; 32], - vcrb: [u8; 32], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_generators.py - let sapling_generators = SaplingGenerators { - skb: [ - 0x30, 0xb5, 0xf2, 0xaa, 0xad, 0x32, 0x56, 0x30, 0xbc, 0xdd, 0xdb, 0xce, 0x4d, 0x67, - 0x65, 0x6d, 0x05, 0xfd, 0x1c, 0xc2, 0xd0, 0x37, 0xbb, 0x53, 0x75, 0xb6, 0xe9, 0x6d, - 0x9e, 0x01, 0xa1, 0xd7, - ], - pkb: [ - 0xe7, 0xe8, 0x5d, 0xe0, 0xf7, 0xf9, 0x7a, 0x46, 0xd2, 0x49, 0xa1, 0xf5, 0xea, 0x51, - 0xdf, 0x50, 0xcc, 0x48, 0x49, 0x0f, 0x84, 0x01, 0xc9, 0xde, 0x7a, 0x2a, 0xdf, 0x18, - 0x07, 0xd1, 0xb6, 0xd4, - ], - npb: [ - 0x65, 0x00, 0x2b, 0xc7, 0x36, 0xfa, 0xf7, 0xa3, 0x42, 0x2e, 0xff, 0xff, 0xe8, 0xb8, - 0x55, 0xe1, 0x8f, 0xba, 0x96, 0xa0, 0x15, 0x8a, 0x9e, 0xfc, 0xa5, 0x84, 0xbf, 0x40, - 0x54, 0x9d, 0x36, 0xe1, - ], - wprb: [ - 0xac, 0x77, 0x6c, 0x79, 0x65, 0x63, 0xfc, 0xd4, 0x4c, 0xc4, 0x9c, 0xfa, 0xea, 0x8b, - 0xb7, 0x96, 0x95, 0x2c, 0x26, 0x6e, 0x47, 0x77, 0x9d, 0x94, 0x57, 0x4c, 0x10, 0xad, - 0x01, 0x75, 0x4b, 0x11, - ], - vcvb: [ - 0xd7, 0xc8, 0x67, 0x06, 0xf5, 0x81, 0x7a, 0xa7, 0x18, 0xcd, 0x1c, 0xfa, 0xd0, 0x32, - 0x33, 0xbc, 0xd6, 0x4a, 0x77, 0x89, 0xfd, 0x94, 0x22, 0xd3, 0xb1, 0x7a, 0xf6, 0x82, - 0x3a, 0x7e, 0x6a, 0xc6, - ], - vcrb: [ - 0x8b, 0x6a, 0x0b, 0x38, 0xb9, 0xfa, 0xae, 0x3c, 0x3b, 0x80, 0x3b, 0x47, 0xb0, 0xf1, - 0x46, 0xad, 0x50, 0xab, 0x22, 0x1e, 0x6e, 0x2a, 0xfb, 0xe6, 0xdb, 0xde, 0x45, 0xcb, - 0xa9, 0xd3, 0x81, 0xed, - ], - }; - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.skb); - } - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::ProofGenerationKey); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.pkb); - } - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::NullifierPosition); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.npb); - } - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::NoteCommitmentRandomness); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.wprb); - } - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::ValueCommitmentValue); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.vcvb); - } - - { - let mut vec = Vec::new(); - let p = JUBJUB.generator(FixedGenerators::ValueCommitmentRandomness); - p.write(&mut vec).unwrap(); - assert_eq!(&vec, &sapling_generators.vcrb); - } -} diff --git a/librustzcash/src/tests/notes.rs b/librustzcash/src/tests/notes.rs deleted file mode 100644 index 6f8c0f5..0000000 --- a/librustzcash/src/tests/notes.rs +++ /dev/null @@ -1,673 +0,0 @@ -use librustzcash_sapling_compute_cm; -use librustzcash_sapling_compute_nf; - -#[test] -fn notes() { - #![allow(dead_code)] - struct TestVector { - sk: [u8; 32], - ask: [u8; 32], - nsk: [u8; 32], - ovk: [u8; 32], - ak: [u8; 32], - nk: [u8; 32], - ivk: [u8; 32], - default_d: [u8; 11], - default_pk_d: [u8; 32], - note_v: u64, - note_r: [u8; 32], - note_cm: [u8; 32], - note_pos: u64, - note_nf: [u8; 32], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py - let test_vectors = vec![ - TestVector { - sk: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - ask: [ - 0x85, 0x48, 0xa1, 0x4a, 0x47, 0x3e, 0xa5, 0x47, 0xaa, 0x23, 0x78, 0x40, 0x20, 0x44, - 0xf8, 0x18, 0xcf, 0x19, 0x11, 0xcf, 0x5d, 0xd2, 0x05, 0x4f, 0x67, 0x83, 0x45, 0xf0, - 0x0d, 0x0e, 0x88, 0x06, - ], - nsk: [ - 0x30, 0x11, 0x4e, 0xa0, 0xdd, 0x0b, 0xb6, 0x1c, 0xf0, 0xea, 0xea, 0xb6, 0xec, 0x33, - 0x31, 0xf5, 0x81, 0xb0, 0x42, 0x5e, 0x27, 0x33, 0x85, 0x01, 0x26, 0x2d, 0x7e, 0xac, - 0x74, 0x5e, 0x6e, 0x05, - ], - ovk: [ - 0x98, 0xd1, 0x69, 0x13, 0xd9, 0x9b, 0x04, 0x17, 0x7c, 0xab, 0xa4, 0x4f, 0x6e, 0x4d, - 0x22, 0x4e, 0x03, 0xb5, 0xac, 0x03, 0x1d, 0x7c, 0xe4, 0x5e, 0x86, 0x51, 0x38, 0xe1, - 0xb9, 0x96, 0xd6, 0x3b, - ], - ak: [ - 0xf3, 0x44, 0xec, 0x38, 0x0f, 0xe1, 0x27, 0x3e, 0x30, 0x98, 0xc2, 0x58, 0x8c, 0x5d, - 0x3a, 0x79, 0x1f, 0xd7, 0xba, 0x95, 0x80, 0x32, 0x76, 0x07, 0x77, 0xfd, 0x0e, 0xfa, - 0x8e, 0xf1, 0x16, 0x20, - ], - nk: [ - 0xf7, 0xcf, 0x9e, 0x77, 0xf2, 0xe5, 0x86, 0x83, 0x38, 0x3c, 0x15, 0x19, 0xac, 0x7b, - 0x06, 0x2d, 0x30, 0x04, 0x0e, 0x27, 0xa7, 0x25, 0xfb, 0x88, 0xfb, 0x19, 0xa9, 0x78, - 0xbd, 0x3f, 0xd6, 0xba, - ], - ivk: [ - 0xb7, 0x0b, 0x7c, 0xd0, 0xed, 0x03, 0xcb, 0xdf, 0xd7, 0xad, 0xa9, 0x50, 0x2e, 0xe2, - 0x45, 0xb1, 0x3e, 0x56, 0x9d, 0x54, 0xa5, 0x71, 0x9d, 0x2d, 0xaa, 0x0f, 0x5f, 0x14, - 0x51, 0x47, 0x92, 0x04, - ], - default_d: [ - 0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39, - ], - default_pk_d: [ - 0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67, - 0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8, - 0x41, 0xae, 0x74, 0x15, - ], - note_v: 0, - note_r: [ - 0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89, - 0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - note_cm: [ - 0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0, - 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, - 0xdd, 0x07, 0x64, 0x39, - ], - note_pos: 0, - note_nf: [ - 0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86, - 0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27, - 0x47, 0xab, 0x40, 0x63, - ], - }, - TestVector { - sk: [ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, - ], - ask: [ - 0xc9, 0x43, 0x56, 0x29, 0xbf, 0x8b, 0xff, 0xe5, 0x5e, 0x73, 0x35, 0xec, 0x07, 0x77, - 0x18, 0xba, 0x60, 0xba, 0x28, 0xd7, 0xac, 0x37, 0x94, 0xb7, 0x4f, 0x51, 0x2c, 0x31, - 0xaf, 0x0a, 0x53, 0x04, - ], - nsk: [ - 0x11, 0xac, 0xc2, 0xea, 0xd0, 0x7b, 0x5f, 0x00, 0x8c, 0x1f, 0x0f, 0x09, 0x0c, 0xc8, - 0xdd, 0xf3, 0x35, 0x23, 0x6f, 0xf4, 0xb2, 0x53, 0xc6, 0x49, 0x56, 0x95, 0xe9, 0xd6, - 0x39, 0xda, 0xcd, 0x08, - ], - ovk: [ - 0x3b, 0x94, 0x62, 0x10, 0xce, 0x6d, 0x1b, 0x16, 0x92, 0xd7, 0x39, 0x2a, 0xc8, 0x4a, - 0x8b, 0xc8, 0xf0, 0x3b, 0x72, 0x72, 0x3c, 0x7d, 0x36, 0x72, 0x1b, 0x80, 0x9a, 0x79, - 0xc9, 0xd6, 0xe4, 0x5b, - ], - ak: [ - 0x82, 0xff, 0x5e, 0xff, 0xc5, 0x27, 0xae, 0x84, 0x02, 0x0b, 0xf2, 0xd3, 0x52, 0x01, - 0xc1, 0x02, 0x19, 0x13, 0x19, 0x47, 0xff, 0x4b, 0x96, 0xf8, 0x81, 0xa4, 0x5f, 0x2e, - 0x8a, 0xe3, 0x05, 0x18, - ], - nk: [ - 0xc4, 0x53, 0x4d, 0x84, 0x8b, 0xb9, 0x18, 0xcf, 0x4a, 0x7f, 0x8b, 0x98, 0x74, 0x0a, - 0xb3, 0xcc, 0xee, 0x58, 0x67, 0x95, 0xff, 0x4d, 0xf6, 0x45, 0x47, 0xa8, 0x88, 0x8a, - 0x6c, 0x74, 0x15, 0xd2, - ], - ivk: [ - 0xc5, 0x18, 0x38, 0x44, 0x66, 0xb2, 0x69, 0x88, 0xb5, 0x10, 0x90, 0x67, 0x41, 0x8d, - 0x19, 0x2d, 0x9d, 0x6b, 0xd0, 0xd9, 0x23, 0x22, 0x05, 0xd7, 0x74, 0x18, 0xc2, 0x40, - 0xfc, 0x68, 0xa4, 0x06, - ], - default_d: [ - 0xae, 0xf1, 0x80, 0xf6, 0xe3, 0x4e, 0x35, 0x4b, 0x88, 0x8f, 0x81, - ], - default_pk_d: [ - 0xa6, 0xb1, 0x3e, 0xa3, 0x36, 0xdd, 0xb7, 0xa6, 0x7b, 0xb0, 0x9a, 0x0e, 0x68, 0xe9, - 0xd3, 0xcf, 0xb3, 0x92, 0x10, 0x83, 0x1e, 0xa3, 0xa2, 0x96, 0xba, 0x09, 0xa9, 0x22, - 0x06, 0x0f, 0xd3, 0x8b, - ], - note_v: 12227227834928555328, - note_r: [ - 0x47, 0x8b, 0xa0, 0xee, 0x6e, 0x1a, 0x75, 0xb6, 0x00, 0x03, 0x6f, 0x26, 0xf1, 0x8b, - 0x70, 0x15, 0xab, 0x55, 0x6b, 0xed, 0xdf, 0x8b, 0x96, 0x02, 0x38, 0x86, 0x9f, 0x89, - 0xdd, 0x80, 0x4e, 0x06, - ], - note_cm: [ - 0xb5, 0x78, 0x93, 0x50, 0x0b, 0xfb, 0x85, 0xdf, 0x2e, 0x8b, 0x01, 0xac, 0x45, 0x2f, - 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, - 0xca, 0xd4, 0x69, 0x50, - ], - note_pos: 763714296, - note_nf: [ - 0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2, - 0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17, - 0x4c, 0x2e, 0x9d, 0x93, - ], - }, - TestVector { - sk: [ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, - ], - ask: [ - 0xee, 0x1c, 0x3d, 0x7e, 0xfe, 0x0a, 0x78, 0x06, 0x3d, 0x6a, 0xf3, 0xd9, 0xd8, 0x12, - 0x12, 0xaf, 0x47, 0xb7, 0xc1, 0xb7, 0x61, 0xf8, 0x5c, 0xcb, 0x06, 0x6f, 0xc1, 0x1a, - 0x6a, 0x42, 0x17, 0x03, - ], - nsk: [ - 0x1d, 0x3b, 0x71, 0x37, 0x55, 0xd7, 0x48, 0x75, 0xe8, 0xea, 0x38, 0xfd, 0x16, 0x6e, - 0x76, 0xc6, 0x2a, 0x42, 0x50, 0x21, 0x6e, 0x6b, 0xbf, 0xe4, 0x8a, 0x5e, 0x2e, 0xab, - 0xad, 0x11, 0x7f, 0x0b, - ], - ovk: [ - 0x8b, 0xf4, 0x39, 0x0e, 0x28, 0xdd, 0xc9, 0x5b, 0x83, 0x02, 0xc3, 0x81, 0xd5, 0x81, - 0x0b, 0x84, 0xba, 0x8e, 0x60, 0x96, 0xe5, 0xa7, 0x68, 0x22, 0x77, 0x4f, 0xd4, 0x9f, - 0x49, 0x1e, 0x8f, 0x49, - ], - ak: [ - 0xab, 0x83, 0x57, 0x4e, 0xb5, 0xde, 0x85, 0x9a, 0x0a, 0xb8, 0x62, 0x9d, 0xec, 0x34, - 0xc7, 0xbe, 0xe8, 0xc3, 0xfc, 0x74, 0xdf, 0xa0, 0xb1, 0x9a, 0x3a, 0x74, 0x68, 0xd1, - 0x5d, 0xca, 0x64, 0xc6, - ], - nk: [ - 0x95, 0xd5, 0x80, 0x53, 0xe0, 0x59, 0x2e, 0x4a, 0x16, 0x9c, 0xc0, 0xb7, 0x92, 0x8a, - 0xaa, 0xc3, 0xde, 0x24, 0xef, 0x15, 0x31, 0xaa, 0x9e, 0xb6, 0xf4, 0xab, 0x93, 0x91, - 0x4d, 0xa8, 0xa0, 0x6e, - ], - ivk: [ - 0x47, 0x1c, 0x24, 0xa3, 0xdc, 0x87, 0x30, 0xe7, 0x50, 0x36, 0xc0, 0xa9, 0x5f, 0x3e, - 0x2f, 0x7d, 0xd1, 0xbe, 0x6f, 0xb9, 0x3a, 0xd2, 0x95, 0x92, 0x20, 0x3d, 0xef, 0x30, - 0x41, 0x95, 0x45, 0x05, - ], - default_d: [ - 0x75, 0x99, 0xf0, 0xbf, 0x9b, 0x57, 0xcd, 0x2d, 0xc2, 0x99, 0xb6, - ], - default_pk_d: [ - 0x66, 0x14, 0x17, 0x39, 0x51, 0x4b, 0x28, 0xf0, 0x5d, 0xef, 0x8a, 0x18, 0xee, 0xee, - 0x5e, 0xed, 0x4d, 0x44, 0xc6, 0x22, 0x5c, 0x3c, 0x65, 0xd8, 0x8d, 0xd9, 0x90, 0x77, - 0x08, 0x01, 0x2f, 0x5a, - ], - note_v: 6007711596147559040, - note_r: [ - 0x14, 0x7c, 0xf2, 0xb5, 0x1b, 0x4c, 0x7c, 0x63, 0xcb, 0x77, 0xb9, 0x9e, 0x8b, 0x78, - 0x3e, 0x5b, 0x51, 0x11, 0xdb, 0x0a, 0x7c, 0xa0, 0x4d, 0x6c, 0x01, 0x4a, 0x1d, 0x7d, - 0xa8, 0x3b, 0xae, 0x0a, - ], - note_cm: [ - 0xdb, 0x85, 0xa7, 0x0a, 0x98, 0x43, 0x7f, 0x73, 0x16, 0x7f, 0xc3, 0x32, 0xd5, 0xb7, - 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, - 0x4e, 0x55, 0xf1, 0x51, - ], - note_pos: 1527428592, - note_nf: [ - 0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f, - 0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f, - 0x59, 0xae, 0x1d, 0x14, - ], - }, - TestVector { - sk: [ - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, - ], - ask: [ - 0x00, 0xc3, 0xa1, 0xe1, 0xca, 0x8f, 0x4e, 0x04, 0x80, 0xee, 0x1e, 0xe9, 0x0c, 0xa7, - 0x51, 0x78, 0x79, 0xd3, 0xfc, 0x5c, 0x81, 0x5c, 0x09, 0x03, 0xe5, 0xee, 0xbc, 0x94, - 0xbb, 0x80, 0x95, 0x03, - ], - nsk: [ - 0xe6, 0x62, 0x85, 0xa5, 0xe9, 0xb6, 0x5e, 0x15, 0x7a, 0xd2, 0xfc, 0xd5, 0x43, 0xda, - 0xd9, 0x8c, 0x67, 0xa5, 0x8a, 0xbd, 0xf2, 0x87, 0xe0, 0x55, 0x06, 0xbd, 0x1c, 0x2e, - 0x59, 0xb0, 0x72, 0x0b, - ], - ovk: [ - 0x14, 0x76, 0x78, 0xe0, 0x55, 0x3b, 0x97, 0x82, 0x93, 0x47, 0x64, 0x7c, 0x5b, 0xc7, - 0xda, 0xb4, 0xcc, 0x22, 0x02, 0xb5, 0x4e, 0xc2, 0x9f, 0xd3, 0x1a, 0x3d, 0xe6, 0xbe, - 0x08, 0x25, 0xfc, 0x5e, - ], - ak: [ - 0x3c, 0x9c, 0xde, 0x7e, 0x5d, 0x0d, 0x38, 0xa8, 0x61, 0x0f, 0xaa, 0xdb, 0xcf, 0x4c, - 0x34, 0x3f, 0x5d, 0x3c, 0xfa, 0x31, 0x55, 0xa5, 0xb9, 0x46, 0x61, 0xa6, 0x75, 0x3e, - 0x96, 0xe8, 0x84, 0xea, - ], - nk: [ - 0xb7, 0x7d, 0x36, 0xf5, 0x08, 0x94, 0x1d, 0xbd, 0x61, 0xcf, 0xd0, 0xf1, 0x59, 0xee, - 0x05, 0xcf, 0xaa, 0x78, 0xa2, 0x6c, 0x94, 0x92, 0x90, 0x38, 0x06, 0xd8, 0x3b, 0x59, - 0x8d, 0x3c, 0x1c, 0x2a, - ], - ivk: [ - 0x63, 0x6a, 0xa9, 0x64, 0xbf, 0xc2, 0x3c, 0xe4, 0xb1, 0xfc, 0xf7, 0xdf, 0xc9, 0x91, - 0x79, 0xdd, 0xc4, 0x06, 0xff, 0x55, 0x40, 0x0c, 0x92, 0x95, 0xac, 0xfc, 0x14, 0xf0, - 0x31, 0xc7, 0x26, 0x00, - ], - default_d: [ - 0x1b, 0x81, 0x61, 0x4f, 0x1d, 0xad, 0xea, 0x0f, 0x8d, 0x0a, 0x58, - ], - default_pk_d: [ - 0x25, 0xeb, 0x55, 0xfc, 0xcf, 0x76, 0x1f, 0xc6, 0x4e, 0x85, 0xa5, 0x88, 0xef, 0xe6, - 0xea, 0xd7, 0x83, 0x2f, 0xb1, 0xf0, 0xf7, 0xa8, 0x31, 0x65, 0x89, 0x5b, 0xdf, 0xf9, - 0x42, 0x92, 0x5f, 0x5c, - ], - note_v: 18234939431076114368, - note_r: [ - 0x34, 0xa4, 0xb2, 0xa9, 0x14, 0x4f, 0xf5, 0xea, 0x54, 0xef, 0xee, 0x87, 0xcf, 0x90, - 0x1b, 0x5b, 0xed, 0x5e, 0x35, 0xd2, 0x1f, 0xbb, 0xd7, 0x88, 0xd5, 0xbd, 0x9d, 0x83, - 0x3e, 0x11, 0x28, 0x04, - ], - note_cm: [ - 0xe0, 0x8c, 0xe4, 0x82, 0xb3, 0xa8, 0xfb, 0x3b, 0x35, 0xcc, 0xdb, 0xe3, 0x43, 0x37, - 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, - 0x60, 0xd1, 0x9b, 0x6c, - ], - note_pos: 2291142888, - note_nf: [ - 0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56, - 0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75, - 0x8b, 0xec, 0x47, 0xa1, - ], - }, - TestVector { - sk: [ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, - ], - ask: [ - 0x82, 0x36, 0xd1, 0x9d, 0x32, 0x05, 0xd8, 0x55, 0x43, 0xa0, 0x68, 0x11, 0x34, 0x3f, - 0x82, 0x7b, 0x65, 0x63, 0x77, 0x0a, 0x49, 0xaa, 0x4d, 0x0c, 0xa0, 0x08, 0x18, 0x05, - 0xd4, 0xc8, 0xea, 0x0d, - ], - nsk: [ - 0x7e, 0xc1, 0xef, 0x0b, 0xed, 0x82, 0x71, 0x82, 0x72, 0xf0, 0xf4, 0x4f, 0x01, 0x7c, - 0x48, 0x41, 0x74, 0x51, 0x3d, 0x66, 0x1d, 0xd1, 0x68, 0xaf, 0x02, 0xd2, 0x09, 0x2a, - 0x1d, 0x8a, 0x05, 0x07, - ], - ovk: [ - 0x1b, 0x6e, 0x75, 0xec, 0xe3, 0xac, 0xe8, 0xdb, 0xa6, 0xa5, 0x41, 0x0d, 0x9a, 0xd4, - 0x75, 0x56, 0x68, 0xe4, 0xb3, 0x95, 0x85, 0xd6, 0x35, 0xec, 0x1d, 0xa7, 0xc8, 0xdc, - 0xfd, 0x5f, 0xc4, 0xed, - ], - ak: [ - 0x55, 0xe8, 0x83, 0x89, 0xbb, 0x7e, 0x41, 0xde, 0x13, 0x0c, 0xfa, 0x51, 0xa8, 0x71, - 0x5f, 0xde, 0x01, 0xff, 0x9c, 0x68, 0x76, 0x64, 0x7f, 0x01, 0x75, 0xad, 0x34, 0xf0, - 0x58, 0xdd, 0xe0, 0x1a, - ], - nk: [ - 0x72, 0x5d, 0x4a, 0xd6, 0xa1, 0x50, 0x21, 0xcd, 0x1c, 0x48, 0xc5, 0xee, 0x19, 0xde, - 0x6c, 0x1e, 0x76, 0x8a, 0x2c, 0xc0, 0xa9, 0xa7, 0x30, 0xa0, 0x1b, 0xb2, 0x1c, 0x95, - 0xe3, 0xd9, 0xe4, 0x3c, - ], - ivk: [ - 0x67, 0xfa, 0x2b, 0xf7, 0xc6, 0x7d, 0x46, 0x58, 0x24, 0x3c, 0x31, 0x7c, 0x0c, 0xb4, - 0x1f, 0xd3, 0x20, 0x64, 0xdf, 0xd3, 0x70, 0x9f, 0xe0, 0xdc, 0xb7, 0x24, 0xf1, 0x4b, - 0xb0, 0x1a, 0x1d, 0x04, - ], - default_d: [ - 0xfc, 0xfb, 0x68, 0xa4, 0x0d, 0x4b, 0xc6, 0xa0, 0x4b, 0x09, 0xc4, - ], - default_pk_d: [ - 0x8b, 0x2a, 0x33, 0x7f, 0x03, 0x62, 0x2c, 0x24, 0xff, 0x38, 0x1d, 0x4c, 0x54, 0x6f, - 0x69, 0x77, 0xf9, 0x05, 0x22, 0xe9, 0x2f, 0xde, 0x44, 0xc9, 0xd1, 0xbb, 0x09, 0x97, - 0x14, 0xb9, 0xdb, 0x2b, - ], - note_v: 12015423192295118080, - note_r: [ - 0xe5, 0x57, 0x85, 0x13, 0x55, 0x74, 0x7c, 0x09, 0xac, 0x59, 0x01, 0x3c, 0xbd, 0xe8, - 0x59, 0x80, 0x96, 0x4e, 0xc1, 0x84, 0x4d, 0x9c, 0x69, 0x67, 0xca, 0x0c, 0x02, 0x9c, - 0x84, 0x57, 0xbb, 0x04, - ], - note_cm: [ - 0xbd, 0xc8, 0x54, 0xbf, 0x3e, 0x7b, 0x00, 0x82, 0x1f, 0x3b, 0x8b, 0x85, 0x23, 0x8c, - 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, - 0xc7, 0x1b, 0x7f, 0x36, - ], - note_pos: 3054857184, - note_nf: [ - 0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f, - 0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03, - 0x01, 0xbf, 0x3d, 0x13, - ], - }, - TestVector { - sk: [ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, - ], - ask: [ - 0xea, 0xe6, 0x88, 0x4d, 0x76, 0x4a, 0x05, 0x40, 0x61, 0xa8, 0xf1, 0xc0, 0x07, 0x6c, - 0x62, 0x4d, 0xcb, 0x73, 0x87, 0x89, 0xf7, 0xad, 0x1e, 0x74, 0x08, 0xe3, 0x1f, 0x24, - 0xdf, 0xc8, 0x26, 0x07, - ], - nsk: [ - 0xfb, 0xe6, 0x10, 0xf4, 0x2a, 0x41, 0x74, 0x9f, 0x9b, 0x6e, 0x6e, 0x4a, 0x54, 0xb5, - 0xa3, 0x2e, 0xbf, 0xe8, 0xf4, 0x38, 0x00, 0x88, 0x1b, 0xa6, 0xcd, 0x13, 0xed, 0x0b, - 0x05, 0x29, 0x46, 0x01, - ], - ovk: [ - 0xc6, 0xbc, 0x1f, 0x39, 0xf0, 0xd7, 0x86, 0x31, 0x4c, 0xb2, 0x0b, 0xf9, 0xab, 0x22, - 0x85, 0x40, 0x91, 0x35, 0x55, 0xf9, 0x70, 0x69, 0x6b, 0x6d, 0x7c, 0x77, 0xbb, 0x33, - 0x23, 0x28, 0x37, 0x2a, - ], - ak: [ - 0xe6, 0x82, 0x76, 0x59, 0x14, 0xe3, 0x86, 0x4c, 0x33, 0x9e, 0x57, 0x82, 0xb8, 0x55, - 0xc0, 0xfd, 0xf4, 0x0e, 0x0d, 0xfc, 0xed, 0xb9, 0xe7, 0xb4, 0x7b, 0xc9, 0x4b, 0x90, - 0xb3, 0xa4, 0xc9, 0x88, - ], - nk: [ - 0x82, 0x25, 0x6b, 0x95, 0x62, 0x3c, 0x67, 0x02, 0x4b, 0x44, 0x24, 0xd9, 0x14, 0x00, - 0xa3, 0x70, 0xe7, 0xac, 0x8e, 0x4d, 0x15, 0x48, 0x2a, 0x37, 0x59, 0xe0, 0x0d, 0x21, - 0x97, 0x49, 0xda, 0xee, - ], - ivk: [ - 0xea, 0x3f, 0x1d, 0x80, 0xe4, 0x30, 0x7c, 0xa7, 0x3b, 0x9f, 0x37, 0x80, 0x1f, 0x91, - 0xfb, 0xa8, 0x10, 0xcc, 0x41, 0xd2, 0x79, 0xfc, 0x29, 0xf5, 0x64, 0x23, 0x56, 0x54, - 0xa2, 0x17, 0x8e, 0x03, - ], - default_d: [ - 0xeb, 0x51, 0x98, 0x82, 0xad, 0x1e, 0x5c, 0xc6, 0x54, 0xcd, 0x59, - ], - default_pk_d: [ - 0x6b, 0x27, 0xda, 0xcc, 0xb5, 0xa8, 0x20, 0x7f, 0x53, 0x2d, 0x10, 0xca, 0x23, 0x8f, - 0x97, 0x86, 0x64, 0x8a, 0x11, 0xb5, 0x96, 0x6e, 0x51, 0xa2, 0xf7, 0xd8, 0x9e, 0x15, - 0xd2, 0x9b, 0x8f, 0xdf, - ], - note_v: 5795906953514121792, - note_r: [ - 0x68, 0xf0, 0x61, 0x04, 0x60, 0x6b, 0x0c, 0x54, 0x49, 0x84, 0x5f, 0xf4, 0xc6, 0x5f, - 0x73, 0xe9, 0x0f, 0x45, 0xef, 0x5a, 0x43, 0xc9, 0xd7, 0x4c, 0xb2, 0xc8, 0x5c, 0xf5, - 0x6c, 0x94, 0xc0, 0x02, - ], - note_cm: [ - 0xe8, 0x26, 0x7d, 0x30, 0xac, 0x11, 0xc1, 0x00, 0xbc, 0x7a, 0x0f, 0xdf, 0x91, 0xf7, - 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, - 0xde, 0x1a, 0x5b, 0x4c, - ], - note_pos: 3818571480, - note_nf: [ - 0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2, - 0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20, - 0xb6, 0xc9, 0xa7, 0xe1, - ], - }, - TestVector { - sk: [ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, - ], - ask: [ - 0xe8, 0xf8, 0x16, 0xb4, 0xbc, 0x08, 0xa7, 0xe5, 0x66, 0x75, 0x0c, 0xc2, 0x8a, 0xfe, - 0x82, 0xa4, 0xce, 0xa9, 0xc2, 0xbe, 0xf2, 0x44, 0xfa, 0x4b, 0x13, 0xc4, 0x73, 0x9b, - 0x28, 0x07, 0x4c, 0x0d, - ], - nsk: [ - 0x32, 0x61, 0x5b, 0x13, 0x7f, 0x28, 0x01, 0xed, 0x44, 0x6e, 0x48, 0x78, 0x1a, 0xb0, - 0x63, 0x45, 0x72, 0xe1, 0x8c, 0xfb, 0x06, 0x93, 0x72, 0x1b, 0x88, 0x03, 0xc0, 0x5b, - 0x82, 0x27, 0xd1, 0x07, - ], - ovk: [ - 0xf6, 0x2c, 0x05, 0xe8, 0x48, 0xa8, 0x73, 0xef, 0x88, 0x5e, 0x12, 0xb0, 0x8c, 0x5e, - 0x7c, 0xa2, 0xf3, 0x24, 0x24, 0xba, 0xcc, 0x75, 0x4c, 0xb6, 0x97, 0x50, 0x44, 0x4d, - 0x35, 0x5f, 0x51, 0x06, - ], - ak: [ - 0xff, 0x27, 0xdb, 0x07, 0x51, 0x94, 0x5d, 0x3e, 0xe4, 0xbe, 0x9c, 0xf1, 0x5c, 0x2e, - 0xa2, 0x11, 0xb2, 0x4b, 0x16, 0x4d, 0x5f, 0x2d, 0x7d, 0xdf, 0xf5, 0xe4, 0xa0, 0x70, - 0x8f, 0x10, 0xb9, 0x5e, - ], - nk: [ - 0x94, 0x38, 0x85, 0x95, 0x9d, 0x4e, 0xf8, 0xa9, 0xcf, 0xca, 0x07, 0xc4, 0x57, 0xf0, - 0x9e, 0xc7, 0x4b, 0x96, 0xf9, 0x93, 0xd8, 0xe0, 0xfa, 0x32, 0xb1, 0x9c, 0x03, 0xe3, - 0xb0, 0x7a, 0x42, 0x0f, - ], - ivk: [ - 0xb5, 0xc5, 0x89, 0x49, 0x43, 0x95, 0x69, 0x33, 0xc0, 0xe5, 0xc1, 0x2d, 0x31, 0x1f, - 0xc1, 0x2c, 0xba, 0x58, 0x35, 0x4b, 0x5c, 0x38, 0x9e, 0xdc, 0x03, 0xda, 0x55, 0x08, - 0x4f, 0x74, 0xc2, 0x05, - ], - default_d: [ - 0xbe, 0xbb, 0x0f, 0xb4, 0x6b, 0x8a, 0xaf, 0xf8, 0x90, 0x40, 0xf6, - ], - default_pk_d: [ - 0xd1, 0x1d, 0xa0, 0x1f, 0x0b, 0x43, 0xbd, 0xd5, 0x28, 0x8d, 0x32, 0x38, 0x5b, 0x87, - 0x71, 0xd2, 0x23, 0x49, 0x3c, 0x69, 0x80, 0x25, 0x44, 0x04, 0x3f, 0x77, 0xcf, 0x1d, - 0x71, 0xc1, 0xcb, 0x8c, - ], - note_v: 18023134788442677120, - note_r: [ - 0x49, 0xf9, 0x0b, 0x47, 0xfd, 0x52, 0xfe, 0xe7, 0xc1, 0xc8, 0x1f, 0x0d, 0xcb, 0x5b, - 0x74, 0xc3, 0xfb, 0x9b, 0x3e, 0x03, 0x97, 0x6f, 0x8b, 0x75, 0x24, 0xea, 0xba, 0xd0, - 0x08, 0x89, 0x21, 0x07, - ], - note_cm: [ - 0x57, 0x2b, 0xa2, 0x05, 0x25, 0xb0, 0xac, 0x4d, 0x6d, 0xc0, 0x1a, 0xc2, 0xea, 0x10, - 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, - 0x78, 0x3a, 0x1e, 0x55, - ], - note_pos: 287318480, - note_nf: [ - 0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae, - 0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4, - 0x07, 0xd5, 0x1e, 0x11, - ], - }, - TestVector { - sk: [ - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, - ], - ask: [ - 0x74, 0xb4, 0x4a, 0x37, 0xf1, 0x50, 0x23, 0xc0, 0x60, 0x42, 0x7e, 0x1d, 0xae, 0xa3, - 0xf6, 0x43, 0x12, 0xdd, 0x8f, 0xeb, 0x7b, 0x2c, 0xed, 0xf0, 0xdd, 0x55, 0x44, 0x49, - 0x3f, 0x87, 0x2c, 0x06, - ], - nsk: [ - 0x07, 0x5c, 0x35, 0xdb, 0x8b, 0x1b, 0x25, 0x75, 0x42, 0x23, 0xec, 0xee, 0x34, 0xab, - 0x73, 0x0d, 0xdd, 0xd1, 0xf1, 0x4a, 0x6a, 0x54, 0xf4, 0xc6, 0xf4, 0x68, 0x45, 0x3c, - 0x3c, 0x6e, 0xd6, 0x0b, - ], - ovk: [ - 0xe9, 0xe0, 0xdc, 0x1e, 0xd3, 0x11, 0xda, 0xed, 0x64, 0xbd, 0x74, 0xda, 0x5d, 0x94, - 0xfe, 0x88, 0xa6, 0xea, 0x41, 0x4b, 0x73, 0x12, 0xde, 0x3d, 0x2a, 0x78, 0xf6, 0x46, - 0x32, 0xbb, 0xe3, 0x73, - ], - ak: [ - 0x28, 0x3f, 0x9a, 0xaf, 0xa9, 0xbc, 0xb3, 0xe6, 0xce, 0x17, 0xe6, 0x32, 0x12, 0x63, - 0x4c, 0xb3, 0xee, 0x55, 0x0c, 0x47, 0x6b, 0x67, 0x6b, 0xd3, 0x56, 0xa6, 0xdf, 0x8a, - 0xdf, 0x51, 0xd2, 0x5e, - ], - nk: [ - 0xdc, 0x4c, 0x67, 0xb1, 0x0d, 0x4b, 0x0a, 0x21, 0x8d, 0xc6, 0xe1, 0x48, 0x70, 0x66, - 0x74, 0x0a, 0x40, 0x93, 0x17, 0x86, 0x6c, 0x32, 0xe6, 0x64, 0xb5, 0x0e, 0x39, 0x7a, - 0xa8, 0x03, 0x89, 0xd4, - ], - ivk: [ - 0x87, 0x16, 0xc8, 0x28, 0x80, 0xe1, 0x36, 0x83, 0xe1, 0xbb, 0x05, 0x9d, 0xd0, 0x6c, - 0x80, 0xc9, 0x01, 0x34, 0xa9, 0x6d, 0x5a, 0xfc, 0xa8, 0xaa, 0xc2, 0xbb, 0xf6, 0x8b, - 0xb0, 0x5f, 0x84, 0x02, - ], - default_d: [ - 0xad, 0x6e, 0x2e, 0x18, 0x5a, 0x31, 0x00, 0xe3, 0xa6, 0xa8, 0xb3, - ], - default_pk_d: [ - 0x32, 0xcb, 0x28, 0x06, 0xb8, 0x82, 0xf1, 0x36, 0x8b, 0x0d, 0x4a, 0x89, 0x8f, 0x72, - 0xc4, 0xc8, 0xf7, 0x28, 0x13, 0x2c, 0xc1, 0x24, 0x56, 0x94, 0x6e, 0x7f, 0x4c, 0xb0, - 0xfb, 0x05, 0x8d, 0xa9, - ], - note_v: 11803618549661680832, - note_r: [ - 0x51, 0x65, 0xaf, 0xf2, 0x2d, 0xd4, 0xed, 0x56, 0xb4, 0xd8, 0x1d, 0x1f, 0x17, 0x1c, - 0xc3, 0xd6, 0x43, 0x2f, 0xed, 0x1b, 0xeb, 0xf2, 0x0a, 0x7b, 0xea, 0xb1, 0x2d, 0xb1, - 0x42, 0xf9, 0x4a, 0x0c, - ], - note_cm: [ - 0xab, 0x7f, 0xc5, 0x66, 0x87, 0x3c, 0xcd, 0xe6, 0x71, 0xf5, 0x98, 0x27, 0x67, 0x85, - 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, - 0xf7, 0x8c, 0x2b, 0x23, - ], - note_pos: 1051032776, - note_nf: [ - 0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f, - 0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc, - 0x8b, 0xb6, 0x98, 0x90, - ], - }, - TestVector { - sk: [ - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, - ], - ask: [ - 0x03, 0x9d, 0xd9, 0x3d, 0xf3, 0x11, 0xff, 0x8f, 0xba, 0xb3, 0xfe, 0x23, 0x02, 0x19, - 0xcd, 0x42, 0xac, 0x87, 0x94, 0x84, 0xf3, 0x0b, 0x90, 0x3a, 0x3c, 0x1e, 0x67, 0xcc, - 0xca, 0x5a, 0x7b, 0x0d, - ], - nsk: [ - 0x04, 0x9f, 0xa1, 0x4f, 0x48, 0x6c, 0x75, 0xb9, 0xfa, 0xd7, 0xe3, 0xb6, 0x73, 0xa4, - 0x43, 0xdd, 0x07, 0x4e, 0xaa, 0x96, 0xed, 0xcb, 0x2a, 0x53, 0xea, 0xaa, 0xbd, 0xaf, - 0x70, 0xff, 0xbb, 0x08, - ], - ovk: [ - 0x14, 0x7d, 0xd1, 0x1d, 0x77, 0xeb, 0xa1, 0xb1, 0x63, 0x6f, 0xd6, 0x19, 0x0c, 0x62, - 0xb9, 0xa5, 0xd0, 0x48, 0x1b, 0xee, 0x7e, 0x91, 0x7f, 0xab, 0x02, 0xe2, 0x18, 0x58, - 0x06, 0x3a, 0xb5, 0x04, - ], - ak: [ - 0x36, 0x40, 0x48, 0xee, 0xdb, 0xe8, 0xca, 0x20, 0x5e, 0xb7, 0xe7, 0xba, 0x0a, 0x90, - 0x12, 0x16, 0x6c, 0x7c, 0x7b, 0xd9, 0xeb, 0x22, 0x8e, 0x08, 0x48, 0x14, 0x48, 0xc4, - 0x88, 0xaa, 0x21, 0xd2, - ], - nk: [ - 0xed, 0x60, 0xaf, 0x1c, 0xe7, 0xdf, 0x38, 0x07, 0x0d, 0x38, 0x51, 0x43, 0x2a, 0x96, - 0x48, 0x0d, 0xb0, 0xb4, 0x17, 0xc3, 0x68, 0x2a, 0x1d, 0x68, 0xe3, 0xe8, 0x93, 0x34, - 0x23, 0x5c, 0x0b, 0xdf, - ], - ivk: [ - 0x99, 0xc9, 0xb4, 0xb8, 0x4f, 0x4b, 0x4e, 0x35, 0x0f, 0x78, 0x7d, 0x1c, 0xf7, 0x05, - 0x1d, 0x50, 0xec, 0xc3, 0x4b, 0x1a, 0x5b, 0x20, 0xd2, 0xd2, 0x13, 0x9b, 0x4a, 0xf1, - 0xf1, 0x60, 0xe0, 0x01, - ], - default_d: [ - 0x21, 0xc9, 0x0e, 0x1c, 0x65, 0x8b, 0x3e, 0xfe, 0x86, 0xaf, 0x58, - ], - default_pk_d: [ - 0x9e, 0x64, 0x17, 0x4b, 0x4a, 0xb9, 0x81, 0x40, 0x5c, 0x32, 0x3b, 0x5e, 0x12, 0x47, - 0x59, 0x45, 0xa4, 0x6d, 0x4f, 0xed, 0xf8, 0x06, 0x08, 0x28, 0x04, 0x1c, 0xd2, 0x0e, - 0x62, 0xfd, 0x2c, 0xef, - ], - note_v: 5584102310880684544, - note_r: [ - 0x8c, 0x3e, 0x56, 0x44, 0x9d, 0xc8, 0x63, 0x54, 0xd3, 0x3b, 0x02, 0x5e, 0xf2, 0x79, - 0x34, 0x60, 0xbc, 0xb1, 0x69, 0xf3, 0x32, 0x4e, 0x4a, 0x6b, 0x64, 0xba, 0xa6, 0x08, - 0x32, 0x31, 0x57, 0x04, - ], - note_cm: [ - 0x7b, 0x48, 0xa8, 0x37, 0x5d, 0x3e, 0xbd, 0x56, 0xbc, 0x64, 0x9b, 0xb5, 0xb5, 0x24, - 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, - 0x8f, 0xea, 0x4d, 0x04, - ], - note_pos: 1814747072, - note_nf: [ - 0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20, - 0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe, - 0xb1, 0x7c, 0xd6, 0x20, - ], - }, - TestVector { - sk: [ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, - ], - ask: [ - 0xeb, 0xbb, 0x40, 0xa9, 0x80, 0xba, 0x3b, 0x88, 0x60, 0x94, 0x8d, 0x01, 0x1e, 0x1b, - 0xfb, 0x4a, 0xff, 0xe1, 0x6c, 0x65, 0x2e, 0x90, 0xe9, 0x82, 0x58, 0x30, 0x2f, 0x44, - 0x64, 0xc9, 0x1e, 0x0c, - ], - nsk: [ - 0x68, 0x43, 0x1b, 0x19, 0x91, 0x04, 0x21, 0x52, 0x00, 0xb9, 0x5e, 0xe5, 0xcb, 0x71, - 0xbf, 0x8b, 0x88, 0x3a, 0x3e, 0x95, 0xb7, 0x98, 0x9c, 0xad, 0x19, 0x70, 0x63, 0x14, - 0x1e, 0xbb, 0xfd, 0x00, - ], - ovk: [ - 0x57, 0x34, 0x67, 0xa7, 0xb3, 0x0e, 0xad, 0x6c, 0xcc, 0x50, 0x47, 0x44, 0xca, 0x9e, - 0x1a, 0x28, 0x1a, 0x0d, 0x1a, 0x08, 0x73, 0x8b, 0x06, 0xa0, 0x68, 0x4f, 0xea, 0xcd, - 0x1e, 0x9d, 0x12, 0x6d, - ], - ak: [ - 0x71, 0xc3, 0x52, 0x3e, 0xec, 0xa3, 0x53, 0x11, 0xfb, 0xd5, 0xd7, 0xe7, 0xd7, 0x0b, - 0x70, 0x9d, 0x6c, 0x35, 0xa2, 0x4f, 0x26, 0x2b, 0x34, 0xbf, 0x64, 0x05, 0x9b, 0xf2, - 0xc0, 0x2e, 0x0b, 0xa8, - ], - nk: [ - 0x62, 0x44, 0x00, 0x10, 0x3b, 0x65, 0x69, 0xb7, 0x35, 0x8f, 0xe8, 0x0f, 0x6f, 0x6c, - 0xad, 0x43, 0x25, 0xde, 0xfd, 0xa9, 0xd9, 0x49, 0x9c, 0x2b, 0x8f, 0x88, 0x6a, 0x62, - 0x69, 0xa2, 0xaa, 0x52, - ], - ivk: [ - 0xdb, 0x95, 0xea, 0x8b, 0xd9, 0xf9, 0x3d, 0x41, 0xb5, 0xab, 0x2b, 0xeb, 0xc9, 0x1a, - 0x38, 0xed, 0xd5, 0x27, 0x08, 0x3e, 0x2a, 0x6e, 0xf9, 0xf3, 0xc2, 0x97, 0x02, 0xd5, - 0xff, 0x89, 0xed, 0x00, - ], - default_d: [ - 0x23, 0x3c, 0x4a, 0xb8, 0x86, 0xa5, 0x5e, 0x3b, 0xa3, 0x74, 0xc0, - ], - default_pk_d: [ - 0xb6, 0x8e, 0x9e, 0xe0, 0xc0, 0x67, 0x8d, 0x7b, 0x30, 0x36, 0x93, 0x1c, 0x83, 0x1a, - 0x25, 0x25, 0x5f, 0x7e, 0xe4, 0x87, 0x38, 0x5a, 0x30, 0x31, 0x6e, 0x15, 0xf6, 0x48, - 0x2b, 0x87, 0x4f, 0xda, - ], - note_v: 17811330145809239872, - note_r: [ - 0x6e, 0xbb, 0xed, 0x74, 0x36, 0x19, 0xa2, 0x56, 0xf9, 0xad, 0x2e, 0x85, 0x88, 0x0c, - 0xfa, 0xa9, 0x09, 0x8a, 0x5f, 0xdb, 0x16, 0x29, 0x99, 0x0d, 0x9a, 0x7d, 0x3b, 0xb9, - 0x3f, 0xc9, 0x00, 0x03, - ], - note_cm: [ - 0xd3, 0x76, 0xa7, 0xbe, 0xe8, 0xce, 0x67, 0xf4, 0xef, 0xde, 0x56, 0xaa, 0x77, 0xcf, - 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, - 0xe4, 0x1d, 0xeb, 0x37, - ], - note_pos: 2578461368, - note_nf: [ - 0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe, - 0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71, - 0x0d, 0x93, 0xc9, 0xe9, - ], - }, - ]; - - for tv in test_vectors { - // Compute commitment and compare with test vector - let mut result = [0u8; 32]; - assert!(librustzcash_sapling_compute_cm( - &tv.default_d, - &tv.default_pk_d, - tv.note_v, - &tv.note_r, - &mut result - )); - assert_eq!(&result, &tv.note_cm); - - // Compute nullifier and compare with test vector - assert!(librustzcash_sapling_compute_nf( - &tv.default_d, - &tv.default_pk_d, - tv.note_v, - &tv.note_r, - &tv.ak, - &tv.nk, - tv.note_pos, - &mut result - )); - assert_eq!(&result, &tv.note_nf); - } -} diff --git a/librustzcash/src/tests/signatures.rs b/librustzcash/src/tests/signatures.rs deleted file mode 100644 index 68ac799..0000000 --- a/librustzcash/src/tests/signatures.rs +++ /dev/null @@ -1,514 +0,0 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; -use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature}; - -use super::JUBJUB; - -#[test] -fn redjubjub_signatures() { - struct TestVector { - sk: [u8; 32], - vk: [u8; 32], - alpha: [u8; 32], - rsk: [u8; 32], - rvk: [u8; 32], - m: [u8; 32], - sig: [u8; 64], - rsig: [u8; 64], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_signatures.py - let test_vectors = vec![ - TestVector { - sk: [ - 0x18, 0xe2, 0x8d, 0xea, 0x5c, 0x11, 0x81, 0x7a, 0xee, 0xb2, 0x1a, 0x19, 0x98, 0x1d, - 0x28, 0x36, 0x8e, 0xc4, 0x38, 0xaf, 0xc2, 0x5a, 0x8d, 0xb9, 0x4e, 0xbe, 0x08, 0xd7, - 0xa0, 0x28, 0x8e, 0x09, - ], - vk: [ - 0x9b, 0x01, 0x53, 0xb0, 0x3d, 0x32, 0x0f, 0xe2, 0x3e, 0x28, 0x34, 0xd5, 0xd6, 0x1d, - 0xbb, 0x1f, 0x51, 0x9b, 0x3f, 0x41, 0xf8, 0xf9, 0x46, 0x15, 0x2b, 0xf0, 0xc3, 0xf2, - 0x47, 0xd1, 0x18, 0x07, - ], - alpha: [ - 0xff, 0xd1, 0xa1, 0x27, 0x32, 0x52, 0xb1, 0x87, 0xf4, 0xed, 0x32, 0x6d, 0xfc, 0x98, - 0x85, 0x3e, 0x29, 0x17, 0xc2, 0xb3, 0x63, 0x79, 0xb1, 0x75, 0xda, 0x63, 0xb9, 0xef, - 0x6d, 0xda, 0x6c, 0x08, - ], - rsk: [ - 0x60, 0x87, 0x38, 0x3b, 0x30, 0x55, 0x9b, 0x31, 0x60, 0x90, 0x85, 0xb9, 0x00, 0x96, - 0x45, 0xce, 0xb6, 0xa0, 0xc6, 0x61, 0x25, 0x99, 0xd7, 0x28, 0x80, 0x72, 0x8e, 0x61, - 0x24, 0x4e, 0x7d, 0x03, - ], - rvk: [ - 0xc1, 0xba, 0xbc, 0xb6, 0xea, 0xe2, 0xb9, 0x94, 0xee, 0x6d, 0x65, 0xc1, 0x0b, 0x9d, - 0xad, 0x59, 0x40, 0xdc, 0x73, 0x5b, 0x07, 0x50, 0x4d, 0xae, 0xd1, 0xe4, 0x6b, 0x07, - 0x09, 0xb4, 0x51, 0x36, - ], - m: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - sig: [ - 0xea, 0xa0, 0x57, 0x47, 0x6b, 0x4a, 0xb4, 0x82, 0x28, 0x8b, 0x93, 0xdf, 0x8f, 0xe0, - 0xc5, 0xce, 0x9d, 0x78, 0x83, 0x67, 0xf2, 0xbe, 0x55, 0x1b, 0x7f, 0x7a, 0x82, 0xa6, - 0xdb, 0x36, 0x04, 0x68, 0xde, 0xb9, 0xa7, 0xb7, 0xaf, 0xaa, 0xdf, 0xec, 0xa6, 0xf4, - 0x81, 0x19, 0x3d, 0xc6, 0x57, 0x57, 0x47, 0xf6, 0x0a, 0x1a, 0x8a, 0x48, 0xff, 0x0a, - 0xd7, 0x0c, 0xf8, 0xcb, 0x8d, 0x52, 0x8e, 0x08, - ], - rsig: [ - 0xd5, 0x6f, 0x0d, 0x91, 0xaf, 0x42, 0x4e, 0x1f, 0x1c, 0x7f, 0xb8, 0x6b, 0xa4, 0xee, - 0xd1, 0x43, 0xcc, 0x16, 0x66, 0x0c, 0x5f, 0xe8, 0xd7, 0xdc, 0x0d, 0x28, 0x4b, 0xcf, - 0x65, 0xa0, 0x89, 0xe9, 0x8b, 0x56, 0x1f, 0x9f, 0x20, 0x1a, 0x63, 0x3d, 0x70, 0x0c, - 0xd3, 0x98, 0x1e, 0x8c, 0xac, 0x07, 0xb5, 0xa8, 0x7e, 0xfa, 0x61, 0x86, 0x06, 0x2d, - 0xd8, 0xe5, 0xd6, 0x32, 0x5e, 0x7b, 0x82, 0x02, - ], - }, - TestVector { - sk: [ - 0x05, 0x96, 0x54, 0xf9, 0x61, 0x27, 0x3d, 0xaf, 0xda, 0x3b, 0x26, 0x77, 0xb3, 0x5c, - 0x18, 0xaf, 0x6b, 0x11, 0xad, 0xfb, 0x9e, 0xe9, 0x0b, 0x48, 0x93, 0x5e, 0x55, 0x7c, - 0x8d, 0x5d, 0x9c, 0x04, - ], - vk: [ - 0xfa, 0xf6, 0xc3, 0xb7, 0x37, 0xe8, 0xe6, 0x11, 0xaa, 0xfe, 0xa5, 0x2f, 0x03, 0xbb, - 0x27, 0x86, 0xe1, 0x83, 0x53, 0xeb, 0xe0, 0xd3, 0x13, 0x9e, 0x3c, 0x54, 0x49, 0x87, - 0x80, 0xc8, 0xc1, 0x99, - ], - alpha: [ - 0xc3, 0x0b, 0x96, 0x20, 0x8d, 0xa8, 0x00, 0xe1, 0x0a, 0xf0, 0x25, 0x42, 0xce, 0x69, - 0x4b, 0x7e, 0xd7, 0x6a, 0x28, 0x29, 0x9f, 0x85, 0x99, 0x8e, 0x5d, 0x61, 0x08, 0x12, - 0x68, 0x1b, 0xf0, 0x03, - ], - rsk: [ - 0xc8, 0xa1, 0xea, 0x19, 0xef, 0xcf, 0x3d, 0x90, 0xe5, 0x2b, 0x4c, 0xb9, 0x81, 0xc6, - 0x63, 0x2d, 0x43, 0x7c, 0xd5, 0x24, 0x3e, 0x6f, 0xa5, 0xd6, 0xf0, 0xbf, 0x5d, 0x8e, - 0xf5, 0x78, 0x8c, 0x08, - ], - rvk: [ - 0xd5, 0x24, 0xdc, 0xe7, 0x73, 0x40, 0x69, 0x75, 0x8a, 0x91, 0xf0, 0x07, 0xa8, 0x69, - 0x50, 0x5d, 0xfc, 0x4a, 0xba, 0x17, 0x20, 0x59, 0x4d, 0x4d, 0x74, 0xf0, 0x07, 0x70, - 0x0e, 0x62, 0xee, 0x00, - ], - m: [ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, - ], - sig: [ - 0x22, 0x35, 0x54, 0x94, 0xa8, 0x31, 0x6a, 0xb1, 0x34, 0x73, 0xf5, 0x5e, 0x62, 0x66, - 0xb2, 0xfb, 0x41, 0x97, 0x31, 0x5e, 0xac, 0x62, 0xf8, 0x2c, 0xc7, 0x3d, 0xca, 0xca, - 0x19, 0x90, 0x90, 0xf1, 0x5b, 0xe1, 0x98, 0xce, 0x7d, 0x3f, 0x9f, 0xc8, 0xff, 0xf5, - 0x50, 0xe1, 0x08, 0x81, 0xec, 0x49, 0xff, 0x27, 0x36, 0x9e, 0x7d, 0x4f, 0xd9, 0x64, - 0x01, 0x53, 0x49, 0x2a, 0x0a, 0x06, 0x25, 0x08, - ], - rsig: [ - 0xf4, 0xb8, 0x94, 0xba, 0x84, 0xce, 0x1e, 0xc3, 0x8a, 0x63, 0x15, 0x2f, 0xc4, 0x09, - 0xf9, 0x47, 0xd6, 0x1a, 0xbb, 0x1f, 0x48, 0x91, 0x63, 0x6b, 0xc3, 0xee, 0x19, 0xef, - 0x6d, 0x4b, 0x30, 0xc0, 0xfd, 0x22, 0x86, 0x6b, 0x84, 0xff, 0xbc, 0x7e, 0x2a, 0x78, - 0xc4, 0x3f, 0x57, 0x83, 0xd2, 0xd2, 0xea, 0xd0, 0x78, 0x59, 0x55, 0x03, 0x74, 0x43, - 0xc2, 0xf4, 0xd5, 0x2f, 0x78, 0x5e, 0xee, 0x07, - ], - }, - TestVector { - sk: [ - 0xad, 0xe7, 0xab, 0xb5, 0x51, 0xc7, 0x9d, 0x0f, 0x0e, 0x42, 0xef, 0x7f, 0x12, 0x06, - 0xb8, 0x77, 0x12, 0xa8, 0x4a, 0x61, 0xde, 0xa3, 0xf3, 0x7b, 0x42, 0x49, 0x6d, 0x7e, - 0xfd, 0x12, 0x52, 0x0c, - ], - vk: [ - 0x36, 0x9e, 0xa7, 0x51, 0x76, 0x2f, 0x83, 0x9d, 0x25, 0x70, 0x1a, 0x5e, 0xeb, 0x55, - 0x1e, 0xc4, 0xf0, 0x6c, 0x12, 0x90, 0xb3, 0xb9, 0xc3, 0xa7, 0x24, 0x40, 0x2d, 0xec, - 0x02, 0x73, 0x92, 0x21, - ], - alpha: [ - 0x81, 0x92, 0x25, 0x29, 0xa6, 0x3e, 0xe7, 0x43, 0xfc, 0x4f, 0xbb, 0xac, 0x45, 0xc4, - 0x98, 0x83, 0x16, 0xbc, 0x9b, 0x6e, 0x42, 0x8b, 0x01, 0xa8, 0xd3, 0x1f, 0xc1, 0xc2, - 0xa6, 0xca, 0x62, 0x05, - ], - rsk: [ - 0x77, 0x4d, 0xda, 0x07, 0x99, 0xf7, 0xed, 0x82, 0x87, 0x81, 0xe2, 0x5f, 0xc4, 0xa9, - 0xe8, 0x54, 0x28, 0x29, 0xb2, 0xce, 0x1f, 0xf4, 0x8d, 0x1d, 0x6d, 0xb9, 0xfa, 0xdb, - 0xb9, 0x28, 0x37, 0x03, - ], - rvk: [ - 0x0d, 0x92, 0xad, 0x6d, 0x46, 0xed, 0xac, 0xd0, 0x23, 0xd4, 0xd2, 0xef, 0x70, 0x3a, - 0x6c, 0xa0, 0xa7, 0x92, 0xcf, 0xc4, 0xb7, 0xda, 0x11, 0xc2, 0x35, 0x3b, 0xc8, 0x45, - 0xa2, 0x7a, 0x97, 0x4d, - ], - m: [ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, - ], - sig: [ - 0xdd, 0x65, 0x21, 0x01, 0x4d, 0xff, 0x70, 0x6e, 0x3a, 0x38, 0x52, 0x7a, 0x86, 0xb6, - 0xc1, 0x6e, 0x94, 0x14, 0x80, 0xe7, 0x33, 0xef, 0xf7, 0x9e, 0xbe, 0x0c, 0x43, 0x03, - 0x79, 0xd7, 0x57, 0x04, 0x9d, 0xb7, 0x90, 0xcd, 0x5e, 0x14, 0x44, 0x7c, 0x38, 0x6f, - 0x5f, 0xcb, 0x41, 0x9f, 0x27, 0xc4, 0x41, 0x3f, 0x35, 0x88, 0xfa, 0x21, 0x42, 0xd2, - 0xcf, 0xba, 0xed, 0x08, 0x2c, 0xc6, 0xdb, 0x07, - ], - rsig: [ - 0xd8, 0x94, 0x45, 0xcb, 0x9b, 0xd1, 0x03, 0x35, 0x69, 0x23, 0x1d, 0xd6, 0x28, 0xaa, - 0x62, 0x81, 0x09, 0xfe, 0x93, 0x50, 0x2b, 0xf2, 0x2f, 0x9a, 0x5f, 0x37, 0xb1, 0x4e, - 0x51, 0x7f, 0x9a, 0x20, 0x54, 0xae, 0xe3, 0xc8, 0x1b, 0x60, 0xb3, 0xf0, 0x55, 0x1e, - 0x32, 0xf7, 0x93, 0x5a, 0xbc, 0x2f, 0x37, 0xb9, 0x9a, 0xb3, 0xec, 0x99, 0x68, 0x02, - 0xef, 0xd6, 0x50, 0x69, 0xe1, 0x28, 0x12, 0x08, - ], - }, - TestVector { - sk: [ - 0xc9, 0xd2, 0xae, 0x1f, 0x6d, 0x32, 0xa6, 0x75, 0xd0, 0x9e, 0xb0, 0x82, 0x3f, 0x46, - 0x7f, 0xa9, 0x21, 0xb3, 0x28, 0x4a, 0xcb, 0x35, 0xfa, 0xbd, 0xfc, 0x99, 0x4d, 0xe5, - 0x49, 0xb8, 0x59, 0x0d, - ], - vk: [ - 0x2d, 0x2f, 0x31, 0x6e, 0x5c, 0x36, 0x9a, 0xe4, 0xdd, 0x2c, 0x82, 0x5f, 0x3d, 0x86, - 0x46, 0x00, 0x58, 0x40, 0x71, 0x84, 0x60, 0x3b, 0x21, 0x2c, 0xf3, 0x45, 0x9f, 0x36, - 0xc8, 0x69, 0x7f, 0xd8, - ], - alpha: [ - 0xeb, 0xbc, 0x89, 0x03, 0x11, 0x07, 0xc4, 0x4f, 0x47, 0x88, 0x9e, 0xd4, 0xd4, 0x37, - 0x5a, 0x41, 0x14, 0xcf, 0x8a, 0x75, 0xdd, 0x33, 0xb9, 0x62, 0xf2, 0xd7, 0x59, 0xd3, - 0xf4, 0xc6, 0xdf, 0x06, - ], - rsk: [ - 0xfd, 0x62, 0x41, 0x4c, 0x1f, 0x2b, 0xd3, 0xf4, 0x94, 0x16, 0x87, 0x8a, 0x80, 0x5d, - 0x71, 0x44, 0x35, 0x47, 0x7f, 0xbe, 0xa7, 0x2e, 0x4c, 0x1a, 0x46, 0xc2, 0x73, 0x53, - 0x54, 0xca, 0xbb, 0x05, - ], - rvk: [ - 0xf0, 0x43, 0x0e, 0x95, 0x3b, 0xe6, 0x0b, 0xf4, 0x38, 0xdb, 0xdc, 0xc2, 0x30, 0x3f, - 0x0e, 0x32, 0xa6, 0xf7, 0xce, 0x2f, 0xbe, 0xdf, 0xb1, 0x3a, 0xc5, 0x18, 0xf7, 0x5a, - 0x3f, 0xd1, 0x0e, 0xb5, - ], - m: [ - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, - ], - sig: [ - 0x72, 0x79, 0xa7, 0x5c, 0x01, 0x36, 0x75, 0xb3, 0x29, 0x84, 0xe5, 0xc7, 0x3a, 0x98, - 0x91, 0xeb, 0xf0, 0xb2, 0x29, 0xb1, 0x6e, 0x62, 0x35, 0xba, 0x36, 0xdf, 0xa1, 0xb5, - 0xa1, 0x0c, 0x5e, 0x44, 0x57, 0x81, 0x91, 0x89, 0x7c, 0x06, 0xb8, 0x52, 0x4a, 0x26, - 0x74, 0xaa, 0x7a, 0x0c, 0x8c, 0x23, 0x5f, 0x52, 0xd3, 0x3a, 0xc9, 0x2c, 0x70, 0x56, - 0xb2, 0xbe, 0x95, 0x3c, 0x3f, 0xaa, 0x3d, 0x07, - ], - rsig: [ - 0xaa, 0xd4, 0x82, 0x8c, 0xb3, 0x42, 0xcf, 0x09, 0xb0, 0x0e, 0x30, 0x2c, 0xbb, 0xe7, - 0xcc, 0x3e, 0x95, 0xfe, 0x1f, 0xf8, 0x28, 0x74, 0x8e, 0x5f, 0x5b, 0xc6, 0x9c, 0xbf, - 0xde, 0x6e, 0x27, 0x22, 0xd7, 0x64, 0x35, 0x68, 0x7e, 0x85, 0x0c, 0xd3, 0x07, 0xa9, - 0xc1, 0x82, 0xec, 0x10, 0xe6, 0x88, 0x1d, 0xd6, 0x5e, 0xed, 0xc1, 0x1f, 0xa7, 0xb4, - 0x6d, 0xe3, 0xa7, 0x19, 0x59, 0xce, 0xc0, 0x02, - ], - }, - TestVector { - sk: [ - 0x33, 0xbc, 0xd2, 0x86, 0x45, 0x41, 0xb8, 0xbb, 0x7f, 0xdc, 0x77, 0xa1, 0x9d, 0x97, - 0x0f, 0x92, 0x4e, 0xae, 0xec, 0xf4, 0x10, 0x3c, 0x38, 0xc8, 0xd2, 0xb0, 0x66, 0x81, - 0x42, 0xf2, 0x7d, 0x09, - ], - vk: [ - 0x74, 0x17, 0x94, 0xe6, 0x2c, 0xf9, 0x32, 0x0c, 0x58, 0xba, 0xc5, 0x94, 0xa2, 0xb9, - 0x0e, 0x34, 0x0a, 0x6d, 0x8a, 0x68, 0x05, 0x6f, 0x6e, 0xd5, 0xc7, 0x86, 0x8c, 0x5f, - 0xf3, 0xe4, 0xd6, 0x16, - ], - alpha: [ - 0x7c, 0xe7, 0x25, 0xa5, 0xfe, 0xf6, 0x1b, 0xd4, 0xa1, 0xe9, 0xc7, 0x73, 0x28, 0xe8, - 0x21, 0x0e, 0xb7, 0x29, 0x2d, 0x95, 0x4c, 0x64, 0xe9, 0x9e, 0x8b, 0xed, 0xd0, 0x7a, - 0xb3, 0xab, 0x0e, 0x0d, - ], - rsk: [ - 0xf8, 0x76, 0x01, 0x55, 0xe5, 0x29, 0x3d, 0xbf, 0x9e, 0xb5, 0x77, 0x48, 0x32, 0x5f, - 0xc9, 0xf9, 0x04, 0x9d, 0xe5, 0x88, 0x5c, 0x65, 0xba, 0x60, 0xb5, 0xee, 0x03, 0x97, - 0x0b, 0xe9, 0x0e, 0x08, - ], - rvk: [ - 0x66, 0x62, 0xba, 0x09, 0x95, 0x0a, 0xcc, 0xd2, 0xce, 0xa3, 0xc7, 0xa8, 0x12, 0x90, - 0xcd, 0x59, 0x78, 0xa6, 0x2b, 0x5a, 0xc5, 0xbb, 0xc4, 0x8d, 0x9f, 0x58, 0x19, 0xcd, - 0xc9, 0x64, 0x6f, 0x0a, - ], - m: [ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, - ], - sig: [ - 0x51, 0x23, 0xb3, 0x1f, 0x84, 0xaf, 0x0c, 0x35, 0x5e, 0x13, 0xe7, 0x8a, 0x64, 0xd7, - 0xa3, 0xcd, 0xfd, 0x6b, 0xdf, 0xfd, 0xc7, 0x33, 0x38, 0xd9, 0x31, 0x7f, 0x73, 0x43, - 0x91, 0xa5, 0x5a, 0xe6, 0x25, 0x8f, 0x69, 0x80, 0xb9, 0xc7, 0xd1, 0x90, 0xcf, 0xa3, - 0x65, 0x81, 0xa9, 0xa4, 0x7a, 0x86, 0x3f, 0xd3, 0xbf, 0x76, 0x59, 0x42, 0x22, 0x95, - 0xb7, 0x5f, 0xd1, 0x22, 0xc3, 0xdd, 0x8a, 0x05, - ], - rsig: [ - 0x5b, 0xae, 0x25, 0x4f, 0xbd, 0xed, 0x60, 0x7a, 0x5c, 0x48, 0xb5, 0x30, 0x29, 0xf5, - 0x9b, 0xa7, 0x06, 0x32, 0x48, 0x79, 0xaa, 0x18, 0xd9, 0xc4, 0x73, 0x19, 0x00, 0x4b, - 0xe0, 0x2c, 0xec, 0xe0, 0xb8, 0xbb, 0x02, 0x4a, 0x7a, 0xab, 0xaa, 0x0a, 0x64, 0x0f, - 0x3a, 0x54, 0xdc, 0xda, 0xf2, 0x11, 0x31, 0x46, 0x9a, 0x50, 0x06, 0xbe, 0x27, 0x81, - 0xa5, 0x67, 0xff, 0xa6, 0x50, 0x3a, 0x35, 0x03, - ], - }, - TestVector { - sk: [ - 0xca, 0x35, 0x06, 0xd6, 0xaf, 0x77, 0x67, 0xb5, 0x79, 0x0e, 0xf0, 0xc5, 0x19, 0x0f, - 0xb3, 0xf3, 0x87, 0x7c, 0x4a, 0xab, 0x40, 0xe0, 0xdd, 0x65, 0x1a, 0xbb, 0xda, 0xcb, - 0x54, 0x4e, 0xd0, 0x05, - ], - vk: [ - 0xba, 0xb6, 0xcf, 0xb5, 0xc8, 0xea, 0x34, 0x91, 0x25, 0x1b, 0x46, 0xd5, 0x2a, 0xca, - 0x25, 0xd9, 0xe9, 0xaf, 0x69, 0xfa, 0xa9, 0xb4, 0xe4, 0x0b, 0x03, 0xad, 0x00, 0x86, - 0xde, 0x59, 0xb5, 0x1f, - ], - alpha: [ - 0xbe, 0xa3, 0x87, 0x20, 0x3f, 0x43, 0x76, 0x0a, 0xd3, 0x7d, 0x61, 0xde, 0x0e, 0xb5, - 0x9f, 0xca, 0x6c, 0xab, 0x75, 0x60, 0xdf, 0x64, 0xfa, 0xbb, 0x95, 0x11, 0x57, 0x9f, - 0x6f, 0x68, 0x26, 0x06, - ], - rsk: [ - 0x88, 0xd9, 0x8d, 0xf6, 0xee, 0xba, 0xdd, 0xbf, 0x4c, 0x8c, 0x51, 0xa4, 0x28, 0xc4, - 0x52, 0xbe, 0xf4, 0x27, 0xc0, 0x0b, 0x20, 0x45, 0xd8, 0x21, 0xb0, 0xcc, 0x31, 0x6b, - 0xc4, 0xb6, 0xf6, 0x0b, - ], - rvk: [ - 0x11, 0x26, 0x7d, 0x14, 0xd5, 0xe0, 0xb2, 0xbb, 0x3c, 0xe0, 0x99, 0xe8, 0xef, 0x84, - 0x49, 0x47, 0x1c, 0xbc, 0xfc, 0x69, 0x39, 0xa4, 0xb3, 0x48, 0xde, 0xa2, 0xc1, 0x73, - 0x56, 0xa1, 0xe8, 0xdd, - ], - m: [ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, - ], - sig: [ - 0xdc, 0x18, 0xc8, 0x8d, 0x96, 0x44, 0x42, 0x40, 0x6d, 0x65, 0x0a, 0xa2, 0xff, 0xbd, - 0x83, 0xd1, 0x13, 0xbf, 0x6a, 0x19, 0xda, 0x78, 0xf2, 0x66, 0x5b, 0x29, 0x4f, 0xa5, - 0xfa, 0x45, 0x0b, 0x92, 0x81, 0xa0, 0x7e, 0x32, 0x0c, 0x1a, 0xa3, 0x1d, 0x32, 0x44, - 0x9e, 0x00, 0xc5, 0xc3, 0x2d, 0xb2, 0xf4, 0x13, 0xdf, 0x0b, 0x63, 0xd0, 0x72, 0x8f, - 0xa4, 0x09, 0x41, 0xa8, 0xda, 0x02, 0x4f, 0x01, - ], - rsig: [ - 0x59, 0xe2, 0xe8, 0x18, 0x76, 0x6c, 0x50, 0xfc, 0x8f, 0x38, 0x40, 0xb2, 0x72, 0xaf, - 0x9a, 0xd9, 0x47, 0x56, 0xc8, 0x41, 0x32, 0x95, 0xfc, 0x79, 0x5f, 0xaf, 0xbc, 0xc0, - 0x71, 0x8e, 0x6c, 0x08, 0x16, 0x9a, 0x00, 0xd5, 0x83, 0x02, 0x77, 0x2a, 0x28, 0x28, - 0x43, 0xe8, 0x88, 0xd9, 0x81, 0xfa, 0x04, 0x79, 0x5d, 0x01, 0x4c, 0xf9, 0xc8, 0xcd, - 0xb9, 0x07, 0xff, 0x1b, 0x43, 0x0d, 0x92, 0x00, - ], - }, - TestVector { - sk: [ - 0xbc, 0x27, 0x83, 0x8d, 0xe2, 0xa6, 0x14, 0xcf, 0xba, 0x6c, 0x3e, 0x92, 0x2a, 0x8f, - 0x84, 0x24, 0xd9, 0x85, 0x6f, 0x68, 0x16, 0xf3, 0xbc, 0x61, 0x02, 0x31, 0x3b, 0x7f, - 0xaf, 0x5c, 0x3a, 0x0c, - ], - vk: [ - 0xd7, 0x9b, 0xe9, 0xff, 0x22, 0x9a, 0x2e, 0x35, 0xf5, 0xbc, 0xa4, 0x48, 0xe5, 0xeb, - 0x4a, 0x8a, 0xa9, 0x7f, 0xb4, 0x18, 0x02, 0x91, 0x25, 0xcf, 0xba, 0xa7, 0x8a, 0x91, - 0xa3, 0x82, 0xb0, 0x94, - ], - alpha: [ - 0x21, 0xa7, 0x15, 0x0e, 0x19, 0x4f, 0xed, 0xfe, 0xf9, 0x0c, 0x5d, 0x10, 0xe4, 0x20, - 0x85, 0x8b, 0xca, 0x40, 0x04, 0x04, 0x0e, 0xb6, 0x81, 0xd1, 0x4e, 0x75, 0xc4, 0x47, - 0x13, 0x51, 0xcb, 0x02, - ], - rsk: [ - 0x26, 0xa2, 0xa1, 0xc4, 0x9c, 0xe7, 0x6a, 0xfd, 0x31, 0x69, 0xd3, 0xd5, 0x7a, 0x8f, - 0xa1, 0x09, 0xa3, 0x8b, 0x3f, 0x6b, 0x23, 0x6e, 0xd7, 0x2c, 0xa8, 0xf6, 0xcb, 0x61, - 0xd8, 0xf8, 0x87, 0x00, - ], - rvk: [ - 0x54, 0xbf, 0x1b, 0xe7, 0x2e, 0x6d, 0x41, 0x20, 0x8b, 0x8a, 0xec, 0x11, 0x61, 0xd3, - 0xba, 0x59, 0x51, 0x9f, 0xb9, 0x3d, 0xa0, 0x1a, 0x55, 0xe6, 0x78, 0xe2, 0x75, 0x20, - 0x06, 0x60, 0x36, 0xc9, - ], - m: [ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, - ], - sig: [ - 0x9a, 0xf6, 0xf2, 0x80, 0x0f, 0x4b, 0x80, 0xf7, 0x93, 0xbe, 0x64, 0x8a, 0x43, 0x9f, - 0x86, 0xe5, 0x7d, 0xa1, 0xb9, 0x19, 0x99, 0x9e, 0x41, 0x91, 0x09, 0x99, 0xd4, 0x2e, - 0xd0, 0xf3, 0x89, 0x6d, 0xb7, 0x6e, 0x06, 0x38, 0x8b, 0x27, 0x2c, 0x99, 0x85, 0x8b, - 0x55, 0x04, 0xd0, 0x2e, 0xc6, 0xb4, 0xd5, 0x25, 0xb8, 0x71, 0x38, 0x10, 0x50, 0x5f, - 0x4f, 0xc0, 0x31, 0x08, 0x3a, 0x14, 0xbf, 0x09, - ], - rsig: [ - 0x3f, 0x7d, 0x50, 0x71, 0xb8, 0x76, 0x17, 0x49, 0x05, 0x71, 0xa8, 0xbe, 0x91, 0x74, - 0x9e, 0x69, 0xf6, 0xbc, 0xba, 0x5a, 0xb6, 0x26, 0xe4, 0x2f, 0xf9, 0x2d, 0x0d, 0x7d, - 0xab, 0x73, 0xf3, 0x03, 0x61, 0xe5, 0xa2, 0x24, 0x99, 0x8e, 0x1f, 0x5e, 0xa1, 0xe5, - 0xf8, 0x68, 0x9a, 0x06, 0xa2, 0x77, 0x48, 0xbf, 0x74, 0x19, 0x63, 0xef, 0x51, 0x33, - 0x22, 0xf4, 0xa1, 0xba, 0x99, 0xaa, 0x36, 0x03, - ], - }, - TestVector { - sk: [ - 0xb2, 0x08, 0x59, 0xb8, 0x8e, 0xe3, 0x33, 0x8a, 0x64, 0x95, 0x4f, 0x8a, 0x9e, 0x8e, - 0x9b, 0xf3, 0xe7, 0x11, 0x5a, 0xcf, 0x7c, 0x6e, 0x7f, 0x01, 0x43, 0x2c, 0x5f, 0x76, - 0x96, 0xd2, 0xd0, 0x05, - ], - vk: [ - 0xa8, 0x1f, 0xe6, 0x84, 0x6d, 0xbe, 0x0a, 0x75, 0xc0, 0xf4, 0x9b, 0x21, 0x32, 0x32, - 0xbe, 0xad, 0xd1, 0xf9, 0xa5, 0x64, 0x67, 0x3d, 0x25, 0xb9, 0x1e, 0xe0, 0xf1, 0x7c, - 0xe9, 0xca, 0xa3, 0x63, - ], - alpha: [ - 0x44, 0xd9, 0x08, 0xe1, 0xc1, 0x5e, 0x6b, 0xd9, 0x38, 0x0a, 0x8b, 0x23, 0x5a, 0xce, - 0x02, 0xfa, 0xc1, 0xc0, 0x87, 0x94, 0x45, 0x4b, 0xcd, 0xb4, 0xa6, 0xf4, 0x8c, 0xea, - 0x78, 0xa7, 0x4a, 0x04, - ], - rsk: [ - 0xf6, 0xe1, 0x61, 0x99, 0x50, 0x42, 0x9f, 0x63, 0x9d, 0x9f, 0xda, 0xad, 0xf8, 0x5c, - 0x9e, 0xed, 0xa9, 0xd2, 0xe1, 0x63, 0xc2, 0xb9, 0x4c, 0xb6, 0xe9, 0x20, 0xec, 0x60, - 0x0f, 0x7a, 0x1b, 0x0a, - ], - rvk: [ - 0x0b, 0x68, 0xd5, 0x0f, 0x91, 0x3c, 0xd1, 0xb7, 0x8b, 0x59, 0x92, 0x1e, 0x16, 0x56, - 0xd5, 0x76, 0xb0, 0xeb, 0x17, 0x1e, 0xd3, 0x87, 0x0d, 0x39, 0xfe, 0xc6, 0x94, 0x41, - 0xb3, 0x4b, 0x25, 0x38, - ], - m: [ - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, - ], - sig: [ - 0x64, 0x59, 0x67, 0x6a, 0x94, 0x16, 0x34, 0xec, 0xb6, 0x1e, 0x59, 0xb7, 0x9a, 0x98, - 0xab, 0xe5, 0x87, 0x6f, 0x35, 0x6f, 0x72, 0x8a, 0xa0, 0x9e, 0x0c, 0xca, 0x9e, 0xfe, - 0x05, 0x76, 0x1a, 0x33, 0x09, 0xaa, 0x88, 0xb2, 0xfa, 0x0e, 0xe2, 0xd0, 0x4c, 0x1c, - 0x46, 0xe9, 0xf2, 0xa0, 0x48, 0xd5, 0x9d, 0x55, 0x65, 0xaf, 0xa6, 0xc3, 0xf1, 0x5b, - 0xce, 0x70, 0x8d, 0xaa, 0xab, 0x7b, 0x34, 0x0e, - ], - rsig: [ - 0xc9, 0x66, 0x84, 0xec, 0x7e, 0xa6, 0x0b, 0xde, 0x87, 0x88, 0x22, 0xdd, 0xca, 0xf6, - 0xb8, 0xb0, 0xbd, 0x31, 0x98, 0x51, 0x54, 0xdf, 0x9a, 0xd4, 0xf6, 0x90, 0x7d, 0xf8, - 0xfe, 0xd9, 0x5c, 0x1d, 0x84, 0xfe, 0x67, 0xe6, 0x78, 0x75, 0xa5, 0x39, 0x55, 0x0e, - 0xb2, 0x51, 0x4f, 0x19, 0x3b, 0x8e, 0xd4, 0x57, 0x25, 0x6c, 0x8d, 0x30, 0x28, 0x1d, - 0x6f, 0x8b, 0xb9, 0x54, 0x49, 0x24, 0xca, 0x0c, - ], - }, - TestVector { - sk: [ - 0x32, 0x16, 0xae, 0x47, 0xe9, 0xf5, 0x3e, 0x8a, 0x52, 0x79, 0x6f, 0x24, 0xb6, 0x24, - 0x60, 0x77, 0x6b, 0xd5, 0xf2, 0x05, 0xa7, 0x8e, 0x15, 0x95, 0xbc, 0x8e, 0xfe, 0xdc, - 0x51, 0x9d, 0x36, 0x0b, - ], - vk: [ - 0xdf, 0x74, 0xbf, 0x04, 0x79, 0x61, 0xcc, 0x5c, 0xda, 0xc8, 0x28, 0x90, 0xc7, 0x6e, - 0xc6, 0x75, 0xbd, 0x4e, 0x89, 0xea, 0xd2, 0x80, 0xc9, 0x52, 0xd7, 0xc3, 0x3e, 0xea, - 0xf2, 0xb5, 0xa6, 0x6b, - ], - alpha: [ - 0xc9, 0x61, 0xf2, 0xdd, 0x93, 0x68, 0x2a, 0xdb, 0x93, 0xf5, 0xc0, 0x5a, 0x73, 0xfd, - 0xbc, 0x6d, 0x43, 0xc7, 0x0e, 0x1b, 0x15, 0xe8, 0xd5, 0x3e, 0x3f, 0x17, 0xa8, 0x24, - 0x94, 0xe3, 0xf2, 0x09, - ], - rsk: [ - 0x44, 0x4b, 0xa9, 0x4e, 0x1e, 0x50, 0xd2, 0x94, 0x63, 0x5e, 0x68, 0xb2, 0x95, 0x01, - 0xb5, 0x3e, 0xae, 0x61, 0xcd, 0x1f, 0xbb, 0x3b, 0x84, 0xcd, 0x52, 0xf6, 0x72, 0x9c, - 0xfb, 0xcb, 0xab, 0x06, - ], - rvk: [ - 0x0a, 0xfb, 0xe4, 0x06, 0xa8, 0x91, 0xc3, 0xb8, 0xc3, 0x10, 0xc2, 0x15, 0xbc, 0x68, - 0xa9, 0x13, 0xde, 0x7c, 0xda, 0x06, 0xaf, 0x29, 0x42, 0x00, 0x56, 0x46, 0x8d, 0x0c, - 0x08, 0x85, 0x5b, 0x28, - ], - m: [ - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, - ], - sig: [ - 0x24, 0x93, 0x2c, 0x1f, 0xaa, 0x01, 0x63, 0xca, 0x9a, 0x7f, 0xcd, 0xe4, 0x76, 0x11, - 0x29, 0xd2, 0xe5, 0xe9, 0x9c, 0xf5, 0xef, 0xa2, 0x5d, 0x27, 0x04, 0x58, 0x8e, 0x1c, - 0x75, 0x67, 0x7b, 0x5e, 0xeb, 0xe4, 0x55, 0x04, 0x8d, 0x7c, 0xe1, 0xb0, 0xd2, 0x01, - 0x27, 0x53, 0xf7, 0x1b, 0x27, 0x25, 0x01, 0x2e, 0xe1, 0x85, 0x49, 0x28, 0x73, 0x18, - 0xf9, 0xcd, 0x73, 0xf0, 0x7f, 0x0f, 0xb5, 0x02, - ], - rsig: [ - 0xf7, 0xfa, 0x26, 0xca, 0x22, 0xf3, 0x86, 0xc4, 0x3c, 0x19, 0x1a, 0x0b, 0x3e, 0xa6, - 0x57, 0x7e, 0x8e, 0xea, 0xa3, 0xf3, 0x6b, 0x9b, 0xd1, 0xa3, 0xac, 0x3d, 0xf6, 0xf8, - 0x83, 0xa3, 0xff, 0xdb, 0x31, 0x32, 0x0b, 0xde, 0x62, 0x7f, 0xf4, 0x6f, 0xc2, 0x26, - 0x4a, 0x32, 0x63, 0xb9, 0xab, 0x67, 0x12, 0x3b, 0xa5, 0xe1, 0x08, 0x43, 0x20, 0xd9, - 0x10, 0xb3, 0x94, 0xef, 0x8c, 0x65, 0xba, 0x09, - ], - }, - TestVector { - sk: [ - 0x85, 0x83, 0x6f, 0x98, 0x32, 0xb2, 0x8d, 0xe7, 0xc6, 0x36, 0x13, 0xe2, 0xa6, 0xed, - 0x36, 0xfb, 0x1a, 0xb4, 0x4f, 0xb0, 0xc1, 0x3f, 0xa8, 0x79, 0x8c, 0xd9, 0xcd, 0x30, - 0x30, 0xd4, 0x55, 0x03, - ], - vk: [ - 0xbf, 0xd5, 0xbc, 0x00, 0xc7, 0xc0, 0x22, 0xaa, 0x89, 0x01, 0xae, 0x08, 0x3c, 0x12, - 0xd5, 0x4b, 0x82, 0xf0, 0xdd, 0xff, 0x8e, 0xd6, 0xdb, 0x9a, 0x12, 0xd5, 0x9a, 0x5e, - 0xf6, 0xa5, 0xa2, 0xe0, - ], - alpha: [ - 0xa2, 0xe8, 0xb9, 0xe1, 0x6d, 0x6f, 0xf3, 0xca, 0x6c, 0x53, 0xd4, 0xe8, 0x8a, 0xbb, - 0xb9, 0x9b, 0xe7, 0xaf, 0x7e, 0x36, 0x59, 0x63, 0x1f, 0x1e, 0xae, 0x1e, 0xff, 0x23, - 0x87, 0x4d, 0x8e, 0x0c, - ], - rsk: [ - 0x70, 0x3f, 0x32, 0xa3, 0x41, 0x13, 0xea, 0xe1, 0xb0, 0x79, 0x1f, 0xfe, 0x9d, 0x88, - 0x88, 0xf0, 0x01, 0x29, 0x9a, 0xe5, 0x19, 0x68, 0x60, 0x91, 0x91, 0x48, 0x99, 0xef, - 0xcc, 0x6c, 0x66, 0x01, - ], - rvk: [ - 0xeb, 0x92, 0x97, 0x03, 0x6c, 0xf5, 0x17, 0xe1, 0x5e, 0x9e, 0xfe, 0x39, 0x75, 0x32, - 0x8d, 0xb4, 0x8e, 0xe7, 0xc2, 0x69, 0x4e, 0x94, 0x6d, 0xb2, 0x5f, 0x52, 0x87, 0x88, - 0xf6, 0xa1, 0xdb, 0x14, - ], - m: [ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, - ], - sig: [ - 0x64, 0xab, 0xd1, 0x25, 0xbf, 0xc4, 0xc6, 0x54, 0xfa, 0xf2, 0xb6, 0xdd, 0x75, 0x3e, - 0xc6, 0x90, 0x22, 0x4d, 0xbc, 0xab, 0x8c, 0xd6, 0x32, 0xdd, 0x59, 0x3c, 0x91, 0xce, - 0x3a, 0xb0, 0xbc, 0xad, 0xca, 0x92, 0x76, 0x34, 0x02, 0x1c, 0x31, 0x47, 0x6c, 0x78, - 0xc5, 0xac, 0x7c, 0xcc, 0xab, 0xbd, 0x6f, 0x92, 0x7d, 0xf2, 0x05, 0xea, 0xa7, 0x07, - 0xcc, 0x00, 0xd4, 0x7d, 0x39, 0xf3, 0xe4, 0x0c, - ], - rsig: [ - 0xeb, 0x7a, 0x06, 0x5d, 0x75, 0xf8, 0x45, 0xdc, 0x09, 0x41, 0xb7, 0x09, 0xc0, 0xb1, - 0x49, 0xea, 0xfd, 0x80, 0x5e, 0xa5, 0x8f, 0x38, 0x0b, 0x92, 0xb9, 0xd3, 0x10, 0x8a, - 0x56, 0x1b, 0xda, 0x17, 0x85, 0xdf, 0x8f, 0x10, 0x1e, 0x0e, 0x14, 0x0f, 0xca, 0xee, - 0x99, 0xb7, 0xdb, 0xb7, 0xdf, 0xbf, 0x7e, 0x61, 0xf3, 0xa1, 0x2f, 0x46, 0x09, 0x50, - 0x69, 0xe0, 0x6e, 0x88, 0x96, 0xa9, 0xe4, 0x04, - ], - }, - ]; - - for tv in test_vectors { - let sk = PrivateKey::::read(&tv.sk[..]).unwrap(); - let vk = PublicKey::::read(&tv.vk[..], &JUBJUB).unwrap(); - let rvk = PublicKey::::read(&tv.rvk[..], &JUBJUB).unwrap(); - let sig = Signature::read(&tv.sig[..]).unwrap(); - let rsig = Signature::read(&tv.rsig[..]).unwrap(); - - let mut alpha_repr = <::Fs as PrimeField>::Repr::default(); - alpha_repr.read_le(&tv.alpha[..]).unwrap(); - let alpha = ::Fs::from_repr(alpha_repr).unwrap(); - - { - let mut vec = Vec::new(); - sk.randomize(alpha.clone()).write(&mut vec).unwrap(); - assert_eq!(&vec, &tv.rsk); - } - { - let mut vec = Vec::new(); - vk.randomize(alpha, FixedGenerators::SpendingKeyGenerator, &JUBJUB) - .write(&mut vec) - .unwrap(); - assert_eq!(&vec, &tv.rvk); - } - - assert!(vk.verify(&tv.m, &sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); - assert!(rvk.verify(&tv.m, &rsig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); - assert!(!vk.verify(&tv.m, &rsig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); - assert!(!rvk.verify(&tv.m, &sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); - } -} diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml index 759fd3d..5bd5e1e 100644 --- a/pairing/Cargo.toml +++ b/pairing/Cargo.toml @@ -2,28 +2,39 @@ name = "pairing" # Remember to change version string in README.md. -version = "0.14.2" +version = "0.16.0" authors = [ "Sean Bowe ", "Jack Grigg ", ] +readme = "README.md" license = "MIT/Apache-2.0" description = "Pairing-friendly elliptic curve library" documentation = "https://docs.rs/pairing/" homepage = "https://github.com/ebfull/pairing" repository = "https://github.com/ebfull/pairing" +edition ="2018" [dependencies] byteorder = "1" -ff = { path = "../ff", features = ["derive"] } -group = { path = "../group" } +ff = { version = "0.6", path = "../ff", features = ["derive"] } +group = { version = "0.6", path = "../group" } rand_core = "0.5" +subtle = "2.2.1" [dev-dependencies] +criterion = "0.3" rand_xorshift = "0.2" [features] unstable-features = ["expose-arith"] expose-arith = [] default = [] + +[[bench]] +name = "pairing_benches" +harness = false + +[badges] +maintenance = { status = "actively-developed" } diff --git a/pairing/README.md b/pairing/README.md index bf386de..47a25dc 100644 --- a/pairing/README.md +++ b/pairing/README.md @@ -1,6 +1,16 @@ # pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # -This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented. +`pairing` is a crate for using pairing-friendly elliptic curves. + +Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) +construction is implemented. + +## Roadmap + +`pairing` is being refactored into a generic library for working with +pairing-friendly curves. After the refactor, `pairing` will provide basic traits +for pairing-friendly elliptic curve constructions, while specific curves will be +in separate crates. ## [Documentation](https://docs.rs/pairing/) @@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would. ## Security Warnings -This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks. +This library does not make any guarantees about constant-time operations, memory +access patterns, or resistance to side-channel attacks. ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs index d8f6618..28ce4bf 100644 --- a/pairing/benches/bls12_381/ec.rs +++ b/pairing/benches/bls12_381/ec.rs @@ -1,127 +1,173 @@ -mod g1 { - use rand::{Rand, SeedableRng, XorShiftRng}; +pub(crate) mod g1 { + use criterion::{criterion_group, Criterion}; + use rand_core::SeedableRng; + use rand_xorshift::XorShiftRng; + use std::ops::AddAssign; + use ff::Field; use group::CurveProjective; use pairing::bls12_381::*; - #[bench] - fn bench_g1_mul_assign(b: &mut ::test::Bencher) { + fn bench_g1_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G1, Fr)> = (0..SAMPLES) - .map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng))) + .map(|_| (G1::random(&mut rng), Fr::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G1::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } - #[bench] - fn bench_g1_add_assign(b: &mut ::test::Bencher) { + fn bench_g1_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G1, G1)> = (0..SAMPLES) - .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng))) + .map(|_| (G1::random(&mut rng), G1::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G1::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } - #[bench] - fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) { + fn bench_g1_add_assign_mixed(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G1, G1Affine)> = (0..SAMPLES) - .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into())) + .map(|_| (G1::random(&mut rng), G1::random(&mut rng).into())) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign_mixed(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G1::add_assign_mixed", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } + + criterion_group!( + benches, + bench_g1_add_assign, + bench_g1_add_assign_mixed, + bench_g1_mul_assign, + ); } -mod g2 { - use rand::{Rand, SeedableRng, XorShiftRng}; +pub(crate) mod g2 { + use criterion::{criterion_group, Criterion}; + use rand_core::SeedableRng; + use rand_xorshift::XorShiftRng; + use std::ops::AddAssign; + use ff::Field; use group::CurveProjective; use pairing::bls12_381::*; - #[bench] - fn bench_g2_mul_assign(b: &mut ::test::Bencher) { + fn bench_g2_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G2, Fr)> = (0..SAMPLES) - .map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng))) + .map(|_| (G2::random(&mut rng), Fr::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G2::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } - #[bench] - fn bench_g2_add_assign(b: &mut ::test::Bencher) { + fn bench_g2_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G2, G2)> = (0..SAMPLES) - .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng))) + .map(|_| (G2::random(&mut rng), G2::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G2::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } - #[bench] - fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) { + fn bench_g2_add_assign_mixed(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); let v: Vec<(G2, G2Affine)> = (0..SAMPLES) - .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into())) + .map(|_| (G2::random(&mut rng), G2::random(&mut rng).into())) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign_mixed(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G2::add_assign_mixed", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } + + criterion_group!( + benches, + bench_g2_add_assign, + bench_g2_add_assign_mixed, + bench_g2_mul_assign, + ); } diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs index 053a10c..417ec9f 100644 --- a/pairing/benches/bls12_381/fq.rs +++ b/pairing/benches/bls12_381/fq.rs @@ -1,268 +1,209 @@ -use rand::{Rand, SeedableRng, XorShiftRng}; +use criterion::{criterion_group, Criterion}; +use rand_core::SeedableRng; +use rand_xorshift::XorShiftRng; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField}; use pairing::bls12_381::*; -#[bench] -fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) { +fn bench_fq_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = FqRepr::rand(&mut rng); - let mut tmp2 = FqRepr::rand(&mut rng); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = FqRepr::rand(&mut rng); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_repr_mul2(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_repr_div2(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_add_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_sub_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_mul_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_square(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.square(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_inverse(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].inverse() - }); -} - -#[bench] -fn bench_fq_negate(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.negate(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fq_sqrt(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES) - .map(|_| { - let mut tmp = Fq::rand(&mut rng); - tmp.square(); + c.bench_function("Fq::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; tmp }) + }); +} + +fn bench_fq_sub_assign(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() + c.bench_function("Fq::sub_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq_into_repr(b: &mut ::test::Bencher) { +fn bench_fq_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) + .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].into_repr() + c.bench_function("Fq::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq_from_repr(b: &mut ::test::Bencher) { +fn bench_fq_square(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fq::square", |b| { + b.iter(|| { + let tmp = v[count].square(); + count = (count + 1) % SAMPLES; + tmp + }) + }); +} + +fn bench_fq_invert(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fq::invert", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].invert() + }) + }); +} + +fn bench_fq_neg(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fq::neg", |b| { + b.iter(|| { + let tmp = v[count].neg(); + count = (count + 1) % SAMPLES; + tmp + }) + }); +} + +fn bench_fq_sqrt(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fq::random(&mut rng).square()) + .collect(); + + let mut count = 0; + c.bench_function("Fq::sqrt", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }) + }); +} + +fn bench_fq_to_repr(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fq::to_repr", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].to_repr() + }) + }); +} + +fn bench_fq_from_repr(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec = (0..SAMPLES) - .map(|_| Fq::rand(&mut rng).into_repr()) + .map(|_| Fq::random(&mut rng).to_repr()) .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - Fq::from_repr(v[count]) + c.bench_function("Fq::from_repr", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + Fq::from_repr(v[count]) + }) }); } + +criterion_group!( + benches, + bench_fq_add_assign, + bench_fq_sub_assign, + bench_fq_mul_assign, + bench_fq_square, + bench_fq_invert, + bench_fq_neg, + bench_fq_sqrt, + bench_fq_to_repr, + bench_fq_from_repr, +); diff --git a/pairing/benches/bls12_381/fq12.rs b/pairing/benches/bls12_381/fq12.rs index 84daca2..2d6ed49 100644 --- a/pairing/benches/bls12_381/fq12.rs +++ b/pairing/benches/bls12_381/fq12.rs @@ -1,94 +1,125 @@ -use rand::{Rand, SeedableRng, XorShiftRng}; +use criterion::{criterion_group, Criterion}; +use rand_core::SeedableRng; +use rand_xorshift::XorShiftRng; +use std::ops::{AddAssign, MulAssign, SubAssign}; use ff::Field; use pairing::bls12_381::*; -#[bench] -fn bench_fq12_add_assign(b: &mut ::test::Bencher) { +fn bench_fq12_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq12::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq12_sub_assign(b: &mut ::test::Bencher) { +fn bench_fq12_sub_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq12::sub_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq12_mul_assign(b: &mut ::test::Bencher) { +fn bench_fq12_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq12::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq12_squaring(b: &mut ::test::Bencher) { +fn bench_fq12_squaring(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.square(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq12::square", |b| { + b.iter(|| { + let tmp = v[count].square(); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq12_inverse(b: &mut ::test::Bencher) { +fn bench_fq12_invert(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let tmp = v[count].inverse(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq12::invert", |b| { + b.iter(|| { + let tmp = v[count].invert(); + count = (count + 1) % SAMPLES; + tmp + }) }); } + +criterion_group!( + benches, + bench_fq12_add_assign, + bench_fq12_sub_assign, + bench_fq12_mul_assign, + bench_fq12_squaring, + bench_fq12_invert, +); diff --git a/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs index 521b6ab..1402efa 100644 --- a/pairing/benches/bls12_381/fq2.rs +++ b/pairing/benches/bls12_381/fq2.rs @@ -1,110 +1,146 @@ -use rand::{Rand, SeedableRng, XorShiftRng}; +use criterion::{criterion_group, Criterion}; +use rand_core::SeedableRng; +use rand_xorshift::XorShiftRng; +use std::ops::{AddAssign, MulAssign, SubAssign}; -use ff::{Field, SqrtField}; +use ff::Field; use pairing::bls12_381::*; -#[bench] -fn bench_fq2_add_assign(b: &mut ::test::Bencher) { +fn bench_fq2_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq2_sub_assign(b: &mut ::test::Bencher) { +fn bench_fq2_sub_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::sub_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq2_mul_assign(b: &mut ::test::Bencher) { +fn bench_fq2_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq2_squaring(b: &mut ::test::Bencher) { +fn bench_fq2_squaring(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.square(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::square", |b| { + b.iter(|| { + let tmp = v[count].square(); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq2_inverse(b: &mut ::test::Bencher) { +fn bench_fq2_invert(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let tmp = v[count].inverse(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::invert", |b| { + b.iter(|| { + let tmp = v[count].invert(); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fq2_sqrt(b: &mut ::test::Bencher) { +fn bench_fq2_sqrt(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let tmp = v[count].sqrt(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Fq2::sqrt", |b| { + b.iter(|| { + let tmp = v[count].sqrt(); + count = (count + 1) % SAMPLES; + tmp + }) }); } + +criterion_group!( + benches, + bench_fq2_add_assign, + bench_fq2_sub_assign, + bench_fq2_mul_assign, + bench_fq2_squaring, + bench_fq2_invert, + bench_fq2_sqrt, +); diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs index 13b0d0e..468d68e 100644 --- a/pairing/benches/bls12_381/fr.rs +++ b/pairing/benches/bls12_381/fr.rs @@ -1,268 +1,209 @@ -use rand::{Rand, SeedableRng, XorShiftRng}; +use criterion::{criterion_group, Criterion}; +use rand_core::SeedableRng; +use rand_xorshift::XorShiftRng; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField}; use pairing::bls12_381::*; -#[bench] -fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) { +fn bench_fr_add_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = FrRepr::rand(&mut rng); - let mut tmp2 = FrRepr::rand(&mut rng); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = FrRepr::rand(&mut rng); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_repr_mul2(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_repr_div2(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_add_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_sub_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_mul_assign(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) - .collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_square(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.square(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_inverse(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].inverse() - }); -} - -#[bench] -fn bench_fr_negate(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); - - let mut count = 0; - b.iter(|| { - let mut tmp = v[count]; - tmp.negate(); - count = (count + 1) % SAMPLES; - tmp - }); -} - -#[bench] -fn bench_fr_sqrt(b: &mut ::test::Bencher) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - - let v: Vec = (0..SAMPLES) - .map(|_| { - let mut tmp = Fr::rand(&mut rng); - tmp.square(); + c.bench_function("Fr::add_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; tmp }) + }); +} + +fn bench_fr_sub_assign(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() + c.bench_function("Fr::sub_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fr_into_repr(b: &mut ::test::Bencher) { +fn bench_fr_mul_assign(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) + .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].into_repr() + c.bench_function("Fr::mul_assign", |b| { + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_fr_from_repr(b: &mut ::test::Bencher) { +fn bench_fr_square(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fr::square", |b| { + b.iter(|| { + let tmp = v[count].square(); + count = (count + 1) % SAMPLES; + tmp + }) + }); +} + +fn bench_fr_invert(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fr::invert", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].invert() + }) + }); +} + +fn bench_fr_neg(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fr::neg", |b| { + b.iter(|| { + let tmp = v[count].neg(); + count = (count + 1) % SAMPLES; + tmp + }) + }); +} + +fn bench_fr_sqrt(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fr::random(&mut rng).square()) + .collect(); + + let mut count = 0; + c.bench_function("Fr::sqrt", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }) + }); +} + +fn bench_fr_to_repr(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); + + let mut count = 0; + c.bench_function("Fr::to_repr", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].to_repr() + }) + }); +} + +fn bench_fr_from_repr(c: &mut Criterion) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec = (0..SAMPLES) - .map(|_| Fr::rand(&mut rng).into_repr()) + .map(|_| Fr::random(&mut rng).to_repr()) .collect(); let mut count = 0; - b.iter(|| { - count = (count + 1) % SAMPLES; - Fr::from_repr(v[count]) + c.bench_function("Fr::from_repr", |b| { + b.iter(|| { + count = (count + 1) % SAMPLES; + Fr::from_repr(v[count]) + }) }); } + +criterion_group!( + benches, + bench_fr_add_assign, + bench_fr_sub_assign, + bench_fr_mul_assign, + bench_fr_square, + bench_fr_invert, + bench_fr_neg, + bench_fr_sqrt, + bench_fr_to_repr, + bench_fr_from_repr, +); diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs index 96bcdd5..aa2e2f0 100644 --- a/pairing/benches/bls12_381/mod.rs +++ b/pairing/benches/bls12_381/mod.rs @@ -1,107 +1,139 @@ -mod ec; -mod fq; -mod fq12; -mod fq2; -mod fr; +pub(crate) mod ec; +pub(crate) mod fq; +pub(crate) mod fq12; +pub(crate) mod fq2; +pub(crate) mod fr; -use rand::{Rand, SeedableRng, XorShiftRng}; +use criterion::{criterion_group, Criterion}; +use rand_core::SeedableRng; +use rand_xorshift::XorShiftRng; +use group::CurveProjective; use pairing::bls12_381::*; use pairing::{Engine, PairingCurveAffine}; -#[bench] -fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) { +fn bench_pairing_g1_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let tmp = G1Affine::from(v[count]).prepare(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G1 preparation", |b| { + b.iter(|| { + let tmp = G1Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) { +fn bench_pairing_g2_preparation(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); - let v: Vec = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect(); + let v: Vec = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect(); let mut count = 0; - b.iter(|| { - let tmp = G2Affine::from(v[count]).prepare(); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("G2 preparation", |b| { + b.iter(|| { + let tmp = G2Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_pairing_miller_loop(b: &mut ::test::Bencher) { +fn bench_pairing_miller_loop(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::rand(&mut rng)).prepare(), - G2Affine::from(G2::rand(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)).prepare(), + G2Affine::from(G2::random(&mut rng)).prepare(), ) }) .collect(); let mut count = 0; - b.iter(|| { - let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Miller loop", |b| { + b.iter(|| { + let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) { +fn bench_pairing_final_exponentiation(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec = (0..SAMPLES) .map(|_| { ( - G1Affine::from(G1::rand(&mut rng)).prepare(), - G2Affine::from(G2::rand(&mut rng)).prepare(), + G1Affine::from(G1::random(&mut rng)).prepare(), + G2Affine::from(G2::random(&mut rng)).prepare(), ) }) .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) .collect(); let mut count = 0; - b.iter(|| { - let tmp = Bls12::final_exponentiation(&v[count]); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Final exponentiation", |b| { + b.iter(|| { + let tmp = Bls12::final_exponentiation(&v[count]); + count = (count + 1) % SAMPLES; + tmp + }) }); } -#[bench] -fn bench_pairing_full(b: &mut ::test::Bencher) { +fn bench_pairing_full(c: &mut Criterion) { const SAMPLES: usize = 1000; - let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); let v: Vec<(G1, G2)> = (0..SAMPLES) - .map(|_| (G1::rand(&mut rng), G2::rand(&mut rng))) + .map(|_| (G1::random(&mut rng), G2::random(&mut rng))) .collect(); let mut count = 0; - b.iter(|| { - let tmp = Bls12::pairing(v[count].0, v[count].1); - count = (count + 1) % SAMPLES; - tmp + c.bench_function("Full pairing", |b| { + b.iter(|| { + let tmp = Bls12::pairing(v[count].0, v[count].1); + count = (count + 1) % SAMPLES; + tmp + }) }); } + +criterion_group!( + benches, + bench_pairing_g1_preparation, + bench_pairing_g2_preparation, + bench_pairing_miller_loop, + bench_pairing_final_exponentiation, + bench_pairing_full, +); diff --git a/pairing/benches/pairing_benches.rs b/pairing/benches/pairing_benches.rs index d76e50b..7abc824 100644 --- a/pairing/benches/pairing_benches.rs +++ b/pairing/benches/pairing_benches.rs @@ -1,9 +1,12 @@ -#![feature(test)] - -extern crate ff; -extern crate group; -extern crate pairing; -extern crate rand; -extern crate test; - +use criterion::criterion_main; mod bls12_381; + +criterion_main!( + bls12_381::benches, + bls12_381::ec::g1::benches, + bls12_381::ec::g2::benches, + bls12_381::fq::benches, + bls12_381::fq12::benches, + bls12_381::fq2::benches, + bls12_381::fr::benches, +); diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 9a78e37..1a3f141 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -18,7 +18,7 @@ macro_rules! curve_impl { } impl ::std::fmt::Display for $affine { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { if self.infinity { write!(f, "{}(Infinity)", $name) } else { @@ -35,7 +35,7 @@ macro_rules! curve_impl { } impl ::std::fmt::Display for $projective { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "{}", self.into_affine()) } } @@ -54,10 +54,8 @@ macro_rules! curve_impl { // are equal when (X * Z^2) = (X' * Z'^2) // and (Y * Z^3) = (Y' * Z'^3). - let mut z1 = self.z; - z1.square(); - let mut z2 = other.z; - z2.square(); + let mut z1 = self.z.square(); + let mut z2 = other.z.square(); let mut tmp1 = self.x; tmp1.mul_assign(&z2); @@ -83,12 +81,23 @@ macro_rules! curve_impl { } impl $affine { - fn mul_bits>(&self, bits: BitIterator) -> $projective { + fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { let mut res = $projective::zero(); for i in bits { res.double(); if i { - res.add_assign_mixed(self) + res.add_assign(self) + } + } + res + } + + fn mul_bits_u8>(&self, bits: BitIterator) -> $projective { + let mut res = $projective::zero(); + for i in bits { + res.double(); + if i { + res.add_assign(self) } } res @@ -99,16 +108,14 @@ macro_rules! curve_impl { /// /// If and only if `greatest` is set will the lexicographically /// largest y-coordinate be selected. - fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> { + fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> { // Compute x^3 + b - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&$affine::get_coeff_b()); x3b.sqrt().map(|y| { - let mut negy = y; - negy.negate(); + let negy = y.neg(); $affine { x: x, @@ -123,11 +130,9 @@ macro_rules! curve_impl { true } else { // Check that the point is on the curve - let mut y2 = self.y; - y2.square(); + let y2 = self.y.square(); - let mut x3b = self.x; - x3b.square(); + let mut x3b = self.x.square(); x3b.mul_assign(&self.x); x3b.add_assign(&Self::get_coeff_b()); @@ -140,6 +145,19 @@ macro_rules! curve_impl { } } + impl ::std::ops::Neg for $affine { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + let mut ret = self; + if !ret.is_zero() { + ret.y = ret.y.neg(); + } + ret + } + } + impl CurveAffine for $affine { type Engine = Bls12; type Scalar = $scalarfield; @@ -165,14 +183,8 @@ macro_rules! curve_impl { } fn mul::Repr>>(&self, by: S) -> $projective { - let bits = BitIterator::new(by.into()); - self.mul_bits(bits) - } - - fn negate(&mut self) { - if !self.is_zero() { - self.y.negate(); - } + let bits = BitIterator::::new(by.into()); + self.mul_bits_u8(bits) } fn into_projective(&self) -> $projective { @@ -194,19 +206,323 @@ macro_rules! curve_impl { } } + impl ::std::ops::Neg for $projective { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + let mut ret = self; + if !ret.is_zero() { + ret.y = ret.y.neg(); + } + ret + } + } + + impl<'r> ::std::ops::Add<&'r $projective> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: &Self) -> Self { + let mut ret = self; + ret.add_assign(other); + ret + } + } + + impl ::std::ops::Add for $projective { + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + self + &other + } + } + + impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective { + fn add_assign(&mut self, other: &Self) { + if self.is_zero() { + *self = *other; + return; + } + + if other.is_zero() { + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + + // Z1Z1 = Z1^2 + let z1z1 = self.z.square(); + + // Z2Z2 = Z2^2 + let z2z2 = other.z.square(); + + // U1 = X1*Z2Z2 + let mut u1 = self.x; + u1.mul_assign(&z2z2); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S1 = Y1*Z2*Z2Z2 + let mut s1 = self.y; + s1.mul_assign(&other.z); + s1.mul_assign(&z2z2); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if u1 == u2 && s1 == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + // H = U2-U1 + let mut h = u2; + h.sub_assign(&u1); + + // I = (2*H)^2 + let i = h.double().square(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-S1) + let mut r = s2; + r.sub_assign(&s1); + r = r.double(); + + // V = U1*I + let mut v = u1; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V - X3) - 2*S1*J + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + s1.mul_assign(&j); // S1 = S1 * J * 2 + s1 = s1.double(); + self.y.sub_assign(&s1); + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + self.z.add_assign(&other.z); + self.z = self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&z2z2); + self.z.mul_assign(&h); + } + } + } + + impl ::std::ops::AddAssign for $projective { + #[inline] + fn add_assign(&mut self, other: Self) { + self.add_assign(&other); + } + } + + impl<'r> ::std::ops::Sub<&'r $projective> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: &Self) -> Self { + let mut ret = self; + ret.sub_assign(other); + ret + } + } + + impl ::std::ops::Sub for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + self - &other + } + } + + impl<'r> ::std::ops::SubAssign<&'r $projective> for $projective { + fn sub_assign(&mut self, other: &Self) { + self.add_assign(&other.neg()); + } + } + + impl ::std::ops::SubAssign for $projective { + #[inline] + fn sub_assign(&mut self, other: Self) { + self.sub_assign(&other); + } + } + + impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self { + let mut ret = self; + ret.add_assign(other); + ret + } + } + + impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: <$projective as CurveProjective>::Affine) -> Self { + self + &other + } + } + + impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine> + for $projective + { + fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + if other.is_zero() { + return; + } + + if self.is_zero() { + self.x = other.x; + self.y = other.y; + self.z = $basefield::one(); + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + + // Z1Z1 = Z1^2 + let z1z1 = self.z.square(); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if self.x == u2 && self.y == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + // H = U2-X1 + let mut h = u2; + h.sub_assign(&self.x); + + // HH = H^2 + let hh = h.square(); + + // I = 4*HH + let i = hh.double().double(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-Y1) + let mut r = s2; + r.sub_assign(&self.y); + r = r.double(); + + // V = X1*I + let mut v = self.x; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V-X3)-2*Y1*J + j.mul_assign(&self.y); // J = 2*Y1*J + j = j.double(); + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + self.y.sub_assign(&j); + + // Z3 = (Z1+H)^2-Z1Z1-HH + self.z.add_assign(&h); + self.z = self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&hh); + } + } + } + + impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective { + #[inline] + fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + self.add_assign(&other); + } + } + + impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self { + let mut ret = self; + ret.sub_assign(other); + ret + } + } + + impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self { + self - &other + } + } + + impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine> + for $projective + { + fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + self.add_assign(&other.neg()); + } + } + + impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective { + #[inline] + fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + self.sub_assign(&other); + } + } + impl CurveProjective for $projective { type Engine = Bls12; type Scalar = $scalarfield; type Base = $basefield; type Affine = $affine; - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let x = $basefield::random(rng); let greatest = rng.next_u32() % 2 != 0; - if let Some(p) = $affine::get_point_from_x(x, greatest) { - let p = p.scale_by_cofactor(); + let p = $affine::get_point_from_x(x, greatest); + if p.is_some().into() { + let p = p.unwrap().scale_by_cofactor(); if !p.is_zero() { return p; @@ -257,7 +573,7 @@ macro_rules! curve_impl { } // Invert `tmp`. - tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. + tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero. // Second pass: iterate backwards to compute inverses for (g, s) in v @@ -284,8 +600,7 @@ macro_rules! curve_impl { // Perform affine transformations for g in v.iter_mut().filter(|g| !g.is_normalized()) { - let mut z = g.z; // 1/z - z.square(); // 1/z^2 + let mut z = g.z.square(); // 1/z^2 g.x.mul_assign(&z); // x/z^2 z.mul_assign(&g.z); // 1/z^3 g.y.mul_assign(&z); // y/z^3 @@ -306,37 +621,32 @@ macro_rules! curve_impl { // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l // A = X1^2 - let mut a = self.x; - a.square(); + let a = self.x.square(); // B = Y1^2 - let mut b = self.y; - b.square(); + let b = self.y.square(); // C = B^2 - let mut c = b; - c.square(); + let mut c = b.square(); // D = 2*((X1+B)2-A-C) let mut d = self.x; d.add_assign(&b); - d.square(); + d = d.square(); d.sub_assign(&a); d.sub_assign(&c); - d.double(); + d = d.double(); // E = 3*A - let mut e = a; - e.double(); + let mut e = a.double(); e.add_assign(&a); // F = E^2 - let mut f = e; - f.square(); + let f = e.square(); // Z3 = 2*Y1*Z1 self.z.mul_assign(&self.y); - self.z.double(); + self.z = self.z.double(); // X3 = F-2*D self.x = f; @@ -347,196 +657,16 @@ macro_rules! curve_impl { self.y = d; self.y.sub_assign(&self.x); self.y.mul_assign(&e); - c.double(); - c.double(); - c.double(); + c = c.double().double().double(); self.y.sub_assign(&c); } - fn add_assign(&mut self, other: &Self) { - if self.is_zero() { - *self = *other; - return; - } - - if other.is_zero() { - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl - - // Z1Z1 = Z1^2 - let mut z1z1 = self.z; - z1z1.square(); - - // Z2Z2 = Z2^2 - let mut z2z2 = other.z; - z2z2.square(); - - // U1 = X1*Z2Z2 - let mut u1 = self.x; - u1.mul_assign(&z2z2); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S1 = Y1*Z2*Z2Z2 - let mut s1 = self.y; - s1.mul_assign(&other.z); - s1.mul_assign(&z2z2); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if u1 == u2 && s1 == s2 { - // The two points are equal, so we double. - self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-U1 - let mut h = u2; - h.sub_assign(&u1); - - // I = (2*H)^2 - let mut i = h; - i.double(); - i.square(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-S1) - let mut r = s2; - r.sub_assign(&s1); - r.double(); - - // V = U1*I - let mut v = u1; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r; - self.x.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V - X3) - 2*S1*J - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - s1.mul_assign(&j); // S1 = S1 * J * 2 - s1.double(); - self.y.sub_assign(&s1); - - // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H - self.z.add_assign(&other.z); - self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&z2z2); - self.z.mul_assign(&h); - } - } - - fn add_assign_mixed(&mut self, other: &Self::Affine) { - if other.is_zero() { - return; - } - - if self.is_zero() { - self.x = other.x; - self.y = other.y; - self.z = $basefield::one(); - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl - - // Z1Z1 = Z1^2 - let mut z1z1 = self.z; - z1z1.square(); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if self.x == u2 && self.y == s2 { - // The two points are equal, so we double. - self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-X1 - let mut h = u2; - h.sub_assign(&self.x); - - // HH = H^2 - let mut hh = h; - hh.square(); - - // I = 4*HH - let mut i = hh; - i.double(); - i.double(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-Y1) - let mut r = s2; - r.sub_assign(&self.y); - r.double(); - - // V = X1*I - let mut v = self.x; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r; - self.x.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V-X3)-2*Y1*J - j.mul_assign(&self.y); // J = 2*Y1*J - j.double(); - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - self.y.sub_assign(&j); - - // Z3 = (Z1+H)^2-Z1Z1-HH - self.z.add_assign(&h); - self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&hh); - } - } - - fn negate(&mut self) { - if !self.is_zero() { - self.y.negate() - } - } - fn mul_assign::Repr>>(&mut self, other: S) { let mut res = Self::zero(); let mut found_one = false; - for i in BitIterator::new(other.into()) { + for i in BitIterator::::new(other.into()) { if found_one { res.double(); } else { @@ -544,7 +674,7 @@ macro_rules! curve_impl { } if i { - res.add_assign(self); + res.add_assign(&*self); } } @@ -555,8 +685,10 @@ macro_rules! curve_impl { (*self).into() } - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { - Self::empirical_recommended_wnaf_for_scalar(scalar) + fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { + Self::empirical_recommended_wnaf_for_scalar( + ::NUM_BITS as usize, + ) } fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { @@ -595,9 +727,8 @@ macro_rules! curve_impl { } } else { // Z is nonzero, so it must have an inverse in a field. - let zinv = p.z.inverse().unwrap(); - let mut zinv_powered = zinv; - zinv_powered.square(); + let zinv = p.z.invert().unwrap(); + let mut zinv_powered = zinv.square(); // X/Z^2 let mut x = p.x; @@ -620,13 +751,15 @@ macro_rules! curve_impl { } pub mod g1 { - use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; + use super::super::{Bls12, Fq, Fq12, FqRepr, Fr}; use super::g2::G2Affine; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use crate::{Engine, PairingCurveAffine}; + use ff::{BitIterator, Field, PrimeField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; - use {Engine, PairingCurveAffine}; + use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; + use subtle::CtOption; curve_impl!( "G1", @@ -656,7 +789,7 @@ pub mod g1 { } impl fmt::Debug for G1Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.0[..].fmt(formatter) } } @@ -711,22 +844,21 @@ pub mod g1 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x = FqRepr([0; 6]); - let mut y = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - y.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x = FqRepr(copy_segment(©, 0)); + let y = FqRepr(copy_segment(©, 48)); + Ok(G1Affine { - x: Fq::from_repr(x).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate", e) + x: Fq::from_repr(x).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate") })?, - y: Fq::from_repr(y).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate", e) + y: Fq::from_repr(y).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate") })?, infinity: false, }) @@ -740,10 +872,8 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - let mut writer = &mut res.0[..]; - - affine.x.into_repr().write_be(&mut writer).unwrap(); - affine.y.into_repr().write_be(&mut writer).unwrap(); + res.0[..48].copy_from_slice(&affine.x.to_repr().0); + res.0[48..].copy_from_slice(&affine.y.to_repr().0); } res @@ -766,7 +896,7 @@ pub mod g1 { } impl fmt::Debug for G1Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.0[..].fmt(formatter) } } @@ -819,19 +949,16 @@ pub mod g1 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - } - // Interpret as Fq element. - let x = Fq::from_repr(x) - .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; + let x = Fq::from_repr(FqRepr(copy)) + .ok_or_else(|| GroupDecodingError::CoordinateDecodingError("x coordinate"))?; - G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + let ret = G1Affine::get_point_from_x(x, greatest); + if ret.is_some().into() { + Ok(ret.unwrap()) + } else { + Err(GroupDecodingError::NotOnCurve) + } } } fn from_affine(affine: G1Affine) -> Self { @@ -842,14 +969,9 @@ pub mod g1 { // is at infinity. res.0[0] |= 1 << 6; } else { - { - let mut writer = &mut res.0[..]; + res.0 = affine.x.to_repr().0; - affine.x.into_repr().write_be(&mut writer).unwrap(); - } - - let mut negy = affine.y; - negy.negate(); + let negy = affine.y.neg(); // Set the third most significant bit if the correct y-coordinate // is lexicographically largest. @@ -868,8 +990,8 @@ pub mod g1 { impl G1Affine { fn scale_by_cofactor(&self) -> G1 { // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 - let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); - self.mul_bits(cofactor) + let cofactor = BitIterator::::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); + self.mul_bits_u64(cofactor) } fn get_generator() -> Self { @@ -890,9 +1012,7 @@ pub mod g1 { } impl G1 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - + fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { if num_bits >= 130 { 4 } else if num_bits >= 34 { @@ -934,26 +1054,22 @@ pub mod g1 { #[test] fn g1_generator() { - use SqrtField; - let mut x = Fq::zero(); let mut i = 0; loop { // y^2 = x^3 + b - let mut rhs = x; - rhs.square(); + let mut rhs = x.square(); rhs.mul_assign(&x); rhs.add_assign(&G1Affine::get_coeff_b()); - if let Some(y) = rhs.sqrt() { - let yrepr = y.into_repr(); - let mut negy = y; - negy.negate(); - let negyrepr = negy.into_repr(); + let y = rhs.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); + let negy = y.neg(); let p = G1Affine { x, - y: if yrepr < negyrepr { y } else { negy }, + y: if y < negy { y } else { negy }, infinity: false, }; assert!(!p.is_in_correct_subgroup_assuming_on_curve()); @@ -981,21 +1097,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0xc58d887b66c035dc, - 0x10cbfd301d553822, - 0xaf23e064f1131ee5, - 0x9fe83b1b4a5d648d, - 0xf583cc5a508f6a40, - 0xc3ad2aefde0bb13, + 0x0c, 0x3a, 0xd2, 0xae, 0xfd, 0xe0, 0xbb, 0x13, 0xf5, 0x83, 0xcc, 0x5a, 0x50, + 0x8f, 0x6a, 0x40, 0x9f, 0xe8, 0x3b, 0x1b, 0x4a, 0x5d, 0x64, 0x8d, 0xaf, 0x23, + 0xe0, 0x64, 0xf1, 0x13, 0x1e, 0xe5, 0x10, 0xcb, 0xfd, 0x30, 0x1d, 0x55, 0x38, + 0x22, 0xc5, 0x8d, 0x88, 0x7b, 0x66, 0xc0, 0x35, 0xdc, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x60aa6f9552f03aae, - 0xecd01d5181300d35, - 0x8af1cdb8aa8ce167, - 0xe760f57922998c9d, - 0x953703f5795a39e5, - 0xfe3ae0922df702c, + 0x0f, 0xe3, 0xae, 0x09, 0x22, 0xdf, 0x70, 0x2c, 0x95, 0x37, 0x03, 0xf5, 0x79, + 0x5a, 0x39, 0xe5, 0xe7, 0x60, 0xf5, 0x79, 0x22, 0x99, 0x8c, 0x9d, 0x8a, 0xf1, + 0xcd, 0xb8, 0xaa, 0x8c, 0xe1, 0x67, 0xec, 0xd0, 0x1d, 0x51, 0x81, 0x30, 0x0d, + 0x35, 0x60, 0xaa, 0x6f, 0x95, 0x52, 0xf0, 0x3a, 0xae, ])) .unwrap(), infinity: false, @@ -1008,21 +1120,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0xee6adf83511e15f5, - 0x92ddd328f27a4ba6, - 0xe305bd1ac65adba7, - 0xea034ee2928b30a8, - 0xbd8833dc7c79a7f7, - 0xe45c9f0c0438675, + 0x0e, 0x45, 0xc9, 0xf0, 0xc0, 0x43, 0x86, 0x75, 0xbd, 0x88, 0x33, 0xdc, 0x7c, + 0x79, 0xa7, 0xf7, 0xea, 0x03, 0x4e, 0xe2, 0x92, 0x8b, 0x30, 0xa8, 0xe3, 0x05, + 0xbd, 0x1a, 0xc6, 0x5a, 0xdb, 0xa7, 0x92, 0xdd, 0xd3, 0x28, 0xf2, 0x7a, 0x4b, + 0xa6, 0xee, 0x6a, 0xdf, 0x83, 0x51, 0x1e, 0x15, 0xf5, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x3b450eb1ab7b5dad, - 0xa65cb81e975e8675, - 0xaa548682b21726e5, - 0x753ddf21a2601d20, - 0x532d0b640bd3ff8b, - 0x118d2c543f031102, + 0x11, 0x8d, 0x2c, 0x54, 0x3f, 0x03, 0x11, 0x02, 0x53, 0x2d, 0x0b, 0x64, 0x0b, + 0xd3, 0xff, 0x8b, 0x75, 0x3d, 0xdf, 0x21, 0xa2, 0x60, 0x1d, 0x20, 0xaa, 0x54, + 0x86, 0x82, 0xb2, 0x17, 0x26, 0xe5, 0xa6, 0x5c, 0xb8, 0x1e, 0x97, 0x5e, 0x86, + 0x75, 0x3b, 0x45, 0x0e, 0xb1, 0xab, 0x7b, 0x5d, 0xad, ])) .unwrap(), infinity: false, @@ -1036,21 +1144,17 @@ pub mod g1 { { let p = G1Affine { x: Fq::from_repr(FqRepr([ - 0x76e1c971c6db8fe8, - 0xe37e1a610eff2f79, - 0x88ae9c499f46f0c0, - 0xf35de9ce0d6b4e84, - 0x265bddd23d1dec54, - 0x12a8778088458308, + 0x12, 0xa8, 0x77, 0x80, 0x88, 0x45, 0x83, 0x08, 0x26, 0x5b, 0xdd, 0xd2, 0x3d, + 0x1d, 0xec, 0x54, 0xf3, 0x5d, 0xe9, 0xce, 0x0d, 0x6b, 0x4e, 0x84, 0x88, 0xae, + 0x9c, 0x49, 0x9f, 0x46, 0xf0, 0xc0, 0xe3, 0x7e, 0x1a, 0x61, 0x0e, 0xff, 0x2f, + 0x79, 0x76, 0xe1, 0xc9, 0x71, 0xc6, 0xdb, 0x8f, 0xe8, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x8a22defa0d526256, - 0xc57ca55456fcb9ae, - 0x1ba194e89bab2610, - 0x921beef89d4f29df, - 0x5b6fda44ad85fa78, - 0xed74ab9f302cbe0, + 0x0e, 0xd7, 0x4a, 0xb9, 0xf3, 0x02, 0xcb, 0xe0, 0x5b, 0x6f, 0xda, 0x44, 0xad, + 0x85, 0xfa, 0x78, 0x92, 0x1b, 0xee, 0xf8, 0x9d, 0x4f, 0x29, 0xdf, 0x1b, 0xa1, + 0x94, 0xe8, 0x9b, 0xab, 0x26, 0x10, 0xc5, 0x7c, 0xa5, 0x54, 0x56, 0xfc, 0xb9, + 0xae, 0x8a, 0x22, 0xde, 0xfa, 0x0d, 0x52, 0x62, 0x56, ])) .unwrap(), infinity: false, @@ -1064,21 +1168,17 @@ pub mod g1 { fn test_g1_addition_correctness() { let mut p = G1 { x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, + 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, + 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, + 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, + 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, + 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, + 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, + 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, + 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, ])) .unwrap(), z: Fq::one(), @@ -1086,21 +1186,17 @@ pub mod g1 { p.add_assign(&G1 { x: Fq::from_repr(FqRepr([ - 0xeec78f3096213cbf, - 0xa12beb1fea1056e6, - 0xc286c0211c40dd54, - 0x5f44314ec5e3fb03, - 0x24e8538737c6e675, - 0x8abd623a594fba8, + 0x08, 0xab, 0xd6, 0x23, 0xa5, 0x94, 0xfb, 0xa8, 0x24, 0xe8, 0x53, 0x87, 0x37, 0xc6, + 0xe6, 0x75, 0x5f, 0x44, 0x31, 0x4e, 0xc5, 0xe3, 0xfb, 0x03, 0xc2, 0x86, 0xc0, 0x21, + 0x1c, 0x40, 0xdd, 0x54, 0xa1, 0x2b, 0xeb, 0x1f, 0xea, 0x10, 0x56, 0xe6, 0xee, 0xc7, + 0x8f, 0x30, 0x96, 0x21, 0x3c, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x6b0528f088bb7044, - 0x2fdeb5c82917ff9e, - 0x9a5181f2fac226ad, - 0xd65104c6f95a872a, - 0x1f2998a5a9c61253, - 0xe74846154a9e44, + 0x00, 0xe7, 0x48, 0x46, 0x15, 0x4a, 0x9e, 0x44, 0x1f, 0x29, 0x98, 0xa5, 0xa9, 0xc6, + 0x12, 0x53, 0xd6, 0x51, 0x04, 0xc6, 0xf9, 0x5a, 0x87, 0x2a, 0x9a, 0x51, 0x81, 0xf2, + 0xfa, 0xc2, 0x26, 0xad, 0x2f, 0xde, 0xb5, 0xc8, 0x29, 0x17, 0xff, 0x9e, 0x6b, 0x05, + 0x28, 0xf0, 0x88, 0xbb, 0x70, 0x44, ])) .unwrap(), z: Fq::one(), @@ -1112,21 +1208,17 @@ pub mod g1 { p, G1Affine { x: Fq::from_repr(FqRepr([ - 0x6dd3098f22235df, - 0xe865d221c8090260, - 0xeb96bb99fa50779f, - 0xc4f9a52a428e23bb, - 0xd178b28dd4f407ef, - 0x17fb8905e9183c69 + 0x17, 0xfb, 0x89, 0x05, 0xe9, 0x18, 0x3c, 0x69, 0xd1, 0x78, 0xb2, 0x8d, 0xd4, + 0xf4, 0x07, 0xef, 0xc4, 0xf9, 0xa5, 0x2a, 0x42, 0x8e, 0x23, 0xbb, 0xeb, 0x96, + 0xbb, 0x99, 0xfa, 0x50, 0x77, 0x9f, 0xe8, 0x65, 0xd2, 0x21, 0xc8, 0x09, 0x02, + 0x60, 0x06, 0xdd, 0x30, 0x98, 0xf2, 0x22, 0x35, 0xdf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd0de9d65292b7710, - 0xf6a05f2bcf1d9ca7, - 0x1040e27012f20b64, - 0xeec8d1a5b7466c58, - 0x4bc362649dce6376, - 0x430cbdc5455b00a + 0x04, 0x30, 0xcb, 0xdc, 0x54, 0x55, 0xb0, 0x0a, 0x4b, 0xc3, 0x62, 0x64, 0x9d, + 0xce, 0x63, 0x76, 0xee, 0xc8, 0xd1, 0xa5, 0xb7, 0x46, 0x6c, 0x58, 0x10, 0x40, + 0xe2, 0x70, 0x12, 0xf2, 0x0b, 0x64, 0xf6, 0xa0, 0x5f, 0x2b, 0xcf, 0x1d, 0x9c, + 0xa7, 0xd0, 0xde, 0x9d, 0x65, 0x29, 0x2b, 0x77, 0x10, ])) .unwrap(), infinity: false, @@ -1138,21 +1230,17 @@ pub mod g1 { fn test_g1_doubling_correctness() { let mut p = G1 { x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, + 0x08, 0x6e, 0xd4, 0xd9, 0x90, 0x6f, 0xb0, 0x64, 0x4c, 0x6f, 0xca, 0xc4, 0xb5, 0x5f, + 0xd4, 0x79, 0x48, 0x5e, 0x77, 0xd5, 0x0a, 0x5d, 0xf1, 0x0d, 0x08, 0x1f, 0x33, 0x39, + 0xe5, 0xf9, 0x96, 0x8f, 0x79, 0xa3, 0xb0, 0x44, 0x8f, 0x31, 0xa2, 0xaa, 0x47, 0xfd, + 0x1f, 0x89, 0x1d, 0x6e, 0x8b, 0xbf, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, + 0x15, 0x2c, 0xa6, 0x96, 0xfe, 0x03, 0x44, 0x42, 0x57, 0x0c, 0x80, 0x05, 0xf8, 0x57, + 0x3f, 0xa6, 0xce, 0xfc, 0xa6, 0x83, 0x33, 0xc3, 0x52, 0x88, 0xa0, 0x6f, 0xd3, 0xf1, + 0xe5, 0x40, 0x91, 0x0d, 0x9f, 0x3b, 0xbb, 0x2e, 0xcd, 0x37, 0x19, 0xb9, 0x0d, 0x25, + 0xee, 0x64, 0x61, 0x53, 0x8c, 0x65, ])) .unwrap(), z: Fq::one(), @@ -1166,21 +1254,17 @@ pub mod g1 { p, G1Affine { x: Fq::from_repr(FqRepr([ - 0xf939ddfe0ead7018, - 0x3b03942e732aecb, - 0xce0e9c38fdb11851, - 0x4b914c16687dcde0, - 0x66c8baf177d20533, - 0xaf960cff3d83833 + 0x0a, 0xf9, 0x60, 0xcf, 0xf3, 0xd8, 0x38, 0x33, 0x66, 0xc8, 0xba, 0xf1, 0x77, + 0xd2, 0x05, 0x33, 0x4b, 0x91, 0x4c, 0x16, 0x68, 0x7d, 0xcd, 0xe0, 0xce, 0x0e, + 0x9c, 0x38, 0xfd, 0xb1, 0x18, 0x51, 0x03, 0xb0, 0x39, 0x42, 0xe7, 0x32, 0xae, + 0xcb, 0xf9, 0x39, 0xdd, 0xfe, 0x0e, 0xad, 0x70, 0x18, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x3f0675695f5177a8, - 0x2b6d82ae178a1ba0, - 0x9096380dd8e51b11, - 0x1771a65b60572f4e, - 0x8b547c1313b27555, - 0x135075589a687b1e + 0x13, 0x50, 0x75, 0x58, 0x9a, 0x68, 0x7b, 0x1e, 0x8b, 0x54, 0x7c, 0x13, 0x13, + 0xb2, 0x75, 0x55, 0x17, 0x71, 0xa6, 0x5b, 0x60, 0x57, 0x2f, 0x4e, 0x90, 0x96, + 0x38, 0x0d, 0xd8, 0xe5, 0x1b, 0x11, 0x2b, 0x6d, 0x82, 0xae, 0x17, 0x8a, 0x1b, + 0xa0, 0x3f, 0x06, 0x75, 0x69, 0x5f, 0x51, 0x77, 0xa8, ])) .unwrap(), infinity: false, @@ -1199,21 +1283,17 @@ pub mod g1 { let a = G1Affine { x: Fq::from_repr(FqRepr([ - 0xea431f2cc38fc94d, - 0x3ad2354a07f5472b, - 0xfe669f133f16c26a, - 0x71ffa8021531705, - 0x7418d484386d267, - 0xd5108d8ff1fbd6, + 0x00, 0xd5, 0x10, 0x8d, 0x8f, 0xf1, 0xfb, 0xd6, 0x07, 0x41, 0x8d, 0x48, 0x43, 0x86, + 0xd2, 0x67, 0x07, 0x1f, 0xfa, 0x80, 0x21, 0x53, 0x17, 0x05, 0xfe, 0x66, 0x9f, 0x13, + 0x3f, 0x16, 0xc2, 0x6a, 0x3a, 0xd2, 0x35, 0x4a, 0x07, 0xf5, 0x47, 0x2b, 0xea, 0x43, + 0x1f, 0x2c, 0xc3, 0x8f, 0xc9, 0x4d, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, + 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, + 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, + 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, + 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, ])) .unwrap(), infinity: false, @@ -1221,21 +1301,17 @@ pub mod g1 { let b = G1Affine { x: Fq::from_repr(FqRepr([ - 0xe06cdb156b6356b6, - 0xd9040b2d75448ad9, - 0xe702f14bb0e2aca5, - 0xc6e05201e5f83991, - 0xf7c75910816f207c, - 0x18d4043e78103106, + 0x18, 0xd4, 0x04, 0x3e, 0x78, 0x10, 0x31, 0x06, 0xf7, 0xc7, 0x59, 0x10, 0x81, 0x6f, + 0x20, 0x7c, 0xc6, 0xe0, 0x52, 0x01, 0xe5, 0xf8, 0x39, 0x91, 0xe7, 0x02, 0xf1, 0x4b, + 0xb0, 0xe2, 0xac, 0xa5, 0xd9, 0x04, 0x0b, 0x2d, 0x75, 0x44, 0x8a, 0xd9, 0xe0, 0x6c, + 0xdb, 0x15, 0x6b, 0x63, 0x56, 0xb6, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, + 0x0e, 0xe2, 0xc3, 0xd9, 0x22, 0x00, 0x8c, 0xc6, 0x04, 0x84, 0xc8, 0xfc, 0x98, 0x20, + 0x08, 0xf0, 0x52, 0x0f, 0x74, 0x77, 0x3e, 0x74, 0xc8, 0xc3, 0xc0, 0x97, 0x44, 0xe6, + 0x50, 0xb0, 0x04, 0x99, 0x25, 0x56, 0x32, 0x96, 0x4f, 0xf4, 0x0f, 0x4a, 0xa7, 0x76, + 0xcc, 0xbf, 0xe9, 0x98, 0x17, 0x66, ])) .unwrap(), infinity: false, @@ -1246,21 +1322,17 @@ pub mod g1 { // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 let c = G1Affine { x: Fq::from_repr(FqRepr([ - 0xef4f05bdd10c8aa8, - 0xad5bf87341a2df9, - 0x81c7424206b78714, - 0x9676ff02ec39c227, - 0x4c12c15d7e55b9f3, - 0x57fd1e317db9bd, + 0x00, 0x57, 0xfd, 0x1e, 0x31, 0x7d, 0xb9, 0xbd, 0x4c, 0x12, 0xc1, 0x5d, 0x7e, 0x55, + 0xb9, 0xf3, 0x96, 0x76, 0xff, 0x02, 0xec, 0x39, 0xc2, 0x27, 0x81, 0xc7, 0x42, 0x42, + 0x06, 0xb7, 0x87, 0x14, 0x0a, 0xd5, 0xbf, 0x87, 0x34, 0x1a, 0x2d, 0xf9, 0xef, 0x4f, + 0x05, 0xbd, 0xd1, 0x0c, 0x8a, 0xa8, ])) .unwrap(), y: Fq::from_repr(FqRepr([ - 0x1288334016679345, - 0xf955cd68615ff0b5, - 0xa6998dbaa600f18a, - 0x1267d70db51049fb, - 0x4696deb9ab2ba3e7, - 0xb1e4e11177f59d4, + 0x0b, 0x1e, 0x4e, 0x11, 0x17, 0x7f, 0x59, 0xd4, 0x46, 0x96, 0xde, 0xb9, 0xab, 0x2b, + 0xa3, 0xe7, 0x12, 0x67, 0xd7, 0x0d, 0xb5, 0x10, 0x49, 0xfb, 0xa6, 0x99, 0x8d, 0xba, + 0xa6, 0x00, 0xf1, 0x8a, 0xf9, 0x55, 0xcd, 0x68, 0x61, 0x5f, 0xf0, 0xb5, 0x12, 0x88, + 0x33, 0x40, 0x16, 0x67, 0x93, 0x45, ])) .unwrap(), infinity: false, @@ -1276,7 +1348,7 @@ pub mod g1 { assert_eq!(tmp1, c.into_projective()); let mut tmp2 = a.into_projective(); - tmp2.add_assign_mixed(&b); + tmp2.add_assign(&b); assert_eq!(tmp2.into_affine(), c); assert_eq!(tmp2, c.into_projective()); } @@ -1289,13 +1361,15 @@ pub mod g1 { } pub mod g2 { - use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; + use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr}; use super::g1::G1Affine; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; + use crate::{Engine, PairingCurveAffine}; + use ff::{BitIterator, Field, PrimeField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use rand_core::RngCore; use std::fmt; - use {Engine, PairingCurveAffine}; + use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; + use subtle::CtOption; curve_impl!( "G2", @@ -1325,7 +1399,7 @@ pub mod g2 { } impl fmt::Debug for G2Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.0[..].fmt(formatter) } } @@ -1380,35 +1454,32 @@ pub mod g2 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x_c0 = FqRepr([0; 6]); - let mut x_c1 = FqRepr([0; 6]); - let mut y_c0 = FqRepr([0; 6]); - let mut y_c1 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); - y_c1.read_be(&mut reader).unwrap(); - y_c0.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x_c1 = FqRepr(copy_segment(©, 0)); + let x_c0 = FqRepr(copy_segment(©, 48)); + let y_c1 = FqRepr(copy_segment(©, 96)); + let y_c0 = FqRepr(copy_segment(©, 144)); + Ok(G2Affine { x: Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + c0: Fq::from_repr(x_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + c1: Fq::from_repr(x_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") })?, }, y: Fq2 { - c0: Fq::from_repr(y_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e) + c0: Fq::from_repr(y_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c0)") })?, - c1: Fq::from_repr(y_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e) + c1: Fq::from_repr(y_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c1)") })?, }, infinity: false, @@ -1423,12 +1494,10 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - let mut writer = &mut res.0[..]; - - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - affine.y.c1.into_repr().write_be(&mut writer).unwrap(); - affine.y.c0.into_repr().write_be(&mut writer).unwrap(); + res.0[0..48].copy_from_slice(&affine.x.c1.to_repr().0); + res.0[48..96].copy_from_slice(&affine.x.c0.to_repr().0); + res.0[96..144].copy_from_slice(&affine.y.c1.to_repr().0); + res.0[144..192].copy_from_slice(&affine.y.c0.to_repr().0); } res @@ -1451,7 +1520,7 @@ pub mod g2 { } impl fmt::Debug for G2Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.0[..].fmt(formatter) } } @@ -1504,27 +1573,31 @@ pub mod g2 { // Unset the three most significant bits. copy[0] &= 0x1f; - let mut x_c1 = FqRepr([0; 6]); - let mut x_c0 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); + fn copy_segment(s: &[u8], start: usize) -> [u8; 48] { + let mut ret = [0; 48]; + ret.copy_from_slice(&s[start..start + 48]); + ret } + let x_c1 = FqRepr(copy_segment(©, 0)); + let x_c0 = FqRepr(copy_segment(©, 48)); + // Interpret as Fq element. let x = Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + c0: Fq::from_repr(x_c0).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)") })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + c1: Fq::from_repr(x_c1).ok_or_else(|| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)") })?, }; - G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + let ret = G2Affine::get_point_from_x(x, greatest); + if ret.is_some().into() { + Ok(ret.unwrap()) + } else { + Err(GroupDecodingError::NotOnCurve) + } } } fn from_affine(affine: G2Affine) -> Self { @@ -1535,15 +1608,10 @@ pub mod g2 { // is at infinity. res.0[0] |= 1 << 6; } else { - { - let mut writer = &mut res.0[..]; + res.0[..48].copy_from_slice(&affine.x.c1.to_repr().0); + res.0[48..].copy_from_slice(&affine.x.c0.to_repr().0); - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - } - - let mut negy = affine.y; - negy.negate(); + let negy = affine.y.neg(); // Set the third most significant bit if the correct y-coordinate // is lexicographically largest. @@ -1584,7 +1652,7 @@ pub mod g2 { fn scale_by_cofactor(&self) -> G2 { // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 - let cofactor = BitIterator::new([ + let cofactor = BitIterator::::new([ 0xcf1c38e31c7238e5, 0x1616ec6e786f0c70, 0x21537e293a6691ae, @@ -1594,7 +1662,7 @@ pub mod g2 { 0x91d50792876a202, 0x5d543a95414e7f1, ]); - self.mul_bits(cofactor) + self.mul_bits_u64(cofactor) } fn perform_pairing(&self, other: &G1Affine) -> Fq12 { @@ -1603,9 +1671,7 @@ pub mod g2 { } impl G2 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - + fn empirical_recommended_wnaf_for_scalar(num_bits: usize) -> usize { if num_bits >= 103 { 4 } else if num_bits >= 37 { @@ -1640,20 +1706,18 @@ pub mod g2 { #[test] fn g2_generator() { - use SqrtField; - let mut x = Fq2::zero(); let mut i = 0; loop { // y^2 = x^3 + b - let mut rhs = x; - rhs.square(); + let mut rhs = x.square(); rhs.mul_assign(&x); rhs.add_assign(&G2Affine::get_coeff_b()); - if let Some(y) = rhs.sqrt() { - let mut negy = y; - negy.negate(); + let y = rhs.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); + let negy = y.neg(); let p = G2Affine { x, @@ -1686,41 +1750,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xa757072d9fa35ba9, - 0xae3fb2fb418f6e8a, - 0xc1598ec46faa0c7c, - 0x7a17a004747e3dbe, - 0xcc65406a7c2e5a73, - 0x10b8c03d64db4d0c, + 0x10, 0xb8, 0xc0, 0x3d, 0x64, 0xdb, 0x4d, 0x0c, 0xcc, 0x65, 0x40, 0x6a, + 0x7c, 0x2e, 0x5a, 0x73, 0x7a, 0x17, 0xa0, 0x04, 0x74, 0x7e, 0x3d, 0xbe, + 0xc1, 0x59, 0x8e, 0xc4, 0x6f, 0xaa, 0x0c, 0x7c, 0xae, 0x3f, 0xb2, 0xfb, + 0x41, 0x8f, 0x6e, 0x8a, 0xa7, 0x57, 0x07, 0x2d, 0x9f, 0xa3, 0x5b, 0xa9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xd30e70fe2f029778, - 0xda30772df0f5212e, - 0x5b47a9ff9a233a50, - 0xfb777e5b9b568608, - 0x789bac1fec71a2b9, - 0x1342f02e2da54405, + 0x13, 0x42, 0xf0, 0x2e, 0x2d, 0xa5, 0x44, 0x05, 0x78, 0x9b, 0xac, 0x1f, + 0xec, 0x71, 0xa2, 0xb9, 0xfb, 0x77, 0x7e, 0x5b, 0x9b, 0x56, 0x86, 0x08, + 0x5b, 0x47, 0xa9, 0xff, 0x9a, 0x23, 0x3a, 0x50, 0xda, 0x30, 0x77, 0x2d, + 0xf0, 0xf5, 0x21, 0x2e, 0xd3, 0x0e, 0x70, 0xfe, 0x2f, 0x02, 0x97, 0x78, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xfe0812043de54dca, - 0xe455171a3d47a646, - 0xa493f36bc20be98a, - 0x663015d9410eb608, - 0x78e82a79d829a544, - 0x40a00545bb3c1e, + 0x00, 0x40, 0xa0, 0x05, 0x45, 0xbb, 0x3c, 0x1e, 0x78, 0xe8, 0x2a, 0x79, + 0xd8, 0x29, 0xa5, 0x44, 0x66, 0x30, 0x15, 0xd9, 0x41, 0x0e, 0xb6, 0x08, + 0xa4, 0x93, 0xf3, 0x6b, 0xc2, 0x0b, 0xe9, 0x8a, 0xe4, 0x55, 0x17, 0x1a, + 0x3d, 0x47, 0xa6, 0x46, 0xfe, 0x08, 0x12, 0x04, 0x3d, 0xe5, 0x4d, 0xca, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4709802348e79377, - 0xb5ac4dc9204bcfbd, - 0xda361c97d02f42b2, - 0x15008b1dc399e8df, - 0x68128fd0548a3829, - 0x16a613db5c873aaa, + 0x16, 0xa6, 0x13, 0xdb, 0x5c, 0x87, 0x3a, 0xaa, 0x68, 0x12, 0x8f, 0xd0, + 0x54, 0x8a, 0x38, 0x29, 0x15, 0x00, 0x8b, 0x1d, 0xc3, 0x99, 0xe8, 0xdf, + 0xda, 0x36, 0x1c, 0x97, 0xd0, 0x2f, 0x42, 0xb2, 0xb5, 0xac, 0x4d, 0xc9, + 0x20, 0x4b, 0xcf, 0xbd, 0x47, 0x09, 0x80, 0x23, 0x48, 0xe7, 0x93, 0x77, ])) .unwrap(), }, @@ -1735,41 +1791,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf4fdfe95a705f917, - 0xc2914df688233238, - 0x37c6b12cca35a34b, - 0x41abba710d6c692c, - 0xffcc4b2b62ce8484, - 0x6993ec01b8934ed, + 0x06, 0x99, 0x3e, 0xc0, 0x1b, 0x89, 0x34, 0xed, 0xff, 0xcc, 0x4b, 0x2b, + 0x62, 0xce, 0x84, 0x84, 0x41, 0xab, 0xba, 0x71, 0x0d, 0x6c, 0x69, 0x2c, + 0x37, 0xc6, 0xb1, 0x2c, 0xca, 0x35, 0xa3, 0x4b, 0xc2, 0x91, 0x4d, 0xf6, + 0x88, 0x23, 0x32, 0x38, 0xf4, 0xfd, 0xfe, 0x95, 0xa7, 0x05, 0xf9, 0x17, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xb94e92d5f874e26, - 0x44516408bc115d95, - 0xe93946b290caa591, - 0xa5a0c2b7131f3555, - 0x83800965822367e7, - 0x10cf1d3ad8d90bfa, + 0x10, 0xcf, 0x1d, 0x3a, 0xd8, 0xd9, 0x0b, 0xfa, 0x83, 0x80, 0x09, 0x65, + 0x82, 0x23, 0x67, 0xe7, 0xa5, 0xa0, 0xc2, 0xb7, 0x13, 0x1f, 0x35, 0x55, + 0xe9, 0x39, 0x46, 0xb2, 0x90, 0xca, 0xa5, 0x91, 0x44, 0x51, 0x64, 0x08, + 0xbc, 0x11, 0x5d, 0x95, 0x0b, 0x94, 0xe9, 0x2d, 0x5f, 0x87, 0x4e, 0x26, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xbf00334c79701d97, - 0x4fe714f9ff204f9a, - 0xab70b28002f3d825, - 0x5a9171720e73eb51, - 0x38eb4fd8d658adb7, - 0xb649051bbc1164d, + 0x0b, 0x64, 0x90, 0x51, 0xbb, 0xc1, 0x16, 0x4d, 0x38, 0xeb, 0x4f, 0xd8, + 0xd6, 0x58, 0xad, 0xb7, 0x5a, 0x91, 0x71, 0x72, 0x0e, 0x73, 0xeb, 0x51, + 0xab, 0x70, 0xb2, 0x80, 0x02, 0xf3, 0xd8, 0x25, 0x4f, 0xe7, 0x14, 0xf9, + 0xff, 0x20, 0x4f, 0x9a, 0xbf, 0x00, 0x33, 0x4c, 0x79, 0x70, 0x1d, 0x97, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x9225814253d7df75, - 0xc196c2513477f887, - 0xe05e2fbd15a804e0, - 0x55f2b8efad953e04, - 0x7379345eda55265e, - 0x377f2e6208fd4cb, + 0x03, 0x77, 0xf2, 0xe6, 0x20, 0x8f, 0xd4, 0xcb, 0x73, 0x79, 0x34, 0x5e, + 0xda, 0x55, 0x26, 0x5e, 0x55, 0xf2, 0xb8, 0xef, 0xad, 0x95, 0x3e, 0x04, + 0xe0, 0x5e, 0x2f, 0xbd, 0x15, 0xa8, 0x04, 0xe0, 0xc1, 0x96, 0xc2, 0x51, + 0x34, 0x77, 0xf8, 0x87, 0x92, 0x25, 0x81, 0x42, 0x53, 0xd7, 0xdf, 0x75, ])) .unwrap(), }, @@ -1785,41 +1833,33 @@ pub mod g2 { let p = G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x262cea73ea1906c, - 0x2f08540770fabd6, - 0x4ceb92d0a76057be, - 0x2199bc19c48c393d, - 0x4a151b732a6075bf, - 0x17762a3b9108c4a7, + 0x17, 0x76, 0x2a, 0x3b, 0x91, 0x08, 0xc4, 0xa7, 0x4a, 0x15, 0x1b, 0x73, + 0x2a, 0x60, 0x75, 0xbf, 0x21, 0x99, 0xbc, 0x19, 0xc4, 0x8c, 0x39, 0x3d, + 0x4c, 0xeb, 0x92, 0xd0, 0xa7, 0x60, 0x57, 0xbe, 0x02, 0xf0, 0x85, 0x40, + 0x77, 0x0f, 0xab, 0xd6, 0x02, 0x62, 0xce, 0xa7, 0x3e, 0xa1, 0x90, 0x6c, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x26f461e944bbd3d1, - 0x298f3189a9cf6ed6, - 0x74328ad8bc2aa150, - 0x7e147f3f9e6e241, - 0x72a9b63583963fff, - 0x158b0083c000462, + 0x01, 0x58, 0xb0, 0x08, 0x3c, 0x00, 0x04, 0x62, 0x72, 0xa9, 0xb6, 0x35, + 0x83, 0x96, 0x3f, 0xff, 0x07, 0xe1, 0x47, 0xf3, 0xf9, 0xe6, 0xe2, 0x41, + 0x74, 0x32, 0x8a, 0xd8, 0xbc, 0x2a, 0xa1, 0x50, 0x29, 0x8f, 0x31, 0x89, + 0xa9, 0xcf, 0x6e, 0xd6, 0x26, 0xf4, 0x61, 0xe9, 0x44, 0xbb, 0xd3, 0xd1, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x91fb0b225ecf103b, - 0x55d42edc1dc46ba0, - 0x43939b11997b1943, - 0x68cad19430706b4d, - 0x3ccfb97b924dcea8, - 0x1660f93434588f8d, + 0x16, 0x60, 0xf9, 0x34, 0x34, 0x58, 0x8f, 0x8d, 0x3c, 0xcf, 0xb9, 0x7b, + 0x92, 0x4d, 0xce, 0xa8, 0x68, 0xca, 0xd1, 0x94, 0x30, 0x70, 0x6b, 0x4d, + 0x43, 0x93, 0x9b, 0x11, 0x99, 0x7b, 0x19, 0x43, 0x55, 0xd4, 0x2e, 0xdc, + 0x1d, 0xc4, 0x6b, 0xa0, 0x91, 0xfb, 0x0b, 0x22, 0x5e, 0xcf, 0x10, 0x3b, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xaaed3985b6dcb9c7, - 0xc1e985d6d898d9f4, - 0x618bd2ac3271ac42, - 0x3940a2dbb914b529, - 0xbeb88137cf34f3e7, - 0x1699ee577c61b694, + 0x16, 0x99, 0xee, 0x57, 0x7c, 0x61, 0xb6, 0x94, 0xbe, 0xb8, 0x81, 0x37, + 0xcf, 0x34, 0xf3, 0xe7, 0x39, 0x40, 0xa2, 0xdb, 0xb9, 0x14, 0xb5, 0x29, + 0x61, 0x8b, 0xd2, 0xac, 0x32, 0x71, 0xac, 0x42, 0xc1, 0xe9, 0x85, 0xd6, + 0xd8, 0x98, 0xd9, 0xf4, 0xaa, 0xed, 0x39, 0x85, 0xb6, 0xdc, 0xb9, 0xc7, ])) .unwrap(), }, @@ -1835,41 +1875,33 @@ pub mod g2 { let mut p = G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, + 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, + 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, + 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, + 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, + 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, + 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, + 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, + 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, + 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, + 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, + 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, + 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, + 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, + 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, + 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, + 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, ])) .unwrap(), }, @@ -1879,41 +1911,33 @@ pub mod g2 { p.add_assign(&G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xa8c763d25910bdd3, - 0x408777b30ca3add4, - 0x6115fcc12e2769e, - 0x8e73a96b329ad190, - 0x27c546f75ee1f3ab, - 0xa33d27add5e7e82, + 0x0a, 0x33, 0xd2, 0x7a, 0xdd, 0x5e, 0x7e, 0x82, 0x27, 0xc5, 0x46, 0xf7, 0x5e, + 0xe1, 0xf3, 0xab, 0x8e, 0x73, 0xa9, 0x6b, 0x32, 0x9a, 0xd1, 0x90, 0x06, 0x11, + 0x5f, 0xcc, 0x12, 0xe2, 0x76, 0x9e, 0x40, 0x87, 0x77, 0xb3, 0x0c, 0xa3, 0xad, + 0xd4, 0xa8, 0xc7, 0x63, 0xd2, 0x59, 0x10, 0xbd, 0xd3, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x93b1ebcd54870dfe, - 0xf1578300e1342e11, - 0x8270dca3a912407b, - 0x2089faf462438296, - 0x828e5848cd48ea66, - 0x141ecbac1deb038b, + 0x14, 0x1e, 0xcb, 0xac, 0x1d, 0xeb, 0x03, 0x8b, 0x82, 0x8e, 0x58, 0x48, 0xcd, + 0x48, 0xea, 0x66, 0x20, 0x89, 0xfa, 0xf4, 0x62, 0x43, 0x82, 0x96, 0x82, 0x70, + 0xdc, 0xa3, 0xa9, 0x12, 0x40, 0x7b, 0xf1, 0x57, 0x83, 0x00, 0xe1, 0x34, 0x2e, + 0x11, 0x93, 0xb1, 0xeb, 0xcd, 0x54, 0x87, 0x0d, 0xfe, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf5d2c28857229c3f, - 0x8c1574228757ca23, - 0xe8d8102175f5dc19, - 0x2767032fc37cc31d, - 0xd5ee2aba84fd10fe, - 0x16576ccd3dd0a4e8, + 0x16, 0x57, 0x6c, 0xcd, 0x3d, 0xd0, 0xa4, 0xe8, 0xd5, 0xee, 0x2a, 0xba, 0x84, + 0xfd, 0x10, 0xfe, 0x27, 0x67, 0x03, 0x2f, 0xc3, 0x7c, 0xc3, 0x1d, 0xe8, 0xd8, + 0x10, 0x21, 0x75, 0xf5, 0xdc, 0x19, 0x8c, 0x15, 0x74, 0x22, 0x87, 0x57, 0xca, + 0x23, 0xf5, 0xd2, 0xc2, 0x88, 0x57, 0x22, 0x9c, 0x3f, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4da9b6f6a96d1dd2, - 0x9657f7da77f1650e, - 0xbc150712f9ffe6da, - 0x31898db63f87363a, - 0xabab040ddbd097cc, - 0x11ad236b9ba02990, + 0x11, 0xad, 0x23, 0x6b, 0x9b, 0xa0, 0x29, 0x90, 0xab, 0xab, 0x04, 0x0d, 0xdb, + 0xd0, 0x97, 0xcc, 0x31, 0x89, 0x8d, 0xb6, 0x3f, 0x87, 0x36, 0x3a, 0xbc, 0x15, + 0x07, 0x12, 0xf9, 0xff, 0xe6, 0xda, 0x96, 0x57, 0xf7, 0xda, 0x77, 0xf1, 0x65, + 0x0e, 0x4d, 0xa9, 0xb6, 0xf6, 0xa9, 0x6d, 0x1d, 0xd2, ])) .unwrap(), }, @@ -1927,41 +1951,33 @@ pub mod g2 { G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xcde7ee8a3f2ac8af, - 0xfc642eb35975b069, - 0xa7de72b7dd0e64b7, - 0xf1273e6406eef9cc, - 0xababd760ff05cb92, - 0xd7c20456617e89 + 0x00, 0xd7, 0xc2, 0x04, 0x56, 0x61, 0x7e, 0x89, 0xab, 0xab, 0xd7, 0x60, + 0xff, 0x05, 0xcb, 0x92, 0xf1, 0x27, 0x3e, 0x64, 0x06, 0xee, 0xf9, 0xcc, + 0xa7, 0xde, 0x72, 0xb7, 0xdd, 0x0e, 0x64, 0xb7, 0xfc, 0x64, 0x2e, 0xb3, + 0x59, 0x75, 0xb0, 0x69, 0xcd, 0xe7, 0xee, 0x8a, 0x3f, 0x2a, 0xc8, 0xaf, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xd1a50b8572cbd2b8, - 0x238f0ac6119d07df, - 0x4dbe924fe5fd6ac2, - 0x8b203284c51edf6b, - 0xc8a0b730bbb21f5e, - 0x1a3b59d29a31274 + 0x01, 0xa3, 0xb5, 0x9d, 0x29, 0xa3, 0x12, 0x74, 0xc8, 0xa0, 0xb7, 0x30, + 0xbb, 0xb2, 0x1f, 0x5e, 0x8b, 0x20, 0x32, 0x84, 0xc5, 0x1e, 0xdf, 0x6b, + 0x4d, 0xbe, 0x92, 0x4f, 0xe5, 0xfd, 0x6a, 0xc2, 0x23, 0x8f, 0x0a, 0xc6, + 0x11, 0x9d, 0x07, 0xdf, 0xd1, 0xa5, 0x0b, 0x85, 0x72, 0xcb, 0xd2, 0xb8, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x9e709e78a8eaa4c9, - 0xd30921c93ec342f4, - 0x6d1ef332486f5e34, - 0x64528ab3863633dc, - 0x159384333d7cba97, - 0x4cb84741f3cafe8 + 0x04, 0xcb, 0x84, 0x74, 0x1f, 0x3c, 0xaf, 0xe8, 0x15, 0x93, 0x84, 0x33, + 0x3d, 0x7c, 0xba, 0x97, 0x64, 0x52, 0x8a, 0xb3, 0x86, 0x36, 0x33, 0xdc, + 0x6d, 0x1e, 0xf3, 0x32, 0x48, 0x6f, 0x5e, 0x34, 0xd3, 0x09, 0x21, 0xc9, + 0x3e, 0xc3, 0x42, 0xf4, 0x9e, 0x70, 0x9e, 0x78, 0xa8, 0xea, 0xa4, 0xc9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x242af0dc3640e1a4, - 0xe90a73ad65c66919, - 0x2bd7ca7f4346f9ec, - 0x38528f92b689644d, - 0xb6884deec59fb21f, - 0x3c075d3ec52ba90 + 0x03, 0xc0, 0x75, 0xd3, 0xec, 0x52, 0xba, 0x90, 0xb6, 0x88, 0x4d, 0xee, + 0xc5, 0x9f, 0xb2, 0x1f, 0x38, 0x52, 0x8f, 0x92, 0xb6, 0x89, 0x64, 0x4d, + 0x2b, 0xd7, 0xca, 0x7f, 0x43, 0x46, 0xf9, 0xec, 0xe9, 0x0a, 0x73, 0xad, + 0x65, 0xc6, 0x69, 0x19, 0x24, 0x2a, 0xf0, 0xdc, 0x36, 0x40, 0xe1, 0xa4, ])) .unwrap(), }, @@ -1975,41 +1991,33 @@ pub mod g2 { let mut p = G2 { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, + 0x10, 0x0b, 0x2f, 0xe5, 0xbf, 0xfe, 0x03, 0x0b, 0x46, 0x17, 0xf2, 0xe6, 0x77, + 0x4e, 0x97, 0x11, 0x72, 0x55, 0x6c, 0x99, 0x9f, 0x37, 0x07, 0xac, 0x27, 0x50, + 0x94, 0xf1, 0x35, 0x21, 0x23, 0xa9, 0xf0, 0x34, 0x64, 0x2d, 0x2c, 0x9e, 0x85, + 0xbd, 0x6c, 0x99, 0x4c, 0xc1, 0xe3, 0x03, 0x09, 0x4e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, + 0x0d, 0xe8, 0x84, 0xf8, 0x9a, 0x9a, 0x37, 0x1b, 0x93, 0xeb, 0xe7, 0xc3, 0xe4, + 0x1f, 0x6a, 0xcc, 0x46, 0x37, 0xc4, 0xf4, 0x17, 0x66, 0x7e, 0x2e, 0x19, 0xce, + 0x46, 0x78, 0xae, 0xd4, 0xfc, 0xb5, 0xe2, 0x30, 0x39, 0xd1, 0xfe, 0x9c, 0x08, + 0x81, 0x07, 0xa3, 0x35, 0x55, 0x97, 0x7e, 0xc6, 0x08, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, + 0x19, 0x1b, 0x24, 0x32, 0x40, 0x7c, 0xbb, 0x7f, 0x0d, 0x83, 0x11, 0x2a, 0xac, + 0xe3, 0x5c, 0xae, 0x25, 0xfd, 0x42, 0x7b, 0x41, 0x22, 0xf2, 0x31, 0xaa, 0x9b, + 0x06, 0x6d, 0x74, 0x69, 0x40, 0x06, 0x44, 0xfb, 0x33, 0x91, 0xfe, 0x3c, 0x9c, + 0x30, 0xe0, 0x73, 0x11, 0x94, 0x72, 0xe1, 0xeb, 0x62, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, + 0x03, 0xbd, 0xaf, 0xaf, 0x7c, 0xa9, 0xb3, 0x9b, 0xf6, 0xa0, 0x3d, 0x31, 0xe2, + 0xec, 0x21, 0x83, 0x9e, 0xaa, 0x6d, 0x19, 0xde, 0x56, 0x91, 0x96, 0x96, 0xc3, + 0x0f, 0x04, 0x11, 0x59, 0x0b, 0x48, 0xe9, 0x86, 0x05, 0x70, 0x68, 0xb5, 0x0b, + 0x7d, 0xf6, 0x8a, 0xe8, 0x2f, 0xe9, 0x76, 0x62, 0xf5, ])) .unwrap(), }, @@ -2025,41 +2033,33 @@ pub mod g2 { G2Affine { x: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x91ccb1292727c404, - 0x91a6cb182438fad7, - 0x116aee59434de902, - 0xbcedcfce1e52d986, - 0x9755d4a3926e9862, - 0x18bab73760fd8024 + 0x18, 0xba, 0xb7, 0x37, 0x60, 0xfd, 0x80, 0x24, 0x97, 0x55, 0xd4, 0xa3, + 0x92, 0x6e, 0x98, 0x62, 0xbc, 0xed, 0xcf, 0xce, 0x1e, 0x52, 0xd9, 0x86, + 0x11, 0x6a, 0xee, 0x59, 0x43, 0x4d, 0xe9, 0x02, 0x91, 0xa6, 0xcb, 0x18, + 0x24, 0x38, 0xfa, 0xd7, 0x91, 0xcc, 0xb1, 0x29, 0x27, 0x27, 0xc4, 0x04, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4e7c5e0a2ae5b99e, - 0x96e582a27f028961, - 0xc74d1cf4ef2d5926, - 0xeb0cf5e610ef4fe7, - 0x7b4c2bae8db6e70b, - 0xf136e43909fca0 + 0x00, 0xf1, 0x36, 0xe4, 0x39, 0x09, 0xfc, 0xa0, 0x7b, 0x4c, 0x2b, 0xae, + 0x8d, 0xb6, 0xe7, 0x0b, 0xeb, 0x0c, 0xf5, 0xe6, 0x10, 0xef, 0x4f, 0xe7, + 0xc7, 0x4d, 0x1c, 0xf4, 0xef, 0x2d, 0x59, 0x26, 0x96, 0xe5, 0x82, 0xa2, + 0x7f, 0x02, 0x89, 0x61, 0x4e, 0x7c, 0x5e, 0x0a, 0x2a, 0xe5, 0xb9, 0x9e, ])) .unwrap(), }, y: Fq2 { c0: Fq::from_repr(FqRepr([ - 0x954d4466ab13e58, - 0x3ee42eec614cf890, - 0x853bb1d28877577e, - 0xa5a2a51f7fde787b, - 0x8b92866bc6384188, - 0x81a53fe531d64ef + 0x08, 0x1a, 0x53, 0xfe, 0x53, 0x1d, 0x64, 0xef, 0x8b, 0x92, 0x86, 0x6b, + 0xc6, 0x38, 0x41, 0x88, 0xa5, 0xa2, 0xa5, 0x1f, 0x7f, 0xde, 0x78, 0x7b, + 0x85, 0x3b, 0xb1, 0xd2, 0x88, 0x77, 0x57, 0x7e, 0x3e, 0xe4, 0x2e, 0xec, + 0x61, 0x4c, 0xf8, 0x90, 0x09, 0x54, 0xd4, 0x46, 0x6a, 0xb1, 0x3e, 0x58, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4c5d607666239b34, - 0xeddb5f48304d14b3, - 0x337167ee6e8e3cb6, - 0xb271f52f12ead742, - 0x244e6c2015c83348, - 0x19e2deae6eb9b441 + 0x19, 0xe2, 0xde, 0xae, 0x6e, 0xb9, 0xb4, 0x41, 0x24, 0x4e, 0x6c, 0x20, + 0x15, 0xc8, 0x33, 0x48, 0xb2, 0x71, 0xf5, 0x2f, 0x12, 0xea, 0xd7, 0x42, + 0x33, 0x71, 0x67, 0xee, 0x6e, 0x8e, 0x3c, 0xb6, 0xed, 0xdb, 0x5f, 0x48, + 0x30, 0x4d, 0x14, 0xb3, 0x4c, 0x5d, 0x60, 0x76, 0x66, 0x23, 0x9b, 0x34, ])) .unwrap(), }, diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index d272545..21ae050 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1,15 +1,19 @@ use super::fq2::Fq2; -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; +use ff::{Field, PrimeField}; +use std::ops::{AddAssign, MulAssign, SubAssign}; + +#[cfg(test)] +use std::ops::Neg; // B coefficient of BLS12-381 curve, 4. -pub const B_COEFF: Fq = Fq(FqRepr([ +pub const B_COEFF: Fq = Fq([ 0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x9d645513d83de7e, -])); +]); // The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, // and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the @@ -18,228 +22,228 @@ pub const B_COEFF: Fq = Fq(FqRepr([ // Generator of G1 // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ +pub const G1_GENERATOR_X: Fq = Fq([ 0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75, -])); -pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ +]); +pub const G1_GENERATOR_Y: Fq = Fq([ 0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, 0x51ac582950405194, 0xe1c8c3fad0059c0, 0xbbc3efc5008a26a, -])); +]); // Generator of G2 // x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 // y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 -pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ +pub const G2_GENERATOR_X_C0: Fq = Fq([ 0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, 0x6f67b7631863366b, 0x58191924350bcd7, -])); -pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_X_C1: Fq = Fq([ 0xa5a9c0759e23f606, 0xaaa0c59dbccd60c3, 0x3bb17e18e2867806, 0x1b1ab6cc8541b367, 0xc2b6ed0ef2158547, 0x11922a097360edf3, -])); -pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_Y_C0: Fq = Fq([ 0x4c730af860494c4a, 0x597cfa1f5e369c5a, 0xe7e6856caa0a635a, 0xbbefb5e96e0d495f, 0x7d3a975f0ef25a2, 0x83fd8e7e80dae5, -])); -pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ +]); +pub const G2_GENERATOR_Y_C1: Fq = Fq([ 0xadc0fc92df64b05d, 0x18aa270a2b1461dc, 0x86adac6a3be4eba0, 0x79495c4ec93da33a, 0xe7175850a43ccaed, 0xb2bc2a163de1bf2, -])); +]); // Coefficients for the Frobenius automorphism. pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ // Fq(-1)**(((q^0) - 1) / 2) - Fq(FqRepr([ + Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), + ]), // Fq(-1)**(((q^1) - 1) / 2) - Fq(FqRepr([ + Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), + ]), ]; pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ // Fq2(u + 1)**(((q^0) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^1) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), + ]), }, // Fq2(u + 1)**(((q^2) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^3) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), + ]), }, // Fq2(u + 1)**(((q^4) - 1) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^5) - 1) / 3) Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ + c0: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), + c1: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), + ]), }, ]; pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ // Fq2(u + 1)**(((2q^0) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^1) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^2) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^3) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^4) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((2q^5) - 2) / 3) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, ]; @@ -247,222 +251,223 @@ pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ // Fq2(u + 1)**(((q^0) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^1) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf, - ])), + ]), }, // Fq2(u + 1)**(((q^2) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^3) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2, - ])), + ]), }, // Fq2(u + 1)**(((q^4) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^5) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd, - ])), + ]), }, // Fq2(u + 1)**(((q^6) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^7) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb, - ])), + ]), }, // Fq2(u + 1)**(((q^8) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^9) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8, - ])), + ]), }, // Fq2(u + 1)**(((q^10) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), }, // Fq2(u + 1)**(((q^11) - 1) / 6) Fq2 { - c0: Fq(FqRepr([ + c0: Fq([ 0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd, - ])), - c1: Fq(FqRepr([ + ]), + c1: Fq([ 0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd, - ])), + ]), }, ]; // -((2**384) mod q) mod q -pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ +pub const NEGATIVE_ONE: Fq = Fq([ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206, -])); +]); #[derive(PrimeField)] #[PrimeFieldModulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787"] #[PrimeFieldGenerator = "2"] -pub struct Fq(FqRepr); +#[PrimeFieldReprEndianness = "big"] +pub struct Fq([u64; 6]); #[test] fn test_b_coeff() { - assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); + assert_eq!(Fq::from(4), B_COEFF); } #[test] +#[allow(clippy::cognitive_complexity)] fn test_frob_coeffs() { - let mut nqr = Fq::one(); - nqr.negate(); + let nqr = Fq::one().neg(); assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one()); assert_eq!( FROBENIUS_COEFF_FQ2_C1[1], - nqr.pow([ - 0xdcff7fffffffd555, + nqr.pow_vartime([ + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, @@ -479,8 +484,8 @@ fn test_frob_coeffs() { assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one()); assert_eq!( FROBENIUS_COEFF_FQ6_C1[1], - nqr.pow([ - 0x9354ffffffffe38e, + nqr.pow_vartime([ + 0x9354ffffffffe38eu64, 0xa395554e5c6aaaa, 0xcd104635a790520c, 0xcc27c3d6fbd7063f, @@ -490,8 +495,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C1[2], - nqr.pow([ - 0xb78e0000097b2f68, + nqr.pow_vartime([ + 0xb78e0000097b2f68u64, 0xd44f23b47cbd64e3, 0x5cb9668120b069a9, 0xccea85f9bf7b3d16, @@ -507,8 +512,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C1[3], - nqr.pow([ - 0xdbc6fcd6f35b9e06, + nqr.pow_vartime([ + 0xdbc6fcd6f35b9e06u64, 0x997dead10becd6aa, 0x9dbbd24c17206460, 0x72b97acc6057c45e, @@ -530,8 +535,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C1[4], - nqr.pow([ - 0x4649add3c71c6d90, + nqr.pow_vartime([ + 0x4649add3c71c6d90u64, 0x43caa6528972a865, 0xcda8445bbaaa0fbb, 0xc93dea665662aa66, @@ -559,8 +564,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C1[5], - nqr.pow([ - 0xf896f792732eb2be, + nqr.pow_vartime([ + 0xf896f792732eb2beu64, 0x49c86a6d1dc593a1, 0xe5b31e94581f91c3, 0xe3da5cc0a6b20d7f, @@ -596,8 +601,8 @@ fn test_frob_coeffs() { assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one()); assert_eq!( FROBENIUS_COEFF_FQ6_C2[1], - nqr.pow([ - 0x26a9ffffffffc71c, + nqr.pow_vartime([ + 0x26a9ffffffffc71cu64, 0x1472aaa9cb8d5555, 0x9a208c6b4f20a418, 0x984f87adf7ae0c7f, @@ -607,8 +612,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C2[2], - nqr.pow([ - 0x6f1c000012f65ed0, + nqr.pow_vartime([ + 0x6f1c000012f65ed0u64, 0xa89e4768f97ac9c7, 0xb972cd024160d353, 0x99d50bf37ef67a2c, @@ -624,8 +629,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C2[3], - nqr.pow([ - 0xb78df9ade6b73c0c, + nqr.pow_vartime([ + 0xb78df9ade6b73c0cu64, 0x32fbd5a217d9ad55, 0x3b77a4982e40c8c1, 0xe572f598c0af88bd, @@ -647,8 +652,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C2[4], - nqr.pow([ - 0x8c935ba78e38db20, + nqr.pow_vartime([ + 0x8c935ba78e38db20u64, 0x87954ca512e550ca, 0x9b5088b775541f76, 0x927bd4ccacc554cd, @@ -676,8 +681,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ6_C2[5], - nqr.pow([ - 0xf12def24e65d657c, + nqr.pow_vartime([ + 0xf12def24e65d657cu64, 0x9390d4da3b8b2743, 0xcb663d28b03f2386, 0xc7b4b9814d641aff, @@ -713,8 +718,8 @@ fn test_frob_coeffs() { assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one()); assert_eq!( FROBENIUS_COEFF_FQ12_C1[1], - nqr.pow([ - 0x49aa7ffffffff1c7, + nqr.pow_vartime([ + 0x49aa7ffffffff1c7u64, 0x51caaaa72e35555, 0xe688231ad3c82906, 0xe613e1eb7deb831f, @@ -724,8 +729,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[2], - nqr.pow([ - 0xdbc7000004bd97b4, + nqr.pow_vartime([ + 0xdbc7000004bd97b4u64, 0xea2791da3e5eb271, 0x2e5cb340905834d4, 0xe67542fcdfbd9e8b, @@ -741,8 +746,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[3], - nqr.pow(vec![ - 0x6de37e6b79adcf03, + nqr.pow_vartime(vec![ + 0x6de37e6b79adcf03u64, 0x4cbef56885f66b55, 0x4edde9260b903230, 0x395cbd66302be22f, @@ -764,8 +769,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[4], - nqr.pow(vec![ - 0xa324d6e9e38e36c8, + nqr.pow_vartime(vec![ + 0xa324d6e9e38e36c8u64, 0xa1e5532944b95432, 0x66d4222ddd5507dd, 0xe49ef5332b315533, @@ -793,8 +798,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[5], - nqr.pow(vec![ - 0xfc4b7bc93997595f, + nqr.pow_vartime(vec![ + 0xfc4b7bc93997595fu64, 0xa4e435368ee2c9d0, 0xf2d98f4a2c0fc8e1, 0xf1ed2e60535906bf, @@ -828,8 +833,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[6], - nqr.pow(vec![ - 0x21219610a012ba3c, + nqr.pow_vartime(vec![ + 0x21219610a012ba3cu64, 0xa5c19ad35375325, 0x4e9df1e497674396, 0xfb05b717c991c6ef, @@ -869,8 +874,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[7], - nqr.pow(vec![ - 0x742754a1f22fdb, + nqr.pow_vartime(vec![ + 0x742754a1f22fdbu64, 0x2a1955c2dec3a702, 0x9747b28c796d134e, 0xc113a0411f59db79, @@ -916,8 +921,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[8], - nqr.pow(vec![ - 0x802f5720d0b25710, + nqr.pow_vartime(vec![ + 0x802f5720d0b25710u64, 0x6714f0a258b85c7c, 0x31394c90afdf16e, 0xe9d2b0c64f957b19, @@ -969,8 +974,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[9], - nqr.pow(vec![ - 0x4af4accf7de0b977, + nqr.pow_vartime(vec![ + 0x4af4accf7de0b977u64, 0x742485e21805b4ee, 0xee388fbc4ac36dec, 0x1e199da57ad178a, @@ -1028,8 +1033,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[10], - nqr.pow(vec![ - 0xe5953a4f96cdda44, + nqr.pow_vartime(vec![ + 0xe5953a4f96cdda44u64, 0x336b2d734cbc32bb, 0x3f79bfe3cd7410e, 0x267ae19aaa0f0332, @@ -1093,8 +1098,8 @@ fn test_frob_coeffs() { ); assert_eq!( FROBENIUS_COEFF_FQ12_C1[11], - nqr.pow(vec![ - 0x107db680942de533, + nqr.pow_vartime(vec![ + 0x107db680942de533u64, 0x6262b24d2052393b, 0x6136df824159ebc, 0xedb052c9970c5deb, @@ -1166,8 +1171,7 @@ fn test_frob_coeffs() { #[test] fn test_neg_one() { - let mut o = Fq::one(); - o.negate(); + let o = Fq::one().neg(); assert_eq!(NEGATIVE_ONE, o); } @@ -1177,428 +1181,30 @@ use rand_core::SeedableRng; #[cfg(test)] use rand_xorshift::XorShiftRng; -#[test] -fn test_fq_repr_ordering() { - use std::cmp::Ordering; - - fn assert_equality(a: FqRepr, b: FqRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == Ordering::Equal); - } - - fn assert_lt(a: FqRepr, b: FqRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9999, 9998]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); -} - -#[test] -fn test_fq_repr_from() { - assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_fq_repr_is_odd() { - assert!(!FqRepr::from(0).is_odd()); - assert!(FqRepr::from(0).is_even()); - assert!(FqRepr::from(1).is_odd()); - assert!(!FqRepr::from(1).is_even()); - assert!(!FqRepr::from(324834872).is_odd()); - assert!(FqRepr::from(324834872).is_even()); - assert!(FqRepr::from(324834873).is_odd()); - assert!(!FqRepr::from(324834873).is_even()); -} - -#[test] -fn test_fq_repr_is_zero() { - assert!(FqRepr::from(0).is_zero()); - assert!(!FqRepr::from(1).is_zero()); - assert!(!FqRepr([0, 0, 0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fq_repr_div2() { - let mut a = FqRepr([ - 0x8b0ad39f8dd7482a, - 0x147221c9a7178b69, - 0x54764cb08d8a6aa0, - 0x8519d708e1d83041, - 0x41f82777bd13fdb, - 0xf43944578f9b771b, - ]); - a.div2(); - assert_eq!( - a, - FqRepr([ - 0xc58569cfc6eba415, - 0xa3910e4d38bc5b4, - 0xaa3b265846c53550, - 0xc28ceb8470ec1820, - 0x820fc13bbde89fed, - 0x7a1ca22bc7cdbb8d - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FqRepr([ - 0x6d31615a73f1bae9, - 0x54028e443934e2f1, - 0x82a8ec99611b14d, - 0xfb70a33ae11c3b06, - 0xe36083f04eef7a27, - 0x1e87288af1f36e - ]) - ); - for _ in 0..300 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7288af1f36ee3608, 0x1e8, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..50 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7a1ca2, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..22 { - a.div2(); - } - assert_eq!(a, FqRepr([0x1, 0x0, 0x0, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_shr() { - let mut a = FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e, - ]); - a.shr(0); - assert_eq!( - a, - FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e - ]) - ); - a.shr(1); - assert_eq!( - a, - FqRepr([ - 0xd52e6eb0b9423ffe, - 0x21921603576aa943, - 0xceeead98979ee882, - 0xe2aa0fea40235bf3, - 0xb04067a038f0582, - 0x912f9480d7528a7 - ]) - ); - a.shr(50); - assert_eq!( - a, - FqRepr([ - 0x8580d5daaa50f54b, - 0xab6625e7ba208864, - 0x83fa9008d6fcf3bb, - 0x19e80e3c160b8aa, - 0xbe52035d4a29c2c1, - 0x244 - ]) - ); - a.shr(130); - assert_eq!( - a, - FqRepr([ - 0xa0fea40235bf3cee, - 0x4067a038f0582e2a, - 0x2f9480d7528a70b0, - 0x91, - 0x0, - 0x0 - ]) - ); - a.shr(64); - assert_eq!( - a, - FqRepr([0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0, 0x0]) - ); -} - -#[test] -fn test_fq_repr_mul2() { - let mut a = FqRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FqRepr([0xb0acd6c96, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!( - a, - FqRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0, 0x0, 0x0]) - ); - for _ in 0..300 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0xcd6c960000000000])); - for _ in 0..17 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x2c00000000000000])); - for _ in 0..6 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_num_bits() { - let mut a = FqRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FqRepr::from(1); - for i in 1..385 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fq_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.sub_noborrow(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0x40a12b8967c54bae, - 0xdeae37a0837d0d7b, - 0xe592c487bae374e, - 0xaf26bbc934462a61, - 0x32d6cc6e2b7a4a03, - 0xcdaf23e091c0313 - ]) - ); - - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - a.0[5] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } - - // Subtracting q+1 from q should produce -1 (mod 2**384) - let mut qplusone = FqRepr([ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ]); - qplusone.sub_noborrow(&FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ])); - assert_eq!( - qplusone, - FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ); -} - -#[test] -fn test_fq_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.add_nocarry(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0xcfae1db798be8c04, - 0x999906db15a10d5a, - 0x270fa8d9defc6f79, - 0x83abb199c240f7b6, - 0x27469abae93e1ff6, - 0xdd2fd2d4dfab6be - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - let mut b = Fq::random(&mut rng).into_repr(); - let mut c = Fq::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[5] >>= 3; - b.0[5] >>= 3; - c.0[5] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } - - // Adding 1 to (2^384 - 1) should produce zero - let mut x = FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - x.add_nocarry(&FqRepr::from(1)); - assert!(x.is_zero()); -} - #[test] fn test_fq_is_valid() { - let mut a = Fq(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FqRepr::from(1)); + a.sub_noborrow(&Fq([1, 0, 0, 0, 0, 0])); assert!(a.is_valid()); - assert!(Fq(FqRepr::from(0)).is_valid()); - assert!(Fq(FqRepr([ + assert!(Fq::from(0).is_valid()); + assert!(Fq([ 0xdf4671abd14dab3e, 0xe2dc0c9f534fbd33, 0x31ca6c880cc444a6, 0x257a67e70ef33359, 0xf9b29e493f899b36, 0x17c8be1800b9f059 - ])) + ]) .is_valid()); - assert!(!Fq(FqRepr([ + assert!(!Fq([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -1616,103 +1222,103 @@ fn test_fq_is_valid() { fn test_fq_add_assign() { { // Random number - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0x624434821df92b69, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b, - ])); + ]); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fq(FqRepr::from(0))); + tmp.add_assign(&Fq([0, 0, 0, 0, 0, 0])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x624434821df92b69, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fq(FqRepr::from(1))); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x624434821df92b6a, 0x503260c04fd2e2ea, 0xd9df726e0d16e8ce, 0xfbcb39adfd5dfaeb, 0x86b8a22b0c88b112, 0x165a2ed809e4201b - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fq(FqRepr([ + tmp.add_assign(&Fq([ 0x374d8f8ea7a648d8, 0xe318bb0ebb8bfa9b, 0x613d996f0a95b400, 0x9fac233cb7e4fef1, 0x67e47552d253c52, 0x5c31b227edf25da, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0xdf92c410c59fc997, 0x149f1bd05a0add85, 0xd3ec393c20fba6ab, 0x37001165c1bde71d, 0x421b41c9f662408e, 0x21c38104f435f5b - ])) + ]) ); // Add one to (q - 1) and test for the result. - tmp = Fq(FqRepr([ + tmp = Fq([ 0xb9feffffffffaaaa, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a, - ])); - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is q - 1 - tmp = Fq(FqRepr([ + tmp = Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ])); - tmp.add_assign(&Fq(FqRepr([ + ]); + tmp.add_assign(&Fq([ 0x66ecde5bef0fe14f, 0xac2a6cf8aed568e8, 0x861d70d86483edd, 0xcc98f1b7839a22e8, 0x6ee5e2a4eae7674e, 0x194e40737930c599, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0xb9feffffffffaaaa, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fq([1, 0, 0, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -1746,87 +1352,87 @@ fn test_fq_add_assign() { fn test_fq_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ])); - tmp.sub_assign(&Fq(FqRepr([ + ]); + tmp.sub_assign(&Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x748014838971292c, 0xfd20fad49fddde5c, 0xcf87f198e3d3f336, 0x3d62d6e6e41883db, 0x45a3443cd88dc61b, 0x151d57aaf755ff94 - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fq(FqRepr([ + tmp = Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr([ + ]); + tmp.sub_assign(&Fq([ 0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100, - ]))); + ])); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x457eeb7c768e817f, 0x218b052a117621a3, 0x97a8e10812dd02ed, 0x2714749e0f6c8ee3, 0x57863796abde6bc, 0x4e3ba3f4229e706 - ])) + ]) ); // Test for sensible results with zero - tmp = Fq(FqRepr::from(0)); - tmp.sub_assign(&Fq(FqRepr::from(0))); + tmp = Fq::zero(); + tmp.sub_assign(&Fq::zero()); assert!(tmp.is_zero()); - tmp = Fq(FqRepr([ + tmp = Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr::from(0))); + ]); + tmp.sub_assign(&Fq::zero()); assert_eq!( tmp, - Fq(FqRepr([ + Fq([ 0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806 - ])) + ]) ); } @@ -1853,31 +1459,31 @@ fn test_fq_sub_assign() { #[test] fn test_fq_mul_assign() { - let mut tmp = Fq(FqRepr([ + let mut tmp = Fq([ 0xcc6200000020aa8a, 0x422800801dd8001a, 0x7f4f5e619041c62c, 0x8a55171ac70ed2ba, 0x3f69cc3a3d07d58b, 0xb972455fd09b8ef, - ])); - tmp.mul_assign(&Fq(FqRepr([ + ]); + tmp.mul_assign(&Fq([ 0x329300000030ffcf, 0x633c00c02cc40028, 0xbef70d925862a942, 0x4f7fa2a82a963c17, 0xdf1eb2575b8bc051, 0x1162b680fb8e9566, - ]))); + ])); assert!( - tmp == Fq(FqRepr([ + tmp == Fq([ 0x9dc4000001ebfe14, 0x2850078997b00193, 0xa8197f1abb4d7bf, 0xc0309573f4bfe871, 0xf48d0923ffaf7620, 0x11d4b58c7a926e66 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -1928,25 +1534,22 @@ fn test_fq_mul_assign() { #[test] fn test_fq_squaring() { - let mut a = Fq(FqRepr([ + let a = Fq([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x19ffffffffffffff, - ])); + ]); assert!(a.is_valid()); - a.square(); assert_eq!( - a, + a.square(), Fq::from_repr(FqRepr([ - 0x1cfb28fe7dfbbb86, - 0x24cbe1731577a59, - 0xcce1d4edc120e66e, - 0xdc05c659b4e15b27, - 0x79361e5a802c6a23, - 0x24bcbe5d51b9a6f + 0x02, 0x4b, 0xcb, 0xe5, 0xd5, 0x1b, 0x9a, 0x6f, 0x79, 0x36, 0x1e, 0x5a, 0x80, 0x2c, + 0x6a, 0x23, 0xdc, 0x05, 0xc6, 0x59, 0xb4, 0xe1, 0x5b, 0x27, 0xcc, 0xe1, 0xd4, 0xed, + 0xc1, 0x20, 0xe6, 0x6e, 0x02, 0x4c, 0xbe, 0x17, 0x31, 0x57, 0x7a, 0x59, 0x1c, 0xfb, + 0x28, 0xfe, 0x7d, 0xfb, 0xbb, 0x86, ])) .unwrap() ); @@ -1959,20 +1562,13 @@ fn test_fq_squaring() { for _ in 0..1000000 { // Ensure that (a * a) = a^2 let a = Fq::random(&mut rng); - - let mut tmp = a; - tmp.square(); - - let mut tmp2 = a; - tmp2.mul_assign(&a); - - assert_eq!(tmp, tmp2); + assert_eq!(a.square(), a * a); } } #[test] -fn test_fq_inverse() { - assert!(Fq::zero().inverse().is_none()); +fn test_fq_invert() { + assert!(bool::from(Fq::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1984,7 +1580,7 @@ fn test_fq_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fq::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -1999,19 +1595,15 @@ fn test_fq_double() { for _ in 0..1000 { // Ensure doubling a is equivalent to adding a to itself. - let mut a = Fq::random(&mut rng); - let mut b = a; - b.add_assign(&a); - a.double(); - assert_eq!(a, b); + let a = Fq::random(&mut rng); + assert_eq!(a.double(), a + a); } } #[test] -fn test_fq_negate() { +fn test_fq_neg() { { - let mut a = Fq::zero(); - a.negate(); + let a = Fq::zero().neg(); assert!(a.is_zero()); } @@ -2024,8 +1616,7 @@ fn test_fq_negate() { for _ in 0..1000 { // Ensure (a - (-a)) = 0. let mut a = Fq::random(&mut rng); - let mut b = a; - b.negate(); + let b = a.neg(); a.add_assign(&b); assert!(a.is_zero()); @@ -2039,11 +1630,11 @@ fn test_fq_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fq::random(&mut rng); - let target = a.pow(&[i]); + let target = a.pow_vartime(&[i]); let mut c = Fq::one(); for _ in 0..i { c.mul_assign(&a); @@ -2051,18 +1642,21 @@ fn test_fq_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 6]; + byteorder::BigEndian::read_u64_into(Fq::char().as_ref(), &mut char_limbs); + char_limbs.reverse(); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fq::random(&mut rng); - assert_eq!(a, a.pow(Fq::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } #[test] fn test_fq_sqrt() { - use ff::SqrtField; - let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -2073,10 +1667,8 @@ fn test_fq_sqrt() { for _ in 0..1000 { // Ensure sqrt(a^2) = a or -a let a = Fq::random(&mut rng); - let mut nega = a; - nega.negate(); - let mut b = a; - b.square(); + let nega = a.neg(); + let b = a.square(); let b = b.sqrt().unwrap(); @@ -2087,62 +1679,53 @@ fn test_fq_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fq::random(&mut rng); - if let Some(mut tmp) = a.sqrt() { - tmp.square(); - - assert_eq!(a, tmp); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } #[test] -fn test_fq_from_into_repr() { +fn test_fq_from_to_repr() { // q + 1 should not be in the field assert!(Fq::from_repr(FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, + 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, + 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xaa, 0xac, ])) - .is_err()); + .is_none()); // q should not be in the field - assert!(Fq::from_repr(Fq::char()).is_err()); + assert!(Fq::from_repr(Fq::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. let a = FqRepr([ - 0x4a49dad4ff6cde2d, - 0xac62a82a8f51cd50, - 0x2b1f41ab9f36d640, - 0x908a387f480735f1, - 0xae30740c08a875d7, - 0x6c80918a365ef78, + 0x06, 0xc8, 0x09, 0x18, 0xa3, 0x65, 0xef, 0x78, 0xae, 0x30, 0x74, 0x0c, 0x08, 0xa8, 0x75, + 0xd7, 0x90, 0x8a, 0x38, 0x7f, 0x48, 0x07, 0x35, 0xf1, 0x2b, 0x1f, 0x41, 0xab, 0x9f, 0x36, + 0xd6, 0x40, 0xac, 0x62, 0xa8, 0x2a, 0x8f, 0x51, 0xcd, 0x50, 0x4a, 0x49, 0xda, 0xd4, 0xff, + 0x6c, 0xde, 0x2d, ]); let mut a_fq = Fq::from_repr(a).unwrap(); let b = FqRepr([ - 0xbba57917c32f0cf0, - 0xe7f878cf87f05e5d, - 0x9498b4292fd27459, - 0xd59fd94ee4572cfa, - 0x1f607186d5bb0059, - 0xb13955f5ac7f6a3, + 0x0b, 0x13, 0x95, 0x5f, 0x5a, 0xc7, 0xf6, 0xa3, 0x1f, 0x60, 0x71, 0x86, 0xd5, 0xbb, 0x00, + 0x59, 0xd5, 0x9f, 0xd9, 0x4e, 0xe4, 0x57, 0x2c, 0xfa, 0x94, 0x98, 0xb4, 0x29, 0x2f, 0xd2, + 0x74, 0x59, 0xe7, 0xf8, 0x78, 0xcf, 0x87, 0xf0, 0x5e, 0x5d, 0xbb, 0xa5, 0x79, 0x17, 0xc3, + 0x2f, 0x0c, 0xf0, ]); let b_fq = Fq::from_repr(b).unwrap(); let c = FqRepr([ - 0xf5f70713b717914c, - 0x355ea5ac64cbbab1, - 0xce60dd43417ec960, - 0xf16b9d77b0ad7d10, - 0xa44c204c1de7cdb7, - 0x1684487772bc9a5a, + 0x16, 0x84, 0x48, 0x77, 0x72, 0xbc, 0x9a, 0x5a, 0xa4, 0x4c, 0x20, 0x4c, 0x1d, 0xe7, 0xcd, + 0xb7, 0xf1, 0x6b, 0x9d, 0x77, 0xb0, 0xad, 0x7d, 0x10, 0xce, 0x60, 0xdd, 0x43, 0x41, 0x7e, + 0xc9, 0x60, 0x35, 0x5e, 0xa5, 0xac, 0x64, 0xcb, 0xba, 0xb1, 0xf5, 0xf7, 0x07, 0x13, 0xb7, + 0x17, 0x91, 0x4c, ]); a_fq.mul_assign(&b_fq); - assert_eq!(a_fq.into_repr(), c); + assert_eq!(a_fq.to_repr(), c); // Zero should be in the field. - assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); + assert!(Fq::from_repr(FqRepr([0; 48])).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -2152,7 +1735,7 @@ fn test_fq_from_into_repr() { for _ in 0..1000 { // Try to turn Fq elements into representations and back again, and compare. let a = Fq::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FqRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fq::from_repr(a_repr).unwrap(); @@ -2162,35 +1745,37 @@ fn test_fq_from_into_repr() { } #[test] -fn test_fq_repr_display() { +fn test_fq_display() { assert_eq!( - format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])), - "0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string() + format!("{}", Fq::from_repr(FqRepr([ + 0x19, 0x47, 0xf0, 0xd5, 0xf4, 0xfe, 0x32, 0x5a, 0xb1, 0xd4, 0xaa, 0xd8, 0x76, 0x51, + 0xe6, 0x94, 0x67, 0x6c, 0xc4, 0xee, 0xf4, 0xc4, 0x6f, 0x2c, 0xb3, 0x8d, 0x35, 0xb3, + 0xf6, 0x77, 0x95, 0x85, 0x39, 0xa8, 0xf1, 0x84, 0xf3, 0x53, 0x5c, 0x7b, 0xa9, 0x56, + 0xba, 0xbf, 0x93, 0x01, 0xea, 0x24, + ])).unwrap()), + "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() ); assert_eq!( - format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])), - "0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0, 0, 0, 0, 0, 0])), - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string() + format!("{}", Fq::from_repr(FqRepr([ + 0x06, 0xc9, 0xf5, 0xa1, 0x06, 0x0d, 0xe9, 0x74, 0x9a, 0x55, 0x18, 0x59, 0xb1, 0xe4, + 0x3a, 0x9a, 0xb7, 0xf8, 0x9f, 0x88, 0xf5, 0x9c, 0x1d, 0xc5, 0xa4, 0xb6, 0x2a, 0xf4, + 0xa7, 0x92, 0xa6, 0x89, 0x41, 0x3f, 0x6f, 0x7f, 0x06, 0xea, 0x87, 0xeb, 0xe2, 0x8e, + 0x79, 0x39, 0x6a, 0xc2, 0xbb, 0xf8, + ])).unwrap()), + "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() ); } #[test] -fn test_fq_display() { - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()), - "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() - ); - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()), - "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() - ); +fn test_fq_is_odd() { + assert!(!Fq::from(0).is_odd()); + assert!(Fq::from(0).is_even()); + assert!(Fq::from(1).is_odd()); + assert!(!Fq::from(1).is_even()); + assert!(!Fq::from(324834872).is_odd()); + assert!(Fq::from(324834872).is_even()); + assert!(Fq::from(324834873).is_odd()); + assert!(!Fq::from(324834873).is_even()); } #[test] @@ -2201,16 +1786,11 @@ fn test_fq_num_bits() { #[test] fn test_fq_root_of_unity() { - use ff::SqrtField; - assert_eq!(Fq::S, 1); + assert_eq!(Fq::multiplicative_generator(), Fq::from(2)); assert_eq!( - Fq::multiplicative_generator(), - Fq::from_repr(FqRepr::from(2)).unwrap() - ); - assert_eq!( - Fq::multiplicative_generator().pow([ - 0xdcff7fffffffd555, + Fq::multiplicative_generator().pow_vartime([ + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, @@ -2219,67 +1799,26 @@ fn test_fq_root_of_unity() { ]), Fq::root_of_unity() ); - assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one()); - assert!(Fq::multiplicative_generator().sqrt().is_none()); + assert_eq!(Fq::root_of_unity().pow_vartime([1u64 << Fq::S]), Fq::one()); + assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none())); } #[test] fn fq_field_tests() { - ::tests::field::random_field_tests::(); - ::tests::field::random_sqrt_tests::(); - ::tests::field::random_frobenius_tests::(Fq::char(), 13); - ::tests::field::from_str_tests::(); + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::from_str_tests::(); } #[test] fn test_fq_ordering() { - // FqRepr's ordering is well-tested, but we still need to make sure the Fq - // elements aren't being compared in Montgomery form. + // We need to make sure the Fq elements aren't being compared in Montgomery form. for i in 0..100 { - assert!( - Fq::from_repr(FqRepr::from(i + 1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap() - ); + assert!(Fq::from(i + 1) > Fq::from(i)); } } #[test] fn fq_repr_tests() { - ::tests::repr::random_repr_tests::(); -} - -#[test] -fn test_fq_legendre() { - use ff::LegendreSymbol::*; - use ff::SqrtField; - - assert_eq!(QuadraticResidue, Fq::one().legendre()); - assert_eq!(Zero, Fq::zero().legendre()); - - assert_eq!( - QuadraticNonResidue, - Fq::from_repr(FqRepr::from(2)).unwrap().legendre() - ); - assert_eq!( - QuadraticResidue, - Fq::from_repr(FqRepr::from(4)).unwrap().legendre() - ); - - let e = FqRepr([ - 0x52a112f249778642, - 0xd0bedb989b7991f, - 0xdad3b6681aa63c05, - 0xf2efc0bb4721b283, - 0x6057a98f18c24733, - 0x1022c2fd122889e4, - ]); - assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); - let e = FqRepr([ - 0x6dae594e53a96c74, - 0x19b16ca9ba64b37b, - 0x5c764661a59bfc68, - 0xaa346e9b31c60a, - 0x346059f9d87a9fa9, - 0x1d61ac6bfd5c88b, - ]); - assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); + crate::tests::repr::random_repr_tests::(); } diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs index dcfdef1..3cab598 100644 --- a/pairing/src/bls12_381/fq12.rs +++ b/pairing/src/bls12_381/fq12.rs @@ -3,23 +3,25 @@ use super::fq2::Fq2; use super::fq6::Fq6; use ff::Field; use rand_core::RngCore; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq12, represented by c0 + c1 * w. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq12 { pub c0: Fq6, pub c1: Fq6, } impl ::std::fmt::Display for Fq12 { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fq12({} + {} * w)", self.c0, self.c1) } } impl Fq12 { pub fn conjugate(&mut self) { - self.c1.negate(); + self.c1 = self.c1.neg(); } pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { @@ -37,10 +39,145 @@ impl Fq12 { self.c0.mul_by_nonresidue(); self.c0.add_assign(&aa); } + + pub fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } +} + +impl ConditionallySelectable for Fq12 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fq12 { + c0: Fq6::conditional_select(&a.c0, &b.c0, choice), + c1: Fq6::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl Neg for Fq12 { + type Output = Self; + + fn neg(self) -> Self { + Fq12 { + c0: self.c0.neg(), + c1: self.c1.neg(), + } + } +} + +impl<'r> Add<&'r Fq12> for Fq12 { + type Output = Self; + + fn add(self, other: &Self) -> Self { + Fq12 { + c0: self.c0 + other.c0, + c1: self.c1 + other.c1, + } + } +} + +impl Add for Fq12 { + type Output = Self; + + fn add(self, other: Self) -> Self { + self.add(&other) + } +} + +impl<'r> AddAssign<&'r Fq12> for Fq12 { + fn add_assign(&mut self, other: &'r Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } +} + +impl AddAssign for Fq12 { + fn add_assign(&mut self, other: Self) { + self.add_assign(&other); + } +} + +impl<'r> Sub<&'r Fq12> for Fq12 { + type Output = Self; + + fn sub(self, other: &Self) -> Self { + Fq12 { + c0: self.c0 - other.c0, + c1: self.c1 - other.c1, + } + } +} + +impl Sub for Fq12 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + self.sub(&other) + } +} + +impl<'r> SubAssign<&'r Fq12> for Fq12 { + fn sub_assign(&mut self, other: &'r Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } +} + +impl SubAssign for Fq12 { + fn sub_assign(&mut self, other: Self) { + self.sub_assign(&other); + } +} + +impl<'r> Mul<&'r Fq12> for Fq12 { + type Output = Self; + + fn mul(self, other: &Self) -> Self { + let mut ret = self; + ret.mul_assign(other); + ret + } +} + +impl Mul for Fq12 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + self.mul(&other) + } +} + +impl<'r> MulAssign<&'r Fq12> for Fq12 { + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = bb; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&aa); + } +} + +impl MulAssign for Fq12 { + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); + } } impl Field for Fq12 { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fq12 { c0: Fq6::random(rng), c1: Fq6::random(rng), @@ -65,36 +202,14 @@ impl Field for Fq12 { self.c0.is_zero() && self.c1.is_zero() } - fn double(&mut self) { - self.c0.double(); - self.c1.double(); + fn double(&self) -> Self { + Fq12 { + c0: self.c0.double(), + c1: self.c1.double(), + } } - fn negate(&mut self) { - self.c0.negate(); - self.c1.negate(); - } - - fn add_assign(&mut self, other: &Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } - - fn sub_assign(&mut self, other: &Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } - - fn frobenius_map(&mut self, power: usize) { - self.c0.frobenius_map(power); - self.c1.frobenius_map(power); - - self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); - } - - fn square(&mut self) { + fn square(&self) -> Self { let mut ab = self.c0; ab.mul_assign(&self.c1); let mut c0c1 = self.c0; @@ -104,46 +219,28 @@ impl Field for Fq12 { c0.add_assign(&self.c0); c0.mul_assign(&c0c1); c0.sub_assign(&ab); - self.c1 = ab; - self.c1.add_assign(&ab); + let mut c1 = ab; + c1.add_assign(&ab); ab.mul_by_nonresidue(); c0.sub_assign(&ab); - self.c0 = c0; + Fq12 { c0, c1 } } - fn mul_assign(&mut self, other: &Self) { - let mut aa = self.c0; - aa.mul_assign(&other.c0); - let mut bb = self.c1; - bb.mul_assign(&other.c1); - let mut o = other.c0; - o.add_assign(&other.c1); - self.c1.add_assign(&self.c0); - self.c1.mul_assign(&o); - self.c1.sub_assign(&aa); - self.c1.sub_assign(&bb); - self.c0 = bb; - self.c0.mul_by_nonresidue(); - self.c0.add_assign(&aa); - } - - fn inverse(&self) -> Option { - let mut c0s = self.c0; - c0s.square(); - let mut c1s = self.c1; - c1s.square(); + fn invert(&self) -> CtOption { + let mut c0s = self.c0.square(); + let mut c1s = self.c1.square(); c1s.mul_by_nonresidue(); c0s.sub_assign(&c1s); - c0s.inverse().map(|t| { - let mut tmp = Fq12 { c0: t, c1: t }; - tmp.c0.mul_assign(&self.c0); - tmp.c1.mul_assign(&self.c1); - tmp.c1.negate(); - - tmp + c0s.invert().map(|t| Fq12 { + c0: t.mul(&self.c0), + c1: t.mul(&self.c1).neg(), }) } + + fn sqrt(&self) -> CtOption { + unimplemented!() + } } #[cfg(test)] @@ -185,8 +282,5 @@ fn test_fq12_mul_by_014() { #[test] fn fq12_field_tests() { - use ff::PrimeField; - - ::tests::field::random_field_tests::(); - ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); + crate::tests::field::random_field_tests::(); } diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 7818280..38b48bc 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -1,18 +1,19 @@ use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::{Field, SqrtField}; +use ff::Field; use rand_core::RngCore; - use std::cmp::Ordering; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq2, represented by c0 + c1 * u. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq2 { pub c0: Fq, pub c1: Fq, } impl ::std::fmt::Display for Fq2 { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fq2({} + {} * u)", self.c0, self.c1) } } @@ -46,18 +47,145 @@ impl Fq2 { /// Norm of Fq2 as extension field in i over Fq pub fn norm(&self) -> Fq { - let mut t0 = self.c0; - let mut t1 = self.c1; - t0.square(); - t1.square(); + let t0 = self.c0.square(); + let mut t1 = self.c1.square(); t1.add_assign(&t0); t1 } + + pub fn frobenius_map(&mut self, power: usize) { + self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); + } +} + +impl ConditionallySelectable for Fq2 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fq2 { + c0: Fq::conditional_select(&a.c0, &b.c0, choice), + c1: Fq::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl Neg for Fq2 { + type Output = Self; + + fn neg(self) -> Self { + Fq2 { + c0: self.c0.neg(), + c1: self.c1.neg(), + } + } +} + +impl<'r> Add<&'r Fq2> for Fq2 { + type Output = Self; + + fn add(self, other: &Self) -> Self { + Fq2 { + c0: self.c0 + other.c0, + c1: self.c1 + other.c1, + } + } +} + +impl Add for Fq2 { + type Output = Self; + + fn add(self, other: Self) -> Self { + self.add(&other) + } +} + +impl<'r> AddAssign<&'r Fq2> for Fq2 { + fn add_assign(&mut self, other: &'r Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } +} + +impl AddAssign for Fq2 { + fn add_assign(&mut self, other: Self) { + self.add_assign(&other); + } +} + +impl<'r> Sub<&'r Fq2> for Fq2 { + type Output = Self; + + fn sub(self, other: &Self) -> Self { + Fq2 { + c0: self.c0 - other.c0, + c1: self.c1 - other.c1, + } + } +} + +impl Sub for Fq2 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + self.sub(&other) + } +} + +impl<'r> SubAssign<&'r Fq2> for Fq2 { + fn sub_assign(&mut self, other: &'r Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } +} + +impl SubAssign for Fq2 { + fn sub_assign(&mut self, other: Self) { + self.sub_assign(&other); + } +} + +impl<'r> Mul<&'r Fq2> for Fq2 { + type Output = Self; + + fn mul(self, other: &Self) -> Self { + let mut ret = self; + ret.mul_assign(other); + ret + } +} + +impl Mul for Fq2 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + self.mul(&other) + } +} + +impl<'r> MulAssign<&'r Fq2> for Fq2 { + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = aa; + self.c0.sub_assign(&bb); + } +} + +impl MulAssign for Fq2 { + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); + } } impl Field for Fq2 { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { Fq2 { c0: Fq::random(rng), c1: Fq::random(rng), @@ -82,103 +210,56 @@ impl Field for Fq2 { self.c0.is_zero() && self.c1.is_zero() } - fn square(&mut self) { + fn square(&self) -> Self { let mut ab = self.c0; ab.mul_assign(&self.c1); let mut c0c1 = self.c0; c0c1.add_assign(&self.c1); - let mut c0 = self.c1; - c0.negate(); + let mut c0 = self.c1.neg(); c0.add_assign(&self.c0); c0.mul_assign(&c0c1); c0.sub_assign(&ab); - self.c1 = ab; - self.c1.add_assign(&ab); + let mut c1 = ab; + c1.add_assign(&ab); c0.add_assign(&ab); - self.c0 = c0; + Fq2 { c0, c1 } } - fn double(&mut self) { - self.c0.double(); - self.c1.double(); + fn double(&self) -> Self { + Fq2 { + c0: self.c0.double(), + c1: self.c1.double(), + } } - fn negate(&mut self) { - self.c0.negate(); - self.c1.negate(); - } - - fn add_assign(&mut self, other: &Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } - - fn sub_assign(&mut self, other: &Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } - - fn mul_assign(&mut self, other: &Self) { - let mut aa = self.c0; - aa.mul_assign(&other.c0); - let mut bb = self.c1; - bb.mul_assign(&other.c1); - let mut o = other.c0; - o.add_assign(&other.c1); - self.c1.add_assign(&self.c0); - self.c1.mul_assign(&o); - self.c1.sub_assign(&aa); - self.c1.sub_assign(&bb); - self.c0 = aa; - self.c0.sub_assign(&bb); - } - - fn inverse(&self) -> Option { - let mut t1 = self.c1; - t1.square(); - let mut t0 = self.c0; - t0.square(); + fn invert(&self) -> CtOption { + let t1 = self.c1.square(); + let mut t0 = self.c0.square(); t0.add_assign(&t1); - t0.inverse().map(|t| { - let mut tmp = Fq2 { - c0: self.c0, - c1: self.c1, - }; - tmp.c0.mul_assign(&t); - tmp.c1.mul_assign(&t); - tmp.c1.negate(); - - tmp + t0.invert().map(|t| Fq2 { + c0: self.c0.mul(&t), + c1: self.c1.mul(&t).neg(), }) } - fn frobenius_map(&mut self, power: usize) { - self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); - } -} - -impl SqrtField for Fq2 { - fn legendre(&self) -> ::ff::LegendreSymbol { - self.norm().legendre() - } - - fn sqrt(&self) -> Option { + /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! + /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! + fn sqrt(&self) -> CtOption { // Algorithm 9, https://eprint.iacr.org/2012/685.pdf if self.is_zero() { - Some(Self::zero()) + CtOption::new(Self::zero(), Choice::from(1)) } else { // a1 = self^((q - 3) / 4) - let mut a1 = self.pow([ - 0xee7fbfffffffeaaa, + let mut a1 = self.pow_vartime([ + 0xee7fbfffffffeaaau64, 0x7aaffffac54ffff, 0xd9cc34a83dac3d89, 0xd91dd2e13ce144af, 0x92c6e9ed90d2eb35, 0x680447a8e5ff9a6, ]); - let mut alpha = a1; - alpha.square(); + let mut alpha = a1.square(); alpha.mul_assign(self); let mut a0 = alpha; a0.frobenius_map(1); @@ -190,7 +271,7 @@ impl SqrtField for Fq2 { }; if a0 == neg1 { - None + CtOption::new(Self::zero(), Choice::from(0)) } else { a1.mul_assign(self); @@ -202,8 +283,8 @@ impl SqrtField for Fq2 { } else { alpha.add_assign(&Fq2::one()); // alpha = alpha^((q - 1) / 2) - alpha = alpha.pow([ - 0xdcff7fffffffd555, + alpha = alpha.pow_vartime([ + 0xdcff7fffffffd555u64, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, @@ -213,12 +294,17 @@ impl SqrtField for Fq2 { a1.mul_assign(&alpha); } - Some(a1) + CtOption::new(a1, Choice::from(1)) } } } } +#[cfg(test)] +use super::fq::FqRepr; +#[cfg(test)] +use ff::PrimeField; + #[test] fn test_fq2_ordering() { let mut a = Fq2 { @@ -226,7 +312,7 @@ fn test_fq2_ordering() { c1: Fq::zero(), }; - let mut b = a.clone(); + let mut b = a; assert!(a.cmp(&b) == Ordering::Equal); b.c0.add_assign(&Fq::one()); @@ -270,76 +356,60 @@ fn test_fq2_basics() { #[test] fn test_fq2_squaring() { - use super::fq::FqRepr; - use ff::PrimeField; - - let mut a = Fq2 { + let a = Fq2 { c0: Fq::one(), c1: Fq::one(), }; // u + 1 - a.square(); assert_eq!( - a, + a.square(), Fq2 { c0: Fq::zero(), - c1: Fq::from_repr(FqRepr::from(2)).unwrap(), + c1: Fq::from(2), } ); // 2u - let mut a = Fq2 { + let a = Fq2 { c0: Fq::zero(), c1: Fq::one(), }; // u - a.square(); - assert_eq!(a, { - let mut neg1 = Fq::one(); - neg1.negate(); + assert_eq!(a.square(), { Fq2 { - c0: neg1, + c0: Fq::one().neg(), c1: Fq::zero(), } }); // -1 - let mut a = Fq2 { + let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x9c2c6309bbf8b598, - 0x4eef5c946536f602, - 0x90e34aab6fb6a6bd, - 0xf7f295a94e58ae7c, - 0x41b76dcc1c3fbe5e, - 0x7080c5fa1d8e042, + 0x07, 0x08, 0x0c, 0x5f, 0xa1, 0xd8, 0xe0, 0x42, 0x41, 0xb7, 0x6d, 0xcc, 0x1c, 0x3f, + 0xbe, 0x5e, 0xf7, 0xf2, 0x95, 0xa9, 0x4e, 0x58, 0xae, 0x7c, 0x90, 0xe3, 0x4a, 0xab, + 0x6f, 0xb6, 0xa6, 0xbd, 0x4e, 0xef, 0x5c, 0x94, 0x65, 0x36, 0xf6, 0x02, 0x9c, 0x2c, + 0x63, 0x09, 0xbb, 0xf8, 0xb5, 0x98, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x38f473b3c870a4ab, - 0x6ad3291177c8c7e5, - 0xdac5a4c911a4353e, - 0xbfb99020604137a0, - 0xfc58a7b7be815407, - 0x10d1615e75250a21, + 0x10, 0xd1, 0x61, 0x5e, 0x75, 0x25, 0x0a, 0x21, 0xfc, 0x58, 0xa7, 0xb7, 0xbe, 0x81, + 0x54, 0x07, 0xbf, 0xb9, 0x90, 0x20, 0x60, 0x41, 0x37, 0xa0, 0xda, 0xc5, 0xa4, 0xc9, + 0x11, 0xa4, 0x35, 0x3e, 0x6a, 0xd3, 0x29, 0x11, 0x77, 0xc8, 0xc7, 0xe5, 0x38, 0xf4, + 0x73, 0xb3, 0xc8, 0x70, 0xa4, 0xab, ])) .unwrap(), }; - a.square(); assert_eq!( - a, + a.square(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0xf262c28c538bcf68, - 0xb9f2a66eae1073ba, - 0xdc46ab8fad67ae0, - 0xcb674157618da176, - 0x4cf17b5893c3d327, - 0x7eac81369c43361 + 0x07, 0xea, 0xc8, 0x13, 0x69, 0xc4, 0x33, 0x61, 0x4c, 0xf1, 0x7b, 0x58, 0x93, 0xc3, + 0xd3, 0x27, 0xcb, 0x67, 0x41, 0x57, 0x61, 0x8d, 0xa1, 0x76, 0x0d, 0xc4, 0x6a, 0xb8, + 0xfa, 0xd6, 0x7a, 0xe0, 0xb9, 0xf2, 0xa6, 0x6e, 0xae, 0x10, 0x73, 0xba, 0xf2, 0x62, + 0xc2, 0x8c, 0x53, 0x8b, 0xcf, 0x68, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xc1579cf58e980cf8, - 0xa23eb7e12dd54d98, - 0xe75138bce4cec7aa, - 0x38d0d7275a9689e1, - 0x739c983042779a65, - 0x1542a61c8a8db994 + 0x15, 0x42, 0xa6, 0x1c, 0x8a, 0x8d, 0xb9, 0x94, 0x73, 0x9c, 0x98, 0x30, 0x42, 0x77, + 0x9a, 0x65, 0x38, 0xd0, 0xd7, 0x27, 0x5a, 0x96, 0x89, 0xe1, 0xe7, 0x51, 0x38, 0xbc, + 0xe4, 0xce, 0xc7, 0xaa, 0xa2, 0x3e, 0xb7, 0xe1, 0x2d, 0xd5, 0x4d, 0x98, 0xc1, 0x57, + 0x9c, 0xf5, 0x8e, 0x98, 0x0c, 0xf8, ])) .unwrap(), } @@ -353,41 +423,33 @@ fn test_fq2_mul() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x85c9f989e1461f03, - 0xa2e33c333449a1d6, - 0x41e461154a7354a3, - 0x9ee53e7e84d7532e, - 0x1c202d8ed97afb45, - 0x51d3f9253e2516f, + 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, + 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, + 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, + 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xa7348a8b511aedcf, - 0x143c215d8176b319, - 0x4cc48081c09b8903, - 0x9533e4a9a5158be, - 0x7a5e1ecb676d65f9, - 0x180c3ee46656b008, + 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, + 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, + 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, + 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, ])) .unwrap(), }; a.mul_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0xe21f9169805f537e, - 0xfc87e62e179c285d, - 0x27ece175be07a531, - 0xcd460f9f0c23e430, - 0x6c9110292bfa409, - 0x2c93a72eb8af83e, + 0x02, 0xc9, 0x3a, 0x72, 0xeb, 0x8a, 0xf8, 0x3e, 0x06, 0xc9, 0x11, 0x02, 0x92, 0xbf, + 0xa4, 0x09, 0xcd, 0x46, 0x0f, 0x9f, 0x0c, 0x23, 0xe4, 0x30, 0x27, 0xec, 0xe1, 0x75, + 0xbe, 0x07, 0xa5, 0x31, 0xfc, 0x87, 0xe6, 0x2e, 0x17, 0x9c, 0x28, 0x5d, 0xe2, 0x1f, + 0x91, 0x69, 0x80, 0x5f, 0x53, 0x7e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x4b1c3f936d8992d4, - 0x1d2a72916dba4c8a, - 0x8871c508658d1e5f, - 0x57a06d3135a752ae, - 0x634cd3c6c565096d, - 0x19e17334d4e93558, + 0x19, 0xe1, 0x73, 0x34, 0xd4, 0xe9, 0x35, 0x58, 0x63, 0x4c, 0xd3, 0xc6, 0xc5, 0x65, + 0x09, 0x6d, 0x57, 0xa0, 0x6d, 0x31, 0x35, 0xa7, 0x52, 0xae, 0x88, 0x71, 0xc5, 0x08, + 0x65, 0x8d, 0x1e, 0x5f, 0x1d, 0x2a, 0x72, 0x91, 0x6d, 0xba, 0x4c, 0x8a, 0x4b, 0x1c, + 0x3f, 0x93, 0x6d, 0x89, 0x92, 0xd4, ])) .unwrap(), }); @@ -395,21 +457,17 @@ fn test_fq2_mul() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x95b5127e6360c7e4, - 0xde29c31a19a6937e, - 0xf61a96dacf5a39bc, - 0x5511fe4d84ee5f78, - 0x5310a202d92f9963, - 0x1751afbe166e5399 + 0x17, 0x51, 0xaf, 0xbe, 0x16, 0x6e, 0x53, 0x99, 0x53, 0x10, 0xa2, 0x02, 0xd9, 0x2f, + 0x99, 0x63, 0x55, 0x11, 0xfe, 0x4d, 0x84, 0xee, 0x5f, 0x78, 0xf6, 0x1a, 0x96, 0xda, + 0xcf, 0x5a, 0x39, 0xbc, 0xde, 0x29, 0xc3, 0x1a, 0x19, 0xa6, 0x93, 0x7e, 0x95, 0xb5, + 0x12, 0x7e, 0x63, 0x60, 0xc7, 0xe4, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x84af0e1bd630117a, - 0x6c63cd4da2c2aa7, - 0x5ba6e5430e883d40, - 0xc975106579c275ee, - 0x33a9ac82ce4c5083, - 0x1ef1a36c201589d + 0x01, 0xef, 0x1a, 0x36, 0xc2, 0x01, 0x58, 0x9d, 0x33, 0xa9, 0xac, 0x82, 0xce, 0x4c, + 0x50, 0x83, 0xc9, 0x75, 0x10, 0x65, 0x79, 0xc2, 0x75, 0xee, 0x5b, 0xa6, 0xe5, 0x43, + 0x0e, 0x88, 0x3d, 0x40, 0x06, 0xc6, 0x3c, 0xd4, 0xda, 0x2c, 0x2a, 0xa7, 0x84, 0xaf, + 0x0e, 0x1b, 0xd6, 0x30, 0x11, 0x7a, ])) .unwrap(), } @@ -417,52 +475,44 @@ fn test_fq2_mul() { } #[test] -fn test_fq2_inverse() { +fn test_fq2_invert() { use super::fq::FqRepr; use ff::PrimeField; - assert!(Fq2::zero().inverse().is_none()); + assert!(bool::from(Fq2::zero().invert().is_none())); let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x85c9f989e1461f03, - 0xa2e33c333449a1d6, - 0x41e461154a7354a3, - 0x9ee53e7e84d7532e, - 0x1c202d8ed97afb45, - 0x51d3f9253e2516f, + 0x05, 0x1d, 0x3f, 0x92, 0x53, 0xe2, 0x51, 0x6f, 0x1c, 0x20, 0x2d, 0x8e, 0xd9, 0x7a, + 0xfb, 0x45, 0x9e, 0xe5, 0x3e, 0x7e, 0x84, 0xd7, 0x53, 0x2e, 0x41, 0xe4, 0x61, 0x15, + 0x4a, 0x73, 0x54, 0xa3, 0xa2, 0xe3, 0x3c, 0x33, 0x34, 0x49, 0xa1, 0xd6, 0x85, 0xc9, + 0xf9, 0x89, 0xe1, 0x46, 0x1f, 0x03, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xa7348a8b511aedcf, - 0x143c215d8176b319, - 0x4cc48081c09b8903, - 0x9533e4a9a5158be, - 0x7a5e1ecb676d65f9, - 0x180c3ee46656b008, + 0x18, 0x0c, 0x3e, 0xe4, 0x66, 0x56, 0xb0, 0x08, 0x7a, 0x5e, 0x1e, 0xcb, 0x67, 0x6d, + 0x65, 0xf9, 0x09, 0x53, 0x3e, 0x4a, 0x9a, 0x51, 0x58, 0xbe, 0x4c, 0xc4, 0x80, 0x81, + 0xc0, 0x9b, 0x89, 0x03, 0x14, 0x3c, 0x21, 0x5d, 0x81, 0x76, 0xb3, 0x19, 0xa7, 0x34, + 0x8a, 0x8b, 0x51, 0x1a, 0xed, 0xcf, ])) .unwrap(), }; - let a = a.inverse().unwrap(); + let a = a.invert().unwrap(); assert_eq!( a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x70300f9bcb9e594, - 0xe5ecda5fdafddbb2, - 0x64bef617d2915a8f, - 0xdfba703293941c30, - 0xa6c3d8f9586f2636, - 0x1351ef01941b70c4 + 0x13, 0x51, 0xef, 0x01, 0x94, 0x1b, 0x70, 0xc4, 0xa6, 0xc3, 0xd8, 0xf9, 0x58, 0x6f, + 0x26, 0x36, 0xdf, 0xba, 0x70, 0x32, 0x93, 0x94, 0x1c, 0x30, 0x64, 0xbe, 0xf6, 0x17, + 0xd2, 0x91, 0x5a, 0x8f, 0xe5, 0xec, 0xda, 0x5f, 0xda, 0xfd, 0xdb, 0xb2, 0x07, 0x03, + 0x00, 0xf9, 0xbc, 0xb9, 0xe5, 0x94, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x8c39fd76a8312cb4, - 0x15d7b6b95defbff0, - 0x947143f89faedee9, - 0xcbf651a0f367afb2, - 0xdf4e54f0d3ef15a6, - 0x103bdf241afb0019 + 0x10, 0x3b, 0xdf, 0x24, 0x1a, 0xfb, 0x00, 0x19, 0xdf, 0x4e, 0x54, 0xf0, 0xd3, 0xef, + 0x15, 0xa6, 0xcb, 0xf6, 0x51, 0xa0, 0xf3, 0x67, 0xaf, 0xb2, 0x94, 0x71, 0x43, 0xf8, + 0x9f, 0xae, 0xde, 0xe9, 0x15, 0xd7, 0xb6, 0xb9, 0x5d, 0xef, 0xbf, 0xf0, 0x8c, 0x39, + 0xfd, 0x76, 0xa8, 0x31, 0x2c, 0xb4, ])) .unwrap(), } @@ -476,41 +526,33 @@ fn test_fq2_addition() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; a.add_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0x619a02d78dc70ef2, - 0xb93adfc9119e33e8, - 0x4bf0b99a9f0dca12, - 0x3b88899a42a6318f, - 0x986a4a62fa82a49d, - 0x13ce433fa26027f5, + 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, + 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, + 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, + 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x66323bf80b58b9b9, - 0xa1379b6facf6e596, - 0x402aef1fb797e32f, - 0x2236f55246d0d44d, - 0x4c8c1800eb104566, - 0x11d6e20e986c2085, + 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, + 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, + 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, + 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, ])) .unwrap(), }); @@ -518,21 +560,17 @@ fn test_fq2_addition() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8e9a7adaf6eb0eb9, - 0xcb207e6b3341eaba, - 0xd70b0c7b481d23ff, - 0xf4ef57d604b6bca2, - 0x65309427b3d5d090, - 0x14c715d5553f01d2 + 0x14, 0xc7, 0x15, 0xd5, 0x55, 0x3f, 0x01, 0xd2, 0x65, 0x30, 0x94, 0x27, 0xb3, 0xd5, + 0xd0, 0x90, 0xf4, 0xef, 0x57, 0xd6, 0x04, 0xb6, 0xbc, 0xa2, 0xd7, 0x0b, 0x0c, 0x7b, + 0x48, 0x1d, 0x23, 0xff, 0xcb, 0x20, 0x7e, 0x6b, 0x33, 0x41, 0xea, 0xba, 0x8e, 0x9a, + 0x7a, 0xda, 0xf6, 0xeb, 0x0e, 0xb9, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xfdb032e7d9079a94, - 0x35a2809d15468d83, - 0xfe4b23317e0796d5, - 0xd62fa51334f560fa, - 0x9ad265eb46e01984, - 0x1303f3465112c8bc + 0x13, 0x03, 0xf3, 0x46, 0x51, 0x12, 0xc8, 0xbc, 0x9a, 0xd2, 0x65, 0xeb, 0x46, 0xe0, + 0x19, 0x84, 0xd6, 0x2f, 0xa5, 0x13, 0x34, 0xf5, 0x60, 0xfa, 0xfe, 0x4b, 0x23, 0x31, + 0x7e, 0x07, 0x96, 0xd5, 0x35, 0xa2, 0x80, 0x9d, 0x15, 0x46, 0x8d, 0x83, 0xfd, 0xb0, + 0x32, 0xe7, 0xd9, 0x07, 0x9a, 0x94, ])) .unwrap(), } @@ -546,41 +584,33 @@ fn test_fq2_subtraction() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; a.sub_assign(&Fq2 { c0: Fq::from_repr(FqRepr([ - 0x619a02d78dc70ef2, - 0xb93adfc9119e33e8, - 0x4bf0b99a9f0dca12, - 0x3b88899a42a6318f, - 0x986a4a62fa82a49d, - 0x13ce433fa26027f5, + 0x13, 0xce, 0x43, 0x3f, 0xa2, 0x60, 0x27, 0xf5, 0x98, 0x6a, 0x4a, 0x62, 0xfa, 0x82, + 0xa4, 0x9d, 0x3b, 0x88, 0x89, 0x9a, 0x42, 0xa6, 0x31, 0x8f, 0x4b, 0xf0, 0xb9, 0x9a, + 0x9f, 0x0d, 0xca, 0x12, 0xb9, 0x3a, 0xdf, 0xc9, 0x11, 0x9e, 0x33, 0xe8, 0x61, 0x9a, + 0x02, 0xd7, 0x8d, 0xc7, 0x0e, 0xf2, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x66323bf80b58b9b9, - 0xa1379b6facf6e596, - 0x402aef1fb797e32f, - 0x2236f55246d0d44d, - 0x4c8c1800eb104566, - 0x11d6e20e986c2085, + 0x11, 0xd6, 0xe2, 0x0e, 0x98, 0x6c, 0x20, 0x85, 0x4c, 0x8c, 0x18, 0x00, 0xeb, 0x10, + 0x45, 0x66, 0x22, 0x36, 0xf5, 0x52, 0x46, 0xd0, 0xd4, 0x4d, 0x40, 0x2a, 0xef, 0x1f, + 0xb7, 0x97, 0xe3, 0x2f, 0xa1, 0x37, 0x9b, 0x6f, 0xac, 0xf6, 0xe5, 0x96, 0x66, 0x32, + 0x3b, 0xf8, 0x0b, 0x58, 0xb9, 0xb9, ])) .unwrap(), }); @@ -588,21 +618,17 @@ fn test_fq2_subtraction() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8565752bdb5c9b80, - 0x7756bed7c15982e9, - 0xa65a6be700b285fe, - 0xe255902672ef6c43, - 0x7f77a718021c342d, - 0x72ba14049fe9881 + 0x07, 0x2b, 0xa1, 0x40, 0x49, 0xfe, 0x98, 0x81, 0x7f, 0x77, 0xa7, 0x18, 0x02, 0x1c, + 0x34, 0x2d, 0xe2, 0x55, 0x90, 0x26, 0x72, 0xef, 0x6c, 0x43, 0xa6, 0x5a, 0x6b, 0xe7, + 0x00, 0xb2, 0x85, 0xfe, 0x77, 0x56, 0xbe, 0xd7, 0xc1, 0x59, 0x82, 0xe9, 0x85, 0x65, + 0x75, 0x2b, 0xdb, 0x5c, 0x9b, 0x80, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xeb4abaf7c255d1cd, - 0x11df49bc6cacc256, - 0xe52617930588c69a, - 0xf63905f39ad8cb1f, - 0x4cd5dd9fb40b3b8f, - 0x957411359ba6e4c + 0x09, 0x57, 0x41, 0x13, 0x59, 0xba, 0x6e, 0x4c, 0x4c, 0xd5, 0xdd, 0x9f, 0xb4, 0x0b, + 0x3b, 0x8f, 0xf6, 0x39, 0x05, 0xf3, 0x9a, 0xd8, 0xcb, 0x1f, 0xe5, 0x26, 0x17, 0x93, + 0x05, 0x88, 0xc6, 0x9a, 0x11, 0xdf, 0x49, 0xbc, 0x6c, 0xac, 0xc2, 0x56, 0xeb, 0x4a, + 0xba, 0xf7, 0xc2, 0x55, 0xd1, 0xcd, ])) .unwrap(), } @@ -614,46 +640,38 @@ fn test_fq2_negation() { use super::fq::FqRepr; use ff::PrimeField; - let mut a = Fq2 { + let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), - }; - a.negate(); + } + .neg(); assert_eq!( a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x8cfe87fc96dbaae4, - 0xcc6615c8fb0492d, - 0xdc167fc04da19c37, - 0xab107d49317487ab, - 0x7e555df189f880e3, - 0x19083f5486a10cbd + 0x19, 0x08, 0x3f, 0x54, 0x86, 0xa1, 0x0c, 0xbd, 0x7e, 0x55, 0x5d, 0xf1, 0x89, 0xf8, + 0x80, 0xe3, 0xab, 0x10, 0x7d, 0x49, 0x31, 0x74, 0x87, 0xab, 0xdc, 0x16, 0x7f, 0xc0, + 0x4d, 0xa1, 0x9c, 0x37, 0x0c, 0xc6, 0x61, 0x5c, 0x8f, 0xb0, 0x49, 0x2d, 0x8c, 0xfe, + 0x87, 0xfc, 0x96, 0xdb, 0xaa, 0xe4, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x228109103250c9d0, - 0x8a411ad149045812, - 0xa9109e8f3041427e, - 0xb07e9bc405608611, - 0xfcd559cbe77bd8b8, - 0x18d400b280d93e62 + 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, + 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, + 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, + 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, ])) .unwrap(), } @@ -665,46 +683,37 @@ fn test_fq2_doubling() { use super::fq::FqRepr; use ff::PrimeField; - let mut a = Fq2 { + let a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; - a.double(); assert_eq!( - a, + a.double(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0x5a00f006d247ff8e, - 0x23cb3d4443476da4, - 0x1634a5c1521eb3da, - 0x72cd9c7784211627, - 0x998c938972a657e7, - 0x1f1a52b65bdb3b9 + 0x01, 0xf1, 0xa5, 0x2b, 0x65, 0xbd, 0xb3, 0xb9, 0x99, 0x8c, 0x93, 0x89, 0x72, 0xa6, + 0x57, 0xe7, 0x72, 0xcd, 0x9c, 0x77, 0x84, 0x21, 0x16, 0x27, 0x16, 0x34, 0xa5, 0xc1, + 0x52, 0x1e, 0xb3, 0xda, 0x23, 0xcb, 0x3d, 0x44, 0x43, 0x47, 0x6d, 0xa4, 0x5a, 0x00, + 0xf0, 0x06, 0xd2, 0x47, 0xff, 0x8e, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x2efbeddf9b5dc1b6, - 0x28d5ca5ad09f4fdb, - 0x7c4068238cdf674b, - 0x67f15f81dc49195b, - 0x9c8c9bd4b79fa83d, - 0x25a226f714d506e + 0x02, 0x5a, 0x22, 0x6f, 0x71, 0x4d, 0x50, 0x6e, 0x9c, 0x8c, 0x9b, 0xd4, 0xb7, 0x9f, + 0xa8, 0x3d, 0x67, 0xf1, 0x5f, 0x81, 0xdc, 0x49, 0x19, 0x5b, 0x7c, 0x40, 0x68, 0x23, + 0x8c, 0xdf, 0x67, 0x4b, 0x28, 0xd5, 0xca, 0x5a, 0xd0, 0x9f, 0x4f, 0xdb, 0x2e, 0xfb, + 0xed, 0xdf, 0x9b, 0x5d, 0xc1, 0xb6, ])) .unwrap(), } @@ -718,21 +727,17 @@ fn test_fq2_frobenius_map() { let mut a = Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc, + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837, + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), }; @@ -741,21 +746,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -765,21 +766,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x228109103250c9d0, - 0x8a411ad149045812, - 0xa9109e8f3041427e, - 0xb07e9bc405608611, - 0xfcd559cbe77bd8b8, - 0x18d400b280d93e62 + 0x18, 0xd4, 0x00, 0xb2, 0x80, 0xd9, 0x3e, 0x62, 0xfc, 0xd5, 0x59, 0xcb, 0xe7, 0x7b, + 0xd8, 0xb8, 0xb0, 0x7e, 0x9b, 0xc4, 0x05, 0x60, 0x86, 0x11, 0xa9, 0x10, 0x9e, 0x8f, + 0x30, 0x41, 0x42, 0x7e, 0x8a, 0x41, 0x1a, 0xd1, 0x49, 0x04, 0x58, 0x12, 0x22, 0x81, + 0x09, 0x10, 0x32, 0x50, 0xc9, 0xd0, ])) .unwrap(), } @@ -789,21 +786,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -813,21 +806,17 @@ fn test_fq2_frobenius_map() { a, Fq2 { c0: Fq::from_repr(FqRepr([ - 0x2d0078036923ffc7, - 0x11e59ea221a3b6d2, - 0x8b1a52e0a90f59ed, - 0xb966ce3bc2108b13, - 0xccc649c4b9532bf3, - 0xf8d295b2ded9dc + 0x00, 0xf8, 0xd2, 0x95, 0xb2, 0xde, 0xd9, 0xdc, 0xcc, 0xc6, 0x49, 0xc4, 0xb9, 0x53, + 0x2b, 0xf3, 0xb9, 0x66, 0xce, 0x3b, 0xc2, 0x10, 0x8b, 0x13, 0x8b, 0x1a, 0x52, 0xe0, + 0xa9, 0x0f, 0x59, 0xed, 0x11, 0xe5, 0x9e, 0xa2, 0x21, 0xa3, 0xb6, 0xd2, 0x2d, 0x00, + 0x78, 0x03, 0x69, 0x23, 0xff, 0xc7, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0x977df6efcdaee0db, - 0x946ae52d684fa7ed, - 0xbe203411c66fb3a5, - 0xb3f8afc0ee248cad, - 0x4e464dea5bcfd41e, - 0x12d1137b8a6a837 + 0x01, 0x2d, 0x11, 0x37, 0xb8, 0xa6, 0xa8, 0x37, 0x4e, 0x46, 0x4d, 0xea, 0x5b, 0xcf, + 0xd4, 0x1e, 0xb3, 0xf8, 0xaf, 0xc0, 0xee, 0x24, 0x8c, 0xad, 0xbe, 0x20, 0x34, 0x11, + 0xc6, 0x6f, 0xb3, 0xa5, 0x94, 0x6a, 0xe5, 0x2d, 0x68, 0x4f, 0xa7, 0xed, 0x97, 0x7d, + 0xf6, 0xef, 0xcd, 0xae, 0xe0, 0xdb, ])) .unwrap(), } @@ -842,21 +831,17 @@ fn test_fq2_sqrt() { assert_eq!( Fq2 { c0: Fq::from_repr(FqRepr([ - 0x476b4c309720e227, - 0x34c2d04faffdab6, - 0xa57e6fc1bab51fd9, - 0xdb4a116b5bf74aa1, - 0x1e58b2159dfe10e2, - 0x7ca7da1f13606ac + 0x07, 0xca, 0x7d, 0xa1, 0xf1, 0x36, 0x06, 0xac, 0x1e, 0x58, 0xb2, 0x15, 0x9d, 0xfe, + 0x10, 0xe2, 0xdb, 0x4a, 0x11, 0x6b, 0x5b, 0xf7, 0x4a, 0xa1, 0xa5, 0x7e, 0x6f, 0xc1, + 0xba, 0xb5, 0x1f, 0xd9, 0x03, 0x4c, 0x2d, 0x04, 0xfa, 0xff, 0xda, 0xb6, 0x47, 0x6b, + 0x4c, 0x30, 0x97, 0x20, 0xe2, 0x27, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xfa8de88b7516d2c3, - 0x371a75ed14f41629, - 0x4cec2dca577a3eb6, - 0x212611bca4e99121, - 0x8ee5394d77afb3d, - 0xec92336650e49d5 + 0x0e, 0xc9, 0x23, 0x36, 0x65, 0x0e, 0x49, 0xd5, 0x08, 0xee, 0x53, 0x94, 0xd7, 0x7a, + 0xfb, 0x3d, 0x21, 0x26, 0x11, 0xbc, 0xa4, 0xe9, 0x91, 0x21, 0x4c, 0xec, 0x2d, 0xca, + 0x57, 0x7a, 0x3e, 0xb6, 0x37, 0x1a, 0x75, 0xed, 0x14, 0xf4, 0x16, 0x29, 0xfa, 0x8d, + 0xe8, 0x8b, 0x75, 0x16, 0xd2, 0xc3, ])) .unwrap(), } @@ -864,21 +849,17 @@ fn test_fq2_sqrt() { .unwrap(), Fq2 { c0: Fq::from_repr(FqRepr([ - 0x40b299b2704258c5, - 0x6ef7de92e8c68b63, - 0x6d2ddbe552203e82, - 0x8d7f1f723d02c1d3, - 0x881b3e01b611c070, - 0x10f6963bbad2ebc5 + 0x10, 0xf6, 0x96, 0x3b, 0xba, 0xd2, 0xeb, 0xc5, 0x88, 0x1b, 0x3e, 0x01, 0xb6, 0x11, + 0xc0, 0x70, 0x8d, 0x7f, 0x1f, 0x72, 0x3d, 0x02, 0xc1, 0xd3, 0x6d, 0x2d, 0xdb, 0xe5, + 0x52, 0x20, 0x3e, 0x82, 0x6e, 0xf7, 0xde, 0x92, 0xe8, 0xc6, 0x8b, 0x63, 0x40, 0xb2, + 0x99, 0xb2, 0x70, 0x42, 0x58, 0xc5, ])) .unwrap(), c1: Fq::from_repr(FqRepr([ - 0xc099534fc209e752, - 0x7670594665676447, - 0x28a20faed211efe7, - 0x6b852aeaf2afcb1b, - 0xa4c93b08105d71a9, - 0x8d7cfff94216330 + 0x08, 0xd7, 0xcf, 0xff, 0x94, 0x21, 0x63, 0x30, 0xa4, 0xc9, 0x3b, 0x08, 0x10, 0x5d, + 0x71, 0xa9, 0x6b, 0x85, 0x2a, 0xea, 0xf2, 0xaf, 0xcb, 0x1b, 0x28, 0xa2, 0x0f, 0xae, + 0xd2, 0x11, 0xef, 0xe7, 0x76, 0x70, 0x59, 0x46, 0x65, 0x67, 0x64, 0x47, 0xc0, 0x99, + 0x53, 0x4f, 0xc2, 0x09, 0xe7, 0x52, ])) .unwrap(), } @@ -887,12 +868,10 @@ fn test_fq2_sqrt() { assert_eq!( Fq2 { c0: Fq::from_repr(FqRepr([ - 0xb9f78429d1517a6b, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, + 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, + 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xf7, + 0x84, 0x29, 0xd1, 0x51, 0x7a, 0x6b, ])) .unwrap(), c1: Fq::zero(), @@ -902,31 +881,16 @@ fn test_fq2_sqrt() { Fq2 { c0: Fq::zero(), c1: Fq::from_repr(FqRepr([ - 0xb9fefffffd4357a3, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, + 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, + 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, + 0xff, 0xff, 0xfd, 0x43, 0x57, 0xa3, ])) .unwrap(), } ); } -#[test] -fn test_fq2_legendre() { - use ff::LegendreSymbol::*; - - assert_eq!(Zero, Fq2::zero().legendre()); - // i^2 = -1 - let mut m1 = Fq2::one(); - m1.negate(); - assert_eq!(QuadraticResidue, m1.legendre()); - m1.mul_by_nonresidue(); - assert_eq!(QuadraticNonResidue, m1.legendre()); -} - #[cfg(test)] use rand_core::SeedableRng; #[cfg(test)] @@ -956,9 +920,6 @@ fn test_fq2_mul_nonresidue() { #[test] fn fq2_field_tests() { - use ff::PrimeField; - - ::tests::field::random_field_tests::(); - ::tests::field::random_sqrt_tests::(); - ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); } diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs index b85c95d..b0183df 100644 --- a/pairing/src/bls12_381/fq6.rs +++ b/pairing/src/bls12_381/fq6.rs @@ -2,9 +2,11 @@ use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; use super::fq2::Fq2; use ff::Field; use rand_core::RngCore; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq6 { pub c0: Fq2, pub c1: Fq2, @@ -12,7 +14,7 @@ pub struct Fq6 { } impl ::std::fmt::Display for Fq6 { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) } } @@ -97,62 +99,8 @@ impl Fq6 { self.c1 = t2; self.c2 = t3; } -} -impl Field for Fq6 { - fn random(rng: &mut R) -> Self { - Fq6 { - c0: Fq2::random(rng), - c1: Fq2::random(rng), - c2: Fq2::random(rng), - } - } - - fn zero() -> Self { - Fq6 { - c0: Fq2::zero(), - c1: Fq2::zero(), - c2: Fq2::zero(), - } - } - - fn one() -> Self { - Fq6 { - c0: Fq2::one(), - c1: Fq2::zero(), - c2: Fq2::zero(), - } - } - - fn is_zero(&self) -> bool { - self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() - } - - fn double(&mut self) { - self.c0.double(); - self.c1.double(); - self.c2.double(); - } - - fn negate(&mut self) { - self.c0.negate(); - self.c1.negate(); - self.c2.negate(); - } - - fn add_assign(&mut self, other: &Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - self.c2.add_assign(&other.c2); - } - - fn sub_assign(&mut self, other: &Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - self.c2.sub_assign(&other.c2); - } - - fn frobenius_map(&mut self, power: usize) { + pub fn frobenius_map(&mut self, power: usize) { self.c0.frobenius_map(power); self.c1.frobenius_map(power); self.c2.frobenius_map(power); @@ -160,40 +108,117 @@ impl Field for Fq6 { self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); } +} - fn square(&mut self) { - let mut s0 = self.c0; - s0.square(); - let mut ab = self.c0; - ab.mul_assign(&self.c1); - let mut s1 = ab; - s1.double(); - let mut s2 = self.c0; - s2.sub_assign(&self.c1); - s2.add_assign(&self.c2); - s2.square(); - let mut bc = self.c1; - bc.mul_assign(&self.c2); - let mut s3 = bc; - s3.double(); - let mut s4 = self.c2; - s4.square(); - - self.c0 = s3; - self.c0.mul_by_nonresidue(); - self.c0.add_assign(&s0); - - self.c1 = s4; - self.c1.mul_by_nonresidue(); - self.c1.add_assign(&s1); - - self.c2 = s1; - self.c2.add_assign(&s2); - self.c2.add_assign(&s3); - self.c2.sub_assign(&s0); - self.c2.sub_assign(&s4); +impl ConditionallySelectable for Fq6 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fq6 { + c0: Fq2::conditional_select(&a.c0, &b.c0, choice), + c1: Fq2::conditional_select(&a.c1, &b.c1, choice), + c2: Fq2::conditional_select(&a.c2, &b.c2, choice), + } } +} +impl Neg for Fq6 { + type Output = Self; + + fn neg(self) -> Self { + Fq6 { + c0: self.c0.neg(), + c1: self.c1.neg(), + c2: self.c2.neg(), + } + } +} + +impl<'r> Add<&'r Fq6> for Fq6 { + type Output = Self; + + fn add(self, other: &Self) -> Self { + Fq6 { + c0: self.c0 + other.c0, + c1: self.c1 + other.c1, + c2: self.c2 + other.c2, + } + } +} + +impl Add for Fq6 { + type Output = Self; + + fn add(self, other: Self) -> Self { + self.add(&other) + } +} + +impl<'r> AddAssign<&'r Fq6> for Fq6 { + fn add_assign(&mut self, other: &'r Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + self.c2.add_assign(&other.c2); + } +} + +impl AddAssign for Fq6 { + fn add_assign(&mut self, other: Self) { + self.add_assign(&other); + } +} + +impl<'r> Sub<&'r Fq6> for Fq6 { + type Output = Self; + + fn sub(self, other: &Self) -> Self { + Fq6 { + c0: self.c0 - other.c0, + c1: self.c1 - other.c1, + c2: self.c2 - other.c2, + } + } +} + +impl Sub for Fq6 { + type Output = Self; + + fn sub(self, other: Self) -> Self { + self.sub(&other) + } +} + +impl<'r> SubAssign<&'r Fq6> for Fq6 { + fn sub_assign(&mut self, other: &'r Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + self.c2.sub_assign(&other.c2); + } +} + +impl SubAssign for Fq6 { + fn sub_assign(&mut self, other: Self) { + self.sub_assign(&other); + } +} + +impl<'r> Mul<&'r Fq6> for Fq6 { + type Output = Self; + + fn mul(self, other: &Self) -> Self { + let mut ret = self; + ret.mul_assign(other); + ret + } +} + +impl Mul for Fq6 { + type Output = Self; + + fn mul(self, other: Self) -> Self { + self.mul(&other) + } +} + +impl<'r> MulAssign<&'r Fq6> for Fq6 { fn mul_assign(&mut self, other: &Self) { let mut a_a = self.c0; let mut b_b = self.c1; @@ -244,27 +269,99 @@ impl Field for Fq6 { self.c1 = t2; self.c2 = t3; } +} - fn inverse(&self) -> Option { +impl MulAssign for Fq6 { + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); + } +} + +impl Field for Fq6 { + fn random(rng: &mut R) -> Self { + Fq6 { + c0: Fq2::random(rng), + c1: Fq2::random(rng), + c2: Fq2::random(rng), + } + } + + fn zero() -> Self { + Fq6 { + c0: Fq2::zero(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn one() -> Self { + Fq6 { + c0: Fq2::one(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() + } + + fn double(&self) -> Self { + Fq6 { + c0: self.c0.double(), + c1: self.c1.double(), + c2: self.c2.double(), + } + } + + fn square(&self) -> Self { + let s0 = self.c0.square(); + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let s1 = ab.double(); + let mut s2 = self.c0; + s2.sub_assign(&self.c1); + s2.add_assign(&self.c2); + s2 = s2.square(); + let mut bc = self.c1; + bc.mul_assign(&self.c2); + let s3 = bc.double(); + let s4 = self.c2.square(); + + let mut c0 = s3; + c0.mul_by_nonresidue(); + c0.add_assign(&s0); + + let mut c1 = s4; + c1.mul_by_nonresidue(); + c1.add_assign(&s1); + + let mut c2 = s1; + c2.add_assign(&s2); + c2.add_assign(&s3); + c2.sub_assign(&s0); + c2.sub_assign(&s4); + + Fq6 { c0, c1, c2 } + } + + fn invert(&self) -> CtOption { let mut c0 = self.c2; c0.mul_by_nonresidue(); c0.mul_assign(&self.c1); - c0.negate(); + c0 = c0.neg(); { - let mut c0s = self.c0; - c0s.square(); + let c0s = self.c0.square(); c0.add_assign(&c0s); } - let mut c1 = self.c2; - c1.square(); + let mut c1 = self.c2.square(); c1.mul_by_nonresidue(); { let mut c01 = self.c0; c01.mul_assign(&self.c1); c1.sub_assign(&c01); } - let mut c2 = self.c1; - c2.square(); + let mut c2 = self.c1.square(); { let mut c02 = self.c0; c02.mul_assign(&self.c2); @@ -281,21 +378,22 @@ impl Field for Fq6 { tmp2.mul_assign(&c0); tmp1.add_assign(&tmp2); - match tmp1.inverse() { - Some(t) => { - let mut tmp = Fq6 { - c0: t, - c1: t, - c2: t, - }; - tmp.c0.mul_assign(&c0); - tmp.c1.mul_assign(&c1); - tmp.c2.mul_assign(&c2); + tmp1.invert().map(|t| { + let mut tmp = Fq6 { + c0: t, + c1: t, + c2: t, + }; + tmp.c0.mul_assign(&c0); + tmp.c1.mul_assign(&c1); + tmp.c2.mul_assign(&c2); - Some(tmp) - } - None => None, - } + tmp + }) + } + + fn sqrt(&self) -> CtOption { + unimplemented!() } } @@ -376,8 +474,5 @@ fn test_fq6_mul_by_01() { #[test] fn fq6_field_tests() { - use ff::PrimeField; - - ::tests::field::random_field_tests::(); - ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); + crate::tests::field::random_field_tests::(); } diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 018e67a..9bab427 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -1,406 +1,39 @@ -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; +use ff::{Field, PrimeField}; +use std::ops::{AddAssign, MulAssign, SubAssign}; #[derive(PrimeField)] #[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] #[PrimeFieldGenerator = "7"] -pub struct Fr(FrRepr); +#[PrimeFieldReprEndianness = "little"] +pub struct Fr([u64; 4]); #[cfg(test)] use rand_core::SeedableRng; #[cfg(test)] use rand_xorshift::XorShiftRng; - -#[test] -fn test_fr_repr_ordering() { - fn assert_equality(a: FrRepr, b: FrRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); - } - - fn assert_lt(a: FrRepr, b: FrRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FrRepr([9999, 9999, 9999, 9999]), - FrRepr([9999, 9999, 9999, 9999]), - ); - assert_equality( - FrRepr([9999, 9998, 9999, 9999]), - FrRepr([9999, 9998, 9999, 9999]), - ); - assert_equality( - FrRepr([9999, 9999, 9999, 9997]), - FrRepr([9999, 9999, 9999, 9997]), - ); - assert_lt( - FrRepr([9999, 9997, 9999, 9998]), - FrRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FrRepr([9999, 9997, 9998, 9999]), - FrRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FrRepr([9, 9999, 9999, 9997]), - FrRepr([9999, 9999, 9999, 9997]), - ); -} - -#[test] -fn test_fr_repr_from() { - assert_eq!(FrRepr::from(100), FrRepr([100, 0, 0, 0])); -} - -#[test] -fn test_fr_repr_is_odd() { - assert!(!FrRepr::from(0).is_odd()); - assert!(FrRepr::from(0).is_even()); - assert!(FrRepr::from(1).is_odd()); - assert!(!FrRepr::from(1).is_even()); - assert!(!FrRepr::from(324834872).is_odd()); - assert!(FrRepr::from(324834872).is_even()); - assert!(FrRepr::from(324834873).is_odd()); - assert!(!FrRepr::from(324834873).is_even()); -} - -#[test] -fn test_fr_repr_is_zero() { - assert!(FrRepr::from(0).is_zero()); - assert!(!FrRepr::from(1).is_zero()); - assert!(!FrRepr([0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fr_repr_div2() { - let mut a = FrRepr([ - 0xbd2920b19c972321, - 0x174ed0466a3be37e, - 0xd468d5e3b551f0b5, - 0xcb67c072733beefc, - ]); - a.div2(); - assert_eq!( - a, - FrRepr([ - 0x5e949058ce4b9190, - 0x8ba76823351df1bf, - 0x6a346af1daa8f85a, - 0x65b3e039399df77e - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FrRepr([ - 0x6fd7a524163392e4, - 0x16a2e9da08cd477c, - 0xdf9a8d1abc76aa3e, - 0x196cf80e4e677d - ]) - ); - for _ in 0..200 { - a.div2(); - } - assert_eq!(a, FrRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); - for _ in 0..40 { - a.div2(); - } - assert_eq!(a, FrRepr([0x19, 0x0, 0x0, 0x0])); - for _ in 0..4 { - a.div2(); - } - assert_eq!(a, FrRepr([0x1, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fr_repr_shr() { - let mut a = FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, - ]); - a.shr(0); - assert_eq!( - a, - FrRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1 - ]) - ); - a.shr(1); - assert_eq!( - a, - FrRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0 - ]) - ); - a.shr(50); - assert_eq!( - a, - FrRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x75611bce1b4335e, - 0x6c0 - ]) - ); - a.shr(130); - assert_eq!(a, FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); - a.shr(64); - assert_eq!(a, FrRepr([0x1b0, 0x0, 0x0, 0x0])); -} - -#[test] -fn test_fr_repr_mul2() { - let mut a = FrRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FrRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); - for _ in 0..128 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FrRepr([0x0, 0x0, 0x0, 0x9600000000000000])); - for _ in 0..7 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fr_repr_num_bits() { - let mut a = FrRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FrRepr::from(1); - for i in 1..257 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fr_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FrRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ]); - t.sub_noborrow(&FrRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ])); - assert!( - t == FrRepr([ - 0xb813415048991c1f, - 0x10ad07ae88725d92, - 0x5a7b851271759961, - 0x36850eedd30c39c5 - ]) - ); - - for _ in 0..1000 { - let mut a = Fr::random(&mut rng).into_repr(); - a.0[3] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } - - // Subtracting r+1 from r should produce -1 (mod 2**256) - let mut qplusone = FrRepr([ - 0xffffffff00000001, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ]); - qplusone.sub_noborrow(&FrRepr([ - 0xffffffff00000002, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, - ])); - assert_eq!( - qplusone, - FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ); -} - -#[test] -fn test_fr_legendre() { - use ff::LegendreSymbol::*; - use ff::SqrtField; - - assert_eq!(QuadraticResidue, Fr::one().legendre()); - assert_eq!(Zero, Fr::zero().legendre()); - - let e = FrRepr([ - 0x0dbc5349cd5664da, - 0x8ac5b6296e3ae29d, - 0x127cb819feceaa3b, - 0x3a6b21fb03867191, - ]); - assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); - let e = FrRepr([ - 0x96341aefd047c045, - 0x9b5f4254500a4d65, - 0x1ee08223b68ac240, - 0x31d9cd545c0ec7c6, - ]); - assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); -} - -#[test] -fn test_fr_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FrRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ]); - t.add_nocarry(&FrRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ])); - assert_eq!( - t, - FrRepr([ - 0x64b20e805c30a967, - 0x59a9ee9aa114a02, - 0x5871a104789020c9, - 0x8999707c5c726f65 - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fr::random(&mut rng).into_repr(); - let mut b = Fr::random(&mut rng).into_repr(); - let mut c = Fr::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[3] >>= 3; - b.0[3] >>= 3; - c.0[3] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } - - // Adding 1 to (2^256 - 1) should produce zero - let mut x = FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - x.add_nocarry(&FrRepr::from(1)); - assert!(x.is_zero()); -} +#[cfg(test)] +use std::ops::Neg; #[test] fn test_fr_is_valid() { - let mut a = Fr(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FrRepr::from(1)); + a.sub_noborrow(&Fr([1, 0, 0, 0])); assert!(a.is_valid()); - assert!(Fr(FrRepr::from(0)).is_valid()); - assert!(Fr(FrRepr([ + assert!(Fr::from(0).is_valid()); + assert!(Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 - ])) + ]) .is_valid()); - assert!(!Fr(FrRepr([ + assert!(!Fr([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -418,85 +51,85 @@ fn test_fr_is_valid() { fn test_fr_add_assign() { { // Random number - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca, - ])); + ]); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fr(FrRepr::from(0))); + tmp.add_assign(&Fr([0, 0, 0, 0])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fr(FrRepr::from(1))); + tmp.add_assign(&Fr([1, 0, 0, 0])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580766, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fr(FrRepr([ + tmp.add_assign(&Fr([ 0x946f435944f7dc79, 0xb55e7ee6533a9b9b, 0x1e43b84c2f6194ca, 0x58717ab525463496, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xd7ec2abbb24fe3de, 0x35cdf7ae7d0d62f7, 0xd899557c477cd0e9, 0x3371b52bc43de018 - ])) + ]) ); // Add one to (r - 1) and test for the result. - tmp = Fr(FrRepr([ + tmp = Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48, - ])); - tmp.add_assign(&Fr(FrRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fr([1, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is r - 1 - tmp = Fr(FrRepr([ + tmp = Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ])); - tmp.add_assign(&Fr(FrRepr([ + ]); + tmp.add_assign(&Fr([ 0x521a525223349e70, 0xa99bb5f3d8231f31, 0xde8e397bebe477e, 0x1ad08e5041d7c321, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fr(FrRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fr([1, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -530,71 +163,71 @@ fn test_fr_add_assign() { fn test_fr_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c, - ])); - tmp.sub_assign(&Fr(FrRepr([ + ]); + tmp.sub_assign(&Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0xbc83189d92a7f89c, 0x7f908737d62d38a3, 0x45aa62cfe7e4c3e1, 0x24ffc5896108547d - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fr(FrRepr([ + tmp = Fr([ 0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27, - ])); - tmp.sub_assign(&Fr(FrRepr([ + ]); + tmp.sub_assign(&Fr([ 0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c, - ]))); + ])); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); // Test for sensible results with zero - tmp = Fr(FrRepr::from(0)); - tmp.sub_assign(&Fr(FrRepr::from(0))); + tmp = Fr::from(0); + tmp.sub_assign(&Fr::from(0)); assert!(tmp.is_zero()); - tmp = Fr(FrRepr([ + tmp = Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca, - ])); - tmp.sub_assign(&Fr(FrRepr::from(0))); + ]); + tmp.sub_assign(&Fr::from(0)); assert_eq!( tmp, - Fr(FrRepr([ + Fr([ 0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca - ])) + ]) ); } @@ -621,25 +254,25 @@ fn test_fr_sub_assign() { #[test] fn test_fr_mul_assign() { - let mut tmp = Fr(FrRepr([ + let mut tmp = Fr([ 0x6b7e9b8faeefc81a, 0xe30a8463f348ba42, 0xeff3cb67a8279c9c, 0x3d303651bd7c774d, - ])); - tmp.mul_assign(&Fr(FrRepr([ + ]); + tmp.mul_assign(&Fr([ 0x13ae28e3bc35ebeb, 0xa10f4488075cae2c, 0x8160e95a853c3b5d, 0x5ae3f03b561a841d, - ]))); + ])); assert!( - tmp == Fr(FrRepr([ + tmp == Fr([ 0x23717213ce710f71, 0xdbee1fe53a16e1af, 0xf565d3e1c2a48000, 0x4426507ee75df9d7 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -690,21 +323,19 @@ fn test_fr_mul_assign() { #[test] fn test_fr_squaring() { - let mut a = Fr(FrRepr([ + let a = Fr([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x73eda753299d7d47, - ])); + ]); assert!(a.is_valid()); - a.square(); assert_eq!( - a, + a.square(), Fr::from_repr(FrRepr([ - 0xc0d698e7bde077b8, - 0xb79a310579e76ec2, - 0xac1da8d0a9af4e5f, - 0x13f629c49bf23e97 + 0xb8, 0x77, 0xe0, 0xbd, 0xe7, 0x98, 0xd6, 0xc0, 0xc2, 0x6e, 0xe7, 0x79, 0x05, 0x31, + 0x9a, 0xb7, 0x5f, 0x4e, 0xaf, 0xa9, 0xd0, 0xa8, 0x1d, 0xac, 0x97, 0x3e, 0xf2, 0x9b, + 0xc4, 0x29, 0xf6, 0x13, ])) .unwrap() ); @@ -717,20 +348,13 @@ fn test_fr_squaring() { for _ in 0..1000000 { // Ensure that (a * a) = a^2 let a = Fr::random(&mut rng); - - let mut tmp = a; - tmp.square(); - - let mut tmp2 = a; - tmp2.mul_assign(&a); - - assert_eq!(tmp, tmp2); + assert_eq!(a.square(), a * a); } } #[test] -fn test_fr_inverse() { - assert!(Fr::zero().inverse().is_none()); +fn test_fr_invert() { + assert!(bool::from(Fr::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -742,7 +366,7 @@ fn test_fr_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fr::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -757,19 +381,15 @@ fn test_fr_double() { for _ in 0..1000 { // Ensure doubling a is equivalent to adding a to itself. - let mut a = Fr::random(&mut rng); - let mut b = a; - b.add_assign(&a); - a.double(); - assert_eq!(a, b); + let a = Fr::random(&mut rng); + assert_eq!(a.double(), a + a); } } #[test] -fn test_fr_negate() { +fn test_fr_neg() { { - let mut a = Fr::zero(); - a.negate(); + let a = Fr::zero().neg(); assert!(a.is_zero()); } @@ -782,8 +402,7 @@ fn test_fr_negate() { for _ in 0..1000 { // Ensure (a - (-a)) = 0. let mut a = Fr::random(&mut rng); - let mut b = a; - b.negate(); + let b = a.neg(); a.add_assign(&b); assert!(a.is_zero()); @@ -797,11 +416,11 @@ fn test_fr_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fr::random(&mut rng); - let target = a.pow(&[i]); + let target = a.pow_vartime(&[i]); let mut c = Fr::one(); for _ in 0..i { c.mul_assign(&a); @@ -809,18 +428,20 @@ fn test_fr_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(Fr::char().as_ref(), &mut char_limbs); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fr::random(&mut rng); - assert_eq!(a, a.pow(Fr::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } #[test] fn test_fr_sqrt() { - use ff::SqrtField; - let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -831,10 +452,8 @@ fn test_fr_sqrt() { for _ in 0..1000 { // Ensure sqrt(a^2) = a or -a let a = Fr::random(&mut rng); - let mut nega = a; - nega.negate(); - let mut b = a; - b.square(); + let nega = a.neg(); + let b = a.square(); let b = b.sqrt().unwrap(); @@ -845,54 +464,49 @@ fn test_fr_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fr::random(&mut rng); - if let Some(mut tmp) = a.sqrt() { - tmp.square(); - - assert_eq!(a, tmp); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } #[test] -fn test_fr_from_into_repr() { +fn test_fr_from_to_repr() { // r + 1 should not be in the field assert!(Fr::from_repr(FrRepr([ - 0xffffffff00000002, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48 + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, + 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, + 0xed, 0x73, ])) - .is_err()); + .is_none()); // r should not be in the field - assert!(Fr::from_repr(Fr::char()).is_err()); + assert!(Fr::from_repr(Fr::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. let a = FrRepr([ - 0x25ebe3a3ad3c0c6a, - 0x6990e39d092e817c, - 0x941f900d42f5658e, - 0x44f8a103b38a71e0, + 0x6a, 0x0c, 0x3c, 0xad, 0xa3, 0xe3, 0xeb, 0x25, 0x7c, 0x81, 0x2e, 0x09, 0x9d, 0xe3, 0x90, + 0x69, 0x8e, 0x65, 0xf5, 0x42, 0x0d, 0x90, 0x1f, 0x94, 0xe0, 0x71, 0x8a, 0xb3, 0x03, 0xa1, + 0xf8, 0x44, ]); let mut a_fr = Fr::from_repr(a).unwrap(); let b = FrRepr([ - 0x264e9454885e2475, - 0x46f7746bb0308370, - 0x4683ef5347411f9, - 0x58838d7f208d4492, + 0x75, 0x24, 0x5e, 0x88, 0x54, 0x94, 0x4e, 0x26, 0x70, 0x83, 0x30, 0xb0, 0x6b, 0x74, 0xf7, + 0x46, 0xf9, 0x11, 0x74, 0x34, 0xf5, 0x3e, 0x68, 0x04, 0x92, 0x44, 0x8d, 0x20, 0x7f, 0x8d, + 0x83, 0x58, ]); let b_fr = Fr::from_repr(b).unwrap(); let c = FrRepr([ - 0x48a09ab93cfc740d, - 0x3a6600fbfc7a671, - 0x838567017501d767, - 0x7161d6da77745512, + 0x0d, 0x74, 0xfc, 0x3c, 0xb9, 0x9a, 0xa0, 0x48, 0x71, 0xa6, 0xc7, 0xbf, 0x0f, 0x60, 0xa6, + 0x03, 0x67, 0xd7, 0x01, 0x75, 0x01, 0x67, 0x85, 0x83, 0x12, 0x55, 0x74, 0x77, 0xda, 0xd6, + 0x61, 0x71, ]); a_fr.mul_assign(&b_fr); - assert_eq!(a_fr.into_repr(), c); + assert_eq!(a_fr.to_repr(), c); // Zero should be in the field. - assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); + assert!(Fr::from_repr(FrRepr([0; 32])).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -902,7 +516,7 @@ fn test_fr_from_into_repr() { for _ in 0..1000 { // Try to turn Fr elements into representations and back again, and compare. let a = Fr::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FrRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fr::from_repr(a_repr).unwrap(); @@ -911,60 +525,15 @@ fn test_fr_from_into_repr() { } } -#[test] -fn test_fr_repr_display() { - assert_eq!( - format!( - "{}", - FrRepr([ - 0x2829c242fa826143, - 0x1f32cf4dd4330917, - 0x932e4e479d168cd9, - 0x513c77587f563f64 - ]) - ), - "0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string() - ); - assert_eq!( - format!( - "{}", - FrRepr([ - 0x25ebe3a3ad3c0c6a, - 0x6990e39d092e817c, - 0x941f900d42f5658e, - 0x44f8a103b38a71e0 - ]) - ), - "0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string() - ); - assert_eq!( - format!( - "{}", - FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FrRepr([0, 0, 0, 0])), - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - #[test] fn test_fr_display() { assert_eq!( format!( "{}", Fr::from_repr(FrRepr([ - 0xc3cae746a3b5ecc7, - 0x185ec8eb3f5b5aee, - 0x684499ffe4b9dd99, - 0x7c9bba7afb68faa + 0xc7, 0xec, 0xb5, 0xa3, 0x46, 0xe7, 0xca, 0xc3, 0xee, 0x5a, 0x5b, 0x3f, 0xeb, 0xc8, + 0x5e, 0x18, 0x99, 0xdd, 0xb9, 0xe4, 0xff, 0x99, 0x44, 0x68, 0xaa, 0x8f, 0xb6, 0xaf, + 0xa7, 0xbb, 0xc9, 0x07, ])) .unwrap() ), @@ -974,10 +543,9 @@ fn test_fr_display() { format!( "{}", Fr::from_repr(FrRepr([ - 0x44c71298ff198106, - 0xb0ad10817df79b6a, - 0xd034a80a2b74132b, - 0x41cf9a1336f50719 + 0x06, 0x81, 0x19, 0xff, 0x98, 0x12, 0xc7, 0x44, 0x6a, 0x9b, 0xf7, 0x7d, 0x81, 0x10, + 0xad, 0xb0, 0x2b, 0x13, 0x74, 0x2b, 0x0a, 0xa8, 0x34, 0xd0, 0x19, 0x07, 0xf5, 0x36, + 0x13, 0x9a, 0xcf, 0x41, ])) .unwrap() ), @@ -985,6 +553,18 @@ fn test_fr_display() { ); } +#[test] +fn test_fr_is_odd() { + assert!(!Fr::from(0).is_odd()); + assert!(Fr::from(0).is_even()); + assert!(Fr::from(1).is_odd()); + assert!(!Fr::from(1).is_even()); + assert!(!Fr::from(324834872).is_odd()); + assert!(Fr::from(324834872).is_even()); + assert!(Fr::from(324834873).is_odd()); + assert!(!Fr::from(324834873).is_even()); +} + #[test] fn test_fr_num_bits() { assert_eq!(Fr::NUM_BITS, 255); @@ -993,35 +573,29 @@ fn test_fr_num_bits() { #[test] fn test_fr_root_of_unity() { - use ff::SqrtField; - assert_eq!(Fr::S, 32); + assert_eq!(Fr::multiplicative_generator(), Fr::from(7)); assert_eq!( - Fr::multiplicative_generator(), - Fr::from_repr(FrRepr::from(7)).unwrap() - ); - assert_eq!( - Fr::multiplicative_generator().pow([ - 0xfffe5bfeffffffff, + Fr::multiplicative_generator().pow_vartime([ + 0xfffe5bfeffffffffu64, 0x9a1d80553bda402, 0x299d7d483339d808, 0x73eda753 ]), Fr::root_of_unity() ); - assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one()); - assert!(Fr::multiplicative_generator().sqrt().is_none()); + assert_eq!(Fr::root_of_unity().pow_vartime([1u64 << Fr::S]), Fr::one()); + assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none())); } #[test] fn fr_field_tests() { - ::tests::field::random_field_tests::(); - ::tests::field::random_sqrt_tests::(); - ::tests::field::random_frobenius_tests::(Fr::char(), 13); - ::tests::field::from_str_tests::(); + crate::tests::field::random_field_tests::(); + crate::tests::field::random_sqrt_tests::(); + crate::tests::field::from_str_tests::(); } #[test] fn fr_repr_tests() { - ::tests::repr::random_repr_tests::(); + crate::tests::repr::random_repr_tests::(); } diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index deb6ffc..afe9c82 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -1,3 +1,6 @@ +//! An implementation of the BLS12-381 pairing-friendly elliptic curve +//! construction. + mod ec; mod fq; mod fq12; @@ -22,6 +25,8 @@ use super::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; // The BLS parameter x for BLS12-381 is -0xd201000000010000 const BLS_X: u64 = 0xd201000000010000; @@ -77,7 +82,7 @@ impl Engine for Bls12 { let mut f = Fq12::one(); let mut found_one = false; - for i in BitIterator::new(&[BLS_X >> 1]) { + for i in BitIterator::::new(&[BLS_X >> 1]) { if !found_one { found_one = i; continue; @@ -93,7 +98,7 @@ impl Engine for Bls12 { } } - f.square(); + f = f.square(); } for &mut (p, ref mut coeffs) in &mut pairs { @@ -107,62 +112,58 @@ impl Engine for Bls12 { f } - fn final_exponentiation(r: &Fq12) -> Option { + fn final_exponentiation(r: &Fq12) -> CtOption { let mut f1 = *r; f1.conjugate(); - match r.inverse() { - Some(mut f2) => { - let mut r = f1; - r.mul_assign(&f2); - f2 = r; - r.frobenius_map(2); - r.mul_assign(&f2); + r.invert().map(|mut f2| { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); - fn exp_by_x(f: &mut Fq12, x: u64) { - *f = f.pow(&[x]); - if BLS_X_IS_NEGATIVE { - f.conjugate(); - } + fn exp_by_x(f: &mut Fq12, x: u64) { + *f = f.pow_vartime(&[x]); + if BLS_X_IS_NEGATIVE { + f.conjugate(); } - - let mut x = BLS_X; - let mut y0 = r; - y0.square(); - let mut y1 = y0; - exp_by_x(&mut y1, x); - x >>= 1; - let mut y2 = y1; - exp_by_x(&mut y2, x); - x <<= 1; - let mut y3 = r; - y3.conjugate(); - y1.mul_assign(&y3); - y1.conjugate(); - y1.mul_assign(&y2); - y2 = y1; - exp_by_x(&mut y2, x); - y3 = y2; - exp_by_x(&mut y3, x); - y1.conjugate(); - y3.mul_assign(&y1); - y1.conjugate(); - y1.frobenius_map(3); - y2.frobenius_map(2); - y1.mul_assign(&y2); - y2 = y3; - exp_by_x(&mut y2, x); - y2.mul_assign(&y0); - y2.mul_assign(&r); - y1.mul_assign(&y2); - y2 = y3; - y2.frobenius_map(1); - y1.mul_assign(&y2); - - Some(y1) } - None => None, - } + + let mut x = BLS_X; + let y0 = r.square(); + let mut y1 = y0; + exp_by_x(&mut y1, x); + x >>= 1; + let mut y2 = y1; + exp_by_x(&mut y2, x); + x <<= 1; + let mut y3 = r; + y3.conjugate(); + y1.mul_assign(&y3); + y1.conjugate(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.conjugate(); + y3.mul_assign(&y1); + y1.conjugate(); + y1.frobenius_map(3); + y2.frobenius_map(2); + y1.mul_assign(&y2); + y2 = y3; + exp_by_x(&mut y2, x); + y2.mul_assign(&y0); + y2.mul_assign(&r); + y1.mul_assign(&y2); + y2 = y3; + y2.frobenius_map(1); + y1.mul_assign(&y2); + + y1 + }) } } @@ -181,41 +182,35 @@ impl G2Prepared { fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - let mut tmp0 = r.x; - tmp0.square(); + let mut tmp0 = r.x.square(); - let mut tmp1 = r.y; - tmp1.square(); + let mut tmp1 = r.y.square(); - let mut tmp2 = tmp1; - tmp2.square(); + let mut tmp2 = tmp1.square(); let mut tmp3 = tmp1; tmp3.add_assign(&r.x); - tmp3.square(); + tmp3 = tmp3.square(); tmp3.sub_assign(&tmp0); tmp3.sub_assign(&tmp2); - tmp3.double(); + tmp3 = tmp3.double(); - let mut tmp4 = tmp0; - tmp4.double(); + let mut tmp4 = tmp0.double(); tmp4.add_assign(&tmp0); let mut tmp6 = r.x; tmp6.add_assign(&tmp4); - let mut tmp5 = tmp4; - tmp5.square(); + let tmp5 = tmp4.square(); - let mut zsquared = r.z; - zsquared.square(); + let zsquared = r.z.square(); r.x = tmp5; r.x.sub_assign(&tmp3); r.x.sub_assign(&tmp3); r.z.add_assign(&r.y); - r.z.square(); + r.z = r.z.square(); r.z.sub_assign(&tmp1); r.z.sub_assign(&zsquared); @@ -223,47 +218,41 @@ impl G2Prepared { r.y.sub_assign(&r.x); r.y.mul_assign(&tmp4); - tmp2.double(); - tmp2.double(); - tmp2.double(); + tmp2 = tmp2.double().double().double(); r.y.sub_assign(&tmp2); tmp3 = tmp4; tmp3.mul_assign(&zsquared); - tmp3.double(); - tmp3.negate(); + tmp3 = tmp3.double().neg(); - tmp6.square(); + tmp6 = tmp6.square(); tmp6.sub_assign(&tmp0); tmp6.sub_assign(&tmp5); - tmp1.double(); - tmp1.double(); + tmp1 = tmp1.double().double(); tmp6.sub_assign(&tmp1); tmp0 = r.z; tmp0.mul_assign(&zsquared); - tmp0.double(); + tmp0 = tmp0.double(); (tmp0, tmp3, tmp6) } fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - let mut zsquared = r.z; - zsquared.square(); + let zsquared = r.z.square(); - let mut ysquared = q.y; - ysquared.square(); + let ysquared = q.y.square(); let mut t0 = zsquared; t0.mul_assign(&q.x); let mut t1 = q.y; t1.add_assign(&r.z); - t1.square(); + t1 = t1.square(); t1.sub_assign(&ysquared); t1.sub_assign(&zsquared); t1.mul_assign(&zsquared); @@ -271,12 +260,9 @@ impl G2Prepared { let mut t2 = t0; t2.sub_assign(&r.x); - let mut t3 = t2; - t3.square(); + let t3 = t2.square(); - let mut t4 = t3; - t4.double(); - t4.double(); + let t4 = t3.double().double(); let mut t5 = t4; t5.mul_assign(&t2); @@ -291,14 +277,13 @@ impl G2Prepared { let mut t7 = t4; t7.mul_assign(&r.x); - r.x = t6; - r.x.square(); + r.x = t6.square(); r.x.sub_assign(&t5); r.x.sub_assign(&t7); r.x.sub_assign(&t7); r.z.add_assign(&t2); - r.z.square(); + r.z = r.z.square(); r.z.sub_assign(&zsquared); r.z.sub_assign(&t3); @@ -311,29 +296,26 @@ impl G2Prepared { t0 = r.y; t0.mul_assign(&t5); - t0.double(); + t0 = t0.double(); r.y = t8; r.y.sub_assign(&t0); - t10.square(); + t10 = t10.square(); t10.sub_assign(&ysquared); - let mut ztsquared = r.z; - ztsquared.square(); + let ztsquared = r.z.square(); t10.sub_assign(&ztsquared); - t9.double(); + t9 = t9.double(); t9.sub_assign(&t10); - t10 = r.z; - t10.double(); + t10 = r.z.double(); - t6.negate(); + t6 = t6.neg(); - t1 = t6; - t1.double(); + t1 = t6.double(); (t10, t1, t9) } @@ -342,7 +324,7 @@ impl G2Prepared { let mut r: G2 = q.into(); let mut found_one = false; - for i in BitIterator::new([BLS_X >> 1]) { + for i in BitIterator::::new([BLS_X >> 1]) { if !found_one { found_one = i; continue; @@ -366,5 +348,5 @@ impl G2Prepared { #[test] fn bls12_engine_tests() { - ::tests::engine::engine_tests::(); + crate::tests::engine::engine_tests::(); } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index b5b75a3..e866319 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,8 +1,8 @@ -use ff::PrimeFieldRepr; +use ff::PrimeField; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use super::*; -use *; +use crate::*; #[test] fn test_pairing_result_against_relic() { @@ -151,9 +151,9 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate"); } else { panic!("should have rejected the point") @@ -162,9 +162,9 @@ fn test_g1_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate"); } else { panic!("should have rejected the point") @@ -172,10 +172,10 @@ fn test_g1_uncompressed_invalid_vectors() { } { - let m = Fq::zero().into_repr(); + let m = Fq::zero().to_repr(); let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { // :) @@ -189,15 +189,17 @@ fn test_g1_uncompressed_invalid_vectors() { let mut x = Fq::one(); loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? + + let y = x3b.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); - if let Some(y) = x3b.sqrt() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(y.to_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -265,9 +267,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c1)"); } else { panic!("should have rejected the point") @@ -276,9 +278,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c0)"); } else { panic!("should have rejected the point") @@ -287,9 +289,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[96..]).unwrap(); + o.as_mut()[96..144].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c1)"); } else { panic!("should have rejected the point") @@ -298,9 +300,9 @@ fn test_g2_uncompressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[144..]).unwrap(); + o.as_mut()[144..].copy_from_slice(m.as_ref()); - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "y coordinate (c0)"); } else { panic!("should have rejected the point") @@ -308,11 +310,11 @@ fn test_g2_uncompressed_invalid_vectors() { } { - let m = Fq::zero().into_repr(); + let m = Fq::zero().to_repr(); let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { // :) @@ -326,20 +328,22 @@ fn test_g2_uncompressed_invalid_vectors() { let mut x = Fq2::one(); loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? - if let Some(y) = x3b.sqrt() { + let y = x3b.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); + // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); - y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap(); - y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..96].copy_from_slice(x.c0.to_repr().as_ref()); + o.as_mut()[96..144].copy_from_slice(y.c1.to_repr().as_ref()); + o.as_mut()[144..].copy_from_slice(y.c0.to_repr().as_ref()); if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { break; @@ -407,10 +411,10 @@ fn test_g1_compressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate"); } else { panic!("should have rejected the point") @@ -422,15 +426,14 @@ fn test_g1_compressed_invalid_vectors() { let mut x = Fq::one(); loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { x.add_assign(&Fq::one()); } else { - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -447,14 +450,13 @@ fn test_g1_compressed_invalid_vectors() { let mut x = Fq::one(); loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + x3b.add_assign(&Fq::from(4)); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { @@ -523,10 +525,10 @@ fn test_g2_compressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c1)"); } else { panic!("should have rejected the point") @@ -535,10 +537,10 @@ fn test_g2_compressed_invalid_vectors() { { let mut o = o; - m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[48..96].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate)) = o.into_affine() { assert_eq!(coordinate, "x coordinate (c0)"); } else { panic!("should have rejected the point") @@ -553,19 +555,18 @@ fn test_g2_compressed_invalid_vectors() { }; loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { x.add_assign(&Fq2::one()); } else { - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { @@ -585,18 +586,17 @@ fn test_g2_compressed_invalid_vectors() { }; loop { - let mut x3b = x; - x3b.square(); + let mut x3b = x.square(); x3b.mul_assign(&x); x3b.add_assign(&Fq2 { - c0: Fq::from_repr(FqRepr::from(4)).unwrap(), - c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + c0: Fq::from(4), + c1: Fq::from(4), }); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. - x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); - x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[..48].copy_from_slice(x.c1.to_repr().as_ref()); + o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 952185c..645c70d 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -1,3 +1,5 @@ +//! A library for working with pairing-friendly curves. + // `clippy` is a code linting tool for improving code quality by catching // common mistakes or strange code patterns. If the `cargo-clippy` feature // is provided, all compiler warnings are prohibited. @@ -8,36 +10,29 @@ #![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] // Force public structures to implement Debug #![deny(missing_debug_implementations)] -extern crate byteorder; -extern crate ff; -extern crate group; -extern crate rand_core; - -#[cfg(test)] -extern crate rand_xorshift; - #[cfg(test)] pub mod tests; pub mod bls12_381; -use ff::{Field, PrimeField, ScalarEngine, SqrtField}; -use group::{CurveAffine, CurveProjective}; +use ff::{Field, PrimeField, ScalarEngine}; +use group::{CurveAffine, CurveOps, CurveOpsOwned, CurveProjective}; +use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are /// of prime order `r`, and are equipped with a bilinear pairing function. pub trait Engine: ScalarEngine { /// The projective representation of an element in G1. - type G1: CurveProjective< - Engine = Self, - Base = Self::Fq, - Scalar = Self::Fr, - Affine = Self::G1Affine, - > + From; + type G1: CurveProjective + + From + + CurveOps + + CurveOpsOwned; /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< @@ -50,12 +45,10 @@ pub trait Engine: ScalarEngine { > + From; /// The projective representation of an element in G2. - type G2: CurveProjective< - Engine = Self, - Base = Self::Fqe, - Scalar = Self::Fr, - Affine = Self::G2Affine, - > + From; + type G2: CurveProjective + + From + + CurveOps + + CurveOpsOwned; /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< @@ -68,10 +61,10 @@ pub trait Engine: ScalarEngine { > + From; /// The base field that hosts G1. - type Fq: PrimeField + SqrtField; + type Fq: PrimeField; /// The extension field that hosts G2. - type Fqe: SqrtField; + type Fqe: Field; /// The extension field that hosts the target group of the pairing. type Fqk: Field; @@ -87,7 +80,7 @@ pub trait Engine: ScalarEngine { >; /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(&Self::Fqk) -> Option; + fn final_exponentiation(_: &Self::Fqk) -> CtOption; /// Performs a complete pairing operation `(p, q)`. fn pairing(p: G1, q: G2) -> Self::Fqk diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index fc74f1b..e9f0570 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,8 +1,10 @@ +use ff::{Endianness, Field, PrimeField}; use group::{CurveAffine, CurveProjective}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; +use std::ops::MulAssign; -use {Engine, Field, PairingCurveAffine, PrimeField}; +use crate::{Engine, PairingCurveAffine}; pub fn engine_tests() { let mut rng = XorShiftRng::from_seed([ @@ -128,8 +130,14 @@ fn random_bilinearity_tests() { let mut cd = c; cd.mul_assign(&d); + let mut cd = cd.to_repr(); + ::ReprEndianness::toggle_little_endian(&mut cd); - let abcd = E::pairing(a, b).pow(cd.into_repr()); + use byteorder::ByteOrder; + let mut cd_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(cd.as_ref(), &mut cd_limbs); + + let abcd = E::pairing(a, b).pow_vartime(cd_limbs); assert_eq!(acbd, adbc); assert_eq!(acbd, abcd); diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index 8f3d8d9..eb2c8fe 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -1,29 +1,8 @@ -use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; -pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - for i in 0..(maxpower + 1) { - let mut a = F::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow(&characteristic); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - -pub fn random_sqrt_tests() { +pub fn random_sqrt_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -31,27 +10,22 @@ pub fn random_sqrt_tests() { for _ in 0..10000 { let a = F::random(&mut rng); - let mut b = a; - b.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + let b = a.square(); let b = b.sqrt().unwrap(); - let mut negb = b; - negb.negate(); + let negb = b.neg(); assert!(a == b || a == negb); } let mut c = F::one(); for _ in 0..10000 { - let mut b = c; - b.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + let mut b = c.square(); b = b.sqrt().unwrap(); if b != c { - b.negate(); + b = b.neg(); } assert_eq!(b, c); @@ -77,12 +51,11 @@ pub fn random_field_tests() { assert!(F::zero().is_zero()); { - let mut z = F::zero(); - z.negate(); + let z = F::zero().neg(); assert!(z.is_zero()); } - assert!(F::zero().inverse().is_none()); + assert!(bool::from(F::zero().invert().is_none())); // Multiplication by zero { @@ -125,7 +98,7 @@ pub fn from_str_tests() { let n = rng.next_u64(); let a = F::from_str(&format!("{}", n)).unwrap(); - let b = F::from_repr(n.into()).unwrap(); + let b = F::from(n); assert_eq!(a, b); } @@ -204,8 +177,7 @@ fn random_subtraction_tests(rng: &mut R) { fn random_negation_tests(rng: &mut R) { for _ in 0..10000 { let a = F::random(rng); - let mut b = a; - b.negate(); + let mut b = a.neg(); b.add_assign(&a); assert!(b.is_zero()); @@ -214,32 +186,24 @@ fn random_negation_tests(rng: &mut R) { fn random_doubling_tests(rng: &mut R) { for _ in 0..10000 { - let mut a = F::random(rng); - let mut b = a; - a.add_assign(&b); - b.double(); - - assert_eq!(a, b); + let a = F::random(rng); + assert_eq!(a + a, a.double()); } } fn random_squaring_tests(rng: &mut R) { for _ in 0..10000 { - let mut a = F::random(rng); - let mut b = a; - a.mul_assign(&b); - b.square(); - - assert_eq!(a, b); + let a = F::random(rng); + assert_eq!(a * a, a.square()); } } fn random_inversion_tests(rng: &mut R) { - assert!(F::zero().inverse().is_none()); + assert!(bool::from(F::zero().invert().is_none())); for _ in 0..10000 { let mut a = F::random(rng); - let b = a.inverse().unwrap(); // probablistically nonzero + let b = a.invert().unwrap(); // probablistically nonzero a.mul_assign(&b); assert_eq!(a, F::one()); diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs index 67badd8..bdaffaa 100644 --- a/pairing/src/tests/repr.rs +++ b/pairing/src/tests/repr.rs @@ -1,11 +1,9 @@ -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; pub fn random_repr_tests() { random_encoding_tests::

(); - random_shl_tests::

(); - random_shr_tests::

(); } fn random_encoding_tests() { @@ -15,94 +13,11 @@ fn random_encoding_tests() { ]); for _ in 0..1000 { - let r = P::random(&mut rng).into_repr(); + let r = P::random(&mut rng); - // Big endian - { - let mut rdecoded =

::Repr::default(); + let v = r.to_repr(); + let rdecoded = P::from_repr(v).unwrap(); - let mut v: Vec = vec![]; - r.write_be(&mut v).unwrap(); - rdecoded.read_be(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - // Little endian - { - let mut rdecoded =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_le(&mut v).unwrap(); - rdecoded.read_le(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - { - let mut rdecoded_le =

::Repr::default(); - let mut rdecoded_be_flip =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_le(&mut v).unwrap(); - - // This reads in little-endian, so we are done. - rdecoded_le.read_le(&v[..]).unwrap(); - - // This reads in big-endian, so we perform a swap of the - // bytes beforehand. - let v: Vec = v.into_iter().rev().collect(); - rdecoded_be_flip.read_be(&v[..]).unwrap(); - - assert_eq!(rdecoded_le, rdecoded_be_flip); - } - } -} - -fn random_shl_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); - - for shift in 0..(r.num_bits() + 1) { - let mut r1 = r; - let mut r2 = r; - - for _ in 0..shift { - r1.mul2(); - } - - r2.shl(shift); - - assert_eq!(r1, r2); - } - } -} - -fn random_shr_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); - - for shift in 0..(r.num_bits() + 1) { - let mut r1 = r; - let mut r2 = r; - - for _ in 0..shift { - r1.div2(); - } - - r2.shr(shift); - - assert_eq!(r1, r2); - } + assert_eq!(r, rdecoded); } } diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..5edffce --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +1.39.0 diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index b522e28..cebc2f0 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -1,20 +1,25 @@ [package] name = "zcash_client_backend" -version = "0.0.0" +description = "APIs for creating shielded Zcash light clients" +version = "0.2.0" authors = [ "Jack Grigg ", ] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" edition = "2018" [dependencies] bech32 = "0.7" bs58 = { version = "0.2", features = ["check"] } -ff = { path = "../ff" } +ff = { version = "0.6", path = "../ff" } hex = "0.3" -pairing = { path = "../pairing" } +pairing = { version = "0.16", path = "../pairing" } protobuf = "2" subtle = "2" -zcash_primitives = { path = "../zcash_primitives" } +zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [build-dependencies] protobuf-codegen-pure = "2" @@ -23,3 +28,6 @@ protobuf-codegen-pure = "2" rand_core = "0.5" rand_os = "0.2" rand_xorshift = "0.2" + +[badges] +maintenance = { status = "actively-developed" } diff --git a/zcash_client_backend/README.md b/zcash_client_backend/README.md index da5c9e2..af9a7ff 100644 --- a/zcash_client_backend/README.md +++ b/zcash_client_backend/README.md @@ -1,12 +1,14 @@ # zcash_client_backend -This library contains Rust structs and traits for creating shielded Zcash light clients. +This library contains Rust structs and traits for creating shielded Zcash light +clients. ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/zcash_client_backend/src/constants.rs b/zcash_client_backend/src/constants.rs index 28f0b2c..fd40bcb 100644 --- a/zcash_client_backend/src/constants.rs +++ b/zcash_client_backend/src/constants.rs @@ -1,8 +1,8 @@ //! Zcash global and per-network constants. pub mod mainnet; -pub mod testnet; pub mod regtest; +pub mod testnet; pub const SPROUT_CONSENSUS_BRANCH_ID: u32 = 0; pub const OVERWINTER_CONSENSUS_BRANCH_ID: u32 = 0x5ba8_1b19; diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_client_backend/src/constants/mainnet.rs index b004c0c..db932fa 100644 --- a/zcash_client_backend/src/constants/mainnet.rs +++ b/zcash_client_backend/src/constants/mainnet.rs @@ -1,3 +1,5 @@ +//! Constants for the Zcash main network. + /// The mainnet coin type for ZEC, as defined by [SLIP 44]. /// /// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md @@ -23,7 +25,7 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews"; /// /// Defined in section 5.6.4 of the [Zcash Protocol Specification]. /// -/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress +/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs"; diff --git a/zcash_client_backend/src/constants/regtest.rs b/zcash_client_backend/src/constants/regtest.rs index 962101f..8f6c415 100644 --- a/zcash_client_backend/src/constants/regtest.rs +++ b/zcash_client_backend/src/constants/regtest.rs @@ -1,38 +1,45 @@ -/// The testnet coin type for ZEC, as defined by [SLIP 44]. -/// -/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md +//! # Regtest constants +//! +//! `regtest` is a `zcashd`-specific environment used for local testing. They mostly reuse +//! the testnet constants. +//! These constants are defined in [the `zcashd` codebase]. +//! [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L482-L496 + +/// The regtest cointype reuses the testnet cointype pub const COIN_TYPE: u32 = 1; -/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`]. +/// The HRP for a Bech32-encoded regtest [`ExtendedSpendingKey`]. /// -/// Defined in [ZIP 32]. +/// It is defined in [the `zcashd` codebase]. /// /// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey -/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst +/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L496 pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-regtest"; -/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`]. +/// The HRP for a Bech32-encoded regtest [`ExtendedFullViewingKey`]. /// -/// Defined in [ZIP 32]. +/// It is defined in [the `zcashd` codebase]. /// /// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey -/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst +/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L494 pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling"; -/// The HRP for a Bech32-encoded testnet [`PaymentAddress`]. +/// The HRP for a Bech32-encoded regtest [`PaymentAddress`]. /// -/// Defined in section 5.6.4 of the [Zcash Protocol Specification]. +/// It is defined in [the `zcashd` codebase]. /// -/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress -/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf +/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress +/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L493 pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling"; -/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`]. +/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::PublicKey`]. +/// Same as the testnet prefix. /// /// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25]; -/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`]. +/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::Script`]. +/// Same as the testnet prefix. /// /// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba]; diff --git a/zcash_client_backend/src/constants/testnet.rs b/zcash_client_backend/src/constants/testnet.rs index 012c2d0..d39a11d 100644 --- a/zcash_client_backend/src/constants/testnet.rs +++ b/zcash_client_backend/src/constants/testnet.rs @@ -1,3 +1,5 @@ +//! Constants for the Zcash test network. + /// The testnet coin type for ZEC, as defined by [SLIP 44]. /// /// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md @@ -23,7 +25,7 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling"; /// /// Defined in section 5.6.4 of the [Zcash Protocol Specification]. /// -/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress +/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling"; diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs new file mode 100644 index 0000000..f9830f6 --- /dev/null +++ b/zcash_client_backend/src/decrypt.rs @@ -0,0 +1,76 @@ +use pairing::bls12_381::Bls12; +use zcash_primitives::{ + note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo}, + primitives::{Note, PaymentAddress}, + transaction::Transaction, + zip32::ExtendedFullViewingKey, + JUBJUB, +}; + +/// A decrypted shielded output. +pub struct DecryptedOutput { + /// The index of the output within [`shielded_outputs`]. + /// + /// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData + pub index: usize, + /// The note within the output. + pub note: Note, + /// The address the note was sent to. + pub to: PaymentAddress, + /// The memo included with the note. + pub memo: Memo, + /// True if this output was recovered using an [`OutgoingViewingKey`], meaning that + /// this is a logical output of the transaction. + /// + /// [`OutgoingViewingKey`]: zcash_primitives::keys::OutgoingViewingKey + pub outgoing: bool, +} + +/// Scans a [`Transaction`] for any information that can be decrypted by the set of +/// [`ExtendedFullViewingKey`]s. +pub fn decrypt_transaction( + tx: &Transaction, + extfvks: &[ExtendedFullViewingKey], +) -> Vec { + let mut decrypted = vec![]; + + // Cache IncomingViewingKey calculation + let vks: Vec<_> = extfvks + .iter() + .map(|extfvk| (extfvk.fvk.vk.ivk(), extfvk.fvk.ovk)) + .collect(); + + for (index, output) in tx.shielded_outputs.iter().enumerate() { + let epk = match output.ephemeral_key.as_prime_order(&JUBJUB) { + Some(p) => p, + None => continue, + }; + + for (ivk, ovk) in &vks { + let ((note, to, memo), outgoing) = + match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) { + Some(ret) => (ret, false), + None => match try_sapling_output_recovery( + ovk, + &output.cv, + &output.cmu, + &epk, + &output.enc_ciphertext, + &output.out_ciphertext, + ) { + Some(ret) => (ret, true), + None => continue, + }, + }; + decrypted.push(DecryptedOutput { + index, + note, + to, + memo, + outgoing, + }) + } + } + + decrypted +} diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 0bd1644..4903eb4 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -2,17 +2,15 @@ //! //! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`] //! module. +//! +//! [`constants`]: crate::constants use bech32::{self, Error, FromBase32, ToBase32}; -use bs58::{self, decode::DecodeError}; use pairing::bls12_381::Bls12; use std::io::{self, Write}; -use zcash_primitives::{ - jubjub::edwards, - primitives::{Diversifier, PaymentAddress}, -}; use zcash_primitives::{ legacy::TransparentAddress, + primitives::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, }; @@ -115,10 +113,11 @@ pub fn decode_extended_full_viewing_key( /// 0xbc, 0xe5, /// ]); /// -/// let pa = PaymentAddress { -/// diversifier: Diversifier([0u8; 11]), -/// pk_d: edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), -/// }; +/// let pa = PaymentAddress::from_parts( +/// Diversifier([0u8; 11]), +/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// ) +/// .unwrap(); /// /// assert_eq!( /// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa), @@ -126,10 +125,7 @@ pub fn decode_extended_full_viewing_key( /// ); /// ``` pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { - bech32_encode(hrp, |w| { - w.write_all(&addr.diversifier.0)?; - addr.pk_d.write(w) - }) + bech32_encode(hrp, |w| w.write_all(&addr.to_bytes())) } /// Decodes a [`PaymentAddress`] from a Bech32-encoded string. @@ -155,10 +151,11 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// 0xbc, 0xe5, /// ]); /// -/// let pa = PaymentAddress { -/// diversifier: Diversifier([0u8; 11]), -/// pk_d: edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), -/// }; +/// let pa = PaymentAddress::from_parts( +/// Diversifier([0u8; 11]), +/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// ) +/// .unwrap(); /// /// assert_eq!( /// decode_payment_address( @@ -170,71 +167,16 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// ``` pub fn decode_payment_address(hrp: &str, s: &str) -> Result>, Error> { bech32_decode(hrp, s, |data| { - let mut diversifier = Diversifier([0; 11]); - diversifier.0.copy_from_slice(&data[0..11]); - // Check that the diversifier is valid - if diversifier.g_d::(&JUBJUB).is_none() { + if data.len() != 43 { return None; } - edwards::Point::::read(&data[11..], &JUBJUB) - .ok()? - .as_prime_order(&JUBJUB) - .map(|pk_d| PaymentAddress { pk_d, diversifier }) + let mut bytes = [0; 43]; + bytes.copy_from_slice(&data); + PaymentAddress::::from_bytes(&bytes, &JUBJUB) }) } -/// Writes a [`TransparentAddress`] as a Base58Check-encoded string. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_backend::{ -/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX}, -/// encoding::encode_transparent_address, -/// }; -/// use zcash_primitives::legacy::TransparentAddress; -/// -/// assert_eq!( -/// encode_transparent_address( -/// &B58_PUBKEY_ADDRESS_PREFIX, -/// &B58_SCRIPT_ADDRESS_PREFIX, -/// &TransparentAddress::PublicKey([0; 20]), -/// ), -/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", -/// ); -/// -/// assert_eq!( -/// encode_transparent_address( -/// &B58_PUBKEY_ADDRESS_PREFIX, -/// &B58_SCRIPT_ADDRESS_PREFIX, -/// &TransparentAddress::Script([0; 20]), -/// ), -/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", -/// ); -/// ``` -pub fn encode_transparent_address( - pubkey_version: &[u8], - script_version: &[u8], - addr: &TransparentAddress, -) -> String { - let decoded = match addr { - TransparentAddress::PublicKey(key_id) => { - let mut decoded = vec![0; pubkey_version.len() + 20]; - decoded[..pubkey_version.len()].copy_from_slice(pubkey_version); - decoded[pubkey_version.len()..].copy_from_slice(key_id); - decoded - } - TransparentAddress::Script(script_id) => { - let mut decoded = vec![0; script_version.len() + 20]; - decoded[..script_version.len()].copy_from_slice(script_version); - decoded[script_version.len()..].copy_from_slice(script_id); - decoded - } - }; - bs58::encode(decoded).with_check().into_string() -} - /// Decodes a [`TransparentAddress`] from a Base58Check-encoded string. /// /// # Examples @@ -268,7 +210,7 @@ pub fn decode_transparent_address( pubkey_version: &[u8], script_version: &[u8], s: &str, -) -> Result, DecodeError> { +) -> Result, bs58::decode::Error> { let decoded = bs58::decode(s).with_check(None).into_vec()?; if &decoded[..pubkey_version.len()] == pubkey_version { if decoded.len() == pubkey_version.len() + 20 { @@ -300,11 +242,95 @@ mod tests { use zcash_primitives::{ jubjub::edwards, primitives::{Diversifier, PaymentAddress}, + zip32::ExtendedSpendingKey, }; - use super::{decode_payment_address, encode_payment_address}; + use super::{ + decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address, + encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address, + }; use crate::constants; + #[test] + fn extended_spending_key() { + let extsk = ExtendedSpendingKey::master(&[0; 32][..]); + + let encoded_main = "secret-extended-key-main1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs87qvlj"; + let encoded_test = "secret-extended-key-test1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsvzyw8j"; + + assert_eq!( + encode_extended_spending_key( + constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + &extsk + ), + encoded_main + ); + assert_eq!( + decode_extended_spending_key( + constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + encoded_main + ) + .unwrap(), + Some(extsk.clone()) + ); + + assert_eq!( + encode_extended_spending_key( + constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + &extsk + ), + encoded_test + ); + assert_eq!( + decode_extended_spending_key( + constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + encoded_test + ) + .unwrap(), + Some(extsk) + ); + } + + #[test] + fn extended_full_viewing_key() { + let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into(); + + let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf"; + let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz"; + + assert_eq!( + encode_extended_full_viewing_key( + constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + &extfvk + ), + encoded_main + ); + assert_eq!( + decode_extended_full_viewing_key( + constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + encoded_main + ) + .unwrap(), + Some(extfvk.clone()) + ); + + assert_eq!( + encode_extended_full_viewing_key( + constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + &extfvk + ), + encoded_test + ); + assert_eq!( + decode_extended_full_viewing_key( + constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + encoded_test + ) + .unwrap(), + Some(extfvk) + ); + } + #[test] fn payment_address() { let rng = &mut XorShiftRng::from_seed([ @@ -312,10 +338,11 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress { - diversifier: Diversifier([0u8; 11]), - pk_d: edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - }; + let addr = PaymentAddress::from_parts( + Diversifier([0u8; 11]), + edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + ) + .unwrap(); let encoded_main = "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd"; @@ -356,10 +383,11 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress { - diversifier: Diversifier([1u8; 11]), - pk_d: edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - }; + let addr = PaymentAddress::from_parts( + Diversifier([1u8; 11]), + edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), + ) + .unwrap(); let encoded_main = encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr); diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index 4d4cd6f..63dd5ee 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -5,6 +5,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; /// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the /// given seed. /// +/// # Panics +/// +/// Panics if `seed` is shorter than 32 bytes. +/// /// # Examples /// /// ``` @@ -13,6 +17,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; /// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0); /// ``` pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey { + if seed.len() < 32 { + panic!("ZIP 32 seeds MUST be at least 32 bytes"); + } + ExtendedSpendingKey::from_path( &ExtendedSpendingKey::master(&seed), &[ @@ -22,3 +30,14 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi ], ) } + +#[cfg(test)] +mod tests { + use super::spending_key; + + #[test] + #[should_panic] + fn spending_key_panics_on_short_seed() { + let _ = spending_key(&[0; 31][..], 0, 0); + } +} diff --git a/zcash_client_backend/src/lib.rs b/zcash_client_backend/src/lib.rs index 7f2af0a..e3852e6 100644 --- a/zcash_client_backend/src/lib.rs +++ b/zcash_client_backend/src/lib.rs @@ -3,9 +3,15 @@ //! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash //! light clients. +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] + pub mod constants; +mod decrypt; pub mod encoding; pub mod keys; pub mod proto; pub mod wallet; pub mod welding_rig; + +pub use decrypt::{decrypt_transaction, DecryptedOutput}; diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index 0ab1b6d..0872fbb 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -1,6 +1,6 @@ //! Generated code for handling light client protobuf structs. -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use zcash_primitives::{ block::{BlockHash, BlockHeader}, @@ -67,8 +67,8 @@ impl compact_formats::CompactOutput { /// [`CompactOutput.cmu`]: #structfield.cmu pub fn cmu(&self) -> Result { let mut repr = FrRepr::default(); - repr.read_le(&self.cmu[..]).map_err(|_| ())?; - Fr::from_repr(repr).map_err(|_| ()) + repr.as_mut().copy_from_slice(&self.cmu[..]); + Fr::from_repr(repr).ok_or(()) } /// Returns the ephemeral public key for this output. diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 4d133d4..4099e76 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -36,7 +36,7 @@ fn scan_output( let ct = output.ciphertext; // Increment tree and witnesses - let node = Node::new(cmu.into_repr()); + let node = Node::new(cmu.to_repr()); for witness in existing_witnesses { witness.append(node).unwrap(); } @@ -134,12 +134,11 @@ pub fn scan_block( // mutable references to wtxs for too long. let mut block_witnesses: Vec<_> = wtxs .iter_mut() - .map(|tx| { + .flat_map(|tx| { tx.shielded_outputs .iter_mut() .map(|output| &mut output.witness) }) - .flatten() .collect(); for to_scan in tx.outputs.into_iter().enumerate() { @@ -184,10 +183,9 @@ pub fn scan_block( #[cfg(test)] mod tests { - use ff::{Field, PrimeField, PrimeFieldRepr}; + use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr}; - use rand_core::RngCore; - use rand_os::OsRng; + use rand_core::{OsRng, RngCore}; use zcash_primitives::{ jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, merkle_tree::CommitmentTree, @@ -209,9 +207,7 @@ mod tests { }; let fake_cmu = { let fake_cmu = Fr::random(rng); - let mut bytes = vec![]; - fake_cmu.into_repr().write_le(&mut bytes).unwrap(); - bytes + fake_cmu.to_repr().as_ref().to_owned() }; let fake_epk = { let mut buffer = vec![0; 64]; @@ -254,8 +250,8 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; let note = Note { - g_d: to.diversifier.g_d::(&JUBJUB).unwrap(), - pk_d: to.pk_d.clone(), + g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + pk_d: to.pk_d().clone(), value: value.into(), r: Fs::random(&mut rng), }; @@ -266,8 +262,7 @@ mod tests { Memo::default(), &mut rng, ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); + let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned(); let mut epk = vec![]; encryptor.epk().write(&mut epk).unwrap(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml deleted file mode 100644 index 042a43b..0000000 --- a/zcash_client_sqlite/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "zcash_client_sqlite" -version = "0.0.0" -authors = [ - "Jack Grigg ", -] -edition = "2018" - -[dependencies] -bech32 = "0.7" -bs58 = { version = "0.2", features = ["check"] } -ff = { path = "../ff" } -pairing = { path = "../pairing" } -protobuf = "2" -rusqlite = { version = "0.20", features = ["bundled"] } -time = "0.1" -zcash_client_backend = { path = "../zcash_client_backend" } -zcash_primitives = { path = "../zcash_primitives" } - -[dev-dependencies] -rand_core = "0.5" -rand_os = "0.2" -tempfile = "3" -zcash_proofs = { path = "../zcash_proofs" } - -[features] -mainnet = [] diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md deleted file mode 100644 index d73e3fe..0000000 --- a/zcash_client_sqlite/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Security Disclaimer - -#### :warning: WARNING: This is an *early preview* - ----- - -In the spirit of transparency, we provide this as a window into what we are actively -developing. This is an alpha build, not yet intended for 3rd party use. Please be advised -of the following: - -* 🛑 This code currently is not audited. 🛑 -* ❌ This is a public, active branch with **no support**. -* ❌ The code **does not have** documentation that is reviewed and approved by our Documentation team. -* ❌ The code **does not have** adequate unit tests, acceptance tests and stress tests. -* ❌ The code **does not have** automated tests that use the officially supported CI system. -* ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. -* :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this. -* :heavy_check_mark: The library **is not** majorly broken in some way. -* :heavy_check_mark: The library **does run** on mainnet and testnet. -* ❌ We **are actively rebasing** this branch and adding features where/when needed. -* ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.). -* :heavy_check_mark: There is a product manager for this library. -* :heavy_check_mark: Electric Coin Company maintains the library as we discover bugs and do network upgrades/minor releases. -* :heavy_check_mark: Users can expect to get a response within a few weeks after submitting an issue. -* ❌ The User Support team **has not yet been briefed** on the features provided to users and the functionality of the associated test-framework. -* ❌ The code is **not fully-documented**. - - -### 🛑 Use of this code may lead to a loss of funds 🛑 - -Use of this code in its current form or with modifications may lead to loss of funds, loss -of "expected" privacy, or denial of service for a large portion of users, or a bug which -could leverage any of those kinds of attacks (especially a "0 day" where we suspect few -people know about the vulnerability). - -### :eyes: At this time, this is for preview purposes only. :eyes: - ----- - -# zcash_client_sqlite - -This library contains APIs that collectively implement a Zcash light client in -an SQLite database. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. - diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs deleted file mode 100644 index f72512f..0000000 --- a/zcash_client_sqlite/src/address.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Structs for handling supported address types. - -use pairing::bls12_381::Bls12; -use zcash_client_backend::encoding::{ - decode_payment_address, decode_transparent_address, encode_payment_address, - encode_transparent_address, -}; -use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress}; - -#[cfg(feature = "mainnet")] -use zcash_client_backend::constants::mainnet::{ - B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, -}; - -#[cfg(not(feature = "mainnet"))] -use zcash_client_backend::constants::testnet::{ - B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, -}; - -/// An address that funds can be sent to. -pub enum RecipientAddress { - Shielded(PaymentAddress), - Transparent(TransparentAddress), -} - -impl From> for RecipientAddress { - fn from(addr: PaymentAddress) -> Self { - RecipientAddress::Shielded(addr) - } -} - -impl From for RecipientAddress { - fn from(addr: TransparentAddress) -> Self { - RecipientAddress::Transparent(addr) - } -} - -impl RecipientAddress { - pub fn from_str(s: &str) -> Option { - if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { - Some(pa.into()) - } else if let Ok(Some(addr)) = - decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s) - { - Some(addr.into()) - } else { - None - } - } - - pub fn to_string(&self) -> String { - match self { - RecipientAddress::Shielded(pa) => { - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa) - } - RecipientAddress::Transparent(addr) => encode_transparent_address( - &B58_PUBKEY_ADDRESS_PREFIX, - &B58_SCRIPT_ADDRESS_PREFIX, - addr, - ), - } - } -} diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs deleted file mode 100644 index faa1219..0000000 --- a/zcash_client_sqlite/src/chain.rs +++ /dev/null @@ -1,469 +0,0 @@ -//! Functions for enforcing chain validity and handling chain reorgs. -//! -//! # Examples -//! -//! ``` -//! use zcash_client_sqlite::{ -//! chain::{rewind_to_height, validate_combined_chain}, -//! error::ErrorKind, -//! scan::scan_cached_blocks, -//! }; -//! -//! let db_cache = "/path/to/cache.db"; -//! let db_data = "/path/to/data.db"; -//! -//! // 1) Download new CompactBlocks into db_cache. -//! -//! // 2) Run the chain validator on the received blocks. -//! // -//! // Given that we assume the server always gives us correct-at-the-time blocks, any -//! // errors are in the blocks we have previously cached or scanned. -//! if let Err(e) = validate_combined_chain(&db_cache, &db_data) { -//! match e.kind() { -//! ErrorKind::InvalidChain(upper_bound, _) => { -//! // a) Pick a height to rewind to. -//! // -//! // This might be informed by some external chain reorg information, or -//! // heuristics such as the platform, available bandwidth, size of recent -//! // CompactBlocks, etc. -//! let rewind_height = upper_bound - 10; -//! -//! // b) Rewind scanned block information. -//! rewind_to_height(&db_data, rewind_height); -//! -//! // c) Delete cached blocks from rewind_height onwards. -//! // -//! // This does imply that assumed-valid blocks will be re-downloaded, but it -//! // is also possible that in the intervening time, a chain reorg has -//! // occurred that orphaned some of those blocks. -//! -//! // d) If there is some separate thread or service downloading -//! // CompactBlocks, tell it to go back and download from rewind_height -//! // onwards. -//! } -//! _ => { -//! // Handle other errors. -//! } -//! } -//! } -//! -//! // 3) Scan (any remaining) cached blocks. -//! // -//! // At this point, the cache and scanned data are locally consistent (though not -//! // necessarily consistent with the latest chain tip - this would be discovered the -//! // next time this codepath is executed after new blocks are received). -//! scan_cached_blocks(&db_cache, &db_data); -//! ``` - -use protobuf::parse_from_bytes; -use rusqlite::{Connection, NO_PARAMS}; -use std::path::Path; -use zcash_client_backend::proto::compact_formats::CompactBlock; - -use crate::{ - error::{Error, ErrorKind}, - SAPLING_ACTIVATION_HEIGHT, -}; - -#[derive(Debug)] -pub enum ChainInvalidCause { - PrevHashMismatch, -} - -struct CompactBlockRow { - height: i32, - data: Vec, -} - -/// Checks that the scanned blocks in the data database, when combined with the recent -/// `CompactBlock`s in the cache database, form a valid chain. -/// -/// This function is built on the core assumption that the information provided in the -/// cache database is more likely to be accurate than the previously-scanned information. -/// This follows from the design (and trust) assumption that the `lightwalletd` server -/// provides accurate block information as of the time it was requested. -/// -/// Returns: -/// - `Ok(())` if the combined chain is valid. -/// - `Err(ErrorKind::InvalidChain(upper_bound, cause))` if the combined chain is invalid. -/// `upper_bound` is the height of the highest invalid block (on the assumption that the -/// highest block in the cache database is correct). -/// - `Err(e)` if there was an error during validation unrelated to chain validity. -/// -/// This function does not mutate either of the databases. -pub fn validate_combined_chain, Q: AsRef>( - db_cache: P, - db_data: Q, -) -> Result<(), Error> { - let cache = Connection::open(db_cache)?; - let data = Connection::open(db_data)?; - - // Recall where we synced up to previously. - // If we have never synced, use Sapling activation height to select all cached CompactBlocks. - let (have_scanned, last_scanned_height) = - data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0) - .map(|h| (true, h)) - .or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1))) - })?; - - // Fetch the CompactBlocks we need to validate - let mut stmt_blocks = cache - .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height DESC")?; - let mut rows = stmt_blocks.query_map(&[last_scanned_height], |row| { - Ok(CompactBlockRow { - height: row.get(0)?, - data: row.get(1)?, - }) - })?; - - // Take the highest cached block as accurate. - let (mut last_height, mut last_prev_hash) = { - let assumed_correct = match rows.next() { - Some(row) => row?, - None => { - // No cached blocks, and we've already validated the blocks we've scanned, - // so there's nothing to validate. - // TODO: Maybe we still want to check if there are cached blocks that are - // at heights we previously scanned? Check scanning flow again. - return Ok(()); - } - }; - let block: CompactBlock = parse_from_bytes(&assumed_correct.data)?; - (block.height as i32, block.prev_hash()) - }; - - for row in rows { - let row = row?; - - // Scanned blocks MUST be height-sequential. - if row.height != (last_height - 1) { - return Err(Error(ErrorKind::InvalidHeight(last_height - 1, row.height))); - } - last_height = row.height; - - let block: CompactBlock = parse_from_bytes(&row.data)?; - - // Cached blocks MUST be hash-chained. - if block.hash() != last_prev_hash { - return Err(Error(ErrorKind::InvalidChain( - last_height, - ChainInvalidCause::PrevHashMismatch, - ))); - } - last_prev_hash = block.prev_hash(); - } - - if have_scanned { - // Cached blocks MUST hash-chain to the last scanned block. - let last_scanned_hash = data.query_row( - "SELECT hash FROM blocks WHERE height = ?", - &[last_scanned_height], - |row| row.get::<_, Vec<_>>(0), - )?; - if &last_scanned_hash[..] != &last_prev_hash.0[..] { - return Err(Error(ErrorKind::InvalidChain( - last_scanned_height, - ChainInvalidCause::PrevHashMismatch, - ))); - } - } - - // All good! - Ok(()) -} - -/// Rewinds the data database to the given height. -/// -/// If the requested height is greater than or equal to the height of the last scanned -/// block, this function does nothing. -pub fn rewind_to_height>(db_data: P, height: i32) -> Result<(), Error> { - let data = Connection::open(db_data)?; - - // Recall where we synced up to previously. - // If we have never synced, use Sapling activation height. - let last_scanned_height = - data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) - })?; - - if height >= last_scanned_height { - // Nothing to do. - return Ok(()); - } - - // Start an SQL transaction for rewinding. - data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - - // Decrement witnesses. - data.execute("DELETE FROM sapling_witnesses WHERE block > ?", &[height])?; - - // Un-mine transactions. - data.execute( - "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", - &[height], - )?; - - // Now that they aren't depended on, delete scanned blocks. - data.execute("DELETE FROM blocks WHERE height > ?", &[height])?; - - // Commit the SQL transaction, rewinding atomically. - data.execute("COMMIT", NO_PARAMS)?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use tempfile::NamedTempFile; - use zcash_primitives::{ - block::BlockHash, - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; - - use super::{rewind_to_height, validate_combined_chain}; - use crate::{ - error::ErrorKind, - init::{init_accounts_table, init_cache_database, init_data_database}, - query::get_balance, - scan::scan_cached_blocks, - tests::{fake_compact_block, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, - }; - - #[test] - fn valid_chain_states() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Empty chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Create a fake CompactBlock sending value to the address - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - Amount::from_u64(5).unwrap(), - ); - insert_into_cache(db_cache, &cb); - - // Cache-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Create a second fake CompactBlock sending more value to the address - let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, - cb.hash(), - extfvk, - Amount::from_u64(7).unwrap(), - ); - insert_into_cache(db_cache, &cb2); - - // Data+cache chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - } - - #[test] - fn invalid_chain_cache_disconnected() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Create some fake CompactBlocks - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - Amount::from_u64(5).unwrap(), - ); - let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, - cb.hash(), - extfvk.clone(), - Amount::from_u64(7).unwrap(), - ); - insert_into_cache(db_cache, &cb); - insert_into_cache(db_cache, &cb2); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Create more fake CompactBlocks that don't connect to the scanned ones - let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, - BlockHash([1; 32]), - extfvk.clone(), - Amount::from_u64(8).unwrap(), - ); - let (cb4, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 3, - cb3.hash(), - extfvk.clone(), - Amount::from_u64(3).unwrap(), - ); - insert_into_cache(db_cache, &cb3); - insert_into_cache(db_cache, &cb4); - - // Data+cache chain should be invalid at the data/cache boundary - match validate_combined_chain(db_cache, db_data) { - Err(e) => match e.kind() { - ErrorKind::InvalidChain(upper_bound, _) => { - assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 1) - } - _ => panic!(), - }, - _ => panic!(), - } - } - - #[test] - fn invalid_chain_cache_reorg() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Create some fake CompactBlocks - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - Amount::from_u64(5).unwrap(), - ); - let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, - cb.hash(), - extfvk.clone(), - Amount::from_u64(7).unwrap(), - ); - insert_into_cache(db_cache, &cb); - insert_into_cache(db_cache, &cb2); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); - - // Create more fake CompactBlocks that contain a reorg - let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, - cb2.hash(), - extfvk.clone(), - Amount::from_u64(8).unwrap(), - ); - let (cb4, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 3, - BlockHash([1; 32]), - extfvk.clone(), - Amount::from_u64(3).unwrap(), - ); - insert_into_cache(db_cache, &cb3); - insert_into_cache(db_cache, &cb4); - - // Data+cache chain should be invalid inside the cache - match validate_combined_chain(db_cache, db_data) { - Err(e) => match e.kind() { - ErrorKind::InvalidChain(upper_bound, _) => { - assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 2) - } - _ => panic!(), - }, - _ => panic!(), - } - } - - #[test] - fn data_db_rewinding() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Account balance should be zero - assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); - - // Create fake CompactBlocks sending value to the address - let value = Amount::from_u64(5).unwrap(); - let value2 = Amount::from_u64(7).unwrap(); - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); - insert_into_cache(db_cache, &cb); - insert_into_cache(db_cache, &cb2); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Account balance should reflect both received notes - assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); - - // "Rewind" to height of last scanned block - rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT + 1).unwrap(); - - // Account balance should be unaltered - assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); - - // Rewind so that one block is dropped - rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT).unwrap(); - - // Account balance should only contain the first received note - assert_eq!(get_balance(db_data, 0).unwrap(), value); - } -} diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs deleted file mode 100644 index 12b2d98..0000000 --- a/zcash_client_sqlite/src/error.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::error; -use std::fmt; -use zcash_primitives::{ - sapling::Node, - transaction::{builder, TxId}, -}; - -#[derive(Debug)] -pub enum ErrorKind { - CorruptedData(&'static str), - IncorrectHRPExtFVK, - InsufficientBalance(u64, u64), - InvalidChain(i32, crate::chain::ChainInvalidCause), - InvalidExtSK(u32), - InvalidHeight(i32, i32), - InvalidMemo(std::str::Utf8Error), - InvalidNewWitnessAnchor(usize, TxId, i32, Node), - InvalidNote, - InvalidWitnessAnchor(i64, i32), - ScanRequired, - TableNotEmpty, - Bech32(bech32::Error), - Base58(bs58::decode::DecodeError), - Builder(builder::Error), - Database(rusqlite::Error), - Io(std::io::Error), - Protobuf(protobuf::ProtobufError), -} - -#[derive(Debug)] -pub struct Error(pub(crate) ErrorKind); - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.0 { - ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason), - ErrorKind::IncorrectHRPExtFVK => write!(f, "Incorrect HRP for extfvk"), - ErrorKind::InsufficientBalance(have, need) => write!( - f, - "Insufficient balance (have {}, need {} including fee)", - have, need - ), - ErrorKind::InvalidChain(upper_bound, cause) => { - write!(f, "Invalid chain (upper bound: {}): {:?}", upper_bound, cause) - } - ErrorKind::InvalidExtSK(account) => { - write!(f, "Incorrect ExtendedSpendingKey for account {}", account) - } - ErrorKind::InvalidHeight(expected, actual) => write!( - f, - "Expected height of next CompactBlock to be {}, but was {}", - expected, actual - ), - ErrorKind::InvalidMemo(e) => write!(f, "{}", e), - ErrorKind::InvalidNewWitnessAnchor(output, txid, last_height, anchor) => write!( - f, - "New witness for output {} in tx {} has incorrect anchor after scanning block {}: {:?}", - output, txid, last_height, anchor, - ), - ErrorKind::InvalidNote => write!(f, "Invalid note"), - ErrorKind::InvalidWitnessAnchor(id_note, last_height) => write!( - f, - "Witness for note {} has incorrect anchor after scanning block {}", - id_note, last_height - ), - ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), - ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), - ErrorKind::Bech32(e) => write!(f, "{}", e), - ErrorKind::Base58(e) => write!(f, "{}", e), - ErrorKind::Builder(e) => write!(f, "{:?}", e), - ErrorKind::Database(e) => write!(f, "{}", e), - ErrorKind::Io(e) => write!(f, "{}", e), - ErrorKind::Protobuf(e) => write!(f, "{}", e), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match &self.0 { - ErrorKind::InvalidMemo(e) => Some(e), - ErrorKind::Bech32(e) => Some(e), - ErrorKind::Builder(e) => Some(e), - ErrorKind::Database(e) => Some(e), - ErrorKind::Io(e) => Some(e), - ErrorKind::Protobuf(e) => Some(e), - _ => None, - } - } -} - -impl From for Error { - fn from(e: bech32::Error) -> Self { - Error(ErrorKind::Bech32(e)) - } -} - -impl From for Error { - fn from(e: bs58::decode::DecodeError) -> Self { - Error(ErrorKind::Base58(e)) - } -} - -impl From for Error { - fn from(e: builder::Error) -> Self { - Error(ErrorKind::Builder(e)) - } -} - -impl From for Error { - fn from(e: rusqlite::Error) -> Self { - Error(ErrorKind::Database(e)) - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error(ErrorKind::Io(e)) - } -} - -impl From for Error { - fn from(e: protobuf::ProtobufError) -> Self { - Error(ErrorKind::Protobuf(e)) - } -} - -impl Error { - pub fn kind(&self) -> &ErrorKind { - &self.0 - } -} diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs deleted file mode 100644 index e6eeaff..0000000 --- a/zcash_client_sqlite/src/init.rs +++ /dev/null @@ -1,303 +0,0 @@ -//! Functions for initializing the various databases. - -use rusqlite::{types::ToSql, Connection, NO_PARAMS}; -use std::path::Path; -use zcash_client_backend::encoding::encode_extended_full_viewing_key; -use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey}; - -use crate::{ - address_from_extfvk, - error::{Error, ErrorKind}, - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, -}; - -/// Sets up the internal structure of the cache database. -/// -/// # Examples -/// -/// ``` -/// use tempfile::NamedTempFile; -/// use zcash_client_sqlite::init::init_cache_database; -/// -/// let data_file = NamedTempFile::new().unwrap(); -/// let db_cache = data_file.path(); -/// init_cache_database(&db_cache).unwrap(); -/// ``` -pub fn init_cache_database>(db_cache: P) -> Result<(), Error> { - let cache = Connection::open(db_cache)?; - cache.execute( - "CREATE TABLE IF NOT EXISTS compactblocks ( - height INTEGER PRIMARY KEY, - data BLOB NOT NULL - )", - NO_PARAMS, - )?; - Ok(()) -} - -/// Sets up the internal structure of the data database. -/// -/// # Examples -/// -/// ``` -/// use tempfile::NamedTempFile; -/// use zcash_client_sqlite::init::init_data_database; -/// -/// let data_file = NamedTempFile::new().unwrap(); -/// let db_data = data_file.path(); -/// init_data_database(&db_data).unwrap(); -/// ``` -pub fn init_data_database>(db_data: P) -> Result<(), Error> { - let data = Connection::open(db_data)?; - data.execute( - "CREATE TABLE IF NOT EXISTS accounts ( - account INTEGER PRIMARY KEY, - extfvk TEXT NOT NULL, - address TEXT NOT NULL - )", - NO_PARAMS, - )?; - data.execute( - "CREATE TABLE IF NOT EXISTS blocks ( - height INTEGER PRIMARY KEY, - hash BLOB NOT NULL, - time INTEGER NOT NULL, - sapling_tree BLOB NOT NULL - )", - NO_PARAMS, - )?; - data.execute( - "CREATE TABLE IF NOT EXISTS transactions ( - id_tx INTEGER PRIMARY KEY, - txid BLOB NOT NULL UNIQUE, - created TEXT, - block INTEGER, - tx_index INTEGER, - expiry_height INTEGER, - raw BLOB, - FOREIGN KEY (block) REFERENCES blocks(height) - )", - NO_PARAMS, - )?; - data.execute( - "CREATE TABLE IF NOT EXISTS received_notes ( - id_note INTEGER PRIMARY KEY, - tx INTEGER NOT NULL, - output_index INTEGER NOT NULL, - account INTEGER NOT NULL, - diversifier BLOB NOT NULL, - value INTEGER NOT NULL, - rcm BLOB NOT NULL, - nf BLOB NOT NULL UNIQUE, - is_change BOOLEAN NOT NULL, - memo BLOB, - spent INTEGER, - FOREIGN KEY (tx) REFERENCES transactions(id_tx), - FOREIGN KEY (account) REFERENCES accounts(account), - FOREIGN KEY (spent) REFERENCES transactions(id_tx), - CONSTRAINT tx_output UNIQUE (tx, output_index) - )", - NO_PARAMS, - )?; - data.execute( - "CREATE TABLE IF NOT EXISTS sapling_witnesses ( - id_witness INTEGER PRIMARY KEY, - note INTEGER NOT NULL, - block INTEGER NOT NULL, - witness BLOB NOT NULL, - FOREIGN KEY (note) REFERENCES received_notes(id_note), - FOREIGN KEY (block) REFERENCES blocks(height), - CONSTRAINT witness_height UNIQUE (note, block) - )", - NO_PARAMS, - )?; - data.execute( - "CREATE TABLE IF NOT EXISTS sent_notes ( - id_note INTEGER PRIMARY KEY, - tx INTEGER NOT NULL, - output_index INTEGER NOT NULL, - from_account INTEGER NOT NULL, - address TEXT NOT NULL, - value INTEGER NOT NULL, - memo BLOB, - FOREIGN KEY (tx) REFERENCES transactions(id_tx), - FOREIGN KEY (from_account) REFERENCES accounts(account), - CONSTRAINT tx_output UNIQUE (tx, output_index) - )", - NO_PARAMS, - )?; - Ok(()) -} - -/// Initialises the data database with the given [`ExtendedFullViewingKey`]s. -/// -/// The [`ExtendedFullViewingKey`]s are stored internally and used by other APIs such as -/// [`get_address`], [`scan_cached_blocks`], and [`create_to_address`]. `extfvks` **MUST** -/// be arranged in account-order; that is, the [`ExtendedFullViewingKey`] for ZIP 32 -/// account `i` **MUST** be at `extfvks[i]`. -/// -/// # Examples -/// -/// ``` -/// use tempfile::NamedTempFile; -/// use zcash_client_sqlite::init::{init_accounts_table, init_data_database}; -/// use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; -/// -/// let data_file = NamedTempFile::new().unwrap(); -/// let db_data = data_file.path(); -/// init_data_database(&db_data).unwrap(); -/// -/// let extsk = ExtendedSpendingKey::master(&[]); -/// let extfvks = [ExtendedFullViewingKey::from(&extsk)]; -/// init_accounts_table(&db_data, &extfvks).unwrap(); -/// ``` -/// -/// [`get_address`]: crate::query::get_address -/// [`scan_cached_blocks`]: crate::scan::scan_cached_blocks -/// [`create_to_address`]: crate::transact::create_to_address -pub fn init_accounts_table>( - db_data: P, - extfvks: &[ExtendedFullViewingKey], -) -> Result<(), Error> { - let data = Connection::open(db_data)?; - - let mut empty_check = data.prepare("SELECT * FROM accounts LIMIT 1")?; - if empty_check.exists(NO_PARAMS)? { - return Err(Error(ErrorKind::TableNotEmpty)); - } - - // Insert accounts atomically - data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - for (account, extfvk) in extfvks.iter().enumerate() { - let address = address_from_extfvk(extfvk); - let extfvk = - encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extfvk); - data.execute( - "INSERT INTO accounts (account, extfvk, address) - VALUES (?, ?, ?)", - &[ - (account as u32).to_sql()?, - extfvk.to_sql()?, - address.to_sql()?, - ], - )?; - } - data.execute("COMMIT", NO_PARAMS)?; - - Ok(()) -} - -/// Initialises the data database with the given block. -/// -/// This enables a newly-created database to be immediately-usable, without needing to -/// synchronise historic blocks. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::init::init_blocks_table; -/// use zcash_primitives::block::BlockHash; -/// -/// // The block height. -/// let height = 500_000; -/// // The hash of the block header. -/// let hash = BlockHash([0; 32]); -/// // The nTime field from the block header. -/// let time = 12_3456_7890; -/// // The serialized Sapling commitment tree as of this block. -/// // Pre-compute and hard-code, or obtain from a service. -/// let sapling_tree = &[]; -/// -/// init_blocks_table("/path/to/data.db", height, hash, time, sapling_tree); -/// ``` -pub fn init_blocks_table>( - db_data: P, - height: i32, - hash: BlockHash, - time: u32, - sapling_tree: &[u8], -) -> Result<(), Error> { - let data = Connection::open(db_data)?; - - let mut empty_check = data.prepare("SELECT * FROM blocks LIMIT 1")?; - if empty_check.exists(NO_PARAMS)? { - return Err(Error(ErrorKind::TableNotEmpty)); - } - - data.execute( - "INSERT INTO blocks (height, hash, time, sapling_tree) - VALUES (?, ?, ?, ?)", - &[ - height.to_sql()?, - hash.0.to_sql()?, - time.to_sql()?, - sapling_tree.to_sql()?, - ], - )?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use tempfile::NamedTempFile; - use zcash_client_backend::encoding::decode_payment_address; - use zcash_primitives::{ - block::BlockHash, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; - - use super::{init_accounts_table, init_blocks_table, init_data_database}; - use crate::{query::get_address, HRP_SAPLING_PAYMENT_ADDRESS}; - - #[test] - fn init_accounts_table_only_works_once() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // We can call the function as many times as we want with no data - init_accounts_table(&db_data, &[]).unwrap(); - init_accounts_table(&db_data, &[]).unwrap(); - - // First call with data should initialise the accounts table - let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master( - &[], - ))]; - init_accounts_table(&db_data, &extfvks).unwrap(); - - // Subsequent calls should return an error - init_accounts_table(&db_data, &[]).unwrap_err(); - init_accounts_table(&db_data, &extfvks).unwrap_err(); - } - - #[test] - fn init_blocks_table_only_works_once() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // First call with data should initialise the blocks table - init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); - - // Subsequent calls should return an error - init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err(); - } - - #[test] - fn init_accounts_table_stores_correct_address() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); - - // The account's address should be in the data DB - let addr = get_address(&db_data, 0).unwrap(); - let pa = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr).unwrap(); - assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1); - } -} diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs deleted file mode 100644 index 9e4d947..0000000 --- a/zcash_client_sqlite/src/lib.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! *An SQLite-based Zcash light client.* -//! -//! `zcash_client_backend` contains a set of APIs that collectively implement an -//! SQLite-based light client for the Zcash network. -//! -//! # Design -//! -//! The light client is built around two SQLite databases: -//! -//! - A cache database, used to inform the light client about new [`CompactBlock`]s. It is -//! read-only within all light client APIs *except* for [`init_cache_database`] which -//! can be used to initialize the database. -//! -//! - A data database, where the light client's state is stored. It is read-write within -//! the light client APIs, and **assumed to be read-only outside these APIs**. Callers -//! **MUST NOT** write to the database without using these APIs. Callers **MAY** read -//! the database directly in order to extract information for display to users. -//! -//! # Features -//! -//! The `mainnet` feature configures the light client for use with the Zcash mainnet. By -//! default, the light client is configured for use with the Zcash testnet. -//! -//! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock -//! [`init_cache_database`]: crate::init::init_cache_database - -use rusqlite::{Connection, NO_PARAMS}; -use std::cmp; -use zcash_client_backend::encoding::encode_payment_address; -use zcash_primitives::zip32::ExtendedFullViewingKey; - -#[cfg(feature = "mainnet")] -use zcash_client_backend::constants::mainnet::{ - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, -}; - -#[cfg(not(feature = "mainnet"))] -use zcash_client_backend::constants::testnet::{ - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, -}; - -pub mod address; -pub mod chain; -pub mod error; -pub mod init; -pub mod query; -pub mod scan; -pub mod transact; - -const ANCHOR_OFFSET: u32 = 10; - -#[cfg(feature = "mainnet")] -const SAPLING_ACTIVATION_HEIGHT: i32 = 419_200; - -#[cfg(not(feature = "mainnet"))] -const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; - -fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { - let addr = extfvk.default_address().unwrap().1; - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr) -} - -/// Determines the target height for a transaction, and the height from which to -/// select anchors, based on the current synchronised block chain. -fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error::Error> { - data.query_row_and_then( - "SELECT MIN(height), MAX(height) FROM blocks", - NO_PARAMS, - |row| match (row.get::<_, u32>(0), row.get::<_, u32>(1)) { - // If there are no blocks, the query returns NULL. - (Err(rusqlite::Error::InvalidColumnType(_, _, _)), _) - | (_, Err(rusqlite::Error::InvalidColumnType(_, _, _))) => { - Err(error::Error(error::ErrorKind::ScanRequired)) - } - (Err(e), _) | (_, Err(e)) => Err(e.into()), - (Ok(min_height), Ok(max_height)) => { - let target_height = max_height + 1; - - // Select an anchor ANCHOR_OFFSET back from the target block, - // unless that would be before the earliest block we have. - let anchor_height = - cmp::max(target_height.saturating_sub(ANCHOR_OFFSET), min_height); - - Ok((target_height, anchor_height)) - } - }, - ) -} - -#[cfg(test)] -mod tests { - use ff::{Field, PrimeField, PrimeFieldRepr}; - use pairing::bls12_381::Bls12; - use protobuf::Message; - use rand_core::RngCore; - use rand_os::OsRng; - use rusqlite::{types::ToSql, Connection}; - use std::path::Path; - use zcash_client_backend::proto::compact_formats::{ - CompactBlock, CompactOutput, CompactSpend, CompactTx, - }; - use zcash_primitives::{ - block::BlockHash, - jubjub::fs::Fs, - note_encryption::{Memo, SaplingNoteEncryption}, - primitives::{Note, PaymentAddress}, - transaction::components::Amount, - zip32::ExtendedFullViewingKey, - JUBJUB, - }; - - /// Create a fake CompactBlock at the given height, containing a single output paying - /// the given address. Returns the CompactBlock and the nullifier for the new note. - pub(crate) fn fake_compact_block( - height: i32, - prev_hash: BlockHash, - extfvk: ExtendedFullViewingKey, - value: Amount, - ) -> (CompactBlock, Vec) { - let to = extfvk.default_address().unwrap().1; - - // Create a fake Note for the account - let mut rng = OsRng; - let note = Note { - g_d: to.diversifier.g_d::(&JUBJUB).unwrap(), - pk_d: to.pk_d.clone(), - value: value.into(), - r: Fs::random(&mut rng), - }; - let encryptor = SaplingNoteEncryption::new( - extfvk.fvk.ovk, - note.clone(), - to.clone(), - Memo::default(), - &mut rng, - ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - // Create a fake CompactBlock containing the note - let mut cout = CompactOutput::new(); - cout.set_cmu(cmu); - cout.set_epk(epk); - cout.set_ciphertext(enc_ciphertext[..52].to_vec()); - let mut ctx = CompactTx::new(); - let mut txid = vec![0; 32]; - rng.fill_bytes(&mut txid); - ctx.set_hash(txid); - ctx.outputs.push(cout); - let mut cb = CompactBlock::new(); - cb.set_height(height as u64); - cb.hash.resize(32, 0); - rng.fill_bytes(&mut cb.hash); - cb.prevHash.extend_from_slice(&prev_hash.0); - cb.vtx.push(ctx); - (cb, note.nf(&extfvk.fvk.vk, 0, &JUBJUB)) - } - - /// Create a fake CompactBlock at the given height, spending a single note from the - /// given address. - pub(crate) fn fake_compact_block_spending( - height: i32, - prev_hash: BlockHash, - (nf, in_value): (Vec, Amount), - extfvk: ExtendedFullViewingKey, - to: PaymentAddress, - value: Amount, - ) -> CompactBlock { - let mut rng = OsRng; - - // Create a fake CompactBlock containing the note - let mut cspend = CompactSpend::new(); - cspend.set_nf(nf); - let mut ctx = CompactTx::new(); - let mut txid = vec![0; 32]; - rng.fill_bytes(&mut txid); - ctx.set_hash(txid); - ctx.spends.push(cspend); - - // Create a fake Note for the payment - ctx.outputs.push({ - let note = Note { - g_d: to.diversifier.g_d::(&JUBJUB).unwrap(), - pk_d: to.pk_d.clone(), - value: value.into(), - r: Fs::random(&mut rng), - }; - let encryptor = SaplingNoteEncryption::new( - extfvk.fvk.ovk, - note.clone(), - to, - Memo::default(), - &mut rng, - ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - let mut cout = CompactOutput::new(); - cout.set_cmu(cmu); - cout.set_epk(epk); - cout.set_ciphertext(enc_ciphertext[..52].to_vec()); - cout - }); - - // Create a fake Note for the change - ctx.outputs.push({ - let change_addr = extfvk.default_address().unwrap().1; - let note = Note { - g_d: change_addr.diversifier.g_d::(&JUBJUB).unwrap(), - pk_d: change_addr.pk_d.clone(), - value: (in_value - value).into(), - r: Fs::random(&mut rng), - }; - let encryptor = SaplingNoteEncryption::new( - extfvk.fvk.ovk, - note.clone(), - change_addr, - Memo::default(), - &mut rng, - ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - let mut cout = CompactOutput::new(); - cout.set_cmu(cmu); - cout.set_epk(epk); - cout.set_ciphertext(enc_ciphertext[..52].to_vec()); - cout - }); - - let mut cb = CompactBlock::new(); - cb.set_height(height as u64); - cb.hash.resize(32, 0); - rng.fill_bytes(&mut cb.hash); - cb.prevHash.extend_from_slice(&prev_hash.0); - cb.vtx.push(ctx); - cb - } - - /// Insert a fake CompactBlock into the cache DB. - pub(crate) fn insert_into_cache>(db_cache: P, cb: &CompactBlock) { - let cb_bytes = cb.write_to_bytes().unwrap(); - let cache = Connection::open(&db_cache).unwrap(); - cache - .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") - .unwrap() - .execute(&[ - (cb.height as i32).to_sql().unwrap(), - cb_bytes.to_sql().unwrap(), - ]) - .unwrap(); - } -} diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs deleted file mode 100644 index 656e692..0000000 --- a/zcash_client_sqlite/src/query.rs +++ /dev/null @@ -1,201 +0,0 @@ -//! Functions for querying information in the data database. - -use rusqlite::Connection; -use std::path::Path; -use zcash_primitives::{note_encryption::Memo, transaction::components::Amount}; - -use crate::{ - error::{Error, ErrorKind}, - get_target_and_anchor_heights, -}; - -/// Returns the address for the account. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::query::get_address; -/// -/// let addr = get_address("/path/to/data.db", 0); -/// ``` -pub fn get_address>(db_data: P, account: u32) -> Result { - let data = Connection::open(db_data)?; - - let addr = data.query_row( - "SELECT address FROM accounts - WHERE account = ?", - &[account], - |row| row.get(0), - )?; - - Ok(addr) -} - -/// Returns the balance for the account, including all mined unspent notes that we know -/// about. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::query::get_balance; -/// -/// let addr = get_balance("/path/to/data.db", 0); -/// ``` -pub fn get_balance>(db_data: P, account: u32) -> Result { - let data = Connection::open(db_data)?; - - let balance = data.query_row( - "SELECT SUM(value) FROM received_notes - INNER JOIN transactions ON transactions.id_tx = received_notes.tx - WHERE account = ? AND spent IS NULL AND transactions.block IS NOT NULL", - &[account], - |row| row.get(0).or(Ok(0)), - )?; - - match Amount::from_i64(balance) { - Ok(amount) if !amount.is_negative() => Ok(amount), - _ => Err(Error(ErrorKind::CorruptedData( - "Sum of values in received_notes is out of range", - ))), - } -} - -/// Returns the verified balance for the account, which ignores notes that have been -/// received too recently and are not yet deemed spendable. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::query::get_verified_balance; -/// -/// let addr = get_verified_balance("/path/to/data.db", 0); -/// ``` -pub fn get_verified_balance>(db_data: P, account: u32) -> Result { - let data = Connection::open(db_data)?; - - let (_, anchor_height) = get_target_and_anchor_heights(&data)?; - - let balance = data.query_row( - "SELECT SUM(value) FROM received_notes - INNER JOIN transactions ON transactions.id_tx = received_notes.tx - WHERE account = ? AND spent IS NULL AND transactions.block <= ?", - &[account, anchor_height], - |row| row.get(0).or(Ok(0)), - )?; - - match Amount::from_i64(balance) { - Ok(amount) if !amount.is_negative() => Ok(amount), - _ => Err(Error(ErrorKind::CorruptedData( - "Sum of values in received_notes is out of range", - ))), - } -} - -/// Returns the memo for a received note, if it is known and a valid UTF-8 string. -/// -/// The note is identified by its row index in the `received_notes` table within the data -/// database. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::query::get_received_memo_as_utf8; -/// -/// let memo = get_received_memo_as_utf8("/path/to/data.db", 27); -pub fn get_received_memo_as_utf8>( - db_data: P, - id_note: i64, -) -> Result, Error> { - let data = Connection::open(db_data)?; - - let memo: Vec<_> = data.query_row( - "SELECT memo FROM received_notes - WHERE id_note = ?", - &[id_note], - |row| row.get(0), - )?; - - match Memo::from_bytes(&memo) { - Some(memo) => match memo.to_utf8() { - Some(Ok(res)) => Ok(Some(res)), - Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), - None => Ok(None), - }, - None => Ok(None), - } -} - -/// Returns the memo for a sent note, if it is known and a valid UTF-8 string. -/// -/// The note is identified by its row index in the `sent_notes` table within the data -/// database. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::query::get_sent_memo_as_utf8; -/// -/// let memo = get_sent_memo_as_utf8("/path/to/data.db", 12); -pub fn get_sent_memo_as_utf8>( - db_data: P, - id_note: i64, -) -> Result, Error> { - let data = Connection::open(db_data)?; - - let memo: Vec<_> = data.query_row( - "SELECT memo FROM sent_notes - WHERE id_note = ?", - &[id_note], - |row| row.get(0), - )?; - - match Memo::from_bytes(&memo) { - Some(memo) => match memo.to_utf8() { - Some(Ok(res)) => Ok(Some(res)), - Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), - None => Ok(None), - }, - None => Ok(None), - } -} - -#[cfg(test)] -mod tests { - use tempfile::NamedTempFile; - use zcash_primitives::{ - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; - - use super::{get_address, get_balance, get_verified_balance}; - use crate::{ - error::ErrorKind, - init::{init_accounts_table, init_data_database}, - }; - - #[test] - fn empty_database_has_no_balance() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); - - // The account should be empty - assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); - - // The account should have no verified balance, as we haven't scanned any blocks - let e = get_verified_balance(db_data, 0).unwrap_err(); - match e.kind() { - ErrorKind::ScanRequired => (), - _ => panic!("Unexpected error: {:?}", e), - } - - // An invalid account has zero balance - assert!(get_address(db_data, 1).is_err()); - assert_eq!(get_balance(db_data, 1).unwrap(), Amount::zero()); - } -} diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs deleted file mode 100644 index 75fd9ca..0000000 --- a/zcash_client_sqlite/src/scan.rs +++ /dev/null @@ -1,500 +0,0 @@ -//! Functions for scanning the chain and extracting relevant information. - -use ff::{PrimeField, PrimeFieldRepr}; -use protobuf::parse_from_bytes; -use rusqlite::{types::ToSql, Connection, NO_PARAMS}; -use std::path::Path; -use zcash_client_backend::{ - encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock, - welding_rig::scan_block, -}; -use zcash_primitives::{ - merkle_tree::{CommitmentTree, IncrementalWitness}, - sapling::Node, - JUBJUB, -}; - -use crate::{ - error::{Error, ErrorKind}, - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, -}; - -struct CompactBlockRow { - height: i32, - data: Vec, -} - -#[derive(Clone)] -struct WitnessRow { - id_note: i64, - witness: IncrementalWitness, -} - -/// Scans new blocks added to the cache for any transactions received by the tracked -/// accounts. -/// -/// This function pays attention only to cached blocks with heights greater than the -/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified -/// against previously-scanned blocks. In particular, this function **assumes** that the -/// caller is handling rollbacks. -/// -/// For brand-new light client databases, this function starts scanning from the Sapling -/// activation height. This height can be fast-forwarded to a more recent block by calling -/// [`init_blocks_table`] before this function. -/// -/// Scanned blocks are required to be height-sequential. If a block is missing from the -/// cache, an error will be returned with kind [`ErrorKind::InvalidHeight`]. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_sqlite::scan::scan_cached_blocks; -/// -/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db"); -/// ``` -/// -/// [`init_blocks_table`]: crate::init::init_blocks_table -pub fn scan_cached_blocks, Q: AsRef>( - db_cache: P, - db_data: Q, -) -> Result<(), Error> { - let cache = Connection::open(db_cache)?; - let data = Connection::open(db_data)?; - - // Recall where we synced up to previously. - // If we have never synced, use sapling activation height to select all cached CompactBlocks. - let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) - })?; - - // Fetch the CompactBlocks we need to scan - let mut stmt_blocks = cache - .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC")?; - let rows = stmt_blocks.query_map(&[last_height], |row| { - Ok(CompactBlockRow { - height: row.get(0)?, - data: row.get(1)?, - }) - })?; - - // Fetch the ExtendedFullViewingKeys we are tracking - let mut stmt_fetch_accounts = - data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; - let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { - row.get(0).map(|extfvk: String| { - decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) - }) - })?; - // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. - let extfvks: Vec<_> = extfvks - .collect::, _>, _>>()?? - .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; - - // Get the most recent CommitmentTree - let mut stmt_fetch_tree = data.prepare("SELECT sapling_tree FROM blocks WHERE height = ?")?; - let mut tree = stmt_fetch_tree - .query_row(&[last_height], |row| { - row.get(0).map(|data: Vec<_>| { - CommitmentTree::read(&data[..]).unwrap_or_else(|_| CommitmentTree::new()) - }) - }) - .unwrap_or_else(|_| CommitmentTree::new()); - - // Get most recent incremental witnesses for the notes we are tracking - let mut stmt_fetch_witnesses = - data.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?; - let witnesses = stmt_fetch_witnesses.query_map(&[last_height], |row| { - let id_note = row.get(0)?; - let data: Vec<_> = row.get(1)?; - Ok(IncrementalWitness::read(&data[..]).map(|witness| WitnessRow { id_note, witness })) - })?; - let mut witnesses: Vec<_> = witnesses.collect::, _>>()??; - - // Get the nullifiers for the notes we are tracking - let mut stmt_fetch_nullifiers = - data.prepare("SELECT id_note, nf, account FROM received_notes WHERE spent IS NULL")?; - let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| { - let nf: Vec<_> = row.get(1)?; - let account: i64 = row.get(2)?; - Ok((nf, account as usize)) - })?; - let mut nullifiers: Vec<_> = nullifiers.collect::>()?; - - // Prepare per-block SQL statements - let mut stmt_insert_block = data.prepare( - "INSERT INTO blocks (height, hash, time, sapling_tree) - VALUES (?, ?, ?, ?)", - )?; - let mut stmt_update_tx = data.prepare( - "UPDATE transactions - SET block = ?, tx_index = ? WHERE txid = ?", - )?; - let mut stmt_insert_tx = data.prepare( - "INSERT INTO transactions (txid, block, tx_index) - VALUES (?, ?, ?)", - )?; - let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; - let mut stmt_mark_spent_note = - data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; - let mut stmt_insert_note = data.prepare( - "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)", - )?; - let mut stmt_insert_witness = data.prepare( - "INSERT INTO sapling_witnesses (note, block, witness) - VALUES (?, ?, ?)", - )?; - let mut stmt_prune_witnesses = data.prepare("DELETE FROM sapling_witnesses WHERE block < ?")?; - let mut stmt_update_expired = data.prepare( - "UPDATE received_notes SET spent = NULL WHERE EXISTS ( - SELECT id_tx FROM transactions - WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ? - )", - )?; - - for row in rows { - let row = row?; - - // Start an SQL transaction for this block. - data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - - // Scanned blocks MUST be height-sequential. - if row.height != (last_height + 1) { - return Err(Error(ErrorKind::InvalidHeight(last_height + 1, row.height))); - } - last_height = row.height; - - let block: CompactBlock = parse_from_bytes(&row.data)?; - let block_hash = block.hash.clone(); - let block_time = block.time; - - let txs = { - let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); - let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect(); - scan_block( - block, - &extfvks[..], - &nf_refs, - &mut tree, - &mut witness_refs[..], - ) - }; - - // Enforce that all roots match. This is slow, so only include in debug builds. - #[cfg(debug_assertions)] - { - let cur_root = tree.root(); - for row in &witnesses { - if row.witness.root() != cur_root { - return Err(Error(ErrorKind::InvalidWitnessAnchor( - row.id_note, - last_height, - ))); - } - } - for tx in &txs { - for output in tx.shielded_outputs.iter() { - if output.witness.root() != cur_root { - return Err(Error(ErrorKind::InvalidNewWitnessAnchor( - output.index, - tx.txid, - last_height, - output.witness.root(), - ))); - } - } - } - } - - // Insert the block into the database. - let mut encoded_tree = Vec::new(); - tree.write(&mut encoded_tree) - .expect("Should be able to write to a Vec"); - stmt_insert_block.execute(&[ - row.height.to_sql()?, - block_hash.to_sql()?, - block_time.to_sql()?, - encoded_tree.to_sql()?, - ])?; - - for tx in txs { - // First try update an existing transaction in the database. - let txid = tx.txid.0.to_vec(); - let tx_row = if stmt_update_tx.execute(&[ - row.height.to_sql()?, - (tx.index as i64).to_sql()?, - txid.to_sql()?, - ])? == 0 - { - // It isn't there, so insert our transaction into the database. - stmt_insert_tx.execute(&[ - txid.to_sql()?, - row.height.to_sql()?, - (tx.index as i64).to_sql()?, - ])?; - data.last_insert_rowid() - } else { - // It was there, so grab its row number. - stmt_select_tx.query_row(&[txid], |row| row.get(0))? - }; - - // Mark notes as spent and remove them from the scanning cache - for spend in &tx.shielded_spends { - stmt_mark_spent_note.execute(&[tx_row.to_sql()?, spend.nf.to_sql()?])?; - } - nullifiers = nullifiers - .into_iter() - .filter(|(nf, _acc)| { - tx.shielded_spends - .iter() - .find(|spend| &spend.nf == nf) - .is_none() - }) - .collect(); - - for output in tx.shielded_outputs { - let mut rcm = [0; 32]; - output.note.r.into_repr().write_le(&mut rcm[..])?; - let nf = output.note.nf( - &extfvks[output.account].fvk.vk, - output.witness.position() as u64, - &JUBJUB, - ); - - // Insert received note into the database. - // Assumptions: - // - A transaction will not contain more than 2^63 shielded outputs. - // - A note value will never exceed 2^63 zatoshis. - stmt_insert_note.execute(&[ - tx_row.to_sql()?, - (output.index as i64).to_sql()?, - (output.account as i64).to_sql()?, - output.to.diversifier.0.to_sql()?, - (output.note.value as i64).to_sql()?, - rcm.to_sql()?, - nf.to_sql()?, - output.is_change.to_sql()?, - ])?; - let note_row = data.last_insert_rowid(); - - // Save witness for note. - witnesses.push(WitnessRow { - id_note: note_row, - witness: output.witness, - }); - - // Cache nullifier for note (to detect subsequent spends in this scan). - nullifiers.push((nf, output.account)); - } - } - - // Insert current witnesses into the database. - let mut encoded = Vec::new(); - for witness_row in witnesses.iter() { - encoded.clear(); - witness_row - .witness - .write(&mut encoded) - .expect("Should be able to write to a Vec"); - stmt_insert_witness.execute(&[ - witness_row.id_note.to_sql()?, - last_height.to_sql()?, - encoded.to_sql()?, - ])?; - } - - // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). - stmt_prune_witnesses.execute(&[last_height - 100])?; - - // Update now-expired transactions that didn't get mined. - stmt_update_expired.execute(&[last_height])?; - - // Commit the SQL transaction, writing this block's data atomically. - data.execute("COMMIT", NO_PARAMS)?; - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use tempfile::NamedTempFile; - use zcash_primitives::{ - block::BlockHash, - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; - - use super::scan_cached_blocks; - use crate::{ - init::{init_accounts_table, init_cache_database, init_data_database}, - query::get_balance, - tests::{fake_compact_block, fake_compact_block_spending, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, - }; - - #[test] - fn scan_cached_blocks_requires_sequential_blocks() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Create a block with height SAPLING_ACTIVATION_HEIGHT - let value = Amount::from_u64(50000).unwrap(); - let (cb1, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb1); - scan_cached_blocks(db_cache, db_data).unwrap(); - assert_eq!(get_balance(db_data, 0).unwrap(), value); - - // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next - let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, - cb1.hash(), - extfvk.clone(), - value, - ); - let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, - cb2.hash(), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb3); - match scan_cached_blocks(db_cache, db_data) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - format!( - "Expected height of next CompactBlock to be {}, but was {}", - SAPLING_ACTIVATION_HEIGHT + 1, - SAPLING_ACTIVATION_HEIGHT + 2 - ) - ), - } - - // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both - insert_into_cache(db_cache, &cb2); - scan_cached_blocks(db_cache, db_data).unwrap(); - assert_eq!( - get_balance(db_data, 0).unwrap(), - Amount::from_u64(150_000).unwrap() - ); - } - - #[test] - fn scan_cached_blocks_finds_received_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Account balance should be zero - assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); - - // Create a fake CompactBlock sending value to the address - let value = Amount::from_u64(5).unwrap(); - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Account balance should reflect the received note - assert_eq!(get_balance(db_data, 0).unwrap(), value); - - // Create a second fake CompactBlock sending more value to the address - let value2 = Amount::from_u64(7).unwrap(); - let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); - insert_into_cache(db_cache, &cb2); - - // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Account balance should reflect both received notes - assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); - } - - #[test] - fn scan_cached_blocks_finds_change_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Account balance should be zero - assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); - - // Create a fake CompactBlock sending value to the address - let value = Amount::from_u64(5).unwrap(); - let (cb, nf) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - - // Scan the cache - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Account balance should reflect the received note - assert_eq!(get_balance(db_data, 0).unwrap(), value); - - // Create a second fake CompactBlock spending value from the address - let extsk2 = ExtendedSpendingKey::master(&[0]); - let to2 = extsk2.default_address().unwrap().1; - let value2 = Amount::from_u64(2).unwrap(); - insert_into_cache( - db_cache, - &fake_compact_block_spending( - SAPLING_ACTIVATION_HEIGHT + 1, - cb.hash(), - (nf, value), - extfvk, - to2, - value2, - ), - ); - - // Scan the cache again - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Account balance should equal the change - assert_eq!(get_balance(db_data, 0).unwrap(), value - value2); - } -} diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs deleted file mode 100644 index ad95bc9..0000000 --- a/zcash_client_sqlite/src/transact.rs +++ /dev/null @@ -1,665 +0,0 @@ -//! Functions for creating transactions. - -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use rusqlite::{types::ToSql, Connection, NO_PARAMS}; -use std::path::Path; -use zcash_client_backend::encoding::encode_extended_full_viewing_key; -use zcash_primitives::{ - jubjub::fs::{Fs, FsRepr}, - merkle_tree::IncrementalWitness, - note_encryption::Memo, - primitives::{Diversifier, Note}, - prover::TxProver, - sapling::Node, - transaction::{ - builder::Builder, - components::{amount::DEFAULT_FEE, Amount}, - }, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, -}; - -use crate::{ - address::RecipientAddress, - error::{Error, ErrorKind}, - get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, -}; - -struct SelectedNoteRow { - diversifier: Diversifier, - note: Note, - witness: IncrementalWitness, -} - -/// Creates a transaction paying the specified address from the given account. -/// -/// Returns the row index of the newly-created transaction in the `transactions` table -/// within the data database. The caller can read the raw transaction bytes from the `raw` -/// column in order to broadcast the transaction to the network. -/// -/// Do not call this multiple times in parallel, or you will generate transactions that -/// double-spend the same notes. -/// -/// # Examples -/// -/// ``` -/// use zcash_client_backend::{ -/// constants::{testnet::COIN_TYPE, SAPLING_CONSENSUS_BRANCH_ID}, -/// keys::spending_key, -/// }; -/// use zcash_client_sqlite::transact::create_to_address; -/// use zcash_primitives::transaction::components::Amount; -/// use zcash_proofs::prover::LocalTxProver; -/// -/// let tx_prover = match LocalTxProver::with_default_location() { -/// Some(tx_prover) => tx_prover, -/// None => { -/// panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); -/// } -/// }; -/// -/// let account = 0; -/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); -/// let to = extsk.default_address().unwrap().1.into(); -/// match create_to_address( -/// "/path/to/data.db", -/// SAPLING_CONSENSUS_BRANCH_ID, -/// tx_prover, -/// (account, &extsk), -/// &to, -/// Amount::from_u64(1).unwrap(), -/// None, -/// ) { -/// Ok(tx_row) => (), -/// Err(e) => (), -/// } -/// ``` -pub fn create_to_address>( - db_data: P, - consensus_branch_id: u32, - prover: impl TxProver, - (account, extsk): (u32, &ExtendedSpendingKey), - to: &RecipientAddress, - value: Amount, - memo: Option, -) -> Result { - let data = Connection::open(db_data)?; - - // Check that the ExtendedSpendingKey we have been given corresponds to the - // ExtendedFullViewingKey for the account we are spending from. - let extfvk = ExtendedFullViewingKey::from(extsk); - if !data - .prepare("SELECT * FROM accounts WHERE account = ? AND extfvk = ?")? - .exists(&[ - account.to_sql()?, - encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) - .to_sql()?, - ])? - { - return Err(Error(ErrorKind::InvalidExtSK(account))); - } - let ovk = extfvk.fvk.ovk; - - // Target the next block, assuming we are up-to-date. - let (height, anchor_height) = { - let (target_height, anchor_height) = get_target_and_anchor_heights(&data)?; - (target_height, i64::from(anchor_height)) - }; - - // The goal of this SQL statement is to select the oldest notes until the required - // value has been reached, and then fetch the witnesses at the desired height for the - // selected notes. This is achieved in several steps: - // - // 1) Use a window function to create a view of all notes, ordered from oldest to - // newest, with an additional column containing a running sum: - // - Unspent notes accumulate the values of all unspent notes in that note's - // account, up to itself. - // - Spent notes accumulate the values of all notes in the transaction they were - // spent in, up to itself. - // - // 2) Select all unspent notes in the desired account, along with their running sum. - // - // 3) Select all notes for which the running sum was less than the required value, as - // well as a single note for which the sum was greater than or equal to the - // required value, bringing the sum of all selected notes across the threshold. - // - // 4) Match the selected notes against the witnesses at the desired height. - let target_value = i64::from(value + DEFAULT_FEE); - let mut stmt_select_notes = data.prepare( - "WITH selected AS ( - WITH eligible AS ( - SELECT id_note, diversifier, value, rcm, - SUM(value) OVER - (PARTITION BY account, spent ORDER BY id_note) AS so_far - FROM received_notes - INNER JOIN transactions ON transactions.id_tx = received_notes.tx - WHERE account = ? AND spent IS NULL AND transactions.block <= ? - ) - SELECT * FROM eligible WHERE so_far < ? - UNION - SELECT * FROM (SELECT * FROM eligible WHERE so_far >= ? LIMIT 1) - ), witnesses AS ( - SELECT note, witness FROM sapling_witnesses - WHERE block = ? - ) - SELECT selected.diversifier, selected.value, selected.rcm, witnesses.witness - FROM selected - INNER JOIN witnesses ON selected.id_note = witnesses.note", - )?; - - // Select notes - let notes = stmt_select_notes.query_and_then::<_, Error, _, _>( - &[ - i64::from(account), - anchor_height, - target_value, - target_value, - anchor_height, - ], - |row| { - let diversifier = { - let d: Vec<_> = row.get(0)?; - if d.len() != 11 { - return Err(Error(ErrorKind::CorruptedData( - "Invalid diversifier length", - ))); - } - let mut tmp = [0; 11]; - tmp.copy_from_slice(&d); - Diversifier(tmp) - }; - - let note_value: i64 = row.get(1)?; - - let rcm = { - let d: Vec<_> = row.get(2)?; - let mut tmp = FsRepr::default(); - tmp.read_le(&d[..])?; - Fs::from_repr(tmp).map_err(|_| Error(ErrorKind::InvalidNote))? - }; - - let from = extfvk - .fvk - .vk - .into_payment_address(diversifier, &JUBJUB) - .unwrap(); - let note = from.create_note(note_value as u64, rcm, &JUBJUB).unwrap(); - - let witness = { - let d: Vec<_> = row.get(3)?; - IncrementalWitness::read(&d[..])? - }; - - Ok(SelectedNoteRow { - diversifier, - note, - witness, - }) - }, - )?; - let notes: Vec = notes.collect::>()?; - - // Confirm we were able to select sufficient value - let selected_value = notes - .iter() - .fold(0, |acc, selected| acc + selected.note.value); - if selected_value < target_value as u64 { - return Err(Error(ErrorKind::InsufficientBalance( - selected_value, - target_value as u64, - ))); - } - - // Create the transaction - let mut builder = Builder::new(height); - for selected in notes { - builder.add_sapling_spend( - extsk.clone(), - selected.diversifier, - selected.note, - selected.witness, - )?; - } - match to { - RecipientAddress::Shielded(to) => { - builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) - } - RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), - }?; - let (tx, tx_metadata) = builder.build(consensus_branch_id, prover)?; - // We only called add_sapling_output() once. - let output_index = match tx_metadata.output_index(0) { - Some(idx) => idx as i64, - None => panic!("Output 0 should exist in the transaction"), - }; - let created = time::get_time(); - - // Update the database atomically, to ensure the result is internally consistent. - data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - - // Save the transaction in the database. - let mut raw_tx = vec![]; - tx.write(&mut raw_tx)?; - let mut stmt_insert_tx = data.prepare( - "INSERT INTO transactions (txid, created, expiry_height, raw) - VALUES (?, ?, ?, ?)", - )?; - stmt_insert_tx.execute(&[ - tx.txid().0.to_sql()?, - created.to_sql()?, - tx.expiry_height.to_sql()?, - raw_tx.to_sql()?, - ])?; - let id_tx = data.last_insert_rowid(); - - // Mark notes as spent. - // - // This locks the notes so they aren't selected again by a subsequent call to - // create_to_address() before this transaction has been mined (at which point the notes - // get re-marked as spent). - // - // Assumes that create_to_address() will never be called in parallel, which is a - // reasonable assumption for a light client such as a mobile phone. - let mut stmt_mark_spent_note = - data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; - for spend in &tx.shielded_spends { - stmt_mark_spent_note.execute(&[id_tx.to_sql()?, spend.nullifier.to_sql()?])?; - } - - // Save the sent note in the database. - // TODO: Decide how to save transparent output information. - let to_str = to.to_string(); - if let Some(memo) = memo { - let mut stmt_insert_sent_note = data.prepare( - "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) - VALUES (?, ?, ?, ?, ?, ?)", - )?; - stmt_insert_sent_note.execute(&[ - id_tx.to_sql()?, - output_index.to_sql()?, - account.to_sql()?, - to_str.to_sql()?, - i64::from(value).to_sql()?, - memo.as_bytes().to_sql()?, - ])?; - } else { - let mut stmt_insert_sent_note = data.prepare( - "INSERT INTO sent_notes (tx, output_index, from_account, address, value) - VALUES (?, ?, ?, ?, ?)", - )?; - stmt_insert_sent_note.execute(&[ - id_tx.to_sql()?, - output_index.to_sql()?, - account.to_sql()?, - to_str.to_sql()?, - i64::from(value).to_sql()?, - ])?; - } - - data.execute("COMMIT", NO_PARAMS)?; - - // Return the row number of the transaction, so the caller can fetch it for sending. - Ok(id_tx) -} - -#[cfg(test)] -mod tests { - use tempfile::NamedTempFile; - use zcash_primitives::{ - block::BlockHash, - prover::TxProver, - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; - use zcash_proofs::prover::LocalTxProver; - - use super::create_to_address; - use crate::{ - init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database}, - query::{get_balance, get_verified_balance}, - scan::scan_cached_blocks, - tests::{fake_compact_block, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, - }; - - fn test_prover() -> impl TxProver { - match LocalTxProver::with_default_location() { - Some(tx_prover) => tx_prover, - None => { - panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); - } - } - } - - #[test] - fn create_to_address_fails_on_incorrect_extsk() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add two accounts to the wallet - let extsk0 = ExtendedSpendingKey::master(&[]); - let extsk1 = ExtendedSpendingKey::master(&[0]); - let extfvks = [ - ExtendedFullViewingKey::from(&extsk0), - ExtendedFullViewingKey::from(&extsk1), - ]; - init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk0.default_address().unwrap().1.into(); - - // Invalid extsk for the given account should cause an error - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk1), - &to, - Amount::from_u64(1).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 0"), - } - match create_to_address( - db_data, - 1, - test_prover(), - (1, &extsk0), - &to, - Amount::from_u64(1).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 1"), - } - } - - #[test] - fn create_to_address_fails_with_no_blocks() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1.into(); - - // We cannot do anything if we aren't synchronised - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(1).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!(e.to_string(), "Must scan blocks first"), - } - } - - #[test] - fn create_to_address_fails_on_insufficient_balance() { - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1.into(); - - // Account balance should be zero - assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); - - // We cannot spend anything - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(1).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - "Insufficient balance (have 0, need 10001 including fee)" - ), - } - } - - #[test] - fn create_to_address_fails_on_unverified_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Add funds to the wallet in a single note - let value = Amount::from_u64(50000).unwrap(); - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Verified balance matches total balance - assert_eq!(get_balance(db_data, 0).unwrap(), value); - assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); - - // Add more funds to the wallet in a second note - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, - cb.hash(), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Verified balance does not include the second note - assert_eq!(get_balance(db_data, 0).unwrap(), value + value); - assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); - - // Spend fails because there are insufficient verified notes - let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1.into(); - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(70000).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - "Insufficient balance (have 50000, need 80000 including fee)" - ), - } - - // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second - // note is verified - for i in 2..10 { - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + i, - cb.hash(), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - } - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Second spend still fails - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(70000).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - "Insufficient balance (have 50000, need 80000 including fee)" - ), - } - - // Mine block 11 so that the second note becomes verified - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 10, - cb.hash(), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Second spend should now succeed - create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(70000).unwrap(), - None, - ) - .unwrap(); - } - - #[test] - fn create_to_address_fails_on_locked_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = cache_file.path(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let db_data = data_file.path(); - init_data_database(&db_data).unwrap(); - - // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); - - // Add funds to the wallet in a single note - let value = Amount::from_u64(50000).unwrap(); - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, - BlockHash([0; 32]), - extfvk.clone(), - value, - ); - insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); - assert_eq!(get_balance(db_data, 0).unwrap(), value); - - // Send some of the funds to another address - let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1.into(); - create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(15000).unwrap(), - None, - ) - .unwrap(); - - // A second spend fails because there are no usable notes - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(2000).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - "Insufficient balance (have 0, need 12000 including fee)" - ), - } - - // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 21 (that don't send us funds) - // until just before the first transaction expires - for i in 1..22 { - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + i, - cb.hash(), - ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), - value, - ); - insert_into_cache(db_cache, &cb); - } - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Second spend still fails - match create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(2000).unwrap(), - None, - ) { - Ok(_) => panic!("Should have failed"), - Err(e) => assert_eq!( - e.to_string(), - "Insufficient balance (have 0, need 12000 including fee)" - ), - } - - // Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires - let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 22, - cb.hash(), - ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])), - value, - ); - insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data).unwrap(); - - // Second spend should now succeed - create_to_address( - db_data, - 1, - test_prover(), - (0, &extsk), - &to, - Amount::from_u64(2000).unwrap(), - None, - ) - .unwrap(); - } -} diff --git a/zcash_history/.gitignore b/zcash_history/.gitignore new file mode 100644 index 0000000..293dd90 --- /dev/null +++ b/zcash_history/.gitignore @@ -0,0 +1,4 @@ +/target +**/*.rs.bk +Cargo.lock +.idea \ No newline at end of file diff --git a/zcash_history/COPYRIGHT b/zcash_history/COPYRIGHT new file mode 100644 index 0000000..bb8c4e7 --- /dev/null +++ b/zcash_history/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "zcash_history" library are retained by their contributors. No +copyright assignment is required to contribute to the "zcash_history" library. + +The "zcash_history" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/zcash_history/Cargo.toml b/zcash_history/Cargo.toml new file mode 100644 index 0000000..8f254e2 --- /dev/null +++ b/zcash_history/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "zcash_history" +version = "0.2.0" +authors = ["NikVolf "] +edition = "2018" +license = "MIT/Apache-2.0" +documentation = "https://docs.rs/zcash_history/" +description = "Library for Zcash blockchain history tools" + +[dev-dependencies] +assert_matches = "1.3.0" +quickcheck = "0.8" + +[dependencies] +bigint = "4" +byteorder = "1" +blake2 = { package = "blake2b_simd", version = "0.5" } diff --git a/zcash_history/LICENSE-APACHE b/zcash_history/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/zcash_history/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/zcash_history/LICENSE-MIT b/zcash_history/LICENSE-MIT new file mode 100644 index 0000000..5ee6ad6 --- /dev/null +++ b/zcash_history/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019 Nikolay Volf + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/zcash_history/README.md b/zcash_history/README.md new file mode 100644 index 0000000..5658ec1 --- /dev/null +++ b/zcash_history/README.md @@ -0,0 +1,26 @@ +# zcash_history + +Special implementation of Merkle mountain ranges (MMR) for Zcash! + +[![Build Status](https://travis-ci.org/NikVolf/zcash-mmr.svg?branch=master)](https://travis-ci.org/NikVolf/zcash-mmr) + +The main design goals of this MMR implementation are + +- Allow zero-cache and avoid db callbacks. As it is implemented, calling side must just smartly pre-load MMR nodes from the database (about log2(tree length) for append, twice as much for deletion). + +- Reuse as much logic between rust and c++ clients and place it here and librustzcash. + +- Close to zero memory consumption. + +# License + +`zcash_history` is distributed under the terms of both the MIT +license and the Apache License (Version 2.0), at your choice. + +See LICENSE-APACHE, and LICENSE-MIT for details. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in `zcash_history` by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/zcash_history/examples/lib/main.rs b/zcash_history/examples/lib/main.rs new file mode 100644 index 0000000..980f0d6 --- /dev/null +++ b/zcash_history/examples/lib/main.rs @@ -0,0 +1,2 @@ +// dummy example +pub fn main() {} diff --git a/zcash_history/examples/lib/shared.rs b/zcash_history/examples/lib/shared.rs new file mode 100644 index 0000000..49c3f9d --- /dev/null +++ b/zcash_history/examples/lib/shared.rs @@ -0,0 +1,86 @@ +use zcash_history::{Entry, EntryLink, NodeData, Tree}; + +pub struct NodeDataIterator { + return_stack: Vec, + tree: Tree, + cursor: usize, + leaf_cursor: usize, +} + +impl Iterator for NodeDataIterator { + type Item = NodeData; + + fn next(&mut self) -> Option { + let result = if self.cursor == 1 { + self.leaf_cursor = 2; + Some(leaf(1)) + } else if self.cursor == 2 { + self.leaf_cursor = 3; + Some(leaf(2)) + } else if self.cursor == 3 { + Some(self.tree.root_node().expect("always exists").data().clone()) + } else if self.return_stack.len() > 0 { + self.return_stack.pop() + } else { + for n_append in self + .tree + .append_leaf(leaf(self.leaf_cursor as u32)) + .expect("full tree cannot fail") + .into_iter() + .rev() + { + self.return_stack.push( + self.tree + .resolve_link(n_append) + .expect("just pushed") + .data() + .clone(), + ) + } + self.leaf_cursor += 1; + self.return_stack.pop() + }; + + self.cursor += 1; + result + } +} + +impl NodeDataIterator { + pub fn new() -> Self { + let root = Entry::new( + NodeData::combine(&leaf(1), &leaf(2)), + EntryLink::Stored(0), + EntryLink::Stored(1), + ); + let tree = Tree::new( + 3, + vec![(2, root)], + vec![(0, leaf(1).into()), (1, leaf(2).into())], + ); + + NodeDataIterator { + return_stack: Vec::new(), + tree, + cursor: 1, + leaf_cursor: 1, + } + } +} + +fn leaf(height: u32) -> NodeData { + NodeData { + consensus_branch_id: 0, + subtree_commitment: [0u8; 32], + start_time: height * 10 + 1, + end_time: (height + 1) * 10, + start_target: 100 + height * 10, + end_target: 100 + (height + 1) * 10, + start_sapling_root: [0u8; 32], + end_sapling_root: [0u8; 32], + subtree_total_work: 0.into(), + start_height: height as u64, + end_height: height as u64, + sapling_tx: 5 + height as u64, + } +} diff --git a/zcash_history/examples/long.rs b/zcash_history/examples/long.rs new file mode 100644 index 0000000..f718f35 --- /dev/null +++ b/zcash_history/examples/long.rs @@ -0,0 +1,113 @@ +use zcash_history::{Entry, EntryLink, NodeData, Tree}; + +#[path = "lib/shared.rs"] +mod share; + +fn draft(into: &mut Vec<(u32, Entry)>, vec: &Vec, peak_pos: usize, h: u32) { + let node_data = vec[peak_pos - 1].clone(); + let peak: Entry = match h { + 0 => node_data.into(), + _ => Entry::new( + node_data, + EntryLink::Stored((peak_pos - (1 << h) - 1) as u32), + EntryLink::Stored((peak_pos - 2) as u32), + ), + }; + + println!("Entry #{}: {}", into.len(), peak); + + into.push(((peak_pos - 1) as u32, peak)); +} + +fn prepare_tree(vec: &Vec) -> Tree { + assert!(vec.len() > 0); + + // integer log2 of (vec.len()+1), -1 + let mut h = (32 - ((vec.len() + 1) as u32).leading_zeros() - 1) - 1; + let mut peak_pos = (1 << (h + 1)) - 1; + let mut nodes = Vec::new(); + + // used later + let mut last_peak_pos = 0; + let mut last_peak_h = 0; + + loop { + if peak_pos > vec.len() { + // left child, -2^h + peak_pos = peak_pos - (1 << h); + h = h - 1; + } + + if peak_pos <= vec.len() { + draft(&mut nodes, vec, peak_pos, h); + + // save to be used in next loop + last_peak_pos = peak_pos; + last_peak_h = h; + + // right sibling + peak_pos = peak_pos + (1 << (h + 1)) - 1; + } + + if h == 0 { + break; + } + } + + // for deletion, everything on the right slope of the last peak should be pre-loaded + let mut extra = Vec::new(); + let mut h = last_peak_h; + let mut peak_pos = last_peak_pos; + + while h > 0 { + let left_pos = peak_pos - (1 << h); + let right_pos = peak_pos - 1; + h = h - 1; + + // drafting left child + draft(&mut extra, vec, left_pos, h); + + // drafting right child + draft(&mut extra, vec, right_pos, h); + + // continuing on right slope + peak_pos = right_pos; + } + + println!("Total extra of {} required for deletion!", extra.len()); + + Tree::new(vec.len() as u32, nodes, extra) +} + +fn main() { + let number = match std::env::args().skip(1).next() { + None => { + eprintln!("writer []"); + std::process::exit(1); + } + Some(number) => number.parse::().expect("invalid number"), + }; + + let long_vec = share::NodeDataIterator::new() + .take(number) + .collect::>(); + + let now = std::time::Instant::now(); + + let tree = prepare_tree(&long_vec); + let elapsed = now.elapsed(); + + println!( + "Tree final root: {}-{}", + tree.root_node().expect("root").data().start_height, + tree.root_node().expect("root").data().end_height, + ); + + println!( + "Prepare tree of {} length: {} ns / {} mcs / {} ms", + number, + elapsed.as_nanos(), + elapsed.as_micros(), + elapsed.as_millis() + ); +} diff --git a/zcash_history/examples/write.rs b/zcash_history/examples/write.rs new file mode 100644 index 0000000..cd9d651 --- /dev/null +++ b/zcash_history/examples/write.rs @@ -0,0 +1,44 @@ +#[path = "lib/shared.rs"] +mod share; + +// Test data generator +// $ cargo run --example writer -- 16 nodes.dat +// or +// $ cargo run --example writer -- 16 +// to preview + +fn main() { + let mut args = std::env::args().skip(1); + + let (number, out_file) = match args.next() { + None => { + eprintln!("writer []"); + std::process::exit(1); + } + Some(number) => ( + number.parse::().expect("invalid number"), + args.next(), + ), + }; + + let iterator = share::NodeDataIterator::new().take(number); + + if let Some(out_file_path) = out_file { + use std::io::Write; + + let mut buf = Vec::new(); + + for node in iterator { + node.write(&mut buf).expect("Failed to write data"); + } + + let mut file = std::fs::File::create(&out_file_path).expect("Failed to create output file"); + + file.write_all(&buf[..]) + .expect("Failed to write data to file"); + } else { + for n in iterator { + println!("{:?}", n); + } + } +} diff --git a/zcash_history/src/entry.rs b/zcash_history/src/entry.rs new file mode 100644 index 0000000..bfd4d17 --- /dev/null +++ b/zcash_history/src/entry.rs @@ -0,0 +1,123 @@ +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +use crate::{EntryKind, EntryLink, Error, NodeData, MAX_NODE_DATA_SIZE}; + +/// Max serialized length of entry data. +pub const MAX_ENTRY_SIZE: usize = MAX_NODE_DATA_SIZE + 9; + +/// MMR Entry. +#[derive(Debug)] +pub struct Entry { + pub(crate) kind: EntryKind, + pub(crate) data: NodeData, +} + +impl Entry { + /// New entry of type node. + pub fn new(data: NodeData, left: EntryLink, right: EntryLink) -> Self { + Entry { + kind: EntryKind::Node(left, right), + data, + } + } + + /// Returns if is this node complete (has total of 2^N leaves) + pub fn complete(&self) -> bool { + let leaves = self.leaf_count(); + leaves & (leaves - 1) == 0 + } + + /// Number of leaves under this node. + pub fn leaf_count(&self) -> u64 { + self.data.end_height - (self.data.start_height - 1) + } + + /// Is this node a leaf. + pub fn leaf(&self) -> bool { + if let EntryKind::Leaf = self.kind { + true + } else { + false + } + } + + /// Left child + pub fn left(&self) -> Result { + match self.kind { + EntryKind::Leaf => Err(Error::node_expected()), + EntryKind::Node(left, _) => Ok(left), + } + } + + /// Right child. + pub fn right(&self) -> Result { + match self.kind { + EntryKind::Leaf => Err(Error::node_expected()), + EntryKind::Node(_, right) => Ok(right), + } + } + + /// Read from byte representation. + pub fn read(consensus_branch_id: u32, r: &mut R) -> std::io::Result { + let kind = { + match r.read_u8()? { + 0 => { + let left = r.read_u32::()?; + let right = r.read_u32::()?; + EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) + } + 1 => EntryKind::Leaf, + _ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)), + } + }; + + let data = NodeData::read(consensus_branch_id, r)?; + + Ok(Entry { kind, data }) + } + + /// Write to byte representation. + pub fn write(&self, w: &mut W) -> std::io::Result<()> { + match self.kind { + EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) => { + w.write_u8(0)?; + w.write_u32::(left)?; + w.write_u32::(right)?; + } + EntryKind::Leaf => { + w.write_u8(1)?; + } + _ => { + return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)); + } + } + + self.data.write(w)?; + + Ok(()) + } + + /// Convert from byte representation. + pub fn from_bytes>(consensus_branch_id: u32, buf: T) -> std::io::Result { + let mut cursor = std::io::Cursor::new(buf); + Self::read(consensus_branch_id, &mut cursor) + } +} + +impl From for Entry { + fn from(s: NodeData) -> Self { + Entry { + kind: EntryKind::Leaf, + data: s, + } + } +} + +impl std::fmt::Display for Entry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + EntryKind::Node(l, r) => write!(f, "node({}, {}, ..)", l, r), + EntryKind::Leaf => write!(f, "leaf(..)"), + } + } +} diff --git a/zcash_history/src/lib.rs b/zcash_history/src/lib.rs new file mode 100644 index 0000000..9bc487b --- /dev/null +++ b/zcash_history/src/lib.rs @@ -0,0 +1,79 @@ +//! Chain history library for Zcash +//! +//! To be used in zebra and via FFI bindings in zcashd +#![warn(missing_docs)] + +mod entry; +mod node_data; +mod tree; + +pub use entry::{Entry, MAX_ENTRY_SIZE}; +pub use node_data::{NodeData, MAX_NODE_DATA_SIZE}; +pub use tree::Tree; + +/// Crate-level error type +#[derive(Debug)] +pub enum Error { + /// Entry expected to be presented in the tree view while it was not. + ExpectedInMemory(EntryLink), + /// Entry expected to be a node (specifying for which link this is not true). + ExpectedNode(Option), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::ExpectedInMemory(l) => write!(f, "Node/leaf expected to be in memory: {}", l), + Self::ExpectedNode(None) => write!(f, "Node expected"), + Self::ExpectedNode(Some(l)) => write!(f, "Node expected, not leaf: {}", l), + } + } +} + +/// Reference to to the tree node. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub enum EntryLink { + /// Reference to the stored (in the array representation) leaf/node. + Stored(u32), + /// Reference to the generated leaf/node. + Generated(u32), +} + +impl std::fmt::Display for EntryLink { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::Stored(v) => write!(f, "stored({})", v), + Self::Generated(v) => write!(f, "generated({})", v), + } + } +} + +/// MMR Node. It is leaf when `left`, `right` are `None` and node when they are not. +#[repr(C)] +#[derive(Debug)] +pub enum EntryKind { + /// Leaf entry. + Leaf, + /// Node entry with children links. + Node(EntryLink, EntryLink), +} + +impl Error { + /// Entry expected to be a node (specifying for which link this is not true). + pub fn link_node_expected(link: EntryLink) -> Self { + Self::ExpectedNode(Some(link)) + } + + /// Some entry is expected to be node + pub fn node_expected() -> Self { + Self::ExpectedNode(None) + } + + pub(crate) fn augment(self, link: EntryLink) -> Self { + match self { + Error::ExpectedNode(_) => Error::ExpectedNode(Some(link)), + val => val, + } + } +} diff --git a/zcash_history/src/node_data.rs b/zcash_history/src/node_data.rs new file mode 100644 index 0000000..b4563e3 --- /dev/null +++ b/zcash_history/src/node_data.rs @@ -0,0 +1,238 @@ +use bigint::U256; +use blake2::Params as Blake2Params; +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; + +/// Maximum serialized size of the node metadata. +pub const MAX_NODE_DATA_SIZE: usize = 32 + // subtree commitment + 4 + // start time + 4 + // end time + 4 + // start target + 4 + // end target + 32 + // start sapling tree root + 32 + // end sapling tree root + 32 + // subtree total work + 9 + // start height (compact uint) + 9 + // end height (compact uint) + 9; // Sapling tx count (compact uint) + // = total of 171 + +/// Node metadata. +#[repr(C)] +#[derive(Debug, Clone, Default)] +#[cfg_attr(test, derive(PartialEq))] +pub struct NodeData { + /// Consensus branch id, should be provided by deserializing node. + pub consensus_branch_id: u32, + /// Subtree commitment - either block hash for leaves or hashsum of children for nodes. + pub subtree_commitment: [u8; 32], + /// Start time. + pub start_time: u32, + /// End time. + pub end_time: u32, + /// Start target. + pub start_target: u32, + /// End target. + pub end_target: u32, + /// Start sapling tree root. + pub start_sapling_root: [u8; 32], + /// End sapling tree root. + pub end_sapling_root: [u8; 32], + /// Part of tree total work. + pub subtree_total_work: U256, + /// Start height. + pub start_height: u64, + /// End height + pub end_height: u64, + /// Number of Sapling transactions. + pub sapling_tx: u64, +} + +fn blake2b_personal(personalization: &[u8], input: &[u8]) -> [u8; 32] { + let hash_result = Blake2Params::new() + .hash_length(32) + .personal(personalization) + .to_state() + .update(input) + .finalize(); + let mut result = [0u8; 32]; + result.copy_from_slice(hash_result.as_bytes()); + result +} + +fn personalization(branch_id: u32) -> [u8; 16] { + let mut result = [0u8; 16]; + result[..12].copy_from_slice(b"ZcashHistory"); + LittleEndian::write_u32(&mut result[12..], branch_id); + result +} + +impl NodeData { + /// Combine two nodes metadata. + pub fn combine(left: &NodeData, right: &NodeData) -> NodeData { + assert_eq!(left.consensus_branch_id, right.consensus_branch_id); + + let mut hash_buf = [0u8; MAX_NODE_DATA_SIZE * 2]; + let size = { + let mut cursor = ::std::io::Cursor::new(&mut hash_buf[..]); + left.write(&mut cursor) + .expect("Writing to memory buf with enough length cannot fail; qed"); + right + .write(&mut cursor) + .expect("Writing to memory buf with enough length cannot fail; qed"); + cursor.position() as usize + }; + + let hash = blake2b_personal( + &personalization(left.consensus_branch_id), + &hash_buf[..size], + ); + + NodeData { + consensus_branch_id: left.consensus_branch_id, + subtree_commitment: hash, + start_time: left.start_time, + end_time: right.end_time, + start_target: left.start_target, + end_target: right.end_target, + start_sapling_root: left.start_sapling_root, + end_sapling_root: right.end_sapling_root, + subtree_total_work: left.subtree_total_work + right.subtree_total_work, + start_height: left.start_height, + end_height: right.end_height, + sapling_tx: left.sapling_tx + right.sapling_tx, + } + } + + fn write_compact(w: &mut W, compact: u64) -> std::io::Result<()> { + match compact { + 0..=0xfc => w.write_all(&[compact as u8])?, + 0xfd..=0xffff => { + w.write_all(&[0xfd])?; + w.write_u16::(compact as u16)?; + } + 0x10000..=0xffff_ffff => { + w.write_all(&[0xfe])?; + w.write_u32::(compact as u32)?; + } + _ => { + w.write_all(&[0xff])?; + w.write_u64::(compact)?; + } + } + Ok(()) + } + + fn read_compact(reader: &mut R) -> std::io::Result { + let result = match reader.read_u8()? { + i @ 0..=0xfc => i.into(), + 0xfd => reader.read_u16::()?.into(), + 0xfe => reader.read_u32::()?.into(), + _ => reader.read_u64::()?, + }; + + Ok(result) + } + + /// Write to the byte representation. + pub fn write(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(&self.subtree_commitment)?; + w.write_u32::(self.start_time)?; + w.write_u32::(self.end_time)?; + w.write_u32::(self.start_target)?; + w.write_u32::(self.end_target)?; + w.write_all(&self.start_sapling_root)?; + w.write_all(&self.end_sapling_root)?; + + let mut work_buf = [0u8; 32]; + self.subtree_total_work.to_little_endian(&mut work_buf[..]); + w.write_all(&work_buf)?; + + Self::write_compact(w, self.start_height)?; + Self::write_compact(w, self.end_height)?; + Self::write_compact(w, self.sapling_tx)?; + Ok(()) + } + + /// Read from the byte representation. + pub fn read(consensus_branch_id: u32, r: &mut R) -> std::io::Result { + let mut data = Self::default(); + data.consensus_branch_id = consensus_branch_id; + r.read_exact(&mut data.subtree_commitment)?; + data.start_time = r.read_u32::()?; + data.end_time = r.read_u32::()?; + data.start_target = r.read_u32::()?; + data.end_target = r.read_u32::()?; + r.read_exact(&mut data.start_sapling_root)?; + r.read_exact(&mut data.end_sapling_root)?; + + let mut work_buf = [0u8; 32]; + r.read_exact(&mut work_buf)?; + data.subtree_total_work = U256::from_little_endian(&work_buf); + + data.start_height = Self::read_compact(r)?; + data.end_height = Self::read_compact(r)?; + data.sapling_tx = Self::read_compact(r)?; + + Ok(data) + } + + /// Convert to byte representation. + pub fn to_bytes(&self) -> Vec { + let mut buf = [0u8; MAX_NODE_DATA_SIZE]; + let pos = { + let mut cursor = std::io::Cursor::new(&mut buf[..]); + self.write(&mut cursor).expect("Cursor cannot fail"); + cursor.position() as usize + }; + + buf[0..pos].to_vec() + } + + /// Convert from byte representation. + pub fn from_bytes>(consensus_branch_id: u32, buf: T) -> std::io::Result { + let mut cursor = std::io::Cursor::new(buf); + Self::read(consensus_branch_id, &mut cursor) + } + + /// Hash node metadata + pub fn hash(&self) -> [u8; 32] { + let bytes = self.to_bytes(); + + blake2b_personal(&personalization(self.consensus_branch_id), &bytes) + } +} + +#[cfg(test)] +impl quickcheck::Arbitrary for NodeData { + fn arbitrary(gen: &mut G) -> Self { + let mut node_data = NodeData::default(); + node_data.consensus_branch_id = 0; + gen.fill_bytes(&mut node_data.subtree_commitment[..]); + node_data.start_time = gen.next_u32(); + node_data.end_time = gen.next_u32(); + node_data.start_target = gen.next_u32(); + node_data.end_target = gen.next_u32(); + gen.fill_bytes(&mut node_data.start_sapling_root[..]); + gen.fill_bytes(&mut node_data.end_sapling_root[..]); + let mut number = [0u8; 32]; + gen.fill_bytes(&mut number[..]); + node_data.subtree_total_work = U256::from_little_endian(&number[..]); + node_data.start_height = gen.next_u64(); + node_data.end_height = gen.next_u64(); + node_data.sapling_tx = gen.next_u64(); + + node_data + } +} + +#[cfg(test)] +mod tests { + use super::NodeData; + use quickcheck::{quickcheck, TestResult}; + + quickcheck! { + fn serialization_round_trip(node_data: NodeData) -> TestResult { + TestResult::from_bool(NodeData::from_bytes(0, &node_data.to_bytes()).unwrap() == node_data) + } + } +} diff --git a/zcash_history/src/tree.rs b/zcash_history/src/tree.rs new file mode 100644 index 0000000..ca4c8ef --- /dev/null +++ b/zcash_history/src/tree.rs @@ -0,0 +1,741 @@ +use std::collections::HashMap; + +use crate::{Entry, EntryKind, EntryLink, Error, NodeData}; + +/// Represents partially loaded tree. +/// +/// Some kind of "view" into the array representation of the MMR tree. +/// With only some of the leaves/nodes pre-loaded / pre-generated. +/// Exact amount of the loaded data can be calculated by the constructing party, +/// depending on the length of the tree and maximum amount of operations that are going +/// to happen after construction. `Tree` should not be used as self-contained data structure, +/// since it's internal state can grow indefinitely after serial operations. +/// Intended use of this `Tree` is to instantiate it based on partially loaded data (see example +/// how to pick right nodes from the array representation of MMR Tree), perform several operations +/// (append-s/delete-s) and then drop it. +pub struct Tree { + stored: HashMap, + + // This can grow indefinitely if `Tree` is misused as a self-contained data structure + generated: Vec, + + // number of persistent(!) tree entries + stored_count: u32, + + root: EntryLink, +} + +impl Tree { + /// Resolve link originated from this tree + pub fn resolve_link(&self, link: EntryLink) -> Result { + match link { + EntryLink::Generated(index) => self.generated.get(index as usize), + EntryLink::Stored(index) => self.stored.get(&index), + } + .map(|node| IndexedNode { node, link }) + .ok_or(Error::ExpectedInMemory(link)) + } + + fn push(&mut self, data: Entry) -> EntryLink { + let idx = self.stored_count; + self.stored_count += 1; + self.stored.insert(idx, data); + EntryLink::Stored(idx) + } + + fn push_generated(&mut self, data: Entry) -> EntryLink { + self.generated.push(data); + EntryLink::Generated(self.generated.len() as u32 - 1) + } + + /// Populate tree with plain list of the leaves/nodes. For now, only for tests, + /// since this `Tree` structure is for partially loaded tree (but it might change) + #[cfg(test)] + pub fn populate(loaded: Vec, root: EntryLink) -> Self { + let mut result = Tree::invalid(); + result.stored_count = loaded.len() as u32; + for (idx, item) in loaded.into_iter().enumerate() { + result.stored.insert(idx as u32, item); + } + result.root = root; + + result + } + + // Empty tree with invalid root + fn invalid() -> Self { + Tree { + root: EntryLink::Generated(0), + generated: Default::default(), + stored: Default::default(), + stored_count: 0, + } + } + + /// New view into the the tree array representation + /// + /// `length` is total length of the array representation (is generally not a sum of + /// peaks.len + extra.len) + /// `peaks` is peaks of the mmr tree + /// `extra` is some extra nodes that calculated to be required during next one or more + /// operations on the tree. + /// + /// # Panics + /// + /// Will panic if `peaks` is empty. + pub fn new(length: u32, peaks: Vec<(u32, Entry)>, extra: Vec<(u32, Entry)>) -> Self { + assert!(peaks.len() > 0); + + let mut result = Tree::invalid(); + + result.stored_count = length; + + let mut gen = 0; + let mut root = EntryLink::Stored(peaks[0].0); + for (idx, node) in peaks.into_iter() { + result.stored.insert(idx, node); + if gen != 0 { + let next_generated = combine_nodes( + result + .resolve_link(root) + .expect("Inserted before, cannot fail; qed"), + result + .resolve_link(EntryLink::Stored(idx)) + .expect("Inserted before, cannot fail; qed"), + ); + root = result.push_generated(next_generated); + } + gen += 1; + } + + for (idx, node) in extra { + result.stored.insert(idx, node); + } + + result.root = root; + + result + } + + fn get_peaks(&self, root: EntryLink, target: &mut Vec) -> Result<(), Error> { + let (left_child_link, right_child_link) = { + let root = self.resolve_link(root)?; + if root.node.complete() { + target.push(root.link); + return Ok(()); + } + (root.left()?, root.right()?) + }; + + self.get_peaks(left_child_link, target)?; + self.get_peaks(right_child_link, target)?; + Ok(()) + } + + /// Append one leaf to the tree. + /// + /// Returns links to actual nodes that has to be persisted as the result of the append. + /// If completed without error, at least one link to the appended + /// node (with metadata provided in `new_leaf`) will be returned. + pub fn append_leaf(&mut self, new_leaf: NodeData) -> Result, Error> { + let root = self.root; + let new_leaf_link = self.push(new_leaf.into()); + let mut appended = Vec::new(); + appended.push(new_leaf_link); + + let mut peaks = Vec::new(); + self.get_peaks(root, &mut peaks)?; + + let mut merge_stack = Vec::new(); + merge_stack.push(new_leaf_link); + + // Scan the peaks right-to-left, merging together equal-sized adjacent + // complete subtrees. After this, merge_stack only contains peaks of + // unequal-sized subtrees. + while let Some(next_peak) = peaks.pop() { + let next_merge = merge_stack + .pop() + .expect("there should be at least one, initial or re-pushed"); + + if let Some(stored) = { + let peak = self.resolve_link(next_peak)?; + let m = self.resolve_link(next_merge)?; + if peak.node.leaf_count() == m.node.leaf_count() { + Some(combine_nodes(peak, m)) + } else { + None + } + } { + let link = self.push(stored); + merge_stack.push(link); + appended.push(link); + continue; + } else { + merge_stack.push(next_merge); + merge_stack.push(next_peak); + } + } + + let mut new_root = merge_stack + .pop() + .expect("Loop above cannot reduce the merge_stack"); + // Scan the peaks left-to-right, producing new generated nodes that + // connect the subtrees + while let Some(next_child) = merge_stack.pop() { + new_root = self.push_generated(combine_nodes( + self.resolve_link(new_root)?, + self.resolve_link(next_child)?, + )) + } + + self.root = new_root; + + Ok(appended) + } + + #[cfg(test)] + fn for_children(&self, node: EntryLink, f: F) { + let (left, right) = { + let link = self + .resolve_link(node) + .expect("Failed to resolve link in test"); + ( + link.left().expect("Failed to find node in test"), + link.right().expect("Failed to find node in test"), + ) + }; + f(left, right); + } + + fn pop(&mut self) { + self.stored.remove(&(self.stored_count - 1)); + self.stored_count = self.stored_count - 1; + } + + /// Truncate one leaf from the end of the tree. + /// + /// Returns actual number of nodes that should be removed by the caller + /// from the end of the array representation. + pub fn truncate_leaf(&mut self) -> Result { + let root = { + let (leaves, root_left_child) = { + let n = self.resolve_link(self.root)?; + (n.node.leaf_count(), n.node.left()?) + }; + if leaves & 1 != 0 { + self.pop(); + self.root = root_left_child; + return Ok(1); + } else { + self.resolve_link(self.root)? + } + }; + + let mut peaks = vec![root.left()?]; + let mut subtree_root_link = root.right()?; + let mut truncated = 1; + + loop { + let left_link = self.resolve_link(subtree_root_link)?.node; + if let EntryKind::Node(left, right) = left_link.kind { + peaks.push(left); + subtree_root_link = right; + truncated += 1; + } else { + if root.node.complete() { + truncated += 1; + } + break; + } + } + + let mut new_root = *peaks.get(0).expect("At lest 1 elements in peaks"); + + for next_peak in peaks.into_iter().skip(1) { + new_root = self.push_generated(combine_nodes( + self.resolve_link(new_root)?, + self.resolve_link(next_peak)?, + )); + } + + for _ in 0..truncated { + self.pop(); + } + + self.root = new_root; + + Ok(truncated) + } + + /// Length of array representation of the tree. + pub fn len(&self) -> u32 { + self.stored_count + } + + /// Link to the root node + pub fn root(&self) -> EntryLink { + self.root + } + + /// Reference to the root node. + pub fn root_node(&self) -> Result { + self.resolve_link(self.root) + } + + /// If this tree is empty. + pub fn is_empty(&self) -> bool { + self.stored_count == 0 + } +} + +/// Reference to the node with link attached. +#[derive(Debug)] +pub struct IndexedNode<'a> { + node: &'a Entry, + link: EntryLink, +} + +impl<'a> IndexedNode<'a> { + fn left(&self) -> Result { + self.node.left().map_err(|e| e.augment(self.link)) + } + + fn right(&self) -> Result { + self.node.right().map_err(|e| e.augment(self.link)) + } + + /// Reference to the entry struct. + pub fn node(&self) -> &Entry { + self.node + } + + /// Reference to the entry metadata. + pub fn data(&self) -> &NodeData { + &self.node.data + } + + /// Actual link by what this node was resolved. + pub fn link(&self) -> EntryLink { + self.link + } +} + +fn combine_nodes<'a>(left: IndexedNode<'a>, right: IndexedNode<'a>) -> Entry { + Entry { + kind: EntryKind::Node(left.link, right.link), + data: NodeData::combine(&left.node.data, &right.node.data), + } +} + +#[cfg(test)] +mod tests { + + use super::{Entry, EntryKind, EntryLink, NodeData, Tree}; + use assert_matches::assert_matches; + use quickcheck::{quickcheck, TestResult}; + + fn leaf(height: u32) -> NodeData { + NodeData { + consensus_branch_id: 1, + subtree_commitment: [0u8; 32], + start_time: 0, + end_time: 0, + start_target: 0, + end_target: 0, + start_sapling_root: [0u8; 32], + end_sapling_root: [0u8; 32], + subtree_total_work: 0.into(), + start_height: height as u64, + end_height: height as u64, + sapling_tx: 7, + } + } + + fn initial() -> Tree { + let node1: Entry = leaf(1).into(); + let node2: Entry = leaf(2).into(); + + let node3 = Entry { + data: NodeData::combine(&node1.data, &node2.data), + kind: EntryKind::Leaf, + }; + + Tree::populate(vec![node1, node2, node3], EntryLink::Stored(2)) + } + + // returns tree with specified number of leafs and it's root + fn generated(length: u32) -> Tree { + assert!(length >= 3); + let mut tree = initial(); + for i in 2..length { + tree.append_leaf(leaf(i + 1).into()) + .expect("Failed to append"); + } + + tree + } + + #[test] + fn discrete_append() { + let mut tree = initial(); + + // ** APPEND 3 ** + let appended = tree.append_leaf(leaf(3)).expect("Failed to append"); + let new_root = tree.root_node().expect("Failed to resolve root").node; + + // initial tree: (2) + // / \ + // (0) (1) + // + // new tree: + // (4g) + // / \ + // (2) \ + // / \ \ + // (0) (1) (3) + // + // so only (3) is added as real leaf + // while new root, (4g) is generated one + assert_eq!(new_root.data.end_height, 3); + assert_eq!(appended.len(), 1); + + // ** APPEND 4 ** + let appended = tree.append_leaf(leaf(4)).expect("Failed to append"); + + let new_root = tree.root_node().expect("Failed to resolve root").node; + + // intermediate tree: + // (4g) + // / \ + // (2) \ + // / \ \ + // (0) (1) (3) + // + // new tree: + // ( 6 ) + // / \ + // (2) (5) + // / \ / \ + // (0) (1) (3) (4) + // + // so (4), (5), (6) are added as real leaves + // and new root, (6) is stored one + assert_eq!(new_root.data.end_height, 4); + assert_eq!(appended.len(), 3); + assert_matches!(tree.root(), EntryLink::Stored(6)); + + // ** APPEND 5 ** + + let appended = tree.append_leaf(leaf(5)).expect("Failed to append"); + let new_root = tree.root_node().expect("Failed to resolve root").node; + + // intermediate tree: + // ( 6 ) + // / \ + // (2) (5) + // / \ / \ + // (0) (1) (3) (4) + // + // new tree: + // ( 8g ) + // / \ + // ( 6 ) \ + // / \ \ + // (2) (5) \ + // / \ / \ \ + // (0) (1) (3) (4) (7) + // + // so (7) is added as real leaf + // and new root, (8g) is generated one + assert_eq!(new_root.data.end_height, 5); + assert_eq!(appended.len(), 1); + assert_matches!(tree.root(), EntryLink::Generated(_)); + tree.for_children(tree.root(), |l, r| { + assert_matches!(l, EntryLink::Stored(6)); + assert_matches!(r, EntryLink::Stored(7)); + }); + + // *** APPEND #6 *** + let appended = tree.append_leaf(leaf(6)).expect("Failed to append"); + let new_root = tree.root_node().expect("Failed to resolve root").node; + + // intermediate tree: + // ( 8g ) + // / \ + // ( 6 ) \ + // / \ \ + // (2) (5) \ + // / \ / \ \ + // (0) (1) (3) (4) (7) + // + // new tree: + // (---10g--) + // / \ + // ( 6 ) \ + // / \ \ + // (2) (5) (9) + // / \ / \ / \ + // (0) (1) (3) (4) (7) (8) + // + // so (7) is added as real leaf + // and new root, (10g) is generated one + assert_eq!(new_root.data.end_height, 6); + assert_eq!(appended.len(), 2); + assert_matches!(tree.root(), EntryLink::Generated(_)); + tree.for_children(tree.root(), |l, r| { + assert_matches!(l, EntryLink::Stored(6)); + assert_matches!(r, EntryLink::Stored(9)); + }); + + // *** APPEND #7 *** + + let appended = tree.append_leaf(leaf(7)).expect("Failed to append"); + let new_root = tree.root_node().expect("Failed to resolve root").node; + + // intermediate tree: + // (---8g---) + // / \ + // ( 6 ) \ + // / \ \ + // (2) (5) (9) + // / \ / \ / \ + // (0) (1) (3) (4) (7) (8) + // + // new tree: + // (---12g--) + // / \ + // (---11g---) \ + // / \ \ + // ( 6 ) \ \ + // / \ \ \ + // (2) (5) (9) \ + // / \ / \ / \ \ + // (0) (1) (3) (4) (7) (8) (10) + // + // so (10) is added as real leaf + // and new root, (12g) is generated one + assert_eq!(new_root.data.end_height, 7); + assert_eq!(appended.len(), 1); + assert_matches!(tree.root(), EntryLink::Generated(_)); + tree.for_children(tree.root(), |l, r| { + assert_matches!(l, EntryLink::Generated(_)); + tree.for_children(l, |l, r| { + assert_matches!((l, r), (EntryLink::Stored(6), EntryLink::Stored(9))) + }); + assert_matches!(r, EntryLink::Stored(10)); + }); + } + + #[test] + fn truncate_simple() { + let mut tree = generated(9); + let total_truncated = tree.truncate_leaf().expect("Failed to truncate"); + + // initial tree: + // + // (-------16g------) + // / \ + // (--------14-------) \ + // / \ \ + // ( 6 ) ( 13 ) \ + // / \ / \ \ + // (2) (5) (9) (12) \ + // / \ / \ / \ / \ \ + // (0) (1) (3) (4) (7) (8) (10) (11) (15) + // + // new tree: + // (--------14-------) + // / \ + // ( 6 ) ( 13 ) + // / \ / \ + // (2) (5) (9) (12) + // / \ / \ / \ / \ + // (0) (1) (3) (4) (7) (8) (10) (11) + // + // so (15) is truncated + // and new root, (14) is a stored one now + + assert_matches!(tree.root(), EntryLink::Stored(14)); + assert_eq!(total_truncated, 1); + assert_eq!(tree.len(), 15); + } + + #[test] + fn truncate_generated() { + let mut tree = generated(10); + let deleted = tree.truncate_leaf().expect("Failed to truncate"); + + // initial tree: + // + // (--------18g--------) + // / \ + // (--------14-------) \ + // / \ \ + // ( 6 ) ( 13 ) \ + // / \ / \ \ + // (2) (5) (9) (12) (17) + // / \ / \ / \ / \ / \ + // (0) (1) (3) (4) (7) (8) (10) (11) (15) (16) + // + // new tree: + // (-------16g------) + // / \ + // (--------14-------) \ + // / \ \ + // ( 6 ) ( 13 ) \ + // / \ / \ \ + // (2) (5) (9) (12) \ + // / \ / \ / \ / \ \ + // (0) (1) (3) (4) (7) (8) (10) (11) (15) + + // new root is generated + + assert_matches!(tree.root(), EntryLink::Generated(_)); + + tree.for_children(tree.root(), |left, right| { + assert_matches!( + (left, right), + (EntryLink::Stored(14), EntryLink::Stored(15)) + ) + }); + + // two stored nodes should leave us (leaf 16 and no longer needed node 17) + assert_eq!(deleted, 2); + assert_eq!(tree.len(), 16); + } + + #[test] + fn tree_len() { + let mut tree = initial(); + + assert_eq!(tree.len(), 3); + + for i in 0..2 { + tree.append_leaf(leaf(i + 3)).expect("Failed to append"); + } + assert_eq!(tree.len(), 7); + + tree.truncate_leaf().expect("Failed to truncate"); + + assert_eq!(tree.len(), 4); + } + + #[test] + fn tree_len_long() { + let mut tree = initial(); + + assert_eq!(tree.len(), 3); + + for i in 0..4094 { + tree.append_leaf(leaf(i + 3)).expect("Failed to append"); + } + assert_eq!(tree.len(), 8191); // 4096*2-1 (full tree) + + for _ in 0..2049 { + tree.truncate_leaf().expect("Failed to truncate"); + } + + assert_eq!(tree.len(), 4083); // 4095 - log2(4096) + } + + quickcheck! { + fn there_and_back(number: u32) -> TestResult { + if number > 1024*1024 { + TestResult::discard() + } else { + let mut tree = initial(); + for i in 0..number { + tree.append_leaf(leaf(i+3)).expect("Failed to append"); + } + for _ in 0..number { + tree.truncate_leaf().expect("Failed to truncate"); + } + + TestResult::from_bool(if let EntryLink::Stored(2) = tree.root() { true } else { false }) + } + } + + fn leaf_count(number: u32) -> TestResult { + if number > 1024 * 1024 || number < 3 { + TestResult::discard() + } else { + let mut tree = initial(); + for i in 1..(number-1) { + tree.append_leaf(leaf(i+2)).expect("Failed to append"); + } + + TestResult::from_bool( + tree.root_node().expect("no root").node.leaf_count() == number as u64 + ) + } + } + + fn parity(number: u32) -> TestResult { + if number > 2048 * 2048 || number < 3 { + TestResult::discard() + } else { + let mut tree = initial(); + for i in 1..(number-1) { + tree.append_leaf(leaf(i+2)).expect("Failed to append"); + } + + TestResult::from_bool( + if number & (number - 1) == 0 { + if let EntryLink::Stored(_) = tree.root() { true } + else { false } + } else { + if let EntryLink::Generated(_) = tree.root() { true } + else { false } + } + ) + } + } + + fn parity_with_truncate(add: u32, delete: u32) -> TestResult { + // First we add `add` number of leaves, then delete `delete` number of leaves + // What is left should be consistent with generated-stored structure + if add > 2048 * 2048 || add < delete { + TestResult::discard() + } else { + let mut tree = initial(); + for i in 0..add { + tree.append_leaf(leaf(i+3)).expect("Failed to append"); + } + for _ in 0..delete { + tree.truncate_leaf().expect("Failed to truncate"); + } + + let total = add - delete + 2; + + TestResult::from_bool( + if total & total - 1 == 0 { + if let EntryLink::Stored(_) = tree.root() { true } + else { false } + } else { + if let EntryLink::Generated(_) = tree.root() { true } + else { false } + } + ) + } + } + + // Length of tree is always less than number of leaves squared + fn stored_length(add: u32, delete: u32) -> TestResult { + if add > 2048 * 2048 || add < delete { + TestResult::discard() + } else { + let mut tree = initial(); + for i in 0..add { + tree.append_leaf(leaf(i+3)).expect("Failed to append"); + } + for _ in 0..delete { + tree.truncate_leaf().expect("Failed to truncate"); + } + + let total = add - delete + 2; + + TestResult::from_bool(total * total > tree.len()) + } + } + } +} diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index d591535..b40744d 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -1,9 +1,15 @@ [package] name = "zcash_primitives" -version = "0.0.0" +description = "Rust implementations of the Zcash primitives" +version = "0.2.0" authors = [ "Jack Grigg ", ] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" [dependencies] aes = "0.3" @@ -11,21 +17,30 @@ blake2b_simd = "0.5" blake2s_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.2.1" -ff = { path = "../ff" } +ff = { version = "0.6", path = "../ff" } fpe = "0.2" hex = "0.3" lazy_static = "1" -pairing = { path = "../pairing" } +log = "0.4" +pairing = { version = "0.16", path = "../pairing" } rand = "0.7" -rand_core = "0.5" -rand_os = "0.2" +rand_core = "0.5.1" ripemd160 = { version = "0.8", optional = true } secp256k1 = { version = "=0.15.0", optional = true } sha2 = "0.8" +subtle = "2.2.1" [dev-dependencies] -hex-literal = "0.1" +criterion = "0.3" +hex-literal = "0.2" rand_xorshift = "0.2" [features] transparent-inputs = ["ripemd160", "secp256k1"] + +[[bench]] +name = "pedersen_hash" +harness = false + +[badges] +maintenance = { status = "actively-developed" } diff --git a/zcash_primitives/README.md b/zcash_primitives/README.md index b284820..02a0c33 100644 --- a/zcash_primitives/README.md +++ b/zcash_primitives/README.md @@ -6,7 +6,8 @@ This library contains Rust implementations of the Zcash primitives. Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/zcash_primitives/benches/pedersen_hash.rs b/zcash_primitives/benches/pedersen_hash.rs index b647740..6510936 100644 --- a/zcash_primitives/benches/pedersen_hash.rs +++ b/zcash_primitives/benches/pedersen_hash.rs @@ -1,19 +1,10 @@ -#![feature(test)] - -extern crate pairing; -extern crate rand_core; -extern crate rand_os; -extern crate test; -extern crate zcash_primitives; - +use criterion::{criterion_group, criterion_main, Criterion}; use pairing::bls12_381::Bls12; -use rand_core::RngCore; -use rand_os::OsRng; +use rand_core::{OsRng, RngCore}; use zcash_primitives::jubjub::JubjubBls12; use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization}; -#[bench] -fn bench_pedersen_hash(b: &mut test::Bencher) { +fn bench_pedersen_hash(c: &mut Criterion) { let params = JubjubBls12::new(); let rng = &mut OsRng; let bits = (0..510) @@ -21,5 +12,10 @@ fn bench_pedersen_hash(b: &mut test::Bencher) { .collect::>(); let personalization = Personalization::MerkleTree(31); - b.iter(|| pedersen_hash::(personalization, bits.clone(), ¶ms)); + c.bench_function("Pedersen hash", |b| { + b.iter(|| pedersen_hash::(personalization, bits.clone(), ¶ms)) + }); } + +criterion_group!(benches, bench_pedersen_hash); +criterion_main!(benches); diff --git a/zcash_primitives/src/block.rs b/zcash_primitives/src/block.rs index dc9181d..8432cd4 100644 --- a/zcash_primitives/src/block.rs +++ b/zcash_primitives/src/block.rs @@ -1,3 +1,5 @@ +//! Structs and methods for handling Zcash block headers. + use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use hex; use sha2::{Digest, Sha256}; @@ -5,14 +7,16 @@ use std::fmt; use std::io::{self, Read, Write}; use std::ops::Deref; -use serialize::Vector; +use crate::serialize::Vector; + +pub mod equihash; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHash(pub [u8; 32]); impl fmt::Display for BlockHash { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let mut data = self.0.clone(); + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut data = self.0; data.reverse(); formatter.write_str(&hex::encode(data)) } diff --git a/librustzcash/src/equihash.rs b/zcash_primitives/src/block/equihash.rs similarity index 95% rename from librustzcash/src/equihash.rs rename to zcash_primitives/src/block/equihash.rs index d251bc1..38518ba 100644 --- a/librustzcash/src/equihash.rs +++ b/zcash_primitives/src/block/equihash.rs @@ -1,5 +1,10 @@ +//! Verification functions for the [Equihash] proof-of-work algorithm. +//! +//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash + use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use log::error; use std::io::Cursor; use std::mem::size_of; @@ -60,10 +65,7 @@ impl Node { indices.extend(a.indices.iter()); indices }; - Node { - hash: hash, - indices: indices, - } + Node { hash, indices } } fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { @@ -82,10 +84,7 @@ impl Node { indices.extend(b.indices.iter()); indices.extend(a.indices.iter()); } - Node { - hash: hash, - indices: indices, - } + Node { hash, indices } } fn indices_before(&self, other: &Node) -> bool { @@ -141,7 +140,7 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { let mut j = 0; for b in vin { - acc_value = (acc_value << 8) | *b as u32; + acc_value = (acc_value << 8) | u32::from(*b); acc_bits += 8; // When we have bit_len or more bits in the accumulator, write the next @@ -197,18 +196,18 @@ fn distinct_indices(a: &Node, b: &Node) -> bool { } } } - return true; + true } fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { if !has_collision(a, b, p.collision_byte_length()) { - // error!("Invalid solution: invalid collision length between StepRows"); + error!("Invalid solution: invalid collision length between StepRows"); false } else if b.indices_before(a) { - // error!("Invalid solution: Index tree incorrectly ordered"); + error!("Invalid solution: Index tree incorrectly ordered"); false } else if !distinct_indices(a, b) { - // error!("Invalid solution: duplicate indices"); + error!("Invalid solution: duplicate indices"); false } else { true @@ -222,7 +221,7 @@ pub fn is_valid_solution_iterative( nonce: &[u8], indices: &[u32], ) -> bool { - let p = Params { n: n, k: k }; + let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); @@ -249,25 +248,25 @@ pub fn is_valid_solution_iterative( } assert!(rows.len() == 1); - return rows[0].is_zero(hash_len); + rows[0].is_zero(hash_len) } fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option { if indices.len() > 1 { let end = indices.len(); let mid = end / 2; - match tree_validator(p, state, &indices[0..mid]) { - Some(a) => match tree_validator(p, state, &indices[mid..end]) { - Some(b) => { - if validate_subtrees(p, &a, &b) { - Some(Node::from_children(a, b, p.collision_byte_length())) - } else { - None - } + match ( + tree_validator(p, state, &indices[0..mid]), + tree_validator(p, state, &indices[mid..end]), + ) { + (Some(a), Some(b)) => { + if validate_subtrees(p, &a, &b) { + Some(Node::from_children(a, b, p.collision_byte_length())) + } else { + None } - None => None, - }, - None => None, + } + _ => None, } } else { Some(Node::new(&p, &state, indices[0])) @@ -281,7 +280,7 @@ pub fn is_valid_solution_recursive( nonce: &[u8], indices: &[u32], ) -> bool { - let p = Params { n: n, k: k }; + let p = Params { n, k }; let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); @@ -297,7 +296,7 @@ pub fn is_valid_solution_recursive( } pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { - let p = Params { n: n, k: k }; + let p = Params { n, k }; let indices = indices_from_minimal(soln, p.collision_bit_length()); // Recursive validation is faster diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs new file mode 100644 index 0000000..8a7385c --- /dev/null +++ b/zcash_primitives/src/consensus.rs @@ -0,0 +1,237 @@ +//! Consensus parameters. + +use std::convert::TryFrom; +use std::fmt; + +/// Zcash consensus parameters. +pub trait Parameters { + fn activation_height(nu: NetworkUpgrade) -> Option; + + fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { + match Self::activation_height(nu) { + Some(h) if h <= height => true, + _ => false, + } + } +} + +/// Marker struct for the production network. +#[derive(Clone, Copy, Debug)] +pub struct MainNetwork; + +impl Parameters for MainNetwork { + fn activation_height(nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(347_500), + NetworkUpgrade::Sapling => Some(419_200), + NetworkUpgrade::Blossom => Some(653_600), + NetworkUpgrade::Heartwood => None, + } + } +} + +/// Marker struct for the test network. +#[derive(Clone, Copy, Debug)] +pub struct TestNetwork; + +impl Parameters for TestNetwork { + fn activation_height(nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(207_500), + NetworkUpgrade::Sapling => Some(280_000), + NetworkUpgrade::Blossom => Some(584_000), + NetworkUpgrade::Heartwood => None, + } + } +} + +/// An event that occurs at a specified height on the Zcash chain, at which point the +/// consensus rules enforced by the network are altered. +/// +/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details. +#[derive(Clone, Copy, Debug)] +pub enum NetworkUpgrade { + /// The [Overwinter] network upgrade. + /// + /// [Overwinter]: https://z.cash/upgrade/overwinter/ + Overwinter, + /// The [Sapling] network upgrade. + /// + /// [Sapling]: https://z.cash/upgrade/sapling/ + Sapling, + /// The [Blossom] network upgrade. + /// + /// [Blossom]: https://z.cash/upgrade/blossom/ + Blossom, + /// The [Heartwood] network upgrade. + /// + /// [Heartwood]: https://z.cash/upgrade/heartwood/ + Heartwood, +} + +impl fmt::Display for NetworkUpgrade { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NetworkUpgrade::Overwinter => write!(f, "Overwinter"), + NetworkUpgrade::Sapling => write!(f, "Sapling"), + NetworkUpgrade::Blossom => write!(f, "Blossom"), + NetworkUpgrade::Heartwood => write!(f, "Heartwood"), + } + } +} + +impl NetworkUpgrade { + fn branch_id(self) -> BranchId { + match self { + NetworkUpgrade::Overwinter => BranchId::Overwinter, + NetworkUpgrade::Sapling => BranchId::Sapling, + NetworkUpgrade::Blossom => BranchId::Blossom, + NetworkUpgrade::Heartwood => BranchId::Heartwood, + } + } +} + +/// The network upgrades on the Zcash chain in order of activation. +/// +/// This order corresponds to the activation heights, but because Rust enums are +/// full-fledged algebraic data types, we need to define it manually. +const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ + NetworkUpgrade::Overwinter, + NetworkUpgrade::Sapling, + NetworkUpgrade::Blossom, + NetworkUpgrade::Heartwood, +]; + +/// A globally-unique identifier for a set of consensus rules within the Zcash chain. +/// +/// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash +/// network upgrades. For example, `BranchId::Overwinter` corresponds to the blocks +/// starting at Overwinter activation, and ending the block before Sapling activation. +/// +/// The main use of the branch ID is in signature generation: transactions commit to a +/// specific branch ID by including it as part of [`signature_hash`]. This ensures +/// two-way replay protection for transactions across network upgrades. +/// +/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details. +/// +/// [`signature_hash`]: crate::transaction::signature_hash +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum BranchId { + /// The consensus rules at the launch of Zcash. + Sprout, + /// The consensus rules deployed by [`NetworkUpgrade::Overwinter`]. + Overwinter, + /// The consensus rules deployed by [`NetworkUpgrade::Sapling`]. + Sapling, + /// The consensus rules deployed by [`NetworkUpgrade::Blossom`]. + Blossom, + /// The consensus rules deployed by [`NetworkUpgrade::Heartwood`]. + Heartwood, +} + +impl TryFrom for BranchId { + type Error = &'static str; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(BranchId::Sprout), + 0x5ba8_1b19 => Ok(BranchId::Overwinter), + 0x76b8_09bb => Ok(BranchId::Sapling), + 0x2bb4_0e60 => Ok(BranchId::Blossom), + 0xf5b9_230b => Ok(BranchId::Heartwood), + _ => Err("Unknown consensus branch ID"), + } + } +} + +impl From for u32 { + fn from(consensus_branch_id: BranchId) -> u32 { + match consensus_branch_id { + BranchId::Sprout => 0, + BranchId::Overwinter => 0x5ba8_1b19, + BranchId::Sapling => 0x76b8_09bb, + BranchId::Blossom => 0x2bb4_0e60, + BranchId::Heartwood => 0xf5b9_230b, + } + } +} + +impl BranchId { + /// Returns the branch ID corresponding to the consensus rule set that is active at + /// the given height. + /// + /// This is the branch ID that should be used when creating transactions. + pub fn for_height(height: u32) -> Self { + for nu in UPGRADES_IN_ORDER.iter().rev() { + if C::is_nu_active(*nu, height) { + return nu.branch_id(); + } + } + + // Sprout rules apply before any network upgrade + BranchId::Sprout + } +} + +#[cfg(test)] +mod tests { + use std::convert::TryFrom; + + use super::{BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; + + #[test] + fn nu_ordering() { + for i in 1..UPGRADES_IN_ORDER.len() { + let nu_a = UPGRADES_IN_ORDER[i - 1]; + let nu_b = UPGRADES_IN_ORDER[i]; + match ( + MainNetwork::activation_height(nu_a), + MainNetwork::activation_height(nu_b), + ) { + (Some(a), Some(b)) if a < b => (), + (Some(_), None) => (), + (None, None) => (), + _ => panic!( + "{} should not be before {} in UPGRADES_IN_ORDER", + nu_a, nu_b + ), + } + } + } + + #[test] + fn nu_is_active() { + assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0)); + assert!(!MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_499 + )); + assert!(MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_500 + )); + } + + #[test] + fn branch_id_from_u32() { + assert_eq!(BranchId::try_from(0), Ok(BranchId::Sprout)); + assert!(BranchId::try_from(1).is_err()); + } + + #[test] + fn branch_id_for_height() { + assert_eq!(BranchId::for_height::(0), BranchId::Sprout,); + assert_eq!( + BranchId::for_height::(419_199), + BranchId::Overwinter, + ); + assert_eq!( + BranchId::for_height::(419_200), + BranchId::Sapling, + ); + assert_eq!( + BranchId::for_height::(5_000_000), + BranchId::Blossom, + ); + } +} diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index c21184d..37d337d 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -1,32 +1,34 @@ +//! Various constants used by the Zcash primitives. + /// First 64 bytes of the BLAKE2s input during group hash. /// This is chosen to be some random string that we couldn't have anticipated when we designed /// the algorithm, for rigidity purposes. /// We deliberately use an ASCII hex string of 32 bytes here. -pub const GH_FIRST_BLOCK: &'static [u8; 64] = +pub const GH_FIRST_BLOCK: &[u8; 64] = b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0"; // BLAKE2s invocation personalizations /// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk) -pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; +pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk"; /// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) -pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] = b"Zcash_nf"; +pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf"; // Group hash personalizations /// BLAKE2s Personalization for Pedersen hash generators. -pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"Zcash_PH"; +pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &[u8; 8] = b"Zcash_PH"; /// BLAKE2s Personalization for the group hash for key diversification -pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gd"; +pub const KEY_DIVERSIFICATION_PERSONALIZATION: &[u8; 8] = b"Zcash_gd"; /// BLAKE2s Personalization for the spending key base point -pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_G_"; +pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_G_"; /// BLAKE2s Personalization for the proof generation key base point -pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_H_"; +pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_H_"; /// BLAKE2s Personalization for the value commitment generator for the value -pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_cv"; +pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv"; /// BLAKE2s Personalization for the nullifier position generator (for computing rho) -pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_J_"; +pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_"; diff --git a/zcash_primitives/src/group_hash.rs b/zcash_primitives/src/group_hash.rs index dec59cd..6aac847 100644 --- a/zcash_primitives/src/group_hash.rs +++ b/zcash_primitives/src/group_hash.rs @@ -1,9 +1,13 @@ -use jubjub::{edwards, JubjubEngine, PrimeOrder}; +//! Implementation of [group hashing into Jubjub][grouphash]. +//! +//! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub + +use crate::jubjub::{edwards, JubjubEngine, PrimeOrder}; use ff::PrimeField; +use crate::constants; use blake2s_simd::Params; -use constants; /// Produces a random point in the Jubjub curve. /// The point is guaranteed to be prime order diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index b3cdd64..612fbf5 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -1,4 +1,6 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{BitIterator, Field, PrimeField}; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; @@ -81,33 +83,36 @@ impl PartialEq for Point { } impl Point { - pub fn read(reader: R, params: &E::Params) -> io::Result { + pub fn read(mut reader: R, params: &E::Params) -> io::Result { let mut y_repr = ::Repr::default(); - y_repr.read_le(reader)?; + reader.read_exact(y_repr.as_mut())?; - let x_sign = (y_repr.as_ref()[3] >> 63) == 1; - y_repr.as_mut()[3] &= 0x7fffffffffffffff; + let x_sign = (y_repr.as_ref()[31] >> 7) == 1; + y_repr.as_mut()[31] &= 0x7f; match E::Fr::from_repr(y_repr) { - Ok(y) => match Self::get_for_y(y, x_sign, params) { - Some(p) => Ok(p), - None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")), - }, - Err(_) => Err(io::Error::new( + Some(y) => { + let p = Self::get_for_y(y, x_sign, params); + if bool::from(p.is_some()) { + Ok(p.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) + } + } + None => Err(io::Error::new( io::ErrorKind::InvalidInput, "y is not in field", )), } } - pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option { + pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption { // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) // This is defined for all valid y-coordinates, // as dy^2 + 1 = 0 has no solution in Fr. // tmp1 = y^2 - let mut tmp1 = y; - tmp1.square(); + let mut tmp1 = y.square(); // tmp2 = (y^2 * d) + 1 let mut tmp2 = tmp1; @@ -117,33 +122,27 @@ impl Point { // tmp1 = y^2 - 1 tmp1.sub_assign(&E::Fr::one()); - match tmp2.inverse() { - Some(tmp2) => { - // tmp1 = (y^2 - 1) / (dy^2 + 1) - tmp1.mul_assign(&tmp2); + tmp2.invert().and_then(|tmp2| { + // tmp1 = (y^2 - 1) / (dy^2 + 1) + tmp1.mul_assign(&tmp2); - match tmp1.sqrt() { - Some(mut x) => { - if x.into_repr().is_odd() != sign { - x.negate(); - } - - let mut t = x; - t.mul_assign(&y); - - Some(Point { - x: x, - y: y, - t: t, - z: E::Fr::one(), - _marker: PhantomData, - }) - } - None => None, + tmp1.sqrt().map(|mut x| { + if x.is_odd() != sign { + x = x.neg(); } - } - None => None, - } + + let mut t = x; + t.mul_assign(&y); + + Point { + x, + y, + t, + z: E::Fr::one(), + _marker: PhantomData, + } + }) + }) } /// This guarantees the point is in the prime order subgroup @@ -159,31 +158,31 @@ impl Point { let y = E::Fr::random(rng); let sign = rng.next_u32() % 2 != 0; - if let Some(p) = Self::get_for_y(y, sign, params) { - return p; + let p = Self::get_for_y(y, sign, params); + if bool::from(p.is_some()) { + return p.unwrap(); } } } } impl Point { - pub fn write(&self, writer: W) -> io::Result<()> { - let (x, y) = self.into_xy(); + pub fn write(&self, mut writer: W) -> io::Result<()> { + let (x, y) = self.to_xy(); assert_eq!(E::Fr::NUM_BITS, 255); - let x_repr = x.into_repr(); - let mut y_repr = y.into_repr(); - if x_repr.is_odd() { - y_repr.as_mut()[3] |= 0x8000000000000000u64; + let mut y_repr = y.to_repr(); + if x.is_odd() { + y_repr.as_mut()[31] |= 0x80; } - y_repr.write_le(writer) + writer.write_all(y_repr.as_ref()) } /// Convert from a Montgomery point pub fn from_montgomery(m: &montgomery::Point, params: &E::Params) -> Self { - match m.into_xy() { + match m.to_xy() { None => { // Map the point at infinity to the neutral element. Point::zero() @@ -212,12 +211,9 @@ impl Point { // only point of order 2 that is not the neutral element. if y.is_zero() { // This must be the point (0, 0) as above. - let mut neg1 = E::Fr::one(); - neg1.negate(); - Point { x: E::Fr::zero(), - y: neg1, + y: E::Fr::one().neg(), t: E::Fr::zero(), z: E::Fr::one(), _marker: PhantomData, @@ -277,8 +273,8 @@ impl Point { Point { x: u, y: v, - t: t, - z: z, + t, + z, _marker: PhantomData, } } @@ -306,8 +302,9 @@ impl Point { } } - pub fn into_xy(&self) -> (E::Fr, E::Fr) { - let zinv = self.z.inverse().unwrap(); + /// Convert to affine coordinates + pub fn to_xy(&self) -> (E::Fr, E::Fr) { + let zinv = self.z.invert().unwrap(); let mut x = self.x; x.mul_assign(&zinv); @@ -322,8 +319,8 @@ impl Point { pub fn negate(&self) -> Self { let mut p = self.clone(); - p.x.negate(); - p.t.negate(); + p.x = p.x.neg(); + p.t = p.t.neg(); p } @@ -336,27 +333,22 @@ impl Point { // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd // A = X1^2 - let mut a = self.x; - a.square(); + let a = self.x.square(); // B = Y1^2 - let mut b = self.y; - b.square(); + let b = self.y.square(); // C = 2*Z1^2 - let mut c = self.z; - c.square(); - c.double(); + let c = self.z.square().double(); // D = a*A // = -A - let mut d = a; - d.negate(); + let d = a.neg(); // E = (X1+Y1)^2 - A - B let mut e = self.x; e.add_assign(&self.y); - e.square(); + e = e.square(); e.add_assign(&d); // -A = D e.sub_assign(&b); @@ -412,7 +404,7 @@ impl Point { b.mul_assign(&other.y); // C = d * t1 * t2 - let mut c = params.edwards_d().clone(); + let mut c = *params.edwards_d(); c.mul_assign(&self.t); c.mul_assign(&other.t); @@ -475,7 +467,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index 768bd83..fc82d75 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,15 +1,18 @@ use byteorder::{ByteOrder, LittleEndian}; -use ff::{ - adc, mac_with_carry, sbb, BitIterator, Field, - LegendreSymbol::{self, *}, - PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField, -}; +use ff::{adc, mac_with_carry, sbb, BitIterator, Field, PrimeField}; use rand_core::RngCore; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::ToUniform; // s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 const MODULUS: FsRepr = FsRepr([ + 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, + 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, +]); + +const MODULUS_LIMBS: Fs = Fs([ 0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, @@ -24,7 +27,7 @@ const MODULUS_BITS: u32 = 252; const REPR_SHAVE_BITS: u32 = 4; // R = 2**256 % s -const R: FsRepr = FsRepr([ +const R: Fs = Fs([ 0x25f80bb3b99607d9, 0xf315d62f66b6e750, 0x932514eeeb8814f4, @@ -32,7 +35,7 @@ const R: FsRepr = FsRepr([ ]); // R2 = R^2 % s -const R2: FsRepr = FsRepr([ +const R2: Fs = Fs([ 0x67719aa495e57731, 0x51b0cef09ce3fc26, 0x69dab7fac026e9a5, @@ -43,7 +46,7 @@ const R2: FsRepr = FsRepr([ const INV: u64 = 0x1ba3a358ef788ef9; // GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: FsRepr = FsRepr([ +const GENERATOR: Fs = Fs([ 0x720b1b19d49ea8f1, 0xbf4aa36101f13a58, 0x5fa8cc968193ccbb, @@ -54,7 +57,7 @@ const GENERATOR: FsRepr = FsRepr([ const S: u32 = 1; // 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: FsRepr = FsRepr([ +const ROOT_OF_UNITY: Fs = Fs([ 0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, @@ -62,239 +65,281 @@ const ROOT_OF_UNITY: FsRepr = FsRepr([ ]); // -((2**256) mod s) mod s -const NEGATIVE_ONE: Fs = Fs(FsRepr([ +const NEGATIVE_ONE: Fs = Fs([ 0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2, -])); +]); /// This is the underlying representation of an element of `Fs`. #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] -pub struct FsRepr(pub [u64; 4]); +pub struct FsRepr(pub [u8; 32]); impl ::std::fmt::Display for FsRepr { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - try!(write!(f, "0x")); + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "0x")?; for i in self.0.iter().rev() { - try!(write!(f, "{:016x}", *i)); + write!(f, "{:02x}", *i)?; } Ok(()) } } -impl AsRef<[u64]> for FsRepr { +impl AsRef<[u8]> for FsRepr { #[inline(always)] - fn as_ref(&self) -> &[u64] { + fn as_ref(&self) -> &[u8] { &self.0 } } -impl AsMut<[u64]> for FsRepr { +impl AsMut<[u8]> for FsRepr { #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { + fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } -impl From for FsRepr { - #[inline(always)] - fn from(val: u64) -> FsRepr { - let mut repr = Self::default(); - repr.0[0] = val; - repr - } -} - -impl Ord for FsRepr { - #[inline(always)] - fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::std::cmp::Ordering::Less; - } else if a > b { - return ::std::cmp::Ordering::Greater; - } - } - - ::std::cmp::Ordering::Equal - } -} - -impl PartialOrd for FsRepr { - #[inline(always)] - fn partial_cmp(&self, other: &FsRepr) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl PrimeFieldRepr for FsRepr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (4 as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &FsRepr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &FsRepr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = sbb(*a, *b, &mut borrow); - } - } -} - /// This is an element of the scalar field of the Jubjub curve. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Fs(FsRepr); +pub struct Fs([u64; 4]); + +impl Default for Fs { + fn default() -> Self { + Fs::zero() + } +} + +impl ConstantTimeEq for Fs { + fn ct_eq(&self, other: &Fs) -> Choice { + self.0[0].ct_eq(&other.0[0]) + & self.0[1].ct_eq(&other.0[1]) + & self.0[2].ct_eq(&other.0[2]) + & self.0[3].ct_eq(&other.0[3]) + } +} impl ::std::fmt::Display for Fs { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "Fs({})", self.into_repr()) + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "Fs({})", self.to_repr()) + } +} + +impl From for Fs { + #[inline(always)] + fn from(val: u64) -> Fs { + let mut raw = [0u64; 4]; + raw[0] = val; + Fs(raw) * R2 } } impl From for FsRepr { fn from(e: Fs) -> FsRepr { - e.into_repr() + e.to_repr() + } +} + +impl<'a> From<&'a Fs> for FsRepr { + fn from(e: &'a Fs) -> FsRepr { + e.to_repr() + } +} + +impl ConditionallySelectable for Fs { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Fs([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + ]) + } +} + +impl Neg for Fs { + type Output = Self; + + #[inline] + fn neg(mut self) -> Self { + if !self.is_zero() { + let mut tmp = MODULUS_LIMBS; + tmp.sub_noborrow(&self); + self = tmp; + } + self + } +} + +impl<'r> Add<&'r Fs> for Fs { + type Output = Self; + + #[inline] + fn add(self, other: &Self) -> Self { + let mut ret = self; + ret.add_assign(other); + ret + } +} + +impl Add for Fs { + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + self + &other + } +} + +impl<'r> AddAssign<&'r Fs> for Fs { + #[inline] + fn add_assign(&mut self, other: &Self) { + // This cannot exceed the backing capacity. + self.add_nocarry(&other); + + // However, it may need to be reduced. + self.reduce(); + } +} + +impl AddAssign for Fs { + #[inline] + fn add_assign(&mut self, other: Self) { + self.add_assign(&other); + } +} + +impl<'r> Sub<&'r Fs> for Fs { + type Output = Self; + + #[inline] + fn sub(self, other: &Self) -> Self { + let mut ret = self; + ret.sub_assign(other); + ret + } +} + +impl Sub for Fs { + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + self - &other + } +} + +impl<'r> SubAssign<&'r Fs> for Fs { + #[inline] + fn sub_assign(&mut self, other: &Self) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.cmp_native(self) == ::core::cmp::Ordering::Greater { + self.add_nocarry(&MODULUS_LIMBS); + } + + self.sub_noborrow(&other); + } +} + +impl SubAssign for Fs { + #[inline] + fn sub_assign(&mut self, other: Self) { + self.sub_assign(&other); + } +} + +impl<'r> Mul<&'r Fs> for Fs { + type Output = Self; + + #[inline] + fn mul(self, other: &Self) -> Self { + let mut ret = self; + ret.mul_assign(other); + ret + } +} + +impl Mul for Fs { + type Output = Self; + + #[inline] + fn mul(self, other: Self) -> Self { + self * &other + } +} + +impl<'r> MulAssign<&'r Fs> for Fs { + #[inline] + fn mul_assign(&mut self, other: &Self) { + let mut carry = 0; + let r0 = mac_with_carry(0, self.0[0], other.0[0], &mut carry); + let r1 = mac_with_carry(0, self.0[0], other.0[1], &mut carry); + let r2 = mac_with_carry(0, self.0[0], other.0[2], &mut carry); + let r3 = mac_with_carry(0, self.0[0], other.0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r1 = mac_with_carry(r1, self.0[1], other.0[0], &mut carry); + let r2 = mac_with_carry(r2, self.0[1], other.0[1], &mut carry); + let r3 = mac_with_carry(r3, self.0[1], other.0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[1], other.0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r2 = mac_with_carry(r2, self.0[2], other.0[0], &mut carry); + let r3 = mac_with_carry(r3, self.0[2], other.0[1], &mut carry); + let r4 = mac_with_carry(r4, self.0[2], other.0[2], &mut carry); + let r5 = mac_with_carry(r5, self.0[2], other.0[3], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = mac_with_carry(r3, self.0[3], other.0[0], &mut carry); + let r4 = mac_with_carry(r4, self.0[3], other.0[1], &mut carry); + let r5 = mac_with_carry(r5, self.0[3], other.0[2], &mut carry); + let r6 = mac_with_carry(r6, self.0[3], other.0[3], &mut carry); + let r7 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } +} + +impl MulAssign for Fs { + #[inline] + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); } } impl PrimeField for Fs { type Repr = FsRepr; + type ReprEndianness = byteorder::LittleEndian; + + fn from_repr(r: FsRepr) -> Option { + let r = { + let mut inner = [0; 4]; + LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); + Fs(inner) + }; - fn from_repr(r: FsRepr) -> Result { - let mut r = Fs(r); if r.is_valid() { - r.mul_assign(&Fs(R2)); - - Ok(r) + Some(r * &R2) } else { - Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + None } } - fn into_repr(&self) -> FsRepr { + fn to_repr(&self) -> FsRepr { let mut r = *self; - r.mont_reduce( - (self.0).0[0], - (self.0).0[1], - (self.0).0[2], - (self.0).0[3], - 0, - 0, - 0, - 0, - ); - r.0 + r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + let mut repr = [0; 32]; + LittleEndian::write_u64_into(&r.0, &mut repr[..]); + FsRepr(repr) + } + + #[inline(always)] + fn is_odd(&self) -> bool { + let mut r = *self; + r.mont_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + r.0[0] & 1 == 1 } fn char() -> FsRepr { @@ -306,25 +351,25 @@ impl PrimeField for Fs { const CAPACITY: u32 = Self::NUM_BITS - 1; fn multiplicative_generator() -> Self { - Fs(GENERATOR) + GENERATOR } const S: u32 = S; fn root_of_unity() -> Self { - Fs(ROOT_OF_UNITY) + ROOT_OF_UNITY } } impl Field for Fs { - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { loop { let mut tmp = { let mut repr = [0u64; 4]; - for i in 0..4 { - repr[i] = rng.next_u64(); + for limb in &mut repr { + *limb = rng.next_u64(); } - Fs(FsRepr(repr)) + Fs(repr) }; // Mask away the unused most-significant bits. @@ -338,158 +383,70 @@ impl Field for Fs { #[inline] fn zero() -> Self { - Fs(FsRepr::from(0)) + Fs::from(0) } #[inline] fn one() -> Self { - Fs(R) + R } #[inline] fn is_zero(&self) -> bool { - self.0.is_zero() + self.0.iter().all(|&e| e == 0) } #[inline] - fn add_assign(&mut self, other: &Fs) { + fn double(&self) -> Self { + let mut ret = *self; + // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); + let mut last = 0; + for i in &mut ret.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } // However, it may need to be reduced. - self.reduce(); + ret.reduce(); + + ret + } + + fn invert(&self) -> CtOption { + // We need to find b such that b * a ≡ 1 mod p. As we are in a prime + // field, we can apply Fermat's Little Theorem: + // + // a^p ≡ a mod p + // a^(p-1) ≡ 1 mod p + // a^(p-2) * a ≡ 1 mod p + // + // Thus inversion can be implemented with a single exponentiation. + let inverse = self.pow_vartime(&[ + 0xd097_0e5e_d6f7_2cb5u64, + 0xa668_2093_ccc8_1082, + 0x0667_3b01_0134_3b00, + 0x0e7d_b4ea_6533_afa9, + ]); + + CtOption::new(inverse, Choice::from(if self.is_zero() { 0 } else { 1 })) } #[inline] - fn double(&mut self) { - // This cannot exceed the backing capacity. - self.0.mul2(); - - // However, it may need to be reduced. - self.reduce(); - } - - #[inline] - fn sub_assign(&mut self, other: &Fs) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); - } - - self.0.sub_noborrow(&other.0); - } - - #[inline] - fn negate(&mut self) { - if !self.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&self.0); - self.0 = tmp; - } - } - - fn inverse(&self) -> Option { - if self.is_zero() { - None - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - let one = FsRepr::from(1); - - let mut u = self.0; - let mut v = MODULUS; - let mut b = Fs(R2); // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != one && v != one { - while u.is_even() { - u.div2(); - - if b.0.is_even() { - b.0.div2(); - } else { - b.0.add_nocarry(&MODULUS); - b.0.div2(); - } - } - - while v.is_even() { - v.div2(); - - if c.0.is_even() { - c.0.div2(); - } else { - c.0.add_nocarry(&MODULUS); - c.0.div2(); - } - } - - if v < u { - u.sub_noborrow(&v); - b.sub_assign(&c); - } else { - v.sub_noborrow(&u); - c.sub_assign(&b); - } - } - - if u == one { - Some(b) - } else { - Some(c) - } - } - } - - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - - #[inline] - fn mul_assign(&mut self, other: &Fs) { + fn square(&self) -> Self { let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); - let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r1 = mac_with_carry(0, self.0[0], self.0[1], &mut carry); + let r2 = mac_with_carry(0, self.0[0], self.0[2], &mut carry); + let r3 = mac_with_carry(0, self.0[0], self.0[3], &mut carry); let r4 = carry; let mut carry = 0; - let r1 = mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r3 = mac_with_carry(r3, self.0[1], self.0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[1], self.0[3], &mut carry); let r5 = carry; let mut carry = 0; - let r2 = mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); - let r6 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); - let r7 = carry; - self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); - } - - #[inline] - fn square(&mut self) { - let mut carry = 0; - let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); - let r4 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); - let r5 = carry; - let mut carry = 0; - let r5 = mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r5 = mac_with_carry(r5, self.0[2], self.0[3], &mut carry); let r6 = carry; let r7 = r6 >> 63; @@ -501,24 +458,80 @@ impl Field for Fs { let r1 = r1 << 1; let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r0 = mac_with_carry(0, self.0[0], self.0[0], &mut carry); let r1 = adc(r1, 0, &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r2 = mac_with_carry(r2, self.0[1], self.0[1], &mut carry); let r3 = adc(r3, 0, &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r4 = mac_with_carry(r4, self.0[2], self.0[2], &mut carry); let r5 = adc(r5, 0, &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r6 = mac_with_carry(r6, self.0[3], self.0[3], &mut carry); let r7 = adc(r7, 0, &mut carry); - self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + + let mut ret = *self; + ret.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + ret + } + + fn sqrt(&self) -> CtOption { + // Shank's algorithm for s mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + // a1 = self^((s - 3) // 4) + let mut a1 = self.pow_vartime([ + 0xb425c397b5bdcb2du64, + 0x299a0824f3320420, + 0x4199cec0404d0ec0, + 0x39f6d3a994cebea, + ]); + let mut a0 = a1.square(); + a0.mul_assign(self); + a1.mul_assign(self); + + CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) } } impl Fs { + /// Compares two elements in native representation. This is only used + /// internally. + #[inline(always)] + fn cmp_native(&self, other: &Fs) -> ::std::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::std::cmp::Ordering::Less; + } else if a > b { + return ::std::cmp::Ordering::Greater; + } + } + + ::std::cmp::Ordering::Equal + } + /// Determines if the element is really in the field. This is only used /// internally. #[inline(always)] fn is_valid(&self) -> bool { - self.0 < MODULUS + // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use + // this internal function to eliminate the cycle. + self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &Fs) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &Fs) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = sbb(*a, *b, &mut borrow); + } } /// Subtracts the modulus from this element if this element is not in the @@ -526,7 +539,7 @@ impl Fs { #[inline(always)] fn reduce(&mut self) { if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); + self.sub_noborrow(&MODULUS_LIMBS); } } @@ -548,46 +561,46 @@ impl Fs { let k = r0.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r0, k, MODULUS.0[0], &mut carry); - r1 = mac_with_carry(r1, k, MODULUS.0[1], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[2], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + mac_with_carry(r0, k, MODULUS_LIMBS.0[0], &mut carry); + r1 = mac_with_carry(r1, k, MODULUS_LIMBS.0[1], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[2], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[3], &mut carry); r4 = adc(r4, 0, &mut carry); let carry2 = carry; let k = r1.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r1, k, MODULUS.0[0], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[1], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[2], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + mac_with_carry(r1, k, MODULUS_LIMBS.0[0], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS_LIMBS.0[1], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[2], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[3], &mut carry); r5 = adc(r5, carry2, &mut carry); let carry2 = carry; let k = r2.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r2, k, MODULUS.0[0], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[1], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[2], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + mac_with_carry(r2, k, MODULUS_LIMBS.0[0], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS_LIMBS.0[1], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[2], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[3], &mut carry); r6 = adc(r6, carry2, &mut carry); let carry2 = carry; let k = r3.wrapping_mul(INV); let mut carry = 0; - mac_with_carry(r3, k, MODULUS.0[0], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[1], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[2], &mut carry); - r6 = mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + mac_with_carry(r3, k, MODULUS_LIMBS.0[0], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS_LIMBS.0[1], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS_LIMBS.0[2], &mut carry); + r6 = mac_with_carry(r6, k, MODULUS_LIMBS.0[3], &mut carry); r7 = adc(r7, carry2, &mut carry); - (self.0).0[0] = r4; - (self.0).0[1] = r5; - (self.0).0[2] = r6; - (self.0).0[3] = r7; + self.0[0] = r4; + self.0[1] = r5; + self.0[2] = r6; + self.0[3] = r7; self.reduce(); } - fn mul_bits>(&self, bits: BitIterator) -> Self { + fn mul_bits>(&self, bits: BitIterator) -> Self { let mut res = Self::zero(); for bit in bits { - res.double(); + res = res.double(); if bit { res.add_assign(self) @@ -604,58 +617,13 @@ impl ToUniform for Fs { /// Random Oracle output. fn to_uniform(digest: &[u8]) -> Self { assert_eq!(digest.len(), 64); - let mut repr: [u64; 8] = [0; 8]; - LittleEndian::read_u64_into(digest, &mut repr); - Self::one().mul_bits(BitIterator::new(repr)) - } -} - -impl SqrtField for Fs { - fn legendre(&self) -> LegendreSymbol { - // s = self^((s - 1) // 2) - let s = self.pow([ - 0x684b872f6b7b965b, - 0x53341049e6640841, - 0x83339d80809a1d80, - 0x73eda753299d7d4, - ]); - if s == Self::zero() { - Zero - } else if s == Self::one() { - QuadraticResidue - } else { - QuadraticNonResidue - } - } - - fn sqrt(&self) -> Option { - // Shank's algorithm for s mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) - - // a1 = self^((s - 3) // 4) - let mut a1 = self.pow([ - 0xb425c397b5bdcb2d, - 0x299a0824f3320420, - 0x4199cec0404d0ec0, - 0x39f6d3a994cebea, - ]); - let mut a0 = a1; - a0.square(); - a0.mul_assign(self); - - if a0 == NEGATIVE_ONE { - None - } else { - a1.mul_assign(self); - Some(a1) - } + Self::one().mul_bits(BitIterator::::new(digest)) } } #[test] fn test_neg_one() { - let mut o = Fs::one(); - o.negate(); + let o = Fs::one().neg(); assert_eq!(NEGATIVE_ONE, o); } @@ -665,361 +633,26 @@ use rand_core::SeedableRng; #[cfg(test)] use rand_xorshift::XorShiftRng; -#[test] -fn test_fs_repr_ordering() { - fn assert_equality(a: FsRepr, b: FsRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); - } - - fn assert_lt(a: FsRepr, b: FsRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FsRepr([9999, 9999, 9999, 9999]), - FsRepr([9999, 9999, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9998, 9999, 9999]), - FsRepr([9999, 9998, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); - assert_lt( - FsRepr([9999, 9997, 9999, 9998]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9999, 9997, 9998, 9999]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); -} - -#[test] -fn test_fs_repr_from() { - assert_eq!(FsRepr::from(100), FsRepr([100, 0, 0, 0])); -} - -#[test] -fn test_fs_repr_is_odd() { - assert!(!FsRepr::from(0).is_odd()); - assert!(FsRepr::from(0).is_even()); - assert!(FsRepr::from(1).is_odd()); - assert!(!FsRepr::from(1).is_even()); - assert!(!FsRepr::from(324834872).is_odd()); - assert!(FsRepr::from(324834872).is_even()); - assert!(FsRepr::from(324834873).is_odd()); - assert!(!FsRepr::from(324834873).is_even()); -} - -#[test] -fn test_fs_repr_is_zero() { - assert!(FsRepr::from(0).is_zero()); - assert!(!FsRepr::from(1).is_zero()); - assert!(!FsRepr([0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fs_repr_div2() { - let mut a = FsRepr([ - 0xbd2920b19c972321, - 0x174ed0466a3be37e, - 0xd468d5e3b551f0b5, - 0xcb67c072733beefc, - ]); - a.div2(); - assert_eq!( - a, - FsRepr([ - 0x5e949058ce4b9190, - 0x8ba76823351df1bf, - 0x6a346af1daa8f85a, - 0x65b3e039399df77e - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FsRepr([ - 0x6fd7a524163392e4, - 0x16a2e9da08cd477c, - 0xdf9a8d1abc76aa3e, - 0x196cf80e4e677d - ]) - ); - for _ in 0..200 { - a.div2(); - } - assert_eq!(a, FsRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); - for _ in 0..40 { - a.div2(); - } - assert_eq!(a, FsRepr([0x19, 0x0, 0x0, 0x0])); - for _ in 0..4 { - a.div2(); - } - assert_eq!(a, FsRepr([0x1, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_shr() { - let mut a = FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, - ]); - a.shr(0); - assert_eq!( - a, - FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1 - ]) - ); - a.shr(1); - assert_eq!( - a, - FsRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0 - ]) - ); - a.shr(50); - assert_eq!( - a, - FsRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x75611bce1b4335e, - 0x6c0 - ]) - ); - a.shr(130); - assert_eq!(a, FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); - a.shr(64); - assert_eq!(a, FsRepr([0x1b0, 0x0, 0x0, 0x0])); -} - -#[test] -fn test_fs_repr_mul2() { - let mut a = FsRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FsRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); - for _ in 0..128 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x0, 0x9600000000000000])); - for _ in 0..7 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_num_bits() { - let mut a = FsRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FsRepr::from(1); - for i in 1..257 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fs_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ]); - t.sub_noborrow(&FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ])); - assert!( - t == FsRepr([ - 0xb813415048991c1f, - 0x10ad07ae88725d92, - 0x5a7b851271759961, - 0x36850eedd30c39c5 - ]) - ); - - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - a.0[3] >>= 30; - let mut b = a; - for _ in 0..10 { - b.mul2(); - } - let mut c = b; - for _ in 0..10 { - c.mul2(); - } - - assert!(a < b); - assert!(b < c); - - let mut csub_ba = c; - csub_ba.sub_noborrow(&b); - csub_ba.sub_noborrow(&a); - - let mut csub_ab = c; - csub_ab.sub_noborrow(&a); - csub_ab.sub_noborrow(&b); - - assert_eq!(csub_ab, csub_ba); - } -} - -#[test] -fn test_fs_legendre() { - assert_eq!(QuadraticResidue, Fs::one().legendre()); - assert_eq!(Zero, Fs::zero().legendre()); - - let e = FsRepr([ - 0x8385eec23df1f88e, - 0x9a01fb412b2dba16, - 0x4c928edcdd6c22f, - 0x9f2df7ef69ecef9, - ]); - assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre()); - let e = FsRepr([ - 0xe8ed9f299da78568, - 0x35efdebc88b2209, - 0xc82125cb1f916dbe, - 0x6813d2b38c39bd0, - ]); - assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre()); -} - -#[test] -fn test_fr_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ]); - t.add_nocarry(&FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ])); - assert_eq!( - t, - FsRepr([ - 0x64b20e805c30a967, - 0x59a9ee9aa114a02, - 0x5871a104789020c9, - 0x8999707c5c726f65 - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - let mut b = Fs::random(&mut rng).into_repr(); - let mut c = Fs::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[3] >>= 3; - b.0[3] >>= 3; - c.0[3] >>= 3; - - let mut abc = a; - abc.add_nocarry(&b); - abc.add_nocarry(&c); - - let mut acb = a; - acb.add_nocarry(&c); - acb.add_nocarry(&b); - - let mut bac = b; - bac.add_nocarry(&a); - bac.add_nocarry(&c); - - let mut bca = b; - bca.add_nocarry(&c); - bca.add_nocarry(&a); - - let mut cab = c; - cab.add_nocarry(&a); - cab.add_nocarry(&b); - - let mut cba = c; - cba.add_nocarry(&b); - cba.add_nocarry(&a); - - assert_eq!(abc, acb); - assert_eq!(abc, bac); - assert_eq!(abc, bca); - assert_eq!(abc, cab); - assert_eq!(abc, cba); - } -} - #[test] fn test_fs_is_valid() { - let mut a = Fs(MODULUS); + let mut a = MODULUS_LIMBS; assert!(!a.is_valid()); - a.0.sub_noborrow(&FsRepr::from(1)); + a.sub_noborrow(&Fs([1, 0, 0, 0])); assert!(a.is_valid()); - assert!(Fs(FsRepr::from(0)).is_valid()); - assert!(Fs(FsRepr([ + assert!(Fs::zero().is_valid()); + assert!(Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9 - ])) + ]) .is_valid()); - assert!(!Fs(FsRepr([ + assert!(!Fs([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff - ])) + ]) .is_valid()); let mut rng = XorShiftRng::from_seed([ @@ -1043,77 +676,77 @@ fn test_fs_add_assign() { .unwrap(); assert!(tmp.is_valid()); // Test that adding zero has no effect. - tmp.add_assign(&Fs(FsRepr::from(0))); + tmp.add_assign(&Fs::zero()); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x8e6bfff4722d6e67, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162 - ])) + ]) ); // Add one and test for the result. - tmp.add_assign(&Fs(FsRepr::from(1))); + tmp.add_assign(&Fs([1, 0, 0, 0])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x8e6bfff4722d6e68, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162 - ])) + ]) ); // Add another random number that exercises the reduction. - tmp.add_assign(&Fs(FsRepr([ + tmp.add_assign(&Fs([ 0xb634d07bc42d4a70, 0xf724f0c008411f5f, 0x456d4053d865af34, 0x24ce814e8c63027, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x44a0d070365ab8d8, 0x4d68cb1c91616459, 0xd9d3350659f7c99e, 0x4ac5d4227a3a189 - ])) + ]) ); // Add one to (s - 1) and test for the result. - tmp = Fs(FsRepr([ + tmp = Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9, - ])); - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); + ]); + tmp.add_assign(&Fs([1, 0, 0, 0])); + assert!(tmp.is_zero()); // Add a random number to another one such that the result is s - 1 - tmp = Fs(FsRepr([ + tmp = Fs([ 0xa11fda5950ce3636, 0x922e0dbccfe0ca0e, 0xacebb6e215b82d4a, 0x97ffb8cdc3aee93, - ])); - tmp.add_assign(&Fs(FsRepr([ + ]); + tmp.add_assign(&Fs([ 0x2f7734058628f680, 0x143a12d6fce74674, 0x597b841eeb7c0db6, 0x4fdb95d88f8c115, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9 - ])) + ]) ); // Add one to the result and test for it. - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); + tmp.add_assign(&Fs([1, 0, 0, 0])); + assert!(tmp.is_zero()); } // Test associativity @@ -1147,71 +780,71 @@ fn test_fs_add_assign() { fn test_fs_sub_assign() { { // Test arbitrary subtraction that tests reduction. - let mut tmp = Fs(FsRepr([ + let mut tmp = Fs([ 0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2, - ])); - tmp.sub_assign(&Fs(FsRepr([ + ]); + tmp.sub_assign(&Fs([ 0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x97c015841f9b79f6, 0xe7fcb121eb6ffc49, 0xb8c050814de2a3c1, 0x943c0589dcafa21 - ])) + ]) ); // Test the opposite subtraction which doesn't test reduction. - tmp = Fs(FsRepr([ + tmp = Fs([ 0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679, - ])); - tmp.sub_assign(&Fs(FsRepr([ + ]); + tmp.sub_assign(&Fs([ 0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2, - ]))); + ])); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x38d6f8dab75bb2c1, 0xbe6b6f71e1581439, 0x4da6ea7fb351973e, 0x539f491c768b587 - ])) + ]) ); // Test for sensible results with zero - tmp = Fs(FsRepr::from(0)); - tmp.sub_assign(&Fs(FsRepr::from(0))); + tmp = Fs::zero(); + tmp.sub_assign(&Fs::from(0)); assert!(tmp.is_zero()); - tmp = Fs(FsRepr([ + tmp = Fs([ 0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230, - ])); - tmp.sub_assign(&Fs(FsRepr::from(0))); + ]); + tmp.sub_assign(&Fs::from(0)); assert_eq!( tmp, - Fs(FsRepr([ + Fs([ 0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230 - ])) + ]) ); } @@ -1238,25 +871,25 @@ fn test_fs_sub_assign() { #[test] fn test_fs_mul_assign() { - let mut tmp = Fs(FsRepr([ + let mut tmp = Fs([ 0xb433b01287f71744, 0x4eafb86728c4d108, 0xfdd52c14b9dfbe65, 0x2ff1f3434821118, - ])); - tmp.mul_assign(&Fs(FsRepr([ + ]); + tmp.mul_assign(&Fs([ 0xdae00fc63c9fa90f, 0x5a5ed89b96ce21ce, 0x913cd26101bd6f58, 0x3f0822831697fe9, - ]))); + ])); assert!( - tmp == Fs(FsRepr([ + tmp == Fs([ 0xb68ecb61d54d2992, 0x5ff95874defce6a6, 0x3590eb053894657d, 0x53823a118515933 - ])) + ]) ); let mut rng = XorShiftRng::from_seed([ @@ -1306,22 +939,20 @@ fn test_fs_mul_assign() { } #[test] -fn test_fr_squaring() { - let mut a = Fs(FsRepr([ +fn test_fs_squaring() { + let a = Fs([ 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xe7db4ea6533afa8, - ])); + ]); assert!(a.is_valid()); - a.square(); assert_eq!( - a, + a.square(), Fs::from_repr(FsRepr([ - 0x12c7f55cbc52fbaa, - 0xdedc98a0b5e6ce9e, - 0xad2892726a5396a, - 0x9fe82af8fee77b3 + 0xaa, 0xfb, 0x52, 0xbc, 0x5c, 0xf5, 0xc7, 0x12, 0x9e, 0xce, 0xe6, 0xb5, 0xa0, 0x98, + 0xdc, 0xde, 0x6a, 0x39, 0xa5, 0x26, 0x27, 0x89, 0xd2, 0x0a, 0xb3, 0x77, 0xee, 0x8f, + 0xaf, 0x82, 0xfe, 0x09, ])) .unwrap() ); @@ -1335,8 +966,7 @@ fn test_fr_squaring() { // Ensure that (a * a) = a^2 let a = Fs::random(&mut rng); - let mut tmp = a; - tmp.square(); + let tmp = a.square(); let mut tmp2 = a; tmp2.mul_assign(&a); @@ -1346,8 +976,8 @@ fn test_fr_squaring() { } #[test] -fn test_fs_inverse() { - assert!(Fs::zero().inverse().is_none()); +fn test_fs_invert() { + assert!(bool::from(Fs::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1359,7 +989,7 @@ fn test_fs_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fs::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -1374,19 +1004,15 @@ fn test_fs_double() { for _ in 0..1000 { // Ensure doubling a is equivalent to adding a to itself. - let mut a = Fs::random(&mut rng); - let mut b = a; - b.add_assign(&a); - a.double(); - assert_eq!(a, b); + let a = Fs::random(&mut rng); + assert_eq!(a.double(), a + a); } } #[test] -fn test_fs_negate() { +fn test_fs_neg() { { - let mut a = Fs::zero(); - a.negate(); + let a = Fs::zero().neg(); assert!(a.is_zero()); } @@ -1399,8 +1025,7 @@ fn test_fs_negate() { for _ in 0..1000 { // Ensure (a - (-a)) = 0. let mut a = Fs::random(&mut rng); - let mut b = a; - b.negate(); + let b = a.neg(); a.add_assign(&b); assert!(a.is_zero()); @@ -1414,11 +1039,11 @@ fn test_fs_pow() { 0xe5, ]); - for i in 0..1000 { + for i in 0u64..1000 { // Exponentiate by various small numbers and ensure it consists with repeated // multiplication. let a = Fs::random(&mut rng); - let target = a.pow(&[i]); + let target = a.pow_vartime(&[i]); let mut c = Fs::one(); for _ in 0..i { c.mul_assign(&a); @@ -1426,11 +1051,15 @@ fn test_fs_pow() { assert_eq!(c, target); } + use byteorder::ByteOrder; + let mut char_limbs = [0; 4]; + byteorder::LittleEndian::read_u64_into(Fs::char().as_ref(), &mut char_limbs); + for _ in 0..1000 { // Exponentiating by the modulus should have no effect in a prime field. let a = Fs::random(&mut rng); - assert_eq!(a, a.pow(Fs::char())); + assert_eq!(a, a.pow_vartime(char_limbs)); } } @@ -1446,10 +1075,8 @@ fn test_fs_sqrt() { for _ in 0..1000 { // Ensure sqrt(a^2) = a or -a let a = Fs::random(&mut rng); - let mut nega = a; - nega.negate(); - let mut b = a; - b.square(); + let nega = a.neg(); + let b = a.square(); let b = b.sqrt().unwrap(); @@ -1460,54 +1087,50 @@ fn test_fs_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fs::random(&mut rng); - if let Some(mut tmp) = a.sqrt() { - tmp.square(); - - assert_eq!(a, tmp); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } #[test] -fn test_fs_from_into_repr() { +fn test_fs_from_to_repr() { // r + 1 should not be in the field assert!(Fs::from_repr(FsRepr([ - 0xd0970e5ed6f72cb8, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 + 0xb8, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, + 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, + 0x7d, 0x0e, ])) - .is_err()); + .is_none()); // r should not be in the field - assert!(Fs::from_repr(Fs::char()).is_err()); + assert!(Fs::from_repr(Fs::char()).is_none()); // Multiply some arbitrary representations to see if the result is as expected. - let a = FsRepr([ - 0x5f2d0c05d0337b71, - 0xa1df2b0f8a20479, - 0xad73785e71bb863, - 0x504a00480c9acec, - ]); - let mut a_fs = Fs::from_repr(a).unwrap(); - let b = FsRepr([ - 0x66356ff51e477562, - 0x60a92ab55cf7603, - 0x8e4273c7364dd192, - 0x36df8844a344dc5, - ]); - let b_fs = Fs::from_repr(b).unwrap(); - let c = FsRepr([ - 0x7eef61708f4f2868, - 0x747a7e6cf52946fb, - 0x83dd75d7c9120017, - 0x762f5177f0f3df7, - ]); + let mut a_fs = Fs::from_repr(FsRepr([ + 0x71, 0x7b, 0x33, 0xd0, 0x05, 0x0c, 0x2d, 0x5f, 0x79, 0x04, 0xa2, 0xf8, 0xb0, 0xf2, 0x1d, + 0x0a, 0x63, 0xb8, 0x1b, 0xe7, 0x85, 0x37, 0xd7, 0x0a, 0xec, 0xac, 0xc9, 0x80, 0x04, 0xa0, + 0x04, 0x05, + ])) + .unwrap(); + let b_fs = Fs::from_repr(FsRepr([ + 0x62, 0x75, 0x47, 0x1e, 0xf5, 0x6f, 0x35, 0x66, 0x03, 0x76, 0xcf, 0x55, 0xab, 0x92, 0x0a, + 0x06, 0x92, 0xd1, 0x4d, 0x36, 0xc7, 0x73, 0x42, 0x8e, 0xc5, 0x4d, 0x34, 0x4a, 0x84, 0xf8, + 0x6d, 0x03, + ])) + .unwrap(); + let c_fs = Fs::from_repr(FsRepr([ + 0x68, 0x28, 0x4f, 0x8f, 0x70, 0x61, 0xef, 0x7e, 0xfb, 0x46, 0x29, 0xf5, 0x6c, 0x7e, 0x7a, + 0x74, 0x17, 0x00, 0x12, 0xc9, 0xd7, 0x75, 0xdd, 0x83, 0xf7, 0x3d, 0x0f, 0x7f, 0x17, 0xf5, + 0x62, 0x07, + ])) + .unwrap(); a_fs.mul_assign(&b_fs); - assert_eq!(a_fs.into_repr(), c); + assert_eq!(a_fs, c_fs); // Zero should be in the field. - assert!(Fs::from_repr(FsRepr::from(0)).unwrap().is_zero()); + assert!(Fs::from_repr(FsRepr::default()).unwrap().is_zero()); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1517,7 +1140,7 @@ fn test_fs_from_into_repr() { for _ in 0..1000 { // Try to turn Fs elements into representations and back again, and compare. let a = Fs::random(&mut rng); - let a_repr = a.into_repr(); + let a_repr = a.to_repr(); let b_repr = FsRepr::from(a); assert_eq!(a_repr, b_repr); let a_again = Fs::from_repr(a_repr).unwrap(); @@ -1526,60 +1149,15 @@ fn test_fs_from_into_repr() { } } -#[test] -fn test_fs_repr_display() { - assert_eq!( - format!( - "{}", - FsRepr([ - 0xa296db59787359df, - 0x8d3e33077430d318, - 0xd1abf5c606102eb7, - 0xcbc33ee28108f0 - ]) - ), - "0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0x14cb03535054a620, - 0x312aa2bf2d1dff52, - 0x970fe98746ab9361, - 0xc1e18acf82711e6 - ]) - ), - "0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FsRepr([0, 0, 0, 0])), - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - #[test] fn test_fs_display() { assert_eq!( format!( "{}", Fs::from_repr(FsRepr([ - 0x5528efb9998a01a3, - 0x5bd2add5cb357089, - 0xc061fa6adb491f98, - 0x70db9d143db03d9 + 0xa3, 0x01, 0x8a, 0x99, 0xb9, 0xef, 0x28, 0x55, 0x89, 0x70, 0x35, 0xcb, 0xd5, 0xad, + 0xd2, 0x5b, 0x98, 0x1f, 0x49, 0xdb, 0x6a, 0xfa, 0x61, 0xc0, 0xd9, 0x03, 0xdb, 0x43, + 0xd1, 0xb9, 0x0d, 0x07, ])) .unwrap() ), @@ -1589,10 +1167,9 @@ fn test_fs_display() { format!( "{}", Fs::from_repr(FsRepr([ - 0xd674745e2717999e, - 0xbeb1f52d3e96f338, - 0x9c7ae147549482b9, - 0x999706024530d22 + 0x9e, 0x99, 0x17, 0x27, 0x5e, 0x74, 0x74, 0xd6, 0x38, 0xf3, 0x96, 0x3e, 0x2d, 0xf5, + 0xb1, 0xbe, 0xb9, 0x82, 0x94, 0x54, 0x47, 0xe1, 0x7a, 0x9c, 0x22, 0x0d, 0x53, 0x24, + 0x60, 0x70, 0x99, 0x09, ])) .unwrap() ), @@ -1609,19 +1186,16 @@ fn test_fs_num_bits() { #[test] fn test_fs_root_of_unity() { assert_eq!(Fs::S, 1); + assert_eq!(Fs::multiplicative_generator(), Fs::from(6)); assert_eq!( - Fs::multiplicative_generator(), - Fs::from_repr(FsRepr::from(6)).unwrap() - ); - assert_eq!( - Fs::multiplicative_generator().pow([ - 0x684b872f6b7b965b, + Fs::multiplicative_generator().pow_vartime([ + 0x684b872f6b7b965bu64, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4 ]), Fs::root_of_unity() ); - assert_eq!(Fs::root_of_unity().pow([1 << Fs::S]), Fs::one()); - assert!(Fs::multiplicative_generator().sqrt().is_none()); + assert_eq!(Fs::root_of_unity().pow_vartime([1u64 << Fs::S]), Fs::one()); + assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none())); } diff --git a/zcash_primitives/src/jubjub/mod.rs b/zcash_primitives/src/jubjub/mod.rs index 7ab0d0e..c940681 100644 --- a/zcash_primitives/src/jubjub/mod.rs +++ b/zcash_primitives/src/jubjub/mod.rs @@ -1,3 +1,6 @@ +//! The [Jubjub] curve for efficient elliptic curve operations in circuits built +//! over [BLS12-381]. +//! //! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar //! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with //! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery @@ -16,13 +19,16 @@ //! It is a complete twisted Edwards curve, so the equivalence with //! the Montgomery curve forms a group isomorphism, allowing points //! to be freely converted between the two forms. +//! +//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub +//! [BLS12-381]: pairing::bls12_381 -use ff::{Field, PrimeField, SqrtField}; +use ff::{Field, PrimeField}; use pairing::Engine; -use group_hash::group_hash; +use crate::group_hash::group_hash; -use constants; +use crate::constants; use pairing::bls12_381::{Bls12, Fr}; @@ -89,7 +95,7 @@ pub trait ToUniform { /// and some pre-computed parameters. pub trait JubjubEngine: Engine { /// The scalar field of the Jubjub curve - type Fs: PrimeField + SqrtField + ToUniform; + type Fs: PrimeField + ToUniform; /// The parameters of Jubjub and the Sapling protocol type Params: JubjubParams; } @@ -122,7 +128,7 @@ pub trait JubjubParams: Sized { fn generator(&self, base: FixedGenerators) -> &edwards::Point; /// Returns a window table [0, 1, ..., 8] for different magnitudes of some /// fixed generator. - fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; + fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; /// Returns the window size for exponentiation of Pedersen hash generators /// outside the circuit fn pedersen_hash_exp_window_size() -> u32; @@ -189,8 +195,7 @@ impl JubjubParams for JubjubBls12 { impl JubjubBls12 { pub fn new() -> Self { let montgomery_a = Fr::from_str("40962").unwrap(); - let mut montgomery_2a = montgomery_a; - montgomery_2a.double(); + let montgomery_2a = montgomery_a.double(); let mut tmp_params = JubjubBls12 { // d = -(10240/10241) @@ -199,9 +204,9 @@ impl JubjubBls12 { ) .unwrap(), // A = 40962 - montgomery_a: montgomery_a, + montgomery_a, // 2A = 2.A - montgomery_2a: montgomery_2a, + montgomery_2a, // scaling factor = sqrt(4 / (a - d)) scale: Fr::from_str( "17814886934372412843466061268024708274627479829237077604635722030778476050649", @@ -216,33 +221,11 @@ impl JubjubBls12 { fixed_base_circuit_generators: vec![], }; - fn find_group_hash( - m: &[u8], - personalization: &[u8; 8], - params: &E::Params, - ) -> edwards::Point { - let mut tag = m.to_vec(); - let i = tag.len(); - tag.push(0u8); - - loop { - let gh = group_hash(&tag, personalization, params); - - // We don't want to overflow and start reusing generators - assert!(tag[i] != u8::max_value()); - tag[i] += 1; - - if let Some(gh) = gh { - break gh; - } - } - } - // Create the bases for the Pedersen hashes { let mut pedersen_hash_generators = vec![]; - for m in 0..5 { + for m in 0..6 { use byteorder::{LittleEndian, WriteBytesExt}; let mut segment_number = [0u8; 4]; @@ -250,26 +233,17 @@ impl JubjubBls12 { .write_u32::(m) .unwrap(); - pedersen_hash_generators.push(find_group_hash( + pedersen_hash_generators.push(JubjubBls12::find_group_hash( &segment_number, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params, )); } - // Check for duplicates, far worse than spec inconsistencies! - for (i, p1) in pedersen_hash_generators.iter().enumerate() { - if p1 == &edwards::Point::zero() { - panic!("Neutral element!"); - } - - for p2 in pedersen_hash_generators.iter().skip(i + 1) { - if p1 == p2 { - panic!("Duplicate generator!"); - } - } - } - + JubjubBls12::check_consistency_of_pedersen_hash_generators( + &tmp_params, + &pedersen_hash_generators, + ); tmp_params.pedersen_hash_generators = pedersen_hash_generators; } @@ -314,43 +288,47 @@ impl JubjubBls12 { let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; - fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = find_group_hash( - &[], - constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, - &tmp_params, - ); + fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = + JubjubBls12::find_group_hash( + &[], + constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, + &tmp_params, + ); fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = - find_group_hash( + JubjubBls12::find_group_hash( b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params, ); - fixed_base_generators[FixedGenerators::NullifierPosition as usize] = find_group_hash( - &[], - constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, - &tmp_params, - ); + fixed_base_generators[FixedGenerators::NullifierPosition as usize] = + JubjubBls12::find_group_hash( + &[], + constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, + &tmp_params, + ); - fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = find_group_hash( - b"v", - constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - &tmp_params, - ); + fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = + JubjubBls12::find_group_hash( + b"v", + constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, + &tmp_params, + ); fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = - find_group_hash( + JubjubBls12::find_group_hash( b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params, ); - fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = find_group_hash( - &[], - constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, - &tmp_params, - ); + fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = + JubjubBls12::find_group_hash( + &[], + constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, + &tmp_params, + ); // Check for duplicates, far worse than spec inconsistencies! for (i, p1) in fixed_base_generators.iter().enumerate() { @@ -374,7 +352,7 @@ impl JubjubBls12 { let mut pedersen_circuit_generators = vec![]; // Process each segment - for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() { + for gen in tmp_params.pedersen_hash_generators.iter().cloned() { let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params); let mut windows = vec![]; for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() { @@ -384,7 +362,7 @@ impl JubjubBls12 { // coeffs = g, g*2, g*3, g*4 for _ in 0..4 { - coeffs.push(g.into_xy().expect("cannot produce O")); + coeffs.push(g.to_xy().expect("cannot produce O")); g = g.add(&gen, &tmp_params); } windows.push(coeffs); @@ -411,7 +389,7 @@ impl JubjubBls12 { let mut coeffs = vec![(Fr::zero(), Fr::one())]; let mut g = gen.clone(); for _ in 0..7 { - coeffs.push(g.into_xy()); + coeffs.push(g.to_xy()); g = g.add(&gen, &tmp_params); } windows.push(coeffs); @@ -427,10 +405,71 @@ impl JubjubBls12 { tmp_params } + + fn find_group_hash( + m: &[u8], + personalization: &[u8; 8], + params: &E::Params, + ) -> edwards::Point { + let mut tag = m.to_vec(); + let i = tag.len(); + tag.push(0u8); + + loop { + let gh = group_hash(&tag, personalization, params); + + // We don't want to overflow and start reusing generators + assert!(tag[i] != u8::max_value()); + tag[i] += 1; + + if let Some(gh) = gh { + break gh; + } + } + } + + /// Check for simple relations between the generators, that make finding collisions easy; + /// far worse than spec inconsistencies! + fn check_consistency_of_pedersen_hash_generators( + tmp_params: &E::Params, + pedersen_hash_generators: &[edwards::Point], + ) { + for (i, p1) in pedersen_hash_generators.iter().enumerate() { + if p1 == &edwards::Point::zero() { + panic!("Neutral element!"); + } + for p2 in pedersen_hash_generators.iter().skip(i + 1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + if p1 == &p2.negate() { + panic!("Inverse generator!"); + } + } + + // check for a generator being the sum of any other two + for (j, p2) in pedersen_hash_generators.iter().enumerate() { + if j == i { + continue; + } + for (k, p3) in pedersen_hash_generators.iter().enumerate() { + if k == j || k == i { + continue; + } + let sum = &p2.add(&p3, &tmp_params); + if sum == p1 { + panic!("Linear relation between generators!"); + } + } + } + } + } } #[test] fn test_jubjub_bls12() { + use hex_literal::hex; + let params = JubjubBls12::new(); tests::test_suite::(¶ms); @@ -464,3 +503,35 @@ fn test_jubjub_bls12() { assert!(p == q); } + +#[test] +#[should_panic(expected = "Linear relation between generators!")] +fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { + let params = JubjubBls12::new(); + + let mut pedersen_hash_generators: Vec> = vec![]; + + use byteorder::{LittleEndian, WriteBytesExt}; + + for m in 0..5 { + let mut segment_number = [0u8; 4]; + (&mut segment_number[0..4]) + .write_u32::(m) + .unwrap(); + + let p = JubjubBls12::find_group_hash( + &segment_number, + constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, + ¶ms, + ); + pedersen_hash_generators.push(p); + } + + let p1 = pedersen_hash_generators[0].clone(); + let p2 = pedersen_hash_generators[1].clone(); + + //test for linear relation + pedersen_hash_generators.push(p1.add(&p2, ¶ms)); + + JubjubBls12::check_consistency_of_pedersen_hash_generators(¶ms, &pedersen_hash_generators); +} diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index e0bc4bf..4b56802 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -1,4 +1,6 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{BitIterator, Field, PrimeField}; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; @@ -46,11 +48,10 @@ impl PartialEq for Point { } impl Point { - pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option { + pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption { // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) - let mut x2 = x; - x2.square(); + let mut x2 = x.square(); let mut rhs = x2; rhs.mul_assign(params.montgomery_a()); @@ -58,21 +59,18 @@ impl Point { x2.mul_assign(&x); rhs.add_assign(&x2); - match rhs.sqrt() { - Some(mut y) => { - if y.into_repr().is_odd() != sign { - y.negate(); - } - - return Some(Point { - x: x, - y: y, - infinity: false, - _marker: PhantomData, - }); + rhs.sqrt().map(|mut y| { + if y.is_odd() != sign { + y = y.neg(); } - None => None, - } + + Point { + x, + y, + infinity: false, + _marker: PhantomData, + } + }) } /// This guarantees the point is in the prime order subgroup @@ -88,9 +86,9 @@ impl Point { let x = E::Fr::random(rng); let sign = rng.next_u32() % 2 != 0; - match Self::get_for_x(x, sign, params) { - Some(p) => return p, - None => {} + let p = Self::get_for_x(x, sign, params); + if p.is_some().into() { + return p.unwrap(); } } } @@ -99,7 +97,7 @@ impl Point { impl Point { /// Convert from an Edwards point pub fn from_edwards(e: &edwards::Point, params: &E::Params) -> Self { - let (x, y) = e.into_xy(); + let (x, y) = e.to_xy(); if y == E::Fr::one() { // The only solution for y = 1 is x = 0. (0, 1) is @@ -140,11 +138,11 @@ impl Point { { let mut tmp = E::Fr::one(); tmp.sub_assign(&y); - u.mul_assign(&tmp.inverse().unwrap()) + u.mul_assign(&tmp.invert().unwrap()) } let mut v = u; - v.mul_assign(&x.inverse().unwrap()); + v.mul_assign(&x.invert().unwrap()); // Scale it into the correct curve constants v.mul_assign(params.scale()); @@ -178,7 +176,7 @@ impl Point { } } - pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> { + pub fn to_xy(&self) -> Option<(E::Fr, E::Fr)> { if self.infinity { None } else { @@ -190,7 +188,7 @@ impl Point { pub fn negate(&self) -> Self { let mut p = self.clone(); - p.y.negate(); + p.y = p.y.neg(); p } @@ -214,26 +212,24 @@ impl Point { let mut delta = E::Fr::one(); { - let mut tmp = params.montgomery_a().clone(); + let mut tmp = *params.montgomery_a(); tmp.mul_assign(&self.x); - tmp.double(); + tmp = tmp.double(); delta.add_assign(&tmp); } { - let mut tmp = self.x; - tmp.square(); + let mut tmp = self.x.square(); delta.add_assign(&tmp); - tmp.double(); + tmp = tmp.double(); delta.add_assign(&tmp); } { - let mut tmp = self.y; - tmp.double(); - delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero")); + let tmp = self.y.double(); + // y is nonzero so this must be nonzero + delta.mul_assign(&tmp.invert().unwrap()); } - let mut x3 = delta; - x3.square(); + let mut x3 = delta.square(); x3.sub_assign(params.montgomery_a()); x3.sub_assign(&self.x); x3.sub_assign(&self.x); @@ -242,7 +238,7 @@ impl Point { y3.sub_assign(&self.x); y3.mul_assign(&delta); y3.add_assign(&self.y); - y3.negate(); + y3 = y3.neg(); Point { x: x3, @@ -276,14 +272,11 @@ impl Point { { let mut tmp = other.x; tmp.sub_assign(&self.x); - delta.mul_assign( - &tmp.inverse() - .expect("self.x != other.x, so this must be nonzero"), - ); + // self.x != other.x, so this must be nonzero + delta.mul_assign(&tmp.invert().unwrap()); } - let mut x3 = delta; - x3.square(); + let mut x3 = delta.square(); x3.sub_assign(params.montgomery_a()); x3.sub_assign(&self.x); x3.sub_assign(&other.x); @@ -292,7 +285,7 @@ impl Point { y3.sub_assign(&self.x); y3.mul_assign(&delta); y3.add_assign(&self.y); - y3.negate(); + y3 = y3.neg(); Point { x: x3, @@ -311,7 +304,7 @@ impl Point { let mut res = Self::zero(); - for b in BitIterator::new(scalar.into()) { + for b in BitIterator::::new(scalar.into()) { res = res.double(params); if b { diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index 1b4f8d1..a8b5274 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -1,6 +1,7 @@ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; -use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Endianness, Field, PrimeField}; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -19,11 +20,9 @@ pub fn test_suite(params: &E::Params) { } fn is_on_mont_curve>(x: E::Fr, y: E::Fr, params: &P) -> bool { - let mut lhs = y; - lhs.square(); + let lhs = y.square(); - let mut x2 = x; - x2.square(); + let x2 = x.square(); let mut x3 = x2; x3.mul_assign(&x); @@ -41,11 +40,9 @@ fn is_on_twisted_edwards_curve>( y: E::Fr, params: &P, ) -> bool { - let mut x2 = x; - x2.square(); + let x2 = x.square(); - let mut y2 = y; - y2.square(); + let y2 = y.square(); // -x^2 + y^2 let mut lhs = y2; @@ -119,13 +116,13 @@ fn test_mul_associativity(params: &E::Params) { assert!(res2 == res3); assert!(res3 == res4); - let (x, y) = res1.into_xy(); + let (x, y) = res1.to_xy(); assert!(is_on_twisted_edwards_curve(x, y, params)); - let (x, y) = res2.into_xy(); + let (x, y) = res2.to_xy(); assert!(is_on_twisted_edwards_curve(x, y, params)); - let (x, y) = res3.into_xy(); + let (x, y) = res3.to_xy(); assert!(is_on_twisted_edwards_curve(x, y, params)); } } @@ -237,8 +234,10 @@ fn test_get_for(params: &E::Params) { let y = E::Fr::random(rng); let sign = rng.next_u32() % 2 == 1; - if let Some(mut p) = edwards::Point::::get_for_y(y, sign, params) { - assert!(p.into_xy().0.into_repr().is_odd() == sign); + let p = edwards::Point::::get_for_y(y, sign, params); + if bool::from(p.is_some()) { + let mut p = p.unwrap(); + assert!(p.to_xy().0.is_odd() == sign); p = p.negate(); assert!(edwards::Point::::get_for_y(y, !sign, params).unwrap() == p); } @@ -274,12 +273,12 @@ fn test_rand(params: &E::Params) { let e = edwards::Point::::rand(rng, params); { - let (x, y) = p.into_xy().unwrap(); + let (x, y) = p.to_xy().unwrap(); assert!(is_on_mont_curve(x, y, params)); } { - let (x, y) = e.into_xy(); + let (x, y) = e.to_xy(); assert!(is_on_twisted_edwards_curve(x, y, params)); } } @@ -309,23 +308,19 @@ fn test_back_and_forth(params: &E::Params) { fn test_jubjub_params(params: &E::Params) { // a = -1 - let mut a = E::Fr::one(); - a.negate(); + let a = E::Fr::one().neg(); { // Check that 2A is consistent with A - let mut tmp = *params.montgomery_a(); - tmp.double(); - - assert_eq!(&tmp, params.montgomery_2a()); + assert_eq!(¶ms.montgomery_a().double(), params.montgomery_2a()); } { // The twisted Edwards addition law is complete when d is nonsquare // and a is square. - assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue); - assert!(a.legendre() == LegendreSymbol::QuadraticResidue); + assert!(bool::from(params.edwards_d().sqrt().is_none())); + assert!(bool::from(a.sqrt().is_some())); } { @@ -335,38 +330,37 @@ fn test_jubjub_params(params: &E::Params) { let mut tmp = *params.edwards_d(); // 1 / d is nonsquare - assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); // tmp = -d - tmp.negate(); + tmp = tmp.neg(); // -d is nonsquare - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); // 1 / -d is nonsquare - assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); } { // Check that A^2 - 4 is nonsquare: - let mut tmp = params.montgomery_a().clone(); - tmp.square(); + let mut tmp = params.montgomery_a().square(); tmp.sub_assign(&E::Fr::from_str("4").unwrap()); - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); } { // Check that A - 2 is nonsquare: let mut tmp = params.montgomery_a().clone(); tmp.sub_assign(&E::Fr::from_str("2").unwrap()); - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); } { // Check the validity of the scaling factor let mut tmp = a; tmp.sub_assign(¶ms.edwards_d()); - tmp = tmp.inverse().unwrap(); + tmp = tmp.invert().unwrap(); tmp.mul_assign(&E::Fr::from_str("4").unwrap()); tmp = tmp.sqrt().unwrap(); assert_eq!(&tmp, params.scale()); @@ -376,32 +370,55 @@ fn test_jubjub_params(params: &E::Params) { // Check that the number of windows per generator // in the Pedersen hash does not allow for collisions - let mut cur = E::Fs::one().into_repr(); + let mut cur = E::Fs::one(); - let mut max = E::Fs::char(); - { - max.sub_noborrow(&E::Fs::one().into_repr()); - max.div2(); - } + let max = { + // Grab char - 1 in little endian. + let mut tmp = (-E::Fs::one()).to_repr(); + ::ReprEndianness::toggle_little_endian(&mut tmp); - let mut pacc = E::Fs::zero().into_repr(); - let mut nacc = E::Fs::char(); + // Shift right by 1 bit. + let mut borrow = 0; + for b in tmp.as_mut().iter_mut().rev() { + let new_borrow = *b & 1; + *b = (borrow << 7) | (*b >> 1); + borrow = new_borrow; + } + + // Turns out we want this in little endian! + tmp + }; + + let mut pacc = E::Fs::zero(); + let mut nacc = E::Fs::zero(); for _ in 0..params.pedersen_hash_chunks_per_generator() { // tmp = cur * 4 - let mut tmp = cur; - tmp.mul2(); - tmp.mul2(); + let tmp = cur.double().double(); - pacc.add_nocarry(&tmp); - nacc.sub_noborrow(&tmp); + pacc += &tmp; + nacc -= &tmp; // The first subtraction wraps intentionally. - assert!(pacc < max); - assert!(pacc < nacc); + let mut pacc_repr = pacc.to_repr(); + let mut nacc_repr = nacc.to_repr(); + ::ReprEndianness::toggle_little_endian(&mut pacc_repr); + ::ReprEndianness::toggle_little_endian(&mut nacc_repr); + + fn less_than(val: &[u8], bound: &[u8]) -> bool { + for (a, b) in val.iter().rev().zip(bound.iter().rev()) { + if a < b { + return true; + } + } + + false + } + assert!(less_than(pacc_repr.as_ref(), max.as_ref())); + assert!(less_than(pacc_repr.as_ref(), nacc_repr.as_ref())); // cur = cur * 16 for _ in 0..4 { - cur.mul2(); + cur = cur.double(); } } } diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index ad86059..50c2de5 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -1,20 +1,22 @@ //! Sapling key components. //! -//! Implements section 4.2.2 of the Zcash Protocol Specification. +//! Implements [section 4.2.2] of the Zcash Protocol Specification. +//! +//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents use crate::{ jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown}, primitives::{ProofGenerationKey, ViewingKey}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use std::io::{self, Read, Write}; -pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; +pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; /// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) pub fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bHash { - prf_expand_vec(sk, &vec![t]) + prf_expand_vec(sk, &[t]) } pub fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bHash { @@ -69,14 +71,14 @@ impl ExpandedSpendingKey { pub fn read(mut reader: R) -> io::Result { let mut ask_repr = ::Repr::default(); - ask_repr.read_le(&mut reader)?; + reader.read_exact(ask_repr.as_mut())?; let ask = E::Fs::from_repr(ask_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?; let mut nsk_repr = ::Repr::default(); - nsk_repr.read_le(&mut reader)?; + reader.read_exact(nsk_repr.as_mut())?; let nsk = E::Fs::from_repr(nsk_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; let mut ovk = [0; 32]; reader.read_exact(&mut ovk)?; @@ -89,8 +91,8 @@ impl ExpandedSpendingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.ask.into_repr().write_le(&mut writer)?; - self.nsk.into_repr().write_le(&mut writer)?; + writer.write_all(self.ask.to_repr().as_ref())?; + writer.write_all(self.nsk.to_repr().as_ref())?; writer.write_all(&self.ovk.0)?; Ok(()) @@ -111,7 +113,7 @@ impl Clone for FullViewingKey { ak: self.vk.ak.clone(), nk: self.vk.nk.clone(), }, - ovk: self.ovk.clone(), + ovk: self.ovk, } } } diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 1384117..2f6ce5b 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -1,19 +1,10 @@ -#[macro_use] -extern crate lazy_static; +//! *General Zcash primitives.* +//! +//! `zcash_primitives` is a library that provides the core structs and functions necessary +//! for working with Zcash. -extern crate aes; -extern crate blake2b_simd; -extern crate blake2s_simd; -extern crate byteorder; -extern crate crypto_api_chachapoly; -extern crate ff; -extern crate fpe; -extern crate hex; -extern crate pairing; -extern crate rand; -extern crate rand_core; -extern crate rand_os; -extern crate sha2; +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] #[cfg(feature = "transparent-inputs")] extern crate ripemd160; @@ -27,8 +18,10 @@ extern crate hex_literal; #[cfg(test)] extern crate rand_xorshift; +use lazy_static::lazy_static; pub mod block; +pub mod consensus; pub mod constants; pub mod group_hash; pub mod jubjub; @@ -49,7 +42,7 @@ pub mod zip32; #[cfg(test)] mod test_vectors; -use jubjub::JubjubBls12; +use crate::jubjub::JubjubBls12; lazy_static! { pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index aa1596d..9aa3ed5 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -5,8 +5,8 @@ use std::collections::VecDeque; use std::io::{self, Read, Write}; use std::iter; -use sapling::SAPLING_COMMITMENT_TREE_DEPTH; -use serialize::{Optional, Vector}; +use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH; +use crate::serialize::{Optional, Vector}; /// A hashable node within a Merkle tree. pub trait Hashable: Clone + Copy { @@ -17,13 +17,13 @@ pub trait Hashable: Clone + Copy { fn write(&self, writer: W) -> io::Result<()>; /// Returns the parent node within the tree of the two given nodes. - fn combine(usize, &Self, &Self) -> Self; + fn combine(_: usize, _: &Self, _: &Self) -> Self; /// Returns a blank leaf node. fn blank() -> Self; /// Returns the empty root for the given depth. - fn empty_root(usize) -> Self; + fn empty_root(_: usize) -> Self; } struct PathFiller { @@ -164,14 +164,8 @@ impl CommitmentTree { // - Empty leaves are used as needed. let leaf_root = Node::combine( 0, - &match self.left { - Some(node) => node, - None => filler.next(0), - }, - &match self.right { - Some(node) => node, - None => filler.next(0), - }, + &self.left.unwrap_or_else(|| filler.next(0)), + &self.right.unwrap_or_else(|| filler.next(0)), ); // 2) Hash in parents up to the currently-filled depth. @@ -202,27 +196,28 @@ impl CommitmentTree { /// ``` /// extern crate ff; /// extern crate pairing; -/// extern crate rand_os; +/// extern crate rand_core; /// extern crate zcash_primitives; /// /// use ff::{Field, PrimeField}; /// use pairing::bls12_381::Fr; -/// use rand_os::OsRng; +/// use rand_core::OsRng; /// use zcash_primitives::{ /// merkle_tree::{CommitmentTree, IncrementalWitness}, /// sapling::Node, /// }; /// -/// let mut rng = OsRng::new().unwrap(); +/// let mut rng = OsRng; +/// /// let mut tree = CommitmentTree::::new(); /// -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); +/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); +/// tree.append(Node::new(Fr::random(&mut rng).to_repr())); /// let mut witness = IncrementalWitness::from_tree(&tree); /// assert_eq!(witness.position(), 1); /// assert_eq!(tree.root(), witness.root()); /// -/// let cmu = Node::new(Fr::random(&mut rng).into_repr()); +/// let cmu = Node::new(Fr::random(&mut rng).to_repr()); /// tree.append(cmu); /// witness.append(cmu); /// assert_eq!(tree.root(), witness.root()); @@ -380,19 +375,19 @@ impl IncrementalWitness { } /// Returns the current witness, or None if the tree is empty. - pub fn path(&self) -> Option> { + pub fn path(&self) -> Option> { self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH) } - fn path_inner(&self, depth: usize) -> Option> { + fn path_inner(&self, depth: usize) -> Option> { let mut filler = self.filler(); let mut auth_path = Vec::new(); if let Some(node) = self.tree.left { if self.tree.right.is_some() { - auth_path.push(Some((node, true))); + auth_path.push((node, true)); } else { - auth_path.push(Some((filler.next(0), false))); + auth_path.push((filler.next(0), false)); } } else { // Can't create an authentication path for the beginning of the tree @@ -401,41 +396,37 @@ impl IncrementalWitness { for (i, p) in self.tree.parents.iter().enumerate() { auth_path.push(match p { - Some(node) => Some((*node, true)), - None => Some((filler.next(i + 1), false)), + Some(node) => (*node, true), + None => (filler.next(i + 1), false), }); } for i in self.tree.parents.len()..(depth - 1) { - auth_path.push(Some((filler.next(i + 1), false))); + auth_path.push((filler.next(i + 1), false)); } assert_eq!(auth_path.len(), depth); - Some(CommitmentTreeWitness::from_path( - auth_path, - self.position() as u64, - )) + Some(MerklePath::from_path(auth_path, self.position() as u64)) } } -/// A witness to a path from a position in a particular commitment tree to the root of -/// that tree. +/// A path from a position in a particular commitment tree to the root of that tree. #[derive(Clone, Debug, PartialEq)] -pub struct CommitmentTreeWitness { - pub auth_path: Vec>, +pub struct MerklePath { + pub auth_path: Vec<(Node, bool)>, pub position: u64, } -impl CommitmentTreeWitness { - /// Constructs a witness directly from its path and position. - pub fn from_path(auth_path: Vec>, position: u64) -> Self { - CommitmentTreeWitness { +impl MerklePath { + /// Constructs a Merkle path directly from a path and position. + pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self { + MerklePath { auth_path, position, } } - /// Reads a witness from its serialized form. + /// Reads a Merkle path from its serialized form. pub fn from_slice(witness: &[u8]) -> Result { Self::from_slice_with_depth(witness, SAPLING_COMMITMENT_TREE_DEPTH) } @@ -449,46 +440,41 @@ impl CommitmentTreeWitness { witness = &witness[1..]; // Begin to construct the authentication path - let mut auth_path = vec![None; depth]; + let iter = witness.chunks_exact(33); + witness = iter.remainder(); // The vector works in reverse - for i in (0..depth).rev() { - // skip length of inner vector - if witness[0] != 32 { - // the length of a pedersen hash - return Err(()); - } - witness = &witness[1..]; - - // Grab the sibling node at this depth in the tree - let mut sibling = [0u8; 32]; - sibling.copy_from_slice(&witness[0..32]); - witness = &witness[32..]; - - // Sibling node should be an element of Fr - let sibling = match Node::read(&sibling[..]) { - Ok(p) => p, - Err(_) => return Err(()), - }; - - // Set the value in the auth path; we put false here - // for now (signifying the position bit) which we'll - // fill in later. - auth_path[i] = Some((sibling, false)); + let mut auth_path = iter + .rev() + .map(|bytes| { + // Length of inner vector should be the length of a Pedersen hash + if bytes[0] == 32 { + // Sibling node should be an element of Fr + Node::read(&bytes[1..]) + .map(|sibling| { + // Set the value in the auth path; we put false here + // for now (signifying the position bit) which we'll + // fill in later. + (sibling, false) + }) + .map_err(|_| ()) + } else { + Err(()) + } + }) + .collect::, _>>()?; + if auth_path.len() != depth { + return Err(()); } // Read the position from the witness - let position = match witness.read_u64::() { - Ok(pos) => pos, - Err(_) => return Err(()), - }; + let position = witness.read_u64::().map_err(|_| ())?; // Given the position, let's finish constructing the authentication // path let mut tmp = position; - for i in 0..depth { - auth_path[i].as_mut().map(|p| p.1 = (tmp & 1) == 1); - + for entry in auth_path.iter_mut() { + entry.1 = (tmp & 1) == 1; tmp >>= 1; } @@ -496,7 +482,7 @@ impl CommitmentTreeWitness { // have provided more information than they should have, indicating // a bug downstream if witness.is_empty() { - Ok(CommitmentTreeWitness { + Ok(MerklePath { auth_path, position, }) @@ -504,16 +490,30 @@ impl CommitmentTreeWitness { Err(()) } } + + /// Returns the root of the tree corresponding to this path applied to `leaf`. + pub fn root(&self, leaf: Node) -> Node { + self.auth_path + .iter() + .enumerate() + .fold( + leaf, + |root, (i, (p, leaf_is_on_right))| match leaf_is_on_right { + false => Node::combine(i, &root, p), + true => Node::combine(i, p, &root), + }, + ) + } } #[cfg(test)] mod tests { - use super::{CommitmentTree, CommitmentTreeWitness, Hashable, IncrementalWitness, PathFiller}; - use sapling::Node; + use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller}; + use crate::sapling::Node; - use ff::PrimeFieldRepr; use hex; use pairing::bls12_381::FrRepr; + use std::convert::TryInto; use std::io::{self, Read, Write}; const HEX_EMPTY_ROOTS: [&str; 33] = [ @@ -607,7 +607,7 @@ mod tests { self.0.root_inner(TESTING_DEPTH) } - fn path(&self) -> Option> { + fn path(&self) -> Option> { self.0.path_inner(TESTING_DEPTH) } } @@ -1012,17 +1012,16 @@ mod tests { assert_eq!(tree.size(), 0); let mut witnesses = vec![]; + let mut last_cm = None; let mut paths_i = 0; let mut witness_ser_i = 0; for i in 0..16 { - let mut cm = FrRepr::default(); - cm.read_le(&hex::decode(commitments[i]).unwrap()[..]) - .expect("length is 32 bytes"); + let cm = FrRepr(hex::decode(commitments[i]).unwrap()[..].try_into().unwrap()); let cm = Node::new(cm); // Witness here - witnesses.push(TestIncrementalWitness::from_tree(&tree)); + witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm)); // Now append a commitment to the tree assert!(tree.append(cm).is_ok()); @@ -1036,22 +1035,23 @@ mod tests { // Check serialization of tree assert_tree_ser_eq(&tree, tree_ser[i]); - let mut first = true; // The first witness can never form a path - for witness in witnesses.as_mut_slice() { + for (witness, leaf) in witnesses.as_mut_slice() { // Append the same commitment to all the witnesses assert!(witness.append(cm).is_ok()); - if first { - assert!(witness.path().is_none()); - } else { + if let Some(leaf) = leaf { let path = witness.path().expect("should be able to create a path"); - let expected = CommitmentTreeWitness::from_slice_with_depth( + let expected = MerklePath::from_slice_with_depth( &mut hex::decode(paths[paths_i]).unwrap(), TESTING_DEPTH, ) .unwrap(); assert_eq!(path, expected); + assert_eq!(path.root(*leaf), witness.root()); paths_i += 1; + } else { + // The first witness can never form a path + assert!(witness.path().is_none()); } // Check witness serialization @@ -1059,15 +1059,15 @@ mod tests { witness_ser_i += 1; assert_eq!(witness.root(), tree.root()); - - first = false; } + + last_cm = Some(cm); } // Tree should be full now let node = Node::blank(); assert!(tree.append(node).is_err()); - for witness in witnesses.as_mut_slice() { + for (witness, _) in witnesses.as_mut_slice() { assert!(witness.append(node).is_err()); } } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 5412945..1ad6cce 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -11,16 +11,17 @@ use crate::{ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr}; use rand_core::{CryptoRng, RngCore}; +use std::convert::TryInto; use std::fmt; use std::str; use crate::{keys::OutgoingViewingKey, JUBJUB}; -pub const KDF_SAPLING_PERSONALIZATION: &'static [u8; 16] = b"Zcash_SaplingKDF"; -pub const PRF_OCK_PERSONALIZATION: &'static [u8; 16] = b"Zcash_Derive_ock"; +pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; +pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; const COMPACT_NOTE_SIZE: usize = ( 1 + // version @@ -85,7 +86,7 @@ impl Default for Memo { impl PartialEq for Memo { fn eq(&self, rhs: &Memo) -> bool { - &self.0[..] == &rhs.0[..] + self.0[..] == rhs.0[..] } } @@ -106,11 +107,6 @@ impl Memo { } } - /// Returns a `Memo` containing the given string, or `None` if the string is too long. - pub fn from_str(memo: &str) -> Option { - Memo::from_bytes(memo.as_bytes()) - } - /// Returns the underlying bytes of the `Memo`. pub fn as_bytes(&self) -> &[u8] { &self.0[..] @@ -134,6 +130,15 @@ impl Memo { } } +impl str::FromStr for Memo { + type Err = (); + + /// Returns a `Memo` containing the given string, or an error if the string is too long. + fn from_str(memo: &str) -> Result { + Memo::from_bytes(memo.as_bytes()).ok_or(()) + } +} + pub fn generate_esk(rng: &mut R) -> Fs { // create random 64 byte buffer let mut buffer = [0u8; 64]; @@ -188,7 +193,7 @@ fn prf_ock( let mut ock_input = [0u8; 128]; ock_input[0..32].copy_from_slice(&ovk.0); cv.write(&mut ock_input[32..64]).unwrap(); - cmu.into_repr().write_le(&mut ock_input[64..96]).unwrap(); + ock_input[64..96].copy_from_slice(cmu.to_repr().as_ref()); epk.write(&mut ock_input[96..128]).unwrap(); Blake2bParams::new() @@ -210,12 +215,12 @@ fn prf_ock( /// ``` /// extern crate ff; /// extern crate pairing; -/// extern crate rand_os; +/// extern crate rand_core; /// extern crate zcash_primitives; /// /// use ff::Field; /// use pairing::bls12_381::Bls12; -/// use rand_os::OsRng; +/// use rand_core::OsRng; /// use zcash_primitives::{ /// jubjub::fs::Fs, /// keys::OutgoingViewingKey, @@ -228,10 +233,7 @@ fn prf_ock( /// /// let diversifier = Diversifier([0; 11]); /// let pk_d = diversifier.g_d::(&JUBJUB).unwrap(); -/// let to = PaymentAddress { -/// pk_d, -/// diversifier, -/// }; +/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); /// let ovk = OutgoingViewingKey([0; 32]); /// /// let value = 1000; @@ -290,22 +292,18 @@ impl SaplingNoteEncryption { /// Generates `encCiphertext` for this note. pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] { - let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d); + let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d()); let key = kdf_sapling(shared_secret, &self.epk); // Note plaintext encoding is defined in section 5.5 of the Zcash Protocol // Specification. let mut input = [0; NOTE_PLAINTEXT_SIZE]; input[0] = 1; - input[1..12].copy_from_slice(&self.to.diversifier.0); + input[1..12].copy_from_slice(&self.to.diversifier().0); (&mut input[12..20]) .write_u64::(self.note.value) .unwrap(); - self.note - .r - .into_repr() - .write_le(&mut input[20..COMPACT_NOTE_SIZE]) - .unwrap(); + input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.to_repr().as_ref()); input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; @@ -329,10 +327,7 @@ impl SaplingNoteEncryption { let mut input = [0u8; OUT_PLAINTEXT_SIZE]; self.note.pk_d.write(&mut input[0..32]).unwrap(); - self.esk - .into_repr() - .write_le(&mut input[32..OUT_PLAINTEXT_SIZE]) - .unwrap(); + input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref()); let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; assert_eq!( @@ -362,16 +357,18 @@ fn parse_note_plaintext_without_memo( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let rcm = Fs::from_repr(FsRepr( + plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let diversifier = Diversifier(d); let pk_d = diversifier .g_d::(&JUBJUB)? - .mul(ivk.into_repr(), &JUBJUB); + .mul(ivk.to_repr(), &JUBJUB); - let to = PaymentAddress { pk_d, diversifier }; + let to = PaymentAddress::from_parts(diversifier, pk_d)?; let note = to.create_note(v, rcm, &JUBJUB).unwrap(); if note.cm(&JUBJUB) != *cmu { @@ -482,9 +479,11 @@ pub fn try_sapling_output_recovery( .ok()? .as_prime_order(&JUBJUB)?; - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).ok()?; - let esk = Fs::from_repr(esk).ok()?; + let esk = Fs::from_repr(FsRepr( + op[32..OUT_PLAINTEXT_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let shared_secret = sapling_ka_agree(&esk, &pk_d); let key = kdf_sapling(shared_secret, &epk); @@ -514,9 +513,11 @@ pub fn try_sapling_output_recovery( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let rcm = Fs::from_repr(FsRepr( + plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"), + ))?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -524,14 +525,14 @@ pub fn try_sapling_output_recovery( let diversifier = Diversifier(d); if diversifier .g_d::(&JUBJUB)? - .mul(esk.into_repr(), &JUBJUB) + .mul(esk.to_repr(), &JUBJUB) != *epk { // Published epk doesn't match calculated epk return None; } - let to = PaymentAddress { pk_d, diversifier }; + let to = PaymentAddress::from_parts(diversifier, pk_d)?; let note = to.create_note(v, rcm, &JUBJUB).unwrap(); if note.cm(&JUBJUB) != *cmu { @@ -553,10 +554,12 @@ mod tests { primitives::{Diversifier, PaymentAddress, ValueCommitment}, }; use crypto_api_chachapoly::ChachaPolyIetf; - use ff::{Field, PrimeField, PrimeFieldRepr}; + use ff::{Field, PrimeField}; use pairing::bls12_381::{Bls12, Fr, FrRepr}; + use rand_core::OsRng; use rand_core::{CryptoRng, RngCore}; - use rand_os::OsRng; + use std::convert::TryInto; + use std::str::FromStr; use super::{ kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption, @@ -661,16 +664,18 @@ mod tests { 0x74, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68 ]) ); - assert!(Memo::from_str( - "thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \ - iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ - veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \ - looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \ - meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \ - but it's now a bit too long" - ) - .is_none()); + assert_eq!( + Memo::from_str( + "thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \ + iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ + veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \ + looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \ + meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \ + but it's now a bit too long" + ), + Err(()) + ); } #[test] @@ -691,10 +696,47 @@ mod tests { [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { - let diversifier = Diversifier([0; 11]); let ivk = Fs::random(&mut rng); + + let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(ivk, rng); + + assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some()); + assert!(try_sapling_compact_note_decryption( + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ) + .is_some()); + assert!(try_sapling_output_recovery( + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ) + .is_some()); + + (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) + } + + fn random_enc_ciphertext_with( + ivk: Fs, + mut rng: &mut R, + ) -> ( + OutgoingViewingKey, + Fs, + edwards::Point, + Fr, + edwards::Point, + [u8; ENC_CIPHERTEXT_SIZE], + [u8; OUT_CIPHERTEXT_SIZE], + ) { + let diversifier = Diversifier([0; 11]); let pk_d = diversifier.g_d::(&JUBJUB).unwrap().mul(ivk, &JUBJUB); - let pa = PaymentAddress { diversifier, pk_d }; + let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d); // Construct the value commitment for the proof instance let value = 100; @@ -715,24 +757,6 @@ mod tests { let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); - assert!(try_sapling_note_decryption(&ivk, epk, &cmu, &enc_ciphertext).is_some()); - assert!(try_sapling_compact_note_decryption( - &ivk, - epk, - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ) - .is_some()); - assert!(try_sapling_output_recovery( - &ovk, - &cv, - &cmu, - &epk, - &enc_ciphertext, - &out_ciphertext - ) - .is_some()); - ( ovk, ivk, @@ -768,9 +792,7 @@ mod tests { .as_prime_order(&JUBJUB) .unwrap(); - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).unwrap(); - let esk = Fs::from_repr(esk).unwrap(); + let esk = Fs::from_repr(FsRepr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())).unwrap(); let shared_secret = sapling_ka_agree(&esk, &pk_d); let key = kdf_sapling(shared_secret, &epk); @@ -1249,23 +1271,33 @@ mod tests { ); } + #[test] + fn recovery_with_invalid_pk_d() { + let mut rng = OsRng; + + let ivk = Fs::zero(); + let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(ivk, &mut rng); + + assert_eq!( + try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), + None + ); + } + #[test] fn test_vectors() { let test_vectors = crate::test_vectors::note_encryption::make_test_vectors(); macro_rules! read_fr { ($field:expr) => {{ - let mut repr = FrRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fr::from_repr(repr).unwrap() + Fr::from_repr(FrRepr($field[..].try_into().unwrap())).unwrap() }}; } macro_rules! read_fs { ($field:expr) => {{ - let mut repr = FsRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fs::from_repr(repr).unwrap() + Fs::from_repr(FsRepr($field[..].try_into().unwrap())).unwrap() }}; } @@ -1310,10 +1342,7 @@ mod tests { let ock = prf_ock(&ovk, &cv, &cmu, &epk); assert_eq!(ock.as_bytes(), tv.ock); - let to = PaymentAddress { - pk_d, - diversifier: Diversifier(tv.default_d), - }; + let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap(); assert_eq!(note.cm(&JUBJUB), cmu); diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index f21b17b..747ffcb 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -1,5 +1,9 @@ -use ff::{Field, PrimeField, PrimeFieldRepr}; -use jubjub::*; +//! Implementation of the Pedersen hash function used in Sapling. + +use crate::jubjub::*; +use byteorder::{ByteOrder, LittleEndian}; +use ff::{Endianness, Field, PrimeField}; +use std::ops::{AddAssign, Neg}; #[derive(Copy, Clone)] pub enum Personalization { @@ -55,14 +59,14 @@ where if a { tmp.add_assign(&cur); } - cur.double(); // 2^1 * cur + cur = cur.double(); // 2^1 * cur if b { tmp.add_assign(&cur); } // conditionally negate if c { - tmp.negate(); + tmp = tmp.neg(); } acc.add_assign(&tmp); @@ -72,9 +76,7 @@ where if chunks_remaining == 0 { break; } else { - cur.double(); // 2^2 * cur - cur.double(); // 2^3 * cur - cur.double(); // 2^4 * cur + cur = cur.double().double().double(); // 2^4 * cur } } @@ -84,19 +86,32 @@ where let mut table: &[Vec>] = &generators.next().expect("we don't have enough generators"); - let window = JubjubBls12::pedersen_hash_exp_window_size(); - let window_mask = (1 << window) - 1; + let window = JubjubBls12::pedersen_hash_exp_window_size() as usize; + let window_mask = (1u64 << window) - 1; - let mut acc = acc.into_repr(); + let mut acc = acc.to_repr(); + ::ReprEndianness::toggle_little_endian(&mut acc); + let num_limbs: usize = acc.as_ref().len() / 8; + let mut limbs = vec![0u64; num_limbs + 1]; + LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]); let mut tmp = edwards::Point::zero(); - while !acc.is_zero() { - let i = (acc.as_ref()[0] & window_mask) as usize; + let mut pos = 0; + while pos < E::Fs::NUM_BITS as usize { + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let i = (if bit_idx + window < 64 { + // This window's bits are contained in a single u64. + limbs[u64_idx] >> bit_idx + } else { + // Combine the current u64's bits with the bits from the next u64. + (limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx)) + } & window_mask) as usize; tmp = tmp.add(&table[0][i], params); - acc.shr(window); + pos += window; table = &table[1..]; } @@ -105,3 +120,44 @@ where result } + +#[cfg(test)] +pub mod test { + + use super::*; + use crate::test_vectors::pedersen_hash_vectors; + use pairing::bls12_381::Bls12; + + pub struct TestVector<'a> { + pub personalization: Personalization, + pub input_bits: Vec, + pub hash_x: &'a str, + pub hash_y: &'a str, + } + + #[test] + fn test_pedersen_hash_points() { + let test_vectors = pedersen_hash_vectors::get_vectors(); + + assert!(test_vectors.len() > 0); + + for v in test_vectors.iter() { + let params = &JubjubBls12::new(); + + let input_bools: Vec = v.input_bits.iter().map(|&i| i == 1).collect(); + + // The 6 bits prefix is handled separately + assert_eq!(v.personalization.get_bits(), &input_bools[..6]); + + let (x, y) = pedersen_hash::( + v.personalization, + input_bools.into_iter().skip(6), + params, + ) + .to_xy(); + + assert_eq!(x.to_string(), v.hash_x); + assert_eq!(y.to_string(), v.hash_y); + } + } +} diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index d1282b7..cd06a60 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -1,14 +1,16 @@ -use ff::{Field, PrimeField, PrimeFieldRepr}; +//! Structs for core Zcash primitives. -use constants; +use ff::{Field, PrimeField}; -use group_hash::group_hash; +use crate::constants; -use pedersen_hash::{pedersen_hash, Personalization}; +use crate::group_hash::group_hash; + +use crate::pedersen_hash::{pedersen_hash, Personalization}; use byteorder::{LittleEndian, WriteBytesExt}; -use jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; +use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; use blake2s_simd::Params as Blake2sParams; @@ -22,7 +24,7 @@ impl ValueCommitment { pub fn cm(&self, params: &E::Params) -> edwards::Point { params .generator(FixedGenerators::ValueCommitmentValue) - .mul(self.value, params) + .mul(E::Fs::from(self.value), params) .add( ¶ms .generator(FixedGenerators::ValueCommitmentRandomness) @@ -39,7 +41,7 @@ pub struct ProofGenerationKey { } impl ProofGenerationKey { - pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey { + pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey { ViewingKey { ak: self.ak.clone(), nk: params @@ -84,23 +86,20 @@ impl ViewingKey { h[31] &= 0b0000_0111; let mut e = ::Repr::default(); - e.read_le(&h[..]).unwrap(); + e.as_mut().copy_from_slice(&h[..]); E::Fs::from_repr(e).expect("should be a valid scalar") } - pub fn into_payment_address( + pub fn to_payment_address( &self, diversifier: Diversifier, params: &E::Params, ) -> Option> { - diversifier.g_d(params).map(|g_d| { + diversifier.g_d(params).and_then(|g_d| { let pk_d = g_d.mul(self.ivk(), params); - PaymentAddress { - pk_d: pk_d, - diversifier: diversifier, - } + PaymentAddress::from_parts(diversifier, pk_d) }) } } @@ -121,10 +120,16 @@ impl Diversifier { } } +/// A Sapling payment address. +/// +/// # Invariants +/// +/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub, +/// and not the identity). #[derive(Clone, Debug)] pub struct PaymentAddress { - pub pk_d: edwards::Point, - pub diversifier: Diversifier, + pk_d: edwards::Point, + diversifier: Diversifier, } impl PartialEq for PaymentAddress { @@ -134,6 +139,67 @@ impl PartialEq for PaymentAddress { } impl PaymentAddress { + /// Constructs a PaymentAddress from a diversifier and a Jubjub point. + /// + /// Returns None if `pk_d` is the identity. + pub fn from_parts( + diversifier: Diversifier, + pk_d: edwards::Point, + ) -> Option { + if pk_d == edwards::Point::zero() { + None + } else { + Some(PaymentAddress { pk_d, diversifier }) + } + } + + /// Constructs a PaymentAddress from a diversifier and a Jubjub point. + /// + /// Only for test code, as this explicitly bypasses the invariant. + #[cfg(test)] + pub(crate) fn from_parts_unchecked( + diversifier: Diversifier, + pk_d: edwards::Point, + ) -> Self { + PaymentAddress { pk_d, diversifier } + } + + /// Parses a PaymentAddress from bytes. + pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option { + let diversifier = { + let mut tmp = [0; 11]; + tmp.copy_from_slice(&bytes[0..11]); + Diversifier(tmp) + }; + // Check that the diversifier is valid + if diversifier.g_d::(params).is_none() { + return None; + } + + edwards::Point::::read(&bytes[11..43], params) + .ok()? + .as_prime_order(params) + .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d)) + } + + /// Returns the byte encoding of this `PaymentAddress`. + pub fn to_bytes(&self) -> [u8; 43] { + let mut bytes = [0; 43]; + bytes[0..11].copy_from_slice(&self.diversifier.0); + self.pk_d.write(&mut bytes[11..]).unwrap(); + bytes + } + + /// Returns the [`Diversifier`] for this `PaymentAddress`. + pub fn diversifier(&self) -> &Diversifier { + &self.diversifier + } + + /// Returns `pk_d` for this `PaymentAddress`. + pub fn pk_d(&self) -> &edwards::Point { + &self.pk_d + } + pub fn g_d(&self, params: &E::Params) -> Option> { self.diversifier.g_d(params) } @@ -145,9 +211,9 @@ impl PaymentAddress { params: &E::Params, ) -> Option> { self.g_d(params).map(|g_d| Note { - value: value, + value, r: randomness, - g_d: g_d, + g_d, pk_d: self.pk_d.clone(), }) } @@ -225,7 +291,7 @@ impl Note { let rho = self.cm_full_point(params).add( ¶ms .generator(FixedGenerators::NullifierPosition) - .mul(position, params), + .mul(E::Fs::from(position), params), params, ); @@ -245,6 +311,6 @@ impl Note { pub fn cm(&self, params: &E::Params) -> E::Fr { // The commitment is in the prime order subgroup, so mapping the // commitment to the x-coordinate is an injective encoding. - self.cm_full_point(params).into_xy().0 + self.cm_full_point(params).to_xy().0 } } diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 7c66737..932573d 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -7,7 +7,7 @@ use crate::{ use pairing::bls12_381::{Bls12, Fr}; use crate::{ - merkle_tree::CommitmentTreeWitness, + merkle_tree::MerklePath, redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, @@ -35,7 +35,7 @@ pub trait TxProver { ar: Fs, value: u64, anchor: Fr, - witness: CommitmentTreeWitness, + merkle_path: MerklePath, ) -> Result< ( [u8; GROTH_PROOF_SIZE], @@ -74,7 +74,7 @@ pub trait TxProver { pub(crate) mod mock { use ff::Field; use pairing::bls12_381::{Bls12, Fr}; - use rand_os::OsRng; + use rand_core::OsRng; use crate::{ jubjub::{edwards, fs::Fs, FixedGenerators, Unknown}, @@ -82,7 +82,7 @@ pub(crate) mod mock { }; use crate::{ - merkle_tree::CommitmentTreeWitness, + merkle_tree::MerklePath, redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, @@ -108,7 +108,7 @@ pub(crate) mod mock { ar: Fs, value: u64, _anchor: Fr, - _witness: CommitmentTreeWitness, + _merkle_path: MerklePath, ) -> Result< ( [u8; GROTH_PROOF_SIZE], diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index 370f63d..c8088f6 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -1,28 +1,26 @@ -//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. -//! See section 5.4.6 of the Sapling protocol specification. +//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub +//! curve. +//! +//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown}; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::{Field, PrimeField}; use rand_core::RngCore; use std::io::{self, Read, Write}; +use std::ops::{AddAssign, MulAssign, Neg}; -use util::hash_to_scalar; +use crate::util::hash_to_scalar; -fn read_scalar(reader: R) -> io::Result { +fn read_scalar(mut reader: R) -> io::Result { let mut s_repr = ::Repr::default(); - s_repr.read_le(reader)?; + reader.read_exact(s_repr.as_mut())?; - match E::Fs::from_repr(s_repr) { - Ok(s) => Ok(s), - Err(_) => Err(io::Error::new( - io::ErrorKind::InvalidInput, - "scalar is not in field", - )), - } + E::Fs::from_repr(s_repr) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) } -fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { - s.into_repr().write_le(writer) +fn write_scalar(s: &E::Fs, mut writer: W) -> io::Result<()> { + writer.write_all(s.to_repr().as_ref()) } fn h_star(a: &[u8], b: &[u8]) -> E::Fs { @@ -191,7 +189,7 @@ pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>( let z = E::Fs::random(rng); s.mul_assign(&z); - s.negate(); + s = s.neg(); r = r.mul(z, params); diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 02282d3..0251803 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -5,14 +5,15 @@ use crate::{ pedersen_hash::{pedersen_hash, Personalization}, primitives::Note, }; -use ff::{BitIterator, PrimeField, PrimeFieldRepr}; +use ff::{BitIterator, PrimeField}; +use lazy_static::lazy_static; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use rand_core::{CryptoRng, RngCore}; use std::io::{self, Read, Write}; use crate::merkle_tree::Hashable; use crate::redjubjub::{PrivateKey, PublicKey, Signature}; -use JUBJUB; +use crate::JUBJUB; pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; @@ -20,7 +21,7 @@ pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let lhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(lhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { *a = b; } tmp @@ -28,7 +29,7 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let rhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(rhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(rhs)) { *a = b; } tmp @@ -37,14 +38,14 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { pedersen_hash::( Personalization::MerkleTree(depth), lhs.iter() - .map(|&x| x) + .copied() .take(Fr::NUM_BITS as usize) - .chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)), + .chain(rhs.iter().copied().take(Fr::NUM_BITS as usize)), &JUBJUB, ) - .into_xy() + .to_xy() .0 - .into_repr() + .to_repr() } /// A node within the Sapling commitment tree. @@ -61,13 +62,13 @@ impl Node { impl Hashable for Node { fn read(mut reader: R) -> io::Result { - let mut repr = FrRepr::default(); - repr.read_le(&mut reader)?; + let mut repr = FrRepr([0; 32]); + reader.read_exact(&mut repr.0)?; Ok(Node::new(repr)) } fn write(&self, mut writer: W) -> io::Result<()> { - self.repr.write_le(&mut writer) + writer.write_all(self.repr.as_ref()) } fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self { @@ -78,7 +79,7 @@ impl Hashable for Node { fn blank() -> Self { Node { - repr: Note::::uncommitted().into_repr(), + repr: Note::::uncommitted().to_repr(), } } diff --git a/zcash_primitives/src/serialize.rs b/zcash_primitives/src/serialize.rs index 41778dc..4e0fb93 100644 --- a/zcash_primitives/src/serialize.rs +++ b/zcash_primitives/src/serialize.rs @@ -70,7 +70,7 @@ impl Vector { F: Fn(&mut R) -> io::Result, { let count = CompactSize::read(&mut reader)?; - (0..count).into_iter().map(|_| func(&mut reader)).collect() + (0..count).map(|_| func(&mut reader)).collect() } pub fn write(mut writer: W, vec: &[E], func: F) -> io::Result<()> diff --git a/zcash_primitives/src/test_vectors.rs b/zcash_primitives/src/test_vectors.rs index 403fbc9..1347b2d 100644 --- a/zcash_primitives/src/test_vectors.rs +++ b/zcash_primitives/src/test_vectors.rs @@ -1 +1,2 @@ pub(crate) mod note_encryption; +pub(crate) mod pedersen_hash_vectors; diff --git a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs new file mode 100644 index 0000000..ba4f2a8 --- /dev/null +++ b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs @@ -0,0 +1,715 @@ +//! Test vectors from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py + +use crate::pedersen_hash::{test::TestVector, Personalization}; + +pub fn get_vectors<'a>() -> Vec> { + return vec![ + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![1, 1, 1, 1, 1, 1], + hash_x: "Fr(0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b)", + hash_y: "Fr(0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![1, 1, 1, 1, 1, 1, 0], + hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)", + hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![1, 1, 1, 1, 1, 1, 1], + hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", + hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0], + hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", + hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + ], + hash_x: "Fr(0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f)", + hash_y: "Fr(0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, + ], + hash_x: "Fr(0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd)", + hash_y: "Fr(0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, + ], + hash_x: "Fr(0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40)", + hash_y: "Fr(0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, + 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + ], + hash_x: "Fr(0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27)", + hash_y: "Fr(0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, + ], + hash_x: "Fr(0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e)", + hash_y: "Fr(0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, + 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, + 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, + 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, + ], + hash_x: "Fr(0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f)", + hash_y: "Fr(0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, + 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, + 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, + ], + hash_x: "Fr(0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7)", + hash_y: "Fr(0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![0, 0, 0, 0, 0, 0], + hash_x: "Fr(0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d)", + hash_y: "Fr(0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![0, 0, 0, 0, 0, 0, 0], + hash_x: "Fr(0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea)", + hash_y: "Fr(0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![0, 0, 0, 0, 0, 0, 1], + hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", + hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0], + hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", + hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, + 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, + ], + hash_x: "Fr(0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd)", + hash_y: "Fr(0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, + ], + hash_x: "Fr(0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555)", + hash_y: "Fr(0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, + ], + hash_x: "Fr(0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d)", + hash_y: "Fr(0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, + 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, + 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, + ], + hash_x: "Fr(0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85)", + hash_y: "Fr(0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, + 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, + 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, + 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 0, + ], + hash_x: "Fr(0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68)", + hash_y: "Fr(0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, + 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, + 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, + ], + hash_x: "Fr(0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37)", + hash_y: "Fr(0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, + 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, + ], + hash_x: "Fr(0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05)", + hash_y: "Fr(0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![0, 1, 0, 0, 0, 1], + hash_x: "Fr(0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a)", + hash_y: "Fr(0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![0, 1, 0, 0, 0, 1, 0], + hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)", + hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![0, 1, 0, 0, 0, 1, 1], + hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", + hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0], + hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", + hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, + ], + hash_x: "Fr(0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2)", + hash_y: "Fr(0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, + ], + hash_x: "Fr(0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb)", + hash_y: "Fr(0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, + ], + hash_x: "Fr(0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2)", + hash_y: "Fr(0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, + 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + ], + hash_x: "Fr(0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc)", + hash_y: "Fr(0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, + 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, + 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 1, + ], + hash_x: "Fr(0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda)", + hash_y: "Fr(0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, + 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, + 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, + 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, + ], + hash_x: "Fr(0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666)", + hash_y: "Fr(0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289)", + }, + TestVector { + personalization: Personalization::MerkleTree(34), + input_bits: vec![ + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, + 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, + 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, + 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, + ], + hash_x: "Fr(0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7)", + hash_y: "Fr(0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad)", + }, + TestVector { + personalization: Personalization::MerkleTree(27), + input_bits: vec![ + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + ], + hash_x: "Fr(0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4)", + hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + }, + TestVector { + personalization: Personalization::MerkleTree(36), + input_bits: vec![ + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + ], + hash_x: "Fr(0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024)", + hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + }, + TestVector { + personalization: Personalization::MerkleTree(0), + input_bits: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + hash_x: "Fr(0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd)", + hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + }, + TestVector { + personalization: Personalization::NoteCommitment, + input_bits: vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ], + hash_x: "Fr(0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d)", + hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + }, + ]; +} diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 2d186df..44318e0 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -1,5 +1,6 @@ //! Structs for building transactions. +use crate::zip32::ExtendedSpendingKey; use crate::{ jubjub::fs::Fs, primitives::{Diversifier, Note, PaymentAddress}, @@ -7,14 +8,12 @@ use crate::{ use ff::Field; use pairing::bls12_381::{Bls12, Fr}; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; -use std::error; -use std::fmt; -use zip32::ExtendedSpendingKey; use crate::{ + consensus, keys::OutgoingViewingKey, legacy::TransparentAddress, - merkle_tree::{CommitmentTreeWitness, IncrementalWitness}, + merkle_tree::MerklePath, note_encryption::{generate_esk, Memo, SaplingNoteEncryption}, prover::TxProver, redjubjub::PrivateKey, @@ -45,38 +44,16 @@ pub enum Error { ChangeIsNegative(Amount), InvalidAddress, InvalidAmount, - InvalidWitness, NoChangeAddress, SpendProof, } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::AnchorMismatch => { - write!(f, "Anchor mismatch (anchors for all spends must be equal)") - } - Error::BindingSig => write!(f, "Failed to create bindingSig"), - Error::ChangeIsNegative(amount) => { - write!(f, "Change is negative ({:?} zatoshis)", amount) - } - Error::InvalidAddress => write!(f, "Invalid address"), - Error::InvalidAmount => write!(f, "Invalid amount"), - Error::InvalidWitness => write!(f, "Invalid note witness"), - Error::NoChangeAddress => write!(f, "No change address specified or discoverable"), - Error::SpendProof => write!(f, "Failed to create Sapling spend proof"), - } - } -} - -impl error::Error for Error {} - struct SpendDescriptionInfo { extsk: ExtendedSpendingKey, diversifier: Diversifier, note: Note, alpha: Fs, - witness: CommitmentTreeWitness, + merkle_path: MerklePath, } pub struct SaplingOutput { @@ -106,7 +83,7 @@ impl SaplingOutput { let note = Note { g_d, - pk_d: to.pk_d.clone(), + pk_d: to.pk_d().clone(), value: value.into(), r: rcm, }; @@ -187,7 +164,38 @@ impl Default for TransparentInputs { struct TransparentInputs; impl TransparentInputs { - fn input_sum(&self) -> Amount { + #[cfg(feature = "transparent-inputs")] + fn push( + &mut self, + mtx: &mut TransactionData, + sk: secp256k1::SecretKey, + utxo: OutPoint, + coin: TxOut, + ) -> Result<(), Error> { + if coin.value.is_negative() { + return Err(Error::InvalidAmount); + } + + let pubkey = secp256k1::PublicKey::from_secret_key(&self.secp, &sk).serialize(); + match coin.script_pubkey.address() { + Some(TransparentAddress::PublicKey(hash)) => { + use ripemd160::Ripemd160; + use sha2::{Digest, Sha256}; + + if &hash[..] != &Ripemd160::digest(&Sha256::digest(&pubkey))[..] { + return Err(Error::InvalidAddress); + } + } + _ => return Err(Error::InvalidAddress), + } + + mtx.vin.push(TxIn::new(utxo)); + self.inputs.push(TransparentInputInfo { sk, pubkey, coin }); + + Ok(()) + } + + fn value_sum(&self) -> Amount { #[cfg(feature = "transparent-inputs")] { self.inputs @@ -201,6 +209,36 @@ impl TransparentInputs { Amount::zero() } } + + #[cfg(feature = "transparent-inputs")] + fn apply_signatures( + &self, + mtx: &mut TransactionData, + consensus_branch_id: consensus::BranchId, + ) { + let mut sighash = [0u8; 32]; + for (i, info) in self.inputs.iter().enumerate() { + sighash.copy_from_slice(&signature_hash_data( + mtx, + consensus_branch_id, + SIGHASH_ALL, + Some((i, &info.coin.script_pubkey, info.coin.value)), + )); + + let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); + let sig = self.secp.sign(&msg, &info.sk); + + // Signature has to have "SIGHASH_ALL" appended to it + let mut sig_bytes: Vec = sig.serialize_der()[..].to_vec(); + sig_bytes.extend(&[SIGHASH_ALL as u8]); + + // P2PKH scriptSig + mtx.vin[i].script_sig = Script::default() << &sig_bytes[..] << &info.pubkey[..]; + } + } + + #[cfg(not(feature = "transparent-inputs"))] + fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {} } /// Metadata about a transaction created by a [`Builder`]. @@ -226,7 +264,7 @@ impl TransactionMetadata { /// they added (via the first call to [`Builder::add_sapling_spend`]) is the first /// [`SpendDescription`] in the transaction. pub fn spend_index(&self, n: usize) -> Option { - self.spend_indices.get(n).map(|i| *i) + self.spend_indices.get(n).copied() } /// Returns the index within the transaction of the [`OutputDescription`] corresponding @@ -237,7 +275,7 @@ impl TransactionMetadata { /// they added (via the first call to [`Builder::add_sapling_output`]) is the first /// [`OutputDescription`] in the transaction. pub fn output_index(&self, n: usize) -> Option { - self.output_indices.get(n).map(|i| *i) + self.output_indices.get(n).copied() } } @@ -249,7 +287,7 @@ pub struct Builder { anchor: Option, spends: Vec, outputs: Vec, - legacy: TransparentInputs, + transparent_inputs: TransparentInputs, change_address: Option<(OutgoingViewingKey, PaymentAddress)>, } @@ -289,32 +327,32 @@ impl Builder { anchor: None, spends: vec![], outputs: vec![], - legacy: TransparentInputs::default(), + transparent_inputs: TransparentInputs::default(), change_address: None, } } /// Adds a Sapling note to be spent in this transaction. /// - /// Returns an error if the given witness does not have the same anchor as previous - /// witnesses, or has no path. + /// Returns an error if the given Merkle path does not have the same anchor as the + /// paths for previous Sapling notes. pub fn add_sapling_spend( &mut self, extsk: ExtendedSpendingKey, diversifier: Diversifier, note: Note, - witness: IncrementalWitness, + merkle_path: MerklePath, ) -> Result<(), Error> { // Consistency check: all anchors must equal the first one + let cm = Node::new(note.cm(&JUBJUB).into()); if let Some(anchor) = self.anchor { - let witness_root: Fr = witness.root().into(); - if witness_root != anchor { + let path_root: Fr = merkle_path.root(cm).into(); + if path_root != anchor { return Err(Error::AnchorMismatch); } } else { - self.anchor = Some(witness.root().into()) + self.anchor = Some(merkle_path.root(cm).into()) } - let witness = witness.path().ok_or(Error::InvalidWitness)?; let alpha = Fs::random(&mut self.rng); @@ -325,7 +363,7 @@ impl Builder { diversifier, note, alpha, - witness, + merkle_path, }); Ok(()) @@ -356,29 +394,7 @@ impl Builder { utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { - if coin.value.is_negative() { - return Err(Error::InvalidAmount); - } - - let pubkey = secp256k1::PublicKey::from_secret_key(&self.legacy.secp, &sk).serialize(); - match coin.script_pubkey.address() { - Some(TransparentAddress::PublicKey(hash)) => { - use ripemd160::Ripemd160; - use sha2::{Digest, Sha256}; - - if &hash[..] != &Ripemd160::digest(&Sha256::digest(&pubkey))[..] { - return Err(Error::InvalidAddress); - } - } - _ => return Err(Error::InvalidAddress), - } - - self.mtx.vin.push(TxIn::new(utxo)); - self.legacy - .inputs - .push(TransparentInputInfo { sk, pubkey, coin }); - - Ok(()) + self.transparent_inputs.push(&mut self.mtx, sk, utxo, coin) } /// Adds a transparent address to send funds to. @@ -418,8 +434,8 @@ impl Builder { /// the network. pub fn build( mut self, - consensus_branch_id: u32, - prover: impl TxProver, + consensus_branch_id: consensus::BranchId, + prover: &impl TxProver, ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); @@ -428,7 +444,7 @@ impl Builder { // // Valid change - let change = self.mtx.value_balance - self.fee + self.legacy.input_sum() + let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() - self .mtx .vout @@ -451,10 +467,11 @@ impl Builder { } else if !self.spends.is_empty() { ( self.spends[0].extsk.expsk.ovk, - PaymentAddress { - diversifier: self.spends[0].diversifier, - pk_d: self.spends[0].note.pk_d.clone(), - }, + PaymentAddress::from_parts( + self.spends[0].diversifier, + self.spends[0].note.pk_d.clone(), + ) + .ok_or(Error::InvalidAddress)?, ) } else { return Err(Error::NoChangeAddress); @@ -498,16 +515,16 @@ impl Builder { let binding_sig_needed = !spends.is_empty() || !outputs.is_empty(); // Create Sapling SpendDescriptions - if spends.len() > 0 { + if !spends.is_empty() { let anchor = self.anchor.expect("anchor was set if spends were added"); - + for (i, (pos, spend)) in spends.iter().enumerate() { let proof_generation_key = spend.extsk.expsk.proof_generation_key(&JUBJUB); let mut nullifier = [0u8; 32]; nullifier.copy_from_slice(&spend.note.nf( - &proof_generation_key.into_viewing_key(&JUBJUB), - spend.witness.position, + &proof_generation_key.to_viewing_key(&JUBJUB), + spend.merkle_path.position, &JUBJUB, )); @@ -520,13 +537,13 @@ impl Builder { spend.alpha, spend.note.value, anchor, - spend.witness.clone(), + spend.merkle_path.clone(), ) .map_err(|()| Error::SpendProof)?; self.mtx.shielded_spends.push(SpendDescription { cv, - anchor: anchor, + anchor, nullifier, rk, zkproof, @@ -544,7 +561,7 @@ impl Builder { // Record the post-randomized output location tx_metadata.output_indices[pos] = i; - output.build(&prover, &mut ctx, &mut self.rng) + output.build(prover, &mut ctx, &mut self.rng) } else { // This is a dummy output let (dummy_to, dummy_note) = { @@ -563,16 +580,16 @@ impl Builder { (diversifier, g_d) }; - let pk_d = { + let (pk_d, payment_address) = loop { let dummy_ivk = Fs::random(&mut self.rng); - g_d.mul(dummy_ivk, &JUBJUB) + let pk_d = g_d.mul(dummy_ivk, &JUBJUB); + if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) { + break (pk_d, addr); + } }; ( - PaymentAddress { - diversifier, - pk_d: pk_d.clone(), - }, + payment_address, Note { g_d, pk_d, @@ -643,28 +660,8 @@ impl Builder { } // Transparent signatures - #[cfg(feature = "transparent-inputs")] - { - for (i, info) in self.legacy.inputs.iter().enumerate() { - sighash.copy_from_slice(&signature_hash_data( - &self.mtx, - consensus_branch_id, - SIGHASH_ALL, - Some((i, &info.coin.script_pubkey, info.coin.value)), - )); - - let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); - let sig = self.legacy.secp.sign(&msg, &info.sk); - - // Signature has to have "SIGHASH_ALL" appended to it - let mut sig_bytes: Vec = sig.serialize_der()[..].to_vec(); - sig_bytes.extend(&[SIGHASH_ALL as u8]); - - // P2PKH scriptSig - self.mtx.vin[i].script_sig = - Script::default() << &sig_bytes[..] << &info.pubkey[..]; - } - } + self.transparent_inputs + .apply_signatures(&mut self.mtx, consensus_branch_id); Ok(( self.mtx.freeze().expect("Transaction should be complete"), @@ -676,12 +673,13 @@ impl Builder { #[cfg(test)] mod tests { use ff::{Field, PrimeField}; - use rand::rngs::OsRng; + use rand_core::OsRng; use crate::jubjub::fs::Fs; use super::{Builder, Error}; use crate::{ + consensus, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, prover::mock::MockTxProver, @@ -737,6 +735,77 @@ mod tests { assert!(tx.binding_sig.is_none()); } + #[test] + fn binding_sig_absent_if_no_shielded_spend_or_output() { + use crate::transaction::{ + builder::{self, TransparentInputs}, + TransactionData, + }; + + // Create a builder with 0 fee, so we can construct t outputs + let mut builder = builder::Builder { + rng: OsRng, + mtx: TransactionData::new(), + fee: Amount::zero(), + anchor: None, + spends: vec![], + outputs: vec![], + transparent_inputs: TransparentInputs::default(), + change_address: None, + }; + + // Create a tx with only t output. No binding_sig should be present + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + let (tx, _) = builder + .build(consensus::BranchId::Sapling, &MockTxProver) + .unwrap(); + // No binding signature, because only t input and outputs + assert!(tx.binding_sig.is_none()); + } + + #[test] + fn binding_sig_present_if_shielded_spend() { + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let to = extfvk.default_address().unwrap().1; + + let mut rng = OsRng; + + let note1 = to + .create_note(50000, Fs::random(&mut rng), &JUBJUB) + .unwrap(); + let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); + let mut tree = CommitmentTree::new(); + tree.append(cm1).unwrap(); + let witness1 = IncrementalWitness::from_tree(&tree); + + let mut builder = Builder::new(0); + + // Create a tx with a sapling spend. binding_sig should be present + builder + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1.clone(), + witness1.path().unwrap(), + ) + .unwrap(); + + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + // Expect a binding signature error, because our inputs aren't valid, but this shows + // that a binding signature was attempted + assert_eq!( + builder.build(consensus::BranchId::Sapling, &MockTxProver), + Err(Error::BindingSig) + ); + } + #[test] fn fails_on_negative_transparent_output() { let mut builder = Builder::new(0); @@ -761,7 +830,7 @@ mod tests { { let builder = Builder::new(0); assert_eq!( - builder.build(1, MockTxProver), + builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) ); } @@ -783,7 +852,7 @@ mod tests { ) .unwrap(); assert_eq!( - builder.build(1, MockTxProver), + builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-60000).unwrap())) ); } @@ -799,7 +868,7 @@ mod tests { ) .unwrap(); assert_eq!( - builder.build(1, MockTxProver), + builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-60000).unwrap())) ); } @@ -807,7 +876,7 @@ mod tests { let note1 = to .create_note(59999, Fs::random(&mut rng), &JUBJUB) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).into_repr()); + let cm1 = Node::new(note1.cm(&JUBJUB).to_repr()); let mut tree = CommitmentTree::new(); tree.append(cm1).unwrap(); let mut witness1 = IncrementalWitness::from_tree(&tree); @@ -819,9 +888,9 @@ mod tests { builder .add_sapling_spend( extsk.clone(), - to.diversifier, + *to.diversifier(), note1.clone(), - witness1.clone(), + witness1.path().unwrap(), ) .unwrap(); builder @@ -839,13 +908,13 @@ mod tests { ) .unwrap(); assert_eq!( - builder.build(1, MockTxProver), + builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-1).unwrap())) ); } let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap(); - let cm2 = Node::new(note2.cm(&JUBJUB).into_repr()); + let cm2 = Node::new(note2.cm(&JUBJUB).to_repr()); tree.append(cm2).unwrap(); witness1.append(cm2).unwrap(); let witness2 = IncrementalWitness::from_tree(&tree); @@ -858,10 +927,15 @@ mod tests { { let mut builder = Builder::new(0); builder - .add_sapling_spend(extsk.clone(), to.diversifier, note1, witness1) + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1, + witness1.path().unwrap(), + ) .unwrap(); builder - .add_sapling_spend(extsk, to.diversifier, note2, witness2) + .add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap()) .unwrap(); builder .add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None) @@ -872,7 +946,10 @@ mod tests { Amount::from_u64(20000).unwrap(), ) .unwrap(); - assert_eq!(builder.build(1, MockTxProver), Err(Error::BindingSig)) + assert_eq!( + builder.build(consensus::BranchId::Sapling, &MockTxProver), + Err(Error::BindingSig) + ) } } } diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index b8b9077..296bd42 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -1,12 +1,14 @@ +//! Structs representing the components within Zcash transactions. + use crate::jubjub::{edwards, Unknown}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use pairing::bls12_381::{Bls12, Fr, FrRepr}; use std::io::{self, Read, Write}; -use legacy::Script; -use redjubjub::{PublicKey, Signature}; -use JUBJUB; +use crate::legacy::Script; +use crate::redjubjub::{PublicKey, Signature}; +use crate::JUBJUB; pub mod amount; pub use self::amount::Amount; @@ -27,7 +29,7 @@ pub struct OutPoint { impl OutPoint { pub fn new(hash: [u8; 32], n: u32) -> Self { - OutPoint {hash, n} + OutPoint { hash, n } } pub fn read(mut reader: R) -> io::Result { @@ -117,7 +119,7 @@ pub struct SpendDescription { } impl std::fmt::Debug for SpendDescription { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})", @@ -136,9 +138,10 @@ impl SpendDescription { // Consensus rule (§7.3): Canonical encoding is enforced here let anchor = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = FrRepr([0; 32]); + reader.read_exact(&mut f.0)?; + Fr::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))? }; let mut nullifier = [0; 32]; @@ -173,18 +176,16 @@ impl SpendDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - self.anchor.into_repr().write_le(&mut writer)?; + writer.write_all(self.anchor.to_repr().as_ref())?; writer.write_all(&self.nullifier)?; self.rk.write(&mut writer)?; writer.write_all(&self.zkproof)?; match self.spend_auth_sig { Some(sig) => sig.write(&mut writer), - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Missing spend auth signature", - )); - } + None => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Missing spend auth signature", + )), } } } @@ -199,7 +200,7 @@ pub struct OutputDescription { } impl std::fmt::Debug for OutputDescription { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})", @@ -218,9 +219,10 @@ impl OutputDescription { // Consensus rule (§7.4): Canonical encoding is enforced here let cmu = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = FrRepr([0; 32]); + reader.read_exact(&mut f.0)?; + Fr::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))? }; // Consensus rules (§4.5): @@ -252,7 +254,7 @@ impl OutputDescription { pub fn write(&self, mut writer: W) -> io::Result<()> { self.cv.write(&mut writer)?; - self.cmu.into_repr().write_le(&mut writer)?; + writer.write_all(self.cmu.to_repr().as_ref())?; self.ephemeral_key.write(&mut writer)?; writer.write_all(&self.enc_ciphertext)?; writer.write_all(&self.out_ciphertext)?; @@ -266,7 +268,7 @@ enum SproutProof { } impl std::fmt::Debug for SproutProof { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { SproutProof::Groth(_) => write!(f, "SproutProof::Groth"), SproutProof::PHGR(_) => write!(f, "SproutProof::PHGR"), @@ -288,7 +290,7 @@ pub struct JSDescription { } impl std::fmt::Debug for JSDescription { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "JSDescription( @@ -360,23 +362,20 @@ impl JSDescription { .map(|mac| reader.read_exact(mac)) .collect::>()?; - let proof = match use_groth { - true => { - // Consensus rules (§4.3): - // - Canonical encoding is enforced in librustzcash_sprout_verify() - // - Proof validity is enforced in librustzcash_sprout_verify() - let mut proof = [0; GROTH_PROOF_SIZE]; - reader.read_exact(&mut proof)?; - SproutProof::Groth(proof) - } - false => { - // Consensus rules (§4.3): - // - Canonical encoding is enforced by PHGRProof in zcashd - // - Proof validity is enforced by JSDescription::Verify() in zcashd - let mut proof = [0; PHGR_PROOF_SIZE]; - reader.read_exact(&mut proof)?; - SproutProof::PHGR(proof) - } + let proof = if use_groth { + // Consensus rules (§4.3): + // - Canonical encoding is enforced in librustzcash_sprout_verify() + // - Proof validity is enforced in librustzcash_sprout_verify() + let mut proof = [0; GROTH_PROOF_SIZE]; + reader.read_exact(&mut proof)?; + SproutProof::Groth(proof) + } else { + // Consensus rules (§4.3): + // - Canonical encoding is enforced by PHGRProof in zcashd + // - Proof validity is enforced by JSDescription::Verify() in zcashd + let mut proof = [0; PHGR_PROOF_SIZE]; + reader.read_exact(&mut proof)?; + SproutProof::PHGR(proof) }; let mut ciphertexts = [[0; 601]; ZC_NUM_JS_OUTPUTS]; diff --git a/zcash_primitives/src/transaction/components/amount.rs b/zcash_primitives/src/transaction/components/amount.rs index 8d358fb..f3f89af 100644 --- a/zcash_primitives/src/transaction/components/amount.rs +++ b/zcash_primitives/src/transaction/components/amount.rs @@ -206,7 +206,7 @@ mod tests { #[should_panic] fn add_panics_on_overflow() { let v = Amount(MAX_MONEY); - let sum = v + Amount(1); + let _sum = v + Amount(1); } #[test] @@ -220,7 +220,7 @@ mod tests { #[should_panic] fn sub_panics_on_underflow() { let v = Amount(-MAX_MONEY); - let diff = v - Amount(1); + let _diff = v - Amount(1); } #[test] diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 2ef09b8..f0ed822 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -1,3 +1,5 @@ +//! Structs and methods for handling Zcash transactions. + use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use hex; use sha2::{Digest, Sha256}; @@ -5,8 +7,8 @@ use std::fmt; use std::io::{self, Read, Write}; use std::ops::Deref; -use redjubjub::Signature; -use serialize::Vector; +use crate::redjubjub::Signature; +use crate::serialize::Vector; pub mod builder; pub mod components; @@ -28,8 +30,8 @@ const SAPLING_TX_VERSION: u32 = 4; pub struct TxId(pub [u8; 32]); impl fmt::Display for TxId { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let mut data = self.0.clone(); + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut data = self.0; data.reverse(); formatter.write_str(&hex::encode(data)) } @@ -74,7 +76,7 @@ pub struct TransactionData { } impl std::fmt::Debug for TransactionData { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "TransactionData( @@ -164,9 +166,10 @@ impl Transaction { let overwintered = (header >> 31) == 1; let version = header & 0x7FFFFFFF; - let version_group_id = match overwintered { - true => reader.read_u32::()?, - false => 0, + let version_group_id = if overwintered { + reader.read_u32::()? + } else { + 0 }; let is_overwinter_v3 = overwintered @@ -185,9 +188,10 @@ impl Transaction { let vin = Vector::read(&mut reader, TxIn::read)?; let vout = Vector::read(&mut reader, TxOut::read)?; let lock_time = reader.read_u32::()?; - let expiry_height = match is_overwinter_v3 || is_sapling_v4 { - true => reader.read_u32::()?, - false => 0, + let expiry_height = if is_overwinter_v3 || is_sapling_v4 { + reader.read_u32::()? + } else { + 0 }; let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 { @@ -223,9 +227,10 @@ impl Transaction { }; let binding_sig = - match is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { - true => Some(Signature::read(&mut reader)?), - false => None, + if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { + Some(Signature::read(&mut reader)?) + } else { + None }; Transaction::from_data(TransactionData { diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 1b88ef8..89ee192 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -1,21 +1,21 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; use super::{ components::{Amount, TxOut}, Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, }; -use legacy::Script; +use crate::{consensus, legacy::Script}; -const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash"; -const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashPrevoutHash"; -const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSequencHash"; -const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashOutputsHash"; -const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashJSplitsHash"; -const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSSpendsHash"; -const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSOutputHash"; +const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash"; +const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash"; +const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash"; +const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash"; +const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash"; +const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash"; +const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash"; pub const SIGHASH_ALL: u32 = 1; const SIGHASH_NONE: u32 = 2; @@ -128,7 +128,7 @@ fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { s_spend.cv.write(&mut data).unwrap(); - s_spend.anchor.into_repr().write_le(&mut data).unwrap(); + data.extend_from_slice(s_spend.anchor.to_repr().as_ref()); data.extend_from_slice(&s_spend.nullifier); s_spend.rk.write(&mut data).unwrap(); data.extend_from_slice(&s_spend.zkproof); @@ -152,7 +152,7 @@ fn shielded_outputs_hash(tx: &TransactionData) -> Blake2bHash { pub fn signature_hash_data( tx: &TransactionData, - consensus_branch_id: u32, + consensus_branch_id: consensus::BranchId, hash_type: u32, transparent_input: Option<(usize, &Script, Amount)>, ) -> Vec { @@ -162,7 +162,7 @@ pub fn signature_hash_data( let mut personal = [0; 16]; (&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX); (&mut personal[12..]) - .write_u32::(consensus_branch_id) + .write_u32::(consensus_branch_id.into()) .unwrap(); let mut h = Blake2bParams::new() @@ -230,7 +230,7 @@ pub fn signature_hash_data( pub fn signature_hash( tx: &Transaction, - consensus_branch_id: u32, + consensus_branch_id: consensus::BranchId, hash_type: u32, transparent_input: Option<(usize, &Script, Amount)>, ) -> Vec { diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index 80f0900..2fc267c 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -1,154 +1,16 @@ use ff::Field; use pairing::bls12_381::Bls12; -use rand_os::OsRng; +use rand_core::OsRng; use crate::jubjub::{fs::Fs, FixedGenerators}; use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData}; -use legacy::Script; -use redjubjub::PrivateKey; -use JUBJUB; +use crate::redjubjub::PrivateKey; +use crate::JUBJUB; #[test] fn tx_read_write() { - // TxID: 64f0bd7fe30ce23753358fe3a2dc835b8fba9c0274c4e2c54a6f73114cb55639 - // From testnet block 280003. - let data = [ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0x8f, 0x64, 0x29, 0x96, 0xdf, 0x1e, - 0x93, 0xa6, 0xd7, 0x9a, 0xe5, 0xba, 0xae, 0x34, 0x93, 0xf4, 0x23, 0xca, 0x6c, 0x82, 0xe9, - 0x9f, 0x3e, 0x8d, 0x95, 0x24, 0xfa, 0x78, 0xbc, 0xf1, 0x61, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x6b, 0x48, 0x30, 0x45, 0x02, 0x21, 0x00, 0xb6, 0x5e, 0x37, 0x22, 0x97, 0x07, 0xd9, 0xcd, - 0x48, 0x39, 0x40, 0xd2, 0xab, 0x8b, 0xdc, 0x0b, 0x74, 0xb1, 0x2d, 0xda, 0x66, 0xd0, 0x2d, - 0xbd, 0xf3, 0x6f, 0xd3, 0x83, 0xb9, 0x60, 0x2a, 0x51, 0x02, 0x20, 0x4b, 0xe7, 0xfd, 0x7a, - 0x39, 0xa4, 0xa4, 0x2d, 0xff, 0x07, 0x1a, 0x5a, 0x2b, 0xc5, 0x1b, 0x49, 0x2d, 0x33, 0xf0, - 0xbc, 0x39, 0x4b, 0xc8, 0x78, 0x61, 0xe1, 0xbc, 0xaa, 0xf2, 0xba, 0xc9, 0x3b, 0x01, 0x21, - 0x02, 0x48, 0xe7, 0x8b, 0xdc, 0x18, 0xf1, 0xa8, 0x31, 0x10, 0xc1, 0x2e, 0x40, 0x08, 0xb7, - 0x64, 0x02, 0x69, 0x61, 0xb1, 0x68, 0xfe, 0x8d, 0x5a, 0x8d, 0x94, 0x7e, 0xfe, 0x6a, 0xf8, - 0x3c, 0xc8, 0x8e, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf0, 0xf2, 0x70, 0x18, 0x02, 0x00, 0x00, - 0x00, 0x19, 0x76, 0xa9, 0x14, 0xa2, 0x84, 0xd0, 0x51, 0x1d, 0x0e, 0x52, 0x0d, 0x36, 0xf4, - 0x44, 0xa3, 0x6c, 0x10, 0xbf, 0x54, 0xb4, 0xb0, 0x17, 0xcd, 0x88, 0xac, 0x00, 0x00, 0x00, - 0x00, 0xd7, 0x45, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0xca, 0x9a, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x13, 0x31, 0xa3, 0x05, 0x9e, 0x66, 0xaa, 0x6c, 0xa9, 0x7a, 0x62, 0xf5, 0x6e, - 0xa2, 0x34, 0x20, 0x75, 0x68, 0x56, 0x6f, 0x69, 0x71, 0xb3, 0x72, 0x2a, 0xe0, 0xdd, 0x82, - 0xc0, 0x03, 0x99, 0x69, 0x2a, 0xac, 0xb5, 0xfb, 0x12, 0xac, 0x58, 0x0a, 0xc2, 0x66, 0x24, - 0xa8, 0xcf, 0x0a, 0x90, 0x4c, 0xd6, 0xf4, 0xbf, 0xea, 0x55, 0x62, 0x52, 0x05, 0xcb, 0x58, - 0xf0, 0x6b, 0x1c, 0x19, 0x74, 0x23, 0x28, 0x0d, 0xea, 0xc7, 0x4e, 0xea, 0x97, 0x59, 0x8c, - 0x43, 0x14, 0xd8, 0x99, 0xa4, 0xfd, 0x85, 0x31, 0x1e, 0x04, 0x62, 0x57, 0xd2, 0xd4, 0xc2, - 0x97, 0xf1, 0x40, 0x6c, 0xf7, 0x09, 0xd9, 0x2a, 0x86, 0x07, 0xf7, 0x69, 0x8d, 0x45, 0xfe, - 0x9f, 0x41, 0xde, 0xa3, 0xa0, 0x57, 0x1c, 0x5d, 0xa5, 0xcf, 0xa7, 0x8e, 0x18, 0xeb, 0xf5, - 0x80, 0xc3, 0x61, 0x79, 0xd9, 0xd6, 0xe6, 0x32, 0x0a, 0x34, 0x8f, 0x14, 0x6c, 0x40, 0x7a, - 0xda, 0xb4, 0xcb, 0x31, 0x03, 0x92, 0xa5, 0xf5, 0xb5, 0xab, 0x28, 0x3b, 0x78, 0x34, 0x3b, - 0xa9, 0x1a, 0xbc, 0x7c, 0x4b, 0xfe, 0x23, 0xa3, 0xdb, 0xaf, 0x80, 0x37, 0xc6, 0x76, 0xe5, - 0x95, 0xa2, 0x65, 0x74, 0xb1, 0x81, 0x3b, 0xc2, 0xbf, 0x2d, 0x2e, 0x91, 0x1f, 0x6f, 0x3a, - 0xbb, 0x0b, 0xa6, 0xbc, 0xac, 0x7a, 0x29, 0x01, 0xfb, 0xdc, 0xe6, 0x5f, 0xb0, 0x7b, 0x56, - 0x36, 0x01, 0x7e, 0xf1, 0x4d, 0xff, 0x44, 0xcd, 0xee, 0xa7, 0x30, 0x47, 0x72, 0x94, 0xf2, - 0xf8, 0x61, 0x9b, 0xd3, 0xd5, 0xe6, 0xbe, 0x48, 0x98, 0xbf, 0x8d, 0x39, 0xc0, 0xe0, 0xea, - 0xe5, 0xa3, 0x68, 0x64, 0x62, 0x52, 0x06, 0xb9, 0xa8, 0xf9, 0x94, 0x0b, 0xf1, 0x66, 0x50, - 0xde, 0xf7, 0x92, 0x6e, 0xb0, 0xdb, 0x43, 0xb7, 0xd7, 0x61, 0x5e, 0x47, 0x74, 0xcf, 0x10, - 0x94, 0x82, 0xf2, 0xe8, 0x07, 0xfe, 0xe6, 0xc0, 0xc8, 0x84, 0xe8, 0x31, 0x4c, 0x67, 0xc5, - 0xd8, 0x5f, 0x4c, 0x22, 0x9c, 0xde, 0xab, 0x1e, 0x96, 0x4c, 0xf0, 0xc1, 0xad, 0xcb, 0x47, - 0xce, 0xbf, 0xc7, 0xc0, 0x67, 0xa0, 0xf3, 0xc8, 0x06, 0x81, 0x4a, 0x28, 0x5e, 0xdb, 0xb6, - 0x24, 0xf4, 0x71, 0x06, 0x29, 0x09, 0x89, 0x44, 0xac, 0x75, 0xe7, 0xc9, 0xcb, 0xc5, 0x6b, - 0xd0, 0xa0, 0x29, 0xe1, 0x11, 0x0e, 0xac, 0x60, 0xcb, 0x40, 0x77, 0xeb, 0xf1, 0x08, 0xfe, - 0x3e, 0x67, 0xcd, 0x06, 0x13, 0x91, 0xe5, 0xd6, 0x91, 0x6d, 0x5f, 0x41, 0xc0, 0x2b, 0x89, - 0x14, 0xc1, 0x2c, 0xf6, 0x05, 0xdb, 0x7d, 0x95, 0x92, 0x26, 0xe2, 0xe8, 0xff, 0x71, 0x26, - 0x3b, 0x9a, 0xf4, 0xc5, 0x9b, 0x0f, 0x4d, 0xb3, 0x15, 0xb7, 0x4c, 0xa2, 0xb0, 0xb7, 0xd2, - 0x52, 0x13, 0xd5, 0x29, 0x39, 0x54, 0xc3, 0xe5, 0x11, 0x72, 0x37, 0x0f, 0xb6, 0xc3, 0x5a, - 0xbe, 0x9c, 0xe3, 0x6e, 0xf2, 0x53, 0xe3, 0xa7, 0x2e, 0x19, 0xda, 0xc9, 0xbd, 0x73, 0x62, - 0xc4, 0x49, 0x92, 0x97, 0x42, 0x15, 0xc8, 0x2c, 0xb9, 0x0c, 0x99, 0x48, 0x8d, 0xbd, 0xe1, - 0x19, 0x63, 0xe8, 0x57, 0xce, 0xa6, 0xb8, 0x1b, 0x8e, 0xaa, 0xe3, 0x4b, 0x7c, 0xf5, 0xa9, - 0x7d, 0x6b, 0x60, 0xd4, 0x9f, 0xdf, 0xa2, 0x0f, 0x5f, 0x3c, 0x12, 0x0e, 0xf3, 0x82, 0xca, - 0x24, 0x69, 0x60, 0x4f, 0xb0, 0xc6, 0x84, 0x2c, 0x6d, 0x4f, 0xae, 0x96, 0x61, 0x66, 0x5b, - 0x5c, 0xbc, 0x61, 0x2c, 0xef, 0x13, 0x2f, 0x88, 0xfb, 0x7d, 0xa3, 0x93, 0xf3, 0x56, 0xe3, - 0xad, 0x13, 0xfc, 0x35, 0x57, 0x98, 0x0a, 0x77, 0x34, 0x23, 0x14, 0x53, 0xe4, 0x40, 0x79, - 0x04, 0x2f, 0xb4, 0x32, 0xf5, 0x5e, 0x75, 0x14, 0x84, 0xd5, 0xd6, 0xd3, 0x0f, 0xbc, 0x4f, - 0x99, 0x90, 0x13, 0xd5, 0xd4, 0xf2, 0xfb, 0x62, 0xf7, 0x14, 0x4e, 0x8d, 0xcd, 0x2a, 0xe5, - 0x95, 0x46, 0xcc, 0x43, 0x79, 0xad, 0x9f, 0x18, 0x59, 0xef, 0x80, 0xde, 0xc6, 0x6b, 0x1a, - 0x9b, 0x0b, 0x7f, 0xd2, 0xc4, 0x7b, 0xd3, 0x83, 0x02, 0xd2, 0x9c, 0x31, 0x99, 0x03, 0x29, - 0xa8, 0x95, 0x87, 0x6e, 0xd1, 0xd8, 0x4d, 0xb7, 0x57, 0x85, 0x6e, 0x75, 0xce, 0x9a, 0x1d, - 0xc7, 0xc7, 0x47, 0x2b, 0xc2, 0x18, 0xfb, 0x8d, 0x7c, 0x7d, 0x02, 0x8b, 0xb0, 0x2f, 0x10, - 0xef, 0xe7, 0xfe, 0x6a, 0x8c, 0x9c, 0xe0, 0x34, 0xfe, 0xa6, 0x6b, 0x90, 0x9c, 0x8d, 0x41, - 0x26, 0x25, 0x1c, 0x7d, 0x6e, 0x54, 0xf4, 0xcf, 0xc7, 0x78, 0xcd, 0x4f, 0x0e, 0x0b, 0xad, - 0x10, 0x96, 0x17, 0x6f, 0x2d, 0xd4, 0x5c, 0x45, 0xcb, 0xe1, 0x5e, 0x11, 0x8f, 0x90, 0xff, - 0x25, 0x45, 0xf8, 0x32, 0xf2, 0x36, 0x98, 0xf2, 0xc9, 0x53, 0x1b, 0x52, 0x65, 0x5a, 0x4c, - 0x0c, 0x89, 0x53, 0x55, 0x99, 0x28, 0xee, 0xdf, 0xc7, 0x56, 0xc3, 0x65, 0xcf, 0x92, 0x9b, - 0x84, 0x47, 0xdc, 0xdc, 0x7d, 0x82, 0x38, 0x49, 0xe0, 0x2f, 0xf6, 0x8b, 0x62, 0x78, 0xd7, - 0x54, 0x2c, 0xe0, 0xf1, 0x07, 0x0b, 0xb1, 0xad, 0x91, 0x3c, 0x1a, 0x35, 0x36, 0x25, 0xf5, - 0xd3, 0x5b, 0x14, 0xcf, 0xec, 0x84, 0xa6, 0x33, 0xd7, 0xfe, 0x25, 0x25, 0x6d, 0xcf, 0xfe, - 0x92, 0xf9, 0xa6, 0xf0, 0xfe, 0x00, 0xca, 0xaa, 0xa5, 0xb3, 0x9c, 0xc2, 0xab, 0x06, 0x76, - 0x8a, 0x42, 0xa5, 0xb4, 0x00, 0x83, 0xce, 0xa0, 0x1c, 0x96, 0xb3, 0xe6, 0x8d, 0x0f, 0x6a, - 0x58, 0x7e, 0xaf, 0x2d, 0xa6, 0xfd, 0xad, 0xc8, 0x25, 0x27, 0xf1, 0x86, 0xa6, 0x04, 0x71, - 0xce, 0x98, 0xe2, 0x7d, 0x2b, 0x11, 0xef, 0xc4, 0x79, 0x98, 0xf3, 0x03, 0x0a, 0x7a, 0x2e, - 0x5d, 0x0b, 0x0a, 0x7e, 0xb8, 0x0f, 0x6b, 0xd0, 0xe4, 0xb9, 0xc8, 0x36, 0x7c, 0x6c, 0x52, - 0x2d, 0x94, 0x15, 0xf8, 0xca, 0xec, 0x7b, 0x0a, 0x73, 0x18, 0xd5, 0x3d, 0xce, 0x39, 0x1c, - 0xf7, 0xe7, 0x38, 0x9c, 0x9a, 0x74, 0xaa, 0x6a, 0x4c, 0x21, 0x7c, 0x28, 0x85, 0x19, 0xaf, - 0x81, 0xba, 0x21, 0x22, 0xca, 0x0c, 0x58, 0x40, 0xcc, 0x02, 0xcf, 0x1b, 0xcf, 0x15, 0x0c, - 0xd3, 0xdf, 0x33, 0xc0, 0xac, 0xfd, 0x00, 0x53, 0xe6, 0x68, 0xb9, 0x26, 0x56, 0x1b, 0x92, - 0x40, 0x98, 0xd9, 0x7a, 0xaa, 0xb5, 0x7e, 0xe1, 0x11, 0x3d, 0xf9, 0x66, 0xa4, 0x22, 0xef, - 0x9b, 0x01, 0x46, 0x17, 0xbc, 0xee, 0xf0, 0x5f, 0xb6, 0x46, 0x8e, 0x33, 0x0e, 0x2d, 0xec, - 0xe3, 0xf3, 0x75, 0xe9, 0x8e, 0xf0, 0x3e, 0x5b, 0x18, 0xa9, 0x53, 0xe2, 0x30, 0x1f, 0xcc, - 0xec, 0x86, 0x20, 0x0a, 0xe4, 0x32, 0xc9, 0xc1, 0x2c, 0x30, 0x77, 0x54, 0x37, 0xf3, 0x62, - 0x97, 0x14, 0xa9, 0xfa, 0xbe, 0xb5, 0x32, 0x89, 0x40, 0x2b, 0x7f, 0xd3, 0x86, 0xce, 0xf2, - 0xb1, 0x14, 0x67, 0x23, 0xa8, 0x9d, 0x0f, 0x81, 0x65, 0x1e, 0x00, 0xca, 0xea, 0x2f, 0x3a, - 0xc9, 0xee, 0xfe, 0xfb, 0x86, 0x8d, 0x85, 0xed, 0x23, 0x54, 0xf5, 0x30, 0xfe, 0x38, 0xfe, - 0x3a, 0x3a, 0x6a, 0xab, 0x47, 0xd4, 0x2d, 0xc2, 0x13, 0x29, 0xe3, 0xad, 0x1b, 0x9d, 0x06, - 0xc0, 0xc8, 0xd6, 0x53, 0x74, 0x56, 0xf5, 0x4a, 0xd0, 0x45, 0x3f, 0x44, 0x41, 0x75, 0xd8, - 0x7e, 0xf5, 0xcd, 0xd1, 0x69, 0x46, 0x62, 0xe0, 0xa1, 0xe6, 0xe3, 0x63, 0x2e, 0xd7, 0xa8, - 0xe7, 0x6b, 0xc7, 0xb1, 0xb5, 0xa4, 0x18, 0xf0, 0x86, 0xd3, 0x40, 0x81, 0x5e, 0xc3, 0x98, - 0xf0, 0x92, 0xe9, 0x78, 0x69, 0xf5, 0xe2, 0x01, 0xc2, 0x2c, 0x87, 0x91, 0x8f, 0x76, 0x6a, - 0x35, 0x32, 0xeb, 0x9a, 0x4f, 0xc9, 0xac, 0xf1, 0x96, 0xcb, 0xc2, 0xd0, 0x28, 0x51, 0x19, - 0xa4, 0x21, 0x6d, 0x25, 0x81, 0xcd, 0x2d, 0x91, 0xbc, 0xdc, 0xe8, 0x68, 0xc4, 0x68, 0xf6, - 0xf3, 0x4c, 0xf4, 0x9e, 0x3a, 0x56, 0xce, 0x24, 0x9a, 0x2f, 0xd8, 0xcf, 0x36, 0xb0, 0x1b, - 0x0f, 0x77, 0xde, 0x72, 0x2b, 0xbc, 0xe2, 0x67, 0xe3, 0xe5, 0x52, 0x16, 0x88, 0xe6, 0x52, - 0x22, 0x23, 0x5c, 0x91, 0xc2, 0x63, 0xd8, 0x0e, 0x28, 0x29, 0x7e, 0x92, 0x9d, 0x88, 0x5b, - 0x7b, 0x9c, 0x1a, 0x16, 0x54, 0xb2, 0xd0, 0xb8, 0x75, 0x77, 0xc9, 0xa1, 0xc7, 0x25, 0xf5, - 0x44, 0x15, 0xdc, 0x5f, 0x52, 0xdd, 0xe0, 0x69, 0x5f, 0x9f, 0x6d, 0xcb, 0x4b, 0x6e, 0xe3, - 0xe3, 0xea, 0x70, 0x29, 0x04, 0xc1, 0x1f, 0xf9, 0x2f, 0x55, 0x53, 0x4c, 0x7e, 0xf9, 0x8c, - 0xe7, 0x93, 0xd7, 0x47, 0x56, 0xa4, 0x5d, 0x4e, 0x32, 0x0a, 0x42, 0x5e, 0x98, 0x2d, 0x5b, - 0x37, 0x2d, 0x6a, 0x8d, 0x41, 0xfb, 0x86, 0xba, 0x51, 0x64, 0x81, 0x68, 0x32, 0xa4, 0x81, - 0x82, 0x5c, 0x8c, 0x6a, 0xd7, 0x27, 0x09, 0x69, 0x85, 0x9e, 0x55, 0xd2, 0x36, 0x75, 0x35, - 0x06, 0x0f, 0x99, 0x85, 0x70, 0x65, 0x17, 0x04, 0x66, 0xbd, 0xb7, 0x0c, 0xb9, 0x3a, 0xb2, - 0xf9, 0xc0, 0xe2, 0x93, 0xa0, 0xa9, 0x19, 0x84, 0x3b, 0xbf, 0x34, 0xc2, 0xfe, 0x61, 0xb0, - 0xc3, 0xe3, 0x2a, 0xa7, 0x07, 0x8e, 0x83, 0xd4, 0xc1, 0x92, 0x9e, 0x1e, 0x1d, 0x86, 0x14, - 0x1c, 0xde, 0xb1, 0x89, 0x20, 0x91, 0x09, 0x75, 0xdb, 0x3a, 0x76, 0x26, 0x82, 0x05, 0x99, - 0x63, 0x0c, 0x42, 0x3a, 0xde, 0x23, 0x3d, 0x5d, 0x60, 0x68, 0x55, 0x24, 0xe8, 0xd8, 0x03, - 0x2b, 0x86, 0x1b, 0x4a, 0xad, 0x20, 0x02, 0xa8, 0xfd, 0x17, 0xc9, 0x28, 0x2b, 0x82, 0x5f, - 0x02, 0xd3, 0x53, 0xe2, 0x91, 0x37, 0x9c, 0xed, 0x00, 0xeb, 0xaa, 0x3c, 0x03, 0xe0, 0x1d, - 0x9c, 0x59, 0xf4, 0x05, 0x09, 0x9d, 0x1c, 0x34, 0x32, 0xba, 0xd0, 0x63, 0x58, 0xd6, 0xb1, - 0x94, 0x2f, 0x0b, 0xaf, 0x71, 0x09, 0x98, 0xd1, 0x0a, 0x22, 0xd1, 0x55, 0xb0, 0xfe, 0x84, - 0x99, 0x52, 0x89, 0x31, 0x26, 0x94, 0x9f, 0xf9, 0x2d, 0xe3, 0xa4, 0xc2, 0xee, 0xaf, 0xdf, - 0x68, 0x84, 0x35, 0xe3, 0x25, 0xd8, 0x1c, 0x2c, 0xe0, 0x08, 0xcf, 0x6c, 0x76, 0x03, 0x0d, - 0x4d, 0x46, 0x34, 0x2a, 0xc3, 0x37, 0x2c, 0x73, 0x98, 0x65, 0x60, 0xc4, 0xec, 0x35, 0xa6, - 0xf6, 0x49, 0xef, 0x02, 0xc1, 0x19, 0x36, 0xb7, 0x03, 0x9b, 0xc6, 0xf5, 0xd0, 0x94, 0x38, - 0xdb, 0xe4, 0x76, 0x25, 0x1b, 0x59, 0x64, 0xb6, 0x8f, 0x02, 0xee, 0xdf, 0xf7, 0xa9, 0xe0, - 0xed, 0x3e, 0x30, 0x90, 0x96, 0x5a, 0x22, 0xf2, 0xc5, 0x52, 0xce, 0x3b, 0x2b, 0x47, 0x4f, - 0xd2, 0xfc, 0x06, 0xb5, 0x09, 0x27, 0x83, 0x0a, 0x05, 0xa3, 0x03, 0xfa, 0xff, 0xd6, 0x84, - 0x82, 0xd7, 0xb7, 0x85, 0x38, 0x43, 0x25, 0x40, 0xdd, 0x32, 0x61, 0xab, 0x75, 0x9b, 0x65, - 0x82, 0x12, 0x9a, 0x7f, 0x18, 0xd8, 0x01, 0xc5, 0x43, 0x19, 0xca, 0x52, 0xa3, 0xc6, 0xa3, - 0xdb, 0x63, 0x50, 0x44, 0xd6, 0x25, 0xe2, 0x40, 0x38, 0xad, 0x42, 0x77, 0xf8, 0xd5, 0xbf, - 0x01, 0x60, 0x35, 0x16, 0x5f, 0x21, 0xb0, 0x70, 0xe8, 0x16, 0x9d, 0x65, 0x7d, 0x6e, 0xd1, - 0xfa, 0x7f, 0x8e, 0xd0, 0x9b, 0x4e, 0x1d, 0x9c, 0xa2, 0xe5, 0x1a, 0x24, 0xda, 0x55, 0xe4, - 0x3b, 0x3f, 0xca, 0x98, 0x59, 0xb2, 0x40, 0x8c, 0x26, 0xaa, 0xcb, 0xad, 0x74, 0x9e, 0xbe, - 0x88, 0x2c, 0x31, 0xe7, 0x20, 0x5e, 0x63, 0x8b, 0xb7, 0xe2, 0xbf, 0xc8, 0xa3, 0xf1, 0xc0, - 0x2c, 0x0c, 0xa7, 0xbb, 0x9d, 0xaa, 0xab, 0x7f, 0xcb, 0xf8, 0x45, 0xd8, 0x00, 0x2c, 0x3d, - 0xe7, 0x99, 0x24, 0xdc, 0xaa, 0xdc, 0x24, 0xbd, 0xc0, 0x08, 0x2f, 0x4a, 0x6b, 0x61, 0x87, - 0x6f, 0x31, 0x92, 0xa8, 0x81, 0xf5, 0x9a, 0x68, 0x2d, 0x27, 0x36, 0x85, 0xd4, 0x79, 0x5c, - 0x9b, 0xd7, 0xcc, 0xcf, 0x49, 0xde, 0x34, 0x44, 0x3a, 0x9f, 0x9c, 0xb3, 0x5b, 0xbf, 0x25, - 0x4c, 0x50, 0x61, 0x1b, 0x7c, 0x13, 0x24, 0xb1, 0x10, 0x94, 0x66, 0x7b, 0x6b, 0x60, 0x8c, - 0x39, 0xd1, 0x25, 0x2c, 0xeb, 0xcc, 0x48, 0x77, 0xce, 0xea, 0x76, 0xe1, 0x9b, 0x84, 0x2b, - 0x67, 0xf6, 0x26, 0x74, 0x3f, 0xab, 0x29, 0x77, 0x76, 0xcc, 0x9c, 0xf7, 0x9e, 0x90, 0xe8, - 0xfc, 0xe1, 0x00, 0x17, 0x90, 0xc2, 0xe7, 0xd5, 0xc9, 0x58, 0x64, 0x7c, 0xca, 0x5d, 0x33, - 0x97, 0xd2, 0x0a, 0xfc, 0xf2, 0x9b, 0xa4, 0x4f, 0x62, 0xa7, 0xc6, 0x2e, 0x90, 0x8d, 0x84, - 0x8d, 0x81, 0xa7, 0x9f, 0xad, 0xbb, 0x37, 0x0a, 0xba, 0x93, 0xb0, 0x3e, 0x41, 0xd4, 0xbc, - 0x49, 0xe2, 0x99, 0xd6, 0xd3, 0x3f, 0xaf, 0x86, 0x9f, 0x36, 0x37, 0x14, 0x14, 0xce, 0x64, - 0x6f, 0xc2, 0xca, 0x6d, 0xcf, 0xf5, 0x5a, 0x6e, 0x06, 0x39, 0xd5, 0x0c, 0xae, 0xb1, 0x14, - 0xc4, 0x18, 0xc6, 0x26, 0xb8, 0x67, 0x15, 0x43, 0x64, 0x81, 0xd1, 0x92, 0x8d, 0x55, 0xa7, - 0x56, 0xa6, 0x03, 0xe7, 0x11, 0x0c, 0x3a, 0xfe, 0x96, 0x3c, 0x2b, 0x29, 0xa4, 0x78, 0xf9, - 0xd4, 0x39, 0x7b, 0x88, 0x5a, 0x67, 0xb0, 0x93, 0xa3, 0x45, 0x79, 0x62, 0x19, 0xc1, 0x11, - 0xb7, 0xe9, 0x4d, 0xb3, 0x90, 0xaa, 0x4b, 0xb7, 0x6b, 0x66, 0xa5, 0x34, 0xe5, 0xe2, 0x67, - 0x9b, 0x27, 0xdb, 0x5f, 0x95, 0xfd, 0x09, 0xa3, 0x6b, 0x05, - ]; + let data = &self::data::tx_read_write::TX_READ_WRITE; let tx = Transaction::read(&data[..]).unwrap(); assert_eq!( format!("{}", tx.txid()), @@ -208,1710 +70,21 @@ fn tx_write_rejects_unexpected_binding_sig() { } } +mod data; #[test] fn zip_0143() { - struct TestVector { - tx: Vec, - script_code: Script, - transparent_input: Option, - hash_type: u32, - amount: i64, - consensus_branch_id: u32, - sighash: [u8; 32], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0143.py - let test_vectors = vec![ - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0xe7, 0x71, 0x98, 0x11, - 0x89, 0x3e, 0x00, 0x00, 0x09, 0x52, 0x00, 0xac, 0x65, 0x51, 0xac, 0x63, 0x65, 0x65, - 0xb2, 0x83, 0x5a, 0x08, 0x05, 0x75, 0x02, 0x00, 0x02, 0x51, 0x51, 0x48, 0x1c, 0xdd, - 0x86, 0xb3, 0xcc, 0x43, 0x18, 0x00, - ], - script_code: Script(vec![0x6a, 0x00, 0x00, 0x00, 0x63, 0xac, 0x53]), - transparent_input: None, - hash_type: 1, - amount: 1672704339313879, - consensus_branch_id: 1537743641, - sighash: [ - 0xa1, 0xf1, 0xa4, 0xe5, 0xcd, 0x9b, 0xd5, 0x22, 0x32, 0x2d, 0x66, 0x1e, 0xdd, 0x2a, - 0xf1, 0xbf, 0x2a, 0x70, 0x19, 0xcf, 0xab, 0x94, 0xec, 0xe1, 0x8f, 0x4b, 0xa9, 0x35, - 0xb0, 0xa1, 0x90, 0x73, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x42, 0x01, 0xcf, 0xb1, 0xcd, - 0x8d, 0xbf, 0x69, 0xb8, 0x25, 0x0c, 0x18, 0xef, 0x41, 0x29, 0x4c, 0xa9, 0x79, 0x93, - 0xdb, 0x54, 0x6c, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, 0x9d, - 0x4e, 0x30, 0xa7, 0x03, 0xac, 0x6a, 0x00, 0x98, 0x42, 0x1c, 0x69, 0x37, 0x8a, 0xf1, - 0xe4, 0x0f, 0x64, 0xe1, 0x25, 0x94, 0x6f, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, - 0xb6, 0x4b, 0x69, 0x68, 0x91, 0x2a, 0x63, 0x81, 0xce, 0x3d, 0xc1, 0x66, 0xd5, 0x6a, - 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x05, 0x63, 0x63, 0x63, 0x53, 0x53, 0xe8, 0xc7, 0x20, - 0x3d, 0x02, 0xd2, 0xda, 0x86, 0x38, 0x7a, 0xe6, 0x01, 0x00, 0x08, 0x00, 0x63, 0x65, - 0x6a, 0x63, 0xac, 0x52, 0x00, 0xa7, 0x62, 0x29, 0x97, 0xf4, 0xff, 0x04, 0x00, 0x07, - 0x51, 0x51, 0x00, 0x53, 0x53, 0x65, 0x65, 0x97, 0xb0, 0xe4, 0xe4, 0xc7, 0x05, 0xfc, - 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, - 0xfa, 0x3d, 0x5a, 0x57, 0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, - 0xfb, 0x1a, 0x38, 0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c, 0x3e, 0x0a, 0xd3, 0x36, 0x0c, - 0x1d, 0x37, 0x10, 0xac, 0xd2, 0x0b, 0x18, 0x3e, 0x31, 0xd4, 0x9f, 0x25, 0xc9, 0xa1, - 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x51, 0xa7, - 0xaf, 0x9d, 0xb6, 0x99, 0x0e, 0xd8, 0x3d, 0xd6, 0x4a, 0xf3, 0x59, 0x7c, 0x04, 0x32, - 0x3e, 0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, 0x32, - 0x0d, 0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, 0xdf, 0x65, 0x8d, 0x24, 0xae, 0x67, - 0xc2, 0x2c, 0x8d, 0x13, 0x09, 0x13, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, - 0x76, 0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x91, 0xe0, 0x0c, 0x7a, 0x1d, 0x48, 0xaf, 0x04, - 0x68, 0x27, 0x59, 0x1e, 0x97, 0x33, 0xa9, 0x7f, 0xa6, 0xb6, 0x79, 0xf3, 0xdc, 0x60, - 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0xfc, 0x1b, 0xe4, 0xaa, - 0xc0, 0x0f, 0xf2, 0x71, 0x1e, 0xbd, 0x93, 0x1d, 0xe5, 0x18, 0x85, 0x68, 0x78, 0xf7, - 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, 0xf7, 0x39, 0x3c, - 0x94, 0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, - 0x0f, 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, 0xf4, - 0x73, 0xf4, 0x68, 0xa0, 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88, 0x0d, 0x78, 0x0c, - 0xb0, 0x7f, 0xcf, 0xaa, 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, - 0x3d, 0x88, 0x2d, 0x2b, 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, 0x94, 0x94, 0xc6, 0xac, - 0x89, 0x3c, 0x49, 0x72, 0x38, 0x33, 0xec, 0x89, 0x26, 0xc1, 0x03, 0x95, 0x86, 0xa7, - 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, 0x5d, 0x99, 0x58, 0x9c, 0x03, 0xb8, - 0x38, 0xe8, 0xaa, 0xf7, 0x45, 0x53, 0x3e, 0xd9, 0xe8, 0xae, 0x3a, 0x1c, 0xd0, 0x74, - 0xa5, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, 0xed, - 0x42, 0x43, 0x5e, 0x02, 0x47, 0x69, 0x30, 0xd0, 0x69, 0x89, 0x6c, 0xff, 0x30, 0xeb, - 0x41, 0x4f, 0x72, 0x7b, 0x89, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, - 0x75, 0xa4, 0xa6, 0xf2, 0x65, 0x72, 0x50, 0x4b, 0x0b, 0x22, 0x32, 0xec, 0xb9, 0xf0, - 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96, 0xbc, 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, - 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11, 0x7d, 0x41, 0x7a, - 0xdb, 0x3d, 0x15, 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50, 0x0c, 0x6b, 0x8f, - 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, 0xcc, 0x40, 0xa9, - 0x8d, 0x5f, 0x29, 0x03, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, 0xfd, 0xbb, 0x5d, - 0x47, 0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, 0xb7, - 0xfa, 0xe2, 0xdb, 0x58, 0x71, 0x05, 0x41, 0x5d, 0x02, 0x42, 0x78, 0x9d, 0x38, 0xf5, - 0x0b, 0x8d, 0xbc, 0xc1, 0x29, 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, 0x35, 0x5b, 0xcf, - 0x73, 0xce, 0xcb, 0x8c, 0xb8, 0xa5, 0xda, 0x01, 0x30, 0x71, 0x52, 0xf1, 0x39, 0x02, - 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, 0x26, 0xc6, 0xcb, 0x4c, 0xd4, - 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a, 0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15, 0x40, - 0x6f, 0x2f, 0xdd, 0x2a, 0x02, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, 0x2a, - 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, 0x79, - 0xf8, 0x80, 0x08, 0xe3, 0x15, 0xdc, 0x7d, 0x83, 0x88, 0x03, 0x6c, 0x17, 0x82, 0xfd, - 0x27, 0x95, 0xd1, 0x8a, 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, 0x48, - 0x9c, 0xe7, 0x57, 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, 0xdf, - 0x73, 0xca, 0xec, 0x65, 0x60, 0x40, 0x37, 0x31, 0x4f, 0xaa, 0xce, 0xb5, 0x62, 0x18, - 0xc6, 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f, 0x21, 0xa9, 0xfb, - 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, 0x00, 0xe1, 0xb1, 0xa1, - 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, 0x49, 0xe9, 0x50, - 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, 0x1f, 0xe3, 0x89, 0x5b, 0x8c, 0xc3, - 0xd1, 0x4d, 0x2c, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a, 0x69, - 0xf5, 0x33, 0x57, 0xd7, 0x76, 0x7f, 0x4f, 0x5c, 0xcb, 0xdb, 0xc5, 0x96, 0x63, 0x12, - 0x77, 0xf8, 0xfe, 0xcd, 0x08, 0xcb, 0x05, 0x6b, 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, - 0xff, 0xf7, 0xf2, 0x44, 0xfc, 0x71, 0x62, 0x69, 0xb9, 0x26, 0xd6, 0x2e, 0x95, 0x96, - 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff, 0x9e, 0x68, 0x62, 0x5a, 0x19, 0x24, 0x40, - 0xea, 0x06, 0x82, 0x81, 0x23, 0xd9, 0x78, 0x84, 0x80, 0x6f, 0x15, 0xfa, 0x08, 0xda, - 0x52, 0x75, 0x4a, 0x10, 0x95, 0xe3, 0xff, 0x1a, 0xbd, 0x5c, 0xe4, 0xfd, 0xdf, 0xcc, - 0xfc, 0x3a, 0x61, 0x28, 0xae, 0xf7, 0x84, 0xa6, 0x46, 0x10, 0xa8, 0x9d, 0x1a, 0x70, - 0x99, 0x21, 0x6d, 0x08, 0x14, 0xd3, 0xa2, 0xd4, 0x52, 0x43, 0x1c, 0x32, 0xd4, 0x11, - 0xac, 0x1c, 0xce, 0x82, 0xad, 0x02, 0x29, 0x40, 0x7b, 0xbc, 0x48, 0x98, 0x56, 0x75, - 0xe3, 0xf8, 0x74, 0xa4, 0x53, 0x3f, 0x1d, 0x63, 0xa8, 0x4d, 0xfa, 0x3e, 0x0f, 0x46, - 0x0f, 0xe2, 0xf5, 0x7e, 0x34, 0xfb, 0xc7, 0x54, 0x23, 0xc3, 0x73, 0x7f, 0x5b, 0x2a, - 0x06, 0x15, 0xf5, 0x72, 0x2d, 0xb0, 0x41, 0xa3, 0xef, 0x66, 0xfa, 0x48, 0x3a, 0xfd, - 0x3c, 0x2e, 0x19, 0xe5, 0x94, 0x44, 0xa6, 0x4a, 0xdd, 0x6d, 0xf1, 0xd9, 0x63, 0xf5, - 0xdd, 0x5b, 0x50, 0x10, 0xd3, 0xd0, 0x25, 0xf0, 0x28, 0x7c, 0x4c, 0xf1, 0x9c, 0x75, - 0xf3, 0x3d, 0x51, 0xdd, 0xdd, 0xba, 0x5d, 0x65, 0x7b, 0x43, 0xee, 0x8d, 0xa6, 0x45, - 0x44, 0x38, 0x14, 0xcc, 0x73, 0x29, 0xf3, 0xe9, 0xb4, 0xe5, 0x4c, 0x23, 0x6c, 0x29, - 0xaf, 0x39, 0x23, 0x10, 0x17, 0x56, 0xd9, 0xfa, 0x4b, 0xd0, 0xf7, 0xd2, 0xdd, 0xaa, - 0xcb, 0x6b, 0x0f, 0x86, 0xa2, 0x65, 0x8e, 0x0a, 0x07, 0xa0, 0x5a, 0xc5, 0xb9, 0x50, - 0x05, 0x1c, 0xd2, 0x4c, 0x47, 0xa8, 0x8d, 0x13, 0xd6, 0x59, 0xba, 0x2a, 0x46, 0xca, - 0x18, 0x30, 0x81, 0x6d, 0x09, 0xcd, 0x76, 0x46, 0xf7, 0x6f, 0x71, 0x6a, 0xbe, 0xc5, - 0xde, 0x07, 0xfe, 0x9b, 0x52, 0x34, 0x10, 0x80, 0x6e, 0xa6, 0xf2, 0x88, 0xf8, 0x73, - 0x6c, 0x23, 0x35, 0x7c, 0x85, 0xf4, 0x57, 0x91, 0xe1, 0x70, 0x80, 0x29, 0xd9, 0x82, - 0x4d, 0x90, 0x70, 0x46, 0x07, 0xf3, 0x87, 0xa0, 0x3e, 0x49, 0xbf, 0x98, 0x36, 0x57, - 0x44, 0x31, 0x34, 0x5a, 0x78, 0x77, 0xef, 0xaa, 0x8a, 0x08, 0xe7, 0x30, 0x81, 0xef, - 0x8d, 0x62, 0xcb, 0x78, 0x0a, 0xb6, 0x88, 0x3a, 0x50, 0xa0, 0xd4, 0x70, 0x19, 0x0d, - 0xfb, 0xa1, 0x0a, 0x85, 0x7f, 0x82, 0x84, 0x2d, 0x38, 0x25, 0xb3, 0xd6, 0xda, 0x05, - 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, 0xc0, 0xb7, 0x16, 0xc4, 0x8f, 0xbd, 0x46, 0x7f, - 0x75, 0xb7, 0x80, 0x14, 0x9a, 0xe8, 0x80, 0x8f, 0x4e, 0x68, 0xf5, 0x0c, 0x05, 0x36, - 0xac, 0xdd, 0xf6, 0xf1, 0xae, 0xab, 0x01, 0x6b, 0x6b, 0xc1, 0xec, 0x14, 0x4b, 0x4e, - 0x55, 0x3a, 0xcf, 0xd6, 0x70, 0xf7, 0x7e, 0x75, 0x5f, 0xc8, 0x8e, 0x06, 0x77, 0xe3, - 0x1b, 0xa4, 0x59, 0xb4, 0x4e, 0x30, 0x77, 0x68, 0x95, 0x8f, 0xe3, 0x78, 0x9d, 0x41, - 0xc2, 0xb1, 0xff, 0x43, 0x4c, 0xb3, 0x0e, 0x15, 0x91, 0x4f, 0x01, 0xbc, 0x6b, 0xc2, - 0x30, 0x7b, 0x48, 0x8d, 0x25, 0x56, 0xd7, 0xb7, 0x38, 0x0e, 0xa4, 0xff, 0xd7, 0x12, - 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, 0x40, 0x59, 0xf3, 0x96, 0xbf, - 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, 0xa9, 0x44, 0xf7, 0x2d, 0x43, - 0x6a, 0x10, 0x2f, 0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, 0xb0, 0x86, 0xfe, 0x9d, 0x2e, - 0x71, 0x62, 0x47, 0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, 0xec, 0x95, 0x12, 0xbf, 0xb3, - 0xf3, 0x83, 0x27, 0x29, 0x6e, 0xfa, 0xa7, 0x43, 0x28, 0xb1, 0x18, 0xc2, 0x74, 0x02, - 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4, 0xbb, 0xc6, 0x8e, 0x37, 0xc0, 0xaa, 0x7d, - 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, 0x3b, 0x84, 0x1e, 0x75, 0x17, 0x13, 0xa0, 0x29, - 0x43, 0x90, 0x5a, 0xae, 0x08, 0x03, 0xfd, 0x69, 0x44, 0x2e, 0xb7, 0x68, 0x1e, 0xc2, - 0xa0, 0x56, 0x00, 0x05, 0x4e, 0x92, 0xee, 0xd5, 0x55, 0x02, 0x8f, 0x21, 0xb6, 0xa1, - 0x55, 0x26, 0x8a, 0x2d, 0xd6, 0x64, 0x0a, 0x69, 0x30, 0x1a, 0x52, 0xa3, 0x8d, 0x4d, - 0x9f, 0x9f, 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71, 0x18, 0x14, 0x1c, 0xe4, 0xc9, - 0xbe, 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81, 0xa1, 0x55, 0xfa, 0x3a, 0x2b, - 0x9d, 0xaf, 0xd8, 0x2e, 0x65, 0x0b, 0x38, 0x6a, 0xd3, 0xa0, 0x8c, 0xb6, 0xb8, 0x31, - 0x31, 0xac, 0x30, 0x0b, 0x08, 0x46, 0x35, 0x4a, 0x7e, 0xef, 0x9c, 0x41, 0x0e, 0x4b, - 0x62, 0xc4, 0x7c, 0x54, 0x26, 0x90, 0x7d, 0xfc, 0x66, 0x85, 0xc5, 0xc9, 0x9b, 0x71, - 0x41, 0xac, 0x62, 0x6a, 0xb4, 0x76, 0x1f, 0xd3, 0xf4, 0x1e, 0x72, 0x8e, 0x1a, 0x28, - 0xf8, 0x9d, 0xb8, 0x9f, 0xfd, 0xec, 0xa3, 0x64, 0xdd, 0x2f, 0x0f, 0x07, 0x39, 0xf0, - 0x53, 0x45, 0x56, 0x48, 0x31, 0x99, 0xc7, 0x1f, 0x18, 0x93, 0x41, 0xac, 0x9b, 0x78, - 0xa2, 0x69, 0x16, 0x42, 0x06, 0xa0, 0xea, 0x1c, 0xe7, 0x3b, 0xfb, 0x2a, 0x94, 0x2e, - 0x73, 0x70, 0xb2, 0x47, 0xc0, 0x46, 0xf8, 0xe7, 0x5e, 0xf8, 0xe3, 0xf8, 0xbd, 0x82, - 0x1c, 0xf5, 0x77, 0x49, 0x18, 0x64, 0xe2, 0x0e, 0x6d, 0x08, 0xfd, 0x2e, 0x32, 0xb5, - 0x55, 0xc9, 0x2c, 0x66, 0x1f, 0x19, 0x58, 0x8b, 0x72, 0xa8, 0x95, 0x99, 0x71, 0x0a, - 0x88, 0x06, 0x12, 0x53, 0xca, 0x28, 0x5b, 0x63, 0x04, 0xb3, 0x7d, 0xa2, 0xb5, 0x29, - 0x4f, 0x5c, 0xb3, 0x54, 0xa8, 0x94, 0x32, 0x28, 0x48, 0xcc, 0xbd, 0xc7, 0xc2, 0x54, - 0x5b, 0x7d, 0xa5, 0x68, 0xaf, 0xac, 0x87, 0xff, 0xa0, 0x05, 0xc3, 0x12, 0x24, 0x1c, - 0x2d, 0x57, 0xf4, 0xb4, 0x5d, 0x64, 0x19, 0xf0, 0xd2, 0xe2, 0xc5, 0xaf, 0x33, 0xae, - 0x24, 0x37, 0x85, 0xb3, 0x25, 0xcd, 0xab, 0x95, 0x40, 0x4f, 0xc7, 0xae, 0xd7, 0x05, - 0x25, 0xcd, 0xdb, 0x41, 0x87, 0x2c, 0xfc, 0xc2, 0x14, 0xb1, 0x32, 0x32, 0xed, 0xc7, - 0x86, 0x09, 0x75, 0x3d, 0xbf, 0xf9, 0x30, 0xeb, 0x0d, 0xc1, 0x56, 0x61, 0x2b, 0x9c, - 0xb4, 0x34, 0xbc, 0x4b, 0x69, 0x33, 0x92, 0xde, 0xb8, 0x7c, 0x53, 0x04, 0x35, 0x31, - 0x2e, 0xdc, 0xed, 0xc6, 0xa9, 0x61, 0x13, 0x33, 0x38, 0xd7, 0x86, 0xc4, 0xa3, 0xe1, - 0x03, 0xf6, 0x01, 0x10, 0xa1, 0x6b, 0x13, 0x37, 0x12, 0x97, 0x04, 0xbf, 0x47, 0x54, - 0xff, 0x6b, 0xa9, 0xfb, 0xe6, 0x59, 0x51, 0xe6, 0x10, 0x62, 0x0f, 0x71, 0xcd, 0xa8, - 0xfc, 0x87, 0x76, 0x25, 0xf2, 0xc5, 0xbb, 0x04, 0xcb, 0xe1, 0x22, 0x8b, 0x1e, 0x88, - 0x6f, 0x40, 0x50, 0xaf, 0xd8, 0xfe, 0x94, 0xe9, 0x7d, 0x2e, 0x9e, 0x85, 0xc6, 0xbb, - 0x74, 0x8c, 0x00, 0x42, 0xd3, 0x24, 0x9a, 0xbb, 0x13, 0x42, 0xbb, 0x0e, 0xeb, 0xf6, - 0x20, 0x58, 0xbf, 0x3d, 0xe0, 0x80, 0xd9, 0x46, 0x11, 0xa3, 0x75, 0x09, 0x15, 0xb5, - 0xdc, 0x6c, 0x0b, 0x38, 0x99, 0xd4, 0x12, 0x22, 0xba, 0xce, 0x76, 0x0e, 0xe9, 0xc8, - 0x81, 0x8d, 0xed, 0x59, 0x9e, 0x34, 0xc5, 0x6d, 0x73, 0x72, 0xaf, 0x1e, 0xb8, 0x68, - 0x52, 0xf2, 0xa7, 0x32, 0x10, 0x4b, 0xdb, 0x75, 0x07, 0x39, 0xde, 0x6c, 0x2c, 0x6e, - 0x0f, 0x9e, 0xb7, 0xcb, 0x17, 0xf1, 0x94, 0x2b, 0xfc, 0x9f, 0x4f, 0xd6, 0xeb, 0xb6, - 0xb4, 0xcd, 0xd4, 0xda, 0x2b, 0xca, 0x26, 0xfa, 0xc4, 0x57, 0x8e, 0x9f, 0x54, 0x34, - 0x05, 0xac, 0xc7, 0xd8, 0x6f, 0xf5, 0x91, 0x58, 0xbd, 0x0c, 0xba, 0x3a, 0xef, 0x6f, - 0x4a, 0x84, 0x72, 0xd1, 0x44, 0xd9, 0x9f, 0x8b, 0x8d, 0x1d, 0xed, 0xaa, 0x90, 0x77, - 0xd4, 0xf0, 0x1d, 0x4b, 0xb2, 0x7b, 0xbe, 0x31, 0xd8, 0x8f, 0xbe, 0xfa, 0xc3, 0xdc, - 0xd4, 0x79, 0x75, 0x63, 0xa2, 0x6b, 0x1d, 0x61, 0xfc, 0xd9, 0xa4, 0x64, 0xab, 0x21, - 0xed, 0x55, 0x0f, 0xe6, 0xfa, 0x09, 0x69, 0x5b, 0xa0, 0xb2, 0xf1, 0x0e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xea, 0x64, 0x68, 0xcc, 0x6e, 0x20, 0xa6, 0x6f, 0x82, 0x6e, 0x3d, 0x14, 0xc5, 0x00, - 0x6f, 0x05, 0x63, 0x88, 0x7f, 0x5e, 0x12, 0x89, 0xbe, 0x1b, 0x20, 0x04, 0xca, 0xca, - 0x8d, 0x3f, 0x34, 0xd6, 0xe8, 0x4b, 0xf5, 0x9c, 0x1e, 0x04, 0x61, 0x9a, 0x7c, 0x23, - 0xa9, 0x96, 0x94, 0x1d, 0x88, 0x9e, 0x46, 0x22, 0xa9, 0xb9, 0xb1, 0xd5, 0x9d, 0x5e, - 0x31, 0x90, 0x94, 0x31, 0x8c, 0xd4, 0x05, 0xba, 0x27, 0xb7, 0xe2, 0xc0, 0x84, 0x76, - 0x2d, 0x31, 0x45, 0x3e, 0xc4, 0x54, 0x9a, 0x4d, 0x97, 0x72, 0x9d, 0x03, 0x34, 0x60, - 0xfc, 0xf8, 0x9d, 0x64, 0x94, 0xf2, 0xff, 0xd7, 0x89, 0xe9, 0x80, 0x82, 0xea, 0x5c, - 0xe9, 0x53, 0x4b, 0x3a, 0xcd, 0x60, 0xfe, 0x49, 0xe3, 0x7e, 0x4f, 0x66, 0x69, 0x31, - 0x67, 0x73, 0x19, 0xed, 0x89, 0xf8, 0x55, 0x88, 0x74, 0x1b, 0x31, 0x28, 0x90, 0x1a, - 0x93, 0xbd, 0x78, 0xe4, 0xbe, 0x02, 0x25, 0xa9, 0xe2, 0x69, 0x2c, 0x77, 0xc9, 0x69, - 0xed, 0x01, 0x76, 0xbd, 0xf9, 0x55, 0x59, 0x48, 0xcb, 0xd5, 0xa3, 0x32, 0xd0, 0x45, - 0xde, 0x6b, 0xa6, 0xbf, 0x44, 0x90, 0xad, 0xfe, 0x74, 0x44, 0xcd, 0x46, 0x7a, 0x09, - 0x07, 0x54, 0x17, 0xfc, 0xc0, 0x06, 0x2e, 0x49, 0xf0, 0x08, 0xc5, 0x1a, 0xd4, 0x22, - 0x74, 0x39, 0xc1, 0xb4, 0x47, 0x6c, 0xcd, 0x8e, 0x97, 0x86, 0x2d, 0xab, 0x7b, 0xe1, - 0xe8, 0xd3, 0x99, 0xc0, 0x5e, 0xf2, 0x7c, 0x6e, 0x22, 0xee, 0x27, 0x3e, 0x15, 0x78, - 0x6e, 0x39, 0x4c, 0x8f, 0x1b, 0xe3, 0x16, 0x82, 0xa3, 0x01, 0x47, 0x96, 0x3a, 0xc8, - 0xda, 0x8d, 0x41, 0xd8, 0x04, 0x25, 0x84, 0x26, 0xa3, 0xf7, 0x02, 0x89, 0xb8, 0xad, - 0x19, 0xd8, 0xde, 0x13, 0xbe, 0x4e, 0xeb, 0xe3, 0xbd, 0x4c, 0x8a, 0x6f, 0x55, 0xd6, - 0xe0, 0xc3, 0x73, 0xd4, 0x56, 0x85, 0x18, 0x79, 0xf5, 0xfb, 0xc2, 0x82, 0xdb, 0x9e, - 0x13, 0x48, 0x06, 0xbf, 0xf7, 0x1e, 0x11, 0xbc, 0x33, 0xab, 0x75, 0xdd, 0x6c, 0xa0, - 0x67, 0xfb, 0x73, 0xa0, 0x43, 0xb6, 0x46, 0xa7, 0x03, 0x39, 0xca, 0xb4, 0x92, 0x83, - 0x86, 0x78, 0x6d, 0x2f, 0x24, 0x14, 0x1e, 0xe1, 0x20, 0xfd, 0xc3, 0x4d, 0x67, 0x64, - 0xea, 0xfc, 0x66, 0x88, 0x0e, 0xe0, 0x20, 0x4f, 0x53, 0xcc, 0x11, 0x67, 0xed, 0x02, - 0xb4, 0x3a, 0x52, 0xde, 0xa3, 0xca, 0x7c, 0xff, 0x8e, 0xf3, 0x5c, 0xd8, 0xe6, 0xd7, - 0xc1, 0x11, 0xa6, 0x8e, 0xf4, 0x4b, 0xcd, 0x0c, 0x15, 0x13, 0xad, 0x47, 0xca, 0x61, - 0xc6, 0x59, 0xcc, 0x5d, 0x0a, 0x5b, 0x44, 0x0f, 0x6b, 0x9f, 0x59, 0xaf, 0xf6, 0x68, - 0x79, 0xbb, 0x66, 0x88, 0xfd, 0x28, 0x59, 0x36, 0x2b, 0x18, 0x2f, 0x20, 0x7b, 0x31, - 0x75, 0x96, 0x1f, 0x64, 0x11, 0xa4, 0x93, 0xbf, 0xfd, 0x04, 0x8e, 0x7d, 0x0d, 0x87, - 0xd8, 0x2f, 0xe6, 0xf9, 0x90, 0xa2, 0xb0, 0xa2, 0x5f, 0x5a, 0xa0, 0x11, 0x1a, 0x6e, - 0x68, 0xf3, 0x7b, 0xf6, 0xf3, 0xac, 0x2d, 0x26, 0xb8, 0x46, 0x86, 0xe5, 0x69, 0x03, - 0x8d, 0x99, 0xc1, 0x38, 0x35, 0x97, 0xfa, 0xd8, 0x11, 0x93, 0xc4, 0xc1, 0xb1, 0x6e, - 0x6a, 0x90, 0xe2, 0xd5, 0x07, 0xcd, 0xfe, 0x6f, 0xbd, 0xaa, 0x86, 0x16, 0x3e, 0x9c, - 0xf5, 0xde, 0x31, 0x00, 0x03, 0xca, 0x7e, 0x8d, 0xa0, 0x47, 0xb0, 0x90, 0xdb, 0x9f, - 0x37, 0x95, 0x2f, 0xbf, 0xee, 0x76, 0xaf, 0x61, 0x66, 0x81, 0x90, 0xbd, 0x52, 0xed, - 0x49, 0x0e, 0x67, 0x7b, 0x51, 0x5d, 0x01, 0x43, 0x84, 0x03, 0x07, 0x21, 0x9c, 0x7c, - 0x0e, 0xe7, 0xfc, 0x7b, 0xfc, 0x79, 0xf3, 0x25, 0x64, 0x4e, 0x4d, 0xf4, 0xc0, 0xd7, - 0xdb, 0x08, 0xe9, 0xf0, 0xbd, 0x02, 0x49, 0x43, 0xc7, 0x05, 0xab, 0xff, 0x89, 0x94, - 0x03, 0xa6, 0x05, 0xcf, 0xbc, 0x7e, 0xd7, 0x46, 0xa7, 0xd3, 0xf7, 0xc3, 0x7d, 0x9e, - 0x8b, 0xdc, 0x43, 0x3b, 0x7d, 0x79, 0xe0, 0x8a, 0x12, 0xf7, 0x38, 0xa8, 0xf0, 0xdb, - 0xdd, 0xfe, 0xf2, 0xf2, 0x65, 0x02, 0xf3, 0xe4, 0x7d, 0x1b, 0x0f, 0xd1, 0x1e, 0x6a, - 0x13, 0x31, 0x1f, 0xb7, 0x99, 0xc7, 0x9c, 0x64, 0x1d, 0x9d, 0xa4, 0x3b, 0x33, 0xe7, - 0xad, 0x01, 0x2e, 0x28, 0x25, 0x53, 0x98, 0x78, 0x92, 0x62, 0x27, 0x5f, 0x11, 0x75, - 0xbe, 0x84, 0x62, 0xc0, 0x14, 0x91, 0xc4, 0xd8, 0x42, 0x40, 0x6d, 0x0e, 0xc4, 0x28, - 0x2c, 0x95, 0x26, 0x17, 0x4a, 0x09, 0x87, 0x8f, 0xe8, 0xfd, 0xde, 0x33, 0xa2, 0x96, - 0x04, 0xe5, 0xe5, 0xe7, 0xb2, 0xa0, 0x25, 0xd6, 0x65, 0x0b, 0x97, 0xdb, 0xb5, 0x2b, - 0xef, 0xb5, 0x9b, 0x1d, 0x30, 0xa5, 0x74, 0x33, 0xb0, 0xa3, 0x51, 0x47, 0x44, 0x44, - 0x09, 0x9d, 0xaa, 0x37, 0x10, 0x46, 0x61, 0x32, 0x60, 0xcf, 0x33, 0x54, 0xcf, 0xcd, - 0xad, 0xa6, 0x63, 0xec, 0xe8, 0x24, 0xff, 0xd7, 0xe4, 0x43, 0x93, 0x88, 0x6a, 0x86, - 0x16, 0x5d, 0xdd, 0xdf, 0x2b, 0x4c, 0x41, 0x77, 0x35, 0x54, 0xc8, 0x69, 0x95, 0x26, - 0x94, 0x08, 0xb1, 0x1e, 0x67, 0x37, 0xa4, 0xc4, 0x47, 0x58, 0x6f, 0x69, 0x17, 0x34, - 0x46, 0xd8, 0xe4, 0x8b, 0xf8, 0x4c, 0xbc, 0x00, 0x0a, 0x80, 0x78, 0x99, 0x97, 0x3e, - 0xb9, 0x3c, 0x5e, 0x81, 0x9a, 0xad, 0x66, 0x94, 0x13, 0xf8, 0x38, 0x79, 0x33, 0xad, - 0x15, 0x84, 0xaa, 0x35, 0xe4, 0x3f, 0x4e, 0xcd, 0x1e, 0x2d, 0x04, 0x07, 0xc0, 0xb1, - 0xb8, 0x99, 0x20, 0xff, 0xdf, 0xdb, 0x9b, 0xea, 0x51, 0xac, 0x95, 0xb5, 0x57, 0xaf, - 0x71, 0xb8, 0x9f, 0x90, 0x3f, 0x5d, 0x98, 0x48, 0xf1, 0x4f, 0xcb, 0xeb, 0x18, 0x37, - 0x57, 0x0f, 0x54, 0x4d, 0x63, 0x59, 0xeb, 0x23, 0xfa, 0xf3, 0x8a, 0x08, 0x22, 0xda, - 0x36, 0xce, 0x42, 0x6c, 0x4a, 0x2f, 0xbe, 0xff, 0xeb, 0x0a, 0x8a, 0x2e, 0x29, 0x7a, - 0x9d, 0x19, 0xba, 0x15, 0x02, 0x45, 0x90, 0xe3, 0x32, 0x9d, 0x9f, 0xa9, 0x26, 0x1f, - 0x99, 0x38, 0xa4, 0x03, 0x2d, 0xd3, 0x46, 0x06, 0xc9, 0xcf, 0x9f, 0x3d, 0xd3, 0x3e, - 0x57, 0x6f, 0x05, 0xcd, 0x1d, 0xd6, 0x81, 0x1c, 0x62, 0x98, 0x75, 0x7d, 0x77, 0xd9, - 0xe8, 0x10, 0xab, 0xdb, 0x22, 0x6a, 0xfc, 0xaa, 0x43, 0x46, 0xa6, 0x56, 0x0f, 0x89, - 0x32, 0xb3, 0x18, 0x1f, 0xd3, 0x55, 0xd5, 0xd3, 0x91, 0x97, 0x61, 0x83, 0xf8, 0xd9, - 0x93, 0x88, 0x83, 0x96, 0x32, 0xd6, 0x35, 0x4f, 0x66, 0x6d, 0x09, 0xd3, 0xe5, 0x62, - 0x9e, 0xa1, 0x97, 0x37, 0x38, 0x86, 0x13, 0xd3, 0x8a, 0x34, 0xfd, 0x0f, 0x6e, 0x50, - 0xee, 0x5a, 0x0c, 0xc9, 0x67, 0x71, 0x77, 0xf5, 0x00, 0x28, 0xc1, 0x41, 0x37, 0x81, - 0x87, 0xbd, 0x28, 0x19, 0x40, 0x3f, 0xc5, 0x34, 0xf8, 0x00, 0x76, 0xe9, 0x38, 0x0c, - 0xb4, 0x96, 0x4d, 0x3b, 0x6b, 0x45, 0x81, 0x9d, 0x3b, 0x8e, 0x9c, 0xaf, 0x54, 0xf0, - 0x51, 0x85, 0x2d, 0x67, 0x1b, 0xf8, 0xc1, 0xff, 0xde, 0x2d, 0x15, 0x10, 0x75, 0x64, - 0x18, 0xcb, 0x48, 0x10, 0x93, 0x6a, 0xa5, 0x7e, 0x69, 0x65, 0xd6, 0xfb, 0x65, 0x6a, - 0x76, 0x0b, 0x7f, 0x19, 0xad, 0xf9, 0x6c, 0x17, 0x34, 0x88, 0x55, 0x21, 0x93, 0xb1, - 0x47, 0xee, 0x58, 0x85, 0x80, 0x33, 0xda, 0xc7, 0xcd, 0x0e, 0xb2, 0x04, 0xc0, 0x64, - 0x90, 0xbb, 0xde, 0xdf, 0x5f, 0x75, 0x71, 0xac, 0xb2, 0xeb, 0xe7, 0x6a, 0xce, 0xf3, - 0xf2, 0xa0, 0x1e, 0xe9, 0x87, 0x48, 0x6d, 0xfe, 0x6c, 0x3f, 0x0a, 0x5e, 0x23, 0x4c, - 0x12, 0x72, 0x58, 0xf9, 0x7a, 0x28, 0xfb, 0x5d, 0x16, 0x4a, 0x81, 0x76, 0xbe, 0x94, - 0x6b, 0x80, 0x97, 0xd0, 0xe3, 0x17, 0x28, 0x7f, 0x33, 0xbf, 0x9c, 0x16, 0xf9, 0xa5, - 0x45, 0x40, 0x9c, 0xe2, 0x9b, 0x1f, 0x42, 0x73, 0x72, 0x5f, 0xc0, 0xdf, 0x02, 0xa0, - 0x4e, 0xba, 0xe1, 0x78, 0xb3, 0x41, 0x4f, 0xb0, 0xa8, 0x2d, 0x50, 0xde, 0xb0, 0x9f, - 0xcf, 0x4e, 0x6e, 0xe9, 0xd1, 0x80, 0xff, 0x4f, 0x56, 0xff, 0x3b, 0xc1, 0xd3, 0x60, - 0x1f, 0xc2, 0xdc, 0x90, 0xd8, 0x14, 0xc3, 0x25, 0x6f, 0x49, 0x67, 0xd3, 0xa8, 0xd6, - 0x4c, 0x83, 0xfe, 0xa3, 0x39, 0xc5, 0x1f, 0x5a, 0x8e, 0x58, 0x01, 0xfb, 0xb9, 0x78, - 0x35, 0x58, 0x1b, 0x60, 0x24, 0x65, 0xde, 0xe0, 0x4b, 0x59, 0x22, 0xc2, 0x76, 0x1b, - 0x54, 0x24, 0x5b, 0xec, 0x0c, 0x9e, 0xef, 0x2d, 0xb9, 0x7d, 0x22, 0xb2, 0xb3, 0x55, - 0x6c, 0xc9, 0x69, 0xfb, 0xb1, 0x3d, 0x06, 0x50, 0x97, 0x65, 0xa5, 0x2b, 0x3f, 0xac, - 0x54, 0xb9, 0x3f, 0x42, 0x1b, 0xf0, 0x8e, 0x18, 0xd5, 0x2d, 0xdd, 0x52, 0xcc, 0x1c, - 0x8c, 0xa8, 0xad, 0xfa, 0xcc, 0xab, 0x7e, 0x5c, 0xc2, 0xf4, 0x57, 0x3f, 0xbb, 0xf8, - 0x23, 0x9b, 0xb0, 0xb8, 0xae, 0xdb, 0xf8, 0xda, 0xd1, 0x62, 0x82, 0xda, 0x5c, 0x91, - 0x25, 0xdb, 0xa1, 0xc0, 0x59, 0xd0, 0xdf, 0x8a, 0xbf, 0x62, 0x10, 0x78, 0xf0, 0x2d, - 0x6c, 0x4b, 0xc8, 0x6d, 0x40, 0x84, 0x5a, 0xc1, 0xd5, 0x97, 0x10, 0xc4, 0x5f, 0x07, - 0xd5, 0x85, 0xeb, 0x48, 0xb3, 0x2f, 0xc0, 0x16, 0x7b, 0xa2, 0x56, 0xe7, 0x3c, 0xa3, - 0xb9, 0x31, 0x1c, 0x62, 0xd1, 0x09, 0x49, 0x79, 0x57, 0xd8, 0xdb, 0xe1, 0x0a, 0xa3, - 0xe8, 0x66, 0xb4, 0x0c, 0x0b, 0xaa, 0x2b, 0xc4, 0x92, 0xc1, 0x9a, 0xd1, 0xe6, 0x37, - 0x2d, 0x96, 0x22, 0xbf, 0x16, 0x3f, 0xbf, 0xfe, 0xae, 0xee, 0x79, 0x6a, 0x3c, 0xd9, - 0xb6, 0xfb, 0xbf, 0xa4, 0xd7, 0x92, 0xf3, 0x4d, 0x7f, 0xd6, 0xe7, 0x63, 0xcd, 0x58, - 0x59, 0xdd, 0x26, 0x83, 0x3d, 0x21, 0xd9, 0xbc, 0x54, 0x52, 0xbd, 0x19, 0x51, 0x5d, - 0xff, 0x9f, 0x49, 0x95, 0xb3, 0x5b, 0xc0, 0xc1, 0xf8, 0x76, 0xe6, 0xad, 0x11, 0xf2, - 0x45, 0x2d, 0xc9, 0xae, 0x85, 0xae, 0xc0, 0x1f, 0xc5, 0x6f, 0x8c, 0xbf, 0xda, 0x75, - 0xa7, 0x72, 0x7b, 0x75, 0xeb, 0xbd, 0x6b, 0xbf, 0xfb, 0x43, 0xb6, 0x3a, 0x3b, 0x1b, - 0x67, 0x1e, 0x40, 0xfe, 0xb0, 0xdb, 0x00, 0x29, 0x74, 0xa3, 0xc3, 0xb1, 0xa7, 0x88, - 0x56, 0x72, 0x31, 0xbf, 0x63, 0x99, 0xff, 0x89, 0x23, 0x69, 0x81, 0x14, 0x9d, 0x42, - 0x38, 0x02, 0xd2, 0x34, 0x1a, 0x3b, 0xed, 0xb9, 0xdd, 0xcb, 0xac, 0x1f, 0xe7, 0xb6, - 0x43, 0x5e, 0x14, 0x79, 0xc7, 0x2e, 0x70, 0x89, 0xd0, 0x29, 0xe7, 0xfb, 0xba, 0xf3, - 0xcf, 0x37, 0xe9, 0xb9, 0xa6, 0xb7, 0x76, 0x79, 0x1e, 0x4c, 0x5e, 0x6f, 0xda, 0x57, - 0xe8, 0xd5, 0xf1, 0x4c, 0x8c, 0x35, 0xa2, 0xd2, 0x70, 0x84, 0x6b, 0x9d, 0xbe, 0x00, - 0x5c, 0xda, 0x16, 0xaf, 0x44, 0x08, 0xf3, 0xab, 0x06, 0xa9, 0x16, 0xee, 0xeb, 0x9c, - 0x95, 0x94, 0xb7, 0x04, 0x24, 0xa4, 0xc1, 0xd1, 0x71, 0x29, 0x5b, 0x67, 0x63, 0xb2, - 0x2f, 0x47, 0xf8, 0x0b, 0x53, 0xcc, 0xbb, 0x90, 0x4b, 0xd6, 0x8f, 0xd6, 0x5f, 0xbd, - 0x3f, 0xbd, 0xea, 0x10, 0x35, 0xe9, 0x8c, 0x21, 0xa7, 0xdb, 0xc9, 0x1a, 0x9b, 0x5b, - 0xc7, 0x69, 0x0f, 0x05, 0xec, 0x31, 0x7c, 0x97, 0xf8, 0x76, 0x4e, 0xb4, 0x8e, 0x91, - 0x1d, 0x42, 0x8e, 0xc8, 0xd8, 0x61, 0xb7, 0x08, 0xe8, 0x29, 0x8a, 0xcb, 0x62, 0x15, - 0x51, 0x45, 0x15, 0x5a, 0xe9, 0x5f, 0x0a, 0x1d, 0x15, 0x01, 0x03, 0x47, 0x53, 0x14, - 0x6e, 0x22, 0xd0, 0x5f, 0x58, 0x6d, 0x7f, 0x6b, 0x4f, 0xe1, 0x2d, 0xad, 0x9a, 0x17, - 0xf5, 0xdb, 0x70, 0xb1, 0xdb, 0x96, 0xb8, 0xd9, 0xa8, 0x3e, 0xda, 0xdc, 0x96, 0x6c, - 0x8a, 0x54, 0x66, 0xb6, 0x1f, 0xc9, 0x98, 0xc3, 0x1f, 0x10, 0x70, 0xd9, 0xa5, 0xc9, - 0xa6, 0xd2, 0x68, 0xd3, 0x04, 0xfe, 0x6b, 0x8f, 0xd3, 0xb4, 0x01, 0x03, 0x48, 0x61, - 0x1a, 0xbd, 0xcb, 0xd4, 0x9f, 0xe4, 0xf8, 0x5b, 0x62, 0x3c, 0x78, 0x28, 0xc7, 0x13, - 0x82, 0xe1, 0x03, 0x4e, 0xa6, 0x7b, 0xc8, 0xae, 0x97, 0x40, 0x4b, 0x0c, 0x50, 0xb2, - 0xa0, 0x4f, 0x55, 0x9e, 0x49, 0x95, 0x0a, 0xfc, 0xb0, 0xef, 0x46, 0x2a, 0x2a, 0xe0, - 0x24, 0xb0, 0xf0, 0x22, 0x4d, 0xfd, 0x73, 0x68, 0x4b, 0x88, 0xc7, 0xfb, 0xe9, 0x2d, - 0x02, 0xb6, 0x8f, 0x75, 0x9c, 0x47, 0x52, 0x66, 0x3c, 0xd7, 0xb9, 0x7a, 0x14, 0x94, - 0x36, 0x49, 0x30, 0x55, 0x21, 0x32, 0x6b, 0xde, 0x08, 0x56, 0x30, 0x86, 0x46, 0x29, - 0x29, 0x1b, 0xae, 0x25, 0xff, 0x88, 0x22, 0xa1, 0x4c, 0x4b, 0x66, 0x6a, 0x92, 0x59, - 0xad, 0x0d, 0xc4, 0x2a, 0x82, 0x90, 0xac, 0x7b, 0xc7, 0xf5, 0x3a, 0x16, 0xf3, 0x79, - 0xf7, 0x58, 0xe5, 0xde, 0x75, 0x0f, 0x04, 0xfd, 0x7c, 0xad, 0x47, 0x70, 0x1c, 0x85, - 0x97, 0xf9, 0x78, 0x88, 0xbe, 0xa6, 0xfa, 0x0b, 0xf2, 0x99, 0x99, 0x56, 0xfb, 0xfd, - 0x0e, 0xe6, 0x8e, 0xc3, 0x6e, 0x46, 0x88, 0x80, 0x9a, 0xe2, 0x31, 0xeb, 0x8b, 0xc4, - 0x36, 0x9f, 0x5f, 0xe1, 0x57, 0x3f, 0x57, 0xe0, 0x99, 0xd9, 0xc0, 0x99, 0x01, 0xbf, - 0x39, 0xca, 0xac, 0x48, 0xdc, 0x11, 0x95, 0x6a, 0x8a, 0xe9, 0x05, 0xea, 0xd8, 0x69, - 0x54, 0x54, 0x7c, 0x44, 0x8a, 0xe4, 0x3d, 0x31, 0x5e, 0x66, 0x9c, 0x42, 0x42, 0xda, - 0x56, 0x59, 0x38, 0xf4, 0x17, 0xbf, 0x43, 0xce, 0x7b, 0x2b, 0x30, 0xb1, 0xcd, 0x40, - 0x18, 0x38, 0x8e, 0x1a, 0x91, 0x0f, 0x0f, 0xc4, 0x1f, 0xb0, 0x87, 0x7a, 0x59, 0x25, - 0xe4, 0x66, 0x81, 0x9d, 0x37, 0x5b, 0x0a, 0x91, 0x2d, 0x4f, 0xe8, 0x43, 0xb7, 0x6e, - 0xf6, 0xf2, 0x23, 0xf0, 0xf7, 0xc8, 0x94, 0xf3, 0x8f, 0x7a, 0xb7, 0x80, 0xdf, 0xd7, - 0x5f, 0x66, 0x9c, 0x8c, 0x06, 0xcf, 0xfa, 0x43, 0xeb, 0x47, 0x56, 0x5a, 0x50, 0xe3, - 0xb1, 0xfa, 0x45, 0xad, 0x61, 0xce, 0x9a, 0x1c, 0x47, 0x27, 0xb7, 0xaa, 0xa5, 0x35, - 0x62, 0xf5, 0x23, 0xe7, 0x39, 0x52, - ], - script_code: Script(vec![0x53]), - transparent_input: Some(1), - hash_type: 3, - amount: 365293780364847, - consensus_branch_id: 1537743641, - sighash: [ - 0x23, 0x65, 0x2e, 0x76, 0xcb, 0x13, 0xb8, 0x5a, 0x0e, 0x33, 0x63, 0xbb, 0x5f, 0xca, - 0x06, 0x1f, 0xa7, 0x91, 0xc4, 0x0c, 0x53, 0x3e, 0xcc, 0xee, 0x89, 0x93, 0x64, 0xe6, - 0xe6, 0x0b, 0xb4, 0xf7, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x99, 0xa6, 0x9f, 0xdf, 0x1c, - 0x5a, 0xc7, 0x73, 0x21, 0x46, 0xee, 0x5e, 0x1d, 0x6b, 0x6c, 0xa9, 0xb9, 0x18, 0x0f, - 0x96, 0x4c, 0xc9, 0xd0, 0x87, 0x8a, 0xe1, 0x37, 0x35, 0x24, 0xd7, 0xd5, 0x10, 0xe5, - 0x82, 0x27, 0xdf, 0x09, 0x51, 0x53, 0x63, 0x6a, 0x00, 0x6a, 0xac, 0x51, 0x6a, 0xb0, - 0xf1, 0x85, 0x6e, 0x28, 0xd5, 0xc8, 0xaf, 0xb0, 0x95, 0xef, 0x61, 0x84, 0xfe, 0xd6, - 0x51, 0x58, 0x90, 0x22, 0xee, 0xae, 0xa4, 0xc0, 0xce, 0x1f, 0xa6, 0xf0, 0x85, 0x09, - 0x2b, 0x04, 0x97, 0x94, 0x89, 0x17, 0x2b, 0x3e, 0xf8, 0x19, 0x4a, 0x01, 0x63, 0xf5, - 0x72, 0x4d, 0x6b, 0x02, 0xde, 0xe1, 0x36, 0xf3, 0xa9, 0xaa, 0x02, 0x00, 0x03, 0x52, - 0x52, 0xac, 0x17, 0xb7, 0x3f, 0x8d, 0x38, 0x3e, 0x00, 0x00, 0x06, 0xac, 0x63, 0x00, - 0x53, 0xac, 0x51, 0x04, 0xb4, 0x75, 0x56, 0xaf, 0x73, 0xb6, 0x08, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe2, 0x32, 0x1d, 0x14, 0x60, 0x71, 0x78, 0x9d, 0x23, 0x35, 0x93, 0x4a, 0x68, 0x06, - 0x14, 0xe8, 0x35, 0x62, 0xf8, 0x2d, 0xfd, 0x40, 0x5b, 0x54, 0xa4, 0x5e, 0xb3, 0x2c, - 0x16, 0x54, 0x48, 0xd4, 0xd5, 0xd6, 0x1c, 0xa2, 0x85, 0x95, 0x85, 0x36, 0x9f, 0x53, - 0xf1, 0xa1, 0x37, 0xe9, 0xe8, 0x2b, 0x67, 0xb8, 0xfd, 0xaf, 0x01, 0xbd, 0xa5, 0x4a, - 0x31, 0x73, 0x11, 0x89, 0x6a, 0xe1, 0x02, 0x80, 0xa0, 0x32, 0x44, 0x0c, 0x42, 0x0a, - 0x42, 0x1e, 0x94, 0x4d, 0x1e, 0x95, 0x2b, 0x70, 0xd5, 0x82, 0x6c, 0xd3, 0xb0, 0x8b, - 0x7d, 0xb9, 0x63, 0x0f, 0xe4, 0xfd, 0x5f, 0x22, 0x12, 0x5d, 0xe8, 0x40, 0xfc, 0xc4, - 0x0b, 0x98, 0x03, 0x8a, 0xf1, 0x1d, 0x55, 0xbe, 0x25, 0x43, 0x25, 0x97, 0xb4, 0xb6, - 0x5b, 0x9e, 0xc1, 0xc7, 0xa8, 0xbb, 0xfd, 0x05, 0x2c, 0xbf, 0x7e, 0x1c, 0x17, 0x85, - 0x31, 0x49, 0x34, 0xb2, 0x62, 0xd5, 0x85, 0x37, 0x54, 0xf1, 0xf1, 0x77, 0x71, 0xcf, - 0xb7, 0x50, 0x30, 0x72, 0x65, 0x57, 0x53, 0xfa, 0x3f, 0x54, 0xec, 0xc5, 0x87, 0xe9, - 0xf8, 0x3b, 0x58, 0x19, 0x16, 0x09, 0x2d, 0xf2, 0x6e, 0x63, 0xe1, 0x89, 0x94, 0xcb, - 0x0d, 0xb9, 0x1a, 0x0b, 0xbd, 0xc7, 0xb6, 0x11, 0x9b, 0x32, 0x22, 0x2a, 0xdf, 0x5e, - 0x61, 0xd8, 0xd8, 0xae, 0x89, 0xda, 0xe4, 0x95, 0x4b, 0x54, 0x81, 0x3b, 0xb3, 0x3f, - 0x08, 0xd5, 0x62, 0xba, 0x51, 0x3f, 0xee, 0x1b, 0x09, 0xc0, 0xfc, 0xd5, 0x16, 0x05, - 0x54, 0x19, 0x47, 0x4d, 0xd7, 0xfd, 0xa0, 0x38, 0xa8, 0x9c, 0x84, 0xea, 0x7b, 0x94, - 0x68, 0x28, 0x7f, 0x0e, 0xb0, 0xc1, 0x0c, 0x4b, 0x13, 0x25, 0x20, 0x19, 0x4d, 0x3d, - 0x8d, 0x53, 0x51, 0xfc, 0x10, 0xd0, 0x9c, 0x15, 0xc8, 0xcc, 0x10, 0x1a, 0xa1, 0x66, - 0x3b, 0xbf, 0x17, 0xb8, 0x41, 0x11, 0xf3, 0x8b, 0xb4, 0x39, 0xf0, 0x73, 0x53, 0xbd, - 0xea, 0x35, 0x96, 0xd1, 0x5e, 0x71, 0x3e, 0x1e, 0x2e, 0x7d, 0x3f, 0x1c, 0x38, 0x31, - 0x35, 0xb4, 0x7f, 0xa7, 0xf8, 0x1f, 0x46, 0xdf, 0x02, 0x90, 0x2a, 0x40, 0x46, 0x99, - 0xec, 0x91, 0x2f, 0x56, 0x56, 0xc3, 0x5b, 0x85, 0x76, 0x3e, 0x4d, 0xe5, 0x83, 0xae, - 0xca, 0xa1, 0xdf, 0xd5, 0xd2, 0x67, 0x7d, 0x9c, 0x8f, 0xfe, 0xe8, 0x77, 0xf6, 0x03, - 0x40, 0xa5, 0xca, 0x0d, 0x67, 0xf6, 0xe5, 0x54, 0x12, 0x47, 0x39, 0xf8, 0x05, 0xaf, - 0x87, 0x6a, 0xee, 0xde, 0x53, 0xaa, 0x8b, 0x0f, 0x8e, 0x56, 0x04, 0xa7, 0x3c, 0x30, - 0xcb, 0xd0, 0x9d, 0xad, 0x0a, 0x3d, 0x6f, 0x8a, 0x5d, 0xcc, 0x40, 0xde, 0xf4, 0x07, - 0x97, 0x34, 0x21, 0x13, 0xba, 0x20, 0x6f, 0xae, 0x8e, 0xbe, 0x4f, 0x3b, 0xc3, 0xca, - 0xf6, 0x92, 0x59, 0xe4, 0x62, 0xef, 0xf9, 0xba, 0x8b, 0x3f, 0x4b, 0xfa, 0xa1, 0x30, - 0x0c, 0x26, 0x92, 0x5a, 0x87, 0x29, 0xcd, 0x32, 0x91, 0x5b, 0xfc, 0x96, 0x60, 0x86, - 0xf0, 0xd5, 0x56, 0x0b, 0xbe, 0x32, 0xa5, 0x98, 0xc2, 0x2a, 0xdf, 0xb4, 0x8c, 0x03, - 0x72, 0xba, 0x5d, 0x42, 0x87, 0xc0, 0xce, 0xfb, 0xac, 0xfd, 0x8c, 0xe1, 0x95, 0xb4, - 0x96, 0x3c, 0x34, 0xa9, 0x4b, 0xba, 0x7a, 0x17, 0x5d, 0xae, 0x4b, 0xbe, 0x3e, 0xf4, - 0x86, 0x3d, 0x53, 0x70, 0x03, 0x15, 0x09, 0x0f, 0x47, 0xa0, 0x68, 0xe2, 0x27, 0x43, - 0x3f, 0x9e, 0x49, 0xd3, 0xaa, 0x09, 0xe3, 0x56, 0xd8, 0xd6, 0x6d, 0x0c, 0x01, 0x21, - 0xe9, 0x1a, 0x3c, 0x4a, 0xa3, 0xf2, 0x7f, 0xa1, 0xb6, 0x03, 0x96, 0xe2, 0xb4, 0x1d, - 0xb9, 0x08, 0xfd, 0xab, 0x8b, 0x18, 0xcc, 0x73, 0x04, 0xe9, 0x4e, 0x97, 0x05, 0x68, - 0xf9, 0x42, 0x1c, 0x0d, 0xbb, 0xba, 0xf8, 0x45, 0x98, 0xd9, 0x72, 0xb0, 0x53, 0x4f, - 0x02, 0xa5, 0xe5, 0x26, 0x70, 0x43, 0x6a, 0xaa, 0x77, 0x6e, 0xd2, 0x48, 0x2a, 0xd7, - 0x03, 0x43, 0x02, 0x01, 0xe5, 0x34, 0x43, 0xc3, 0x6d, 0xcf, 0xd3, 0x4a, 0x0c, 0xb6, - 0x63, 0x78, 0x76, 0x10, 0x5e, 0x03, 0xbf, 0x3b, 0xd5, 0x8e, 0xc1, 0x48, 0xcb, 0x64, - 0x97, 0x0e, 0x32, 0x23, 0xa9, 0x1f, 0x71, 0xdf, 0xcf, 0xd5, 0xa0, 0x4b, 0x66, 0x7f, - 0xba, 0xf3, 0xd4, 0xb3, 0xb9, 0x08, 0xb9, 0x82, 0x88, 0x20, 0xdf, 0xec, 0xdd, 0x75, - 0x37, 0x50, 0xb5, 0xf9, 0xd2, 0x21, 0x6e, 0x56, 0xc6, 0x15, 0x27, 0x2f, 0x85, 0x44, - 0x64, 0xc0, 0xca, 0x4b, 0x1e, 0x85, 0xae, 0xdd, 0x03, 0x82, 0x92, 0xc4, 0xe1, 0xa5, - 0x77, 0x44, 0xeb, 0xba, 0x01, 0x0b, 0x9e, 0xbf, 0xbb, 0x01, 0x1b, 0xd6, 0xf0, 0xb7, - 0x88, 0x05, 0x02, 0x5d, 0x27, 0xf3, 0xc1, 0x77, 0x46, 0xba, 0xe1, 0x16, 0xc1, 0x5d, - 0x9f, 0x47, 0x1f, 0x0f, 0x62, 0x88, 0xa1, 0x50, 0x64, 0x7b, 0x2a, 0xfe, 0x9d, 0xf7, - 0xcc, 0xcf, 0x01, 0xf5, 0xcd, 0xe5, 0xf0, 0x46, 0x80, 0xbb, 0xfe, 0xd8, 0x7f, 0x6c, - 0xf4, 0x29, 0xfb, 0x27, 0xad, 0x6b, 0xab, 0xe7, 0x91, 0x76, 0x66, 0x11, 0xcf, 0x5b, - 0xc2, 0x0e, 0x48, 0xbe, 0xf1, 0x19, 0x25, 0x9b, 0x9b, 0x8a, 0x0e, 0x39, 0xc3, 0xdf, - 0x28, 0xcb, 0x95, 0x82, 0xea, 0x33, 0x86, 0x01, 0xcd, 0xc4, 0x81, 0xb3, 0x2f, 0xb8, - 0x2a, 0xde, 0xeb, 0xb3, 0xda, 0xde, 0x25, 0xd1, 0xa3, 0xdf, 0x20, 0xc3, 0x7e, 0x71, - 0x25, 0x06, 0xb5, 0xd9, 0x96, 0xc4, 0x9a, 0x9f, 0x0f, 0x30, 0xdd, 0xcb, 0x91, 0xfe, - 0x90, 0x04, 0xe1, 0xe8, 0x32, 0x94, 0xa6, 0xc9, 0x20, 0x3d, 0x94, 0xe8, 0xdc, 0x2c, - 0xbb, 0x44, 0x9d, 0xe4, 0x15, 0x50, 0x32, 0x60, 0x4e, 0x47, 0x99, 0x70, 0x16, 0xb3, - 0x04, 0xfd, 0x43, 0x7d, 0x82, 0x35, 0x04, 0x5e, 0x25, 0x5a, 0x19, 0xb7, 0x43, 0xa0, - 0xa9, 0xf2, 0xe3, 0x36, 0xb4, 0x4c, 0xae, 0x30, 0x7b, 0xb3, 0x98, 0x7b, 0xd3, 0xe4, - 0xe7, 0x77, 0xfb, 0xb3, 0x4c, 0x0a, 0xb8, 0xcc, 0x3d, 0x67, 0x46, 0x6c, 0x0a, 0x88, - 0xdd, 0x4c, 0xca, 0xd1, 0x8a, 0x07, 0xa8, 0xd1, 0x06, 0x8d, 0xf5, 0xb6, 0x29, 0xe5, - 0x71, 0x8d, 0x0f, 0x6d, 0xf5, 0xc9, 0x57, 0xcf, 0x71, 0xbb, 0x00, 0xa5, 0x17, 0x8f, - 0x17, 0x5c, 0xac, 0xa9, 0x44, 0xe6, 0x35, 0xc5, 0x15, 0x9f, 0x73, 0x8e, 0x24, 0x02, - 0xa2, 0xd2, 0x1a, 0xa0, 0x81, 0xe1, 0x0e, 0x45, 0x6a, 0xfb, 0x00, 0xb9, 0xf6, 0x24, - 0x16, 0xc8, 0xb9, 0xc0, 0xf7, 0x22, 0x8f, 0x51, 0x07, 0x29, 0xe0, 0xbe, 0x3f, 0x30, - 0x53, 0x13, 0xd7, 0x7f, 0x73, 0x79, 0xdc, 0x2a, 0xf2, 0x48, 0x69, 0xc6, 0xc7, 0x4e, - 0xe4, 0x47, 0x14, 0x98, 0x86, 0x1d, 0x19, 0x2f, 0x0f, 0xf0, 0xf5, 0x08, 0x28, 0x5d, - 0xab, 0x6b, 0x6a, 0x36, 0xcc, 0xf7, 0xd1, 0x22, 0x56, 0xcc, 0x76, 0xb9, 0x55, 0x03, - 0x72, 0x0a, 0xc6, 0x72, 0xd0, 0x82, 0x68, 0xd2, 0xcf, 0x77, 0x73, 0xb6, 0xba, 0x2a, - 0x5f, 0x66, 0x48, 0x47, 0xbf, 0x70, 0x7f, 0x2f, 0xc1, 0x0c, 0x98, 0xf2, 0xf0, 0x06, - 0xec, 0x22, 0xcc, 0xb5, 0xa8, 0xc8, 0xb7, 0xc4, 0x0c, 0x7c, 0x2d, 0x49, 0xa6, 0x63, - 0x9b, 0x9f, 0x2c, 0xe3, 0x3c, 0x25, 0xc0, 0x4b, 0xc4, 0x61, 0xe7, 0x44, 0xdf, 0xa5, - 0x36, 0xb0, 0x0d, 0x94, 0xba, 0xdd, 0xf4, 0xf4, 0xd1, 0x40, 0x44, 0xc6, 0x95, 0xa3, - 0x38, 0x81, 0x47, 0x7d, 0xf1, 0x24, 0xf0, 0xfc, 0xf2, 0x06, 0xa9, 0xfb, 0x2e, 0x65, - 0xe3, 0x04, 0xcd, 0xbf, 0x0c, 0x4d, 0x23, 0x90, 0x17, 0x0c, 0x13, 0x0a, 0xb8, 0x49, - 0xc2, 0xf2, 0x2b, 0x5c, 0xdd, 0x39, 0x21, 0x64, 0x0c, 0x8c, 0xf1, 0x97, 0x6a, 0xe1, - 0x01, 0x0b, 0x0d, 0xfd, 0x9c, 0xb2, 0x54, 0x3e, 0x45, 0xf9, 0x97, 0x49, 0xcc, 0x4d, - 0x61, 0xf2, 0xe8, 0xaa, 0xbf, 0xe9, 0x8b, 0xd9, 0x05, 0xfa, 0x39, 0x95, 0x1b, 0x33, - 0xea, 0x76, 0x9c, 0x45, 0xab, 0x95, 0x31, 0xc5, 0x72, 0x09, 0x86, 0x2a, 0xd1, 0x2f, - 0xd7, 0x6b, 0xa4, 0x80, 0x7e, 0x65, 0x41, 0x7b, 0x6c, 0xd1, 0x2f, 0xa8, 0xec, 0x91, - 0x6f, 0x01, 0x3e, 0xbb, 0x87, 0x06, 0xa9, 0x6e, 0xff, 0xed, 0xa0, 0x6c, 0x4b, 0xe2, - 0x4b, 0x04, 0x84, 0x63, 0x92, 0xe9, 0xd1, 0xe6, 0x93, 0x0e, 0xae, 0x01, 0xfa, 0x21, - 0xfb, 0xd7, 0x00, 0x58, 0x3f, 0xb5, 0x98, 0xb9, 0x2c, 0x8f, 0x4e, 0xb8, 0xa6, 0x1a, - 0xa6, 0x23, 0x5d, 0xb6, 0x0f, 0x28, 0x41, 0xcf, 0x3a, 0x1c, 0x6a, 0xb5, 0x4c, 0x67, - 0x06, 0x68, 0x44, 0x71, 0x1d, 0x09, 0x1e, 0xb9, 0x31, 0xa1, 0xbd, 0x62, 0x81, 0xae, - 0xdf, 0x2a, 0x0e, 0x8f, 0xab, 0x18, 0x81, 0x72, 0x02, 0xa9, 0xbe, 0x06, 0x40, 0x2e, - 0xd9, 0xcc, 0x72, 0x0c, 0x16, 0xbf, 0xe8, 0x81, 0xe4, 0xdf, 0x42, 0x55, 0xe8, 0x7a, - 0xfb, 0x7f, 0xc6, 0x2f, 0x38, 0x11, 0x6b, 0xbe, 0x03, 0xcd, 0x8a, 0x3c, 0xb1, 0x1a, - 0x27, 0xd5, 0x68, 0x41, 0x47, 0x82, 0xf4, 0x7b, 0x1a, 0x44, 0xc9, 0x7c, 0x68, 0x04, - 0x67, 0x69, 0x4b, 0xc9, 0x70, 0x9d, 0x32, 0x91, 0x6c, 0x97, 0xe8, 0x00, 0x6c, 0xbb, - 0x07, 0xba, 0x0e, 0x41, 0x80, 0xa3, 0x73, 0x80, 0x38, 0xc3, 0x74, 0xc4, 0xcc, 0xe8, - 0xf3, 0x29, 0x59, 0xaf, 0xb2, 0x5f, 0x30, 0x3f, 0x58, 0x15, 0xc4, 0x53, 0x31, 0x24, - 0xac, 0xf9, 0xd1, 0x89, 0x40, 0xe7, 0x75, 0x22, 0xac, 0x5d, 0xc4, 0xb9, 0x57, 0x0a, - 0xae, 0x8f, 0x47, 0xb7, 0xf5, 0x7f, 0xd8, 0x76, 0x7b, 0xea, 0x1a, 0x24, 0xae, 0x7b, - 0xed, 0x65, 0xb4, 0xaf, 0xdc, 0x8f, 0x12, 0x78, 0xc3, 0x0e, 0x2d, 0xb9, 0x8f, 0xd1, - 0x72, 0x73, 0x0a, 0xc6, 0xbb, 0xed, 0x4f, 0x11, 0x27, 0xcd, 0x32, 0xb0, 0x4a, 0x95, - 0xb2, 0x05, 0x52, 0x6c, 0xfc, 0xb4, 0xc4, 0xe1, 0xcc, 0x95, 0x51, 0x75, 0xb3, 0xe8, - 0xde, 0x1f, 0x5d, 0x81, 0xb1, 0x86, 0x69, 0x69, 0x23, 0x50, 0xaa, 0xa1, 0xa1, 0xd7, - 0x97, 0x61, 0x75, 0x82, 0xe5, 0x4d, 0x7a, 0x5b, 0x57, 0xa6, 0x83, 0xb3, 0x2f, 0xb1, - 0x09, 0x80, 0x62, 0xda, 0xd7, 0xb0, 0xc2, 0xeb, 0x51, 0x8f, 0x68, 0x62, 0xe8, 0x3d, - 0xb2, 0x5e, 0x3d, 0xba, 0xf7, 0xae, 0xd5, 0x04, 0xde, 0x93, 0x2a, 0xcb, 0x99, 0xd7, - 0x35, 0x99, 0x2c, 0xe6, 0x2b, 0xae, 0x9e, 0xf8, 0x93, 0xff, 0x6a, 0xcc, 0x0f, 0xfc, - 0xf8, 0xe3, 0x48, 0x3e, 0x14, 0x6b, 0x9d, 0x49, 0xdd, 0x8c, 0x78, 0x35, 0xf4, 0x3a, - 0x37, 0xdc, 0xa0, 0x78, 0x7e, 0x3e, 0xc9, 0xf6, 0x60, 0x52, 0x23, 0xd5, 0xba, 0x7a, - 0xe0, 0xab, 0x90, 0x25, 0xb7, 0x3b, 0xc0, 0x3f, 0x7f, 0xac, 0x36, 0xc0, 0x09, 0xa5, - 0x6d, 0x4d, 0x95, 0xd1, 0xe8, 0x1d, 0x3b, 0x3e, 0xbc, 0xa7, 0xe5, 0x4c, 0xc1, 0xa1, - 0x2d, 0x12, 0x7b, 0x57, 0xc8, 0x13, 0x89, 0x76, 0xe7, 0x91, 0x01, 0x3b, 0x01, 0x5f, - 0x06, 0xa6, 0x24, 0xf5, 0x21, 0xb6, 0xee, 0x04, 0xec, 0x98, 0x08, 0x93, 0xc7, 0xe5, - 0xe0, 0x1a, 0x33, 0x62, 0x03, 0x59, 0x40, 0x94, 0xf8, 0x28, 0x33, 0xd7, 0x44, 0x5f, - 0xe2, 0xd0, 0x91, 0x30, 0xf6, 0x35, 0x11, 0xda, 0x54, 0x83, 0x2d, 0xe9, 0x13, 0x6b, - 0x39, 0xf4, 0x59, 0x9f, 0x5a, 0xa5, 0xdf, 0xbb, 0x45, 0xda, 0x60, 0xcd, 0xce, 0xab, - 0x7e, 0xef, 0xde, 0x89, 0xbe, 0x63, 0xf3, 0xf7, 0xc0, 0xd2, 0x32, 0x48, 0x47, 0xcc, - 0xe1, 0x40, 0x5d, 0xef, 0x7c, 0x46, 0x9b, 0x0e, 0x27, 0x24, 0x94, 0xe5, 0xdf, 0x54, - 0xf5, 0x68, 0x65, 0x6c, 0xb9, 0xc8, 0x81, 0x8d, 0x92, 0xb7, 0x2b, 0x8b, 0xc3, 0x4d, - 0xb7, 0xbb, 0x31, 0x12, 0x48, 0x7e, 0x74, 0x6e, 0xef, 0xe4, 0xe8, 0x08, 0xbb, 0xb2, - 0x87, 0xd9, 0x9b, 0xf0, 0x7d, 0x00, 0xda, 0xbe, 0xde, 0xdc, 0x5e, 0x5f, 0x07, 0x4f, - 0xfe, 0xae, 0x0c, 0xba, 0x7d, 0xa3, 0xa5, 0x16, 0xc1, 0x73, 0xbe, 0x1c, 0x51, 0x33, - 0x23, 0xe1, 0x19, 0xf6, 0x35, 0xe8, 0x20, 0x9a, 0x07, 0x4b, 0x21, 0x6b, 0x70, 0x23, - 0xfa, 0xdc, 0x2d, 0x25, 0x94, 0x9c, 0x90, 0x03, 0x7e, 0x71, 0xe3, 0xe5, 0x50, 0x72, - 0x6d, 0x21, 0x0a, 0x2c, 0x68, 0x83, 0x42, 0xe5, 0x24, 0x40, 0x63, 0x5e, 0x9c, 0xc1, - 0x4a, 0xfe, 0x10, 0x10, 0x26, 0x21, 0xa9, 0xc9, 0xac, 0xcb, 0x78, 0x2e, 0x9e, 0x4a, - 0x5f, 0xa8, 0x7f, 0x0a, 0x95, 0x6f, 0x5b, 0x85, 0x50, 0x99, 0x60, 0x28, 0x5c, 0x22, - 0x62, 0x7c, 0x59, 0x48, 0x3a, 0x5a, 0x4c, 0x28, 0xcc, 0xe4, 0xb1, 0x56, 0xe5, 0x51, - 0x40, 0x6a, 0x7e, 0xe8, 0x35, 0x56, 0x56, 0xa2, 0x1e, 0x43, 0xe3, 0x8c, 0xe1, 0x29, - 0xfd, 0xad, 0xb7, 0x59, 0xed, 0xdf, 0xa0, 0x8f, 0x00, 0xfc, 0x8e, 0x56, 0x7c, 0xef, - 0x93, 0xc6, 0x79, 0x2d, 0x01, 0xdf, 0x05, 0xe6, 0xd5, 0x80, 0xf4, 0xd5, 0xd4, 0x8d, - 0xf0, 0x42, 0x45, 0x1a, 0x33, 0x59, 0x0d, 0x3e, 0x8c, 0xf4, 0x9b, 0x26, 0x27, 0x21, - 0x8f, 0x0c, 0x29, 0x2f, 0xa6, 0x6a, 0xda, 0x94, 0x5f, 0xa5, 0x5b, 0xb2, 0x35, 0x48, - 0xe3, 0x3a, 0x83, 0xa5, 0x62, 0x95, 0x7a, 0x31, 0x49, 0xa9, 0x93, 0xcc, 0x47, 0x23, - 0x62, 0x29, 0x87, 0x36, 0xa8, 0xb7, 0x78, 0xd9, 0x7c, 0xe4, 0x23, 0x01, 0x3d, 0x64, - 0xb3, 0x2c, 0xd1, 0x72, 0xef, 0xa5, 0x51, 0xbf, 0x7f, 0x36, 0x8f, 0x04, 0xbd, 0xae, - 0xc6, 0x09, 0x1a, 0x30, 0x04, 0xa7, 0x57, 0x59, 0x8b, 0x80, 0x1d, 0xcf, 0x67, 0x5c, - 0xb8, 0x3e, 0x43, 0xa5, 0x3a, 0xe8, 0xb2, 0x54, 0xd3, 0x33, 0xbc, 0xda, 0x20, 0xd4, - 0x81, 0x7d, 0x34, 0x77, 0xab, 0xfb, 0xa2, 0x5b, 0xb8, 0x3d, 0xf5, 0x94, 0x9c, 0x12, - 0x6f, 0x14, 0x9b, 0x1d, 0x99, 0x34, 0x1e, 0x4e, 0x6f, 0x91, 0x20, 0xf4, 0xd4, 0x1e, - 0x62, 0x91, 0x85, 0x00, 0x2c, 0x72, 0xc0, 0x12, 0xc4, 0x14, 0xd2, 0x38, 0x2a, 0x6d, - 0x47, 0xc7, 0xb3, 0xde, 0xab, 0xa7, - ], - script_code: Script(vec![0xac, 0x00]), - transparent_input: Some(0), - hash_type: 3, - amount: 711752082734717, - consensus_branch_id: 1537743641, - sighash: [ - 0xb3, 0x8e, 0x31, 0x70, 0x8c, 0xb7, 0x8e, 0xee, 0xc7, 0x66, 0x3e, 0xca, 0x1e, 0x01, - 0xb7, 0x53, 0x9e, 0x26, 0xb7, 0x30, 0xcf, 0x44, 0x6d, 0x3b, 0xf5, 0x7a, 0x99, 0x8e, - 0x9e, 0xd9, 0x2b, 0x47, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0x0d, 0x38, 0x6a, 0xe3, - 0x0d, 0xd3, 0x01, 0x00, 0x07, 0x00, 0x65, 0x51, 0x65, 0x53, 0x53, 0x6a, 0xd5, 0x09, - 0x42, 0xf7, 0x69, 0x67, 0x02, 0x00, 0x05, 0xac, 0x65, 0x63, 0x65, 0xac, 0x61, 0xa7, - 0xb8, 0xb9, 0xf6, 0x99, 0xd6, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x49, 0x23, 0x2f, 0x32, - 0x9f, 0xef, 0x95, 0xc7, 0xaf, 0x37, 0x00, 0x98, 0xff, 0xe4, 0x91, 0x8e, 0x0c, 0xa1, - 0xdf, 0x47, 0xf2, 0x75, 0x86, 0x7b, 0x73, 0x9e, 0x0a, 0x51, 0x4d, 0x32, 0x09, 0x32, - 0x5e, 0x21, 0x70, 0x45, 0x92, 0x7b, 0x47, 0x9c, 0x1c, 0xe2, 0xe5, 0xd5, 0x4f, 0x25, - 0x48, 0x8c, 0xad, 0x15, 0x13, 0xe3, 0xf4, 0x4a, 0x21, 0x26, 0x6c, 0xfd, 0x84, 0x16, - 0x33, 0x32, 0x7d, 0xee, 0x6c, 0xf8, 0x10, 0xfb, 0xf7, 0x39, 0x3e, 0x31, 0x7d, 0x9e, - 0x53, 0xd1, 0xbe, 0x1d, 0x5a, 0xe7, 0x83, 0x9b, 0x66, 0xb9, 0x43, 0xb9, 0xed, 0x18, - 0xf2, 0xc5, 0x30, 0xe9, 0x75, 0x42, 0x23, 0x32, 0xc3, 0x43, 0x9c, 0xce, 0x49, 0xa2, - 0x9f, 0x2a, 0x33, 0x6a, 0x48, 0x51, 0x26, 0x3c, 0x5e, 0x9b, 0xd1, 0x3d, 0x73, 0x11, - 0x09, 0xe8, 0x44, 0xb7, 0xf8, 0xc3, 0x92, 0xa5, 0xc1, 0xdc, 0xaa, 0x2a, 0xe5, 0xf5, - 0x0f, 0xf6, 0x3f, 0xab, 0x97, 0x65, 0xe0, 0x16, 0x70, 0x2c, 0x35, 0xa6, 0x7c, 0xd7, - 0x36, 0x4d, 0x3f, 0xab, 0x55, 0x2f, 0xb3, 0x49, 0xe3, 0x5c, 0x15, 0xc5, 0x02, 0x50, - 0x45, 0x3f, 0xd1, 0x8f, 0x7b, 0x85, 0x59, 0x92, 0x63, 0x2e, 0x2c, 0x76, 0xc0, 0xfb, - 0xf1, 0xef, 0x96, 0x3e, 0xa8, 0x0e, 0x32, 0x23, 0xde, 0x32, 0x77, 0xbc, 0x55, 0x92, - 0x51, 0x72, 0x58, 0x29, 0xec, 0x03, 0xf2, 0x13, 0xba, 0x89, 0x55, 0xca, 0xb2, 0x82, - 0x2f, 0xf2, 0x1a, 0x9b, 0x0a, 0x49, 0x04, 0xd6, 0x68, 0xfc, 0xd7, 0x72, 0x24, 0xbd, - 0xe3, 0xdd, 0x01, 0xf6, 0xff, 0xc4, 0x82, 0x8f, 0x6b, 0x64, 0x23, 0x0b, 0x35, 0xc6, - 0xa0, 0x49, 0x87, 0x34, 0x94, 0x27, 0x6e, 0xa1, 0xd7, 0xed, 0x5e, 0x92, 0xcb, 0x4f, - 0x90, 0xba, 0x83, 0xa9, 0xe4, 0x96, 0x01, 0xb1, 0x94, 0x04, 0x2f, 0x29, 0x00, 0xd9, - 0x9d, 0x31, 0x2d, 0x7b, 0x70, 0x50, 0x8c, 0xf1, 0x76, 0x06, 0x6d, 0x15, 0x4d, 0xbe, - 0x96, 0xef, 0x9d, 0x43, 0x67, 0xe4, 0xc8, 0x40, 0xe4, 0xa1, 0x7b, 0x5e, 0x51, 0x22, - 0xe8, 0xeb, 0xe2, 0x03, 0x8a, 0x3c, 0x5f, 0x4c, 0xba, 0xe2, 0x1e, 0xa3, 0xfa, 0x1a, - 0xe6, 0xc2, 0x5a, 0x94, 0x62, 0xeb, 0xcb, 0xb0, 0xfd, 0x5f, 0x14, 0x55, 0x4b, 0xc9, - 0x77, 0x47, 0xc3, 0x3e, 0x34, 0xda, 0x90, 0xc8, 0x02, 0xd8, 0xd0, 0xd5, 0x0b, 0xfe, - 0x37, 0x61, 0x8c, 0x58, 0x12, 0x89, 0x14, 0x84, 0xfa, 0x25, 0x93, 0x22, 0xc1, 0x50, - 0x92, 0xd4, 0x15, 0x5d, 0x86, 0x96, 0xd6, 0xf1, 0x2f, 0x24, 0xfd, 0x36, 0x44, 0x0a, - 0xb3, 0xbe, 0x08, 0x71, 0xca, 0x3d, 0xd9, 0x62, 0x53, 0x48, 0xa6, 0x14, 0xb5, 0x9b, - 0xde, 0x45, 0x88, 0x56, 0x49, 0xba, 0xe3, 0x6d, 0xe3, 0x4d, 0xef, 0x8f, 0xce, 0xc8, - 0x53, 0x43, 0x47, 0x5d, 0x97, 0x6a, 0xe1, 0xe9, 0xb2, 0x78, 0x29, 0xce, 0x2a, 0xc5, - 0xef, 0xd0, 0xb3, 0x99, 0xa8, 0xb4, 0x48, 0xbe, 0x65, 0x04, 0x29, 0x4e, 0xe6, 0xb3, - 0xc1, 0xc6, 0xa5, 0x34, 0x2d, 0x7c, 0x01, 0xae, 0x03, 0x8a, 0xd3, 0x07, 0x0c, 0x2b, - 0x1a, 0x91, 0x57, 0x3a, 0xf5, 0xe0, 0xc5, 0xe4, 0xcb, 0xbf, 0x4a, 0xcd, 0xc6, 0xb5, - 0x4c, 0x92, 0x72, 0x20, 0x0d, 0x99, 0x70, 0x25, 0x0c, 0x17, 0xc1, 0x03, 0x6f, 0x02, - 0x08, 0x5c, 0x41, 0x85, 0x8e, 0xd3, 0xa0, 0xc4, 0x81, 0x50, 0xbc, 0x69, 0x7e, 0x4a, - 0x69, 0x5f, 0xef, 0x33, 0x5f, 0x7a, 0xd0, 0x7e, 0x1a, 0x46, 0xdc, 0x76, 0x7f, 0xf8, - 0x22, 0xdb, 0x70, 0xe6, 0x02, 0x90, 0x80, 0xb9, 0x81, 0x6b, 0x22, 0x32, 0xc8, 0x1a, - 0x4c, 0x66, 0xcc, 0x58, 0x6a, 0xbf, 0xe1, 0xea, 0xa8, 0xca, 0x6c, 0xf4, 0x1f, 0xc3, - 0xc3, 0xe6, 0xc7, 0xb8, 0x86, 0xfb, 0x6d, 0xac, 0x9f, 0x02, 0x22, 0xb4, 0xfc, 0x6f, - 0xff, 0x9d, 0x05, 0x13, 0xd6, 0x1a, 0x21, 0xc8, 0x0a, 0x37, 0x76, 0x71, 0xd1, 0x35, - 0xa6, 0x68, 0xa0, 0xae, 0x2b, 0xb9, 0x34, 0xc8, 0x2c, 0x41, 0x42, 0xda, 0x69, 0xd1, - 0x02, 0xa7, 0xde, 0x9a, 0x7d, 0xf7, 0x06, 0x40, 0x0e, 0xc7, 0x98, 0x78, 0xd8, 0x68, - 0xe1, 0x7e, 0x8f, 0x71, 0xea, 0x31, 0x49, 0x5a, 0xf8, 0x19, 0xa0, 0x16, 0xcc, 0x41, - 0x9e, 0x07, 0xc5, 0x01, 0xaa, 0x83, 0x09, 0xb2, 0xe6, 0xc8, 0x5b, 0x79, 0xb2, 0x76, - 0x37, 0x33, 0xa3, 0x7b, 0xbc, 0x04, 0x20, 0xd4, 0x25, 0x37, 0xb8, 0x71, 0xb4, 0x29, - 0x4a, 0x65, 0xd3, 0xe0, 0x55, 0xff, 0x71, 0x8d, 0xd9, 0xdc, 0x8c, 0x75, 0xe7, 0xe5, - 0xb2, 0xef, 0xe4, 0x42, 0x63, 0x73, 0x71, 0xb7, 0xc4, 0x8f, 0x6e, 0xe9, 0x9e, 0x3e, - 0xa3, 0x8a, 0x4b, 0x0f, 0x2f, 0x67, 0xfc, 0x2b, 0x90, 0x8c, 0xda, 0x65, 0x7e, 0xae, - 0x75, 0x4e, 0x03, 0x7e, 0x26, 0x2e, 0x9a, 0x9f, 0x9b, 0xd7, 0xec, 0x42, 0x67, 0xed, - 0x8e, 0x96, 0x93, 0x0e, 0x10, 0x84, 0x78, 0x3c, 0x37, 0xd6, 0xf9, 0xdd, 0x15, 0xfd, - 0x29, 0xf4, 0xcc, 0x47, 0x7e, 0x66, 0xf1, 0x30, 0xd6, 0x30, 0x43, 0x0d, 0xcc, 0x01, - 0x04, 0x89, 0x9b, 0x4f, 0x9f, 0x46, 0xeb, 0x09, 0x0e, 0xf7, 0xfc, 0x90, 0xb4, 0x79, - 0xab, 0xf6, 0x1f, 0x93, 0x95, 0x5e, 0xe0, 0x0e, 0x6a, 0x18, 0x48, 0xf1, 0xab, 0x14, - 0xad, 0x33, 0x4f, 0x2b, 0x68, 0x03, 0x58, 0x08, 0xcd, 0xf1, 0xbb, 0x9e, 0x9d, 0x9a, - 0x81, 0x6b, 0xaf, 0x72, 0x8a, 0x95, 0x5b, 0x96, 0x0b, 0x77, 0x01, 0xfa, 0x62, 0x66, - 0x87, 0xdc, 0x3c, 0x9c, 0xba, 0x64, 0x63, 0x37, 0xb5, 0x3e, 0x29, 0x81, 0x6e, 0x94, - 0x82, 0xdd, 0xf5, 0x57, 0x8a, 0x87, 0x68, 0xaa, 0xe4, 0x77, 0xfc, 0xe4, 0x10, 0xac, - 0x2d, 0x5d, 0xe6, 0x09, 0x58, 0x61, 0xc1, 0x11, 0xd7, 0xfe, 0xb3, 0xe6, 0xbb, 0x4f, - 0xbb, 0x5a, 0x54, 0x95, 0x54, 0x95, 0x97, 0x27, 0x98, 0x35, 0x0a, 0x25, 0x3f, 0x05, - 0xf6, 0x6c, 0x2e, 0xcf, 0xcb, 0xc0, 0xed, 0x43, 0xf5, 0xec, 0x2e, 0x6d, 0x8d, 0xba, - 0x15, 0xa5, 0x12, 0x54, 0xd9, 0x7b, 0x18, 0x21, 0x10, 0x7c, 0x07, 0xdd, 0x9a, 0x16, - 0xef, 0x84, 0x06, 0xf9, 0x43, 0xe2, 0x82, 0xb9, 0x5d, 0x4b, 0x36, 0x25, 0x30, 0xc9, - 0x13, 0xd6, 0xba, 0x42, 0x1d, 0xf6, 0x02, 0x7d, 0xe5, 0xaf, 0x1e, 0x47, 0x45, 0xd5, - 0x86, 0x81, 0x06, 0x95, 0x4b, 0xe6, 0xc1, 0x96, 0x27, 0x80, 0xa2, 0x94, 0x10, 0x72, - 0xe9, 0x51, 0x31, 0xb1, 0x67, 0x9d, 0xf0, 0x63, 0x76, 0x25, 0x04, 0x2c, 0x37, 0xd4, - 0x8f, 0xfb, 0x15, 0x2e, 0x5e, 0xbc, 0x18, 0x5c, 0x8a, 0x2b, 0x7d, 0x43, 0x85, 0xf1, - 0xc9, 0x5a, 0xf9, 0x37, 0xdf, 0x78, 0xdf, 0xd8, 0x75, 0x7f, 0xab, 0x43, 0x49, 0x68, - 0xb0, 0xb5, 0x7c, 0x66, 0x57, 0x44, 0x68, 0xf1, 0x60, 0xb4, 0x47, 0xac, 0x82, 0x21, - 0xe5, 0x06, 0x06, 0x76, 0xa8, 0x42, 0xa1, 0xc6, 0xb7, 0x17, 0x2d, 0xd3, 0x34, 0x0f, - 0x76, 0x40, 0x70, 0xab, 0x1f, 0xe0, 0x91, 0xc5, 0xc7, 0x4c, 0x95, 0xa5, 0xdc, 0x04, - 0x33, 0x90, 0x72, 0x3a, 0x4c, 0x12, 0x7d, 0xa1, 0x4c, 0xdd, 0xe1, 0xdc, 0x26, 0x75, - 0xa6, 0x23, 0x40, 0xb3, 0xe6, 0xaf, 0xd0, 0x52, 0x2a, 0x31, 0xde, 0x26, 0xe7, 0xd1, - 0xec, 0x3a, 0x9c, 0x8a, 0x09, 0x1f, 0xfd, 0xc7, 0x5b, 0x7e, 0xcf, 0xdc, 0x7c, 0x12, - 0x99, 0x5a, 0x5e, 0x37, 0xce, 0x34, 0x88, 0xbd, 0x29, 0xf8, 0x62, 0x9d, 0x68, 0xf6, - 0x96, 0x49, 0x24, 0x48, 0xdd, 0x52, 0x66, 0x97, 0x47, 0x6d, 0xc0, 0x61, 0x34, 0x6e, - 0xbe, 0x3f, 0x67, 0x72, 0x17, 0xff, 0x9c, 0x60, 0xef, 0xce, 0x94, 0x3a, 0xf2, 0x8d, - 0xfd, 0x3f, 0x9e, 0x59, 0x69, 0x25, 0x98, 0xa6, 0x04, 0x7c, 0x23, 0xc4, 0xc0, 0x14, - 0x00, 0xf1, 0xab, 0x57, 0x30, 0xea, 0xc0, 0xae, 0x8d, 0x58, 0x43, 0xd5, 0x05, 0x1c, - 0x37, 0x62, 0x40, 0x17, 0x2a, 0xf2, 0x18, 0xd7, 0xa1, 0xec, 0xfe, 0x65, 0xb4, 0xf7, - 0x51, 0x00, 0x63, 0x89, 0x83, 0xc1, 0x4d, 0xe4, 0x97, 0x47, 0x55, 0xda, 0xde, 0x80, - 0x18, 0xc9, 0xb8, 0xf4, 0x54, 0x3f, 0xb0, 0x95, 0x96, 0x15, 0x13, 0xe6, 0x7c, 0x61, - 0xdb, 0xc5, 0x9c, 0x60, 0x7f, 0x9b, 0x51, 0xf8, 0xd0, 0x9b, 0xdc, 0xad, 0x28, 0xbc, - 0xfb, 0x9e, 0x5d, 0x27, 0x44, 0xea, 0x88, 0x48, 0xb2, 0x62, 0x3a, 0xc0, 0x7f, 0x8e, - 0xf6, 0x1a, 0x81, 0xa3, 0x59, 0x10, 0xb8, 0xa1, 0xba, 0xf3, 0x9a, 0x91, 0x9a, 0x7b, - 0x60, 0xbc, 0x60, 0x4d, 0x63, 0x18, 0x5f, 0x75, 0x92, 0x21, 0xd8, 0x47, 0xcc, 0x54, - 0xa2, 0x27, 0x65, 0xa4, 0xc3, 0x34, 0x75, 0xb5, 0x79, 0x1e, 0x9a, 0xf3, 0x27, 0x1f, - 0xc8, 0xd9, 0x35, 0x06, 0x67, 0x09, 0x0d, 0x81, 0x84, 0xec, 0x50, 0x52, 0x2d, 0x80, - 0x4f, 0x23, 0xc4, 0xfb, 0x44, 0xff, 0xa4, 0x81, 0xbc, 0x92, 0xae, 0x40, 0x8d, 0x1b, - 0x9f, 0x2b, 0x13, 0x19, 0x04, 0xf9, 0x70, 0x5c, 0x59, 0xe2, 0xf4, 0xbd, 0xe7, 0xa3, - 0xb2, 0xc0, 0x85, 0xd9, 0x3f, 0xd2, 0xab, 0xc5, 0xe1, 0x4d, 0x16, 0x30, 0x01, 0xa1, - 0x2f, 0x51, 0x93, 0x8d, 0x02, 0x1a, 0xfa, 0x92, 0x23, 0x9b, 0x87, 0x3d, 0xc6, 0xc3, - 0x57, 0xea, 0xa8, 0xaf, 0x4e, 0xe6, 0xd0, 0x05, 0x40, 0x65, 0x7f, 0xe3, 0x29, 0x14, - 0x10, 0x3b, 0x5d, 0x98, 0xf6, 0x8b, 0xd3, 0xe2, 0xb5, 0x35, 0x9f, 0x08, 0xcc, 0xd8, - 0x8d, 0x0c, 0x81, 0x1e, 0x4c, 0x31, 0xfb, 0xb4, 0x9f, 0x3a, 0x90, 0xbb, 0xd0, 0x5d, - 0xce, 0x62, 0xf3, 0x44, 0xe7, 0x07, 0x75, 0x93, 0x15, 0x9a, 0xe3, 0x50, 0x50, 0xb0, - 0x4c, 0x9e, 0x6b, 0x86, 0xbc, 0x43, 0x2d, 0xc8, 0xb0, 0x48, 0xc7, 0x3c, 0x00, 0x18, - 0xca, 0x5b, 0x69, 0x41, 0x12, 0x97, 0x73, 0x2a, 0x4e, 0x1a, 0xa9, 0x9a, 0x92, 0x8c, - 0x71, 0xe7, 0xa2, 0x4f, 0xd2, 0x77, 0x85, 0x6a, 0xa4, 0x25, 0x01, 0xe5, 0x1b, 0x01, - 0x2a, 0xea, 0x94, 0x46, 0xa2, 0x10, 0x4e, 0x93, 0xf8, 0x15, 0xa0, 0xb3, 0xa2, 0x9b, - 0x45, 0x83, 0x14, 0xf3, 0xd8, 0xbe, 0x2b, 0x98, 0x23, 0xd3, 0x42, 0xf4, 0x62, 0x13, - 0xe9, 0x42, 0xa7, 0xe1, 0x9a, 0x46, 0xe9, 0x70, 0xb5, 0xc5, 0x06, 0x70, 0x84, 0x30, - 0x31, 0x7b, 0x1b, 0xb3, 0xb3, 0x5d, 0xf6, 0x8a, 0xe3, 0x3a, 0x49, 0x26, 0xa0, 0x3e, - 0x6b, 0xfe, 0xb5, 0x51, 0x04, 0x16, 0xfc, 0xbb, 0x05, 0x24, 0xc9, 0xca, 0x50, 0x74, - 0x15, 0x6c, 0xc5, 0xa5, 0xd6, 0xfe, 0x1c, 0x99, 0x5e, 0xdc, 0x60, 0xa2, 0xf5, 0x50, - 0x41, 0x1a, 0xa4, 0x1e, 0x3d, 0xa3, 0xbd, 0xcf, 0x64, 0xbc, 0xf0, 0x4a, 0x05, 0x10, - 0x57, 0x1b, 0x93, 0x6d, 0x47, 0xe5, 0x5c, 0xec, 0x03, 0x30, 0xee, 0x8d, 0xfe, 0x73, - 0x56, 0x34, 0x04, 0xf0, 0x47, 0xd7, 0xf3, 0xa8, 0xa3, 0xd7, 0x74, 0x3b, 0xc5, 0x54, - 0x95, 0x52, 0x10, 0xf1, 0xeb, 0x0d, 0x08, 0x59, 0x9e, 0xa7, 0x7d, 0x5f, 0x97, 0x4d, - 0x87, 0x17, 0x6d, 0x37, 0xd9, 0x8b, 0x9c, 0x0a, 0xd4, 0x40, 0x40, 0x72, 0x09, 0xed, - 0x6a, 0x9f, 0x08, 0x46, 0x4d, 0x56, 0x55, 0x93, 0xe1, 0xa6, 0x3b, 0x93, 0x85, 0x36, - 0xb4, 0x92, 0x44, 0xe9, 0x7d, 0x88, 0x01, 0x73, 0xb6, 0x40, 0xf2, 0xdd, 0xb7, 0x4d, - 0x06, 0x8e, 0xcb, 0x46, 0xcf, 0x28, 0x9b, 0x7d, 0x89, 0x13, 0x07, 0xbb, 0xa3, 0x70, - 0x54, 0xcf, 0x91, 0xb3, 0x1f, 0xc8, 0x2f, 0x74, 0xd5, 0xfc, 0xc0, 0x00, 0x94, 0x2e, - 0xde, 0x91, 0x18, 0x25, 0xf5, 0x3f, 0xe6, 0x09, 0x68, 0x6f, 0x46, 0x32, 0x23, 0xb1, - 0xe9, 0xbc, 0x03, 0xbd, 0xe8, 0x95, 0xd1, 0x23, 0x8f, 0xad, 0x04, 0xa3, 0xbf, 0xce, - 0x68, 0xa0, 0x75, 0xe8, 0xa3, 0x7c, 0x0e, 0x87, 0xbf, 0x46, 0xdd, 0x01, 0x55, 0x45, - 0xf9, 0xb4, 0xfb, 0x0e, 0xec, 0x64, 0x5f, 0xfc, 0xbb, 0xe0, 0xca, 0x5f, 0x8c, 0x56, - 0x1b, 0x25, 0x7d, 0x52, 0xd6, 0x02, 0xd8, 0xc9, 0x4c, 0x50, 0x28, 0x73, 0xa0, 0x1d, - 0x92, 0x51, 0xd8, 0xc8, 0x60, 0xc0, 0x41, 0x52, 0x5b, 0x3b, 0xf4, 0xe3, 0xa2, 0xeb, - 0x92, 0x72, 0x81, 0x5c, 0x75, 0x86, 0x76, 0x84, 0x28, 0xb4, 0xc2, 0xb2, 0x5e, 0x37, - 0x45, 0xf0, 0x09, 0xc5, 0xdc, 0xe2, 0x0b, 0x69, 0xd5, 0xd7, 0xc4, 0x3c, 0xeb, 0x73, - 0x6b, 0x68, 0x31, 0xe8, 0xc1, 0x10, 0xf1, 0x6c, 0xfd, 0xb3, 0xa4, 0x67, 0xe9, 0x41, - 0x4c, 0x00, 0xec, 0xf1, 0x37, 0x31, 0x50, 0x08, 0x94, 0x55, 0x56, 0x78, 0xc4, 0x97, - 0xfa, 0xba, 0x9a, 0x95, 0xd0, 0x1c, 0xc4, 0x64, 0x39, 0x0f, 0xc4, 0xa7, 0x6b, 0xfa, - 0x8b, 0x0e, 0x1c, 0x68, 0xa5, 0x25, 0xd7, 0x06, 0xd6, 0x60, 0x4b, 0x23, 0x30, 0xb6, - 0xb3, 0x48, 0x52, 0x15, 0xf6, 0x06, 0xf1, 0x88, 0x3a, 0x75, 0x15, 0x88, 0xc7, 0xef, - 0xa5, 0x06, 0xc3, 0xe8, 0xd0, 0xc6, 0x01, 0x92, 0xe8, 0x47, 0x6b, 0xd1, 0x17, 0x5d, - 0x95, 0x62, 0x08, 0x7b, 0xdb, 0x81, 0x8e, 0x66, 0x21, 0x62, 0x86, 0xba, 0xfe, 0x47, - 0xff, 0x4d, 0xbc, 0xce, 0xd5, 0x14, 0x44, 0x48, 0x0a, 0x9a, 0x56, 0x73, 0xec, 0xe7, - 0xfa, 0xc7, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd4, 0x1a, 0xb0, 0x05, 0x17, 0x53, 0xa7, 0xca, - 0xa8, 0x9b, 0xe3, 0x13, 0x9a, 0xfd, 0x97, 0x93, 0xb3, 0xe0, 0x2f, 0x27, 0xf0, 0x40, - 0x04, 0x65, 0x95, 0xac, 0xd4, 0x7b, 0xf1, 0x3f, 0xd0, 0xda, 0x27, 0xf0, 0x9e, 0xda, - 0x48, 0x03, 0x6d, 0x3e, 0xe4, 0x37, 0xf2, 0xee, 0x8f, 0x86, 0x06, 0xea, 0x97, 0x34, - 0x3c, 0x33, 0x58, 0x46, 0x57, 0xf4, 0x6d, 0xba, 0x99, 0xdb, 0x5c, 0xfe, 0x6c, 0xa1, - 0x76, 0xfa, 0xb7, 0xb0, 0xf3, 0xbf, 0xa0, 0xab, 0x61, 0xe3, 0x40, 0xc3, 0x4e, 0xb9, - 0xf1, 0x7c, 0x7e, 0xc2, 0xbe, 0x03, 0xb1, 0x80, 0xf0, 0xbb, 0x6f, 0x43, 0x4c, 0x2a, - 0x65, 0x42, 0xe0, 0x0e, 0x84, 0x37, 0x3f, 0x4f, 0x46, 0x49, 0xcd, 0xa3, 0x2b, 0xf6, - 0x86, 0x66, 0x61, 0x43, 0xf6, 0x22, 0xaa, 0x48, 0x04, 0x60, 0xb5, 0xaf, 0xac, 0x51, - 0x86, 0x07, 0xcd, 0x9a, 0xf8, 0xbc, 0xd6, 0xb5, 0x8c, 0x30, 0x12, 0x73, 0x16, 0xb2, - 0x5d, 0x5e, 0xa7, 0xbf, 0x6b, 0x0c, 0xab, 0x85, 0x42, 0xff, 0x69, 0xd9, 0xb2, 0xf1, - 0x80, 0xbe, 0x12, 0xed, 0x75, 0x34, 0x4a, 0x39, 0x5a, 0xa1, 0x0f, 0x85, 0x2f, 0x08, - 0x3a, 0xd6, 0x4e, 0xf4, 0x0e, 0x9c, 0x03, 0x09, 0xe9, 0xbb, 0xa5, 0x4b, 0x8c, 0xb3, - 0x3c, 0x95, 0x49, 0x8a, 0x69, 0x53, 0x8d, 0x3a, 0xe5, 0xb2, 0x5e, 0x24, 0x70, 0x98, - 0x30, 0x6f, 0xa8, 0xc7, 0x4a, 0x8e, 0xe5, 0xbc, 0xa9, 0x41, 0x53, 0x1d, 0x61, 0xaa, - 0xc2, 0x7a, 0xab, 0x3d, 0xc5, 0x61, 0x7d, 0x56, 0x06, 0xc9, 0x57, 0x7a, 0x2a, 0x83, - 0x46, 0xe8, 0xd8, 0x5b, 0x32, 0xb8, 0x50, 0x57, 0x75, 0x10, 0x8d, 0xc8, 0x5e, 0x2a, - 0xde, 0x2e, 0xac, 0x1e, 0x63, 0x6e, 0x1a, 0xf4, 0x05, 0x4c, 0x8b, 0x6f, 0x57, 0x63, - 0x2d, 0xf2, 0x69, 0xc3, 0x72, 0x3b, 0x32, 0x08, 0x72, 0xe4, 0xc5, 0x7b, 0x21, 0x83, - 0x58, 0xdc, 0x7e, 0x99, 0x05, 0xbb, 0x04, 0xed, 0xf9, 0x2e, 0xdf, 0x0d, 0xf6, 0x35, - 0xf3, 0xbf, 0x36, 0x1e, 0x57, 0xa1, 0x32, 0x96, 0xe1, 0x44, 0x7a, 0xf5, 0x08, 0x02, - 0x72, 0xd6, 0x36, 0xe2, 0x75, 0x18, 0xa9, 0x87, 0x6e, 0x15, 0xeb, 0x01, 0xf5, 0xe8, - 0xde, 0xd8, 0x18, 0x92, 0x51, 0x1c, 0xc2, 0x85, 0x1b, 0x00, 0xb8, 0x32, 0x71, 0x2a, - 0x6d, 0x3b, 0xa5, 0x66, 0x03, 0x17, 0xbc, 0xd3, 0x56, 0x76, 0x21, 0xa7, 0xcf, 0x84, - 0x45, 0x58, 0x96, 0x53, 0x26, 0x20, 0x20, 0xc3, 0x3b, 0xf7, 0x80, 0x31, 0xb8, 0xee, - 0x07, 0x07, 0xde, 0x07, 0x20, 0x68, 0xc1, 0x70, 0x57, 0x0b, 0x27, 0xe6, 0xd9, 0xf5, - 0xc6, 0xdd, 0xc3, 0x35, 0x40, 0x2e, 0xfc, 0x54, 0x88, 0x62, 0xf5, 0xa0, 0x70, 0x94, - 0xfd, 0x42, 0x8a, 0x7b, 0xbc, 0x15, 0xd7, 0xb3, 0x8d, 0x05, 0x36, 0x2c, 0x9c, 0xa9, - 0x85, 0xf5, 0x8a, 0x76, 0x64, 0x7d, 0x2b, 0xe4, 0xc2, 0xcd, 0x6b, 0x3d, 0x17, 0xd6, - 0x87, 0x09, 0x71, 0xd7, 0xa0, 0x98, 0xba, 0xf7, 0x2c, 0x6f, 0x6f, 0x12, 0x14, 0xcf, - 0x1f, 0xaa, 0xe4, 0x88, 0x03, 0x7d, 0xe2, 0x59, 0xd3, 0x41, 0x5c, 0x2f, 0x0d, 0xde, - 0xc7, 0x45, 0x70, 0x04, 0xf3, 0x57, 0x08, 0xd1, 0xec, 0xcc, 0xcc, 0x0d, 0xf6, 0x5a, - 0x04, 0x94, 0x3a, 0xd5, 0xcb, 0xc1, 0x3f, 0x29, 0x5f, 0x02, 0x0f, 0xe0, 0x56, 0xc4, - 0x0b, 0x2d, 0x88, 0xf2, 0x7d, 0xc3, 0x4c, 0xfe, 0xb8, 0x03, 0xbe, 0x34, 0x83, 0xa9, - 0xeb, 0xf9, 0xb5, 0xa9, 0x02, 0x60, 0x57, 0x72, 0x5d, 0x63, 0xea, 0xd2, 0xc0, 0xc0, - 0x03, 0x1f, 0xe2, 0x6a, 0xc1, 0xe7, 0xbd, 0xfc, 0xd6, 0xfa, 0xd8, 0x75, 0x84, 0x2d, - 0x19, 0x4f, 0x33, 0x17, 0x50, 0x46, 0x2c, 0x06, 0xb8, 0xd7, 0x98, 0x2d, 0x67, 0x99, - 0x5e, 0xd5, 0xd3, 0xae, 0x96, 0x02, 0x5a, 0xe0, 0x06, 0x7f, 0x4e, 0xb1, 0xc7, 0xc9, - 0x32, 0x31, 0xbd, 0x39, 0x77, 0x3c, 0xbe, 0x0a, 0x9d, 0x66, 0xb0, 0xc9, 0xaa, 0x8c, - 0xff, 0x6a, 0x37, 0x6e, 0x1f, 0x37, 0x2e, 0xac, 0x6a, 0xc4, 0x02, 0x6c, 0xc0, 0x94, - 0x22, 0x45, 0xd4, 0xc2, 0xdc, 0xf0, 0x2d, 0x76, 0x40, 0xff, 0xcc, 0x5a, 0x6a, 0xc3, - 0xa8, 0x7f, 0x5c, 0x41, 0x15, 0x51, 0xbc, 0xc2, 0xf2, 0x6c, 0xb9, 0x49, 0x61, 0xd5, - 0x3f, 0x95, 0xdd, 0xb1, 0x9a, 0xe9, 0x30, 0xc8, 0xd7, 0x0f, 0x03, 0x1b, 0x29, 0xa5, - 0xdf, 0x99, 0xff, 0x36, 0x69, 0x5e, 0x80, 0x2c, 0xbc, 0xb6, 0xb5, 0x8c, 0x1b, 0xa7, - 0xed, 0x5e, 0xac, 0xfa, 0x76, 0x41, 0x4a, 0x41, 0xad, 0x4a, 0x44, 0xf7, 0x1f, 0x1b, - 0x58, 0x0d, 0x34, 0xc3, 0xa9, 0x52, 0x92, 0x0b, 0x25, 0x4a, 0x14, 0x5f, 0xea, 0x51, - 0x7f, 0x5b, 0x42, 0xb2, 0xf6, 0x5e, 0xcd, 0x0f, 0x82, 0x59, 0x54, 0x78, 0xd8, 0x0a, - 0xe5, 0xc8, 0xce, 0xea, 0x12, 0xa1, 0x61, 0xcc, 0xbb, 0x5e, 0xac, 0x09, 0x99, 0x0f, - 0xc6, 0x19, 0xa4, 0x60, 0x80, 0x43, 0x6d, 0xbd, 0x08, 0xd7, 0x47, 0x84, 0xaf, 0x00, - 0x2d, 0x58, 0xe0, 0x6f, 0xaf, 0x7f, 0x3c, 0xea, 0xe7, 0xd3, 0x41, 0x9b, 0x1f, 0xca, - 0x26, 0x5a, 0x55, 0x59, 0xcf, 0x9e, 0x2d, 0x3b, 0x60, 0x97, 0x8d, 0x81, 0xa6, 0x78, - 0xb9, 0xed, 0x8e, 0x44, 0x86, 0xb4, 0xd1, 0x46, 0x09, 0xd6, 0xc1, 0x27, 0xc0, 0xc2, - 0xfb, 0xff, 0xe3, 0x0a, 0x60, 0xf7, 0xbf, 0xf1, 0xd9, 0xfb, 0x83, 0x00, 0xed, 0x00, - 0x92, 0x53, 0xba, 0x9b, 0x99, 0x6f, 0xa0, 0x52, 0x41, 0xb1, 0x0f, 0x5a, 0xc9, 0xa8, - 0x40, 0x8e, 0x92, 0x5b, 0x62, 0x6b, 0xb2, 0x1a, 0x47, 0x1f, 0xe3, 0xbe, 0xde, 0x52, - 0xbb, 0xa0, 0x97, 0xb2, 0xa9, 0x9a, 0x9b, 0xa5, 0xa8, 0x66, 0x58, 0xc3, 0xfd, 0x9e, - 0xc5, 0x5b, 0xfa, 0x9b, 0x32, 0x85, 0x67, 0x25, 0x4a, 0xb3, 0x6d, 0x2c, 0x7f, 0x44, - 0xd2, 0xc7, 0xe1, 0x3e, 0xb5, 0x4b, 0xeb, 0x70, 0xea, 0x8f, 0xa9, 0x4b, 0x6c, 0x6e, - 0x01, 0x2d, 0x79, 0xe3, 0xf5, 0x36, 0x89, 0xc2, 0xb1, 0xa1, 0x8e, 0xaf, 0x2d, 0x47, - 0x1d, 0x13, 0xc1, 0xab, 0x39, 0xd9, 0x19, 0x4a, 0xe8, 0x43, 0xab, 0x1d, 0x28, 0xff, - 0xa8, 0xf6, 0x9d, 0xc7, 0xe1, 0x5c, 0xc3, 0x8b, 0x12, 0xe8, 0xfc, 0xd7, 0x92, 0x55, - 0xb7, 0x21, 0x60, 0x56, 0xd9, 0xed, 0xb7, 0x48, 0x2f, 0xb9, 0x8a, 0xa0, 0x33, 0xb6, - 0x5e, 0x51, 0xc1, 0xa0, 0x8b, 0x8a, 0x11, 0xd8, 0x4d, 0x04, 0x09, 0xb7, 0x34, 0xf4, - 0x52, 0xaa, 0xf0, 0xd6, 0xb1, 0x8f, 0x50, 0x25, 0x86, 0x83, 0xd3, 0xf9, 0xa7, 0x6d, - 0x39, 0x9f, 0xd0, 0x47, 0xee, 0xe2, 0x88, 0xbb, 0x45, 0x85, 0x85, 0x1d, 0xc9, 0x3e, - 0xcc, 0xc6, 0x23, 0x22, 0x92, 0x4c, 0xd1, 0x3b, 0x5d, 0xd4, 0xee, 0xd6, 0x6e, 0xd8, - 0xd9, 0x97, 0x2d, 0x77, 0x26, 0x29, 0xea, 0x64, 0x74, 0x2e, 0x54, 0x73, 0x39, 0x81, - 0xb0, 0x06, 0xc0, 0x62, 0x46, 0x8e, 0x4b, 0xd8, 0xf7, 0xdd, 0x9a, 0xf6, 0x98, 0xf5, - 0x2a, 0xe8, 0x14, 0x63, 0x4e, 0x81, 0xd7, 0xf3, 0xe0, 0xc4, 0x20, 0x31, 0x7c, 0xac, - 0xa9, 0xae, 0x48, 0x11, 0xc6, 0xaf, 0x06, 0xfe, 0x80, 0xa8, 0xc0, 0x2a, 0xb7, 0xa0, - 0x0e, 0x18, 0xe4, 0xa6, 0xaa, 0x1e, 0xa1, 0xb7, 0x69, 0x45, 0xd2, 0x61, 0x5d, 0x43, - 0xac, 0x11, 0x8b, 0x56, 0xc2, 0xf2, 0x96, 0x0f, 0xe9, 0x3a, 0x02, 0x5f, 0x13, 0xec, - 0x91, 0xff, 0xc6, 0xd2, 0xc3, 0x53, 0x69, 0x9a, 0xbb, 0x09, 0x2d, 0xed, 0xc0, 0x65, - 0xdb, 0x8f, 0xa2, 0x14, 0xdb, 0xc4, 0x64, 0x66, 0xf8, 0x97, 0xb8, 0x8c, 0x58, 0xb3, - 0x01, 0x52, 0x13, 0x3a, 0xa3, 0x83, 0x1a, 0xf3, 0x7c, 0x74, 0xd9, 0x9e, 0x9e, 0x36, - 0xff, 0x70, 0x11, 0xd3, 0x23, 0x83, 0x05, 0x69, 0x15, 0x08, 0xa2, 0xc3, 0xa4, 0x3e, - 0x75, 0x5d, 0xc0, 0x81, 0xb5, 0x11, 0xd6, 0x48, 0x2a, 0x7d, 0xb6, 0x5f, 0xa9, 0x69, - 0x9e, 0xa8, 0x7f, 0xf4, 0x70, 0x99, 0xed, 0x36, 0x37, 0xdb, 0xb0, 0xa3, 0xd0, 0xef, - 0x79, 0x79, 0x6a, 0x8e, 0xf1, 0xe4, 0xd9, 0x4d, 0x42, 0xb4, 0xbc, 0x2b, 0x4a, 0x03, - 0x8a, 0xe6, 0xe4, 0x6b, 0x24, 0xcf, 0xc8, 0x41, 0x53, 0xd3, 0x1e, 0xaf, 0x89, 0x50, - 0x63, 0xa5, 0xca, 0x95, 0x9b, 0xe6, 0x3f, 0x37, 0xf2, 0xba, 0x0d, 0x43, 0x23, 0x66, - 0x73, 0x6d, 0x86, 0x32, 0xfc, 0xe0, 0x72, 0xb6, 0xae, 0x5b, 0x6f, 0x3f, 0xd5, 0x9d, - 0x3f, 0xaf, 0xf6, 0x38, 0x27, 0x5a, 0x99, 0x2f, 0xef, 0xc8, 0x7e, 0x60, 0xd4, 0x4c, - 0x2c, 0xad, 0xc2, 0xb5, 0xc4, 0x94, 0xe3, 0xe7, 0x2e, 0xb4, 0x59, 0x7c, 0x96, 0xb4, - 0x01, 0x67, 0x79, 0x9a, 0x90, 0x01, 0xa2, 0xed, 0x36, 0x76, 0xa8, 0xb4, 0x03, 0xae, - 0x25, 0xff, 0xd7, 0x72, 0xf7, 0x08, 0x1e, 0x9a, 0x32, 0xbc, 0xc1, 0xc5, 0xe2, 0xed, - 0xd4, 0xe2, 0xa6, 0x57, 0x6b, 0x78, 0x3c, 0xce, 0x3a, 0xae, 0x11, 0xfa, 0x43, 0x22, - 0x62, 0x54, 0x88, 0x56, 0x18, 0x3e, 0xe6, 0x82, 0xd5, 0xdc, 0x31, 0xbe, 0xb3, 0x8f, - 0x06, 0x1c, 0xbd, 0xec, 0xa7, 0x02, 0x1a, 0x44, 0x4e, 0x2d, 0xd4, 0x17, 0xdf, 0x26, - 0xdc, 0xd2, 0x20, 0xf2, 0xb7, 0x31, 0x77, 0x2b, 0x43, 0x9e, 0x96, 0xd6, 0x14, 0xe1, - 0xfa, 0xcb, 0x48, 0x6c, 0x7a, 0x7d, 0x51, 0x71, 0xb1, 0xde, 0x35, 0x9f, 0x6a, 0xd3, - 0xa9, 0x6f, 0x64, 0x9c, 0x96, 0x91, 0x02, 0xa1, 0x96, 0x4f, 0xb4, 0xb4, 0xa1, 0xa4, - 0x27, 0x9c, 0x68, 0xe6, 0xc3, 0x72, 0xe4, 0x21, 0x87, 0xd7, 0x54, 0xe8, 0x04, 0xa6, - 0x16, 0x53, 0x09, 0x20, 0x69, 0xfb, 0x9b, 0x6d, 0x25, 0x26, 0x68, 0x90, 0x80, 0x8b, - 0x01, 0x5d, 0xf2, 0x8c, 0x80, 0x10, 0x65, 0xda, 0x6f, 0xeb, 0xdc, 0x1a, 0x56, 0xbf, - 0xd0, 0x02, 0x62, 0x5a, 0xcf, 0xaa, 0x53, 0x73, 0xfd, 0xe1, 0x49, 0xc1, 0xcf, 0xc3, - 0x64, 0x9b, 0x48, 0x69, 0x69, 0x6d, 0x44, 0xec, 0xb1, 0x24, 0x79, 0xc5, 0xeb, 0xef, - 0x99, 0x5f, 0x10, 0x02, 0x9f, 0x8b, 0x53, 0x0e, 0xeb, 0x3f, 0xdc, 0x2e, 0x50, 0xe8, - 0x75, 0x7f, 0xc0, 0xbb, 0x9e, 0x26, 0x30, 0x23, 0xdb, 0x82, 0xf8, 0x78, 0xd9, 0xac, - 0x7f, 0xfb, 0x0b, 0xd4, 0x39, 0x1d, 0xf1, 0xd8, 0x79, 0x89, 0x9a, 0x3e, 0xf5, 0x7b, - 0xfd, 0x0d, 0x1f, 0x77, 0x55, 0x64, 0x8e, 0xdd, 0x85, 0xbb, 0x05, 0x2a, 0x6e, 0xdf, - 0x71, 0xcd, 0x26, 0x28, 0xc9, 0x87, 0x42, 0x9f, 0x36, 0xdc, 0x50, 0x5c, 0xcc, 0x43, - 0xf3, 0x0e, 0x7a, 0x86, 0x9c, 0x9e, 0x25, 0x5e, 0x2a, 0xf9, 0xfc, 0xf3, 0x0c, 0x12, - 0x17, 0x96, 0xd1, 0x90, 0x00, 0x09, 0x60, 0xcb, 0x6f, 0xe2, 0xf1, 0xbf, 0x24, 0x61, - 0x18, 0xb4, 0x98, 0xf3, 0x24, 0x7f, 0x9d, 0x48, 0x4c, 0x73, 0xcf, 0x09, 0x39, 0x30, - 0x39, 0xe4, 0x53, 0x26, 0xb8, 0xff, 0xff, 0xb3, 0xe7, 0xe6, 0x15, 0x9c, 0x46, 0x69, - 0x9f, 0x10, 0x07, 0x92, 0xd4, 0x67, 0x29, 0x50, 0x34, 0x8a, 0x90, 0x55, 0x2e, 0x45, - 0x94, 0x3b, 0xee, 0xac, 0xf0, 0x3f, 0x32, 0x16, 0xf9, 0x4e, 0x27, 0x4d, 0x63, 0xd6, - 0x37, 0xd9, 0xf1, 0x90, 0xe8, 0xa2, 0x66, 0xcd, 0xee, 0xf1, 0x53, 0x53, 0x0b, 0xee, - 0x5c, 0xb8, 0x35, 0x52, 0x60, 0x50, 0x5c, 0x2c, 0x2e, 0x5d, 0x99, 0x0f, 0xff, 0xdc, - 0x34, 0xec, 0x0f, 0xf7, 0xf1, 0xaf, 0x81, 0xb2, 0x4c, 0xed, 0x0e, 0xfa, 0x62, 0x13, - 0xda, 0x6c, 0x7c, 0x60, 0xc4, 0x87, 0xf5, 0xf7, 0xb0, 0x3f, 0x81, 0x60, 0xa0, 0x57, - 0xf4, 0x6d, 0x05, 0xbf, 0x82, 0x18, 0xb3, 0xad, 0xd9, 0xc0, 0x68, 0x93, 0xbd, 0x02, - 0xdb, 0x9b, 0x61, 0x19, 0x1d, 0xfb, 0x13, 0x3b, 0xfa, 0xbe, 0x48, 0x58, 0xe4, 0x7a, - 0x4c, 0xc3, 0x2e, 0x41, 0x6e, 0xc0, 0x8b, 0x8a, 0xc7, 0x91, 0x5a, 0x43, 0x73, 0x3f, - 0x44, 0x06, 0xe9, 0xd9, 0x67, 0xc5, 0x60, 0xf3, 0x44, 0xd7, 0xe9, 0x04, 0xa2, 0x80, - 0x45, 0xd9, 0x9f, 0x3a, 0xf8, 0xc8, 0x2e, 0x97, 0xe1, 0xb9, 0xc1, 0xb2, 0x05, 0xe5, - 0x85, 0xfb, 0xeb, 0xb4, 0x8f, 0xaf, 0x58, 0xf1, 0xb6, 0x5d, 0xca, 0x24, 0x97, 0xe0, - 0x9a, 0x70, 0xaa, 0xd4, 0x86, 0x5f, 0x85, 0x71, 0x5a, 0x28, 0x0e, 0x18, 0x6f, 0x3f, - 0xc1, 0x74, 0x0d, 0x81, 0x84, 0xd3, 0x3e, 0x83, 0x22, 0x16, 0x95, 0x21, 0xcd, 0xc1, - 0x32, 0x21, 0x29, 0x39, 0xc8, 0x4a, 0x10, 0x89, 0x64, 0xe2, 0xde, 0x74, 0xb6, 0xea, - 0x55, 0xb4, 0xcb, 0x8f, 0x6f, 0x9b, 0xee, 0x98, 0xb1, 0x0d, 0x41, 0x51, 0x09, 0x45, - 0x5f, 0x48, 0xb7, 0x76, 0x08, 0x2d, 0xc3, 0x0b, 0x4b, 0xc7, 0x34, 0x77, 0x07, 0x55, - 0x11, 0x70, 0x03, 0x08, 0x15, 0x8c, 0xe2, 0xf2, 0xf9, 0xbf, 0x0f, 0x69, 0x1b, 0x2c, - 0xe5, 0x3e, 0x61, 0x14, 0x2c, 0xb7, 0x40, 0xc1, 0x5b, 0x7b, 0x62, 0x3c, 0xf4, 0x8b, - 0x3f, 0x7b, 0xfe, 0xfa, 0x31, 0xbc, 0xdc, 0x66, 0x5c, 0x6d, 0x71, 0x23, 0xe9, 0x53, - 0x50, 0x81, 0x13, 0x75, 0x94, 0x7b, 0x05, 0x5a, 0x43, 0xdb, 0x07, 0xe0, 0x3f, 0x33, - 0x62, 0x7d, 0xf5, 0xc6, 0x38, 0xbf, 0xad, 0x95, 0x6d, 0xdc, 0x1e, 0xa7, 0xd7, 0x62, - 0x0a, 0x20, 0xf2, 0x79, 0x2f, 0x63, 0x81, 0x7a, 0x1c, 0xf3, 0x25, 0x80, 0xd0, 0x42, - 0x74, 0x23, 0x4a, 0xf2, 0xa5, 0x1b, 0x56, 0xbb, 0x68, 0xa2, 0x9e, 0x43, 0xa9, 0x54, - 0x14, 0x2b, 0xa4, 0xca, 0x68, 0x23, 0xbd, 0xe9, 0x05, 0x3d, 0x72, 0xfd, 0xad, 0xbc, - 0x61, 0xad, 0x59, 0x36, 0xc5, 0x3f, 0xdd, 0x75, 0x79, 0x44, 0x6d, 0x11, 0xc4, 0x46, - 0x07, 0xf4, 0x16, 0x30, 0xe4, 0xc0, 0x89, 0x15, 0xe6, 0x31, 0x77, 0x15, 0x50, 0xe9, - 0xce, 0x1f, 0xca, 0x2c, 0x63, 0xfe, 0x06, 0xb7, 0x98, 0x9d, 0x58, 0x4f, 0xa7, 0xd7, - 0x82, 0xa8, 0x8c, 0x1e, 0x7d, 0x64, 0xb6, 0xfb, 0xf5, 0x5e, 0x35, - ], - script_code: Script(vec![0x6a, 0x53, 0x53, 0x63]), - transparent_input: None, - hash_type: 1, - amount: 379068098637835, - consensus_branch_id: 1537743641, - sighash: [ - 0x92, 0xe7, 0xb4, 0x8f, 0x32, 0x81, 0x87, 0x71, 0x26, 0x87, 0xaf, 0x4d, 0xc1, 0x7a, - 0x73, 0xfe, 0x0a, 0x70, 0xac, 0x07, 0x8d, 0x24, 0xcd, 0xcd, 0xd4, 0x58, 0xa3, 0xd6, - 0x86, 0x61, 0xec, 0x0a, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x24, 0x9d, 0xf0, 0x57, 0x01, - 0xda, 0xb0, 0x31, 0xc4, 0xba, 0xc1, 0xea, 0x26, 0x7a, 0x29, 0x96, 0xa2, 0x02, 0x8d, - 0x1e, 0x6a, 0x0f, 0x80, 0xa3, 0x84, 0x7c, 0x53, 0x1d, 0xba, 0x96, 0xee, 0x65, 0xa2, - 0x41, 0x89, 0xbd, 0x09, 0x52, 0xac, 0x65, 0x63, 0x65, 0xac, 0x00, 0x65, 0x00, 0xb2, - 0xa4, 0xf9, 0x51, 0xef, 0x8f, 0x49, 0x7d, 0xff, 0xf2, 0xf2, 0xf2, 0x71, 0xea, 0xb8, - 0x9c, 0x62, 0x8e, 0x18, 0xb5, 0xfc, 0xb4, 0x38, 0x82, 0x53, 0x7e, 0xaf, 0x6a, 0xd2, - 0xa6, 0xb1, 0x75, 0x46, 0x33, 0xca, 0xa8, 0x6b, 0xf2, 0xc7, 0x6f, 0x07, 0x53, 0x63, - 0x6a, 0x6a, 0x65, 0x6a, 0x53, 0xa2, 0x21, 0x0c, 0x27, 0x01, 0xea, 0x6c, 0x54, 0x2c, - 0xc8, 0xc7, 0x06, 0x00, 0x00, 0xe0, 0x11, 0x29, 0xf0, 0x3a, 0x1e, 0x9c, 0x09, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xbe, 0x07, 0x62, 0xc0, 0xb1, 0xc6, 0x58, 0x55, 0xde, 0xba, 0x84, 0x22, - 0xca, 0x4b, 0x88, 0xab, 0xee, 0xa6, 0xa4, 0x38, 0x2c, 0xf1, 0x6c, 0xcd, 0x6d, 0xc7, - 0xc3, 0x7c, 0x44, 0xe5, 0x49, 0xc4, 0x53, 0x48, 0x19, 0xac, 0xd8, 0xbb, 0x0a, 0x02, - 0xa5, 0xfa, 0x7a, 0x1c, 0x1d, 0x38, 0x06, 0xfb, 0xc3, 0x40, 0x7f, 0xd7, 0xda, 0x93, - 0xfd, 0x0d, 0xe6, 0x40, 0x0d, 0x3a, 0xb8, 0x97, 0x74, 0x85, 0xcd, 0xdf, 0xbe, 0xd5, - 0x93, 0x2f, 0x50, 0x7b, 0x79, 0x94, 0x7a, 0xdb, 0x2f, 0xad, 0x37, 0x61, 0x5a, 0xa7, - 0x17, 0xdb, 0x5f, 0x29, 0x80, 0x99, 0xf2, 0x0f, 0x26, 0x3b, 0x35, 0x9a, 0x11, 0x51, - 0xa6, 0xb7, 0x5c, 0x01, 0x36, 0x5e, 0xb1, 0x54, 0xae, 0x42, 0x14, 0x0d, 0x6e, 0x10, - 0x34, 0x2f, 0x14, 0xf3, 0x4d, 0xc3, 0x3e, 0x07, 0xff, 0x0e, 0x4d, 0x1a, 0x6b, 0xe3, - 0x75, 0xb3, 0x2f, 0x84, 0xb9, 0x2e, 0x5d, 0x81, 0xeb, 0xb6, 0x39, 0xc4, 0xf2, 0x7e, - 0x71, 0x5a, 0xa4, 0x2c, 0xc7, 0x57, 0x07, 0xd4, 0xeb, 0xd1, 0xbb, 0xfb, 0xe8, 0xf9, - 0x0f, 0xc7, 0xc9, 0x53, 0xe7, 0xa9, 0x71, 0x5e, 0x65, 0xaf, 0x82, 0x67, 0x37, 0x3d, - 0x34, 0x51, 0x67, 0x4f, 0xf0, 0x84, 0xef, 0xd9, 0x2c, 0xcf, 0x3b, 0xcc, 0x7a, 0xca, - 0x14, 0x67, 0xb6, 0x32, 0x7e, 0x4f, 0x95, 0x22, 0xb2, 0xcc, 0x57, 0x9a, 0x7a, 0x8f, - 0xff, 0x7c, 0xa7, 0xcf, 0x14, 0x5d, 0xfc, 0x13, 0xea, 0xfc, 0x34, 0x15, 0x3b, 0x2c, - 0x3e, 0x8a, 0xfb, 0xe5, 0x34, 0x44, 0xd0, 0xc7, 0x3b, 0x3b, 0xd5, 0xbc, 0x87, 0x0b, - 0x01, 0xcd, 0x45, 0x79, 0x11, 0xe3, 0x56, 0x31, 0x3f, 0xd1, 0xda, 0xfb, 0x4c, 0x81, - 0x51, 0x63, 0x4a, 0x01, 0xaf, 0xf7, 0xcf, 0x11, 0x6d, 0x43, 0x3c, 0x3d, 0x2b, 0x3a, - 0xdd, 0xa9, 0xce, 0xbe, 0x18, 0xf7, 0xd1, 0x72, 0x44, 0x3e, 0x5e, 0x7b, 0x5a, 0xc9, - 0xab, 0xe8, 0xdb, 0x22, 0x56, 0xd7, 0xeb, 0xe2, 0xff, 0x28, 0x02, 0x09, 0x39, 0x50, - 0x38, 0x70, 0x59, 0x7b, 0x9a, 0x95, 0x58, 0x92, 0xc7, 0x38, 0x02, 0x50, 0xa2, 0xd4, - 0x2e, 0xc9, 0x2b, 0xe7, 0x23, 0xfe, 0xdf, 0x2f, 0x2e, 0xde, 0x5a, 0x47, 0x2a, 0xa1, - 0xe7, 0x4f, 0x33, 0xad, 0x41, 0x90, 0x15, 0x44, 0xed, 0xbb, 0xe3, 0xac, 0x46, 0x4c, - 0xf4, 0x03, 0x19, 0x60, 0x15, 0xf4, 0xf2, 0x2a, 0xc2, 0xb8, 0xfc, 0x01, 0x49, 0x6b, - 0xea, 0xb4, 0xd4, 0x59, 0x07, 0xf4, 0x79, 0x81, 0x2a, 0x25, 0x94, 0x31, 0xa2, 0xcb, - 0xc9, 0x3d, 0x4f, 0x3b, 0x84, 0xe4, 0x0b, 0x36, 0x60, 0x20, 0x27, 0x3a, 0x67, 0x52, - 0xe5, 0x01, 0xaf, 0x6f, 0xf1, 0xb7, 0x8d, 0xdc, 0x81, 0x7e, 0x6e, 0xa3, 0x51, 0xd6, - 0x00, 0x6b, 0xec, 0xf8, 0xd2, 0xff, 0xb0, 0x39, 0x90, 0xf6, 0x77, 0x74, 0xa8, 0x1e, - 0x05, 0xb7, 0xf4, 0xbb, 0xad, 0x85, 0x77, 0xfa, 0x27, 0xc9, 0xde, 0x64, 0xe1, 0xb1, - 0x1d, 0xcf, 0x38, 0x4f, 0x59, 0x56, 0x44, 0x37, 0x48, 0x75, 0x5a, 0x9f, 0xc6, 0xf2, - 0xa0, 0x03, 0x10, 0xc3, 0x65, 0x7e, 0xba, 0xc0, 0x3b, 0xfc, 0x0b, 0x58, 0x7b, 0xef, - 0x2f, 0x45, 0xec, 0x8a, 0xcd, 0xaa, 0x51, 0xc1, 0x43, 0xb0, 0xcb, 0x25, 0xb9, 0x14, - 0x2c, 0x61, 0xbd, 0x79, 0x0a, 0x80, 0x03, 0xc2, 0x3f, 0x90, 0xcc, 0x03, 0x49, 0x5b, - 0x51, 0xe4, 0xd2, 0x84, 0x3e, 0x55, 0x7f, 0x9e, 0x25, 0x45, 0x10, 0x8c, 0x6c, 0x6f, - 0xae, 0x35, 0x9f, 0x64, 0x5c, 0x27, 0x68, 0x91, 0xc0, 0xdc, 0xab, 0x03, 0xaf, 0x18, - 0x77, 0x00, 0xc0, 0x82, 0xdc, 0x47, 0x77, 0x40, 0xfb, 0x3f, 0x2c, 0xd7, 0xbb, 0x59, - 0xfb, 0x35, 0x85, 0x54, 0xe9, 0x4c, 0x7e, 0x67, 0x8c, 0xe0, 0x1a, 0xeb, 0xf9, 0x4e, - 0x51, 0x5e, 0x03, 0x72, 0x29, 0x67, 0x99, 0x5a, 0xea, 0x85, 0x8d, 0x64, 0xe7, 0x78, - 0x9f, 0xf3, 0x06, 0x36, 0x95, 0x77, 0x22, 0x81, 0x80, 0x32, 0x6a, 0x5b, 0x0a, 0xf4, - 0x75, 0xe2, 0x7a, 0x54, 0xb2, 0x07, 0xb4, 0x03, 0x92, 0xe3, 0x76, 0x17, 0x0e, 0x3f, - 0xb0, 0x05, 0x02, 0x82, 0x61, 0xc9, 0x9c, 0x2d, 0xbd, 0x0e, 0xed, 0xee, 0x87, 0x1c, - 0x1c, 0x0f, 0x48, 0xb8, 0xe9, 0xb8, 0xe4, 0xbe, 0x77, 0xd1, 0xb7, 0x37, 0xfe, 0x21, - 0xf0, 0xfa, 0x5a, 0x18, 0xeb, 0xb5, 0x27, 0x55, 0xb5, 0xa6, 0xcf, 0x61, 0x30, 0xfb, - 0x56, 0x94, 0x4c, 0xfa, 0xb8, 0x75, 0x27, 0xc2, 0x50, 0xd1, 0x13, 0xb2, 0x9b, 0xca, - 0xc9, 0xaa, 0xa1, 0x0c, 0x2e, 0x7d, 0xe4, 0x15, 0xed, 0xb0, 0x80, 0x6c, 0x6d, 0xa0, - 0x30, 0x20, 0xa1, 0x34, 0xca, 0x7e, 0xcd, 0xc8, 0xda, 0x1b, 0xd5, 0x7a, 0x37, 0xf5, - 0x5a, 0x46, 0x94, 0x0b, 0x45, 0xb2, 0x41, 0xb1, 0xc1, 0x6e, 0xe1, 0x00, 0x92, 0x7d, - 0x1b, 0xd8, 0x60, 0xd4, 0x45, 0xa9, 0xde, 0x50, 0xd4, 0xc3, 0x84, 0xd6, 0xe1, 0xd0, - 0x01, 0x08, 0x02, 0x6c, 0x0e, 0xa5, 0xeb, 0xbf, 0x0b, 0x72, 0xfb, 0xf5, 0xc3, 0x70, - 0xbc, 0xe1, 0x8d, 0x3a, 0xcb, 0xc4, 0x65, 0x99, 0x09, 0x9b, 0xaa, 0xe1, 0xd8, 0x02, - 0xf7, 0x73, 0x33, 0x49, 0x4a, 0x7a, 0xe1, 0x30, 0xfe, 0x86, 0xe8, 0xf8, 0x18, 0xf9, - 0x26, 0x1a, 0x2d, 0xad, 0xb4, 0x12, 0x52, 0x29, 0xba, 0x0f, 0xfc, 0x0e, 0x70, 0x90, - 0x32, 0x44, 0x30, 0xb5, 0x21, 0xa9, 0x0d, 0x22, 0x4a, 0xb7, 0xa1, 0x02, 0x4e, 0x1d, - 0x89, 0x3e, 0x74, 0x04, 0xfe, 0xdb, 0x34, 0x8e, 0x4d, 0x5e, 0x22, 0x35, 0xc5, 0x9a, - 0x78, 0x76, 0xa0, 0xfc, 0x60, 0x14, 0x5c, 0x6a, 0x00, 0x96, 0x87, 0x68, 0x44, 0x60, - 0x27, 0x1e, 0xe1, 0x33, 0xa4, 0x37, 0xfe, 0x52, 0xfb, 0x6c, 0xfb, 0xa9, 0x7f, 0xce, - 0xc1, 0x61, 0xdf, 0x51, 0x5d, 0xde, 0x90, 0x5a, 0x24, 0xda, 0x6d, 0x37, 0xbd, 0xc3, - 0x40, 0x44, 0xa9, 0x55, 0xe6, 0x82, 0xb4, 0x74, 0x71, 0xca, 0x1e, 0x8c, 0x78, 0xc5, - 0x1e, 0xd3, 0x77, 0xcd, 0x4a, 0xfa, 0x89, 0x4b, 0xd9, 0xbd, 0x12, 0xe7, 0x07, 0x15, - 0x6d, 0xa0, 0x72, 0x6f, 0x7c, 0xf5, 0x72, 0x9f, 0xab, 0xe3, 0x72, 0x16, 0x04, 0x63, - 0xfe, 0x04, 0x29, 0x24, 0x4d, 0x06, 0x74, 0x89, 0xba, 0x5d, 0x09, 0x47, 0x2e, 0xcd, - 0x9b, 0xcd, 0xc4, 0xd5, 0xe4, 0xdf, 0x10, 0x1e, 0x18, 0x9d, 0xb8, 0x46, 0x3e, 0xb5, - 0x38, 0x30, 0x7b, 0x58, 0x7d, 0xef, 0xf7, 0x8d, 0xe9, 0xc7, 0x3a, 0xf2, 0x80, 0x80, - 0xb2, 0xfd, 0x05, 0x00, 0x3e, 0x11, 0xd3, 0xe1, 0xb3, 0x29, 0x9d, 0xc9, 0x52, 0x1f, - 0x8b, 0x51, 0x3b, 0xad, 0xb0, 0x10, 0xe9, 0x1b, 0xfe, 0xb9, 0x1b, 0x0b, 0x2a, 0x6c, - 0xb1, 0x29, 0xc2, 0xe8, 0x25, 0xa5, 0x97, 0xb8, 0xfb, 0x75, 0xbc, 0x56, 0x2d, 0x65, - 0x4d, 0x62, 0x10, 0x46, 0x40, 0xdd, 0x74, 0xe5, 0x6c, 0xd1, 0x4b, 0xaa, 0xba, 0x56, - 0x5b, 0x84, 0xb8, 0x45, 0xe1, 0x63, 0xd1, 0xca, 0xef, 0x25, 0x33, 0xc3, 0x98, 0x16, - 0x37, 0x20, 0x4f, 0x96, 0xa5, 0x9c, 0x8e, 0x80, 0x24, 0xd9, 0x04, 0x1b, 0x20, 0x29, - 0xe9, 0x4c, 0x15, 0x24, 0x5f, 0x1a, 0x95, 0x88, 0x40, 0xba, 0x3f, 0x38, 0x0a, 0x4d, - 0x20, 0xf1, 0x18, 0x4e, 0x77, 0x82, 0x7d, 0xe3, 0xff, 0x8f, 0x3d, 0x73, 0x45, 0x9a, - 0xfe, 0x24, 0x1f, 0x72, 0x3c, 0x08, 0x48, 0x23, 0x23, 0x0e, 0x00, 0x3d, 0x3d, 0x21, - 0xe5, 0x35, 0x01, 0xec, 0x04, 0x99, 0xb0, 0x83, 0xa7, 0xda, 0xd6, 0x85, 0xc5, 0x71, - 0x27, 0xf4, 0xde, 0x64, 0x73, 0x3a, 0x88, 0x0c, 0x2d, 0xb2, 0x8f, 0xda, 0xab, 0xf1, - 0xb5, 0x42, 0xd2, 0x05, 0xf6, 0x64, 0xa3, 0x51, 0x35, 0x71, 0x27, 0x11, 0xdc, 0xcc, - 0xd9, 0x31, 0xa5, 0x0b, 0x9c, 0x56, 0x61, 0x88, 0x23, 0x60, 0xd4, 0xca, 0xc0, 0x04, - 0x76, 0x81, 0xbc, 0x2e, 0x2b, 0x3b, 0xf6, 0xc9, 0x97, 0x60, 0xd7, 0xcf, 0xb4, 0xfa, - 0x21, 0x39, 0x43, 0x77, 0xa4, 0x55, 0x1c, 0x76, 0xd1, 0xf7, 0x5a, 0xc0, 0x3c, 0x26, - 0x20, 0x54, 0xdf, 0xfd, 0x79, 0xa9, 0xde, 0xd0, 0x5e, 0x88, 0x89, 0x58, 0x19, 0x9e, - 0xea, 0x45, 0x01, 0xe2, 0x99, 0x0a, 0x53, 0xa5, 0xcd, 0x2a, 0x46, 0xa4, 0x01, 0x57, - 0x65, 0x88, 0xfd, 0x7d, 0x05, 0x8a, 0x26, 0xf2, 0x84, 0x38, 0xe5, 0x78, 0x2f, 0x45, - 0xac, 0x1d, 0x07, 0xf6, 0xf6, 0xf5, 0xed, 0x73, 0x74, 0x1d, 0x57, 0x85, 0x83, 0x7a, - 0x6b, 0x84, 0x4b, 0x47, 0x47, 0x75, 0x71, 0x8c, 0x29, 0xdd, 0x99, 0x08, 0x4e, 0x9f, - 0x88, 0xef, 0x15, 0x3a, 0x83, 0x29, 0xf5, 0x32, 0xa6, 0x90, 0x17, 0xdc, 0x3a, 0x97, - 0xed, 0x75, 0x43, 0x67, 0x72, 0x30, 0x98, 0xe5, 0x76, 0x58, 0x40, 0xb0, 0x22, 0x89, - 0x72, 0x44, 0x74, 0x5f, 0xbb, 0xbb, 0x30, 0xa7, 0xcb, 0x54, 0xfa, 0x05, 0x11, 0x16, - 0x6e, 0x95, 0x44, 0x12, 0x20, 0x00, 0x61, 0x0b, 0xd2, 0xaa, 0xcb, 0xd8, 0x23, 0x25, - 0xa5, 0x9b, 0x95, 0x15, 0x4e, 0xcd, 0x82, 0xc8, 0x8d, 0x23, 0xab, 0xd1, 0xe2, 0x07, - 0x70, 0xff, 0xb8, 0xaa, 0xbf, 0x83, 0xfc, 0x07, 0x34, 0x96, 0x4c, 0xcd, 0x41, 0x1d, - 0x1c, 0x93, 0x57, 0x14, 0xe2, 0x4a, 0xab, 0x56, 0x6f, 0x4f, 0x08, 0x42, 0x40, 0x14, - 0xc4, 0xec, 0xa9, 0x1b, 0x59, 0x0f, 0x08, 0x2b, 0x47, 0x3f, 0x36, 0x1c, 0x87, 0x41, - 0x5d, 0x37, 0xbd, 0x20, 0xd7, 0x0f, 0xd0, 0xb5, 0x2b, 0x6d, 0xdf, 0x18, 0x65, 0xf7, - 0x66, 0x70, 0x2e, 0x32, 0xb0, 0x5b, 0x3c, 0xf1, 0x63, 0x0e, 0xe8, 0x59, 0x7a, 0xae, - 0x19, 0x63, 0x3f, 0x35, 0x16, 0xa8, 0x55, 0x5a, 0xc5, 0xbe, 0x32, 0xc6, 0x75, 0xbe, - 0x18, 0x17, 0xef, 0xbf, 0xfd, 0x93, 0x69, 0x04, 0x1a, 0x08, 0x9c, 0x28, 0x3f, 0x19, - 0x64, 0x99, 0x68, 0xc2, 0x49, 0x8c, 0xde, 0x56, 0xf5, 0x00, 0x43, 0x4f, 0x28, 0x0d, - 0x77, 0xa9, 0xc6, 0x2e, 0x43, 0xcb, 0xd3, 0xf1, 0x36, 0xa4, 0xc6, 0xa0, 0x0a, 0x43, - 0xe6, 0xed, 0x53, 0x0c, 0xb2, 0xe8, 0xae, 0x83, 0x88, 0x60, 0xad, 0xc8, 0x8a, 0xac, - 0xc7, 0xbd, 0x6a, 0x00, 0xae, 0x0c, 0x19, 0xff, 0x45, 0x33, 0xa4, 0x85, 0xef, 0xde, - 0x08, 0x2b, 0x5f, 0x4d, 0x1f, 0x7a, 0x8e, 0xbe, 0x7e, 0xd8, 0x2b, 0x7b, 0x05, 0xa8, - 0xcf, 0xe1, 0xe3, 0x73, 0x45, 0x9f, 0x1b, 0xdc, 0xbf, 0x95, 0x25, 0x74, 0x7e, 0x8c, - 0x95, 0x08, 0xa5, 0x55, 0xfa, 0xcb, 0x79, 0x87, 0x40, 0xe0, 0xbd, 0xf9, 0x94, 0xd9, - 0x73, 0x9b, 0xbe, 0x55, 0x38, 0xa0, 0xae, 0x0f, 0x07, 0x6c, 0x58, 0x2c, 0x0f, 0x5b, - 0xa8, 0x78, 0xb9, 0x9b, 0x82, 0x49, 0xdb, 0x1d, 0x7e, 0x95, 0x05, 0x6c, 0x98, 0xaf, - 0x08, 0x3d, 0x98, 0xcb, 0x0e, 0xd9, 0xe3, 0xf7, 0x43, 0x6e, 0x1c, 0x76, 0x43, 0x76, - 0x6f, 0x96, 0x6b, 0x83, 0xe9, 0x99, 0x20, 0x6e, 0xbd, 0x13, 0x93, 0xb9, 0xb2, 0xa7, - 0xf4, 0x14, 0x48, 0x0f, 0xa0, 0x17, 0x48, 0x00, 0x69, 0xf8, 0x5c, 0x77, 0x49, 0xc4, - 0x35, 0xae, 0x2f, 0xba, 0x2d, 0xdc, 0x10, 0x38, 0xd5, 0x47, 0xd8, 0x48, 0x54, 0x81, - 0x7e, 0xf3, 0x96, 0x35, 0xc2, 0x98, 0x27, 0xaa, 0xd8, 0x67, 0x26, 0xc9, 0xad, 0xe3, - 0xb2, 0x65, 0xb9, 0x08, 0x6c, 0x8b, 0x5b, 0x75, 0xef, 0x56, 0xfe, 0x4b, 0xd8, 0xb4, - 0xd6, 0x28, 0x93, 0x89, 0x5b, 0x3f, 0xd2, 0x73, 0x4f, 0xda, 0xc4, 0x64, 0x15, 0x6d, - 0x7e, 0x5e, 0xbc, 0x7e, 0xcf, 0x1d, 0x83, 0xb8, 0x6f, 0x65, 0x96, 0x37, 0xe3, 0xb1, - 0x42, 0xc1, 0x64, 0x96, 0x3b, 0x8c, 0xdc, 0xf4, 0xba, 0x4f, 0x40, 0x35, 0xdf, 0xfc, - 0x5a, 0x78, 0x94, 0x58, 0x84, 0x77, 0x81, 0x91, 0x8a, 0xc7, 0x2f, 0xc1, 0x8b, 0xbb, - 0xf5, 0x11, 0x00, 0x32, 0xe6, 0x6d, 0x75, 0xb3, 0x17, 0x1e, 0xf4, 0xb5, 0x13, 0x29, - 0x01, 0x64, 0xa7, 0x7b, 0x42, 0xb0, 0xa4, 0xcf, 0xb8, 0x96, 0x39, 0xab, 0x23, 0x84, - 0x5e, 0x1a, 0xa2, 0xa4, 0x52, 0xf3, 0x73, 0x1c, 0x8c, 0xb6, 0x50, 0x82, 0xa6, 0x22, - 0xa7, 0xc2, 0xe0, 0x01, 0x3e, 0xa4, 0x7d, 0x0b, 0xdd, 0x42, 0xd6, 0x99, 0x04, 0x66, - 0x64, 0x9a, 0x90, 0x5c, 0x68, 0x4c, 0x32, 0x51, 0x71, 0x6d, 0x61, 0xf7, 0x60, 0xd5, - 0x3d, 0xe6, 0xe3, 0xf7, 0x90, 0xfb, 0xa7, 0xf5, 0xf1, 0xf4, 0xde, 0x26, 0x71, 0x13, - 0xbd, 0xfc, 0xd7, 0x42, 0x28, 0x22, 0x33, 0x0b, 0x32, 0xd5, 0x8e, 0x67, 0x77, 0x76, - 0x5f, 0x22, 0xa4, 0x11, 0x63, 0x44, 0xee, 0xb6, 0x5b, 0x2e, 0xc5, 0x16, 0x39, 0x3a, - 0xb3, 0x75, 0x1b, 0x53, 0x56, 0xd2, 0xb0, 0xc9, 0x50, 0x0c, 0x0f, 0x3e, 0x46, 0x91, - 0x81, 0x03, 0x5b, 0xc3, 0x66, 0x0f, 0x0b, 0x8f, 0x9f, 0xbe, 0x6e, 0x40, 0xb5, 0xe8, - 0x9c, 0xb7, 0x9b, 0x06, 0x37, 0x14, 0xca, 0x75, 0xe7, 0x2e, 0x2e, 0x10, 0x0a, 0x10, - 0xd6, 0x3b, 0xf7, 0x84, 0xdf, 0x08, 0x20, 0xef, 0x25, 0xf8, 0xef, 0x40, 0xfe, 0x5f, - 0x05, 0xfb, 0x95, 0x68, 0x3f, 0x91, 0x05, 0xff, 0x3c, 0xb2, 0xd2, 0x19, 0xab, 0x76, - 0x60, 0x5a, 0x06, 0x4f, 0x69, 0x21, 0x9f, 0x1d, 0xc0, 0xd0, 0x0b, 0x3b, 0x48, 0x64, - 0x2f, 0x97, 0x0d, 0xc0, 0x0c, 0xca, 0x4b, 0x8b, 0x43, 0x30, 0x8b, 0xe1, 0x82, 0x86, - 0xec, 0x5a, 0x42, 0x88, 0xd6, 0x00, 0xa3, 0x78, 0x5c, 0xb6, 0x22, 0xd4, 0x68, 0xa4, - 0xc6, 0x96, 0x9b, 0x37, 0x92, 0xf2, 0x48, 0x50, 0x27, 0xd0, 0xad, 0x9a, 0xa4, 0xa9, - 0xc2, 0xcc, 0x97, 0x2f, 0x9e, 0xe5, 0x19, 0x0a, 0x95, 0xb1, 0xeb, 0x05, 0x8d, 0xdd, - 0xd8, 0xc0, 0x8e, 0x7d, 0x75, 0x3f, 0x5e, 0x01, 0x1b, 0x2b, 0xcf, 0xee, 0x1d, 0x52, - 0xc1, 0xc4, 0xf2, 0xca, 0xcd, 0xa3, 0x0b, 0xdb, 0x69, 0x30, 0x65, 0x3c, 0x0c, 0xc4, - 0x48, 0x6e, 0x60, 0xe8, 0x9f, 0xa8, 0x49, 0xb3, - ], - script_code: Script(vec![0x53, 0x52]), - transparent_input: Some(0), - hash_type: 3, - amount: 1437866676382615, - consensus_branch_id: 1537743641, - sighash: [ - 0xd8, 0xe9, 0xb9, 0x72, 0xb2, 0x89, 0x8e, 0xfc, 0xca, 0x8e, 0x96, 0xbc, 0x98, 0x70, - 0x00, 0x8c, 0xdb, 0xc1, 0x9d, 0x45, 0xb7, 0x8d, 0x09, 0xef, 0xb1, 0x02, 0xf2, 0xd7, - 0x0d, 0xba, 0x01, 0xad, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x01, 0x87, 0xda, 0xa7, 0x31, 0xf5, - 0x70, 0xa7, 0xa4, 0x06, 0x0a, 0xf0, 0xce, 0x70, 0x0d, 0x31, 0xbc, 0xa7, 0xe7, 0x4b, - 0x3e, 0x3b, 0xa3, 0xd0, 0xe8, 0xa6, 0x39, 0x2a, 0x06, 0x2b, 0x8e, 0x86, 0xd9, 0xd7, - 0xd0, 0x0b, 0x21, 0x02, 0x65, 0x53, 0x06, 0x2e, 0x06, 0xb1, 0x01, 0x30, 0x11, 0xff, - 0x08, 0xf0, 0x83, 0x05, 0x00, 0x09, 0x63, 0x6a, 0x52, 0x63, 0x51, 0x63, 0x00, 0x6a, - 0xac, 0x9a, 0xbc, 0xef, 0x2a, 0x99, 0x08, 0x73, 0x19, 0x00, - ], - script_code: Script(vec![0x63]), - transparent_input: None, - hash_type: 1, - amount: 1993227025071196, - consensus_branch_id: 1537743641, - sighash: [ - 0x2b, 0x62, 0xff, 0x0c, 0x8d, 0xec, 0x4d, 0xf1, 0x8b, 0x99, 0x56, 0x61, 0x5b, 0x57, - 0x4d, 0xda, 0x39, 0x42, 0xfe, 0x45, 0x2d, 0x91, 0x78, 0xb0, 0xbb, 0xb2, 0xea, 0xee, - 0x4d, 0xe4, 0x4a, 0x8c, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x7c, 0x82, 0x97, 0x7c, 0x0f, - 0xf7, 0x97, 0x09, 0x3e, 0x2c, 0x1f, 0x3a, 0xe8, 0x55, 0xf6, 0x5a, 0xea, 0x91, 0xe1, - 0x31, 0x2f, 0xc6, 0xb8, 0xa4, 0x35, 0x1a, 0x2e, 0xc0, 0x3e, 0x02, 0xe5, 0xd0, 0x2f, - 0x53, 0x35, 0x4b, 0x05, 0x6a, 0x53, 0x52, 0x63, 0x6a, 0x82, 0xcd, 0x1f, 0x55, 0xeb, - 0xca, 0x57, 0xb6, 0x33, 0x7c, 0x85, 0x93, 0x8a, 0x79, 0x81, 0x3d, 0x20, 0x21, 0xd6, - 0x09, 0x4c, 0x68, 0xb3, 0x75, 0xe9, 0x84, 0xf6, 0x83, 0x93, 0x30, 0x08, 0x71, 0xe3, - 0x48, 0xfc, 0x52, 0x36, 0xcc, 0xa6, 0x33, 0x05, 0xac, 0x63, 0x65, 0x51, 0x63, 0x41, - 0x87, 0x01, 0xff, 0x01, 0x86, 0xd2, 0x6f, 0xee, 0x28, 0xca, 0x06, 0x00, 0x01, 0xac, - 0x5a, 0xa7, 0x27, 0xab, 0x79, 0x85, 0xda, 0x0e, 0x00, - ], - script_code: Script(vec![0x65, 0x53, 0x51]), - transparent_input: Some(1), - hash_type: 130, - amount: 449567650863240, - consensus_branch_id: 1537743641, - sighash: [ - 0x49, 0x3d, 0x49, 0xc3, 0xe2, 0x22, 0x5d, 0x11, 0xc4, 0x64, 0x05, 0x18, 0x20, 0x14, - 0x76, 0x25, 0xf3, 0x90, 0x9f, 0xa7, 0x18, 0x9f, 0x61, 0xc7, 0xea, 0xec, 0xfc, 0x6d, - 0xad, 0x2e, 0x82, 0x03, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0xe9, 0x6a, 0xa7, 0x3c, - 0xd9, 0xd1, 0x04, 0x00, 0x02, 0x00, 0x53, 0x06, 0xf6, 0x99, 0xe0, 0xb1, 0x9a, 0x04, - 0x00, 0x06, 0xac, 0x65, 0x65, 0x51, 0xac, 0x51, 0x0e, 0x68, 0xae, 0x38, 0x75, 0x05, - 0x51, 0x13, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x92, 0xf1, 0x35, 0xbf, 0x5f, 0x68, 0x78, 0x7d, - 0x37, 0x0c, 0xa8, 0xc4, 0xc4, 0x07, 0x4d, 0xc5, 0xd6, 0x01, 0xae, 0x90, 0x49, 0x54, - 0x37, 0xc3, 0xc2, 0xd4, 0x8a, 0x3d, 0x96, 0x66, 0x83, 0xac, 0x05, 0x16, 0x0b, 0x7a, - 0x84, 0xea, 0xa7, 0xaa, 0xb7, 0x40, 0x09, 0xe5, 0x7a, 0x85, 0xf7, 0xbf, 0x68, 0xa2, - 0xe4, 0x82, 0x00, 0x0f, 0x82, 0x9c, 0x54, 0x50, 0x73, 0xa1, 0x5d, 0x5c, 0xd0, 0xfc, - 0xc5, 0x74, 0x39, 0xa4, 0x35, 0x0e, 0xaf, 0x09, 0x8d, 0xfb, 0x82, 0xa0, 0x85, 0xea, - 0x8a, 0x4a, 0xf6, 0xfa, 0x83, 0x81, 0xf0, 0x65, 0x88, 0x19, 0xea, 0xb4, 0x83, 0xf6, - 0x5b, 0x32, 0x5d, 0x5a, 0xed, 0xa1, 0x52, 0x32, 0xcf, 0xad, 0xec, 0x75, 0xab, 0x18, - 0x66, 0xe4, 0xc0, 0x15, 0x5a, 0x9c, 0x74, 0xa7, 0xa5, 0x7c, 0xcf, 0x34, 0xc4, 0x83, - 0xac, 0x7d, 0xa1, 0x58, 0x8a, 0x1b, 0x6b, 0x99, 0x41, 0xf1, 0x10, 0x40, 0xf9, 0x4c, - 0xf7, 0x8f, 0xad, 0x89, 0xbf, 0x11, 0xfe, 0xd6, 0x9a, 0xa0, 0xd8, 0x31, 0x05, 0xad, - 0xac, 0xdd, 0x4e, 0x5f, 0x04, 0xa6, 0x24, 0x24, 0x02, 0x3c, 0x9b, 0x9e, 0x33, 0xc4, - 0xfb, 0x7f, 0x12, 0xbd, 0xf2, 0x1f, 0x07, 0xf2, 0x65, 0xc5, 0x37, 0xd5, 0x1c, 0x65, - 0x51, 0xf4, 0x61, 0x7b, 0x91, 0x5d, 0x21, 0x99, 0x18, 0x39, 0xc3, 0xd0, 0xd3, 0x63, - 0x93, 0xd6, 0x46, 0xe0, 0xa8, 0xa4, 0x15, 0x09, 0x21, 0x7d, 0x0e, 0x7d, 0x2c, 0xa1, - 0xa0, 0xa0, 0xd6, 0x77, 0xa3, 0xea, 0xca, 0x23, 0xed, 0xeb, 0x07, 0xb7, 0x4e, 0x65, - 0x2a, 0x0b, 0xc5, 0x0c, 0x6c, 0x08, 0x3a, 0x55, 0xd6, 0xc7, 0x30, 0x6e, 0x74, 0x08, - 0x6f, 0x47, 0x68, 0x93, 0x3a, 0xa2, 0x48, 0x73, 0x68, 0x18, 0x67, 0xa7, 0x89, 0x3d, - 0x77, 0xcb, 0x7f, 0x29, 0xb8, 0xc8, 0x47, 0xc5, 0x83, 0xf2, 0xd0, 0x71, 0xa6, 0x86, - 0x61, 0x6e, 0x20, 0x67, 0x19, 0xf7, 0x61, 0xae, 0x39, 0xc1, 0x10, 0x44, 0x2e, 0x06, - 0x16, 0x3d, 0x2b, 0x84, 0x59, 0x03, 0x60, 0x69, 0x5d, 0x4e, 0x19, 0x84, 0x9e, 0x03, - 0x4f, 0x24, 0xd9, 0xad, 0x39, 0x6c, 0x19, 0xff, 0x83, 0xce, 0x74, 0xf4, 0x6e, 0x64, - 0x5f, 0x93, 0x2e, 0x14, 0x1a, 0x41, 0x19, 0x59, 0x36, 0xc8, 0x5d, 0x51, 0x44, 0x14, - 0xf1, 0x12, 0xe6, 0x0b, 0x02, 0x25, 0x37, 0xc3, 0x8d, 0x6d, 0xc6, 0xc4, 0x63, 0x83, - 0x05, 0xc9, 0xbd, 0x6c, 0x62, 0xe3, 0x66, 0xbc, 0x63, 0x12, 0x3e, 0x3e, 0x6d, 0xd3, - 0x6e, 0xed, 0xd3, 0x13, 0x6f, 0xce, 0x8d, 0xee, 0xca, 0x0a, 0xa0, 0x9a, 0x32, 0x98, - 0xa3, 0x9d, 0x83, 0x85, 0x9e, 0xfc, 0x9b, 0x2b, 0x69, 0xcf, 0x9a, 0x7d, 0xee, 0x08, - 0xa9, 0x8e, 0x4b, 0xe5, 0x58, 0xac, 0x79, 0x12, 0xfd, 0xcb, 0x42, 0x20, 0x90, 0x75, - 0x42, 0x02, 0x60, 0xf7, 0xca, 0xd0, 0xf2, 0xc0, 0x1f, 0x2a, 0xfe, 0x33, 0x07, 0x3f, - 0x26, 0x24, 0x9d, 0x94, 0x4f, 0x7a, 0x50, 0xdd, 0x84, 0x83, 0x9b, 0xc3, 0xea, 0x7f, - 0xde, 0xe4, 0xed, 0x71, 0x02, 0x9c, 0xf0, 0x75, 0x33, 0xd2, 0x6e, 0x1e, 0x27, 0xa3, - 0xef, 0xb0, 0x32, 0xc3, 0xa3, 0xb3, 0x4b, 0xd3, 0x09, 0x26, 0x22, 0xd2, 0x06, 0x2a, - 0xe5, 0x36, 0xef, 0x51, 0x49, 0xc4, 0x9b, 0x5b, 0xc9, 0x03, 0x5e, 0xaf, 0xab, 0x6e, - 0x67, 0x57, 0x61, 0x00, 0x8b, 0x0d, 0xad, 0xde, 0xec, 0xaa, 0x60, 0x44, 0x70, 0xbb, - 0xe0, 0xfa, 0xda, 0x25, 0x5d, 0x29, 0x0e, 0x92, 0xb1, 0x90, 0xc2, 0xc2, 0xd8, 0xc2, - 0x02, 0xe5, 0x45, 0x5d, 0x1f, 0xa9, 0xa9, 0xf3, 0xdb, 0x77, 0x79, 0xb5, 0x84, 0x64, - 0x34, 0x64, 0xaa, 0x80, 0x14, 0xba, 0x66, 0x99, 0x4d, 0xe2, 0x55, 0x17, 0xf8, 0x39, - 0x80, 0xe6, 0x6e, 0xe4, 0xf6, 0x03, 0x14, 0xae, 0x6d, 0xbe, 0xf4, 0x52, 0xd5, 0xd3, - 0x8b, 0x0a, 0x16, 0xf3, 0x99, 0x1f, 0x36, 0xd8, 0xa8, 0xb3, 0x9d, 0xdc, 0x0d, 0x55, - 0x95, 0xee, 0xd9, 0x87, 0x62, 0x87, 0x8c, 0xdf, 0x3f, 0x4a, 0x02, 0xdc, 0x5c, 0xda, - 0x77, 0xd5, 0xfe, 0x4f, 0xaf, 0x63, 0xa1, 0x5f, 0x56, 0x8a, 0x54, 0x0d, 0xa5, 0x7d, - 0xd9, 0xbe, 0xb6, 0xfb, 0x1a, 0x97, 0x7c, 0xcb, 0x91, 0xb4, 0xd7, 0x9c, 0xb3, 0x9b, - 0x28, 0x91, 0x1a, 0x29, 0xe7, 0xbf, 0x02, 0x8a, 0xc6, 0x10, 0x37, 0x96, 0xdf, 0xb6, - 0xb2, 0x09, 0x67, 0x23, 0x9a, 0xd3, 0x73, 0xc3, 0x8c, 0x53, 0xf6, 0xdf, 0x18, 0x23, - 0xd4, 0x95, 0x0a, 0x02, 0x83, 0xe9, 0x9b, 0x9c, 0x06, 0xab, 0x29, 0x66, 0x66, 0x7c, - 0x9d, 0xf6, 0x77, 0x71, 0x6b, 0x0c, 0xad, 0xed, 0x81, 0x8d, 0xf9, 0xe4, 0x49, 0xc0, - 0x72, 0xe2, 0x2f, 0x9d, 0x98, 0xbb, 0x0f, 0x9b, 0x03, 0xbd, 0x5f, 0xd0, 0x13, 0xfc, - 0xef, 0x3e, 0xd6, 0xa4, 0x9a, 0xeb, 0x98, 0x72, 0x02, 0x54, 0x08, 0x7e, 0xf7, 0x28, - 0xe3, 0x19, 0x47, 0xff, 0xe8, 0xf7, 0x66, 0xe6, 0x3e, 0xe4, 0x6f, 0xf2, 0x08, 0x16, - 0xd5, 0xfa, 0x8f, 0xf5, 0x5a, 0x26, 0x39, 0x89, 0x61, 0x49, 0x0a, 0xb9, 0xae, 0x36, - 0x6f, 0xc5, 0xa2, 0xd1, 0x99, 0x6e, 0xd6, 0x93, 0xcc, 0xca, 0x82, 0x35, 0x6f, 0x60, - 0x0a, 0xb0, 0x99, 0xf6, 0xec, 0xa8, 0xbf, 0xe6, 0x45, 0x27, 0x0d, 0x3f, 0x95, 0xed, - 0xba, 0x5b, 0x0d, 0xe7, 0xa3, 0x28, 0x19, 0x23, 0x3b, 0xcc, 0x75, 0x4a, 0x5c, 0xe2, - 0xe5, 0xea, 0x07, 0x84, 0x2e, 0x5f, 0xf2, 0xce, 0xbe, 0x62, 0xad, 0x76, 0xe8, 0xef, - 0xf8, 0xd1, 0x5e, 0xa4, 0xc2, 0x4a, 0x5f, 0x20, 0x78, 0x68, 0x31, 0x9a, 0x5a, 0xf6, - 0xb0, 0x35, 0xbe, 0x3f, 0x44, 0xf4, 0x34, 0x09, 0x4f, 0x6e, 0x52, 0x5b, 0xe6, 0x14, - 0xda, 0xc9, 0x20, 0xa3, 0x30, 0xbd, 0xfb, 0x26, 0xd7, 0x5f, 0xe7, 0xb4, 0xb3, 0x65, - 0xd0, 0x94, 0x45, 0x92, 0x50, 0xaa, 0xa5, 0x54, 0x44, 0x89, 0xfb, 0x1d, 0x99, 0x25, - 0x81, 0x80, 0x0a, 0x77, 0xb8, 0x91, 0x21, 0x57, 0xfc, 0x97, 0x13, 0xaa, 0xac, 0x25, - 0xb4, 0xc2, 0x6e, 0xb0, 0x3f, 0x71, 0x66, 0x46, 0x61, 0x9a, 0xf0, 0x24, 0x56, 0xae, - 0x69, 0x59, 0x62, 0xfe, 0x5e, 0x93, 0x1a, 0x63, 0xb5, 0xc7, 0x90, 0x52, 0xec, 0xd3, - 0x33, 0xe1, 0x84, 0x12, 0xdb, 0x91, 0xe1, 0x5f, 0x7c, 0xbc, 0x70, 0xb4, 0xcd, 0x7e, - 0x8e, 0x3c, 0x95, 0x1f, 0x35, 0x85, 0x72, 0xe3, 0x77, 0x67, 0xe7, 0xd5, 0x27, 0x04, - 0xa6, 0x72, 0x1b, 0x30, 0xef, 0xc4, 0x10, 0x17, 0xae, 0x4d, 0x23, 0x15, 0x58, 0xc5, - 0xc8, 0x2c, 0xc7, 0xdd, 0x7e, 0x33, 0x56, 0xc0, 0x9d, 0xc2, 0x49, 0x06, 0xf0, 0x43, - 0x8d, 0xfc, 0xc3, 0x00, 0x85, 0x6a, 0xc2, 0xce, 0xd8, 0xf7, 0x7f, 0xa8, 0x01, 0x57, - 0x36, 0xc6, 0x61, 0xe8, 0x02, 0x48, 0xae, 0xeb, 0x77, 0x48, 0x74, 0xaa, 0x79, 0xd2, - 0x90, 0xb8, 0xf5, 0x02, 0x7a, 0x0a, 0x50, 0x95, 0x37, 0xfc, 0x7c, 0x68, 0x9b, 0x7a, - 0xd8, 0x61, 0x16, 0xcf, 0xec, 0x26, 0x47, 0xcc, 0xaa, 0xe1, 0xc7, 0x4b, 0x41, 0x6f, - 0x3e, 0x6a, 0xe8, 0xf7, 0xcc, 0x60, 0xea, 0xaf, 0x7b, 0x6a, 0x59, 0x0d, 0x51, 0x54, - 0x41, 0x38, 0xe1, 0x73, 0x29, 0x45, 0x60, 0x3a, 0x53, 0x46, 0x2c, 0x60, 0xe1, 0xf6, - 0xcb, 0x0c, 0x9c, 0xa0, 0x39, 0x0c, 0x48, 0x82, 0x24, 0xc3, 0x13, 0x26, 0x9f, 0xcd, - 0x59, 0xfc, 0xb6, 0x11, 0xfb, 0x2d, 0x9b, 0x4c, 0x8f, 0xa6, 0x01, 0xbb, 0x1c, 0xb8, - 0xd0, 0x7d, 0x79, 0x7b, 0xf5, 0xde, 0x52, 0xbc, 0xee, 0xb0, 0x23, 0x01, 0xc8, 0x96, - 0x2a, 0xc1, 0xfc, 0x04, 0x91, 0xdc, 0x81, 0xaf, 0xfd, 0x6c, 0x1e, 0xbf, 0x89, 0xa1, - 0x3d, 0x6f, 0x29, 0x0e, 0xda, 0x5d, 0x5c, 0xef, 0x38, 0x22, 0x15, 0xc5, 0xe9, 0x51, - 0xd7, 0x13, 0x05, 0xef, 0x33, 0xd9, 0x73, 0x71, 0x26, 0xd0, 0xe6, 0x62, 0x90, 0x5f, - 0x12, 0x50, 0x92, 0x6f, 0x6a, 0x22, 0x99, 0x90, 0xe3, 0x8f, 0x69, 0xad, 0x9a, 0x91, - 0x92, 0xb3, 0x02, 0xf2, 0x6b, 0xdd, 0xa4, 0x65, 0xd9, 0x0b, 0x94, 0xb1, 0x2c, 0x57, - 0xfa, 0x3f, 0xd6, 0x93, 0x00, 0x83, 0xf1, 0x84, 0x43, 0x8d, 0x8a, 0x88, 0x9d, 0x3f, - 0x5e, 0xce, 0xa2, 0xc6, 0xd2, 0x3d, 0x67, 0x36, 0xf2, 0xa0, 0xf1, 0x8e, 0x26, 0xf4, - 0xfa, 0x45, 0xd1, 0xbe, 0x8f, 0x3d, 0xc4, 0xa7, 0x07, 0x13, 0x7e, 0x95, 0xd2, 0xad, - 0x59, 0x4f, 0x6c, 0x03, 0xd2, 0x49, 0x23, 0x06, 0x7a, 0xe4, 0x7f, 0xd6, 0x42, 0x5e, - 0xfb, 0x9c, 0x1d, 0x50, 0x4e, 0x6f, 0xd5, 0x57, 0x53, 0x40, 0x94, 0x56, 0x01, 0xfe, - 0x80, 0x6f, 0x57, 0x56, 0xac, 0xb5, 0x62, 0xf1, 0x3c, 0x0c, 0xa1, 0xd8, 0x03, 0xa1, - 0x95, 0xc2, 0xeb, 0xb2, 0xef, 0x02, 0xac, 0x33, 0xe6, 0xa8, 0x8d, 0xea, 0x07, 0x5b, - 0xa9, 0x96, 0xd3, 0xc3, 0x36, 0x64, 0x8e, 0x86, 0x94, 0xd3, 0xa1, 0x9d, 0x3d, 0xca, - 0x53, 0x1b, 0xeb, 0x50, 0xd4, 0x32, 0x7c, 0x5c, 0x0c, 0x23, 0xcb, 0x7c, 0xfd, 0xb0, - 0x8c, 0xa7, 0xcf, 0x2c, 0xac, 0x6b, 0xc1, 0x39, 0xd0, 0x74, 0x14, 0x73, 0xd3, 0x76, - 0x02, 0x9c, 0xb4, 0xab, 0x6b, 0xf0, 0x54, 0x55, 0x7c, 0xe2, 0x94, 0xc7, 0x28, 0xa4, - 0x68, 0x7d, 0x57, 0xec, 0x89, 0x09, 0xff, 0x51, 0xa4, 0xd0, 0x2f, 0x9d, 0xcd, 0x11, - 0x19, 0x3d, 0x7d, 0x1c, 0x9f, 0xda, 0xe6, 0xa1, 0x73, 0x96, 0xa1, 0xbf, 0x57, 0xa9, - 0x94, 0x93, 0x4f, 0x5e, 0x7a, 0x59, 0xf0, 0x45, 0xde, 0xbe, 0xaf, 0xf6, 0x2e, 0xf3, - 0x26, 0xb9, 0x47, 0xf2, 0xa8, 0xb4, 0x95, 0x55, 0xe4, 0xd9, 0x9b, 0x3b, 0xf5, 0xc8, - 0x1f, 0xf9, 0xfe, 0x31, 0x4e, 0x04, 0x7a, 0xf1, 0x52, 0x50, 0x8f, 0x57, 0x01, 0x5c, - 0xa4, 0x02, 0xc6, 0x7d, 0x92, 0x5c, 0x99, 0xac, 0xea, 0x3e, 0xe8, 0xcc, 0x4b, 0x00, - 0x8c, 0x5c, 0xb4, 0x39, 0x66, 0xe7, 0x14, 0xef, 0x48, 0x0f, 0xd0, 0x5e, 0x07, 0xc7, - 0xb2, 0xdd, 0xa9, 0xaa, 0x39, 0x66, 0x11, 0x3e, 0xaa, 0x29, 0x3d, 0x3f, 0x62, 0x2b, - 0x30, 0x9d, 0x64, 0x80, 0x3c, 0xe1, 0xe6, 0x37, 0x8b, 0x6a, 0xac, 0x4f, 0xab, 0x52, - 0x7c, 0x43, 0xcd, 0x45, 0xed, 0x0a, 0x3c, 0x1a, 0x4b, 0x9f, 0xb1, 0x8d, 0xcc, 0xcf, - 0xcd, 0xb6, 0xac, 0x0c, 0x24, 0x21, 0x63, 0x9c, 0xda, 0x00, 0x75, 0xa2, 0x0d, 0xc5, - 0x11, 0x1b, 0x8d, 0x3d, 0x31, 0x99, 0x49, 0x5b, 0xd9, 0x13, 0x3d, 0xba, 0xb9, 0x45, - 0x41, 0x41, 0x0e, 0x4f, 0xba, 0x92, 0xc7, 0xb6, 0x06, 0xa5, 0xcb, 0x12, 0x2f, 0x14, - 0x0c, 0xf1, 0xa3, 0x59, 0x6f, 0x27, 0x88, 0xf3, 0xc8, 0xb9, 0x26, 0x60, 0xf1, 0x4c, - 0xb6, 0x5a, 0xf5, 0xdd, 0x23, 0xdf, 0xdb, 0xac, 0x13, 0x71, 0xec, 0xf4, 0xb3, 0x37, - 0x12, 0xfe, 0xd2, 0x29, 0x2c, 0x44, 0xf7, 0x08, 0x34, 0xcf, 0x96, 0xc0, 0x5d, 0x58, - 0x82, 0x7e, 0x69, 0xbf, 0xc2, 0xe6, 0x96, 0xfa, 0x08, 0x74, 0x86, 0x9c, 0x02, 0xf3, - 0xdc, 0xa1, 0x1c, 0x3b, 0x90, 0xcb, 0x21, 0x4e, 0x68, 0xbc, 0x1c, 0xae, 0x03, 0x9d, - 0x7a, 0x14, 0x6c, 0xdc, 0x1d, 0x60, 0x9d, 0x7a, 0x6b, 0x3f, 0xd5, 0xd4, 0x61, 0xb0, - 0x95, 0x1c, 0x82, 0xcf, 0xb3, 0xe7, 0x63, 0xfa, 0xd2, 0xd1, 0xbc, 0x76, 0x78, 0xcd, - 0xf8, 0x27, 0x79, 0xf8, 0xfd, 0x5a, 0x1c, 0xe2, 0x2a, 0x8d, 0x3c, 0x45, 0x47, 0xab, - 0xd9, 0x59, 0x83, 0x8a, 0x46, 0xfb, 0x80, 0xaf, 0xe0, 0x1f, 0x8e, 0xcc, 0x99, 0x31, - 0x51, 0x3b, 0x19, 0x62, 0xec, 0x54, 0x08, 0x56, 0xcb, 0x18, 0x93, 0x87, 0xcf, 0xbf, - 0xcc, 0x0f, 0x7c, 0x68, 0x22, 0x3c, 0xba, 0x47, 0xfb, 0x0c, 0x9b, 0x48, 0x6e, 0x4d, - 0x99, 0x17, 0x19, 0x41, 0xf7, 0x67, 0x5a, 0x8b, 0x46, 0x32, 0x8a, 0x3b, 0xc1, 0x09, - 0xbf, 0x07, 0xc6, 0x6d, 0x5e, 0xde, 0x77, 0x1c, 0xc4, 0xc7, 0x4c, 0xe8, 0x03, 0x33, - 0x82, 0x91, 0x91, 0xee, 0xdc, 0x49, 0x35, 0x08, 0xa6, 0x44, 0x53, 0x0a, 0x61, 0x44, - 0xf2, 0x2d, 0xcf, 0x97, 0x52, 0x5a, 0x4c, 0xdc, 0xa1, 0xad, 0x71, 0x07, 0x3b, 0x08, - 0x0b, 0x73, 0xea, 0x45, 0x49, 0xf5, 0x40, 0x1b, 0xff, 0x43, 0x18, 0x26, 0x8e, 0x6a, - 0xd6, 0x37, 0x36, 0x31, 0x57, 0xa1, 0x9a, 0x53, 0xf1, 0x23, 0xa0, 0xb0, 0xe1, 0x6d, - 0x0b, 0x77, 0xf0, 0x20, 0x28, 0xda, 0x46, 0x41, 0x00, 0xfd, 0xe7, 0x6d, 0x83, 0xdd, - 0x0b, 0xb2, 0x24, 0xf7, 0xb5, 0x7a, 0x00, 0xc0, 0x2f, 0x68, 0xae, 0x64, 0x8f, 0xdc, - 0x52, 0x99, 0x57, 0xa1, 0x04, 0x90, 0xdc, 0xe1, 0xfd, 0xdb, 0xb0, 0x90, 0x4f, 0x0d, - 0x51, 0x8b, 0xb3, 0x87, 0x54, 0x40, 0x19, 0x98, 0x3b, 0x61, 0x69, 0x75, 0xa7, 0x8e, - 0x74, 0xd8, 0x54, 0xfd, 0xdc, 0x49, 0xb2, 0x55, 0x16, 0x7b, 0x55, 0xef, 0x4b, 0xee, - 0x46, 0x56, 0x68, 0xb2, 0x0e, 0xa4, 0x11, 0x8c, 0xa5, 0x69, 0xae, 0x48, 0x0e, 0x0f, - 0x6e, 0x5e, 0x04, 0x3a, 0x35, 0x7b, 0x36, 0xd3, 0xab, 0x36, 0xc8, 0x61, 0xf2, 0x27, - 0x83, 0x01, 0xdc, 0xe5, 0x76, 0x74, 0xd5, 0x07, 0x3b, 0x3a, 0x6f, 0x51, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xa0, 0x79, 0x3a, 0xf1, 0xb7, 0xd4, 0x6f, 0x95, 0x7e, 0x22, 0xd8, 0xd2, 0x58, - 0x3b, 0xf1, 0x81, 0x83, 0x6c, 0x3b, 0xe9, 0x93, 0x0b, 0xac, 0x8f, 0xa4, 0x60, 0xe9, - 0x68, 0xaa, 0x71, 0x09, 0x87, 0x0b, 0xbe, 0xd1, 0x7d, 0xf5, 0xf8, 0x88, 0xc8, 0xca, - 0x14, 0x67, 0xae, 0x17, 0xdb, 0xbc, 0xde, 0x31, 0xc1, 0x10, 0x5c, 0xb5, 0xbd, 0xa8, - 0x8a, 0xc6, 0xc6, 0x27, 0x00, 0x2c, 0xe2, 0x1c, 0x02, 0x14, 0x0f, 0xfe, 0x81, 0xec, - 0x58, 0xbf, 0x1e, 0x6d, 0x1b, 0xb7, 0xaa, 0xad, 0xa4, 0x1f, 0xba, 0x0b, 0xb5, 0x88, - 0x77, 0x8a, 0x7f, 0x65, 0x20, 0x2a, 0xd8, 0x11, 0xea, 0x73, 0xd2, 0x6c, 0x74, 0x55, - 0x03, 0x95, 0xaf, 0xf7, 0x53, 0x25, 0x10, 0x7c, 0x9b, 0x3f, 0x9a, 0xe9, 0xdc, 0xdc, - 0xd8, 0x6e, 0xd0, 0x81, 0xa2, 0xe7, 0x42, 0x47, 0x19, 0xa3, 0xd1, 0x85, 0xb7, 0xe0, - 0xa4, 0x3a, 0x47, 0x2e, 0x29, 0x8a, 0xc0, 0xaf, 0xdc, 0x52, 0x87, 0xd7, 0xad, 0x12, - 0x4c, 0xd9, 0x40, 0x5a, 0x62, 0xcd, 0x1c, 0xa0, 0x8b, 0x28, 0x2e, 0xfe, 0xf7, 0xf9, - 0x28, 0xdf, 0x76, 0xe2, 0x82, 0x1a, 0x41, 0x84, 0x13, 0xeb, 0x7c, 0xea, 0xa5, 0xff, - 0x12, 0x90, 0xb0, 0x3e, 0xc9, 0x1c, 0xe6, 0xdd, 0x28, 0x13, 0x0c, 0x3a, 0xb0, 0xb2, - 0x3b, 0x60, 0x2b, 0xd5, 0xbe, 0x5d, 0xc2, 0x60, 0x03, 0xaa, 0xe0, 0x4b, 0x33, 0xd7, - 0xbd, 0x25, 0x90, 0xe9, 0x0c, 0x8c, 0x38, 0x8e, 0xa7, 0x95, 0x51, 0x22, 0xdb, 0xac, - 0xa6, 0x7b, 0x30, 0x39, 0x5a, 0x92, 0x8b, 0x57, 0xb8, 0x57, 0x51, 0x23, 0x20, 0x5a, - 0xe1, 0x91, 0x52, 0xe4, 0x1e, 0x00, 0x29, 0x31, 0xb4, 0x57, 0x46, 0x19, 0x8e, 0x5d, - 0xd9, 0x57, 0x1a, 0x56, 0xa7, 0xe0, 0xd4, 0x23, 0xff, 0x27, 0x98, 0x9d, 0x3e, 0xb4, - 0x17, 0xec, 0xd3, 0xc3, 0x09, 0x3f, 0xb8, 0x2c, 0x56, 0x58, 0xe2, 0x96, 0x24, 0xc5, - 0x32, 0x19, 0xa6, 0x0c, 0xd0, 0xa8, 0xc4, 0xda, 0x36, 0x7e, 0x29, 0xa7, 0x17, 0x79, - 0xa7, 0x30, 0x32, 0x98, 0x5a, 0x3d, 0x1f, 0xd0, 0x3d, 0x02, 0xd0, 0x6e, 0x05, 0x56, - 0x6f, 0x3b, 0x84, 0x36, 0x7c, 0xf0, 0xfa, 0xee, 0x9b, 0xc3, 0xbd, 0x7a, 0x3a, 0x60, - 0x6a, 0x9f, 0xdb, 0x84, 0x9c, 0x5d, 0x82, 0xd0, 0xa6, 0x19, 0x23, 0xc2, 0xe5, 0xd8, - 0x02, 0x63, 0xa8, 0xa5, 0x0c, 0x38, 0xbd, 0x03, 0x87, 0x72, 0xc4, 0x14, 0x3d, 0x8b, - 0x7a, 0xcf, 0xd7, 0x4e, 0x72, 0xc0, 0x4d, 0x89, 0x24, 0x8d, 0xff, 0x20, 0xfe, 0x8d, - 0xc5, 0xec, 0x21, 0x49, 0x05, 0x0a, 0xa2, 0x41, 0x64, 0xe8, 0x5f, 0x67, 0x44, 0xad, - 0x0c, 0xac, 0xf1, 0xa8, 0xb7, 0x01, 0x26, 0xf4, 0x82, 0xc0, 0x92, 0xed, 0x9f, 0x61, - 0x27, 0xd2, 0x05, 0x0d, 0x12, 0xe8, 0x78, 0xa7, 0x96, 0x53, 0xa1, 0xe8, 0x4d, 0xae, - 0xc3, 0xeb, 0xe6, 0x2d, 0x5f, 0x6c, 0x4a, 0xbe, 0x5c, 0xe9, 0x0a, 0x7f, 0xe2, 0xe5, - 0x2a, 0x8d, 0x78, 0x46, 0xe8, 0xed, 0xf2, 0xf2, 0xbc, 0xe0, 0x5a, 0x03, 0x7c, 0x82, - 0x03, 0x22, 0xca, 0xad, 0x12, 0x61, 0x46, 0x7d, 0xcf, 0xb7, 0xd6, 0xb6, 0x13, 0x3d, - 0xc2, 0x1e, 0x80, 0x96, 0xc7, 0xe9, 0xf8, 0xe9, 0xe1, 0x0c, 0x1e, 0x3f, 0xac, 0x40, - 0x58, 0xb6, 0x82, 0xc6, 0x8e, 0x02, 0xfa, 0xca, 0xe0, 0xf9, 0xc2, 0xdd, 0x4d, 0x64, - 0xd9, 0x04, 0x61, 0x52, 0xb4, 0x76, 0x23, 0x32, 0x93, 0x9f, 0x17, 0xe6, 0xaa, 0xf7, - 0xd8, 0xb9, 0xd3, 0x58, 0xe2, 0x21, 0x8d, 0x4e, 0x0d, 0x69, 0x02, 0xf1, 0x19, 0xe1, - 0xc6, 0x4e, 0xec, 0x4c, 0x8b, 0x53, 0x28, 0x09, 0x70, 0x71, 0x31, 0xf0, 0x1f, 0x55, - 0xc7, 0xad, 0x04, 0xcf, 0xb6, 0x3f, 0x7c, 0x4a, 0x3d, 0x0a, 0x2b, 0x0f, 0xfb, 0x0b, - 0x05, 0x02, 0xbe, 0x05, 0x5b, 0x8c, 0x94, 0xca, 0x80, 0xbb, 0x0a, 0x1d, 0x13, 0xcd, - 0x4c, 0xd6, 0x9a, 0xb9, 0x83, 0x04, 0xae, 0x25, 0x15, 0xd5, 0xf7, 0x69, 0x9d, 0x4a, - 0xbe, 0xe5, 0xc2, 0x0b, 0xe6, 0x09, 0x02, 0x73, 0x51, 0x10, 0x12, 0xf2, 0x34, 0xbd, - 0x85, 0xa7, 0xef, 0xf5, 0xfb, 0x63, 0x4c, 0xff, 0x26, 0x58, 0xba, 0x65, 0x16, 0x04, - 0x85, 0x63, 0x09, 0x5e, 0xce, 0xfb, 0x30, 0x15, 0xee, 0x3f, 0x03, 0xca, 0x52, 0xa1, - 0x77, 0xf2, 0x61, 0xec, 0xdc, 0x26, 0xbc, 0x08, 0x9d, 0x34, 0xc6, 0x40, 0x48, 0x46, - 0xe9, 0xc6, 0x47, 0xfc, 0xfe, 0x98, 0xcc, 0x6a, 0xcd, 0xbb, 0x46, 0x4f, 0x64, 0x27, - 0x8a, 0xd8, 0xce, 0x9d, 0x1a, 0xe0, 0xd4, 0x15, 0xbc, 0x0c, 0x05, 0x24, 0x5f, 0xdd, - 0xaf, 0x4e, 0xbc, 0x8d, 0xc7, 0x03, 0xa8, 0x5c, 0xb2, 0x70, 0xf7, 0x96, 0xad, 0x2d, - 0x93, 0x7e, 0x2a, 0xc0, 0xd5, 0xe0, 0xa3, 0x48, 0x21, 0x75, 0x80, 0x00, 0xaa, 0x59, - 0xc9, 0xd4, 0x65, 0x24, 0x85, 0x29, 0x4e, 0xe0, 0xab, 0x29, 0x69, 0x6b, 0x21, 0x43, - 0x0f, 0xa5, 0x4d, 0xcf, 0xbf, 0x2b, 0x9c, 0x49, 0xd1, 0x42, 0x06, 0x42, 0x09, 0xee, - 0xee, 0xd4, 0xd4, 0x71, 0xff, 0xc0, 0x17, 0xd4, 0xe2, 0x0a, 0x79, 0x6b, 0x09, 0x27, - 0x80, 0x4c, 0x06, 0x1b, 0x9f, 0x4a, 0x70, 0x91, 0xfe, 0x01, 0x5a, 0xda, 0x68, 0xfd, - 0x84, 0x42, 0xe0, 0x18, 0x25, 0xc8, 0x8d, 0xfe, 0x55, 0xcf, 0x5d, 0xe3, 0x89, 0x36, - 0xf7, 0xce, 0x25, 0x31, 0x1b, 0x90, 0x2b, 0xa9, 0x7a, 0x3c, 0x12, 0xa9, 0x5c, 0xfa, - 0x1c, 0x3a, 0x59, 0x1b, 0x81, 0x8f, 0x60, 0x83, 0x27, 0x09, 0xd9, 0xe4, 0x83, 0x9e, - 0x41, 0x0f, 0xb3, 0x6b, 0x84, 0xf3, 0xac, 0x4f, 0x07, 0x0f, 0xc3, 0x5e, 0x16, 0x19, - 0x78, 0x25, 0x9e, 0x5b, 0x8e, 0xdc, 0x74, 0x4d, 0x90, 0x91, 0x9a, 0xa7, 0x70, 0xbb, - 0x36, 0x21, 0x51, 0x28, 0xe5, 0x82, 0xb5, 0x96, 0x41, 0xe2, 0x38, 0x52, 0xe9, 0x58, - 0xeb, 0x8f, 0xc3, 0xc0, 0xaa, 0x96, 0x15, 0x2b, 0xa4, 0xf7, 0x7f, 0x13, 0x8d, 0x6a, - 0x67, 0x12, 0xa3, 0xae, 0x32, 0x26, 0x01, 0x58, 0x83, 0xf8, 0x1d, 0xb2, 0x3e, 0x58, - 0x3c, 0x86, 0x9c, 0x4c, 0x71, 0x14, 0x3a, 0x6f, 0xff, 0xd6, 0x5e, 0x8d, 0xfd, 0xc5, - 0x0c, 0x99, 0xa2, 0xf1, 0xf3, 0x14, 0xcd, 0xcc, 0x71, 0x35, 0x9e, 0x23, 0x5f, 0x1d, - 0x7d, 0xc2, 0xb5, 0xf3, 0x8e, 0xf7, 0xb9, 0x70, 0x84, 0x31, 0x63, 0xc0, 0x3f, 0x9d, - 0xd4, 0x0a, 0x80, 0x15, 0xef, 0xdc, 0x87, 0x91, 0x95, 0x6a, 0x3f, 0x3c, 0xed, 0xd9, - 0xea, 0x64, 0xf8, 0xef, 0xa7, 0xa0, 0x81, 0x5a, 0x70, 0x38, 0x1d, 0x71, 0x46, 0x78, - 0x17, 0xbd, 0x04, 0xca, 0x52, 0x9a, 0xed, 0xe0, 0x7f, 0xf6, 0x0d, 0x17, 0x6a, 0xed, - 0x0f, 0x85, 0x5a, 0x2e, 0xae, 0xa8, 0x9e, 0xae, 0xac, 0xa8, 0x93, 0x58, 0xc0, 0x81, - 0x82, 0x6a, 0x08, 0x12, 0xa5, 0xbc, 0xa2, 0x8b, 0xe1, 0x37, 0x3f, 0x08, 0x6d, 0xbd, - 0xba, 0x7e, 0x43, 0xe2, 0x03, 0x21, 0x2c, 0x9f, 0xed, 0x21, 0x47, 0x4b, 0xa1, 0x9a, - 0x05, 0x5f, 0xfc, 0xc1, 0x79, 0x41, 0x2e, 0x89, 0x3a, 0x74, 0x48, 0x32, 0x29, 0x8c, - 0x5f, 0xe2, 0x4c, 0xc6, 0xb1, 0x86, 0x67, 0xf4, 0x9b, 0x34, 0xdf, 0xb1, 0x23, 0x79, - 0x26, 0x74, 0x19, 0xa9, 0xcb, 0x94, 0x03, 0xd8, 0x16, 0x7d, 0x8d, 0x1e, 0x91, 0xd2, - 0x81, 0x1a, 0x04, 0x3b, 0x29, 0x24, 0x3b, 0x06, 0x9b, 0x37, 0x58, 0x78, 0x47, 0xdc, - 0x6f, 0xcd, 0xdb, 0x18, 0x31, 0xbd, 0x1c, 0xc2, 0x56, 0x7c, 0xa0, 0x33, 0xac, 0x40, - 0xf7, 0x4a, 0xb6, 0x95, 0x5f, 0x68, 0x3b, 0x12, 0xe4, 0xe8, 0x25, 0x4e, 0x4e, 0xa7, - 0x60, 0xd3, 0x8b, 0x3f, 0x46, 0x79, 0x1c, 0x5c, 0x4c, 0xb1, 0x2b, 0xc7, 0xcc, 0xb0, - 0xed, 0x18, 0x65, 0xf2, 0x5d, 0x60, 0x1c, 0x30, 0x3f, 0x81, 0xfb, 0x1f, 0xa1, 0xdb, - 0x48, 0x53, 0x3d, 0x3d, 0x6b, 0x28, 0x8e, 0x4d, 0x9a, 0x4d, 0xff, 0x8e, 0xc2, 0x1c, - 0x96, 0xf5, 0x78, 0x39, 0x97, 0x10, 0xc8, 0x25, 0xfe, 0x7e, 0x32, 0xf9, 0x3a, 0x8c, - 0x07, 0x43, 0xf9, 0xeb, 0xd5, 0x4c, 0xc1, 0x51, 0xc7, 0x61, 0x03, 0x37, 0xae, 0xbf, - 0x7e, 0x9b, 0x91, 0x57, 0x20, 0xa5, 0x43, 0x51, 0xd4, 0x9a, 0xb8, 0xc2, 0x2f, 0xa3, - 0x49, 0x98, 0xdc, 0xf5, 0x83, 0xd4, 0x38, 0x73, 0x61, 0xef, 0x3f, 0xf8, 0x6f, 0x50, - 0xec, 0x53, 0xf4, 0x92, 0x49, 0xe4, 0xad, 0x34, 0x96, 0x03, 0x06, 0x6f, 0xc9, 0xc6, - 0x61, 0xd6, 0x9f, 0x91, 0x1d, 0xfa, 0x72, 0x41, 0xc8, 0xd5, 0x79, 0x2d, 0x43, 0xc4, - 0x57, 0xd5, 0xde, 0x96, 0x52, 0x3a, 0x53, 0xd6, 0x67, 0xec, 0x5c, 0x4e, 0xf9, 0xd5, - 0x02, 0xa1, 0x6f, 0x15, 0x22, 0x47, 0x58, 0x96, 0xd7, 0x9b, 0xc5, 0x78, 0x33, 0xe9, - 0x77, 0x17, 0x1c, 0x32, 0x4d, 0xce, 0x2a, 0x1e, 0xa1, 0xe4, 0x30, 0x4f, 0x49, 0xe4, - 0x3a, 0xe0, 0x65, 0xe3, 0xfb, 0x19, 0x6f, 0x76, 0xd9, 0xb8, 0x79, 0xc7, 0x20, 0x08, - 0x62, 0xea, 0xd1, 0x8d, 0xea, 0x5f, 0xb6, 0xa1, 0x7a, 0xce, 0xa3, 0x33, 0x86, 0xeb, - 0x4c, 0xa1, 0xb5, 0x14, 0x86, 0xa9, 0x14, 0x8f, 0xbd, 0xf9, 0xa9, 0x53, 0x32, 0xaa, - 0x60, 0x5c, 0x5d, 0x54, 0x83, 0xce, 0x4b, 0xa8, 0xec, 0xe0, 0x1a, 0x8f, 0xf2, 0xb7, - 0xef, 0x82, 0xd0, 0x5c, 0x0b, 0x6e, 0x86, 0x1b, 0x91, 0x5f, 0x13, 0xca, 0x0e, 0xb3, - 0xea, 0x13, 0xd5, 0x07, 0x08, 0x07, 0xa2, 0xcb, 0x66, 0x80, 0xa2, 0x49, 0xea, 0x9c, - 0x72, 0x24, 0x39, 0x2c, 0xbc, 0x8a, 0xb8, 0x25, 0x01, 0xb2, 0x6f, 0x11, 0x2a, 0xc7, - 0x89, 0xa1, 0x2a, 0x31, 0xad, 0x13, 0x14, 0xe2, 0xed, 0xe0, 0x8f, 0xad, 0x31, 0x43, - 0xaf, 0x30, 0xc2, 0x7f, 0x40, 0x3b, 0xc8, 0x66, 0xc7, 0x55, 0x17, 0x78, 0x52, 0xaf, - 0xd0, 0xab, 0xb9, 0x0a, 0xde, 0x1d, 0x68, 0x27, 0x26, 0xf4, 0x20, 0x08, 0xb4, 0x6a, - 0xd7, 0xf8, 0xab, 0xdb, 0x18, 0x11, 0x7f, 0x72, 0x64, 0x13, 0x90, 0xf0, 0x86, 0xb6, - 0xe1, 0x49, 0x8b, 0xe6, 0x95, 0x48, 0x52, 0x7e, 0x6a, 0xda, 0x2b, 0x38, 0xb9, 0xfe, - 0x12, 0x1e, 0xf6, 0x70, 0xaf, 0x74, 0x37, 0xd3, 0x25, 0x36, 0xd5, 0xcf, 0x5c, 0x4a, - 0xb1, 0x9d, 0xd9, 0x97, 0x71, 0x58, 0x2d, 0x03, 0x81, 0x04, 0xb7, 0xe0, 0x39, 0xa3, - 0x76, 0xf7, 0xac, 0xbb, 0xea, 0xdb, 0x34, 0xf9, 0x45, 0xbe, 0xb9, 0xd7, 0xca, 0x0e, - 0x4e, 0x3d, 0x5c, 0x5e, 0x4e, 0xb1, 0xd8, 0x52, 0x6e, 0xbd, 0x13, 0xda, 0xcb, 0x1b, - 0xa3, 0x57, 0x35, 0xc6, 0xd0, 0x4a, 0x45, 0x55, 0xac, 0xf4, 0xbf, 0x11, 0x76, 0x26, - 0x50, 0x0d, 0x77, 0xb3, 0x81, 0x89, 0xdd, 0x48, 0x88, 0x04, 0x12, 0x25, 0xac, 0xbe, - 0x38, 0x74, 0xa4, 0xc0, 0xf6, 0x07, 0xfe, 0x67, 0x45, 0xf9, 0x35, 0x5b, 0x3f, 0xa1, - 0x88, 0xf1, 0xd6, 0x5c, 0x09, 0xf3, 0x89, 0xaf, 0x1b, 0x9d, 0x62, 0x32, 0xaa, 0x79, - 0x44, 0x79, 0x19, 0xc5, 0x50, 0xf6, 0xf3, 0x1f, 0xec, 0x35, 0x48, 0x1c, 0xb9, 0x22, - 0xde, 0x2d, 0xb5, 0xb4, 0xda, 0x2f, 0x81, 0x94, 0x86, 0x17, 0x02, 0x8e, 0x32, 0x17, - 0x06, 0xa3, 0xa7, 0x78, 0xc1, 0x93, 0x8c, 0x44, 0x3b, 0xb0, 0x0e, 0x5b, 0x0f, 0xf0, - 0x6a, 0xd8, 0xab, 0x9b, 0x1a, 0xb0, 0xc1, 0x14, 0x77, 0x67, 0x3f, 0x85, 0xdf, 0x95, - 0x61, 0xdb, 0xea, 0x45, 0xd5, 0xf9, 0x78, 0x1e, 0xbe, 0x31, 0x7a, 0x07, 0x10, 0xae, - 0x54, 0x61, 0xe3, 0x4f, 0xe6, 0xf1, 0xb1, 0xaa, 0x9b, 0x4e, 0x67, 0xb1, 0x49, 0x10, - 0x98, 0x48, 0x02, 0xc2, 0xa7, 0xe3, 0x81, 0x93, 0xbc, 0x7b, 0xdc, 0x8b, 0xa3, 0xe4, - 0xe3, 0xd1, 0xd9, 0x33, 0xbf, 0xb5, 0x80, 0xf5, 0xb3, 0xe8, 0x7a, 0x2a, 0x06, 0x51, - 0x70, 0x51, 0x41, 0x0f, 0xe1, 0xb4, 0xff, 0x1e, 0xa0, 0xad, 0xe8, 0x24, 0xf3, 0x38, - 0x51, 0x54, 0x56, 0xa5, 0x7c, 0x7a, 0x91, 0x6a, 0x74, 0x38, 0x8e, 0xe8, 0xf1, 0x28, - 0x1f, 0x9a, 0xde, 0x0a, 0xe2, 0xa2, 0x61, 0x3a, 0x06, 0x12, 0xc4, 0x69, 0xdf, 0x79, - 0x2b, 0x8d, 0xf4, 0xca, 0xe4, 0xfc, 0x25, 0xc1, 0xca, 0xdb, 0xa9, 0x5a, 0x80, 0x7c, - 0xe6, 0x1e, 0x5a, 0x53, 0x03, 0xfa, 0xaf, 0x9e, 0x14, 0x65, 0x39, 0x96, 0xb5, 0xa8, - 0xad, 0xc3, 0x4f, 0xd4, 0x75, 0xef, 0x14, 0x99, 0x09, 0x4b, 0xab, 0xaf, 0x1f, 0x3f, - 0x07, 0xda, 0x9a, 0x39, 0x0b, 0x1d, 0x9f, 0xc9, 0xa0, 0x83, 0x27, 0x98, 0x7a, 0xdf, - 0xe9, 0x56, 0x48, 0x63, 0xfb, 0xdf, 0xa8, 0xf6, 0xb4, 0x6a, 0x88, 0x41, 0x58, 0x30, - 0x99, 0xaf, 0xb7, 0x87, 0x01, 0x18, 0xfa, 0xce, 0x76, 0x34, 0x7e, 0x40, 0xb6, 0xfd, - 0x8c, 0xd1, 0x55, 0x82, 0xae, 0x8e, 0x23, 0xbe, 0x9a, 0x02, 0x19, 0xbc, 0x3e, 0x4e, - 0x45, 0x46, 0xa3, 0x0d, 0x3b, 0xbb, 0xbd, 0x16, 0x86, 0x08, 0x68, 0x76, 0xbe, 0x0e, - 0x4c, 0x85, 0x9b, 0xe7, 0x1f, 0xb5, 0x8f, 0x4f, 0xab, 0x3d, 0x28, 0xc0, 0xb4, 0xf7, - 0xe7, 0x5a, 0xd1, 0xed, 0xb7, 0xf8, 0x89, 0x46, 0xfb, 0x40, 0xcf, 0xa5, 0x78, 0x6a, - 0x0f, 0xcb, 0xa1, 0x30, 0x3c, 0x83, 0x47, 0xec, 0xee, 0x93, 0xd4, 0x6d, 0x14, 0x0b, - 0xb5, 0xf6, 0x95, 0x31, 0xd6, 0x66, 0x54, 0x8b, 0x10, 0x9c, 0xe7, 0x64, 0xbe, 0xad, - 0x7c, 0x87, 0xbd, 0x4c, 0x87, 0x64, 0x94, 0xde, 0x82, 0xdb, 0x6e, 0x50, 0x73, 0xa6, - 0xc9, 0x4f, 0x7c, 0x09, 0x9a, 0x40, 0xd7, 0xa3, 0x1c, 0x4a, 0x04, 0xb6, 0x9c, 0x9f, - 0xcc, 0xf3, 0xc7, 0xdd, 0x56, 0xf5, 0x54, 0x47, 0x76, 0xc5, 0x3b, 0x4d, 0xf7, 0x95, - 0x39, 0x81, 0xd5, 0x5a, 0x96, 0xa6, 0xdc, 0xff, 0x99, 0x04, 0xa9, 0x08, 0x42, 0xe5, - 0xba, 0xfe, 0xc8, 0x84, 0x0c, 0x2d, 0x25, 0x5b, 0xf5, 0xad, 0x61, 0xc4, 0x60, 0xf9, - 0x8f, 0xeb, 0x82, 0xa1, 0x0f, 0xa1, 0xc0, - ], - script_code: Script(vec![0x65, 0x6a, 0x65, 0x51, 0x52, 0x65, 0x63]), - transparent_input: None, - hash_type: 1, - amount: 1712463999734827, - consensus_branch_id: 1537743641, - sighash: [ - 0xbb, 0x10, 0x30, 0x0e, 0x4d, 0xaf, 0xe3, 0x0c, 0x3f, 0xf0, 0x26, 0x34, 0xd0, 0xe0, - 0x03, 0x2f, 0x17, 0x15, 0xb0, 0x0c, 0xbc, 0x77, 0x3d, 0xf6, 0xb0, 0x9e, 0x00, 0x43, - 0x38, 0x6a, 0x14, 0x18, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x13, 0xe5, 0x6c, 0x77, 0x2f, - 0x2c, 0x3b, 0x86, 0x0e, 0xa5, 0xb0, 0x3a, 0x88, 0x54, 0xbc, 0x6e, 0x65, 0x90, 0xd6, - 0x3c, 0xc0, 0xea, 0x54, 0xf1, 0x0b, 0x73, 0xba, 0x24, 0x1b, 0xf7, 0x4b, 0x63, 0x55, - 0x51, 0xa2, 0xaa, 0x06, 0x65, 0x6a, 0xac, 0x52, 0x51, 0x63, 0x36, 0x8b, 0x26, 0xd7, - 0x0a, 0x73, 0x7f, 0x26, 0x76, 0x85, 0x99, 0x8a, 0x3f, 0x7d, 0x26, 0x37, 0x91, 0x49, - 0x09, 0xc7, 0x46, 0x49, 0x5d, 0x24, 0xc4, 0x98, 0x63, 0x5e, 0xf9, 0x7a, 0xc6, 0x6a, - 0x40, 0x08, 0x94, 0xc0, 0x9f, 0x73, 0x48, 0x8e, 0x07, 0x6a, 0x53, 0x65, 0x52, 0x51, - 0x65, 0x52, 0x05, 0xf9, 0x1a, 0xd7, 0x00, 0x79, 0x65, 0xc2, 0x99, 0x36, 0x1d, 0x60, - 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x89, 0xee, 0x09, 0x62, 0xf5, 0x8c, 0x05, 0x1d, 0x11, - 0xd0, 0x55, 0xfc, 0xe2, 0x04, 0xa5, 0x62, 0xde, 0x68, 0x08, 0x8a, 0x1b, 0x26, 0x48, - 0xb8, 0x17, 0x4c, 0xbc, 0xfc, 0x8b, 0x5b, 0x5c, 0xd0, 0x77, 0x11, 0x5a, 0xfd, 0xe1, - 0x84, 0x05, 0x05, 0x4e, 0x5d, 0xa9, 0xa0, 0x43, 0x10, 0x34, 0x2c, 0x5d, 0x3b, 0x52, - 0x6e, 0x0b, 0x02, 0xc5, 0xca, 0x17, 0x22, 0xba, 0xde, 0xee, 0x23, 0xd1, 0x45, 0xe8, - 0xeb, 0x22, 0x13, 0xfc, 0x4a, 0xf1, 0xe4, 0x50, 0xe4, 0xd5, 0x21, 0x7c, 0x66, 0x17, - 0x00, 0x8c, 0x78, 0xf4, 0xfb, 0x11, 0x12, 0xf4, 0x02, 0x8a, 0x70, 0x4f, 0xc5, 0xa9, - 0x38, 0x2c, 0x6b, 0x03, 0xe7, 0xd8, 0x08, 0x5e, 0x90, 0x6c, 0xf8, 0x4c, 0xa2, 0xc1, - 0x20, 0x7c, 0x87, 0xa2, 0xbc, 0xe2, 0x08, 0x0a, 0x98, 0x91, 0x66, 0x8d, 0x69, 0xb0, - 0x44, 0xbe, 0xce, 0xd6, 0xcd, 0xa3, 0x2c, 0x22, 0x9c, 0x91, 0x17, 0x91, 0x7a, 0xa0, - 0x7d, 0xdf, 0xfc, 0xd3, 0x77, 0x39, 0x5c, 0xba, 0x61, 0x6d, 0x63, 0xc0, 0xb6, 0x9c, - 0x01, 0xfc, 0xc4, 0x53, 0x91, 0xfd, 0x5b, 0x87, 0x63, 0xfb, 0x96, 0xd7, 0xca, 0x33, - 0x3a, 0x12, 0xde, 0x3c, 0xef, 0xa9, 0x1c, 0x6c, 0x98, 0xf9, 0x47, 0x3b, 0x8e, 0x10, - 0x4a, 0x71, 0x29, 0x3e, 0x46, 0x37, 0x47, 0x05, 0xba, 0xf6, 0x5f, 0xa4, 0x13, 0x84, - 0xba, 0x5c, 0x8e, 0x0c, 0x88, 0xa3, 0xeb, 0x07, 0xe0, 0xbe, 0x34, 0xda, 0xdd, 0xfa, - 0xbb, 0x7b, 0x65, 0x54, 0x3b, 0x5f, 0x39, 0xcb, 0x20, 0x23, 0xd4, 0x67, 0x89, 0xeb, - 0x7d, 0x98, 0x9a, 0xf7, 0x79, 0xe5, 0xb8, 0xd2, 0x83, 0x85, 0xa8, 0x5b, 0x0d, 0xa2, - 0xab, 0xe0, 0x7f, 0x0c, 0x2b, 0xb4, 0x25, 0x5f, 0xce, 0xa0, 0x31, 0x88, 0x52, 0x7a, - 0x30, 0x7d, 0x40, 0x91, 0x59, 0xe9, 0x01, 0x66, 0xfa, 0xc6, 0xa0, 0x70, 0xba, 0x05, - 0xb3, 0xe4, 0xdb, 0xfd, 0x3a, 0x2b, 0xfc, 0xc9, 0xee, 0x6e, 0xd0, 0x16, 0xc0, 0xf6, - 0x65, 0xbe, 0x81, 0x33, 0xb7, 0xdc, 0x1d, 0x86, 0x04, 0x4d, 0xb0, 0xf9, 0x03, 0x40, - 0xfb, 0x0e, 0x9f, 0x8b, 0xc2, 0xe4, 0xdb, 0x53, 0x82, 0xa8, 0xb4, 0xf8, 0x15, 0xb4, - 0xe8, 0x43, 0x4a, 0xd0, 0xdf, 0xbc, 0x51, 0xa5, 0xe9, 0xb1, 0x45, 0xe1, 0x59, 0x6c, - 0xbf, 0x46, 0x70, 0x03, 0xe0, 0x5d, 0xfd, 0xaf, 0xbb, 0x0c, 0xf3, 0xdd, 0xee, 0x28, - 0xd7, 0x6a, 0x82, 0x42, 0x8e, 0x8a, 0xba, 0x43, 0x64, 0xe8, 0x4b, 0xac, 0x37, 0x92, - 0x98, 0xdf, 0x29, 0x32, 0xe6, 0x9b, 0xb5, 0xd0, 0x0b, 0x51, 0x6e, 0xfc, 0x33, 0xae, - 0x6c, 0xc3, 0x94, 0x7c, 0xeb, 0x09, 0xed, 0x37, 0x16, 0x67, 0x21, 0x2a, 0x83, 0x1b, - 0x54, 0x85, 0xea, 0xfc, 0xe8, 0x48, 0x81, 0x88, 0xea, 0x4e, 0x27, 0xd0, 0xcd, 0xf7, - 0xdd, 0xd3, 0x48, 0xab, 0xff, 0x77, 0x7f, 0x4a, 0x13, 0xbb, 0xc7, 0x16, 0xb6, 0xa5, - 0x94, 0x4e, 0xe7, 0x27, 0x96, 0x56, 0x90, 0xe2, 0x09, 0xb4, 0x9e, 0xb9, 0x62, 0xc0, - 0x39, 0x97, 0x5f, 0x03, 0x9e, 0xd5, 0xc6, 0xe4, 0xc4, 0x00, 0xd8, 0x87, 0x75, 0x94, - 0x33, 0xd3, 0xad, 0x71, 0x6d, 0xa0, 0xcb, 0x44, 0x61, 0x13, 0xc7, 0x72, 0x7a, 0x64, - 0xb5, 0x8c, 0x3f, 0x8a, 0x0f, 0x81, 0x18, 0x9f, 0x02, 0x00, 0x52, 0x33, 0xa8, 0x13, - 0x66, 0xae, 0xe7, 0x3c, 0xec, 0x85, 0x22, 0x8e, 0xbc, 0xfd, 0x5e, 0xe3, 0xc3, 0xfb, - 0x44, 0xdb, 0x76, 0xba, 0x24, 0x3f, 0x28, 0x42, 0xb7, 0xb5, 0xfc, 0x74, 0x6a, 0x03, - 0x1b, 0x0b, 0xc4, 0xbd, 0x4f, 0xc9, 0xfd, 0x83, 0x35, 0x65, 0xea, 0x85, 0x2b, 0x92, - 0xb2, 0x24, 0xf6, 0x99, 0x03, 0x18, 0xad, 0x8c, 0x7d, 0x94, 0x37, 0xe2, 0x0e, 0x2a, - 0x1f, 0x20, 0xe8, 0x18, 0x03, 0x05, 0x7c, 0x5a, 0xba, 0xaa, 0x2e, 0x5c, 0x15, 0xb9, - 0x49, 0x45, 0xcd, 0x42, 0x4c, 0x28, 0xa5, 0xfa, 0x38, 0x5d, 0xad, 0xfe, 0x49, 0x07, - 0xb2, 0x74, 0xd8, 0x42, 0x70, 0x7d, 0xb3, 0x69, 0x7a, 0x02, 0xe6, 0xc8, 0xf5, 0x42, - 0xe5, 0xec, 0xc0, 0x7f, 0xe4, 0x73, 0x50, 0xd1, 0x01, 0x46, 0x70, 0x21, 0x2e, 0xfe, - 0x81, 0xfb, 0x7c, 0x73, 0xe8, 0x45, 0x0d, 0xf8, 0x14, 0xef, 0x62, 0x32, 0xf7, 0x49, - 0x0f, 0x63, 0xcc, 0xf0, 0x74, 0x80, 0xf8, 0x84, 0xa6, 0x6e, 0xaf, 0xfc, 0x28, 0xfe, - 0xa4, 0x48, 0xd7, 0xb4, 0x01, 0xcd, 0xae, 0x10, 0xe7, 0xc0, 0xc7, 0xf9, 0xa7, 0xb1, - 0x53, 0x31, 0x96, 0x9f, 0xc8, 0xcb, 0x36, 0x39, 0x67, 0x73, 0xde, 0x19, 0x19, 0x31, - 0xc7, 0x50, 0xf6, 0xce, 0x5c, 0xaa, 0xf2, 0x97, 0x68, 0xeb, 0xb2, 0x7d, 0xac, 0xc7, - 0x38, 0x05, 0x6a, 0x81, 0x25, 0xb4, 0x77, 0x2b, 0xf8, 0x7a, 0xe1, 0x0a, 0x8a, 0x30, - 0x9b, 0x9b, 0xd6, 0x55, 0x04, 0x3c, 0xfc, 0x31, 0x59, 0x49, 0x43, 0x68, 0xc5, 0xab, - 0x8c, 0xad, 0xb7, 0xf6, 0x71, 0xe9, 0x62, 0x6b, 0xd2, 0x63, 0xe3, 0x11, 0x81, 0xa6, - 0x04, 0xb5, 0x06, 0xa0, 0x3b, 0x43, 0x9a, 0x7f, 0xfe, 0x43, 0x55, 0x89, 0x24, 0x77, - 0xe2, 0xbd, 0xf3, 0x38, 0xc6, 0x2c, 0x39, 0x22, 0xf7, 0xd3, 0xc9, 0xa5, 0x6c, 0x71, - 0x03, 0xd9, 0x11, 0x94, 0x8a, 0x84, 0xb5, 0xae, 0x2d, 0xbb, 0x16, 0xa3, 0x76, 0x1a, - 0xdd, 0x05, 0x3a, 0x0f, 0x96, 0x7e, 0x6b, 0x5b, 0xc9, 0x42, 0x11, 0xb6, 0x54, 0x71, - 0x53, 0x26, 0x7c, 0x6e, 0xe1, 0xca, 0xd0, 0xd9, 0x74, 0xa7, 0x10, 0x88, 0x58, 0x37, - 0x35, 0xe4, 0xf6, 0x3d, 0x33, 0x15, 0x6d, 0xad, 0xd5, 0x4c, 0x2f, 0xaf, 0x89, 0x11, - 0x4a, 0x12, 0x7b, 0x97, 0xb9, 0x4c, 0xc2, 0xa2, 0x2e, 0xf3, 0x03, 0xf4, 0x59, 0xd0, - 0x4f, 0xc0, 0xb5, 0x3a, 0xce, 0x59, 0x18, 0xd4, 0x7f, 0xf3, 0x3a, 0x55, 0x8b, 0xd7, - 0x1a, 0x75, 0xf3, 0x55, 0xfb, 0xd0, 0x6b, 0xbc, 0xcf, 0x4e, 0x02, 0xc3, 0xc0, 0xa4, - 0xb6, 0x3d, 0x0c, 0xc9, 0x49, 0x80, 0x1d, 0x63, 0xa6, 0x4c, 0xb2, 0xd3, 0x23, 0x73, - 0xb2, 0xc7, 0xb2, 0x74, 0xab, 0x2d, 0xb4, 0x68, 0x21, 0x42, 0xc8, 0xb2, 0x1d, 0x84, - 0xc4, 0x81, 0xf5, 0xef, 0x21, 0xe4, 0xb5, 0xe3, 0x60, 0x34, 0x51, 0xbf, 0x94, 0x77, - 0x4d, 0x0e, 0xf4, 0x7f, 0x63, 0xfa, 0x6a, 0xbb, 0x78, 0xd2, 0x1c, 0x19, 0x3c, 0xbe, - 0x65, 0xb6, 0x95, 0xfe, 0x67, 0x42, 0x3c, 0x1e, 0x2d, 0x31, 0x2e, 0x27, 0x76, 0xfa, - 0x24, 0xec, 0xe8, 0x46, 0x83, 0xe7, 0x48, 0x76, 0xc5, 0x5e, 0xa0, 0x36, 0x9e, 0x4e, - 0xa0, 0xe8, 0x64, 0x94, 0xe0, 0x0d, 0xde, 0x23, 0x6a, 0x16, 0x89, 0x73, 0x1f, 0x0a, - 0x5d, 0x82, 0x03, 0xaf, 0xde, 0x5c, 0x42, 0x36, 0x40, 0xb8, 0x1e, 0x4f, 0x63, 0x1c, - 0x98, 0x1c, 0x11, 0xa2, 0xe1, 0xd1, 0x84, 0xc6, 0x7c, 0x52, 0x8d, 0xf9, 0x2d, 0x53, - 0xae, 0xc4, 0x4a, 0x40, 0xa4, 0xea, 0x2a, 0x13, 0x1b, 0x47, 0x33, 0xcf, 0xe4, 0x5c, - 0x6b, 0x00, 0x12, 0xc3, 0xe9, 0xe2, 0x09, 0x75, 0xba, 0xae, 0xcb, 0x02, 0x32, 0xdf, - 0x88, 0x0b, 0xd7, 0xd1, 0xde, 0x13, 0xe1, 0x34, 0x94, 0x62, 0xec, 0x8d, 0x5d, 0xf3, - 0xe7, 0x80, 0xff, 0xa7, 0x2e, 0xba, 0x8a, 0x8d, 0xf7, 0xfc, 0xf3, 0x98, 0xec, 0x23, - 0x05, 0x13, 0xca, 0x9d, 0x61, 0x23, 0xf8, 0xb9, 0xd8, 0x17, 0x85, 0x60, 0xda, 0xf9, - 0x75, 0x11, 0x19, 0x55, 0xa2, 0xbc, 0xa3, 0x42, 0x3e, 0xee, 0xfc, 0x52, 0x7b, 0xe3, - 0xa8, 0x54, 0x3e, 0xb9, 0x0a, 0x5e, 0xc0, 0x2f, 0x35, 0xa7, 0xc6, 0x4b, 0x7d, 0xd5, - 0x9a, 0x72, 0xda, 0x00, 0x74, 0x63, 0x4e, 0x01, 0xd2, 0xab, 0xf3, 0x63, 0x7a, 0xdd, - 0x77, 0xc7, 0x35, 0x0f, 0x12, 0xb0, 0x11, 0xb2, 0x94, 0x16, 0x8e, 0xc7, 0x55, 0x76, - 0xe4, 0x7d, 0x16, 0x9e, 0x39, 0x38, 0xbf, 0x6a, 0xe2, 0xaa, 0x8f, 0xf7, 0xcf, 0xba, - 0x7c, 0xac, 0xb1, 0xf9, 0x2b, 0x6e, 0x4c, 0x24, 0x97, 0xbf, 0xfa, 0x9f, 0x17, 0xca, - 0xd2, 0x42, 0xfa, 0x9c, 0x31, 0x79, 0xc1, 0xa3, 0xaa, 0x81, 0xf7, 0x36, 0x16, 0x49, - 0x57, 0x2c, 0x71, 0x5c, 0x25, 0xa1, 0xf6, 0xcd, 0x5a, 0xce, 0x82, 0xc0, 0x0a, 0xb2, - 0x34, 0x2b, 0x9c, 0x3c, 0xb4, 0xff, 0xfd, 0xda, 0x16, 0x0c, 0xa5, 0xab, 0x9e, 0x9b, - 0xaf, 0x21, 0x39, 0xef, 0x9a, 0xfb, 0xe1, 0xb1, 0xf3, 0x09, 0x46, 0x2a, 0xfc, 0xe4, - 0x62, 0xa7, 0x9b, 0xb9, 0x69, 0x8e, 0x22, 0xc9, 0x57, 0xc5, 0x90, 0xa7, 0x53, 0xa7, - 0x6b, 0x87, 0xe0, 0x09, 0x12, 0x1e, 0x06, 0xf6, 0xa1, 0xbf, 0x62, 0xa0, 0x8b, 0xf4, - 0x35, 0xd9, 0x2e, 0x2f, 0xff, 0xe8, 0x6e, 0x2a, 0x9c, 0xbb, 0xa9, 0x13, 0x3a, 0x68, - 0xe4, 0xae, 0xbf, 0x33, 0xc3, 0x84, 0x36, 0xf2, 0x54, 0x5f, 0xc2, 0xd5, 0x28, 0x32, - 0xd1, 0x65, 0xaf, 0x41, 0x5b, 0x24, 0x4a, 0xdc, 0x5f, 0x57, 0x37, 0x7d, 0xee, 0xdf, - 0x46, 0x0a, 0xa3, 0xbe, 0xb4, 0x34, 0x19, 0xc6, 0xb0, 0x82, 0xe8, 0x35, 0xce, 0x84, - 0xca, 0x13, 0xb6, 0x90, 0x8a, 0x88, 0x13, 0xc0, 0x21, 0xde, 0x9f, 0xa9, 0xa4, 0x4e, - 0x4c, 0x18, 0xdc, 0xb3, 0xd2, 0x1f, 0xaa, 0xbd, 0xb4, 0x19, 0x31, 0xb2, 0xfd, 0x49, - 0x76, 0x44, 0xdc, 0x3a, 0x15, 0x07, 0xfa, 0x5a, 0xc7, 0xc7, 0x6b, 0xee, 0xbb, 0xdb, - 0xd1, 0xd4, 0x92, 0x99, 0xa5, 0x5b, 0xd4, 0x99, 0x27, 0xe9, 0xd7, 0xf4, 0x88, 0x4e, - 0x6e, 0xd3, 0xfd, 0x5e, 0x4b, 0x7c, 0xb8, 0x35, 0xb8, 0x33, 0x08, 0x96, 0x4e, 0x3c, - 0x46, 0x87, 0x3f, 0xd6, 0x13, 0x31, 0x7b, 0x91, 0xd2, 0x92, 0x36, 0xea, 0x90, 0xe3, - 0x65, 0xd1, 0x62, 0xcc, 0x05, 0x1c, 0x84, 0x6d, 0x24, 0x21, 0x76, 0xda, 0xf6, 0xd2, - 0x86, 0x18, 0xae, 0x31, 0xfb, 0xaa, 0xe9, 0x99, 0xa9, 0x3f, 0x17, 0x5c, 0x69, 0x38, - 0xe6, 0x31, 0xa0, 0x81, 0xf2, 0xc1, 0xf3, 0xfd, 0x78, 0x25, 0x49, 0xd3, 0xf3, 0x24, - 0x57, 0x59, 0x60, 0x6d, 0x9f, 0x92, 0xd5, 0x54, 0x8a, 0xcf, 0xea, 0xdb, 0xaf, 0x9c, - 0xaa, 0x6b, 0x93, 0xdc, 0x08, 0x82, 0x8d, 0x74, 0xf6, 0xd5, 0xfd, 0xd8, 0x33, 0x31, - 0xf0, 0x96, 0x91, 0x45, 0x95, 0x52, 0x97, 0xe6, 0x9f, 0x00, 0xfd, 0x29, 0x87, 0xf2, - 0xda, 0x2b, 0x94, 0xb9, 0x95, 0xfe, 0xcb, 0xe6, 0x22, 0xa7, 0x35, 0xef, 0x7f, 0x12, - 0x07, 0xf6, 0x71, 0x62, 0x94, 0x89, 0x20, 0x2b, 0xea, 0x0b, 0x47, 0x5e, 0x51, 0x68, - 0x1a, 0xa1, 0x67, 0x78, 0xb3, 0x9b, 0xd9, 0x23, 0xc9, 0x8d, 0xc6, 0xff, 0x83, 0x73, - 0xc7, 0x9b, 0xb1, 0x70, 0x30, 0x41, 0x7b, 0xc2, 0x00, 0xc8, 0xf0, 0xb8, 0x55, 0xac, - 0xfe, 0xc1, 0x79, 0xf7, 0x67, 0x4c, 0xec, 0x27, 0x21, 0xa1, 0x0f, 0xca, 0x69, 0x3d, - 0x83, 0xcf, 0xe5, 0xb8, 0xcd, 0xcc, 0x18, 0xf8, 0x1a, 0xd6, 0x17, 0xfa, 0x26, 0xf0, - 0xdf, 0xb8, 0x36, 0x55, 0xb8, 0xa2, 0x9a, 0x7f, 0x83, 0x42, 0x32, 0x42, 0x5e, 0x8c, - 0x47, 0x45, 0x88, 0xf1, 0x8d, 0xd3, 0x26, 0xaa, 0x39, 0x6c, 0x3e, 0x47, 0x75, 0xe0, - 0x02, 0x05, 0xfc, 0x9e, 0x45, 0xf7, 0xb7, 0xd2, 0xe6, 0xd5, 0x5d, 0xcb, 0x90, 0xe2, - 0x3f, 0xf6, 0xb5, 0x08, 0x45, 0x9a, 0xa6, 0x99, 0xbf, 0xcb, 0xd5, 0x6f, 0x10, 0x99, - 0x77, 0x64, 0xd0, 0x87, 0x40, 0x89, 0x86, 0xe7, 0x3d, 0x6e, 0x28, 0x4f, 0xea, 0x9a, - 0x23, 0xc3, 0x93, 0x11, 0x78, 0x2f, 0x86, 0xca, 0xbf, 0xf9, 0x45, 0x5e, 0x4c, 0xf6, - 0x99, 0xe5, 0xf5, 0xd4, 0xbc, 0x0b, 0x39, 0x05, 0xa4, 0xe3, 0xbd, 0x01, 0xc5, 0x4d, - 0xf8, 0x64, 0x34, 0x43, 0xbe, 0x0f, 0x88, 0x90, 0x32, 0xea, 0x32, 0x5b, 0xf0, 0x71, - 0x07, 0xfd, 0x41, 0xd6, 0x73, 0xee, 0xba, 0xe6, 0xfa, 0x63, 0x7b, 0x70, 0xcc, 0x0e, - 0xd3, 0xf0, 0x09, 0x58, 0xdf, 0xb8, 0xdc, 0xf0, 0x0e, 0x85, 0xa1, 0xd0, 0xa6, 0xa8, - 0x90, 0x81, 0x40, 0xc2, 0xf4, 0x34, 0xc2, 0xe2, 0x60, 0xef, 0xb0, 0xbc, 0xa2, 0x00, - 0x35, 0x04, 0xc9, 0x99, 0x93, 0xa9, 0xe1, 0xc0, 0xff, 0x9c, 0xef, 0xe6, 0xa6, 0x65, - 0xd7, 0x91, 0x42, 0x86, 0x90, 0xe4, 0x7e, 0xf8, 0xc1, 0x31, 0xa8, 0xe9, 0xbf, 0xb4, - 0xc3, 0x08, 0x02, 0x35, 0x03, 0x2d, 0x73, 0x1b, 0x0d, 0x38, 0x41, 0x22, 0x5f, 0x1c, - 0x11, 0xe2, 0xc2, 0x8e, 0xe8, 0x4d, 0x35, 0xf9, 0x22, 0x61, 0x00, 0x56, 0x59, 0x72, - 0xeb, 0x26, 0x9d, 0x27, 0x8e, 0xf6, 0x49, 0x79, 0xbf, 0x65, 0x15, 0xed, 0x4a, 0x68, - 0x40, 0xb0, 0x88, 0x3a, 0x9e, 0x6e, 0xf6, 0x4a, 0x0e, 0xfc, 0xae, 0x1c, 0xf2, 0x1d, - 0xfe, 0x74, 0x85, 0x4e, 0x84, 0xc2, 0x74, 0x9f, 0xac, 0x03, 0x82, 0x52, 0x75, 0xc9, - 0xb6, 0x30, 0x21, 0x84, 0xc7, 0x2d, 0xf4, 0xc4, 0xbb, 0x28, 0x62, 0xe4, 0xe8, 0xa7, - 0xd9, 0xa4, 0xa2, 0x82, 0x86, 0x6f, 0x9a, 0x7b, 0x2c, 0xfc, 0x9a, 0x56, 0x31, 0x3d, - 0xa0, 0xc4, 0x7a, 0x34, 0xb7, 0xb9, 0xcd, 0xa3, 0xac, 0xe8, 0x18, 0x5f, 0x07, 0xdf, - 0x36, 0xe4, 0x48, 0xa7, 0x6a, 0xa4, 0x77, 0xf2, 0x24, 0xd8, 0x7a, 0x07, 0x4f, 0x43, - 0xaf, 0x5d, 0x5f, 0x79, 0xb3, 0xab, 0x11, 0x28, 0xf0, 0x81, 0x91, 0x44, 0x7f, 0xa6, - 0x46, 0xbf, 0xdd, 0xe5, 0xb5, 0x1e, 0x23, 0x3c, 0xa6, 0x15, 0x5d, 0x10, 0x15, 0x85, - 0xbc, 0x2c, 0x40, 0x15, 0x8a, 0xc2, 0x10, 0x6e, 0x66, 0xa2, 0x6e, 0x46, 0x42, 0x33, - 0x70, 0x63, 0x68, 0x76, 0xb4, 0x34, 0xa7, 0x4f, 0x8c, 0xe8, 0x06, 0x00, 0x50, 0xb0, - 0x82, 0xa7, 0x9b, 0x61, 0xbb, 0x5d, 0x34, 0x4e, 0xb5, 0xa1, 0x15, 0x83, 0x26, 0xce, - 0xd9, 0xa9, 0xd9, 0xf5, 0x4f, 0xb2, 0xfe, 0x8f, 0x9f, 0x05, 0xcd, 0x11, 0x1e, 0xe4, - 0x6c, 0x47, 0x10, 0xf6, 0xf6, 0x3a, 0x62, 0x69, 0x45, 0x57, - ], - script_code: Script(vec![0x53, 0x52, 0x00]), - transparent_input: Some(1), - hash_type: 1, - amount: 1564816348934332, - consensus_branch_id: 1537743641, - sighash: [ - 0x21, 0x46, 0x62, 0xc6, 0x74, 0x50, 0x60, 0x3d, 0x8a, 0xa7, 0x3b, 0xea, 0xbb, 0xf7, - 0x51, 0x8d, 0x03, 0x6c, 0xe9, 0x1d, 0xc8, 0x7b, 0x01, 0x81, 0xe8, 0xa0, 0xf3, 0xfa, - 0x82, 0x2c, 0x7d, 0x8a, - ], - }, - TestVector { - tx: vec![ - 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x01, 0x59, 0x07, 0x92, 0x9a, 0x2f, - 0x3f, 0xdb, 0x0d, 0x8f, 0x79, 0x14, 0xc4, 0x2d, 0xde, 0x2d, 0x20, 0x00, 0xf5, 0xae, - 0x02, 0xd4, 0x18, 0x21, 0xc8, 0xe1, 0xee, 0x01, 0x38, 0xeb, 0xcb, 0x72, 0x8d, 0x7c, - 0x6c, 0x3c, 0x80, 0x02, 0x65, 0x53, 0x75, 0x94, 0xc6, 0x70, 0x00, 0x6f, 0x39, 0x08, - 0x22, 0x2e, 0x89, 0xd1, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x27, 0x1a, 0xbe, 0x66, 0x0e, - 0x39, 0xe0, 0x51, 0xaa, 0xa6, 0xfc, 0xa1, 0x86, 0x22, 0x76, 0xe2, 0xba, 0xa0, 0xfe, - 0x0b, 0x16, 0x2a, 0xeb, 0xcf, 0xe3, 0xd9, 0x34, 0x9c, 0x8d, 0x15, 0x4b, 0xb7, 0xee, - 0x28, 0x21, 0x2c, 0x1b, 0xaa, 0x70, 0x5d, 0x82, 0x07, 0x0d, 0x70, 0x32, 0xf2, 0x69, - 0x5d, 0x17, 0x96, 0x80, 0x9f, 0xab, 0x41, 0x24, 0x69, 0x26, 0xaf, 0x99, 0x2b, 0x6e, - 0xee, 0x95, 0xa9, 0xa0, 0x6b, 0xc4, 0x56, 0x2c, 0x5f, 0x2f, 0x1b, 0x19, 0x54, 0x95, - 0x00, 0x37, 0x2e, 0x7a, 0xd5, 0x79, 0xa6, 0xd6, 0xd7, 0x8b, 0x33, 0x15, 0x31, 0x30, - 0xfb, 0x44, 0x8f, 0xb7, 0x9e, 0x8a, 0x66, 0x9d, 0xb8, 0xa0, 0xf3, 0x5c, 0xdf, 0x9a, - 0xe5, 0xd3, 0x2d, 0x73, 0x2f, 0xc7, 0x94, 0x18, 0xe2, 0x3b, 0x45, 0x1d, 0xdc, 0x95, - 0xa2, 0x2a, 0xba, 0xbb, 0x05, 0x6e, 0xc6, 0xb5, 0xe8, 0xba, 0x4f, 0x52, 0x4d, 0xfa, - 0xfe, 0x87, 0x52, 0x62, 0xdd, 0x7b, 0xe4, 0x1c, 0xbb, 0xc6, 0x24, 0x20, 0xd4, 0xad, - 0x6d, 0xf5, 0xc9, 0xb7, 0x13, 0x60, 0x4f, 0x65, 0x60, 0x88, 0xa4, 0x48, 0x5e, 0x93, - 0xbe, 0x19, 0x07, 0xd2, 0x7a, 0xc6, 0xec, 0x3c, 0x57, 0x25, 0x9b, 0xd6, 0x98, 0x1d, - 0x42, 0xc1, 0xb7, 0x8a, 0x29, 0xad, 0x96, 0x85, 0xe6, 0x3c, 0x49, 0x4d, 0x41, 0x29, - 0x62, 0x3e, 0xa1, 0xa7, 0xff, 0xec, 0x85, 0xfa, 0x29, 0x41, 0x10, 0x73, 0xed, 0xb2, - 0x97, 0x8e, 0xf4, 0xe4, 0x69, 0xdd, 0xd5, 0xcd, 0xa9, 0x86, 0x18, 0x99, 0x95, 0xf8, - 0x8d, 0x6a, 0xb3, 0x66, 0xdb, 0x01, 0x90, 0x01, 0xf5, 0xb2, 0x52, 0x88, 0xcf, 0x86, - 0x0f, 0xd9, 0x98, 0xee, 0x57, 0x3c, 0x8c, 0xc4, 0x8a, 0xa9, 0xef, 0xcf, 0x9b, 0x61, - 0x7e, 0x04, 0x3c, 0x32, 0x9c, 0xd1, 0xaa, 0x1a, 0x0e, 0xd3, 0xa4, 0x02, 0xfb, 0x96, - 0xe3, 0x36, 0xc7, 0x19, 0xe6, 0x25, 0x3c, 0xb6, 0x91, 0xaa, 0x0d, 0xb5, 0x27, 0x36, - 0x62, 0x6e, 0xd1, 0x97, 0x88, 0x75, 0x88, 0x8e, 0xc7, 0x6c, 0x84, 0x6b, 0xc2, 0x27, - 0x27, 0x2a, 0x02, 0x53, 0x17, 0xdf, 0xf0, 0xb1, 0x14, 0x8d, 0x92, 0xd6, 0xf5, 0xfb, - 0x7d, 0x95, 0x33, 0x67, 0x70, 0xa7, 0xd1, 0x6f, 0xac, 0x1a, 0xdd, 0x86, 0x07, 0x76, - 0xcb, 0x48, 0x02, 0x21, 0xf8, 0xfb, 0x33, 0x03, 0xe4, 0xe9, 0xb0, 0x79, 0x02, 0xd2, - 0xff, 0x86, 0xfd, 0xac, 0x72, 0x09, 0x62, 0x34, 0xae, 0xd4, 0x8d, 0xe8, 0x92, 0xff, - 0x73, 0x55, 0x07, 0x3b, 0xbf, 0x06, 0x15, 0xf6, 0x7b, 0x11, 0x00, 0xcc, 0x0a, 0xa3, - 0xba, 0x3d, 0x6c, 0x1a, 0x1a, 0x90, 0x87, 0xb1, 0x19, 0xba, 0xee, 0xbf, 0xa6, 0x2b, - 0xc9, 0xf0, 0xec, 0x47, 0x9d, 0x99, 0xc1, 0xa3, 0xb1, 0x58, 0xb5, 0x14, 0xd1, 0x62, - 0x9d, 0xb3, 0x99, 0x3f, 0x11, 0x67, 0x2a, 0x26, 0x70, 0x8e, 0x5a, 0xd8, 0x16, 0xb5, - 0x47, 0xab, 0x7e, 0x82, 0x7d, 0x07, 0x1b, 0xa7, 0x84, 0x2b, 0x3e, 0x90, 0x30, 0x53, - 0x83, 0x89, 0x6e, 0xc4, 0x90, 0x5f, 0x70, 0x03, 0x8b, 0x69, 0x4e, 0x6a, 0x5a, 0x3e, - 0x43, 0x12, 0xcd, 0x82, 0x08, 0x13, 0x2b, 0x84, 0x0f, 0x05, 0xc7, 0x14, 0x52, 0x3c, - 0xa8, 0x19, 0x72, 0x0a, 0xe2, 0x27, 0xfd, 0x1a, 0xcb, 0xa7, 0x14, 0xfa, 0x03, 0xc4, - 0x5f, 0xc5, 0x39, 0x88, 0x57, 0xb4, 0x0d, 0xc1, 0x48, 0x79, 0x85, 0x6f, 0x35, 0x4b, - 0xa4, 0xd2, 0x58, 0x1d, 0x0c, 0xda, 0x54, 0xb6, 0x38, 0xba, 0x9d, 0x76, 0xf9, 0xb5, - 0x2d, 0x17, 0xc8, 0x02, 0x8e, 0xe6, 0x3f, 0x58, 0x45, 0xb5, 0xdc, 0xef, 0xa4, 0xc3, - 0x47, 0x9b, 0xce, 0x9a, 0xca, 0xd1, 0x8b, 0x4a, 0xea, 0xe0, 0x3c, 0x0e, 0xae, 0x22, - 0x5d, 0x42, 0x84, 0x8b, 0xde, 0xaa, 0x53, 0x6d, 0x03, 0x8d, 0xd3, 0xbc, 0x97, 0x9f, - 0x06, 0x58, 0x66, 0x73, 0xbc, 0x6f, 0xf1, 0xc5, 0xd3, 0xb3, 0x20, 0xf3, 0x49, 0xa5, - 0xb3, 0xa8, 0xb3, 0x55, 0x59, 0x22, 0x96, 0xaa, 0xf6, 0x1c, 0x5b, 0x72, 0x52, 0x03, - 0x3e, 0xc0, 0xa9, 0x46, 0x6a, 0x1b, 0x85, 0x76, 0x4f, 0xb0, 0x83, 0x1b, 0x4a, 0x1a, - 0x36, 0x89, 0x0e, 0x22, 0x4c, 0x01, 0xac, 0xfc, 0xe4, 0x8e, 0xe3, 0xed, 0x93, 0x87, - 0x73, 0x98, 0xe0, 0x72, 0x6d, 0x02, 0x93, 0x6d, 0x0d, 0x03, 0x2e, 0x18, 0xe3, 0x28, - 0x8b, 0x26, 0x70, 0xe1, 0x36, 0x2c, 0x32, 0xd6, 0xe4, 0x73, 0x3b, 0x9d, 0xd2, 0xd5, - 0xf2, 0x6e, 0x1f, 0xe3, 0x06, 0xf7, 0x3c, 0x00, 0x7f, 0xdd, 0xca, 0xe9, 0xd9, 0xc0, - 0xaa, 0xf1, 0x87, 0xd7, 0x42, 0x8b, 0x1e, 0x9d, 0x47, 0x9c, 0x18, 0x23, 0x7b, 0x98, - 0x28, 0xbc, 0xa8, 0xb9, 0x8c, 0x9d, 0x9b, 0xec, 0x7d, 0x82, 0x70, 0xb5, 0xd8, 0xee, - 0xc3, 0xcc, 0x4f, 0x43, 0xfa, 0x01, 0x88, 0x52, 0x1b, 0xc6, 0x1b, 0x21, 0xdd, 0x04, - 0xe3, 0x7a, 0x83, 0xec, 0xe6, 0x8c, 0xa7, 0xa2, 0xfa, 0x6c, 0x8f, 0x9e, 0x34, 0xa6, - 0x29, 0x03, 0x35, 0xaa, 0x1f, 0xbd, 0x83, 0xd5, 0x4a, 0xaf, 0x44, 0x1e, 0x31, 0x9e, - 0xa4, 0x7a, 0x86, 0x2a, 0xd0, 0x29, 0x3c, 0xed, 0xf5, 0xdd, 0x9e, 0xda, 0xde, 0xee, - 0x33, 0xcb, 0x52, 0x2c, 0xd0, 0x11, 0x8b, 0xbd, 0x81, 0x1a, 0xce, 0x9a, 0x23, 0xbd, - 0xa3, 0x9a, 0xba, 0x72, 0xf1, 0x56, 0x6f, 0xc1, 0x68, 0x84, 0x97, 0xd2, 0xa7, 0x92, - 0x8c, 0x36, 0x70, 0x15, 0x25, 0x67, 0x8b, 0xc9, 0x72, 0x14, 0xb3, 0x1b, 0x37, 0xba, - 0xb4, 0x6b, 0x88, 0xf2, 0x7f, 0x04, 0x48, 0xde, 0xcb, 0x31, 0x62, 0x2d, 0x0f, 0x0f, - 0x87, 0xa8, 0x55, 0xba, 0x54, 0x00, 0x03, 0x32, 0x03, 0x1f, 0x73, 0xab, 0xff, 0xd4, - 0x65, 0x91, 0xda, 0x0b, 0x88, 0x72, 0x35, 0x04, 0xed, 0xb2, 0x33, 0x72, 0x30, 0xda, - 0xd2, 0xac, 0xc0, 0xd8, 0xbb, 0x68, 0xbc, 0x83, 0x7a, 0x2f, 0xf9, 0x30, 0xbf, 0xf0, - 0x6f, 0xde, 0x74, 0xeb, 0x90, 0xaa, 0xe4, 0xf6, 0x0d, 0xbb, 0x6e, 0xb8, 0x27, 0xea, - 0x99, 0x88, 0x4a, 0xcd, 0x62, 0x85, 0xa9, 0x88, 0x92, 0x80, 0x2c, 0xf5, 0x9d, 0x5d, - 0x60, 0xd0, 0x16, 0x63, 0x38, 0x7b, 0x3e, 0xd2, 0x72, 0x3b, 0xd6, 0x48, 0x9e, 0x9c, - 0x2c, 0x10, 0x6d, 0x4a, 0xa2, 0xde, 0x23, 0xce, 0xd1, 0x6c, 0x72, 0x04, 0x29, 0xc7, - 0x75, 0x3a, 0x77, 0x38, 0xec, 0x7d, 0x9d, 0xb8, 0x62, 0x42, 0x29, 0xed, 0xd2, 0x17, - 0xb8, 0x0d, 0x74, 0x87, 0x5a, 0x14, 0xca, 0xe4, 0x86, 0x3f, 0x13, 0x9e, 0x9c, 0x0b, - 0x13, 0x1b, 0x2a, 0x4c, 0x28, 0x07, 0x1a, 0x38, 0xec, 0x61, 0xf6, 0x68, 0x01, 0xaa, - 0x59, 0x56, 0xfc, 0xb2, 0xa4, 0x6b, 0x95, 0x87, 0x66, 0x5b, 0x75, 0x71, 0xaa, 0x03, - 0x48, 0x1f, 0xd8, 0xd9, 0xd5, 0x69, 0x8f, 0x83, 0x6f, 0xc8, 0x63, 0x5e, 0x69, 0xe3, - 0xbd, 0xe4, 0x2f, 0x4a, 0xc0, 0x71, 0x32, 0x8b, 0x54, 0x09, 0xf6, 0xe4, 0x2d, 0x79, - 0x0a, 0xed, 0xd7, 0x3b, 0xc1, 0xa2, 0x35, 0x47, 0x23, 0xb3, 0xb8, 0x19, 0xd0, 0x63, - 0x7a, 0x6f, 0xa4, 0x66, 0x39, 0x46, 0xa3, 0x0a, 0xc5, 0xaf, 0xdd, 0x30, 0xce, 0x83, - 0x0f, 0x67, 0x91, 0xb4, 0x57, 0x52, 0x70, 0xa1, 0x72, 0x0f, 0x91, 0x86, 0x6e, 0x2b, - 0x86, 0xf4, 0x78, 0x88, 0x94, 0xc8, 0xda, 0x62, 0xd8, 0xb9, 0x1f, 0xaf, 0x52, 0x0e, - 0x3b, 0xed, 0xbc, 0x12, 0x06, 0xa5, 0xa5, 0xe6, 0xef, 0xd3, 0xdf, 0xde, 0x08, 0x43, - 0xc3, 0xb0, 0x67, 0x57, 0x64, 0x3f, 0xc0, 0x06, 0x00, 0x88, 0x38, 0xca, 0x47, 0x30, - 0x87, 0xf8, 0x97, 0x79, 0x18, 0xcc, 0x1b, 0x81, 0xc9, 0xe6, 0x8e, 0x3b, 0x88, 0x8f, - 0xe6, 0xf7, 0xc6, 0x30, 0xf1, 0xbc, 0x7a, 0xe1, 0x88, 0xf5, 0x12, 0x84, 0x20, 0x41, - 0xca, 0xda, 0x1e, 0x05, 0xf8, 0x66, 0xd2, 0x56, 0x2d, 0xbe, 0x09, 0xc4, 0xb4, 0x30, - 0x68, 0xf7, 0x54, 0xda, 0xd3, 0x4d, 0xf0, 0xfc, 0xfc, 0x18, 0x1f, 0x31, 0x80, 0x1a, - 0x79, 0x92, 0xd2, 0xf1, 0x6b, 0xe0, 0x21, 0x1b, 0x4a, 0x22, 0xf6, 0x2a, 0xab, 0x64, - 0x70, 0x1b, 0xf4, 0xa4, 0xe6, 0xd6, 0x66, 0xfc, 0x30, 0x4a, 0x5c, 0x79, 0xc6, 0x09, - 0xac, 0xc4, 0x3b, 0x00, 0xb4, 0x86, 0x48, 0x93, 0xd3, 0x7d, 0x50, 0x07, 0xf0, 0xc3, - 0x29, 0xa4, 0x75, 0x50, 0x52, 0x57, 0x75, 0x70, 0xdd, 0x38, 0xfa, 0xc0, 0x43, 0xcd, - 0x91, 0xc1, 0x2e, 0xe3, 0x4e, 0x9c, 0xfa, 0xe3, 0x92, 0xa7, 0x8b, 0xda, 0xbd, 0x4e, - 0xe3, 0x1d, 0xc0, 0xde, 0xb0, 0x2f, 0xe7, 0xb1, 0xd8, 0xb0, 0x17, 0x8a, 0xc9, 0x51, - 0x31, 0x05, 0xfc, 0xc7, 0xe3, 0x0b, 0xa8, 0xe0, 0x16, 0xaa, 0x36, 0xa6, 0xb5, 0xdf, - 0x5e, 0x5a, 0x19, 0x09, 0xf6, 0x3a, 0xba, 0x09, 0x5d, 0x98, 0x77, 0xa8, 0xf2, 0xdc, - 0x53, 0xf4, 0x6f, 0x6c, 0x9b, 0x07, 0xad, 0xdf, 0x14, 0x6f, 0x4f, 0xfa, 0x50, 0x1f, - 0x9d, 0xd3, 0xcf, 0xf9, 0x24, 0xe3, 0x01, 0x0f, 0xaf, 0x50, 0x4e, 0x2b, 0x8a, 0xca, - 0x73, 0x57, 0xac, 0xbf, 0xfe, 0xc7, 0x3a, 0xc3, 0x4c, 0x1a, 0x73, 0x16, 0x0f, 0x2c, - 0xea, 0x1e, 0x05, 0x10, 0xf8, 0x4d, 0x2f, 0xe2, 0xf7, 0x3b, 0x6e, 0x92, 0x19, 0x07, - 0xa1, 0xb7, 0xb3, 0x75, 0x12, 0x13, 0x24, 0x1b, 0x2c, 0xfa, 0xa5, 0x5a, 0x5e, 0xa4, - 0xdd, 0x51, 0x7e, 0x7b, 0x49, 0xd2, 0xde, 0x8c, 0x09, 0x08, 0x43, 0x73, 0x0d, 0x24, - 0x08, 0xa2, 0xa3, 0x04, 0xaa, 0x1e, 0x2e, 0x13, 0x70, 0xa6, 0xbf, 0x6c, 0x2b, 0xc7, - 0x3f, 0xf0, 0x0d, 0x89, 0x3b, 0xc1, 0x28, 0x5e, 0xfc, 0xa8, 0x25, 0x99, 0xd1, 0x81, - 0xf1, 0x23, 0x51, 0xf9, 0x39, 0xa9, 0x4e, 0xa8, 0xb9, 0x75, 0xc0, 0x65, 0xa9, 0x1f, - 0xf2, 0x57, 0xca, 0xc7, 0xa9, 0x23, 0x85, 0xfc, 0x8f, 0xa9, 0x21, 0xb1, 0x06, 0xba, - 0x86, 0x60, 0xc6, 0x0a, 0xc8, 0xba, 0x5e, 0xce, 0x45, 0x60, 0x6f, 0x04, 0xf3, 0x6a, - 0x3a, 0x90, 0xbb, 0x38, 0x38, 0xc4, 0x2a, 0xbf, 0x62, 0xdd, 0x2d, 0x84, 0xba, 0xbe, - 0xf3, 0xe1, 0x88, 0xe9, 0x17, 0x1a, 0xff, 0x9b, 0xc1, 0x16, 0x66, 0x90, 0x09, 0xd8, - 0x87, 0x13, 0x0a, 0xc9, 0xf7, 0x39, 0x6a, 0x62, 0x7a, 0x84, 0x74, 0xc1, 0x81, 0x1b, - 0x69, 0x6f, 0x99, 0x55, 0x2b, 0x14, 0xc4, 0x84, 0xdf, 0xe4, 0x2c, 0x24, 0xd5, 0x7c, - 0x3a, 0x9c, 0x3f, 0xea, 0x13, 0x76, 0xcd, 0xcb, 0x63, 0x42, 0x1c, 0x31, 0x4a, 0x62, - 0x2a, 0x9a, 0xef, 0x0b, 0xc0, 0x57, 0xcb, 0x11, 0xbc, 0x5e, 0x30, 0x66, 0xe3, 0x3a, - 0x3b, 0x9b, 0x31, 0xdf, 0x25, 0x75, 0xcd, 0x51, 0x85, 0xa4, 0xf3, 0xfc, 0x4e, 0x4c, - 0x3d, 0x40, 0x2e, 0xd4, 0x20, 0x46, 0xf8, 0x1f, 0x97, 0x48, 0x16, 0xd2, 0x79, 0xb1, - 0x51, 0x3a, 0xb8, 0x1d, 0x3f, 0x0a, 0x3c, 0x7f, 0x7f, 0xcf, 0x2f, 0xbb, 0x4e, 0x26, - 0x32, 0x19, 0x93, 0xa5, 0x13, 0xad, 0x3d, 0x7f, 0x4a, 0xfe, 0x6c, 0x1b, 0xbd, 0xc6, - 0x57, 0x58, 0x50, 0x80, 0xbb, 0x5a, 0x0f, 0x25, 0x97, 0x3d, 0x63, 0xeb, 0x20, 0xad, - 0xa0, 0x16, 0x6b, 0xbd, 0x8a, 0x39, 0xff, 0x93, 0x24, 0x6f, 0x27, 0x89, 0x73, 0x2a, - 0xd0, 0x55, 0x87, 0xf8, 0xdb, 0x7b, 0xc8, 0x7c, 0x24, 0x2c, 0xfd, 0x36, 0xce, 0x68, - 0x5a, 0x4b, 0x65, 0x69, 0x86, 0xc3, 0x9f, 0xd7, 0xfc, 0xb2, 0x3c, 0x91, 0x91, 0x3e, - 0x46, 0x11, 0x19, 0x1e, 0xdc, 0xc8, 0x8b, 0x78, 0xf1, 0x45, 0xea, 0x29, 0xd2, 0x71, - 0xb9, 0x40, 0xc6, 0x99, 0x41, 0xe4, 0xc3, 0xfd, 0x2d, 0x71, 0xf3, 0xb1, 0x90, 0x69, - 0x0e, 0xe1, 0x6f, 0x5d, 0x14, 0xac, 0x22, 0x24, 0xe6, 0xfc, 0x89, 0x59, 0x76, 0x54, - 0x52, 0x7d, 0xab, 0xe7, 0x2e, 0x75, 0xd2, 0xd2, 0xa1, 0x3a, 0x9f, 0xba, 0xa6, 0x37, - 0x8e, 0x8a, 0x26, 0x43, 0x21, 0x08, 0x7a, 0x19, 0x00, 0xef, 0xe3, 0xca, 0xd1, 0x4a, - 0x57, 0x96, 0x86, 0xaa, 0x36, 0x36, 0xbd, 0x37, 0x5b, 0xd3, 0x13, 0x6b, 0xee, 0x0b, - 0xda, 0xab, 0xcf, 0xac, 0x88, 0x1b, 0xc7, 0x01, 0x81, 0x27, 0x21, 0xe6, 0xfb, 0x75, - 0xaa, 0x07, 0x2d, 0x2d, 0x18, 0x7e, 0x62, 0x25, 0x8d, 0x65, 0xa1, 0x92, 0x15, 0x7c, - 0xdf, 0x2e, 0xc3, 0x21, 0x40, 0x7f, 0x68, 0x2f, 0x5e, 0xec, 0x6a, 0x32, 0x97, 0xab, - 0x20, 0xb7, 0x06, 0x1c, 0x62, 0x24, 0x57, 0x16, 0xa4, 0x4f, 0x71, 0xfb, 0xfc, 0x34, - 0xc7, 0x9b, 0x44, 0xe0, 0x9e, 0x42, 0x12, 0xac, 0x26, 0x53, 0xf6, 0xc4, 0x03, 0x64, - 0x3e, 0x1c, 0x5b, 0x9a, 0xd1, 0x34, 0xd8, 0x9c, 0x68, 0x0b, 0x70, 0x72, 0x83, 0xaf, - 0x54, 0x32, 0x6f, 0xc4, 0xf8, 0x4d, 0x6a, 0x58, 0x29, 0xa0, 0xad, 0x48, 0x30, 0x80, - 0x6c, 0x05, 0x75, 0x84, 0x92, 0xcd, 0x6a, 0xc4, 0x6b, 0xa0, 0x1a, 0x2b, 0x37, 0x22, - 0xb5, 0xe4, 0xcd, 0xaf, 0xbb, 0x3f, 0x36, 0x78, 0x5f, 0x42, 0x4a, 0xf0, 0x44, 0xda, - 0xc5, 0xdb, 0x5f, 0x7d, 0xf8, 0x39, 0xeb, 0x63, 0xc0, 0xc1, 0x7d, 0x8b, 0x0c, 0x79, - 0xdb, 0x86, 0x30, 0x94, 0x20, 0x15, 0xbe, 0x13, 0xf7, 0x9a, 0xf6, 0xf4, 0x3e, 0x5a, - 0xb0, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x14, 0x79, 0x8f, 0x44, 0x22, 0x58, 0xee, 0xdc, 0x43, - 0x6f, 0xcc, 0x38, 0x6b, 0x36, 0xb5, 0x7e, 0x19, 0x17, 0xd7, 0x20, 0x17, 0x73, 0x66, - 0xf4, 0x24, 0xb0, 0xa5, 0x4b, 0x0b, 0x60, 0xf4, 0xfb, 0x13, 0x58, 0xc2, 0x0a, 0xa4, - 0x1d, 0xc5, 0x02, 0xe1, 0xdd, 0x8a, 0x16, 0x33, 0xf3, 0xd8, 0xe3, 0x27, 0x6b, 0x59, - 0xe7, 0xd2, 0xc4, 0xe6, 0x24, 0xa6, 0xf5, 0x36, 0x95, 0xbc, 0xaf, 0x24, 0x7e, 0x36, - 0x48, 0x3f, 0x13, 0xb2, 0x04, 0x42, 0x22, 0x37, 0xfc, 0x6a, 0xb3, 0xeb, 0xa0, 0x2f, - 0xc4, 0x14, 0x2b, 0x42, 0x97, 0xeb, 0xb5, 0x68, 0x3d, 0xb8, 0xd2, 0x43, 0x19, 0x70, - 0x6a, 0xd2, 0x6a, 0xaf, 0xd8, 0x1c, 0x53, 0xb7, 0x40, 0xf3, 0x45, 0x43, 0xa6, 0xb3, - 0xe9, 0xf5, 0xbb, 0x7d, 0x5c, 0x49, 0xe8, 0xc3, 0x7f, 0x61, 0x49, 0x21, 0x25, 0x4f, - 0x32, 0x12, 0x39, 0x4c, 0x79, 0x7d, 0x1c, 0xee, 0x78, 0x99, 0xb7, 0xb4, 0xb6, 0x5b, - 0x59, 0xb7, 0x34, 0x2f, 0x92, 0x53, 0x1c, 0x1d, 0x59, 0xe1, 0x79, 0x70, 0xb7, 0x31, - 0x74, 0x14, 0x43, 0x8c, 0xd8, 0x0b, 0xd0, 0xf9, 0xa6, 0x7c, 0x9b, 0x9e, 0x55, 0x2f, - 0x01, 0x3c, 0x11, 0x5a, 0x95, 0x4f, 0x35, 0xe0, 0x61, 0x6c, 0x68, 0xd4, 0x31, 0x63, - 0xd3, 0x34, 0xda, 0xc3, 0x82, 0x70, 0x33, 0xe5, 0xad, 0x84, 0x88, 0xbf, 0xd9, 0xc4, - 0xbb, 0xbe, 0x8f, 0x59, 0x35, 0xc6, 0xc5, 0xea, 0x04, 0xc3, 0xad, 0x49, 0xc7, 0x47, - 0xa9, 0xe7, 0x23, 0x1b, 0xcd, 0x7d, 0x16, 0x21, 0x5e, 0x6e, 0x80, 0x73, 0x7d, 0x6b, - 0x54, 0xfe, 0xc8, 0xb8, 0x84, 0x02, 0xf0, 0x47, 0x52, 0x45, 0xe1, 0x74, 0xa7, 0x45, - 0xb8, 0x31, 0xf8, 0xfe, 0x03, 0xa7, 0x6f, 0xb9, 0xce, 0xca, 0x4d, 0x22, 0xb7, 0x83, - 0xc3, 0x28, 0xc6, 0x91, 0x5c, 0x43, 0x40, 0x50, 0x64, 0xae, 0x56, 0xbc, 0x89, 0xe6, - 0x4d, 0x15, 0x78, 0xe4, 0xd3, 0xa3, 0x4b, 0xb9, 0x55, 0x91, 0xea, 0xf1, 0xd3, 0xda, - 0x02, 0xa4, 0x54, 0x9f, 0xa8, 0x0d, 0xb0, 0xff, 0x7c, 0xb0, 0x39, 0x93, 0x02, 0x8a, - 0xe1, 0x5a, 0x30, 0xe8, 0x79, 0x49, 0xaa, 0x08, 0x0e, 0x94, 0xab, 0xde, 0x68, 0x89, - 0x8c, 0x33, 0x92, 0xa2, 0x17, 0xd6, 0x49, 0x61, 0x6b, 0xbe, 0x73, 0x9b, 0x13, 0xd1, - 0x4d, 0xf0, 0x3f, 0x02, 0x76, 0x71, 0x48, 0x9b, 0xe0, 0xb4, 0xbe, 0xba, 0xaf, 0xa7, - 0xd1, 0xe6, 0x39, 0xd5, 0xb3, 0xe9, 0x94, 0xff, 0xb6, 0xb7, 0xa2, 0x09, 0xf6, 0xad, - 0xfe, 0x8d, 0x1e, 0x5c, 0xcf, 0x01, 0x0c, 0x19, 0x0a, 0x8a, 0xeb, 0x18, 0xaa, 0x9d, - 0x68, 0x7e, 0x24, 0xad, 0xc0, 0xb1, 0x13, 0x5c, 0x70, 0xc9, 0x70, 0xe0, 0x90, 0x3a, - 0xf6, 0xe1, 0x70, 0x81, 0xd5, 0x81, 0x8e, 0x88, 0xb1, 0x4e, 0x4f, 0x60, 0x1b, 0x8c, - 0x06, 0x3e, 0x3f, 0x43, 0x87, 0xff, 0xa2, 0x32, 0x2a, 0x51, 0x81, 0x90, 0x9f, 0x09, - 0x80, 0xd6, 0x89, 0xde, 0x7f, 0x8e, 0x6a, 0x5c, 0x62, 0xa7, 0x77, 0xd1, 0x75, 0x00, - 0x2a, 0x13, 0x7d, 0x02, 0x5b, 0x88, 0x88, 0x92, 0x91, 0x98, 0x11, 0x7a, 0xa5, 0xd6, - 0x19, 0x93, 0xe1, 0xdc, 0xf7, 0x58, 0x76, 0xdc, 0xa6, 0x09, 0xf9, 0xd2, 0x84, 0x71, - 0xf9, 0x97, 0xfa, 0x11, 0xf9, 0x9d, 0x42, 0x3f, 0x02, 0xf1, 0x73, 0x4b, 0xe8, 0xa5, - 0xff, 0x99, 0x7d, 0x45, 0x1e, 0xb3, 0xcf, 0x4b, 0x3d, 0xfd, 0xd9, 0xd4, 0x54, 0x5c, - 0x35, 0xb2, 0xb5, 0xa7, 0xdc, 0x17, 0xa8, 0x36, 0xb1, 0x2b, 0x43, 0xbe, 0xfc, 0x03, - 0xe0, 0xa1, 0xbd, 0x36, 0x97, 0x72, 0x33, 0x80, 0x78, 0xb4, 0xff, 0x7d, 0x8e, 0x2d, - 0x97, 0x9a, 0x34, 0x41, 0xe1, 0xc8, 0xf5, 0xaf, 0xe4, 0x7b, 0x1e, 0x7d, 0xa5, 0x6c, - 0xf0, 0x06, 0x02, 0xd0, 0x03, 0x11, 0x0c, 0x05, 0xcf, 0x48, 0xfd, 0xa3, 0xe6, 0xcc, - 0xe3, 0x2a, 0x04, 0x40, 0x00, 0xf4, 0x5c, 0x6d, 0x1e, 0x69, 0x6d, 0x24, 0x5c, 0xbd, - 0x31, 0x2b, 0xdc, 0x3a, 0x3a, 0x21, 0xc9, 0x92, 0xd0, 0x03, 0xc8, 0xcc, 0x8f, 0xa6, - 0x30, 0x6d, 0x7e, 0x13, 0x0a, 0x2b, 0xa4, 0x20, 0x18, 0xfe, 0x59, 0x69, 0x49, 0xfd, - 0x82, 0x26, 0x7b, 0xcc, 0x59, 0xdd, 0x46, 0x26, 0xef, 0xc3, 0xea, 0x74, 0x38, 0xd0, - 0x5c, 0x91, 0xb0, 0xf8, 0xe0, 0x92, 0x55, 0x0d, 0x2d, 0x39, 0xa0, 0x1e, 0xb4, 0x5e, - 0xe8, 0xf7, 0xd0, 0x9b, 0x03, 0x8d, 0x83, 0x83, 0xe1, 0x9b, 0xc3, 0x0e, 0x64, 0x03, - 0x82, 0x8c, 0xdb, 0x65, 0x2a, 0x55, 0x6b, 0x12, 0x04, 0x09, 0x31, 0x40, 0x2a, 0xa6, - 0xac, 0x34, 0xfc, 0x19, 0xfd, 0xc0, 0x6e, 0x2e, 0x77, 0x87, 0xf5, 0xb7, 0x7b, 0x04, - 0x5f, 0xd0, 0x98, 0xc0, 0x31, 0xbd, 0xbd, 0x46, 0x27, 0x76, 0x09, 0xd8, 0x42, 0xf4, - 0x84, 0x24, 0xed, 0xa3, 0x1e, 0x3c, 0xf2, 0xcd, 0xd6, 0x43, 0x85, 0xba, 0xd3, 0x11, - 0x88, 0x58, 0xd1, 0x42, 0xd9, 0x06, 0xea, 0xdb, 0x75, 0x90, 0xc9, 0x41, 0x36, 0xda, - 0x6a, 0x06, 0x35, 0x14, 0xd6, 0xa2, 0x5f, 0x7b, 0x37, 0xd7, 0x66, 0x4f, 0x9b, 0x97, - 0x09, 0x43, 0x3e, 0x6e, 0x70, 0x21, 0x18, 0xa4, 0xab, 0x9e, 0x7a, 0x7a, 0x3e, 0x62, - 0x59, 0x12, 0x99, 0x37, 0xd2, 0x9d, 0x0d, 0xb2, 0x60, 0x70, 0x52, 0x3e, 0x8b, 0x06, - 0x43, 0x13, 0x0a, 0xbe, 0xfe, 0x94, 0x3b, 0x40, 0x12, 0x98, 0xae, 0x01, 0xa3, 0xab, - 0x00, 0xab, 0xbc, 0x60, 0xd7, 0xdb, 0x93, 0x3c, 0x7f, 0x07, 0xa8, 0xbf, 0x0f, 0x7c, - 0xe1, 0x66, 0x0b, 0xcc, 0xb4, 0x5e, 0x04, 0x2b, 0x45, 0x1b, 0x93, 0x50, 0x02, 0xce, - 0xce, 0x27, 0xf3, 0x6a, 0xba, 0x56, 0x47, 0xac, 0x28, 0xd8, 0x18, 0x6c, 0xdd, 0x1f, - 0xb9, 0x5d, 0xc1, 0x35, 0xd4, 0x89, 0x92, 0xf6, 0x8d, 0xa1, 0x2a, 0xd6, 0x1a, 0xc7, - 0x56, 0x68, 0x0d, 0xd7, 0xf8, 0xd0, 0x77, 0x4a, 0xbd, 0x6c, 0xfd, 0xa2, 0xf0, 0x32, - 0xaf, 0x3b, 0xe1, 0x39, 0xa6, 0x33, 0xd6, 0x73, 0x3c, 0x75, 0xd1, 0xab, 0xa8, 0x90, - 0x18, 0xc8, 0x57, 0x2b, 0x99, 0xcd, 0x30, 0xc5, 0x37, 0x06, 0x79, 0x41, 0xdf, 0x1c, - 0x4b, 0xc1, 0xfd, 0x57, 0x0f, 0x7b, 0x4d, 0xdc, 0x97, 0x51, 0x86, 0x23, 0xe3, 0xae, - 0x4a, 0x87, 0xbd, 0xb9, 0x66, 0xc9, 0x4d, 0x86, 0x1e, 0x80, 0xde, 0x88, 0xc2, 0x92, - 0xae, 0xe9, 0x38, 0x71, 0x94, 0xe2, 0x56, 0xc6, 0x70, 0x07, 0x52, 0x30, 0x1c, 0x73, - 0xfc, 0x95, 0x65, 0xa4, 0x04, 0x80, 0xd8, 0x12, 0x6e, 0x9d, 0x08, 0x58, 0x79, 0xe2, - 0x4b, 0x16, 0xe9, 0xc4, 0x85, 0xd8, 0xf0, 0xd6, 0x18, 0xca, 0x0d, 0xd1, 0x21, 0xb5, - 0x1a, 0x7c, 0xab, 0x23, 0x0c, 0x5b, 0x45, 0x67, 0x2b, 0xdb, 0x8e, 0xa3, 0xa0, 0x40, - 0xf7, 0xaa, 0xa0, 0x98, 0xba, 0x26, 0x02, 0x5d, 0x2e, 0xab, 0x79, 0x48, 0x69, 0x3d, - 0xd5, 0xf6, 0xd3, 0x09, 0x65, 0x01, 0xe9, 0xe0, 0x71, 0x25, 0xd7, 0xeb, 0x29, 0x3b, - 0x3a, 0xba, 0xd5, 0x7f, 0xd5, 0xf0, 0x11, 0x64, 0x70, 0x2d, 0xae, 0x64, 0xbd, 0xba, - 0x8c, 0x92, 0x4f, 0xb0, 0x79, 0x96, 0x79, 0xd7, 0x7f, 0x98, 0xd3, 0x03, 0x91, 0x9f, - 0xb4, 0xa7, 0xff, 0x26, 0xa9, 0x6f, 0x13, 0x7a, 0x5e, 0x5c, 0xb9, 0x5b, 0xc4, 0xc6, - 0xff, 0x99, 0x93, 0x52, 0x6b, 0xda, 0x15, 0x03, 0x16, 0x8a, 0xb4, 0x8c, 0xbd, 0x45, - 0x15, 0x39, 0x27, 0xd3, 0x04, 0x30, 0x42, 0x3d, 0xbd, 0xf0, 0x66, 0x05, 0xf5, 0xb5, - 0x4b, 0x80, 0x8f, 0xeb, 0x22, 0xb2, 0x08, 0xb0, 0x64, 0x58, 0x18, 0x47, 0xb2, 0xf6, - 0x4c, 0xa6, 0x48, 0x37, 0x00, 0x72, 0x16, 0xde, 0x6e, 0xca, 0xff, 0xeb, 0x4b, 0x69, - 0xe6, 0x33, 0x47, 0xf8, 0x4a, 0xbc, 0xad, 0x8f, 0x2e, 0x75, 0x7d, 0x58, 0x61, 0xce, - 0x77, 0xee, 0x46, 0x51, 0x3d, 0xa7, 0x41, 0x68, 0x37, 0xdc, 0xb2, 0x3d, 0x33, 0xea, - 0x72, 0xaf, 0x23, 0xd0, 0xad, 0x8c, 0x93, 0x07, 0xd0, 0xb5, 0x85, 0x8d, 0xa9, 0x5b, - 0x77, 0xff, 0xf9, 0x02, 0x7b, 0x88, 0x59, 0xe1, 0x1d, 0xcb, 0xd5, 0x98, 0x35, 0x0e, - 0xee, 0x50, 0x93, 0x94, 0x81, 0x70, 0x8e, 0xa7, 0x08, 0xeb, 0x9f, 0x66, 0x43, 0x88, - 0xb9, 0xc6, 0x4d, 0x6a, 0xf0, 0xf9, 0x66, 0x90, 0x34, 0x24, 0x00, 0x34, 0x8e, 0x92, - 0x9e, 0x07, 0x46, 0x02, 0x53, 0xf3, 0x83, 0x90, 0xf8, 0x7b, 0xd6, 0xc0, 0x53, 0x08, - 0xc3, 0xbd, 0xe2, 0x52, 0x28, 0xe0, 0xfa, 0x08, 0x80, 0xb0, 0x8e, 0xf3, 0x4a, 0x5a, - 0x9c, 0xc0, 0xea, 0x0a, 0x67, 0xca, 0x65, 0xb6, 0xff, 0xd0, 0x05, 0x57, 0x29, 0x09, - 0xf1, 0xc4, 0x2d, 0xd7, 0x45, 0xee, 0xee, 0x9d, 0xd6, 0xb4, 0x43, 0x9c, 0x9f, 0x3f, - 0x98, 0xa1, 0x18, 0xfe, 0x16, 0x69, 0x8e, 0x9c, 0xef, 0xf5, 0x58, 0xf1, 0x60, 0x66, - 0x97, 0x5f, 0xe3, 0x95, 0x83, 0xe9, 0xb5, 0x85, 0x3b, 0x13, 0x11, 0x39, 0x15, 0x80, - 0x01, 0x9f, 0xe5, 0x5d, 0x59, 0xd1, 0xc8, 0x28, 0xd3, 0xfe, 0xb6, 0xa3, 0xb9, 0xce, - 0x92, 0xd0, 0x89, 0xae, 0x4b, 0x40, 0x8e, 0x23, 0xd6, 0xa4, 0x37, 0xd4, 0x98, 0x9b, - 0x51, 0x9b, 0x7a, 0x9e, 0xb0, 0x8a, 0xe6, 0xd4, 0x48, 0xa7, 0xa1, 0x6e, 0x8a, 0xed, - 0x26, 0xa2, 0xec, 0xd0, 0xca, 0xd8, 0x08, 0x44, 0xfd, 0x06, 0x50, 0xd8, 0xc4, 0xe4, - 0xd2, 0xaf, 0x90, 0x65, 0x67, 0x48, 0xd8, 0x09, 0x9a, 0x0c, 0x75, 0x6f, 0xc1, 0x6c, - 0xca, 0x06, 0xa3, 0x34, 0x43, 0x07, 0x02, 0xae, 0x19, 0x61, 0x66, 0x5b, 0x48, 0x45, - 0xac, 0xd1, 0xa8, 0xe3, 0x41, 0x01, 0xe6, 0x8b, 0xb6, 0x44, 0xac, 0x03, 0x4d, 0xc6, - 0x3e, 0x6e, 0x34, 0x4c, 0x3d, 0x63, 0x76, 0x2a, 0x7a, 0x5b, 0xf5, 0x9f, 0x13, 0x09, - 0x54, 0x10, 0x98, 0x1d, 0x6b, 0x6b, 0x16, 0xbc, 0xd4, 0xc9, 0xfa, 0x68, 0xaf, 0x6e, - 0x53, 0x65, 0xe9, 0x4e, 0xcb, 0xe7, 0xab, 0x8b, 0x80, 0x43, 0xdf, 0xba, 0xcb, 0x23, - 0xc8, 0x4d, 0x71, 0xa8, 0xfe, 0x5d, 0x9a, 0xc5, 0x50, 0x2c, 0xe9, 0xf7, 0x3f, 0x40, - 0x8e, 0x14, 0x37, 0x6d, 0xb8, 0x6e, 0xf5, 0x7c, 0xc3, 0x7d, 0x09, 0x89, 0x6f, 0xa9, - 0x06, 0x97, 0x2e, 0x55, 0x71, 0x80, 0xa4, 0xab, 0x5a, 0xd0, 0x9d, 0x88, 0x46, 0xdd, - 0x6d, 0xa7, 0x48, 0x76, 0x54, 0x36, 0xe0, 0x16, 0x02, 0x40, 0xf8, 0xd4, 0x1c, 0x0a, - 0xc7, 0x83, 0xf9, 0x39, 0xf2, 0xd0, 0xed, 0x26, 0x2c, 0xe8, 0x59, 0xc1, 0x31, 0xeb, - 0xc9, 0x3f, 0xf2, 0xe6, 0xe4, 0x07, 0xd4, 0xe2, 0x43, 0xe1, 0xe9, 0x31, 0xd5, 0x3a, - 0x45, 0x43, 0xb6, 0xe2, 0x6d, 0x82, 0x59, 0x6f, 0xc5, 0x3b, 0x52, 0x31, 0x2c, 0x77, - 0x6d, 0x12, 0xeb, 0x2b, 0x65, 0x9b, 0x4f, 0xb0, 0x98, 0xdf, 0x87, 0xd6, 0x83, 0xcf, - 0x9e, 0x54, 0x12, 0xee, 0x56, 0xc3, 0xfe, 0x98, 0x41, 0xd7, 0x3f, 0xd0, 0x70, 0xdf, - 0xa5, 0x1f, 0x5b, 0xaf, 0xed, 0xf2, 0x06, 0xf1, 0x3c, 0x52, 0x4e, 0x5c, 0x50, 0xca, - 0xc9, 0x90, 0x6e, 0xfa, 0x39, 0x32, 0x90, 0x04, 0x2e, 0x3b, 0xc5, 0x9f, 0x96, 0x0b, - 0x7d, 0x24, 0x0a, 0xe4, 0x43, 0xfc, 0x49, 0x26, 0x9c, 0xe0, 0x00, 0x61, 0xe6, 0x5c, - 0x6d, 0x74, 0x81, 0x2a, 0x30, 0xdd, 0x5f, 0x5f, 0xe7, 0x4e, 0xff, 0x61, 0xe0, 0xcb, - 0xab, 0x3c, 0xec, 0x75, 0xd0, 0xae, 0xf9, 0x50, 0x83, 0x18, 0x94, 0x52, 0xdd, 0x3d, - 0x9e, 0xdf, 0x44, 0x87, 0xbc, 0x73, 0x4c, 0x8b, 0x24, 0xf2, 0x12, 0x96, 0xe4, 0xe9, - 0xef, 0x11, 0x7d, 0x7f, 0xb9, 0x77, 0xe3, 0xb0, 0xe6, 0x40, 0x6e, 0x63, 0x08, 0x59, - 0x06, 0x33, 0x1a, 0x93, 0x03, 0x3d, 0x1c, 0xb8, 0x36, 0x0f, 0xe6, 0xfe, 0xa6, 0x1a, - 0x68, 0x26, 0xdf, 0x36, 0x25, 0x57, 0x89, 0xf9, 0x2e, 0x40, 0xba, 0xfc, 0xb2, 0xeb, - 0xcb, 0x9e, 0x55, 0x6f, 0x6c, 0x0c, 0xca, 0xdc, 0x6a, 0xf0, 0x8e, 0x31, 0xec, 0x4a, - 0xd5, 0x28, 0x80, 0x34, 0xe1, 0x6d, 0x15, 0x5c, 0xfd, 0xca, 0xda, 0x7b, 0xab, 0x59, - 0x9c, 0x2f, 0xa4, 0xad, 0x2e, 0x62, 0x93, 0xf9, 0xfe, 0x09, 0x71, 0x69, 0x14, 0x82, - 0x76, 0xb6, 0xa9, 0xea, 0xa7, 0x2f, 0x14, 0x8b, 0x0c, 0x95, 0x65, 0xc3, 0xc2, 0xdd, - 0x63, 0x12, 0x5e, 0x0f, 0xa5, 0x30, 0x86, 0x1a, 0x71, 0x0d, 0xf8, 0xe4, 0x81, 0xf2, - 0x71, 0x29, 0x20, 0xf8, 0x78, 0x7e, 0x0a, 0xed, 0xfe, 0x61, 0x8a, 0xff, 0x50, 0xa3, - 0xb5, 0x62, 0x13, 0x88, 0x4d, 0x62, 0x62, 0xc1, 0x1d, 0xeb, 0xf2, 0xba, 0x7e, 0x8a, - 0xd6, 0x69, 0x2c, 0xb1, 0x70, 0x78, 0x33, 0x14, 0x18, 0xda, 0x4b, 0xe0, 0x64, 0xff, - 0x52, 0x70, 0x07, 0x39, 0x34, 0xab, 0xcd, 0x2a, 0xb0, 0x46, 0x9e, 0xca, 0xf7, 0x27, - 0x5b, 0x4b, 0xd7, 0x2b, 0xc6, 0xed, 0x34, 0x47, 0x8e, 0xa4, 0x08, 0x9b, 0x73, 0x6a, - 0x16, 0xdd, 0x90, 0x6d, 0x49, 0xf2, 0x5c, 0x33, 0x82, 0x7c, 0x57, 0x1c, 0xe0, 0xb5, - 0xd7, 0x21, 0x77, 0xaa, 0x35, 0x08, 0x80, 0x4b, 0xc0, 0xf8, 0xfa, 0xa9, 0x47, 0x12, - 0x22, 0x31, 0x40, 0x2d, 0x2f, 0x5c, 0xc9, 0xa0, 0xeb, 0x0e, 0x09, 0xd4, 0x27, 0xb4, - 0x27, 0x28, 0x8d, 0x93, 0x7d, 0x9d, 0x72, 0xb7, 0x74, 0x56, 0xf8, 0x86, 0x59, 0x4c, - 0xd8, 0xc6, 0xa4, 0x62, 0xf7, 0x7f, 0xd8, 0x30, 0x76, 0x46, 0x9c, 0xc0, 0xec, 0xba, - 0x3c, 0xc4, 0x0c, 0xad, 0x69, 0xe5, 0xb5, 0x41, 0x12, 0xea, 0xb3, 0x33, 0x96, 0xae, - 0xcf, 0xbc, 0x21, 0x1f, 0x1f, 0x79, 0xcf, 0x33, 0x10, 0x8e, 0x93, 0xd9, 0x53, 0x78, - 0xba, 0xe6, 0x95, 0x82, 0x74, 0xb3, 0x10, 0x88, 0xfb, 0xd8, 0xb3, 0xa3, 0xa0, 0xd1, - 0x54, 0xa7, 0x89, 0x73, 0x5b, 0x03, 0x49, 0xc4, 0xd5, 0x1c, 0x88, 0x9d, 0x08, 0x95, - 0x2d, 0xdd, 0x54, 0x88, 0xbe, 0x95, 0x56, 0x05, 0x94, 0xe6, - ], - script_code: Script(vec![0x52, 0x63, 0x53, 0x51, 0x65]), - transparent_input: Some(0), - hash_type: 2, - amount: 483959951916902, - consensus_branch_id: 1537743641, - sighash: [ - 0x29, 0x6f, 0xd7, 0x63, 0xf2, 0x54, 0x5e, 0x64, 0xfb, 0x5d, 0x7d, 0x49, 0xc0, 0x00, - 0xd2, 0xb4, 0x18, 0xb9, 0x3b, 0xde, 0x22, 0x34, 0xf8, 0x74, 0x29, 0x11, 0xe8, 0xaf, - 0xef, 0xd0, 0x6d, 0x57, - ], - }, - ]; - - for tv in test_vectors { + for tv in self::data::zip_0143::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = if let Some(n) = tv.transparent_input { - Some(( + let transparent_input = tv.transparent_input.map(|n| { + ( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - )) - } else { - None - }; + ) + }); assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input,), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), tv.sighash ); } @@ -1919,3494 +92,18 @@ fn zip_0143() { #[test] fn zip_0243() { - struct TestVector { - tx: Vec, - script_code: Script, - transparent_input: Option, - hash_type: u32, - amount: i64, - consensus_branch_id: u32, - sighash: [u8; 32], - }; - - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0243.py - let test_vectors = vec![ - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x02, 0xe7, 0x71, 0x98, 0x11, - 0x89, 0x3e, 0x00, 0x00, 0x09, 0x52, 0x00, 0xac, 0x65, 0x51, 0xac, 0x63, 0x65, 0x65, - 0xb2, 0x83, 0x5a, 0x08, 0x05, 0x75, 0x02, 0x00, 0x02, 0x51, 0x51, 0x48, 0x1c, 0xdd, - 0x86, 0xb3, 0xcc, 0x43, 0x18, 0x44, 0x21, 0x17, 0x62, 0x3c, 0xeb, 0x05, 0x00, 0x03, - 0x1b, 0x3d, 0x1a, 0x02, 0x7c, 0x2c, 0x40, 0x59, 0x09, 0x58, 0xb7, 0xeb, 0x13, 0xd7, - 0x42, 0xa9, 0x97, 0x73, 0x8c, 0x46, 0xa4, 0x58, 0x96, 0x5b, 0xaf, 0x27, 0x6b, 0xa9, - 0x2f, 0x27, 0x2c, 0x72, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, - 0x9d, 0x4e, 0x30, 0xa7, 0x35, 0x94, 0xbf, 0x50, 0x98, 0x42, 0x1c, 0x69, 0x37, 0x8a, - 0xf1, 0xe4, 0x0f, 0x64, 0xe1, 0x25, 0x94, 0x6f, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, - 0xbc, 0xb6, 0x4b, 0x69, 0x68, 0x91, 0x2a, 0x63, 0x81, 0xce, 0x3d, 0xc1, 0x66, 0xd5, - 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5, 0xfd, 0x93, 0x13, 0x25, 0xc9, - 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x51, - 0xa7, 0xaf, 0x9d, 0xb6, 0x99, 0x0e, 0xd8, 0x3d, 0xd6, 0x4a, 0xf3, 0x59, 0x7c, 0x04, - 0x32, 0x3e, 0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, - 0x32, 0x0d, 0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, 0xdf, 0x65, 0x8d, 0x24, 0xae, - 0x67, 0xc2, 0x2c, 0x8d, 0x13, 0x09, 0x13, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, - 0x42, 0x76, 0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x91, 0xe0, 0x0c, 0x7a, 0x1d, 0x48, 0xaf, - 0x04, 0x68, 0x27, 0x59, 0x1e, 0x97, 0x33, 0xa9, 0x7f, 0xa6, 0xb6, 0x79, 0xf3, 0xdc, - 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0xfc, 0x1b, 0xe4, - 0xaa, 0xc0, 0x0f, 0xf2, 0x71, 0x1e, 0xbd, 0x93, 0x1d, 0xe5, 0x18, 0x85, 0x68, 0x78, - 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, 0xf7, 0x39, - 0x3c, 0x94, 0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, - 0x79, 0x0f, 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, - 0xf4, 0x73, 0xf4, 0x68, 0xa0, 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88, 0x0d, 0x78, - 0x0c, 0xb0, 0x7f, 0xcf, 0xaa, 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, - 0x15, 0x3d, 0x88, 0x2d, 0x2b, 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, 0x94, 0x94, 0xc6, - 0xac, 0x89, 0x3c, 0x49, 0x72, 0x38, 0x33, 0xec, 0x89, 0x26, 0xc1, 0x03, 0x95, 0x86, - 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, 0x5d, 0x99, 0x58, 0x9c, 0x8b, - 0xb8, 0x38, 0xe8, 0xaa, 0xf7, 0x45, 0x53, 0x3e, 0xd9, 0xe8, 0xae, 0x3a, 0x1c, 0xd0, - 0x74, 0xa5, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, - 0xed, 0x42, 0x43, 0x5e, 0x92, 0x47, 0x69, 0x30, 0xd0, 0x69, 0x89, 0x6c, 0xff, 0x30, - 0xeb, 0x41, 0x4f, 0x72, 0x7b, 0x89, 0x5a, 0x4b, 0x7b, 0xe1, 0x76, 0x93, 0x67, 0xe1, - 0xfe, 0x8a, 0xd1, 0x8d, 0xe1, 0x1e, 0x58, 0xd8, 0x8a, 0x0a, 0xd5, 0x51, 0x1d, 0x35, - 0x25, 0x12, 0x2b, 0x7b, 0x0a, 0x6f, 0x25, 0xd2, 0x8b, 0x16, 0x45, 0x7e, 0x74, 0x59, - 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11, 0x7d, 0x41, - 0x7a, 0xdb, 0x3d, 0x15, 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50, 0x0c, 0x6b, - 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, 0xcc, 0x40, - 0xa9, 0x8d, 0x5f, 0x29, 0x35, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, 0xfd, 0xbb, - 0x5d, 0x47, 0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, - 0xb7, 0xfa, 0xe2, 0xdb, 0x58, 0x71, 0x05, 0x41, 0x5d, 0x46, 0x42, 0x78, 0x9d, 0x38, - 0xf5, 0x0b, 0x8d, 0xbc, 0xc1, 0x29, 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, 0x35, 0x5b, - 0xcf, 0x73, 0xce, 0xcb, 0x8c, 0xb8, 0xa5, 0xda, 0x01, 0x30, 0x71, 0x52, 0xf1, 0x39, - 0x36, 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, 0x26, 0xc6, 0xcb, 0x4c, - 0xd4, 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a, 0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15, - 0x40, 0x6f, 0x2f, 0xdd, 0x2a, 0xfa, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, - 0x2a, 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, - 0x79, 0xf8, 0x80, 0x08, 0xe3, 0x15, 0xdc, 0x7d, 0x83, 0x88, 0xe7, 0x6c, 0x17, 0x82, - 0xfd, 0x27, 0x95, 0xd1, 0x8a, 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, - 0x48, 0x9c, 0xe7, 0x57, 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, - 0xdf, 0x73, 0xca, 0xec, 0x65, 0x60, 0x40, 0x37, 0x31, 0x4f, 0xaa, 0xce, 0xb5, 0x62, - 0x18, 0xc6, 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f, 0x21, 0xa9, - 0xfb, 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, 0x00, 0xe1, 0xb1, - 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, 0x49, 0xe9, - 0x50, 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, 0x1f, 0xe3, 0x89, 0x5b, 0x8c, - 0xc3, 0xd1, 0x4d, 0x2c, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a, - 0x69, 0xf5, 0x33, 0x57, 0xd7, 0x76, 0x7f, 0x4f, 0x5c, 0xcb, 0xdb, 0xc5, 0x96, 0x63, - 0x12, 0x77, 0xf8, 0xfe, 0xcd, 0x08, 0xcb, 0x05, 0x6b, 0x95, 0xe3, 0x02, 0x5b, 0x97, - 0x92, 0xff, 0xf7, 0xf2, 0x44, 0xfc, 0x71, 0x62, 0x69, 0xb9, 0x26, 0xd6, 0x2e, 0x95, - 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff, 0x9e, 0x68, 0x62, 0x5a, 0x6b, 0x4c, - 0xbc, 0x4b, 0x70, 0x0a, 0x36, 0x4f, 0xa7, 0x6b, 0xd8, 0x29, 0x8b, 0xc3, 0xec, 0x60, - 0x8d, 0x4c, 0xf7, 0xf3, 0x56, 0x66, 0x58, 0xd5, 0x58, 0x87, 0x14, 0xec, 0x94, 0x48, - 0xb0, 0xf0, 0x39, 0x61, 0x28, 0xae, 0xf8, 0x84, 0xa6, 0x46, 0x11, 0x4c, 0x9f, 0x1a, - 0x6d, 0xf5, 0x63, 0x19, 0x03, 0x3c, 0x31, 0x99, 0xcc, 0x7a, 0x09, 0xe9, 0xe9, 0x56, - 0x74, 0x82, 0xc9, 0x26, 0x95, 0x39, 0x02, 0x29, 0x40, 0x7b, 0xbc, 0x48, 0x98, 0x56, - 0x75, 0xe3, 0xf8, 0x74, 0xa4, 0x53, 0x3f, 0x1d, 0x63, 0xa8, 0x4d, 0xfa, 0x3e, 0x0f, - 0x46, 0x0f, 0xe2, 0xf5, 0x7e, 0x34, 0xfb, 0xc7, 0x54, 0x23, 0xb6, 0x88, 0x3a, 0x50, - 0xa0, 0xd4, 0x70, 0x19, 0x0d, 0xfb, 0xa1, 0x0a, 0x85, 0x7f, 0x82, 0x84, 0x2d, 0x38, - 0x25, 0xb3, 0xd6, 0xda, 0x05, 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, 0xc0, 0xb7, 0x16, - 0xc4, 0x8f, 0xbd, 0x46, 0x7f, 0x75, 0xb7, 0x80, 0x14, 0x9a, 0xe8, 0x80, 0x8f, 0x4e, - 0x68, 0xf5, 0x0c, 0x05, 0x36, 0xac, 0xdd, 0xf6, 0xf1, 0xae, 0xab, 0x01, 0x6b, 0x6b, - 0xc1, 0xec, 0x14, 0x4b, 0x4e, 0x55, 0x3a, 0xcf, 0xd6, 0x70, 0xf7, 0x7e, 0x75, 0x5f, - 0xc8, 0x8e, 0x06, 0x77, 0xe3, 0x1b, 0xa4, 0x59, 0xb4, 0x4e, 0x30, 0x77, 0x68, 0x95, - 0x8f, 0xe3, 0x78, 0x9d, 0x41, 0xc2, 0xb1, 0xff, 0x43, 0x4c, 0xb3, 0x0e, 0x15, 0x91, - 0x4f, 0x01, 0xbc, 0x6b, 0xc2, 0x30, 0x7b, 0x48, 0x8d, 0x25, 0x56, 0xd7, 0xb7, 0x38, - 0x0e, 0xa4, 0xff, 0xd7, 0x12, 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, - 0x40, 0x59, 0xf3, 0x96, 0xbf, 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, - 0xa9, 0x44, 0xf7, 0x2d, 0x43, 0x6a, 0x10, 0x2f, 0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, - 0xb0, 0x86, 0xfe, 0x9d, 0x2e, 0x71, 0x62, 0x47, 0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, - 0xec, 0x95, 0x12, 0xbf, 0xb3, 0xf3, 0x83, 0x27, 0x29, 0x6e, 0xfa, 0xa7, 0x43, 0x28, - 0xb1, 0x18, 0xc2, 0x74, 0x02, 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4, 0xbb, 0xc6, - 0x8e, 0x37, 0xc0, 0xaa, 0x7d, 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, 0x3b, 0x84, 0x1e, - 0x75, 0x17, 0x13, 0xa0, 0x29, 0x43, 0x90, 0x5a, 0xae, 0x08, 0x03, 0xfd, 0x69, 0x44, - 0x2e, 0xb7, 0x68, 0x1e, 0xc2, 0xa0, 0x56, 0x00, 0x05, 0x4e, 0x92, 0xee, 0xd5, 0x55, - 0x02, 0x8f, 0x21, 0xb6, 0xa1, 0x55, 0x26, 0x8a, 0x2d, 0xd6, 0x64, 0x0a, 0x69, 0x30, - 0x1a, 0x52, 0xa3, 0x8d, 0x4d, 0x9f, 0x9f, 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71, - 0x18, 0x14, 0x1c, 0xe4, 0xc9, 0xbe, 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81, - 0xa1, 0x55, 0xfa, 0x3a, 0x03, 0x49, 0x99, 0xc5, 0x38, 0xf7, 0xa7, 0x58, 0xbb, 0x5b, - 0x1d, 0x28, 0xfd, 0x21, 0x8f, 0xba, 0x19, 0x38, 0x74, 0x4b, 0xdb, 0x77, 0xb4, 0xa4, - 0xdf, 0xa7, 0xa5, 0xfa, 0xe9, 0x6e, 0x8c, 0xd4, 0x9b, 0x26, 0x90, 0x7d, 0xfc, 0x66, - 0x85, 0xc5, 0xc9, 0x9b, 0x71, 0x41, 0xac, 0x62, 0x6a, 0xb4, 0x76, 0x1f, 0xd3, 0xf4, - 0x1e, 0x72, 0x8e, 0x1a, 0x28, 0xf8, 0x9d, 0xb8, 0x9f, 0xfd, 0xec, 0xa3, 0x64, 0xe4, - 0xb2, 0x2d, 0x81, 0xd9, 0x96, 0x8d, 0x01, 0x19, 0xe4, 0xc7, 0xa1, 0x89, 0xad, 0xf2, - 0x2a, 0xd9, 0x68, 0x30, 0xa5, 0x4e, 0x40, 0xdc, 0x73, 0xea, 0xba, 0x6b, 0x2a, 0xaf, - 0x14, 0xf7, 0xca, 0x94, 0x2e, 0x73, 0x70, 0xb2, 0x47, 0xc0, 0x46, 0xf8, 0xe7, 0x5e, - 0xf8, 0xe3, 0xf8, 0xbd, 0x82, 0x1c, 0xf5, 0x77, 0x49, 0x18, 0x64, 0xe2, 0x0e, 0x6d, - 0x08, 0xfd, 0x2e, 0x32, 0xb5, 0x55, 0xc9, 0x2c, 0x66, 0x1f, 0x19, 0x58, 0x8b, 0x72, - 0xa8, 0x95, 0x99, 0x71, 0x0a, 0x88, 0x06, 0x12, 0x53, 0xca, 0x28, 0x5b, 0x63, 0x04, - 0xb3, 0x7d, 0xa2, 0xb5, 0x29, 0x4f, 0x5c, 0xb3, 0x54, 0xa8, 0x94, 0x32, 0x28, 0x48, - 0xcc, 0xbd, 0xc7, 0xc2, 0x54, 0x5b, 0x7d, 0xa5, 0x68, 0xaf, 0xac, 0x87, 0xff, 0xa0, - 0x05, 0xc3, 0x12, 0x24, 0x1c, 0x2d, 0x57, 0xf4, 0xb4, 0x5d, 0x64, 0x19, 0xf0, 0xd2, - 0xe2, 0xc5, 0xaf, 0x33, 0xae, 0x24, 0x37, 0x85, 0xb3, 0x25, 0xcd, 0xab, 0x95, 0x40, - 0x4f, 0xc7, 0xae, 0xd7, 0x05, 0x25, 0xcd, 0xdb, 0x41, 0x87, 0x2c, 0xfc, 0xc2, 0x14, - 0xb1, 0x32, 0x32, 0xed, 0xc7, 0x86, 0x09, 0x75, 0x3d, 0xbf, 0xf9, 0x30, 0xeb, 0x0d, - 0xc1, 0x56, 0x61, 0x2b, 0x9c, 0xb4, 0x34, 0xbc, 0x4b, 0x69, 0x33, 0x92, 0xde, 0xb8, - 0x7c, 0x53, 0x04, 0x35, 0x31, 0x2e, 0xdc, 0xed, 0xc6, 0xa9, 0x61, 0x13, 0x33, 0x38, - 0xd7, 0x86, 0xc4, 0xa3, 0xe1, 0x03, 0xf6, 0x01, 0x10, 0xa1, 0x6b, 0x13, 0x37, 0x12, - 0x97, 0x04, 0xbf, 0x47, 0x54, 0xff, 0x6b, 0xa9, 0xfb, 0xe6, 0x59, 0x51, 0xe6, 0x10, - 0x62, 0x0f, 0x71, 0xcd, 0xa8, 0xfc, 0x87, 0x76, 0x25, 0xf2, 0xc5, 0xbb, 0x04, 0xcb, - 0xe1, 0x22, 0x8b, 0x1e, 0x88, 0x6f, 0x40, 0x50, 0xaf, 0xd8, 0xfe, 0x94, 0xe9, 0x7d, - 0x2e, 0x9e, 0x85, 0xc6, 0xbb, 0x74, 0x8c, 0x00, 0x42, 0xd3, 0x24, 0x9a, 0xbb, 0x13, - 0x42, 0xbb, 0x0e, 0xeb, 0xf6, 0x20, 0x58, 0xbf, 0x3d, 0xe0, 0x80, 0xd9, 0x46, 0x11, - 0xa3, 0x75, 0x09, 0x15, 0xb5, 0xdc, 0x6c, 0x0b, 0x38, 0x99, 0xd4, 0x12, 0x22, 0xba, - 0xce, 0x76, 0x0e, 0xe9, 0xc8, 0x81, 0x8d, 0xed, 0x59, 0x9e, 0x34, 0xc5, 0x6d, 0x73, - 0x72, 0xaf, 0x1e, 0xb8, 0x68, 0x52, 0xf2, 0xa7, 0x32, 0x10, 0x4b, 0xdb, 0x75, 0x07, - 0x39, 0xde, 0x6c, 0x2c, 0x6e, 0x0f, 0x9e, 0xb7, 0xcb, 0x17, 0xf1, 0x94, 0x2b, 0xfc, - 0x9f, 0x4f, 0xd6, 0xeb, 0xb6, 0xb4, 0xcd, 0xd4, 0xda, 0x2b, 0xca, 0x26, 0xfa, 0xc4, - 0x57, 0x8e, 0x9f, 0x54, 0x34, 0x05, 0xac, 0xc7, 0xd8, 0x6f, 0xf5, 0x91, 0x58, 0xbd, - 0x0c, 0xba, 0x3a, 0xef, 0x6f, 0x4a, 0x84, 0x72, 0xd1, 0x44, 0xd9, 0x9f, 0x8b, 0x8d, - 0x1d, 0xed, 0xaa, 0x90, 0x77, 0xd4, 0xf0, 0x1d, 0x4b, 0xb2, 0x7b, 0xbe, 0x31, 0xd8, - 0x8f, 0xbe, 0xfa, 0xc3, 0xdc, 0xd4, 0x79, 0x75, 0x63, 0xa2, 0x6b, 0x1d, 0x61, 0xfc, - 0xd9, 0xa4, 0x64, 0xab, 0x21, 0xed, 0x55, 0x0f, 0xe6, 0xfa, 0x09, 0x69, 0x5b, 0xa0, - 0xb2, 0xf1, 0x0e, 0xea, 0x64, 0x68, 0xcc, 0x6e, 0x20, 0xa6, 0x6f, 0x82, 0x6e, 0x3d, - 0x14, 0xc5, 0x00, 0x6f, 0x05, 0x63, 0x88, 0x7f, 0x5e, 0x12, 0x89, 0xbe, 0x1b, 0x20, - 0x04, 0xca, 0xca, 0x8d, 0x3f, 0x34, 0xd6, 0xe8, 0x4b, 0xf5, 0x9c, 0x1e, 0x04, 0x61, - 0x9a, 0x7c, 0x23, 0xa9, 0x96, 0x94, 0x1d, 0x88, 0x9e, 0x46, 0x22, 0xa9, 0xb9, 0xb1, - 0xd5, 0x9d, 0x5e, 0x31, 0x90, 0x94, 0x31, 0x8c, 0xd4, 0x05, 0xba, 0x27, 0xb7, 0xe2, - 0xc0, 0x84, 0x76, 0x2d, 0x31, 0x45, 0x3e, 0xc4, 0x54, 0x9a, 0x4d, 0x97, 0x72, 0x9d, - 0x03, 0x34, 0x60, 0xfc, 0xf8, 0x9d, 0x64, 0x94, 0xf2, 0xff, 0xd7, 0x89, 0xe9, 0x80, - 0x82, 0xea, 0x5c, 0xe9, 0x53, 0x4b, 0x3a, 0xcd, 0x60, 0xfe, 0x49, 0xe3, 0x7e, 0x4f, - 0x66, 0x69, 0x31, 0x67, 0x73, 0x19, 0xed, 0x89, 0xf8, 0x55, 0x88, 0x74, 0x1b, 0x31, - 0x28, 0x90, 0x1a, 0x93, 0xbd, 0x78, 0xe4, 0xbe, 0x02, 0x25, 0xa9, 0xe2, 0x69, 0x2c, - 0x77, 0xc9, 0x69, 0xed, 0x01, 0x76, 0xbd, 0xf9, 0x55, 0x59, 0x48, 0xcb, 0xd5, 0xa3, - 0x32, 0xd0, 0x45, 0xde, 0x6b, 0xa6, 0xbf, 0x44, 0x90, 0xad, 0xfe, 0x74, 0x44, 0xcd, - 0x46, 0x7a, 0x09, 0x07, 0x54, 0x17, 0xfc, 0xc0, 0x06, 0x2e, 0x49, 0xf0, 0x08, 0xc5, - 0x1a, 0xd4, 0x22, 0x74, 0x39, 0xc1, 0xb4, 0x47, 0x6c, 0xcd, 0x8e, 0x97, 0x86, 0x2d, - 0xab, 0x7b, 0xe1, 0xe8, 0xd3, 0x99, 0xc0, 0x5e, 0xf2, 0x7c, 0x6e, 0x22, 0xee, 0x27, - 0x3e, 0x15, 0x78, 0x6e, 0x39, 0x4c, 0x8f, 0x1b, 0xe3, 0x16, 0x82, 0xa3, 0x01, 0x47, - 0x96, 0x3a, 0xc8, 0xda, 0x8d, 0x41, 0xd8, 0x04, 0x25, 0x84, 0x26, 0xa3, 0xf7, 0x02, - 0x89, 0xb8, 0xad, 0x19, 0xd8, 0xde, 0x13, 0xbe, 0x4e, 0xeb, 0xe3, 0xbd, 0x4c, 0x8a, - 0x6f, 0x55, 0xd6, 0xe0, 0xc3, 0x73, 0xd4, 0x56, 0x85, 0x18, 0x79, 0xf5, 0xfb, 0xc2, - 0x82, 0xdb, 0x9e, 0x13, 0x48, 0x06, 0xbf, 0xf7, 0x1e, 0x11, 0xbc, 0x33, 0xab, 0x75, - 0xdd, 0x6c, 0xa0, 0x67, 0xfb, 0x73, 0xa0, 0x43, 0xb6, 0x46, 0xa7, 0xcf, 0x39, 0xca, - 0xb4, 0x92, 0x83, 0x86, 0x78, 0x6d, 0x2f, 0x24, 0x14, 0x1e, 0xe1, 0x20, 0xfd, 0xc3, - 0x4d, 0x67, 0x64, 0xea, 0xfc, 0x66, 0x88, 0x0e, 0xe0, 0x20, 0x4f, 0x53, 0xcc, 0x11, - 0x67, 0xed, 0x20, 0xb4, 0x3a, 0x52, 0xde, 0xa3, 0xca, 0x7c, 0xff, 0x8e, 0xf3, 0x5c, - 0xd8, 0xe6, 0xd7, 0xc1, 0x11, 0xa6, 0x8e, 0xf4, 0x4b, 0xcd, 0x0c, 0x15, 0x13, 0xad, - 0x47, 0xca, 0x61, 0xc6, 0x59, 0xcc, 0x5d, 0x32, 0x5b, 0x44, 0x0f, 0x6b, 0x9f, 0x59, - 0xaf, 0xf6, 0x68, 0x79, 0xbb, 0x66, 0x88, 0xfd, 0x28, 0x59, 0x36, 0x2b, 0x18, 0x2f, - 0x20, 0x7b, 0x31, 0x75, 0x96, 0x1f, 0x64, 0x11, 0xa4, 0x93, 0xbf, 0xfd, 0x04, 0x8e, - 0x7d, 0x0d, 0x87, 0xd8, 0x2f, 0xe6, 0xf9, 0x90, 0xa2, 0xb0, 0xa2, 0x5f, 0x5a, 0xa0, - 0x11, 0x1a, 0x6e, 0x68, 0xf3, 0x7b, 0xf6, 0xf3, 0xac, 0x2d, 0x26, 0xb8, 0x46, 0x86, - 0xe5, 0x69, 0xd5, 0x8d, 0x99, 0xc1, 0x38, 0x35, 0x97, 0xfa, 0xd8, 0x11, 0x93, 0xc4, - 0xc1, 0xb1, 0x6e, 0x6a, 0x90, 0xe2, 0xd5, 0x07, 0xcd, 0xfe, 0x6f, 0xbd, 0xaa, 0x86, - 0x16, 0x3e, 0x9c, 0xf5, 0xde, 0x31, 0x00, 0xfb, 0xca, 0x7e, 0x8d, 0xa0, 0x47, 0xb0, - 0x90, 0x79, 0x36, 0x2d, 0x77, 0x92, 0xde, 0xb3, 0xca, 0x9d, 0xc1, 0x56, 0x1b, 0x87, - 0xc8, 0x2e, 0x3c, 0xb9, 0x9e, 0xb5, 0x83, 0x73, 0x19, 0x58, 0x22, 0x16, 0xa3, 0x22, - 0x67, 0x74, 0xef, 0xa9, 0x0e, 0xfb, 0x7b, 0xfc, 0x79, 0xf4, 0x25, 0x64, 0x4e, 0x4e, - 0x98, 0xc2, 0xd7, 0xd8, 0x64, 0x2b, 0x9d, 0xb8, 0x2a, 0xa7, 0x39, 0xbf, 0x2d, 0x71, - 0xcc, 0x41, 0x17, 0x22, 0x7d, 0xb2, 0x27, 0xcf, 0x0a, 0x05, 0xad, 0x9a, 0x95, 0x83, - 0x2e, 0x23, 0xc9, 0x4f, 0x27, 0x1c, 0xa0, 0xe4, 0x69, 0x4f, 0xac, 0x63, 0x22, 0x28, - 0x2e, 0xba, 0xc6, 0x98, 0x6b, 0x8f, 0xdc, 0x8a, 0xd8, 0x63, 0x08, 0x4f, 0xf1, 0x0f, - 0xd1, 0x1e, 0x6a, 0x13, 0x31, 0x1f, 0xb7, 0x99, 0xc7, 0x9c, 0x64, 0x1d, 0x9d, 0xa4, - 0x3b, 0x33, 0xe7, 0xad, 0x01, 0x2e, 0x28, 0x25, 0x53, 0x98, 0x78, 0x92, 0x62, 0x27, - 0x5f, 0x11, 0x75, 0xbe, 0x84, 0x62, 0xc0, 0x14, 0x91, 0xc4, 0xd8, 0x42, 0x40, 0x6d, - 0x0e, 0xc4, 0x28, 0x2c, 0x95, 0x26, 0x17, 0x4a, 0x09, 0x87, 0x8f, 0xe8, 0xfd, 0xde, - 0x33, 0xa2, 0x96, 0x04, 0xe5, 0xe5, 0xe7, 0xb2, 0xa0, 0x25, 0xd6, 0x65, 0x0b, 0x97, - 0xdb, 0xb5, 0x2b, 0xef, 0xb5, 0x9b, 0x1d, 0x30, 0xa5, 0x74, 0x33, 0xb0, 0xa3, 0x51, - 0x47, 0x44, 0x44, 0x09, 0x9d, 0xaa, 0x37, 0x10, 0x46, 0x61, 0x32, 0x60, 0xcf, 0x33, - 0x54, 0xcf, 0xcd, 0xad, 0xa6, 0x63, 0xec, 0xe8, 0x24, 0xff, 0xd7, 0xe4, 0x43, 0x93, - 0x88, 0x6a, 0x86, 0x16, 0x5d, 0xdd, 0xdf, 0x2b, 0x4c, 0x41, 0x77, 0x35, 0x54, 0xc8, - 0x69, 0x95, 0x26, 0x94, 0x08, 0xb1, 0x1e, 0x67, 0x37, 0xa4, 0xc4, 0x47, 0x58, 0x6f, - 0x69, 0x17, 0x34, 0x46, 0xd8, 0xe4, 0x8b, 0xf8, 0x4c, 0xbc, 0x00, 0x0a, 0x80, 0x78, - 0x99, 0x97, 0x3e, 0xb9, 0x3c, 0x5e, 0x81, 0x9a, 0xad, 0x66, 0x94, 0x13, 0xf8, 0x38, - 0x79, 0x33, 0xad, 0x15, 0x84, 0xaa, 0x35, 0xe4, 0x3f, 0x4e, 0xcd, 0x1e, 0x2d, 0x04, - 0x07, 0xc0, 0xb1, 0xb8, 0x99, 0x20, 0xff, 0xdf, 0xdb, 0x9b, 0xea, 0x51, 0xac, 0x95, - 0xb5, 0x57, 0xaf, 0x71, 0xb8, 0x9f, 0x90, 0x3f, 0x5d, 0x98, 0x48, 0xf1, 0x4f, 0xcb, - 0xeb, 0x18, 0x37, 0x57, 0x0f, 0x54, 0x4d, 0x63, 0x59, 0xeb, 0x23, 0xfa, 0xf3, 0x8a, - 0x08, 0x22, 0xda, 0x36, 0xce, 0x42, 0x6c, 0x4a, 0x2f, 0xbe, 0xff, 0xeb, 0x0a, 0x8a, - 0x2e, 0x29, 0x7a, 0x9d, 0x19, 0xba, 0x15, 0x02, 0x45, 0x90, 0xe3, 0x32, 0x9d, 0x9f, - 0xa9, 0x26, 0x1f, 0x99, 0x38, 0xa4, 0x03, 0x2d, 0xd3, 0x46, 0x06, 0xc9, 0xcf, 0x9f, - 0x3d, 0xd3, 0x3e, 0x57, 0x6f, 0x05, 0xcd, 0x1d, 0xd6, 0x81, 0x1c, 0x62, 0x98, 0x75, - 0x7d, 0x77, 0xd9, 0xe8, 0x10, 0xab, 0xdb, 0x22, 0x6a, 0xfc, 0xaa, 0x43, 0x46, 0xa6, - 0x56, 0x0f, 0x89, 0x32, 0xb3, 0x18, 0x1f, 0xd3, 0x55, 0xd5, 0xd3, 0x91, 0x97, 0x61, - 0x83, 0xf8, 0xd9, 0x93, 0x88, 0x83, 0x96, 0x32, 0xd6, 0x35, 0x4f, 0x66, 0x6d, 0x09, - 0xd3, 0xe5, 0x62, 0x9e, 0xa1, 0x97, 0x37, 0x38, 0x86, 0x13, 0xd3, 0x8a, 0x34, 0xfd, - 0x0f, 0x6e, 0x50, 0xee, 0x5a, 0x0c, 0xc9, 0x67, 0x71, 0x77, 0xf5, 0x00, 0x28, 0xc1, - 0x41, 0x37, 0x81, 0x87, 0xbd, 0x28, 0x19, 0x40, 0x3f, 0xc5, 0x34, 0xf8, 0x00, 0x76, - 0xe9, 0x38, 0x0c, 0xb4, 0x96, 0x4d, 0x3b, 0x6b, 0x45, 0x81, 0x9d, 0x3b, 0x8e, 0x9c, - 0xaf, 0x54, 0xf0, 0x51, 0x85, 0x2d, 0x67, 0x1b, 0xf8, 0xc1, 0xff, 0xde, 0x2d, 0x15, - 0x10, 0x75, 0x64, 0x18, 0xcb, 0x48, 0x10, 0x93, 0x6a, 0xa5, 0x7e, 0x69, 0x65, 0xd6, - 0xfb, 0x65, 0x6a, 0x76, 0x0b, 0x7f, 0x19, 0xad, 0xf9, 0x6c, 0x17, 0x34, 0x88, 0x55, - 0x21, 0x93, 0xb1, 0x47, 0xee, 0x58, 0x85, 0x80, 0x33, 0xda, 0xc7, 0xcd, 0x0e, 0xb2, - 0x04, 0xc0, 0x64, 0x90, 0xbb, 0xde, 0xdf, 0x5f, 0x75, 0x71, 0xac, 0xb2, 0xeb, 0xe7, - 0x6a, 0xce, 0xf3, 0xf2, 0xa0, 0x1e, 0xe9, 0x87, 0x48, 0x6d, 0xfe, 0x6c, 0x3f, 0x0a, - 0x5e, 0x23, 0x4c, 0x12, 0x72, 0x58, 0xf9, 0x7a, 0x28, 0xfb, 0x5d, 0x16, 0x4a, 0x81, - 0x76, 0xbe, 0x94, 0x6b, 0x80, 0x97, 0xd0, 0xe3, 0x17, 0x28, 0x7f, 0x33, 0xbf, 0x9c, - 0x16, 0xf9, 0xa5, 0x45, 0x40, 0x9c, 0xe2, 0x9b, 0x1f, 0x42, 0x73, 0x72, 0x5f, 0xc0, - 0xdf, 0x02, 0xa0, 0x4e, 0xba, 0xe1, 0x78, 0xb3, 0x41, 0x4f, 0xb0, 0xa8, 0x2d, 0x50, - 0xde, 0xb0, 0x9f, 0xcf, 0x4e, 0x6e, 0xe9, 0xd1, 0x80, 0xff, 0x4f, 0x56, 0xff, 0x3b, - 0xc1, 0xd3, 0x60, 0x1f, 0xc2, 0xdc, 0x90, 0xd8, 0x14, 0xc3, 0x25, 0x6f, 0x49, 0x67, - 0xd3, 0xa8, 0xd6, 0x4c, 0x83, 0xfe, 0xa3, 0x39, 0xc5, 0x1f, 0x5a, 0x8e, 0x58, 0x01, - 0xfb, 0xb9, 0x78, 0x35, 0x58, 0x1b, 0x60, 0x24, 0x65, 0xde, 0xe0, 0x4b, 0x59, 0x22, - 0xc2, 0x76, 0x1b, 0x54, 0x24, 0x5b, 0xec, 0x0c, 0x9e, 0xef, 0x2d, 0xb9, 0x7d, 0x22, - 0xb2, 0xb3, 0x55, 0x6c, 0xc9, 0x69, 0xfb, 0xb1, 0x3d, 0x06, 0x50, 0x97, 0x65, 0xa5, - 0x2b, 0x3f, 0xac, 0x54, 0xb9, 0x3f, 0x42, 0x1b, 0xf0, 0x8e, 0x18, 0xd5, 0x2d, 0xdd, - 0x52, 0xcc, 0x1c, 0x8c, 0xa8, 0xad, 0xfa, 0xcc, 0xab, 0x7e, 0x5c, 0xc2, 0xf4, 0x57, - 0x3f, 0xbb, 0xf8, 0x23, 0x9b, 0xb0, 0xb8, 0xae, 0xdb, 0xf8, 0xda, 0xd1, 0x62, 0x82, - 0xda, 0x5c, 0x91, 0x25, 0xdb, 0xa1, 0xc0, 0x59, 0xd0, 0xdf, 0x8a, 0xbf, 0x62, 0x10, - 0x78, 0xf0, 0x2d, 0x6c, 0x4b, 0xc8, 0x6d, 0x40, 0x84, 0x5a, 0xc1, 0xd5, 0x97, 0x10, - 0xc4, 0x5f, 0x07, 0xd5, 0x85, 0xeb, 0x48, 0xb3, 0x2f, 0xc0, 0x16, 0x7b, 0xa2, 0x56, - 0xe7, 0x3c, 0xa3, 0xb9, 0x31, 0x1c, 0x62, 0xd1, 0x09, 0x49, 0x79, 0x57, 0xd8, 0xdb, - 0xe1, 0x0a, 0xa3, 0xe8, 0x66, 0xb4, 0x0c, 0x0b, 0xaa, 0x2b, 0xc4, 0x92, 0xc1, 0x9a, - 0xd1, 0xe6, 0x37, 0x2d, 0x96, 0x22, 0xbf, 0x16, 0x3f, 0xbf, 0xfe, 0xae, 0xee, 0x79, - 0x6a, 0x3c, 0xd9, 0xb6, 0xfb, 0xbf, 0xa4, 0xd7, 0x92, 0xf3, 0x4d, 0x7f, 0xd6, 0xe7, - 0x63, 0xcd, 0x58, 0x59, 0xdd, 0x26, 0x83, 0x3d, 0x21, 0xd9, 0xbc, 0x54, 0x52, 0xbd, - 0x19, 0x51, 0x5d, 0xff, 0x9f, 0x49, 0x95, 0xb3, 0x5b, 0xc0, 0xc1, 0xf8, 0x76, 0xe6, - 0xad, 0x11, 0xf2, 0x45, 0x2d, 0xc9, 0xae, 0x85, 0xae, 0xc0, 0x1f, 0xc5, 0x6f, 0x8c, - 0xbf, 0xda, 0x75, 0xa7, 0x72, 0x7b, 0x75, 0xeb, 0xbd, 0x6b, 0xbf, 0xfb, 0x43, 0xb6, - 0x3a, 0x3b, 0x1b, 0x67, 0x1e, 0x40, 0xfe, 0xb0, 0xdb, 0x00, 0x29, 0x74, 0xa3, 0xc3, - 0xb1, 0xa7, 0x88, 0x56, 0x72, 0x31, 0xbf, 0x63, 0x99, 0xff, 0x89, 0x23, 0x69, 0x81, - 0x14, 0x9d, 0x42, 0x38, 0x02, 0xd2, 0x34, 0x1a, 0x3b, 0xed, 0xb9, 0xdd, 0xcb, 0xac, - 0x1f, 0xe7, 0xb6, 0x43, 0x5e, 0x14, 0x79, 0xc7, 0x2e, 0x70, 0x89, 0xb5, 0x1b, 0xfe, - 0x2f, 0xf3, 0x45, 0x85, 0x7d, 0xa9, 0xb5, 0x45, 0xe8, 0x8e, 0x32, 0x21, 0xf3, 0xf5, - 0xf7, 0x2d, 0x1e, 0x06, 0x9c, 0x9a, 0x85, 0xdd, 0x22, 0x36, 0xd3, 0x90, 0x98, 0x95, - 0x87, 0xbe, 0x00, 0x5c, 0xda, 0x16, 0xaf, 0x44, 0x08, 0xf3, 0xab, 0x06, 0xa9, 0x16, - 0xee, 0xeb, 0x9c, 0x95, 0x94, 0xb7, 0x04, 0x24, 0xa4, 0xc1, 0xd1, 0x71, 0x29, 0x5b, - 0x67, 0x63, 0xb2, 0x2f, 0x47, 0x12, 0xba, 0x7b, 0xef, 0xf0, 0xff, 0x27, 0x88, 0x3a, - 0xfa, 0xff, 0x26, 0x03, 0x4b, 0x89, 0x57, 0x35, 0x70, 0x9c, 0xf9, 0x37, 0xbd, 0x22, - 0x31, 0x89, 0x1e, 0x70, 0xeb, 0x27, 0x71, 0xe9, 0x92, 0x7c, 0x97, 0xf8, 0x76, 0x4e, - 0xb4, 0x8e, 0x91, 0x1d, 0x42, 0x8e, 0xc8, 0xd8, 0x61, 0xb7, 0x08, 0xe8, 0x29, 0x8a, - 0xcb, 0x62, 0x15, 0x51, 0x45, 0x15, 0x5a, 0xe9, 0x5f, 0x0a, 0x1d, 0x15, 0x01, 0x03, - 0x47, 0x53, 0x14, 0x6e, 0x22, 0xd0, 0x5f, 0x58, 0x6d, 0x7f, 0x6b, 0x4f, 0xe1, 0x2d, - 0xad, 0x9a, 0x17, 0xf5, 0xdb, 0x70, 0xb1, 0xdb, 0x96, 0xb8, 0xd9, 0xa8, 0x3e, 0xda, - 0xdc, 0x96, 0x6c, 0x8a, 0x54, 0x66, 0xb6, 0x1f, 0xc9, 0x98, 0xc3, 0x1f, 0x10, 0x70, - 0xd9, 0xa5, 0xc9, 0xa6, 0xd2, 0x68, 0xd3, 0x04, 0xfe, 0x6b, 0x8f, 0xd3, 0xb4, 0x01, - 0x03, 0x48, 0x61, 0x1a, 0xbd, 0xcb, 0xd4, 0x9f, 0xe4, 0xf8, 0x5b, 0x62, 0x3c, 0x78, - 0x28, 0xc7, 0x13, 0x82, 0xe1, 0x03, 0x4e, 0xa6, 0x7b, 0xc8, 0xae, 0x97, 0x40, 0x4b, - 0x0c, 0x50, 0xb2, 0xa0, 0x4f, 0x55, 0x9e, 0x49, 0x95, 0x0a, 0xfc, 0xb0, 0xef, 0x46, - 0x2a, 0x2a, 0xe0, 0x24, 0xb0, 0xf0, 0x22, 0x4d, 0xfd, 0x73, 0x68, 0x4b, 0x88, 0xc7, - 0xfb, 0xe9, 0x2d, 0x02, 0xb6, 0x8f, 0x75, 0x9c, 0x47, 0x52, 0x66, 0x3c, 0xd7, 0xb9, - 0x7a, 0x14, 0x94, 0x36, 0x49, 0x30, 0x55, 0x21, 0x32, 0x6b, 0xde, 0x08, 0x56, 0x30, - 0x86, 0x46, 0x29, 0x29, 0x1b, 0xae, 0x25, 0xff, 0x88, 0x22, 0xa1, 0x4c, 0x4b, 0x66, - 0x6a, 0x92, 0x59, 0xad, 0x0d, 0xc4, 0x2a, 0x82, 0x90, 0xac, 0x7b, 0xc7, 0xf5, 0x3a, - 0x16, 0xf3, 0x79, 0xf7, 0x58, 0xe5, 0xde, 0x75, 0x0f, 0x04, 0xfd, 0x7c, 0xad, 0x47, - 0x70, 0x1c, 0x85, 0x97, 0xf9, 0x78, 0x88, 0xbe, 0xa6, 0xfa, 0x0b, 0xf2, 0x99, 0x99, - 0x56, 0xfb, 0xfd, 0x0e, 0xe6, 0x8e, 0xc3, 0x6e, 0x46, 0x88, 0x80, 0x9a, 0xe2, 0x31, - 0xeb, 0x8b, 0xc4, 0x36, 0x9f, 0x5f, 0xe1, 0x57, 0x3f, 0x57, 0xe0, 0x99, 0xd9, 0xc0, - 0x99, 0x01, 0xbf, 0x39, 0xca, 0xac, 0x48, 0xdc, 0x11, 0x95, 0x6a, 0x8a, 0xe9, 0x05, - 0xea, 0xd8, 0x69, 0x54, 0x54, 0x7c, 0x44, 0x8a, 0xe4, 0x3d, 0x31, 0x5e, 0x66, 0x9c, - 0x42, 0x42, 0xda, 0x56, 0x59, 0x38, 0xf4, 0x17, 0xbf, 0x43, 0xce, 0x7b, 0x2b, 0x30, - 0xb1, 0xcd, 0x40, 0x18, 0x38, 0x8e, 0x1a, 0x91, 0x0f, 0x0f, 0xc4, 0x1f, 0xb0, 0x87, - 0x7a, 0x59, 0x25, 0xe4, 0x66, 0x81, 0x9d, 0x37, 0x5b, 0x0a, 0x91, 0x2d, 0x4f, 0xe8, - 0x43, 0xb7, 0x6e, 0xf6, 0xf2, 0x23, 0xf0, 0xf7, 0xc8, 0x94, 0xf3, 0x8f, 0x7a, 0xb7, - 0x80, 0xdf, 0xd7, 0x5f, 0x66, 0x9c, 0x8c, 0x06, 0xcf, 0xfa, 0x43, 0xeb, 0x47, 0x56, - 0x5a, 0x50, 0xe3, 0xb1, 0xfa, 0x45, 0xad, 0x61, 0xce, 0x9a, 0x1c, 0x47, 0x27, 0xb7, - 0xaa, 0xa5, 0x35, 0x62, 0xf5, 0x23, 0xe7, 0x39, 0x52, 0xbb, 0xf3, 0x3d, 0x8a, 0x41, - 0x04, 0x07, 0x8a, 0xde, 0x3e, 0xaa, 0xa4, 0x96, 0x99, 0xa6, 0x9f, 0xdf, 0x1c, 0x5a, - 0xc7, 0x73, 0x21, 0x46, 0xee, 0x5e, 0x1d, 0x6b, 0x6c, 0xa9, 0xb9, 0x18, 0x0f, 0x96, - 0x4c, 0xc9, 0xd0, 0x87, 0x8a, 0xe1, 0x37, 0x35, 0x24, 0xd7, 0xd5, 0x10, 0xe5, 0x82, - 0x27, 0xdf, 0x6d, 0xe9, 0xd3, 0x0d, 0x27, 0x18, 0x67, 0x64, 0x01, 0x77, 0xb0, 0xf1, - 0x85, 0x6e, 0x28, 0xd5, 0xc8, 0xaf, 0xb0, 0x95, 0xef, 0x61, 0x84, 0xfe, 0xd6, 0x51, - 0x58, 0x90, 0x22, 0xee, 0xae, 0xa4, 0xc0, 0xce, 0x1f, 0xa6, 0xf0, 0x85, 0x09, 0x2b, - 0x04, 0x97, 0x94, 0x89, 0x17, 0x2b, 0x3e, 0xf8, 0x19, 0x4a, 0x79, 0x8d, 0xf5, 0x72, - 0x4d, 0x6b, 0x05, 0xf1, 0xae, 0x00, 0x00, 0x13, 0xa0, 0x8d, 0x61, 0x2b, 0xca, 0x8a, - 0x8c, 0x31, 0x44, 0x3c, 0x10, 0x34, 0x6d, 0xbf, 0x61, 0xde, 0x84, 0x75, 0xc0, 0xbb, - 0xec, 0x51, 0x04, 0xb4, 0x75, 0x56, 0xaf, 0x3d, 0x51, 0x44, 0x58, 0xe2, 0x32, 0x1d, - 0x14, 0x60, 0x71, 0x78, 0x9d, 0x23, 0x35, 0x93, 0x4a, 0x68, 0x06, 0x14, 0xe8, 0x35, - 0x62, 0xf8, 0x2d, 0xfd, 0x40, 0x5b, 0x54, 0xa4, 0x5e, 0xb3, 0x2c, 0x16, 0x54, 0x48, - 0xd4, 0xd5, 0xd6, 0x1c, 0xa2, 0x85, 0x95, 0x85, 0x36, 0x9f, 0x53, 0xf1, 0xa1, 0x37, - 0xe9, 0xe8, 0x2b, 0x67, 0xb8, 0xfd, 0xaf, 0x01, 0xbd, 0xa5, 0x4a, 0x31, 0x73, 0x11, - 0x89, 0x6a, 0xe1, 0x02, 0x80, 0xa0, 0x32, 0x44, 0x0c, 0x42, 0x0a, 0x42, 0x1e, 0x94, - 0x4d, 0x1e, 0x95, 0x2b, 0x70, 0xd5, 0x82, 0x6c, 0xd3, 0xb0, 0x8b, 0x7d, 0xb9, 0x63, - 0x0f, 0xe4, 0xfd, 0x5f, 0x22, 0x12, 0x5d, 0xe8, 0x40, 0xfc, 0xc4, 0x0b, 0x98, 0x03, - 0x8a, 0xf1, 0x1d, 0x55, 0xbe, 0x25, 0x43, 0x25, 0x97, 0xb4, 0xb6, 0x5b, 0x9e, 0xc1, - 0xc7, 0xa8, 0xbb, 0xfd, 0x05, 0x2c, 0xbf, 0x7e, 0x1c, 0x17, 0x85, 0x31, 0x49, 0x34, - 0xb2, 0x62, 0xd5, 0x85, 0x37, 0x54, 0xf1, 0xf1, 0x77, 0x71, 0xcf, 0xb7, 0x50, 0x30, - 0x72, 0x65, 0x57, 0x53, 0xfa, 0x3f, 0x54, 0xec, 0xc5, 0x87, 0xe9, 0xf8, 0x3b, 0x58, - 0x19, 0x16, 0x09, 0x2d, 0xf2, 0x6e, 0x63, 0xe1, 0x89, 0x94, 0xcb, 0x0d, 0xb9, 0x1a, - 0x0b, 0xbd, 0xc7, 0xb6, 0x11, 0x9b, 0x32, 0x22, 0x2a, 0xdf, 0x5e, 0x61, 0xd8, 0xd8, - 0xae, 0x89, 0xda, 0xe4, 0x95, 0x4b, 0x54, 0x81, 0x3b, 0xb3, 0x3f, 0x08, 0xd5, 0x62, - 0xba, 0x51, 0x3f, 0xee, 0x1b, 0x09, 0xc0, 0xfc, 0xd5, 0x16, 0x05, 0x54, 0x19, 0x47, - 0x4d, 0xd7, 0xfd, 0xa0, 0x38, 0xa8, 0x9c, 0x84, 0xea, 0x7b, 0x94, 0x68, 0x28, 0x7f, - 0x0e, 0xb0, 0xc1, 0x0c, 0x4b, 0x13, 0x25, 0x20, 0x19, 0x4d, 0x3d, 0x8d, 0x53, 0x51, - 0xfc, 0x10, 0xd0, 0x9c, 0x15, 0xc8, 0xcc, 0x10, 0x1a, 0xa1, 0x66, 0x3b, 0xbf, 0x17, - 0xb8, 0x41, 0x11, 0xf3, 0x8b, 0xb4, 0x39, 0xf0, 0x73, 0x53, 0xbd, 0xea, 0x35, 0x96, - 0xd1, 0x5e, 0x71, 0x3e, 0x1e, 0x2e, 0x7d, 0x3f, 0x1c, 0x38, 0x31, 0x35, 0xb4, 0x7f, - 0xa7, 0xf8, 0x1f, 0x46, 0xdf, 0x7a, 0x90, 0x2a, 0x40, 0x46, 0x99, 0xec, 0x91, 0x2f, - 0x56, 0x56, 0xc3, 0x5b, 0x85, 0x76, 0x3e, 0x4d, 0xe5, 0x83, 0xae, 0xca, 0xa1, 0xdf, - 0xd5, 0xd2, 0x67, 0x7d, 0x9c, 0x8f, 0xfe, 0xe8, 0x77, 0xf6, 0x3f, 0x40, 0xa5, 0xca, - 0x0d, 0x67, 0xf6, 0xe5, 0x54, 0x12, 0x47, 0x00, 0xf8, 0x05, 0xaf, 0x87, 0x6a, 0xee, - 0xde, 0x53, 0xaa, 0x8b, 0x0f, 0x8e, 0x56, 0x04, 0xa7, 0x3c, 0x30, 0xcb, 0xd0, 0x9d, - 0xad, 0x96, 0x3d, 0x6f, 0x8a, 0x5d, 0xcc, 0x40, 0xde, 0xf4, 0x07, 0x97, 0x34, 0x21, - 0x13, 0xba, 0x20, 0x6f, 0xae, 0x8e, 0xbe, 0x4f, 0x3b, 0xc3, 0xca, 0xf6, 0x92, 0x59, - 0xe4, 0x62, 0xef, 0xf9, 0xba, 0x8b, 0x3f, 0x4b, 0xfa, 0xa1, 0x30, 0x0c, 0x26, 0x92, - 0x5a, 0x87, - ], - script_code: Script(vec![0x63]), - transparent_input: None, - hash_type: 1, - amount: 1969273897303781, - consensus_branch_id: 1991772603, - sighash: [ - 0x63, 0xd1, 0x85, 0x34, 0xde, 0x5f, 0x2d, 0x1c, 0x9e, 0x16, 0x9b, 0x73, 0xf9, 0xc7, - 0x83, 0x71, 0x8a, 0xdb, 0xef, 0x5c, 0x8a, 0x7d, 0x55, 0xb5, 0xe7, 0xa3, 0x7a, 0xff, - 0xa1, 0xdd, 0x3f, 0xf3, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0x0b, 0xbe, 0x32, 0xa5, 0x98, - 0xc2, 0x2a, 0xdf, 0xb4, 0x8c, 0xef, 0x72, 0xba, 0x5d, 0x42, 0x87, 0xc0, 0xce, 0xfb, - 0xac, 0xfd, 0x8c, 0xe1, 0x95, 0xb4, 0x96, 0x3c, 0x34, 0xa9, 0x4b, 0xba, 0x7a, 0x17, - 0x5d, 0xae, 0x4b, 0x04, 0x65, 0xac, 0x65, 0x63, 0x53, 0x70, 0x89, 0x15, 0x09, 0x0f, - 0x47, 0xa0, 0x68, 0xe2, 0x27, 0x43, 0x3f, 0x9e, 0x49, 0xd3, 0xaa, 0x09, 0xe3, 0x56, - 0xd8, 0xd6, 0x6d, 0x0c, 0x01, 0x21, 0xe9, 0x1a, 0x3c, 0x4a, 0xa3, 0xf2, 0x7f, 0xa1, - 0xb6, 0x33, 0x96, 0xe2, 0xb4, 0x1d, 0x09, 0x00, 0x63, 0x53, 0x53, 0x00, 0xac, 0x53, - 0xac, 0x51, 0x4e, 0x97, 0x05, 0x68, 0x02, 0xda, 0x07, 0x1b, 0x97, 0x0d, 0x48, 0x07, - 0x00, 0x01, 0x52, 0xa8, 0x44, 0x55, 0x0b, 0xdc, 0x20, 0x02, 0x00, 0x07, 0x52, 0x52, - 0x6a, 0x65, 0x52, 0x00, 0x52, 0xd7, 0x03, 0x43, 0x02, 0x01, 0x1b, 0x9a, 0x07, 0x66, - 0x20, 0xed, 0xc0, 0x67, 0xff, 0x02, 0x00, 0x00, 0x03, 0x53, 0xe3, 0xb8, 0xa7, 0x1f, - 0xac, 0xe1, 0xc9, 0xf3, 0x77, 0x45, 0xed, 0x36, 0x88, 0x35, 0x29, 0x30, 0x4b, 0xfd, - 0x5a, 0x39, 0x0b, 0x37, 0xbc, 0x5a, 0x34, 0x45, 0x24, 0x1f, 0x03, 0xf6, 0x4a, 0x81, - 0x88, 0x20, 0xdf, 0xed, 0xdd, 0x75, 0x37, 0x51, 0x59, 0xfb, 0xd2, 0x1e, 0xca, 0x98, - 0x72, 0x10, 0x4f, 0x8d, 0x7b, 0x3c, 0x8c, 0x86, 0x97, 0x03, 0xa1, 0xe7, 0x84, 0x8a, - 0x5c, 0x94, 0x1e, 0x45, 0xa9, 0xc7, 0x94, 0x34, 0x46, 0xd0, 0xdc, 0x96, 0x27, 0xcb, - 0x31, 0xf8, 0x0e, 0x7a, 0xa5, 0x96, 0xd4, 0x82, 0x1d, 0xc9, 0x9a, 0x7d, 0x77, 0x7c, - 0xd5, 0x7e, 0x19, 0x48, 0x42, 0xa0, 0x23, 0x47, 0x1f, 0x0f, 0x62, 0x88, 0xa1, 0x50, - 0x64, 0x7b, 0x2a, 0xfe, 0x9d, 0xf7, 0xcc, 0xcf, 0x01, 0xf5, 0xcd, 0xe5, 0xf0, 0x46, - 0x80, 0xbb, 0xfe, 0xd8, 0x7f, 0x6c, 0xf4, 0x29, 0xfb, 0x27, 0xad, 0x6b, 0xab, 0xe7, - 0x91, 0x76, 0x66, 0x11, 0xcf, 0x5b, 0xc2, 0x0e, 0x48, 0xbe, 0xf1, 0x19, 0x25, 0x9b, - 0x9b, 0x8a, 0x0e, 0x39, 0xc3, 0xdf, 0x28, 0xcb, 0x95, 0x82, 0xea, 0x33, 0x86, 0x01, - 0xcd, 0xc4, 0x81, 0xb3, 0x2f, 0xb8, 0x2a, 0xde, 0xeb, 0xb3, 0xda, 0xde, 0x25, 0xd1, - 0xa3, 0xdf, 0x20, 0xc3, 0x7e, 0x71, 0x25, 0x06, 0xb5, 0xd9, 0x96, 0xc4, 0x9a, 0x9f, - 0x0f, 0x30, 0xdd, 0xcb, 0x91, 0xfe, 0x90, 0x04, 0xe1, 0xe8, 0x32, 0x94, 0xa6, 0xc9, - 0x20, 0x3d, 0x94, 0xe8, 0xdc, 0x2c, 0xbb, 0x44, 0x9d, 0xe4, 0x15, 0x50, 0x32, 0x60, - 0x4e, 0x47, 0x99, 0x70, 0x16, 0xb3, 0x04, 0xfd, 0x43, 0x7d, 0x82, 0x35, 0x04, 0x5e, - 0x25, 0x5a, 0x19, 0xb7, 0x43, 0xa0, 0xa9, 0xf2, 0xe3, 0x36, 0xb4, 0x4c, 0xae, 0x30, - 0x7b, 0xb3, 0x98, 0x7b, 0xd3, 0xe4, 0xe7, 0x77, 0xfb, 0xb3, 0x4c, 0x0a, 0xb8, 0xcc, - 0x3d, 0x67, 0x46, 0x6c, 0x0a, 0x88, 0xdd, 0x4c, 0xca, 0xd1, 0x8a, 0x07, 0xa8, 0xd1, - 0x06, 0x8d, 0xf5, 0xb6, 0x29, 0xe5, 0x71, 0x8d, 0x0f, 0x6d, 0xf5, 0xc9, 0x57, 0xcf, - 0x71, 0xbb, 0x00, 0xa5, 0x17, 0x8f, 0x17, 0x5c, 0xac, 0xa9, 0x44, 0xe6, 0x35, 0xc5, - 0x15, 0x9f, 0x73, 0x8e, 0x24, 0x02, 0xa2, 0xd2, 0x1a, 0xa0, 0x81, 0xe1, 0x0e, 0x45, - 0x6a, 0xfb, 0x00, 0xb9, 0xf6, 0x24, 0x16, 0xc8, 0xb9, 0xc0, 0xf7, 0x22, 0x8f, 0x51, - 0x07, 0x29, 0xe0, 0xbe, 0x3f, 0x30, 0x53, 0x13, 0xd7, 0x7f, 0x73, 0x79, 0xdc, 0x2a, - 0xf2, 0x48, 0x69, 0xc6, 0xc7, 0x4e, 0xe4, 0x47, 0x14, 0x98, 0x86, 0x1d, 0x19, 0x2f, - 0x0f, 0xf0, 0xf5, 0x08, 0x28, 0x5d, 0xab, 0x6b, 0x6a, 0x36, 0xcc, 0xf7, 0xd1, 0x22, - 0x56, 0xcc, 0x76, 0xb9, 0x55, 0x03, 0x72, 0x0a, 0xc6, 0x72, 0xd0, 0x82, 0x68, 0xd2, - 0xcf, 0x77, 0x73, 0xb6, 0xba, 0x2a, 0x5f, 0x66, 0x48, 0x47, 0xbf, 0x70, 0x7f, 0x2f, - 0xc1, 0x0c, 0x98, 0xf2, 0xf0, 0x06, 0xec, 0x22, 0xcc, 0xb5, 0xa8, 0xc8, 0xb7, 0xc4, - 0x0c, 0x7c, 0x2d, 0x49, 0xa6, 0x63, 0x9b, 0x9f, 0x2c, 0xe3, 0x3c, 0x25, 0xc0, 0x4b, - 0xc4, 0x61, 0xe7, 0x44, 0xdf, 0xa5, 0x36, 0xb0, 0x0d, 0x94, 0xba, 0xdd, 0xf4, 0xf4, - 0xd1, 0x40, 0x44, 0xc6, 0x95, 0xa3, 0x38, 0x81, 0x47, 0x7d, 0xf1, 0x24, 0xf0, 0xfc, - 0xf2, 0x06, 0xa9, 0xfb, 0x2e, 0x65, 0xe3, 0x04, 0xcd, 0xbf, 0x0c, 0x4d, 0x23, 0x90, - 0x17, 0x0c, 0x13, 0x0a, 0xb8, 0x49, 0xc2, 0xf2, 0x2b, 0x5c, 0xdd, 0x39, 0x21, 0x64, - 0x0c, 0x8c, 0xf1, 0x97, 0x6a, 0xe1, 0x01, 0x0b, 0x0d, 0xfd, 0x9c, 0xb2, 0x54, 0x3e, - 0x45, 0xf9, 0x97, 0x49, 0xcc, 0x4d, 0x61, 0xf2, 0xe8, 0xaa, 0xbf, 0xe9, 0x8b, 0xd9, - 0x05, 0xfa, 0x39, 0x95, 0x1b, 0x33, 0xea, 0x76, 0x9c, 0x45, 0xab, 0x95, 0x31, 0xc5, - 0x72, 0x09, 0x86, 0x2a, 0xd1, 0x2f, 0xd7, 0x6b, 0xa4, 0x80, 0x7e, 0x65, 0x41, 0x7b, - 0x6c, 0xd1, 0x2f, 0xa8, 0xec, 0x91, 0x6f, 0x01, 0x3e, 0xbb, 0x87, 0x06, 0xa9, 0x6e, - 0xff, 0xed, 0xa0, 0x6c, 0x4b, 0xe2, 0x4b, 0x04, 0x84, 0x63, 0x92, 0xe9, 0xd1, 0xe6, - 0x93, 0x0e, 0xae, 0x01, 0xfa, 0x21, 0xfb, 0xd7, 0x00, 0x58, 0x3f, 0xb5, 0x98, 0xb9, - 0x2c, 0x8f, 0x4e, 0xb8, 0xa6, 0x1a, 0xa6, 0x23, 0x5d, 0xb6, 0x0f, 0x28, 0x41, 0xcf, - 0x3a, 0x1c, 0x6a, 0xb5, 0x4c, 0x67, 0x06, 0x68, 0x44, 0x71, 0x1d, 0x09, 0x1e, 0xb9, - 0x31, 0xa1, 0xbd, 0x62, 0x81, 0xae, 0xdf, 0x2a, 0x0e, 0x8f, 0xab, 0x18, 0x81, 0x72, - 0x02, 0xa9, 0xbe, 0x06, 0x40, 0x2e, 0xd9, 0xcc, 0x72, 0x0c, 0x16, 0xbf, 0xe8, 0x81, - 0xe4, 0xdf, 0x42, 0x55, 0xe8, 0x7a, 0xfb, 0x7f, 0xc6, 0x2f, 0x38, 0x11, 0x6b, 0xbe, - 0x03, 0xcd, 0x8a, 0x3c, 0xb1, 0x1a, 0x27, 0xd5, 0x68, 0x41, 0x47, 0x82, 0xf4, 0x7b, - 0x1a, 0x44, 0xc9, 0x7c, 0x68, 0x04, 0x67, 0x69, 0x4b, 0xc9, 0x70, 0x9d, 0x32, 0x91, - 0x6c, 0x97, 0xe8, 0x00, 0x6c, 0xbb, 0x07, 0xba, 0x0e, 0x41, 0x80, 0xa3, 0x73, 0x80, - 0x38, 0xc3, 0x74, 0xc4, 0xcc, 0xe8, 0xf3, 0x29, 0x59, 0xaf, 0xb2, 0x5f, 0x30, 0x3f, - 0x58, 0x15, 0xc4, 0x53, 0x31, 0x24, 0xac, 0xf9, 0xd1, 0x89, 0x40, 0xe7, 0x75, 0x22, - 0xac, 0x5d, 0xc4, 0xb9, 0x57, 0x0a, 0xae, 0x8f, 0x47, 0xb7, 0xf5, 0x7f, 0xd8, 0x76, - 0x7b, 0xea, 0x1a, 0x24, 0xae, 0x7b, 0xed, 0x65, 0xb4, 0xaf, 0xdc, 0x8f, 0x12, 0x78, - 0xc3, 0x0e, 0x2d, 0xb9, 0x8f, 0xd1, 0x72, 0x73, 0x0a, 0xc6, 0xbb, 0xed, 0x4f, 0x11, - 0x27, 0xcd, 0x32, 0xb0, 0x4a, 0x95, 0xb2, 0x05, 0x52, 0x6c, 0xfc, 0xb4, 0xc4, 0xe1, - 0xcc, 0x95, 0x51, 0x75, 0xb3, 0xe8, 0xde, 0x1f, 0x5d, 0x81, 0xb1, 0x86, 0x69, 0x69, - 0x23, 0x50, 0xaa, 0xa1, 0xa1, 0xd7, 0x97, 0x61, 0x75, 0x82, 0xe5, 0x4d, 0x7a, 0x5b, - 0x57, 0xa6, 0x83, 0xb3, 0x2f, 0xb1, 0x09, 0x80, 0x62, 0xda, 0xd7, 0xb0, 0xc2, 0xeb, - 0x51, 0x8f, 0x68, 0x62, 0xe8, 0x3d, 0xb2, 0x5e, 0x3d, 0xba, 0xf7, 0xae, 0xd5, 0x04, - 0xde, 0x93, 0x2a, 0xcb, 0x99, 0xd7, 0x35, 0x99, 0x2c, 0xe6, 0x2b, 0xae, 0x9e, 0xf8, - 0x93, 0xff, 0x6a, 0xcc, 0x0f, 0xfc, 0xf8, 0xe3, 0x48, 0x3e, 0x14, 0x6b, 0x9d, 0x49, - 0xdd, 0x8c, 0x78, 0x35, 0xf4, 0x3a, 0x37, 0xdc, 0xa0, 0x78, 0x7e, 0x3e, 0xc9, 0xf6, - 0x60, 0x52, 0x23, 0xd5, 0xba, 0x7a, 0xe0, 0xab, 0x90, 0x25, 0xb7, 0x3b, 0xc0, 0x3f, - 0x7f, 0xac, 0x36, 0xc0, 0x09, 0xa5, 0x6d, 0x4d, 0x95, 0xd1, 0xe8, 0x1d, 0x3b, 0x3e, - 0xbc, 0xa7, 0xe5, 0x4c, 0xc1, 0xa1, 0x2d, 0x12, 0x7b, 0x57, 0xc8, 0x13, 0x89, 0x76, - 0xe7, 0x91, 0x01, 0x3b, 0x01, 0x5f, 0x06, 0xa6, 0x24, 0xf5, 0x21, 0xb6, 0xee, 0x04, - 0xec, 0x98, 0x08, 0x93, 0xc7, 0xe5, 0xe0, 0x1a, 0x33, 0x62, 0x03, 0x59, 0x40, 0x94, - 0xf8, 0x28, 0x33, 0xd7, 0x44, 0x27, 0x88, 0x00, 0x84, 0xd3, 0x58, 0x63, 0xc8, 0xe7, - 0xeb, 0xb5, 0xc9, 0xee, 0xd9, 0x8e, 0x72, 0x57, 0x2e, 0xc4, 0x0c, 0x79, 0xb2, 0x66, - 0x23, 0xb5, 0x80, 0x22, 0xf4, 0x89, 0xb0, 0x89, 0x3d, 0x88, 0xbe, 0x63, 0xf3, 0xf8, - 0xc0, 0xd2, 0x32, 0x49, 0xeb, 0xcd, 0xe1, 0x3d, 0xb9, 0x31, 0x29, 0x41, 0xc3, 0x6c, - 0x1d, 0x1c, 0xbc, 0xab, 0xac, 0x0c, 0x78, 0xcb, 0x3b, 0x19, 0x12, 0xdb, 0x0d, 0xcb, - 0xfe, 0x18, 0x93, 0xd9, 0xb5, 0x1b, 0xe4, 0xaf, 0x1d, 0x00, 0x0b, 0xac, 0x1a, 0xd0, - 0xa3, 0xae, 0x2c, 0xe1, 0xe7, 0x32, 0x25, 0xfb, 0x11, 0x4d, 0x05, 0xaf, 0x4c, 0xef, - 0xc0, 0x6e, 0x87, 0x5f, 0x07, 0x4f, 0xfe, 0xae, 0x0c, 0xba, 0x7d, 0xa3, 0xa5, 0x16, - 0xc1, 0x73, 0xbe, 0x1c, 0x51, 0x33, 0x23, 0xe1, 0x19, 0xf6, 0x35, 0xe8, 0x20, 0x9a, - 0x07, 0x4b, 0x21, 0x6b, 0x70, 0x23, 0xfa, 0xdc, 0x2d, 0x25, 0x94, 0x9c, 0x90, 0x03, - 0x7e, 0x71, 0xe3, 0xe5, 0x50, 0x72, 0x6d, 0x21, 0x0a, 0x2c, 0x68, 0x83, 0x42, 0xe5, - 0x24, 0x40, 0x63, 0x5e, 0x9c, 0xc1, 0x4a, 0xfe, 0x10, 0x10, 0x26, 0x21, 0xa9, 0xc9, - 0xac, 0xcb, 0x78, 0x2e, 0x9e, 0x4a, 0x5f, 0xa8, 0x7f, 0x0a, 0x95, 0x6f, 0x5b, 0x85, - 0x50, 0x99, 0x60, 0x28, 0x5c, 0x22, 0x62, 0x7c, 0x59, 0x48, 0x3a, 0x5a, 0x4c, 0x28, - 0xcc, 0xe4, 0xb1, 0x56, 0xe5, 0x51, 0x40, 0x6a, 0x7e, 0xe8, 0x35, 0x56, 0x56, 0xa2, - 0x1e, 0x43, 0xe3, 0x8c, 0xe1, 0x29, 0xfd, 0xad, 0xb7, 0x59, 0xed, 0xdf, 0xa0, 0x8f, - 0x00, 0xfc, 0x8e, 0x56, 0x7c, 0xef, 0x93, 0xc6, 0x79, 0x2d, 0x01, 0xdf, 0x05, 0xe6, - 0xd5, 0x80, 0xf4, 0xd5, 0xd4, 0x8d, 0xf0, 0x42, 0x45, 0x1a, 0x33, 0x59, 0x0d, 0x3e, - 0x8c, 0xf4, 0x9b, 0x26, 0x27, 0x21, 0x8f, 0x0c, 0x29, 0x2f, 0xa6, 0x6a, 0xda, 0x94, - 0x5f, 0xa5, 0x5b, 0xb2, 0x35, 0x48, 0xe3, 0x3a, 0x83, 0xa5, 0x62, 0x95, 0x7a, 0x31, - 0x49, 0xa9, 0x93, 0xcc, 0x47, 0x23, 0x62, 0x29, 0x87, 0x36, 0xa8, 0xb7, 0x78, 0xd9, - 0x7c, 0xe4, 0x23, 0x01, 0x3d, 0x64, 0xb3, 0x2c, 0xd1, 0x72, 0xef, 0xa5, 0x51, 0xbf, - 0x7f, 0x36, 0x8f, 0x04, 0xbd, 0xae, 0xc6, 0x09, 0x1a, 0x30, 0x04, 0xa7, 0x57, 0x59, - 0x8b, 0x80, 0x1d, 0xcf, 0x67, 0x5c, 0xb8, 0x3e, 0x43, 0xa5, 0x3a, 0xe8, 0xb2, 0x54, - 0xd3, 0x33, 0xbc, 0xda, 0x20, 0xd4, 0x81, 0x7d, 0x34, 0x77, 0xab, 0xfb, 0xa2, 0x5b, - 0xb8, 0x3d, 0xf5, 0x94, 0x9c, 0x12, 0x6f, 0x14, 0x9b, 0x1d, 0x99, 0x34, 0x1e, 0x4e, - 0x6f, 0x91, 0x20, 0xf4, 0xd4, 0x1e, 0x62, 0x91, 0x85, 0x00, 0x2c, 0x72, 0xc0, 0x12, - 0xc4, 0x14, 0xd2, 0x38, 0x2a, 0x6d, 0x47, 0xc7, 0xb3, 0xde, 0xab, 0xa7, 0x70, 0xc4, - 0x00, 0xca, 0x96, 0xb2, 0x81, 0x4f, 0x6b, 0x26, 0xc3, 0xef, 0x17, 0x42, 0x9f, 0x1a, - 0x98, 0xc8, 0x5d, 0x83, 0xdb, 0x20, 0xef, 0xad, 0x48, 0xbe, 0x89, 0x96, 0xfb, 0x1b, - 0xff, 0x59, 0x1e, 0xff, 0xf3, 0x60, 0xfe, 0x11, 0x99, 0x05, 0x6c, 0x56, 0xe5, 0xfe, - 0xec, 0x61, 0xa7, 0xb8, 0xb9, 0xf6, 0x99, 0xd6, 0x01, 0x2c, 0x28, 0x49, 0x23, 0x2f, - 0x32, 0x9f, 0xef, 0x95, 0xc7, 0xaf, 0x37, 0x00, 0x98, 0xff, 0xe4, 0x91, 0x8e, 0x0c, - 0xa1, 0xdf, 0x47, 0xf2, 0x75, 0x86, 0x7b, 0x73, 0x9e, 0x0a, 0x51, 0x4d, 0x32, 0x09, - 0x32, 0x5e, 0x21, 0x70, 0x45, 0x92, 0x7b, 0x47, 0x9c, 0x1c, 0xe2, 0xe5, 0xd5, 0x4f, - 0x25, 0x48, 0x8c, 0xad, 0x15, 0x13, 0xe3, 0xf4, 0x4a, 0x21, 0x26, 0x6c, 0xfd, 0x84, - 0x16, 0x33, 0x32, 0x7d, 0xee, 0x6c, 0xf8, 0x10, 0xfb, 0xf7, 0x39, 0x3e, 0x31, 0x7d, - 0x9e, 0x53, 0xd1, 0xbe, 0x1d, 0x5a, 0xe7, 0x83, 0x9b, 0x66, 0xb9, 0x43, 0xb9, 0xed, - 0x18, 0xf2, 0xc5, 0x30, 0xe9, 0x75, 0x42, 0x23, 0x32, 0xc3, 0x43, 0x9c, 0xce, 0x49, - 0xa2, 0x9f, 0x2a, 0x33, 0x6a, 0x48, 0x51, 0x26, 0x3c, 0x5e, 0x9b, 0xd1, 0x3d, 0x73, - 0x11, 0x09, 0xe8, 0x44, 0xb7, 0xf8, 0xc3, 0x92, 0xa5, 0xc1, 0xdc, 0xaa, 0x2a, 0xe5, - 0xf5, 0x0f, 0xf6, 0x3f, 0xab, 0x97, 0x65, 0xe0, 0x16, 0x70, 0x2c, 0x35, 0xa6, 0x7c, - 0xd7, 0x36, 0x4d, 0x3f, 0xab, 0x55, 0x2f, 0xb3, 0x49, 0xe3, 0x5c, 0x15, 0xc5, 0x02, - 0x50, 0x45, 0x3f, 0xd1, 0x8f, 0x7b, 0x85, 0x59, 0x92, 0x63, 0x2e, 0x2c, 0x76, 0xc0, - 0xfb, 0xf1, 0xef, 0x96, 0x3e, 0xa8, 0x0e, 0x32, 0x23, 0xde, 0x32, 0x77, 0xbc, 0x55, - 0x92, 0x51, 0x72, 0x58, 0x29, 0xec, 0x03, 0xf2, 0x13, 0xba, 0x89, 0x55, 0xca, 0xb2, - 0x82, 0x2f, 0xf2, 0x1a, 0x9b, 0x0a, 0x49, 0x04, 0xd6, 0x68, 0xfc, 0xd7, 0x72, 0x24, - 0xbd, 0xe3, 0xdd, 0x01, 0xf6, 0xff, 0xc4, 0x82, 0x8f, 0x6b, 0x64, 0x23, 0x0b, 0x35, - 0xc6, 0xa0, 0x49, 0x87, 0x34, 0x94, 0x27, 0x6e, 0xa1, 0xd7, 0xed, 0x5e, 0x92, 0xcb, - 0x4f, 0x90, 0xba, 0x83, 0xa9, 0xe4, 0x96, 0x01, 0xb1, 0x94, 0x04, 0x2f, 0x29, 0x00, - 0xd9, 0x9d, 0x31, 0x2d, 0x7b, 0x70, 0x50, 0x8c, 0xf1, 0x76, 0x06, 0x6d, 0x15, 0x4d, - 0xbe, 0x96, 0xef, 0x9d, 0x43, 0x67, 0xe4, 0xc8, 0x40, 0xe4, 0xa1, 0x7b, 0x5e, 0x51, - 0x22, 0xe8, 0xeb, 0xe2, 0x15, 0x8a, 0x3c, 0x5f, 0x4c, 0xba, 0xe2, 0x1e, 0xa3, 0xfa, - 0x1a, 0xe6, 0xc2, 0x5a, 0x94, 0x62, 0xeb, 0xcb, 0xb0, 0xfd, 0x5f, 0x14, 0x55, 0x4b, - 0xc9, 0x77, 0x47, 0xc3, 0x3e, 0x34, 0xda, 0x90, 0xc8, 0x16, 0xd8, 0xd0, 0xd5, 0x0b, - 0xfe, 0x37, 0x61, 0x8c, 0x58, 0x12, 0x89, 0x14, 0x84, 0xfa, 0x25, 0x93, 0x22, 0xc1, - 0x50, 0x92, 0xd4, 0x15, 0x5d, 0x86, 0x96, 0xd6, 0xf1, 0x2f, 0x24, 0xfd, 0x36, 0x44, - 0x96, 0xb3, 0xbe, 0x08, 0x71, 0xca, 0x3d, 0xd9, 0x62, 0x53, 0x48, 0xa6, 0x14, 0xb5, - 0x9b, 0xde, 0x45, 0x88, 0x56, 0x49, 0xba, 0xe3, 0x6d, 0xe3, 0x4d, 0xef, 0x8f, 0xce, - 0xc8, 0x53, 0x43, 0x47, 0x5d, 0x97, 0x6a, 0xe1, 0xe9, 0xb2, 0x78, 0x29, 0xce, 0x2a, - 0xc5, 0xef, 0xd0, 0xb3, 0x99, 0xa8, 0xb4, 0x48, 0xbe, 0x65, 0x04, 0x29, 0x4e, 0xe6, - 0xb3, 0xc1, 0xc6, 0xa5, 0x34, 0x2d, 0x7c, 0x01, 0xae, 0x9d, 0x8a, 0xd3, 0x07, 0x0c, - 0x2b, 0x1a, 0x91, 0x57, 0x3a, 0xf5, 0xe0, 0xc5, 0xe4, 0xcb, 0xbf, 0x4a, 0xcd, 0xc6, - 0xb5, 0x4c, 0x92, 0x72, 0x20, 0x0d, 0x99, 0x70, 0x25, 0x0c, 0x17, 0xc1, 0x03, 0x6f, - 0x06, 0x08, 0x5c, 0x41, 0x85, 0x8e, 0xd3, 0xa0, 0xc4, 0x81, 0x50, 0xbc, 0x69, 0x7e, - 0x4a, 0x69, 0x5f, 0xef, 0x33, 0x5f, 0x7a, 0xd0, 0x7e, 0x1a, 0x46, 0xdc, 0x76, 0x7f, - 0xf8, 0x22, 0xdb, 0x70, 0xe6, 0x66, 0x90, 0x80, 0xb9, 0x81, 0x6b, 0x22, 0x32, 0xc8, - 0x1a, 0x4c, 0x66, 0xcc, 0x58, 0x6a, 0xbf, 0xe1, 0xea, 0xa8, 0xca, 0x6c, 0xf4, 0x1f, - 0xc3, 0x0e, 0xb8, 0xdc, 0x57, 0xc3, 0x7a, 0x3c, 0x39, 0xc5, 0x9c, 0x94, 0x23, 0x2d, - 0xf9, 0xd3, 0x88, 0xdb, 0xfa, 0x35, 0xc2, 0xcd, 0x5c, 0x75, 0xf3, 0x28, 0xe9, 0xfe, - 0xa7, 0x8f, 0x65, 0x56, 0x8f, 0x2b, 0xb9, 0x34, 0xc8, 0x2c, 0x41, 0x42, 0xda, 0x69, - 0xd1, 0x2c, 0xa7, 0xde, 0x9a, 0x7d, 0xf7, 0x06, 0x40, 0x0e, 0xc7, 0x98, 0x78, 0xd8, - 0x68, 0xe1, 0x7e, 0x8f, 0x71, 0xea, 0x31, 0x49, 0x5a, 0x8b, 0xae, 0x7b, 0xdc, 0x2e, - 0x48, 0xb5, 0x11, 0x87, 0x71, 0xc2, 0xfc, 0xa0, 0x78, 0xcc, 0xa1, 0xfc, 0xe0, 0xd7, - 0xef, 0x0a, 0xf3, 0x47, 0x8c, 0xf3, 0x6f, 0x69, 0xe8, 0x5a, 0x41, 0xdd, 0x29, 0xb4, - 0x29, 0x4a, 0x65, 0xd3, 0xe0, 0x55, 0xff, 0x71, 0x8d, 0xd9, 0xdc, 0x8c, 0x75, 0xe7, - 0xe5, 0xb2, 0xef, 0xe4, 0x42, 0x63, 0x73, 0x71, 0xb7, 0xc4, 0x8f, 0x6e, 0xe9, 0x9e, - 0x3e, 0xa3, 0x8a, 0x4b, 0x0f, 0x2f, 0x67, 0xfc, 0x2b, 0x90, 0x8c, 0xda, 0x65, 0x7e, - 0xae, 0x75, 0x4e, 0x03, 0x7e, 0x26, 0x2e, 0x9a, 0x9f, 0x9b, 0xd7, 0xec, 0x42, 0x67, - 0xed, 0x8e, 0x96, 0x93, 0x0e, 0x10, 0x84, 0x78, 0x3c, 0x37, 0xd6, 0xf9, 0xdd, 0x15, - 0xfd, 0x29, 0xf4, 0xcc, 0x47, 0x7e, 0x66, 0xf1, 0x30, 0xd6, 0x30, 0x43, 0x0d, 0xcc, - 0x01, 0x04, 0x89, 0x9b, 0x4f, 0x9f, 0x46, 0xeb, 0x09, 0x0e, 0xf7, 0xfc, 0x90, 0xb4, - 0x79, 0xab, 0xf6, 0x1f, 0x93, 0x95, 0x5e, 0xe0, 0x0e, 0x6a, 0x18, 0x48, 0xf1, 0xab, - 0x14, 0xad, 0x33, 0x4f, 0x2b, 0x68, 0x03, 0x58, 0x08, 0xcd, 0xf1, 0xbb, 0x9e, 0x9d, - 0x9a, 0x81, 0x6b, 0xaf, 0x72, 0x8a, 0x95, 0x5b, 0x96, 0x0b, 0x77, 0x01, 0xfa, 0x62, - 0x66, 0x87, 0xdc, 0x3c, 0x9c, 0xba, 0x64, 0x63, 0x37, 0xb5, 0x3e, 0x29, 0x81, 0x6e, - 0x94, 0x82, 0xdd, 0xf5, 0x57, 0x8a, 0x87, 0x68, 0xaa, 0xe4, 0x77, 0xfc, 0xe4, 0x10, - 0xac, 0x2d, 0x5d, 0xe6, 0x09, 0x58, 0x61, 0xc1, 0x11, 0xd7, 0xfe, 0xb3, 0xe6, 0xbb, - 0x4f, 0xbb, 0x5a, 0x54, 0x95, 0x54, 0x95, 0x97, 0x27, 0x98, 0x35, 0x0a, 0x25, 0x3f, - 0x05, 0xf6, 0x6c, 0x2e, 0xcf, 0xcb, 0xc0, 0xed, 0x43, 0xf5, 0xec, 0x2e, 0x6d, 0x8d, - 0xba, 0x15, 0xa5, 0x12, 0x54, 0xd9, 0x7b, 0x18, 0x21, 0x10, 0x7c, 0x07, 0xdd, 0x9a, - 0x16, 0xef, 0x84, 0x06, 0xf9, 0x43, 0xe2, 0x82, 0xb9, 0x5d, 0x4b, 0x36, 0x25, 0x30, - 0xc9, 0x13, 0xd6, 0xba, 0x42, 0x1d, 0xf6, 0x02, 0x7d, 0xe5, 0xaf, 0x1e, 0x47, 0x45, - 0xd5, 0x86, 0x81, 0x06, 0x95, 0x4b, 0xe6, 0xc1, 0x96, 0x27, 0x80, 0xa2, 0x94, 0x10, - 0x72, 0xe9, 0x51, 0x31, 0xb1, 0x67, 0x9d, 0xf0, 0x63, 0x76, 0x25, 0x04, 0x2c, 0x37, - 0xd4, 0x8f, 0xfb, 0x15, 0x2e, 0x5e, 0xbc, 0x18, 0x5c, 0x8a, 0x2b, 0x7d, 0x43, 0x85, - 0xf1, 0xc9, 0x5a, 0xf9, 0x37, 0xdf, 0x78, 0xdf, 0xd8, 0x75, 0x7f, 0xab, 0x43, 0x49, - 0x68, 0xb0, 0xb5, 0x7c, 0x66, 0x57, 0x44, 0x68, 0xf1, 0x60, 0xb4, 0x47, 0xac, 0x82, - 0x21, 0xe5, 0x06, 0x06, 0x76, 0xa8, 0x42, 0xa1, 0xc6, 0xb7, 0x17, 0x2d, 0xd3, 0x34, - 0x0f, 0x76, 0x40, 0x70, 0xab, 0x1f, 0xe0, 0x91, 0xc5, 0xc7, 0x4c, 0x95, 0xa5, 0xdc, - 0x04, 0x33, 0x90, 0x72, 0x3a, 0x4c, 0x12, 0x7d, 0xa1, 0x4c, 0xdd, 0xe1, 0xdc, 0x26, - 0x75, 0xa6, 0x23, 0x40, 0xb3, 0xe6, 0xaf, 0xd0, 0x52, 0x2a, 0x31, 0xde, 0x26, 0xe7, - 0xd1, 0xec, 0x3a, 0x9c, 0x8a, 0x09, 0x1f, 0xfd, 0xc7, 0x5b, 0x7e, 0xcf, 0xdc, 0x7c, - 0x12, 0x99, 0x5a, 0x5e, 0x37, 0xce, 0x34, 0x88, 0xbd, 0x29, 0xf8, 0x62, 0x9d, 0x68, - 0xf6, 0x96, 0x49, 0x24, 0x48, 0xdd, 0x52, 0x66, 0x97, 0x47, 0x6d, 0xc0, 0x61, 0x34, - 0x6e, 0xbe, 0x3f, 0x67, 0x72, 0x17, 0xff, 0x9c, 0x60, 0xef, 0xce, 0x94, 0x3a, 0xf2, - 0x8d, 0xfd, 0x3f, 0x9e, 0x59, 0x69, 0x25, 0x98, 0xa6, 0x04, 0x7c, 0x23, 0xc4, 0xc0, - 0x14, 0x00, 0xf1, 0xab, 0x57, 0x30, 0xea, 0xc0, 0xae, 0x8d, 0x58, 0x43, 0xd5, 0x05, - 0x1c, 0x37, 0x62, 0x40, 0x17, 0x2a, 0xf2, 0x18, 0xd7, 0xa1, 0xec, 0xfe, 0x65, 0xb4, - 0xf7, 0x51, 0x00, 0x63, 0x89, 0x83, 0xc1, 0x4d, 0xe4, 0x97, 0x47, 0x55, 0xda, 0xde, - 0x80, 0x18, 0xc9, 0xb8, 0xf4, 0x54, 0x3f, 0xb0, 0x95, 0x96, 0x15, 0x13, 0xe6, 0x7c, - 0x61, 0xdb, 0xc5, 0x9c, 0x60, 0x7f, 0x9b, 0x51, 0xf8, 0xd0, 0x9b, 0xdc, 0xad, 0x28, - 0xbc, 0xfb, 0x9e, 0x5d, 0x27, 0x44, 0xea, 0x88, 0x48, 0xb2, 0x62, 0x3a, 0xc0, 0x7f, - 0x8e, 0xf6, 0x1a, 0x81, 0xa3, 0x59, 0x10, 0xb8, 0xa1, 0xba, 0xf3, 0x9a, 0x91, 0x9a, - 0x7b, 0x60, 0xbc, 0x60, 0x4d, 0x63, 0x18, 0x5f, 0x75, 0x92, 0x21, 0xd8, 0x47, 0xcc, - 0x54, 0xa2, 0x27, 0x65, 0xa4, 0xc3, 0x34, 0x75, 0xb5, 0x79, 0x1e, 0x9a, 0xf3, 0x27, - 0x1f, 0xc8, 0xd9, 0x35, 0x06, 0x67, 0x09, 0x0d, 0x81, 0x84, 0xec, 0x50, 0x52, 0x2d, - 0x80, 0x4f, 0x23, 0xc4, 0xfb, 0x44, 0xff, 0xa4, 0x81, 0xbc, 0x92, 0xae, 0x40, 0x8d, - 0x1b, 0x9f, 0x2b, 0x13, 0x19, 0x04, 0xf9, 0x70, 0x5c, 0x59, 0xe2, 0xf4, 0xbd, 0xe7, - 0xa3, 0xb2, 0xc0, 0x85, 0xd9, 0x3f, 0xd2, 0xab, 0xc5, 0xe1, 0x4d, 0x16, 0x30, 0x01, - 0xa1, 0x2f, 0x51, 0x93, 0x8d, 0x02, 0x1a, 0xfa, 0x92, 0x23, 0x9b, 0x87, 0x3d, 0xc6, - 0xc3, 0x57, 0xea, 0xa8, 0xaf, 0x4e, 0xe6, 0xd0, 0x05, 0x40, 0x65, 0x7f, 0xe3, 0x29, - 0x14, 0x10, 0x3b, 0x5d, 0x98, 0xf6, 0x8b, 0xd3, 0xe2, 0xb5, 0x35, 0x9f, 0x08, 0xcc, - 0xd8, 0x8d, 0x0c, 0x81, 0x1e, 0x4c, 0x31, 0xfb, 0xb4, 0x9f, 0x3a, 0x90, 0xbb, 0xd0, - 0x5d, 0xce, 0x62, 0xf3, 0x44, 0xe7, 0x07, 0x75, 0x93, 0x15, 0x9a, 0xe3, 0x50, 0x50, - 0xb0, 0x4c, 0x9e, 0x6b, 0x86, 0xbc, 0x43, 0x2d, 0xc8, 0xb0, 0x48, 0xc7, 0x3c, 0x00, - 0x18, 0xca, 0x5b, 0x69, 0x41, 0x12, 0x97, 0x73, 0x2a, 0x4e, 0x1a, 0xa9, 0x9a, 0x92, - 0x8c, 0x71, 0xe7, 0xa2, 0x4f, 0xd2, 0x77, 0x85, 0x6a, 0xa4, 0x25, 0x01, 0xe5, 0x1b, - 0x01, 0x2a, 0xea, 0x94, 0x46, 0xa2, 0x10, 0x4e, 0x93, 0xf8, 0x15, 0xa0, 0xb3, 0xa2, - 0x9b, 0x45, 0x83, 0x14, 0xf3, 0xd8, 0xbe, 0x2b, 0x98, 0x23, 0xd3, 0x42, 0xf4, 0x62, - 0x13, 0xe9, 0x42, 0xa7, 0xe1, 0x9a, 0x46, 0xe9, 0x70, 0xb5, 0xc5, 0x06, 0x70, 0x84, - 0x30, 0x31, 0x7b, 0x1b, 0xb3, 0xb3, 0x5d, 0xf6, 0x8a, 0xe3, 0x3a, 0x49, 0x26, 0xa0, - 0x3e, 0x6b, 0xfe, 0xb5, 0x51, 0x04, 0x16, 0xfc, 0xbb, 0x05, 0x24, 0xc9, 0xca, 0x50, - 0x74, 0x15, 0x6c, 0xc5, 0xa5, 0xd6, 0xfe, 0x1c, 0x99, 0x5e, 0xdc, 0x60, 0xa2, 0xf5, - 0x50, 0x41, 0x1a, 0xa4, 0x1e, 0x3d, 0xa3, 0xbd, 0xcf, 0x64, 0xbc, 0xf0, 0x4a, 0x05, - 0x10, 0x57, 0x1b, 0x93, 0x6d, 0x47, 0xe5, 0x5c, 0xec, 0x03, 0x30, 0x00, 0x8d, 0xfe, - 0x73, 0x56, 0x34, 0x04, 0xf0, 0x47, 0xd7, 0xf3, 0xa8, 0xa3, 0xd7, 0x74, 0x3b, 0xc5, - 0x54, 0x95, 0x52, 0x10, 0xf1, 0xeb, 0x0d, 0x08, 0x59, 0x9e, 0xa7, 0x7d, 0x5f, 0x97, - 0x4d, 0x87, 0x17, 0x6d, 0x37, 0xd9, 0x8b, 0x9c, 0x0a, 0xd4, 0x40, 0x40, 0x72, 0x09, - 0xed, 0x6a, 0x9f, 0x08, 0x46, 0x4d, 0x56, 0x55, 0x93, 0xe1, 0xa6, 0x3b, 0x93, 0x85, - 0x36, 0xb4, 0x92, 0x44, 0xe9, 0x7d, - ], - script_code: Script(vec![]), - transparent_input: Some(1), - hash_type: 2, - amount: 652655344020909, - consensus_branch_id: 1991772603, - sighash: [ - 0xbb, 0xe6, 0xd8, 0x4f, 0x57, 0xc5, 0x6b, 0x29, 0xb9, 0x14, 0xc6, 0x94, 0xba, 0xac, - 0xcb, 0x89, 0x12, 0x97, 0xe9, 0x61, 0xde, 0x3e, 0xb4, 0x6c, 0x68, 0xe3, 0xc8, 0x9c, - 0x47, 0xb1, 0xa1, 0xdb, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0x46, 0xcf, 0x28, 0x9b, 0x7d, - 0x89, 0x13, 0x07, 0xbb, 0xa3, 0x70, 0x54, 0xcf, 0x91, 0xb3, 0x1f, 0xc8, 0x2f, 0x74, - 0xd5, 0xfc, 0xc0, 0x00, 0x94, 0x2e, 0xde, 0x91, 0x18, 0x25, 0xf5, 0x3f, 0xe6, 0x09, - 0x68, 0x6f, 0x46, 0x00, 0x23, 0xb1, 0xe9, 0xbc, 0x00, 0xbd, 0xe8, 0x95, 0xd1, 0x23, - 0x8f, 0xad, 0x04, 0xab, 0xa9, 0x88, 0x99, 0x66, 0x7d, 0x01, 0x00, 0x04, 0xea, 0x42, - 0x71, 0x76, 0x09, 0x84, 0x13, 0x90, 0x59, 0x18, 0xee, 0x21, 0x3d, 0x4e, 0xc1, 0x27, - 0x94, 0x74, 0x2d, 0x19, 0xf6, 0x7d, 0x6f, 0x86, 0xce, 0xf7, 0xe6, 0x98, 0x2e, 0x88, - 0x41, 0x71, 0x28, 0x73, 0xa0, 0x1d, 0x92, 0x51, 0xd8, 0xc8, 0x60, 0xc0, 0x41, 0x52, - 0x5b, 0x3b, 0xf4, 0xe3, 0xa2, 0xeb, 0x92, 0x72, 0x81, 0x5c, 0x75, 0x86, 0x76, 0x84, - 0x28, 0xb4, 0xc2, 0xb2, 0x5e, 0x37, 0x45, 0xf0, 0x09, 0xc5, 0xdc, 0xe2, 0x0b, 0x69, - 0xd5, 0xd7, 0xc4, 0x3c, 0xeb, 0x73, 0x6b, 0x68, 0x31, 0xe8, 0xc1, 0x10, 0xf1, 0x6c, - 0xfd, 0xb3, 0xa4, 0x67, 0xe9, 0x41, 0x4c, 0x00, 0xec, 0xf1, 0x37, 0x31, 0x50, 0x08, - 0x94, 0x55, 0x56, 0x78, 0xc4, 0x97, 0xfa, 0xba, 0x9a, 0x95, 0xd0, 0x1c, 0xc4, 0x64, - 0x39, 0x0f, 0xc4, 0xa7, 0x6b, 0xfa, 0x8b, 0x0e, 0x1c, 0x68, 0xa5, 0x25, 0xd7, 0x06, - 0xd6, 0x60, 0x4b, 0x23, 0x30, 0xb6, 0xb3, 0x48, 0x52, 0x15, 0xf6, 0x06, 0xf1, 0x88, - 0x3a, 0x75, 0x15, 0x88, 0xc7, 0xef, 0xa5, 0x06, 0xc3, 0xe8, 0xd0, 0xc6, 0x01, 0x92, - 0xe8, 0x47, 0x6b, 0xd1, 0x17, 0x5d, 0x95, 0x62, 0x08, 0x7b, 0xdb, 0x81, 0x8e, 0x66, - 0x21, 0x62, 0x86, 0xba, 0xfe, 0x47, 0xff, 0x4d, 0xbc, 0xce, 0xd5, 0x14, 0x44, 0x48, - 0x0a, 0x9a, 0x56, 0x73, 0xec, 0xe7, 0xfa, 0xc7, 0x3a, 0x0e, 0xd4, 0x1a, 0xb0, 0x05, - 0x17, 0x53, 0xa7, 0xca, 0xa8, 0x9b, 0xe3, 0x13, 0x9a, 0xfd, 0x97, 0x93, 0xb3, 0xe0, - 0x2f, 0x27, 0xf0, 0x40, 0x04, 0x65, 0x95, 0xac, 0xd4, 0x7b, 0xf1, 0x3f, 0xd0, 0xda, - 0x27, 0xf0, 0x9e, 0xda, 0x48, 0x03, 0x6d, 0x3e, 0xe4, 0x37, 0xf2, 0xee, 0x8f, 0x86, - 0x06, 0xea, 0x97, 0x34, 0x3c, 0x33, 0x58, 0x46, 0x57, 0xf4, 0x6d, 0xba, 0x99, 0xdb, - 0x5c, 0xfe, 0x6c, 0xa1, 0x76, 0xfa, 0xb7, 0xb0, 0xf3, 0xbf, 0xa0, 0xab, 0x61, 0xe3, - 0x40, 0xc3, 0x4e, 0xb9, 0xf1, 0x7c, 0x7e, 0xc2, 0xbe, 0x03, 0xb1, 0x80, 0xf0, 0xbb, - 0x6f, 0x43, 0x4c, 0x2a, 0x65, 0x42, 0xe0, 0x0e, 0x84, 0x37, 0x3f, 0x4f, 0x46, 0x49, - 0xcd, 0xa3, 0x2b, 0xf6, 0x86, 0x66, 0x61, 0x43, 0xf6, 0x22, 0xaa, 0x48, 0x04, 0x60, - 0xb5, 0xaf, 0xac, 0x51, 0x86, 0x07, 0xcd, 0x9a, 0xf8, 0xbc, 0xd6, 0xb5, 0x8c, 0x30, - 0x12, 0x73, 0x16, 0xb2, 0x5d, 0x5e, 0xa7, 0xbf, 0x6b, 0x0c, 0xab, 0x85, 0x42, 0xff, - 0x69, 0xd9, 0xb2, 0xf1, 0x80, 0xbe, 0x12, 0xed, 0x75, 0x34, 0x4a, 0x39, 0x5a, 0xa1, - 0x0f, 0x85, 0x2f, 0x08, 0x3a, 0xd6, 0x4e, 0xf4, 0x0e, 0x9c, 0x03, 0x09, 0xe9, 0xbb, - 0xa5, 0x4b, 0x8c, 0xb3, 0x3c, 0x95, 0x49, 0x8a, 0x69, 0x53, 0x8d, 0x3a, 0xe5, 0xb2, - 0x5e, 0x24, 0x70, 0x98, 0xe1, 0x11, 0x7c, 0x91, 0x8a, 0xaa, 0xae, 0x9c, 0xb6, 0xef, - 0x77, 0xab, 0xd1, 0xe0, 0x1c, 0xc7, 0x43, 0xd0, 0xdd, 0xd0, 0x22, 0x75, 0x95, 0x1b, - 0x92, 0x49, 0x95, 0x65, 0xce, 0x83, 0x1f, 0x30, 0x32, 0xb8, 0x50, 0x57, 0x75, 0x10, - 0x8d, 0xc8, 0x5e, 0x2a, 0xde, 0x2e, 0xac, 0x1e, 0x63, 0x6e, 0x1a, 0xf4, 0x05, 0x4c, - 0x8b, 0x6f, 0x57, 0x63, 0x2d, 0xf2, 0x69, 0xc3, 0x72, 0x3b, 0x32, 0x08, 0x72, 0xe4, - 0xc5, 0x7b, 0x21, 0x83, 0x58, 0xdc, 0x7e, 0x99, 0x05, 0xbb, 0x04, 0xed, 0xf9, 0x2e, - 0xdf, 0x0d, 0xf6, 0x35, 0xf3, 0xbf, 0x36, 0x1e, 0x57, 0xa1, 0x32, 0x96, 0xe1, 0x44, - 0x7a, 0xf5, 0xa5, 0x66, 0x65, 0x17, 0xbc, 0xd3, 0x56, 0x76, 0x21, 0xa7, 0xcf, 0x84, - 0x45, 0x58, 0x96, 0x53, 0x26, 0x20, 0x20, 0xc3, 0x3b, 0xf7, 0x80, 0x31, 0xb8, 0xee, - 0x07, 0x07, 0xde, 0x07, 0x20, 0x68, 0xc1, 0x70, 0x57, 0x03, 0x27, 0xe6, 0xd9, 0xf5, - 0xc6, 0xdd, 0xc3, 0x35, 0x40, 0x2e, 0xfc, 0x54, 0x88, 0x62, 0xf5, 0xa0, 0x70, 0x94, - 0xfd, 0x42, 0x8a, 0x7b, 0xbc, 0x15, 0xd7, 0xb3, 0x8d, 0x05, 0x36, 0x2c, 0x9c, 0xa9, - 0x85, 0xf5, 0x8a, 0x76, 0x64, 0x7d, 0x2b, 0xe4, 0xc2, 0xcd, 0x6b, 0x3d, 0x17, 0xd6, - 0x87, 0x09, 0x71, 0xd7, 0xa0, 0x98, 0xba, 0xf7, 0x2c, 0x6f, 0x6f, 0x12, 0x14, 0xcf, - 0x1f, 0xaa, 0xe4, 0x88, 0xbd, 0x7d, 0xe2, 0x59, 0xd3, 0x41, 0x5c, 0x2f, 0x0d, 0xde, - 0xc7, 0x45, 0x70, 0x04, 0xf3, 0x57, 0x08, 0xd1, 0xec, 0xcc, 0xcc, 0x0d, 0xf6, 0x5a, - 0x04, 0x94, 0x3a, 0xd5, 0xcb, 0xc1, 0x3f, 0x29, 0x5f, 0x00, 0x0f, 0xe0, 0x56, 0xc4, - 0x0b, 0x2d, 0x88, 0xf2, 0x7d, 0xc3, 0x4c, 0xfe, 0xb8, 0x03, 0xbe, 0x34, 0x83, 0xa9, - 0xeb, 0xf9, 0xb5, 0xa9, 0x02, 0x60, 0x57, 0x72, 0x5d, 0x63, 0xea, 0xd2, 0xc0, 0xc0, - 0xff, 0x1f, 0xe2, 0x6a, 0xc1, 0xe7, 0xbd, 0xfc, 0xd6, 0xfa, 0xd8, 0x75, 0x84, 0x2d, - 0x19, 0x4f, 0x33, 0x17, 0x50, 0x46, 0x2c, 0x06, 0xb8, 0xd7, 0x98, 0x2d, 0x67, 0x99, - 0x5e, 0xd5, 0xd3, 0xae, 0x96, 0xa0, 0x5a, 0xe0, 0x06, 0x7f, 0x4e, 0xb1, 0xc7, 0xc9, - 0x32, 0x31, 0xbd, 0x39, 0x77, 0x3c, 0xbe, 0x0a, 0x9d, 0x66, 0xb0, 0xc9, 0xaa, 0x8c, - 0xff, 0x6a, 0x37, 0x6e, 0x1f, 0x37, 0x2e, 0xac, 0x6a, 0xc4, 0xe4, 0x6c, 0xc0, 0x94, - 0x22, 0x45, 0xd4, 0xc2, 0xdc, 0xf0, 0x2d, 0x76, 0x40, 0xff, 0xcc, 0x5a, 0x6a, 0xc3, - 0xa8, 0x7f, 0x5c, 0x41, 0x15, 0x51, 0xbc, 0xc2, 0xf2, 0x6c, 0xb9, 0x49, 0x61, 0xd5, - 0x3f, 0x95, 0xdd, 0xb1, 0x9a, 0xe9, 0x30, 0xc8, 0xd7, 0x0f, 0x03, 0x1b, 0x29, 0xa5, - 0xdf, 0x99, 0xff, 0x36, 0x69, 0x5e, 0x80, 0x2c, 0xbc, 0xb6, 0xb1, 0xcf, 0x7d, 0xc9, - 0x7a, 0xbb, 0x3b, 0x25, 0x27, 0x64, 0xc0, 0x1a, 0x62, 0x2d, 0xfb, 0x2e, 0xcb, 0x49, - 0xce, 0x71, 0xf7, 0x38, 0x6e, 0x23, 0x89, 0x5b, 0x5a, 0xfe, 0x16, 0x61, 0x98, 0xb6, - 0x7f, 0x5b, 0x42, 0xb2, 0xf6, 0x5e, 0xcd, 0x0f, 0x82, 0x59, 0x54, 0x78, 0xd8, 0x0a, - 0xe5, 0xc8, 0xce, 0xea, 0x12, 0xa1, 0x61, 0xcc, 0xbb, 0x5e, 0xac, 0x09, 0x99, 0x0f, - 0xc6, 0x19, 0xa4, 0x60, 0x80, 0x43, 0x6d, 0xbd, 0x08, 0xd7, 0x47, 0x84, 0xaf, 0x00, - 0x2d, 0x58, 0xe0, 0x6f, 0xaf, 0x7f, 0x3c, 0xea, 0xe7, 0xd3, 0x41, 0x9b, 0x1f, 0xca, - 0x26, 0x5a, 0x55, 0x59, 0xcf, 0x9e, 0x2d, 0x3b, 0x97, 0xb2, 0xa9, 0x9a, 0x9b, 0xa5, - 0xa8, 0x66, 0x58, 0xc3, 0xfd, 0x9e, 0xc5, 0x5b, 0xfa, 0x9b, 0x32, 0x85, 0x67, 0x25, - 0x4a, 0xb3, 0x6d, 0x2c, 0x7f, 0x44, 0xd2, 0xc7, 0xe1, 0x3e, 0xb5, 0x4b, 0xeb, 0x70, - 0xea, 0x8f, 0xa9, 0x4b, 0x6c, 0x6e, 0x01, 0x2d, 0x79, 0xe3, 0xf5, 0x36, 0x89, 0xc2, - 0xb1, 0xa1, 0x8e, 0xaf, 0x2d, 0x47, 0x1d, 0x13, 0xc1, 0xab, 0x39, 0xd9, 0x19, 0x4a, - 0xe8, 0x43, 0xab, 0x1d, 0x28, 0xff, 0xa8, 0xf6, 0x9d, 0xc7, 0xe1, 0x5c, 0xc3, 0x8b, - 0x12, 0xe8, 0xfc, 0xd7, 0x92, 0x55, 0xb7, 0x21, 0x60, 0x56, 0xd9, 0xed, 0xb7, 0x48, - 0x2f, 0xb9, 0x8a, 0xa0, 0x33, 0xb6, 0x5e, 0x51, 0xc1, 0xa0, 0x8b, 0x8a, 0x11, 0xd8, - 0x4d, 0x04, 0x09, 0xb7, 0x34, 0xf4, 0x52, 0xaa, 0xf0, 0xd6, 0xb1, 0x8f, 0x50, 0x25, - 0x86, 0x83, 0xd3, 0xf9, 0xa7, 0x6d, 0x39, 0x9f, 0xd0, 0x47, 0xee, 0xe2, 0x88, 0xbb, - 0x45, 0x85, 0x85, 0x1d, 0xc9, 0x3e, 0xcc, 0xc6, 0x23, 0x22, 0x92, 0x4c, 0xd1, 0x3b, - 0x5d, 0xd4, 0xee, 0xd6, 0x6e, 0xd8, 0xd9, 0x97, 0x2d, 0x77, 0x26, 0x29, 0xea, 0x64, - 0x74, 0x2e, 0x54, 0x73, 0x39, 0x81, 0xb0, 0x06, 0xc0, 0x62, 0x46, 0x8e, 0x4b, 0xd8, - 0xf7, 0xdd, 0x9a, 0xf6, 0x98, 0xf5, 0x2a, 0xe8, 0x14, 0x63, 0x4e, 0x81, 0xd7, 0xf3, - 0xe0, 0xc4, 0x20, 0x31, 0x7c, 0xac, 0xa9, 0xae, 0x48, 0x11, 0xc6, 0xaf, 0x06, 0xfe, - 0x80, 0xa8, 0xc0, 0x2a, 0xb7, 0xa0, 0x0e, 0x18, 0xe4, 0xa6, 0xaa, 0x1e, 0xa1, 0xb7, - 0x69, 0x45, 0xd2, 0x61, 0x5d, 0x43, 0xac, 0x11, 0x8b, 0x56, 0xc2, 0xf2, 0x96, 0x0f, - 0xe9, 0x3a, 0x02, 0x5f, 0x13, 0xec, 0x91, 0xff, 0xc6, 0xd2, 0xc3, 0x53, 0x69, 0x9a, - 0xbb, 0x09, 0x2d, 0xed, 0xc0, 0x65, 0xdb, 0x8f, 0xa2, 0x14, 0xdb, 0xc4, 0x64, 0x66, - 0xf8, 0x97, 0xb8, 0x8c, 0x58, 0xb3, 0x01, 0x52, 0x13, 0x3a, 0xa3, 0x83, 0x1a, 0xf3, - 0x7c, 0x74, 0xd9, 0x9e, 0x9e, 0x36, 0xff, 0x70, 0x11, 0xd3, 0x23, 0x83, 0x05, 0x69, - 0x15, 0x08, 0xd0, 0xf1, 0xf6, 0xaa, 0xaa, 0xa4, 0x25, 0x12, 0x30, 0xc6, 0xcc, 0xc4, - 0x66, 0x68, 0xbb, 0xcf, 0x35, 0xe5, 0xa5, 0xef, 0x2f, 0x86, 0xe6, 0x65, 0xd8, 0xcf, - 0xac, 0x74, 0x76, 0xec, 0xb2, 0x43, 0x78, 0x79, 0x6a, 0x8e, 0xf2, 0xe4, 0xd9, 0x4d, - 0x43, 0x58, 0xbe, 0x2b, 0x47, 0x5f, 0xcc, 0x92, 0xdf, 0x93, 0x82, 0xc5, 0xc0, 0x69, - 0x19, 0xa0, 0xd6, 0x31, 0xec, 0x26, 0x10, 0xfe, 0xdc, 0x21, 0x9b, 0xe6, 0x3f, 0x37, - 0xf2, 0xba, 0x0d, 0x43, 0x23, 0x66, 0x73, 0x6d, 0x86, 0x32, 0xfc, 0xe0, 0x72, 0xb6, - 0xae, 0x5b, 0x6f, 0x3f, 0xd5, 0x9d, 0x3f, 0xaf, 0xf6, 0x38, 0x27, 0x5a, 0x99, 0x2f, - 0xef, 0xc8, 0x7e, 0x60, 0xd4, 0x4c, 0x2c, 0xad, 0xc2, 0xb5, 0xc4, 0x94, 0xe3, 0xe7, - 0x2e, 0xb4, 0x59, 0x7c, 0x96, 0xb4, 0x01, 0x67, 0x79, 0x9a, 0x90, 0x01, 0xa2, 0xed, - 0x36, 0x76, 0xa8, 0xb4, 0x03, 0xae, 0x25, 0xff, 0xd7, 0x72, 0xf7, 0x08, 0x1e, 0x9a, - 0x32, 0xbc, 0xc1, 0xc5, 0xe2, 0xed, 0xd4, 0xe2, 0xa6, 0x57, 0x6b, 0x78, 0x3c, 0xce, - 0x3a, 0xae, 0x11, 0xfa, 0x43, 0x22, 0x62, 0x54, 0x88, 0x56, 0x18, 0x3e, 0xe6, 0x82, - 0xd5, 0xdc, 0x31, 0xbe, 0xb3, 0x8f, 0x06, 0x1c, 0xbd, 0xec, 0xa7, 0x02, 0x1a, 0x44, - 0x4e, 0x2d, 0xd4, 0x17, 0xdf, 0x26, 0xdc, 0xd2, 0x20, 0xf2, 0xb7, 0x31, 0x77, 0x2b, - 0x43, 0x9e, 0x96, 0xd6, 0x14, 0xe1, 0xfa, 0xcb, 0x48, 0x6c, 0x7a, 0x7d, 0x51, 0x71, - 0xb1, 0xde, 0x35, 0x9f, 0x6a, 0xd3, 0xa9, 0x6f, 0x64, 0x9c, 0x96, 0x91, 0x02, 0xa1, - 0x96, 0x4f, 0xb4, 0xb4, 0xa1, 0xa4, 0x27, 0x9c, 0x68, 0xe6, 0xc3, 0x72, 0xe4, 0x21, - 0x87, 0xd7, 0x54, 0xe8, 0x04, 0xa6, 0x16, 0x53, 0x09, 0x20, 0x69, 0xfb, 0x9b, 0x6d, - 0x25, 0x26, 0x68, 0x90, 0x80, 0x8b, 0x01, 0x5d, 0xf2, 0x8c, 0x80, 0x10, 0x65, 0xda, - 0x6f, 0xeb, 0xdc, 0x1a, 0x56, 0xbf, 0xd0, 0x02, 0x62, 0x5a, 0xcf, 0xaa, 0x53, 0x73, - 0xfd, 0xe1, 0x49, 0xc1, 0xcf, 0xc3, 0x64, 0x9b, 0x48, 0x69, 0x69, 0x6d, 0x44, 0xec, - 0xb1, 0x24, 0x79, 0xc5, 0xeb, 0xef, 0x99, 0x5f, 0x10, 0x02, 0x9f, 0x8b, 0x53, 0x0e, - 0xeb, 0x3f, 0xdc, 0x2e, 0x50, 0xe8, 0x75, 0x7f, 0xc0, 0xbb, 0x9e, 0x26, 0x30, 0x23, - 0xdb, 0x82, 0xf8, 0x78, 0xd9, 0xac, 0x7f, 0xfb, 0x0b, 0xd4, 0x39, 0x1d, 0xf1, 0xd8, - 0x79, 0x89, 0x9a, 0x3e, 0xf5, 0x7b, 0xfd, 0x0d, 0x1f, 0x77, 0x55, 0x64, 0x8e, 0xdd, - 0x85, 0xbb, 0x05, 0x2a, 0x6e, 0xdf, 0x71, 0xcd, 0x26, 0x28, 0xc9, 0x87, 0x42, 0x9f, - 0x36, 0xdc, 0x50, 0x5c, 0xcc, 0x43, 0xf3, 0x0e, 0x7a, 0x86, 0x9c, 0x9e, 0x25, 0x5e, - 0x2a, 0xf9, 0xfc, 0xf3, 0x0c, 0x12, 0x17, 0x96, 0x03, 0xae, 0x17, 0x57, 0x55, 0x3b, - 0x5c, 0x94, 0x60, 0x7e, 0x00, 0xd0, 0x32, 0xfb, 0xbe, 0xd2, 0x3c, 0x4c, 0xba, 0xbf, - 0x74, 0x1d, 0x68, 0xaa, 0xb3, 0x0e, 0x0b, 0x8f, 0x15, 0xf4, 0x44, 0xd5, 0x86, 0xb3, - 0xe7, 0xe6, 0x15, 0x9c, 0x46, 0x69, 0x9f, 0x10, 0x07, 0x92, 0xd4, 0x67, 0x29, 0x50, - 0x34, 0x8a, 0x90, 0x55, 0x2e, 0x45, 0x94, 0x3b, 0xee, 0xac, 0xf0, 0x3f, 0x32, 0x16, - 0xf9, 0x4e, 0x27, 0x90, 0x6e, 0xdc, 0x63, 0x23, 0x19, 0xad, 0x8d, 0x37, 0x44, 0x7f, - 0x5c, 0x59, 0xcc, 0xde, 0x35, 0x4f, 0x99, 0xff, 0x6c, 0x7a, 0x76, 0x23, 0xf6, 0xd4, - 0x15, 0x25, 0xa8, 0x09, 0xce, 0x2f, 0x41, 0xec, 0x0f, 0xf7, 0xf1, 0xaf, 0x81, 0xb2, - 0x4c, 0xed, 0x0e, 0xfa, 0x62, 0x13, 0xda, 0x6c, 0x7c, 0x60, 0xc4, 0x87, 0xf5, 0xf7, - 0xb0, 0x3f, 0x81, 0x60, 0xa0, 0x57, 0xf4, 0x6d, 0x05, 0xbf, 0x82, 0x18, 0xb3, 0xad, - 0xd9, 0xc0, 0x68, 0x93, 0xbd, 0x02, 0xdb, 0x9b, 0x61, 0x19, 0x1d, 0xfb, 0x13, 0x3b, - 0xfa, 0xbe, 0x48, 0x58, 0xe4, 0x7a, 0x4c, 0xc3, 0x2e, 0x41, 0x6e, 0xc0, 0x8b, 0x8a, - 0xc7, 0x91, 0x5a, 0x43, 0x73, 0x3f, 0x44, 0x06, 0xe9, 0xd9, 0x67, 0xc5, 0x60, 0xf3, - 0x44, 0xd7, 0xe9, 0x04, 0xa2, 0x80, 0x45, 0xd9, 0x9f, 0x3a, 0xf8, 0xc8, 0x2e, 0x97, - 0xe1, 0xb9, 0xc1, 0xb2, 0x05, 0xe5, 0x85, 0xfb, 0xeb, 0xb4, 0x8f, 0xaf, 0x58, 0xf1, - 0xb6, 0x5d, 0xca, 0x24, 0x97, 0xe0, 0x9a, 0x70, 0xaa, 0xd4, 0x86, 0x5f, 0x85, 0x71, - 0x5a, 0x28, 0x0e, 0x18, 0x6f, 0x3f, 0xc1, 0x74, 0x0d, 0x81, 0x84, 0xd3, 0x3e, 0x83, - 0x22, 0x16, 0x95, 0x21, 0xcd, 0xc1, 0x32, 0x21, 0x29, 0x39, 0xc8, 0x4a, 0x10, 0x89, - 0x64, 0xe2, 0xde, 0x74, 0xb6, 0xea, 0x55, 0xb4, 0xcb, 0x8f, 0x6f, 0x9b, 0xee, 0x98, - 0xb1, 0x0d, 0x41, 0x51, 0x09, 0x45, 0x5f, 0x48, 0xb7, 0x76, 0x08, 0x2d, 0xc3, 0x0b, - 0x4b, 0xc7, 0x34, 0x77, 0x07, 0x55, 0x11, 0x70, 0x03, 0x08, 0x15, 0x8c, 0xe2, 0xf2, - 0xf9, 0xbf, 0x0f, 0x69, 0x1b, 0x2c, 0xe5, 0x3e, 0x61, 0x14, 0x2c, 0xb7, 0x40, 0xc1, - 0x5b, 0x7b, 0x62, 0x3c, 0xf4, 0x8b, 0x3f, 0x7b, 0xfe, 0xfa, 0x31, 0xbc, 0xdc, 0x66, - 0x5c, 0x6d, 0x71, 0x23, 0xe9, 0x53, 0x50, 0x81, 0x13, 0x75, 0x94, 0x7b, 0x05, 0x5a, - 0x43, 0xdb, 0x07, 0xe0, 0x3f, 0x33, 0x62, 0x7d, 0xf5, 0xc6, 0x38, 0xbf, 0xad, 0x95, - 0x6d, 0xdc, 0x1e, 0xa7, 0xd7, 0x62, 0x0a, 0x20, 0xf2, 0x79, 0x2f, 0x63, 0x81, 0x7a, - 0x1c, 0xf3, 0x25, 0x80, 0xd0, 0x42, 0x74, 0x23, 0x4a, 0xf2, 0xa5, 0x1b, 0x56, 0xbb, - 0x68, 0xa2, 0x9e, 0x43, 0xa9, 0x54, 0x14, 0x2b, 0xa4, 0xca, 0x68, 0x23, 0xbd, 0xe9, - 0x05, 0x3d, 0x72, 0xfd, 0xad, 0xbc, 0x61, 0xad, 0x59, 0x36, 0xc5, 0x3f, 0xdd, 0x75, - 0x79, 0x44, 0x6d, 0x11, 0xc4, 0x46, 0x07, 0xf4, 0x16, 0x30, 0xe4, 0xc0, 0x89, 0x15, - 0xe6, 0x31, 0x77, 0x15, 0x50, 0xe9, 0xce, 0x1f, 0xca, 0x2c, 0x63, 0xfe, 0x06, 0xb7, - 0x98, 0x9d, 0x58, 0x4f, 0xa7, 0xd7, 0x82, 0xa8, 0x8c, 0x1e, 0x7d, 0x64, 0xb6, 0xfb, - 0xf5, 0x5e, 0x35, 0x96, 0xaf, 0x9b, 0xcb, 0x75, 0x85, 0xf8, 0xc7, 0xd3, 0xaa, 0x5c, - 0x20, 0x82, 0xb2, 0x65, 0x24, 0x9d, 0xf0, 0x57, 0x01, 0xda, 0xb0, 0x31, 0xc4, 0xba, - 0xc1, 0xea, 0x26, 0x7a, 0x29, 0x96, 0xa2, 0x02, 0x8d, 0x1e, 0x6a, 0x0f, 0x80, 0xa3, - 0x84, 0x7c, 0x53, 0x1d, 0xba, 0x96, 0xee, 0x65, 0xa2, 0x41, 0x89, 0xbd, 0x27, 0x12, - 0xe4, 0x0e, 0x95, 0x96, 0x64, 0x98, 0x1e, 0x58, 0xb2, 0xa4, 0xf9, 0x51, 0xef, 0x8f, - 0x49, 0x7d, 0xff, 0xf2, 0xf2, 0xf2, 0x71, 0xea, 0xb8, 0x9c, 0x62, 0x8e, 0x18, 0xb5, - 0xfc, 0xb4, 0x38, 0x82, 0x53, 0x7e, 0xaf, 0x6a, 0xd2, 0xa6, 0xb1, 0x75, 0x46, 0x33, - 0xca, 0xa8, 0x6b, 0xf2, 0xc7, 0x6f, 0x39, 0x93, 0x15, 0x4f, 0xc7, 0x3e, 0x6f, 0xbb, - 0xa2, 0x21, 0x0c, 0x27, 0x43, 0xf5, 0x30, 0xa4, 0x27, 0x84, 0x9a, 0x30, 0x1e, 0x00, - 0xe0, 0x11, 0x29, 0xf0, 0x3a, 0x46, 0x07, 0xf8, 0x7c, 0xbe, 0x07, 0x62, 0xc0, 0xb1, - 0xc6, 0x58, 0x55, 0xde, 0xba, 0x84, 0x22, 0xca, 0x4b, 0x88, 0xab, 0xee, 0xa6, 0xa4, - 0x38, 0x2c, 0xf1, 0x6c, 0xcd, 0x6d, 0xc7, 0xc3, 0x7c, 0x44, 0xe5, 0x49, 0xc4, 0x53, - 0x48, 0x19, 0xac, 0xd8, 0xbb, 0x0a, 0x02, 0xa5, 0xfa, 0x7a, 0x1c, 0x1d, 0x38, 0x06, - 0xfb, 0xc3, 0x40, 0x7f, 0xd7, 0xda, 0x93, 0xfd, 0x0d, 0xe6, 0x40, 0x0d, 0x3a, 0xb8, - 0x97, 0x74, 0x85, 0xcd, 0xdf, 0xbe, 0xd5, 0x93, 0x2f, 0x50, 0x7b, 0x79, 0x94, 0x7a, - 0xdb, 0x2f, 0xad, 0x37, 0x61, 0x5a, 0xa7, 0x17, 0xdb, 0x5f, 0x29, 0x80, 0x99, 0xf2, - 0x0f, 0x26, 0x3b, 0x35, 0x9a, 0x11, 0x51, 0xa6, 0xb7, 0x5c, 0x01, 0x36, 0x5e, 0xb1, - 0x54, 0xae, 0x42, 0x14, 0x0d, 0x6e, 0x10, 0x34, 0x2f, 0x14, 0xf3, 0x4d, 0xc3, 0x3e, - 0x07, 0xff, 0x0e, 0x4d, 0x1a, 0x6b, 0xe3, 0x75, 0xb3, 0x2f, 0x84, 0xb9, 0x2e, 0x5d, - 0x81, 0xeb, 0xb6, 0x39, 0xc4, 0xf2, 0x7e, 0x71, 0x5a, 0xa4, 0x2c, 0xc7, 0x57, 0x07, - 0xd4, 0xeb, 0xd1, 0xbb, 0xfb, 0xe8, 0xf9, 0x0f, 0xc7, 0xc9, 0x53, 0xe7, 0xa9, 0x71, - 0x5e, 0x65, 0xaf, 0x82, 0x67, 0x37, 0x3d, 0x34, 0x51, 0x67, 0x4f, 0xf0, 0x84, 0xef, - 0xd9, 0x2c, 0xcf, 0x3b, 0xcc, 0x7a, 0xca, 0x14, 0x67, 0xb6, 0x32, 0x7e, 0x4f, 0x95, - 0x22, 0xb2, 0xcc, 0x57, 0x9a, 0x7a, 0x8f, 0xff, 0x7c, 0xa7, 0xcf, 0x14, 0x5d, 0xfc, - 0x13, 0xea, 0xfc, 0x34, 0x15, 0x3b, 0x2c, 0x3e, 0x8a, 0xfb, 0xe5, 0x34, 0x44, 0xd0, - 0xc7, 0x3b, 0x3b, 0xd5, 0xbc, 0x87, 0x0b, 0x01, 0xcd, 0x45, 0x79, 0x11, 0xe3, 0x56, - 0x31, 0x3f, 0xd1, 0xda, 0xfb, 0x4c, 0x81, 0x51, 0x63, 0x4a, 0x01, 0xaf, 0xf7, 0xcf, - 0x11, 0x6d, 0x43, 0x3c, 0x3d, 0x2b, 0x3a, 0xdd, 0xa9, 0xce, 0xbe, 0x18, 0xf7, 0xd1, - 0x72, 0x44, 0x3e, 0x5e, 0x7b, 0x5a, 0xc9, 0xab, 0xe8, 0xdb, 0x22, 0x56, 0xd7, 0xeb, - 0xe2, 0xff, 0x28, 0x02, 0x09, 0x39, 0x50, 0x38, 0x70, 0x59, 0x7b, 0x9a, 0x95, 0x58, - 0x92, 0xc7, 0x38, 0x96, 0x50, 0xa2, 0xd4, 0x2e, 0xc9, 0x2b, 0xe7, 0x23, 0xfe, 0xdf, - 0x2f, 0x2e, 0xde, 0x5a, 0x47, 0x2a, 0xa1, 0xe7, 0x4f, 0x33, 0xad, 0x41, 0x90, 0x15, - 0x44, 0xed, 0xbb, 0xe3, 0xac, 0x46, 0x4c, 0xf4, 0x39, 0x19, 0x60, 0x15, 0xf4, 0xf2, - 0x2a, 0xc2, 0xb8, 0xfc, 0x01, 0x49, 0x6b, 0xea, 0xb4, 0xd4, 0x59, 0x07, 0xf4, 0x79, - 0x81, 0x2a, 0x25, 0x94, 0x31, 0xa2, 0xcb, 0xc9, 0x3d, 0x4f, 0x3b, 0x84, 0xe4, 0xdd, - 0x36, 0x60, 0x20, 0x27, 0x3a, 0x67, 0x52, 0xe5, 0x01, 0xaf, 0x6f, 0xf1, 0xb7, 0x8d, - 0xdc, 0x81, 0x7e, 0x6e, 0xa3, 0xe5, 0x37, 0x5c, 0xa3, 0xfe, 0x2f, 0xb1, 0xd1, 0xa9, - 0x37, 0x15, 0xdf, 0xf9, 0x29, 0x93, 0xcc, 0xc6, 0x50, 0x4f, 0x64, 0xfc, 0xbf, 0x22, - 0x17, 0x30, 0xd9, 0xef, 0xf4, 0xf3, 0x27, 0xe0, 0xad, 0x37, 0x4f, 0x59, 0x56, 0x45, - 0x37, 0x48, 0x75, 0x5b, 0x43, 0xc8, 0xf2, 0x9d, 0x67, 0x52, 0x6f, 0x60, 0xa6, 0x18, - 0xb7, 0x33, 0x24, 0xd2, 0x24, 0x33, 0x72, 0x92, 0x1b, 0x99, 0xe3, 0xdf, 0x36, 0x85, - 0x0c, 0x60, 0xd5, 0xd9, 0x34, 0x3a, 0x48, 0x70, 0xa0, 0xe7, 0x52, 0x8c, 0x65, 0x13, - 0xc2, 0x7c, 0x56, 0x43, 0x90, 0x93, 0xf9, 0x33, 0x68, 0x1d, 0x93, 0xa4, 0xd4, 0xbc, - 0xee, 0xac, 0x2b, 0x10, 0x8c, 0x6c, 0x6f, 0xae, 0x35, 0x9f, 0x64, 0x5c, 0x27, 0x68, - 0x91, 0xc0, 0xdc, 0xab, 0x3f, 0xaf, 0x18, 0x77, 0x00, 0xc0, 0x82, 0xdc, 0x47, 0x77, - 0x40, 0xfb, 0x3f, 0x2c, 0xd7, 0xbb, 0x59, 0xfb, 0x35, 0x85, 0x54, 0xe9, 0x4c, 0x7e, - 0x67, 0x8c, 0xe0, 0x1a, 0xeb, 0xf9, 0x4e, 0x51, 0x5e, 0x49, 0x72, 0x29, 0x67, 0x99, - 0x5a, 0xea, 0x85, 0x8d, 0x64, 0xe7, 0x78, 0x9f, 0xf3, 0x06, 0x36, 0x95, 0x77, 0x22, - 0x81, 0x80, 0x32, 0x6a, 0x5b, 0x0a, 0xf4, 0x75, 0xe2, 0x7a, 0x54, 0xb2, 0x07, 0xb4, - 0x1f, 0x92, 0xe3, 0x76, 0x17, 0x0e, 0x3f, 0xb0, 0x05, 0x02, 0x82, 0x61, 0xc9, 0x9c, - 0x2d, 0xbd, 0x0e, 0xed, 0xee, 0x87, 0x1c, 0x1c, 0x0f, 0x48, 0xb8, 0xe9, 0xb8, 0xe4, - 0xbe, 0x77, 0xd1, 0xb7, 0x37, 0xfe, 0x21, 0xf0, 0xfa, 0x5a, 0x18, 0xeb, 0xb5, 0x27, - 0x55, 0xb5, 0xa6, 0xcf, 0x61, 0x30, 0xfb, 0x56, 0x94, 0x4c, 0xfa, 0xb8, 0x75, 0x27, - 0xc2, 0x50, 0xd1, 0x13, 0xb2, 0x9b, 0xca, 0xc9, 0xaa, 0xa1, 0x0c, 0x2e, 0x7d, 0xe4, - 0x15, 0xed, 0xb0, 0x80, 0x6c, 0x6d, 0xa0, 0x30, 0x20, 0xa1, 0x34, 0xca, 0x7e, 0xcd, - 0xc8, 0xda, 0x1b, 0xd5, 0x7a, 0x37, 0xf5, 0x5a, 0x46, 0x94, 0x0b, 0x45, 0xb2, 0x41, - 0xb1, 0xc1, 0x6e, 0xe1, 0x00, 0x92, 0x7d, 0x1b, 0xd8, 0x60, 0xd4, 0x45, 0xa9, 0xde, - 0x50, 0xd4, 0xc3, 0x84, 0xd6, 0xe1, 0xd0, 0x01, 0x08, 0x02, 0x6c, 0x0e, 0xa5, 0xeb, - 0xbf, 0x0b, 0x72, 0xfb, 0xf5, 0xc3, 0x70, 0xbc, 0xe1, 0x8d, 0x3a, 0xcb, 0xc4, 0x65, - 0x99, 0x09, 0x9b, 0xaa, 0xe1, 0xd8, 0x02, 0xf7, 0x73, 0x33, 0x49, 0x4a, 0x7a, 0xe1, - 0x30, 0xfe, 0x86, 0xe8, 0xf8, 0x18, 0xf9, 0x26, 0x1a, 0x2d, 0xad, 0xb4, 0x12, 0x52, - 0x29, 0xba, 0x0f, 0xfc, 0x0e, 0x70, 0x90, 0x32, 0x44, 0x30, 0xb5, 0x21, 0xa9, 0x0d, - 0x22, 0x4a, 0xb7, 0xa1, 0x02, 0x4e, 0x1d, 0x89, 0x3e, 0x74, 0x04, 0xfe, 0xdb, 0x34, - 0x8e, 0x4d, 0x5e, 0x22, 0x35, 0xc5, 0x9a, 0x78, 0x76, 0xa0, 0xfc, 0x60, 0x14, 0x5c, - 0x6a, 0x00, 0x96, 0x87, 0x68, 0x44, 0x60, 0x27, 0x1e, 0xe1, 0x33, 0xa4, 0x37, 0xfe, - 0x52, 0xfb, 0x6c, 0xfb, 0xa9, 0x7f, 0xce, 0xc1, 0x61, 0xdf, 0x51, 0x5d, 0xde, 0x90, - 0x5a, 0x24, 0xda, 0x6d, 0x37, 0xbd, 0xc3, 0x40, 0x44, 0xa9, 0x55, 0xe6, 0x82, 0xb4, - 0x74, 0x71, 0xca, 0x1e, 0x8c, 0x78, 0xc5, 0x1e, 0xd3, 0x77, 0xcd, 0x4a, 0xfa, 0x89, - 0x4b, 0xd9, 0xbd, 0x12, 0xe7, 0x07, 0x15, 0x6d, 0xa0, 0x72, 0x6f, 0x7c, 0xf5, 0x72, - 0x9f, 0xab, 0xe3, 0x72, 0x16, 0x04, 0x63, 0xfe, 0x04, 0x29, 0x24, 0x4d, 0x06, 0x74, - 0x89, 0xba, 0x5d, 0x09, 0x47, 0x2e, 0xcd, 0x9b, 0xcd, 0xc4, 0xd5, 0xe4, 0xdf, 0x10, - 0x1e, 0x18, 0x9d, 0xb8, 0x46, 0x3e, 0xb5, 0x38, 0x30, 0x7b, 0x58, 0x7d, 0xef, 0xf7, - 0x8d, 0xe9, 0xc7, 0x3a, 0xf2, 0x80, 0x80, 0xb2, 0xfd, 0x05, 0x00, 0x3e, 0x11, 0xd3, - 0xe1, 0xb3, 0x29, 0x9d, 0xc9, 0x52, 0x1f, 0x8b, 0x51, 0x3b, 0xad, 0xb0, 0x10, 0xe9, - 0x1b, 0xfe, 0xb9, 0x1b, 0x0b, 0x2a, 0x6c, 0xb1, 0x29, 0xc2, 0xe8, 0x25, 0xa5, 0x97, - 0xb8, 0xfb, 0x75, 0xbc, 0x56, 0x2d, 0x65, 0x4d, 0x62, 0x10, 0x46, 0x40, 0xdd, 0x74, - 0xe5, 0x6c, 0xd1, 0x4b, 0xaa, 0xba, 0x56, 0x5b, 0x84, 0xb8, 0x45, 0xe1, 0x63, 0xd1, - 0xca, 0xef, 0x25, 0x33, 0xc3, 0x98, 0x16, 0x37, 0x20, 0x4f, 0x96, 0xa5, 0x9c, 0x8e, - 0x80, 0x24, 0xd9, 0x04, 0x1b, 0x20, 0x29, 0xe9, 0x4c, 0x15, 0x24, 0x5f, 0x1a, 0x95, - 0x88, 0x40, 0xba, 0x3f, 0x38, 0x0a, 0x4d, 0x20, 0xf1, 0x18, 0x4e, 0x77, 0x82, 0x7d, - 0xe3, 0xff, 0x8f, 0x3d, 0x73, 0x45, 0x9a, 0xfe, 0x24, 0x1f, 0x72, 0x3c, 0x08, 0x48, - 0x23, 0x23, 0x0e, 0x00, 0x3d, 0x3d, 0x21, 0xe5, 0x35, 0x01, 0xec, 0x04, 0x99, 0xb0, - 0x83, 0xa7, 0xda, 0xd6, 0x85, 0xc5, 0x71, 0x27, 0xf4, 0xde, 0x64, 0x73, 0x3a, 0x88, - 0x0c, 0x2d, 0xb2, 0x8f, 0xda, 0xab, 0xf1, 0xb5, 0x42, 0xd2, 0x05, 0xf6, 0x64, 0xa3, - 0x51, 0x35, 0x71, 0x27, 0x11, 0xdc, 0xcc, 0xd9, 0x31, 0xa5, 0x0b, 0x9c, 0x56, 0x61, - 0x88, 0x23, 0x60, 0xd4, 0xca, 0xc0, 0x04, 0x76, 0x81, 0xbc, 0x2e, 0x2b, 0x3b, 0xf6, - 0xc9, 0x97, 0x60, 0xd7, 0xcf, 0xb4, 0xfa, 0x21, 0x39, 0x43, 0x77, 0xa4, 0x55, 0x1c, - 0x76, 0xd1, 0xf7, 0x5a, 0xc0, 0x3c, 0x26, 0x20, 0x54, 0xdf, 0xfd, 0x79, 0xa9, 0xde, - 0xd0, 0x5e, 0x88, 0x89, 0x58, 0x19, 0x9e, 0xea, 0x45, 0x01, 0xe2, 0x99, 0x0a, 0x53, - 0xa5, 0xcd, 0x2a, 0x46, 0xa4, 0x01, 0x57, 0x65, 0x88, 0xfd, 0x7d, 0x05, 0x8a, 0x26, - 0xf2, 0x84, 0x38, 0xe5, 0x78, 0x2f, 0x45, 0xac, 0x1d, 0x07, 0xf6, 0xf6, 0xf5, 0xed, - 0x73, 0x74, 0x1d, 0x57, 0x85, 0x83, 0x7a, 0x6b, 0x84, 0x4b, 0x47, 0x47, 0x75, 0x71, - 0x8c, 0x29, 0xdd, 0x99, 0x08, 0x4e, 0x9f, 0x88, 0xef, 0x15, 0x3a, 0x83, 0x29, 0xf5, - 0x32, 0xa6, 0x90, 0x17, 0xdc, 0x3a, 0x97, 0xed, 0x75, 0x43, 0x67, 0x72, 0x30, 0x98, - 0xe5, 0x76, 0x58, 0x40, 0xb0, 0x22, 0x89, 0x72, 0x44, 0x74, 0x5f, 0xbb, 0xbb, 0x30, - 0xa7, 0xcb, 0x54, 0xfa, 0x05, 0x11, 0x16, 0x6e, 0x95, 0x44, 0x12, 0x20, 0x00, 0x61, - 0x0b, 0xd2, 0xaa, 0xcb, 0xd8, 0x23, 0x25, 0xa5, 0x9b, 0x95, 0x15, 0x4e, 0xcd, 0x82, - 0xc8, 0x8d, 0x23, 0xab, 0xd1, 0xe2, 0x07, 0x70, 0xff, 0xb8, 0xaa, 0xbf, 0x83, 0xfc, - 0x07, 0x34, 0x96, 0x4c, 0xcd, 0x41, 0x1d, 0x1c, 0x93, 0x57, 0x14, 0xe2, 0x4a, 0xab, - 0x56, 0x6f, 0x4f, 0x08, 0x42, 0x40, 0x14, 0xc4, 0xec, 0xa9, 0x1b, 0x59, 0x0f, 0x08, - 0x2b, 0x47, 0x3f, 0x36, 0x1c, 0x87, 0x41, 0x5d, 0x37, 0xbd, 0x20, 0xd7, 0x0f, 0xd0, - 0xb5, 0x2b, 0x6d, 0xdf, 0x18, 0x65, 0xf7, 0x66, 0x70, 0x2e, 0x32, 0xb0, 0x5b, 0x3c, - 0xf1, 0x63, 0x0e, 0xe8, 0x59, 0x7a, 0xae, 0x19, 0x63, 0x3f, 0x35, 0x16, 0xa8, 0x55, - 0x5a, 0xc5, 0xbe, 0x32, 0xc6, 0x75, 0xbe, 0x18, 0x17, 0xef, 0xbf, 0xfd, 0x93, 0x69, - 0x04, 0xbd, 0x73, 0x4d, 0x97, 0x23, 0x3a, 0xaa, 0x38, 0x3b, 0x41, 0xba, 0x6d, 0x27, - 0xf6, 0x42, 0x84, 0x7d, 0x1e, 0xda, 0xcb, 0x4b, 0xf8, 0x22, 0xe6, 0x70, 0x80, 0x86, - 0x75, 0x18, 0xae, 0x5a, 0x8f, 0x0a, 0x43, 0xe6, 0xed, 0x53, 0x0c, 0xb2, 0xe8, 0xae, - 0x83, 0x88, 0x60, 0xad, 0xc8, 0x8a, 0xac, 0xc7, 0xbd, 0x6a, 0x00, 0xae, 0x0c, 0x19, - 0xff, 0x45, 0x33, 0xa4, 0x85, 0xef, 0xde, 0x08, 0x2b, 0xc5, 0x21, 0x40, 0x18, 0x2b, - 0x23, 0x4d, 0x1a, 0x0d, 0x0e, 0xeb, 0xdf, 0xb9, 0x87, 0x75, 0x98, 0xe0, 0x34, 0x7f, - 0xb1, 0x00, 0x1e, 0x15, 0xb5, 0xd4, 0x44, 0x6e, 0x76, 0x6c, 0xde, 0x25, 0xef, 0x79, - 0x87, 0x40, 0xe0, 0xbd, 0xf9, 0x94, 0xd9, 0x73, 0x9b, 0xbe, 0x55, 0x38, 0xa0, 0xae, - 0x0f, 0x07, 0x6c, 0x58, 0x2c, 0x0f, 0x5b, 0xa8, 0x78, 0xb9, 0x9b, 0x82, 0x49, 0xdb, - 0x1d, 0x7e, 0x95, 0x05, 0x6c, 0x98, 0xaf, 0x08, 0x3d, 0x98, 0xcb, 0x0e, 0xd9, 0xe3, - 0xf7, 0x43, 0x6e, 0x1c, 0x76, 0x43, 0x76, 0x6f, 0x96, 0x6b, 0x83, 0xe9, 0x99, 0x20, - 0x6e, 0xbd, 0x13, 0x93, 0xb9, 0xb2, 0xa7, 0xf4, 0x14, 0x48, 0x0f, 0xa0, 0x17, 0x48, - 0x00, 0x69, 0xf8, 0x5c, 0x77, 0x49, 0xc4, 0x35, 0xae, 0x2f, 0xba, 0x2d, 0xdc, 0x10, - 0x38, 0xd5, 0x47, 0xd8, 0x48, 0x54, 0x81, 0x7e, 0xf3, 0x96, 0x35, 0xc2, 0x98, 0x27, - 0xaa, 0xd8, 0x67, 0x26, 0xc9, 0xad, 0xe3, 0xb2, 0x65, 0xb9, 0x08, 0x6c, 0x8b, 0x5b, - 0x75, 0xef, 0x56, 0xfe, 0x4b, 0xd8, 0xb4, 0xd6, 0x28, 0x93, 0x89, 0x5b, 0x3f, 0xd2, - 0x73, 0x4f, 0xda, 0xc4, 0x64, 0x15, 0x6d, 0x7e, 0x5e, 0xbc, 0x7e, 0xcf, 0x1d, 0x83, - 0xb8, 0x6f, 0x65, 0x96, 0x37, 0xe3, 0xb1, 0x42, 0xc1, 0x64, 0x96, 0x3b, 0x8c, 0xdc, - 0xf4, 0xba, 0x4f, 0x40, 0x35, 0xdf, 0xfc, 0x5a, 0x78, 0x94, 0x58, 0x84, 0x77, 0x81, - 0x91, 0x8a, 0xc7, 0x2f, 0xc1, 0x8b, 0xbb, 0xf5, 0x11, 0x00, 0x32, 0xe6, 0x6d, 0x75, - 0xb3, 0x17, 0x1e, 0xf4, 0xb5, 0x13, 0x29, 0x01, 0x64, 0xa7, 0x7b, 0x42, 0xb0, 0xa4, - 0xcf, 0xb8, 0x96, 0x39, 0xab, 0x23, 0x84, 0x5e, 0x1a, 0xa2, 0xa4, 0x52, 0xf3, 0x73, - 0x1c, 0x8c, 0xb6, 0x50, 0x82, 0xa6, 0x22, 0xa7, 0xc2, 0xe0, 0x01, 0x3e, 0xa4, 0x7d, - 0x0b, 0xdd, 0x42, 0xd6, 0x99, 0x04, 0x66, 0x64, 0x9a, 0x90, 0x5c, 0x68, 0x4c, 0x32, - 0x51, 0x71, 0x6d, 0x61, 0xf7, 0x60, 0xd5, 0x3d, 0xe6, 0xe3, 0xf7, 0x90, 0xfb, 0xa7, - 0xf5, 0xf1, 0xf4, 0xde, 0x26, 0x71, 0x13, 0xbd, 0xfc, 0xd7, 0x42, 0x28, 0x22, 0x33, - 0x0b, 0x32, 0xd5, 0x8e, 0x67, 0x77, 0x76, 0x5f, 0x22, 0xa4, 0x11, 0x63, 0x44, 0xee, - 0xb6, 0x5b, 0x2e, 0xc5, 0x16, 0x39, 0x3a, 0xb3, 0x75, 0x1b, 0x53, 0x56, 0xd2, 0xb0, - 0xc9, 0x50, 0x0c, 0x0f, 0x3e, 0x46, 0x91, 0x81, 0x03, 0x5b, 0xc3, 0x66, 0x0f, 0x0b, - 0x8f, 0x9f, 0xbe, 0x6e, 0x40, 0xb5, 0xe8, 0x9c, 0xb7, 0x9b, 0x06, 0x37, 0x14, 0xca, - 0x75, 0xe7, 0x2e, 0x2e, 0x10, 0x0a, 0x10, 0xd6, 0x3b, 0xf7, 0x84, 0xdf, 0x08, 0x20, - 0xef, 0x25, 0xf8, 0xef, 0x40, 0xfe, 0x5f, 0x05, 0xfb, 0x95, 0x68, 0x3f, 0x91, 0x05, - 0xff, 0x3c, 0xb2, 0xd2, 0x19, 0xab, 0x76, 0x60, 0x5a, 0x06, 0x4f, 0x69, 0x21, 0x9f, - 0x1d, 0xc0, 0xd0, 0x0b, 0x3b, 0x48, 0x64, 0x2f, 0x97, 0x0d, 0xc0, 0x0c, 0xca, 0x4b, - 0x8b, 0x43, 0x30, 0x8b, 0xe1, 0x82, 0x86, 0xec, 0x5a, 0x42, 0x88, 0xd6, 0x00, 0xa3, - 0x78, 0x5c, 0xb6, 0x22, 0xd4, 0x68, 0xa4, 0xc6, 0x96, 0x9b, 0x37, 0x92, 0xf2, 0x48, - 0x50, 0x27, 0xd0, 0xad, 0x9a, 0xa4, 0xa9, 0xc2, 0xcc, 0x97, 0x2f, 0x9e, 0xe5, 0x19, - 0x0a, 0x95, 0xb1, 0xeb, 0x05, 0x8d, 0xdd, 0xd8, 0xc0, 0x8e, 0x7d, 0x75, 0x3f, 0x5e, - 0x01, 0x1b, 0x2b, 0xcf, 0xee, 0x1d, 0x52, 0xc1, 0xc4, 0xf2, 0xca, 0xcd, 0xa3, 0x0b, - 0xdb, 0x69, 0x30, 0x65, 0x3c, 0x0c, 0xc4, 0x48, 0x6e, 0x60, 0xe8, 0x9f, 0xa8, 0x49, - 0xb3, 0x20, 0x83, 0xba, 0x9d, 0xb4, 0x53, 0xfb, 0x8d, 0xf6, 0x83, 0xcd, 0x68, 0x75, - 0x4c, 0x87, 0xda, 0xa7, 0x31, 0xf5, 0x70, 0xa7, 0xa4, 0x06, 0x0a, 0xf0, 0xce, 0x70, - 0x0d, 0x31, 0xbc, 0xa7, 0xe7, 0x4b, 0x3e, 0x3b, 0xa3, 0xd0, 0xe8, 0xa6, 0x39, 0x2a, - 0x06, 0x2b, 0x8e, 0x86, 0xd9, 0xd7, 0xd0, 0x0b, 0x21, 0x70, 0x1e, 0x7b, 0x06, 0x2e, - 0x06, 0xb1, 0xbc, 0xd8, 0x2a, 0x01, 0xd3, 0x75, 0x62, 0x6f, 0xbf, 0x87, 0x2d, 0x27, - 0xfa, 0x45, 0x11, 0xf5, 0xf8, 0xcf, 0x8c, 0x9a, 0xbc, 0xef, 0x2a, 0x99, 0x01, 0x76, - 0xae, 0x33, 0x93, 0x25, 0xd5, 0xa5, 0x88, 0xda, 0x57, 0x96, 0xfa, 0xae, 0x5b, 0xab, - 0x7c, 0x82, 0x97, 0x7c, 0x0f, 0xf7, 0x97, 0x09, 0x3e, 0x2c, 0x1f, 0x3a, 0xe8, 0x55, - 0xf6, 0x5a, 0xea, 0x91, 0xe1, 0x31, 0x2f, 0xc6, 0xb8, 0xa4, 0x35, 0x1a, 0x2e, 0xc0, - 0x3e, 0x02, 0xe5, 0xd0, 0x2f, 0x53, 0x35, 0x4b, 0x05, 0x2f, 0xd3, 0xda, 0x0d, 0xff, - 0x82, 0xcd, 0x1f, 0x55, 0xeb, 0xca, 0x57, 0xb6, 0x33, 0x7c, 0x85, 0x93, 0x8a, 0x79, - 0x81, 0x3d, 0x20, 0x21, 0xd6, 0x09, 0x4c, 0x68, 0xb3, 0x75, 0xe9, 0x84, 0xf6, 0x83, - 0x93, 0x30, 0x08, 0x71, 0xe3, 0x48, 0xfc, 0x52, 0x36, 0xcc, 0xa6, 0x33, 0x05, 0x44, - 0xe5, 0x46, 0x39, 0xb5, 0x41, 0x87, 0x01, 0xff, 0x4c, 0xc4, 0x5a, 0x31, 0xf6, 0x2e, - 0xdd, 0x84, 0x3d, 0xbb, 0xdc, 0x5a, 0xa7, 0x27, 0xab, 0x79, 0xb4, 0x42, 0x68, 0x3c, - 0x49, 0x56, 0xbb, 0xb1, 0x95, 0xa4, 0xfa, 0x66, 0xdc, 0x9c, 0xd5, 0x42, 0xc7, 0x6b, - 0x91, 0x50, 0xc8, 0x4b, 0xf8, 0x90, 0x78, 0x99, 0x42, 0xf5, 0x5c, 0x20, 0x0b, 0x77, - 0x3e, 0xcd, 0xd7, 0x99, 0x2c, 0xff, 0x3e, 0xca, 0x24, 0xde, 0x3e, 0x09, 0x84, 0xe1, - 0x0e, 0x68, 0xae, 0x38, 0x75, 0x34, 0xb9, 0x6c, 0xde, 0x37, 0x92, 0xf1, 0x35, 0xbf, - 0x5f, 0x68, 0x78, 0x7d, 0x37, 0x0c, 0xa8, 0xc4, 0xc4, 0x07, 0x4d, 0xc5, 0xd6, 0x01, - 0xae, 0x90, 0x49, 0x54, 0x37, 0xc3, 0xc2, 0xd4, 0x8a, 0x3d, 0x96, 0x66, 0x83, 0xac, - 0x05, 0x16, 0x0b, 0x7a, 0x84, 0xea, 0xa7, 0xaa, 0xb7, 0x40, 0x09, 0xe5, 0x7a, 0x85, - 0xf7, 0xbf, 0x68, 0xa2, 0xe4, 0x82, 0x00, 0x0f, 0x82, 0x9c, 0x54, 0x50, 0x73, 0xa1, - 0x5d, 0x5c, 0xd0, 0xfc, 0xc5, 0x74, 0x39, 0xa4, 0x35, 0x0e, 0xaf, 0x09, 0x8d, 0xfb, - 0x82, 0xa0, 0x85, 0xea, 0x8a, 0x4a, 0xf6, 0xfa, 0x83, 0x81, 0xf0, 0x65, 0x88, 0x19, - 0xea, 0xb4, 0x83, 0xf6, 0x5b, 0x32, 0x5d, 0x5a, 0xed, 0xa1, 0x52, 0x32, 0xcf, 0xad, - 0xec, 0x75, 0xab, 0x18, 0x66, 0xe4, 0xc0, 0x15, 0x5a, 0x9c, 0x74, 0xa7, 0xa5, 0x7c, - 0xcf, 0x34, 0xc4, 0x83, 0xac, 0x7d, 0xa1, 0x58, 0x8a, 0x1b, 0x6b, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0xf1, 0x10, 0x40, 0xf9, 0x4c, 0xf7, 0x8f, 0xad, 0x89, 0xbf, 0x11, 0xfe, 0xd6, - 0x9a, 0xa0, 0xd8, 0x31, 0x05, 0xad, 0xac, 0xdd, 0x4e, 0x5f, 0x04, 0xa6, 0x24, 0x24, - 0x02, 0x3c, 0x9b, 0x9e, 0x33, 0xc4, 0xfb, 0x7f, 0x12, 0xbd, 0xf2, 0x1f, 0x07, 0xf2, - 0x65, 0xc5, 0x37, 0xd5, 0x1c, 0x65, 0x51, 0xf4, 0x61, 0x7b, 0x91, 0x5d, 0x21, 0x99, - 0x18, 0x39, 0xc3, 0xd0, 0xd3, 0x63, 0x93, 0xd6, 0x46, 0xe0, 0xa8, 0xa4, 0x15, 0x09, - 0x21, 0x7d, 0x0e, 0x7d, 0x2c, 0xa1, 0xa0, 0xa0, 0xd6, 0x77, 0xa3, 0xea, 0xca, 0x23, - 0xed, 0xeb, 0x07, 0xb7, 0x4e, 0x65, 0x2a, 0x0b, 0xc5, 0x0c, 0x6c, 0x08, 0x3a, 0x55, - 0xd6, 0xc7, 0x30, 0x6e, 0x74, 0x08, 0x6f, 0x47, 0x68, 0x93, 0x3a, 0xa2, 0x48, 0x73, - 0x68, 0x18, 0x67, 0xa7, 0x89, 0x3d, 0x77, 0xcb, 0x7f, 0x29, 0xb8, 0xc8, 0x47, 0xc5, - 0x83, 0xf2, 0xd0, 0x71, 0xa6, 0x86, 0x61, 0x6e, 0x20, 0x67, 0x19, 0xf7, 0x61, 0xae, - 0x39, 0xc1, 0x10, 0x44, 0x2e, 0x06, 0x16, 0x3d, 0x2b, 0x84, 0x59, 0x03, 0x60, 0x69, - 0x5d, 0x4e, 0x19, 0x84, 0x9e, 0x63, 0x4f, 0x24, 0xd9, 0xad, 0x39, 0x6c, 0x19, 0xff, - 0x83, 0xce, 0x74, 0xf4, 0x6e, 0x64, 0x5f, 0x93, 0x2e, 0x14, 0x1a, 0x41, 0x19, 0x59, - 0x36, 0xc8, 0x5d, 0x51, 0x44, 0x14, 0xf1, 0x12, 0xe6, 0x0b, 0x1a, 0x25, 0x37, 0xc3, - 0x8d, 0x6d, 0xc6, 0xc4, 0x63, 0x83, 0x05, 0xc9, 0xbd, 0x6c, 0x62, 0xe3, 0x66, 0xbc, - 0x63, 0x12, 0x3e, 0x3e, 0x6d, 0xd3, 0x6e, 0xed, 0xd3, 0x13, 0x6f, 0xce, 0x8d, 0xee, - 0xca, 0x2a, 0xa0, 0x9a, 0x32, 0x98, 0xa3, 0x9d, 0x83, 0x85, 0x9e, 0xfc, 0x9b, 0x2b, - 0x69, 0xcf, 0x9a, 0x7d, 0xee, 0x08, 0xa9, 0x8e, 0x4b, 0xe5, 0x58, 0xac, 0x79, 0x12, - 0xfd, 0xcb, 0x42, 0x20, 0x90, 0x75, 0x42, 0x02, 0x60, 0xf7, 0xca, 0xd0, 0xf2, 0xc0, - 0x1f, 0x2a, 0xfe, 0x33, 0x07, 0x3f, 0x26, 0x24, 0x9d, 0x94, 0x4f, 0x7a, 0x50, 0xdd, - 0x84, 0x83, 0x9b, 0xc3, 0xea, 0x7f, 0xde, 0xe4, 0xed, 0x71, 0x44, 0x9c, 0xf0, 0x75, - 0x33, 0xd2, 0x6e, 0x1e, 0x27, 0xa3, 0xef, 0xb0, 0x32, 0xc3, 0xa3, 0xb3, 0x4b, 0xd3, - 0x09, 0x26, 0x22, 0xd2, 0x06, 0x2a, 0xe5, 0x36, 0xef, 0x51, 0x49, 0xc4, 0x9b, 0x5b, - 0xc9, 0x47, 0x5e, 0xaf, 0xab, 0x6e, 0x67, 0x57, 0x61, 0x00, 0x8b, 0x0d, 0xad, 0xde, - 0xec, 0xaa, 0x60, 0x44, 0x70, 0xbb, 0xe0, 0xfa, 0xda, 0x25, 0x5d, 0x29, 0x0e, 0x92, - 0xb1, 0x90, 0xc2, 0xc2, 0xd8, 0xc2, 0xde, 0xe5, 0x45, 0x5d, 0x1f, 0xa9, 0xa9, 0xf3, - 0xdb, 0x77, 0x79, 0xb5, 0x84, 0x64, 0x34, 0x64, 0xaa, 0x80, 0x14, 0xba, 0x66, 0x99, - 0x4d, 0xe2, 0x55, 0x17, 0xf8, 0x39, 0x80, 0xe6, 0x6e, 0xe4, 0xf6, 0x23, 0x14, 0xae, - 0x6d, 0xbe, 0xf4, 0x52, 0xd5, 0xd3, 0x8b, 0x0a, 0x16, 0xf3, 0x99, 0x1f, 0x36, 0xd8, - 0xa8, 0xb3, 0x9d, 0xdc, 0x0d, 0x55, 0x95, 0xee, 0xd9, 0x87, 0x62, 0x87, 0x8c, 0xdf, - 0x3f, 0x4a, 0x2e, 0xdc, 0x5c, 0xda, 0x77, 0xd5, 0xfe, 0x4f, 0xaf, 0x63, 0xa1, 0x5f, - 0x56, 0x8a, 0x54, 0x0d, 0xa5, 0x7d, 0xd9, 0xbe, 0xb6, 0xfb, 0x1a, 0x97, 0x7c, 0xcb, - 0x91, 0xb4, 0xd7, 0x9c, 0xb3, 0x9b, 0x28, 0x91, 0x1a, 0x29, 0xe7, 0xbf, 0x02, 0x8a, - 0xc6, 0x10, 0x37, 0x96, 0xdf, 0xb6, 0xb2, 0x09, 0x67, 0x23, 0x9a, 0xd3, 0x73, 0xc3, - 0x8c, 0x53, 0xf6, 0xdf, 0x18, 0x23, 0xd4, 0x95, 0x0a, 0x02, 0x83, 0xe9, 0x9b, 0x9c, - 0x06, 0xab, 0x29, 0x66, 0x66, 0x7c, 0x9d, 0xf6, 0x77, 0x71, 0x6b, 0x0c, 0xad, 0xed, - 0x81, 0x8d, 0xf9, 0xe4, 0x49, 0xc0, 0x72, 0xe2, 0x2f, 0x9d, 0x98, 0xbb, 0x0f, 0x9b, - 0x03, 0xbd, 0x5f, 0xd0, 0x13, 0xfc, 0xef, 0x3e, 0xd6, 0xa4, 0x9a, 0xeb, 0x98, 0x72, - 0x02, 0x54, 0x08, 0x7e, 0xf7, 0x28, 0xe3, 0x19, 0x47, 0xff, 0xe8, 0xf7, 0x66, 0xe6, - 0x3e, 0xe4, 0x6f, 0xf2, 0x08, 0x16, 0xd5, 0xfa, 0x8f, 0xf5, 0x5a, 0x26, 0x39, 0x89, - 0x61, 0x49, 0x0a, 0xb9, 0xae, 0x36, 0x6f, 0xc5, 0xa2, 0xd1, 0x99, 0x6e, 0xd6, 0x93, - 0xcc, 0xca, 0x82, 0x35, 0x6f, 0x60, 0x0a, 0xb0, 0x99, 0xf6, 0xec, 0xa8, 0xbf, 0xe6, - 0x45, 0x27, 0x0d, 0x3f, 0x95, 0xed, 0xba, 0x5b, 0x0d, 0xe7, 0xa3, 0x28, 0x19, 0x23, - 0x3b, 0xcc, 0x75, 0x4a, 0x5c, 0xe2, 0xe5, 0xea, 0x07, 0x84, 0x2e, 0x5f, 0xf2, 0xce, - 0xbe, 0x62, 0xad, 0x76, 0xe8, 0xef, 0xf8, 0xd1, 0x5e, 0xa4, 0xc2, 0x4a, 0x5f, 0x20, - 0x78, 0x68, 0x31, 0x9a, 0x5a, 0xf6, 0xb0, 0x35, 0xbe, 0x3f, 0x44, 0xf4, 0x34, 0x09, - 0x4f, 0x6e, 0x52, 0x5b, 0xe6, 0x14, 0xda, 0xc9, 0x20, 0xa3, 0x30, 0xbd, 0xfb, 0x26, - 0xd7, 0x5f, 0xe7, 0xb4, 0xb3, 0x65, 0xd0, 0x94, 0x45, 0x92, 0x50, 0xaa, 0xa5, 0x54, - 0x44, 0x89, 0xfb, 0x1d, 0x99, 0x25, 0x81, 0x80, 0x0a, 0x77, 0xb8, 0x91, 0x21, 0x57, - 0xfc, 0x97, 0x13, 0xaa, 0xac, 0x25, 0xb4, 0xc2, 0x6e, 0xb0, 0x3f, 0x71, 0x66, 0x46, - 0x61, 0x9a, 0xf0, 0x24, 0x56, 0xae, 0x69, 0x59, 0x62, 0xfe, 0x5e, 0x93, 0x1a, 0x63, - 0xb5, 0xc7, 0x90, 0x52, 0xec, 0xd3, 0x33, 0xe1, 0x84, 0x12, 0xdb, 0x91, 0xe1, 0x5f, - 0x7c, 0xbc, 0x70, 0xb4, 0xcd, 0x7e, 0x8e, 0x3c, 0x95, 0x1f, 0x35, 0x85, 0x72, 0xe3, - 0x77, 0x67, 0xe7, 0xd5, 0x27, 0x04, 0xa6, 0x72, 0x1b, 0x30, 0xef, 0xc4, 0x10, 0x17, - 0xae, 0x4d, 0x23, 0x15, 0x58, 0xc5, 0xc8, 0x2c, 0xc7, 0xdd, 0x7e, 0x33, 0x56, 0xc0, - 0x9d, 0xc2, 0x49, 0x06, 0xf0, 0x43, 0x8d, 0xfc, 0xc3, 0x00, 0x85, 0x6a, 0xc2, 0xce, - 0xd8, 0xf7, 0x7f, 0xa8, 0x01, 0x57, 0x36, 0xc6, 0x61, 0xe8, 0x02, 0x48, 0xae, 0xeb, - 0x77, 0x48, 0x74, 0xaa, 0x79, 0xd2, 0x90, 0xb8, 0xf5, 0x02, 0x7a, 0x0a, 0x50, 0x95, - 0x37, 0xfc, 0x7c, 0x68, 0x9b, 0x7a, 0xd8, 0x61, 0x16, 0xcf, 0xec, 0x26, 0x47, 0xcc, - 0xaa, 0xe1, 0xc7, 0x4b, 0x41, 0x6f, 0x3e, 0x6a, 0xe8, 0xf7, 0xcc, 0x60, 0xea, 0xaf, - 0x7b, 0x6a, 0x59, 0x0d, 0x51, 0x54, 0x41, 0x38, 0xe1, 0x73, 0x29, 0x45, 0x60, 0x3a, - 0x53, 0x46, 0x2c, 0x60, 0xe1, 0xf6, 0xcb, 0x0c, 0x9c, 0xa0, 0x39, 0x0c, 0x48, 0x82, - 0x24, 0xc3, 0x13, 0x26, 0x9f, 0xcd, 0x59, 0xfc, 0xb6, 0x11, 0xfb, 0x2d, 0x9b, 0x4c, - 0x8f, 0xa6, 0x01, 0xbb, 0x1c, 0xb8, 0xd0, 0x7d, 0x79, 0x7b, 0xf5, 0xde, 0x52, 0xbc, - 0xee, 0xb0, 0x23, 0x01, 0xc8, 0x96, 0x2a, 0xc1, 0xfc, 0x04, 0x91, 0xdc, 0x81, 0xaf, - 0xfd, 0x6c, 0x1e, 0xbf, 0x89, 0xa1, 0x3d, 0x6f, 0x29, 0x0e, 0xda, 0x5d, 0x5c, 0xef, - 0x38, 0x22, 0x15, 0xc5, 0xe9, 0x51, 0xd7, 0x13, 0x05, 0xef, 0x33, 0xd9, 0x73, 0x71, - 0x26, 0xd0, 0xe6, 0x62, 0x90, 0x5f, 0x12, 0x50, 0x92, 0x6f, 0x6a, 0x22, 0x99, 0x90, - 0xe3, 0x8f, 0x69, 0xad, 0x9a, 0x91, 0x92, 0xb3, 0x02, 0xf2, 0x6b, 0xdd, 0xa4, 0x65, - 0xd9, 0x0b, 0x94, 0xb1, 0x2c, 0x57, 0xfa, 0x3f, 0xd6, 0x93, 0x00, 0x83, 0xf1, 0x84, - 0x43, 0x8d, 0x8a, 0x88, 0x9d, 0x3f, 0x5e, 0xce, 0xa2, 0xc6, 0xd2, 0x3d, 0x67, 0x36, - 0xf2, 0xa0, 0xf1, 0x8e, 0x26, 0xf4, 0xfa, 0x45, 0xd1, 0xbe, 0x8f, 0x3d, 0xc4, 0xa7, - 0x07, 0x13, 0x7e, 0x95, 0xd2, 0xad, 0x59, 0x4f, 0x6c, 0x03, 0xd2, 0x49, 0x23, 0x06, - 0x7a, 0xe4, 0x7f, 0xd6, 0x42, 0x5e, 0xfb, 0x9c, 0x1d, 0x50, 0x4e, 0x6f, 0xd5, 0x57, - 0x53, 0x40, 0x94, 0x56, 0x01, 0xfe, 0x80, 0x6f, 0x57, 0x56, 0xac, 0xb5, 0x62, 0xf1, - 0x3c, 0x0c, 0xa1, 0xd8, 0x03, 0xa1, 0x95, 0xc2, 0xeb, 0xb2, 0xef, 0x02, 0xac, 0x33, - 0xe6, 0xa8, 0x8d, 0xea, 0x07, 0x5b, 0xa9, 0x96, 0xd3, 0xc3, 0x36, 0x64, 0x8e, 0x86, - 0x94, 0xd3, 0xa1, 0x9d, 0x3d, 0xca, 0x53, 0x1b, 0xeb, 0x50, 0xd4, 0x32, 0x7c, 0x5c, - 0x0c, 0x23, 0xcb, 0x7c, 0xfd, 0xb0, 0x8c, 0xa7, 0xcf, 0x2c, 0xac, 0x6b, 0xc1, 0x39, - 0xd0, 0x74, 0x14, 0x73, 0xd3, 0x76, 0x02, 0x9c, 0xb4, 0xab, 0x6b, 0xf0, 0x54, 0x55, - 0x7c, 0xe2, 0x94, 0xc7, 0x28, 0xa4, 0x68, 0x7d, 0x57, 0xec, 0x89, 0x09, 0xff, 0x51, - 0xa4, 0xd0, 0x2f, 0x9d, 0xcd, 0x11, 0x19, 0x3d, 0x7d, 0x1c, 0x9f, 0xda, 0xe6, 0xa1, - 0x73, 0x96, 0xa1, 0xbf, 0x57, 0xa9, 0x94, 0x93, 0x4f, 0x5e, 0x7a, 0x59, 0xf0, 0x45, - 0xde, 0xbe, 0xaf, 0xf6, 0x2e, 0xf3, 0x26, 0xb9, 0x47, 0xf2, 0xa8, 0xb4, 0x95, 0x55, - 0xe4, 0xd9, 0x9b, 0x3b, 0xf5, 0xc8, 0x1f, 0xf9, 0xfe, 0x31, 0x4e, 0x04, 0x7a, 0xf1, - 0x52, 0x50, 0x8f, 0x57, 0x01, 0x5c, 0xa4, 0x02, 0xc6, 0x7d, 0x92, 0x5c, 0x99, 0xac, - 0xea, 0x3e, 0xe8, 0xcc, 0x4b, 0x00, 0x8c, 0x5c, 0xb4, 0x39, 0x66, 0xe7, 0x14, 0xef, - 0x48, 0x0f, 0xd0, 0x5e, 0x07, 0xc7, 0xb2, 0xdd, 0xa9, 0xaa, 0x39, 0x66, 0x11, 0x3e, - 0xaa, 0x29, 0x3d, 0x3f, 0x62, 0x2b, 0x30, 0x9d, 0x64, 0x80, 0x3c, 0xe1, 0xe6, 0x37, - 0x8b, 0x6a, 0xac, 0x4f, 0xab, 0x52, 0x7c, 0x43, 0xcd, 0x45, 0xed, 0x0a, 0x3c, 0x1a, - 0x4b, 0x9f, 0xb1, 0x8d, 0xcc, 0xcf, 0xcd, 0xb6, 0xac, 0x0c, 0x24, 0x21, 0x63, 0x9c, - 0xda, 0x00, 0x75, 0xa2, 0x0d, 0xc5, 0x11, 0x1b, 0x8d, 0x3d, 0x31, 0x99, 0x49, 0x5b, - 0xd9, 0x13, 0x3d, 0xba, 0xb9, 0x45, 0x41, 0x41, 0x0e, 0x4f, 0xba, 0x92, 0xc7, 0xb6, - 0x06, 0xa5, 0xcb, 0x12, 0x2f, 0x14, 0x0c, 0xf1, 0xa3, 0x59, 0x6f, 0x27, 0x88, 0xf3, - 0xc8, 0xb9, 0x26, 0x60, 0xf1, 0x4c, 0xb6, 0x5a, 0xf5, 0xdd, 0x23, 0xdf, 0xdb, 0xac, - 0x13, 0x71, 0xec, 0xf4, 0xb3, 0x37, 0x12, 0xfe, 0xd2, 0x29, 0x2c, 0x44, 0xf7, 0x08, - 0x34, 0xcf, 0x96, 0xc0, 0x5d, 0x58, 0x82, 0x7e, 0x69, 0xbf, 0xc2, 0xe6, 0x96, 0xfa, - 0x08, 0x74, 0x86, 0x9c, 0x02, 0xf3, 0xdc, 0xa1, 0x1c, 0x3b, 0x90, 0xcb, 0x21, 0x4e, - 0x68, 0xbc, 0x1c, 0xae, 0x03, 0x9d, 0x7a, 0x14, 0x6c, 0xdc, 0x1d, 0x60, 0x9d, 0x7a, - 0x6b, 0x3f, 0xd5, 0xd4, 0x61, 0xb0, 0x95, 0x1c, 0x82, 0xcf, 0xb3, 0xe7, 0x63, 0xfa, - 0xd2, 0xd1, 0xbc, 0x76, 0x78, 0xcd, 0xf8, 0x27, 0x79, 0xf8, 0xfd, 0x5a, 0x1c, 0xe2, - 0x2a, 0x8d, 0x3c, 0x45, 0x47, 0xab, 0xd9, 0x59, 0x83, 0x8a, 0x46, 0xfb, 0x80, 0xaf, - 0xe0, 0x1f, 0x8e, 0xcc, 0x99, 0x31, 0x51, 0x3b, 0x19, 0x62, 0xec, 0x54, 0x08, 0x56, - 0xcb, 0x18, 0x93, 0x87, 0xcf, 0xbf, 0xcc, 0x0f, 0x7c, 0x68, 0x22, 0x3c, 0xba, 0x47, - 0xfb, 0x0c, 0x9b, 0x48, 0x6e, 0x4d, 0x99, 0x17, 0x19, 0x41, 0xf7, 0x67, 0x5a, 0x8b, - 0x46, 0x32, 0x8a, 0x3b, 0xc1, 0x09, 0xbf, 0x07, 0xc6, 0x6d, 0x5e, 0xde, 0x77, 0x1c, - 0xc4, 0xc7, 0x4c, 0xe8, 0x03, 0x33, 0x82, 0x91, 0x91, 0xee, 0xdc, 0x49, 0x35, 0x08, - 0xa6, 0x44, 0x53, 0x0a, 0x61, 0x44, 0xf2, 0x2d, 0xcf, 0x97, 0x52, 0x5a, 0x4c, 0xdc, - 0xa1, 0xad, 0x71, 0x07, 0x3b, 0x08, 0x0b, 0x73, 0xea, 0x45, 0x49, 0xf5, 0x40, 0x1b, - 0xff, 0x43, 0x18, 0x26, 0x8e, 0x6a, 0xd6, 0x37, 0x36, 0x31, 0x57, 0xa1, 0x9a, 0x53, - 0xf1, 0x23, 0xa0, 0xb0, 0xe1, 0x6d, 0x0b, 0x77, 0xf0, 0x20, 0x28, 0xda, 0x46, 0x41, - 0x00, 0xfd, 0xe7, 0x6d, 0x83, 0xdd, 0x0b, 0xb2, 0x24, 0xf7, 0xb5, 0x7a, 0x00, 0xc0, - 0x2f, 0x68, 0xae, 0x64, 0x8f, 0xdc, 0x52, 0x99, 0x57, 0xa1, 0x04, 0x90, 0xdc, 0xe1, - 0xfd, 0xdb, 0xb0, 0x90, 0x4f, 0x0d, 0x51, 0x8b, 0xb3, 0x87, 0x54, 0x40, 0x19, 0x98, - 0x3b, 0x61, 0x69, 0x75, 0xa7, 0x8e, 0x74, 0xd8, 0x54, 0xfd, 0xdc, 0x49, 0xb2, 0x55, - 0x16, 0x7b, 0x55, 0xef, 0x4b, 0xee, 0x46, 0x56, 0x68, 0xb2, 0x0e, 0xa4, 0x11, 0x8c, - 0xa5, 0x69, 0xae, 0x48, 0x0e, 0x0f, 0x6e, 0x5e, 0x04, 0x3a, 0x35, 0x7b, 0x36, 0xd3, - 0xab, 0x36, 0xc8, 0x61, 0xf2, 0x27, 0x83, 0x01, 0xdc, 0xe5, 0x76, 0x74, 0xd5, 0x07, - 0x3b, 0x3a, 0x6f, 0x51, 0x03, 0xa0, 0x79, 0x3a, 0xf1, 0xb7, 0xd4, 0x6f, 0x95, 0x7e, - 0x22, 0xd8, 0xd2, 0x58, 0x3b, 0xf1, 0x81, 0x83, 0x6c, 0x3b, 0xe9, 0x93, 0x0b, 0xac, - 0x8f, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x60, 0xe9, 0x68, 0xaa, 0x71, 0x09, 0x87, 0x0b, 0xbe, 0xd1, - 0x7d, 0xf5, 0xf8, 0x88, 0xc8, 0xca, 0x14, 0x67, 0xae, 0x17, 0xdb, 0xbc, 0xde, 0x31, - 0xc1, 0x10, 0x5c, 0xb5, 0xbd, 0xa8, 0x8a, 0xc6, 0xc6, 0x27, 0x00, 0x2c, 0xe2, 0x1c, - 0x02, 0x14, 0x0f, 0xfe, 0x81, 0xec, 0x58, 0xbf, 0x1e, 0x6d, 0x1b, 0xb7, 0xaa, 0xad, - 0xa4, 0x1f, 0xba, 0x0b, 0xb5, 0x88, 0x77, 0x8a, 0x7f, 0x65, 0x20, 0x2a, 0xd8, 0x11, - 0xea, 0x73, 0xd2, 0x6c, 0x74, 0x55, 0x03, 0x95, 0xaf, 0xf7, 0x53, 0x25, 0x10, 0x7c, - 0x9b, 0x3f, 0x9a, 0xe9, 0xdc, 0xdc, 0xd8, 0x6e, 0xd0, 0x81, 0xa2, 0xe7, 0x42, 0x47, - 0x19, 0xa3, 0xd1, 0x85, 0xb7, 0xe0, 0xa4, 0x3a, 0x47, 0x2e, 0x29, 0x8a, 0xc0, 0xaf, - 0xdc, 0x52, 0x87, 0xd7, 0xad, 0x12, 0x4c, 0xd9, 0x40, 0x5a, 0x62, 0xcd, 0x1c, 0xa0, - 0x8b, 0x28, 0x2e, 0xfe, 0xf7, 0xf9, 0x28, 0xdf, 0x76, 0xe2, 0x82, 0x1a, 0x41, 0x84, - 0x13, 0xeb, 0x7c, 0xea, 0xa5, 0xff, 0x12, 0x90, 0xb0, 0x3e, 0xc9, 0x1c, 0xe6, 0xdd, - 0x28, 0x13, 0x0c, 0x3a, 0xb0, 0xb2, 0x3b, 0x60, 0x2b, 0xd5, 0xbe, 0x5d, 0xc2, 0x60, - 0x03, 0xaa, 0xe0, 0x4b, 0x33, 0xd7, 0xbd, 0x25, 0x90, 0xe9, 0x0c, 0x8c, 0x38, 0x8e, - 0xa7, 0x95, 0x51, 0x22, 0xdb, 0xac, 0xa6, 0x7b, 0x30, 0x39, 0x5a, 0x92, 0x8b, 0x57, - 0xb8, 0x57, 0x51, 0x23, 0x20, 0x5a, 0xe1, 0x91, 0x52, 0xe4, 0x1e, 0x00, 0x29, 0x31, - 0xb4, 0x57, 0x46, 0x19, 0x8e, 0x5d, 0xd9, 0x57, 0x1a, 0x56, 0xa7, 0xe0, 0xd4, 0x23, - 0xff, 0x27, 0x98, 0x9d, 0x3e, 0xb4, 0x17, 0xec, 0xd3, 0xc3, 0x09, 0x3f, 0xb8, 0x2c, - 0x56, 0x58, 0xe2, 0x96, 0x24, 0xc5, 0x32, 0x19, 0xa6, 0x0c, 0xd0, 0xa8, 0xc4, 0xda, - 0x36, 0x7e, 0x29, 0xa7, 0x17, 0x79, 0xa7, 0x30, 0x32, 0x98, 0x5a, 0x3d, 0x1f, 0xd0, - 0x3d, 0xd4, 0xd0, 0x6e, 0x05, 0x56, 0x6f, 0x3b, 0x84, 0x36, 0x7c, 0xf0, 0xfa, 0xee, - 0x9b, 0xc3, 0xbd, 0x7a, 0x3a, 0x60, 0x6a, 0x9f, 0xdb, 0x84, 0x9c, 0x5d, 0x82, 0xd0, - 0xa6, 0x19, 0x23, 0xc2, 0xe5, 0xd8, 0xaa, 0x63, 0xa8, 0xa5, 0x0c, 0x38, 0xbd, 0x03, - 0x87, 0x72, 0xc4, 0x14, 0x3d, 0x8b, 0x7a, 0xcf, 0xd7, 0x4e, 0x72, 0xc0, 0x4d, 0x89, - 0x24, 0x8d, 0xff, 0x20, 0xfe, 0x8d, 0xc5, 0xec, 0x21, 0x49, 0x05, 0x4e, 0xa2, 0x41, - 0x64, 0xe8, 0x5f, 0x67, 0x44, 0xad, 0x0c, 0xac, 0xf1, 0xa8, 0xb7, 0x01, 0x26, 0xf4, - 0x82, 0xc0, 0x92, 0xed, 0x9f, 0x61, 0x27, 0xd2, 0x05, 0x0d, 0x12, 0xe8, 0x78, 0xa7, - 0x96, 0x53, 0xa1, 0xe8, 0x4d, 0xae, 0xc3, 0xeb, 0xe6, 0x2d, 0x5f, 0x6c, 0x4a, 0xbe, - 0x5c, 0xe9, 0x0a, 0x7f, 0xe2, 0xe5, 0x2a, 0x8d, 0x78, 0x46, 0xe8, 0xed, 0xf2, 0xf2, - 0xbc, 0xe0, 0x5a, 0x03, 0x7c, 0x82, 0x6f, 0x22, 0xca, 0xad, 0x12, 0x61, 0x46, 0x7d, - 0xcf, 0xb7, 0xd6, 0xb6, 0x13, 0x3d, 0xc2, 0x1e, 0x80, 0x96, 0xc7, 0xe9, 0xf8, 0xe9, - 0xe1, 0x0c, 0x1e, 0x3f, 0xac, 0x40, 0x58, 0xb6, 0x82, 0xc6, 0x8e, 0x54, 0xfa, 0xca, - 0xe0, 0xf9, 0xc2, 0xdd, 0x4d, 0x64, 0xd9, 0x04, 0x61, 0x52, 0xb4, 0x76, 0x23, 0x32, - 0x93, 0x9f, 0x17, 0xe6, 0xaa, 0xf7, 0xd8, 0xb9, 0xd3, 0x58, 0xe2, 0x21, 0x8d, 0x4e, - 0x0d, 0x69, 0xa4, 0xf1, 0x19, 0xe1, 0xc6, 0x4e, 0xec, 0x4c, 0x8b, 0x53, 0x28, 0x09, - 0x70, 0x71, 0x31, 0xf0, 0x1f, 0x55, 0xc7, 0xad, 0x04, 0xcf, 0xb6, 0x3f, 0x7c, 0x4a, - 0x3d, 0x0a, 0x2b, 0x0f, 0xfb, 0x0b, 0x05, 0xa6, 0xbe, 0x05, 0x5b, 0x8c, 0x94, 0xca, - 0x80, 0xbb, 0x0a, 0x1d, 0x13, 0xcd, 0x4c, 0xd6, 0x9a, 0xb9, 0x83, 0x04, 0xae, 0x25, - 0x15, 0xd5, 0xf7, 0x69, 0x9d, 0x4a, 0xbe, 0xe5, 0xc2, 0x0b, 0xe6, 0x09, 0xd8, 0x73, - 0x51, 0x10, 0x12, 0xf2, 0x34, 0xbd, 0x85, 0xa7, 0xef, 0xf5, 0xfb, 0x63, 0x4c, 0xff, - 0x26, 0x58, 0xba, 0x65, 0x16, 0x04, 0x85, 0x63, 0x09, 0x5e, 0xce, 0xfb, 0x30, 0x15, - 0xee, 0x3f, 0x03, 0xca, 0x52, 0xa1, 0x77, 0xf2, 0x61, 0xec, 0xdc, 0x26, 0xbc, 0x08, - 0x9d, 0x34, 0xc6, 0x40, 0x48, 0x46, 0xe9, 0xc6, 0x47, 0xfc, 0xfe, 0x98, 0xcc, 0x6a, - 0xcd, 0xbb, 0x46, 0x4f, 0x64, 0x27, 0x8a, 0xd8, 0xce, 0x9d, 0x1a, 0xe0, 0xd4, 0x15, - 0xbc, 0x0c, 0x05, 0x24, 0x5f, 0xdd, 0xaf, 0x4e, 0xbc, 0x8d, 0xc7, 0x03, 0xa8, 0x5c, - 0xb2, 0x70, 0xf7, 0x96, 0xad, 0x2d, 0x93, 0x7e, 0x2a, 0xc0, 0xd5, 0xe0, 0xa3, 0x48, - 0x21, 0x75, 0x80, 0x00, 0xaa, 0x59, 0xc9, 0xd4, 0x65, 0x24, 0x85, 0x29, 0x4e, 0xe0, - 0xab, 0x29, 0x69, 0x6b, 0x21, 0x43, 0x0f, 0xa5, 0x4d, 0xcf, 0xbf, 0x2b, 0x9c, 0x49, - 0xd1, 0x42, 0x06, 0x42, 0x09, 0xee, 0xee, 0xd4, 0xd4, 0x71, 0xff, 0xc0, 0x17, 0xd4, - 0xe2, 0x0a, 0x79, 0x6b, 0x09, 0x27, 0x80, 0x4c, 0x06, 0x1b, 0x9f, 0x4a, 0x70, 0x91, - 0xfe, 0x01, 0x5a, 0xda, 0x68, 0xfd, 0x84, 0x42, 0xe0, 0x18, 0x25, 0xc8, 0x8d, 0xfe, - 0x55, 0xcf, 0x5d, 0xe3, 0x89, 0x36, 0xf7, 0xce, 0x25, 0x31, 0x1b, 0x90, 0x2b, 0xa9, - 0x7a, 0x3c, 0x12, 0xa9, 0x5c, 0xfa, 0x1c, 0x3a, 0x59, 0x1b, 0x81, 0x8f, 0x60, 0x83, - 0x27, 0x09, 0xd9, 0xe4, 0x83, 0x9e, 0x41, 0x0f, 0xb3, 0x6b, 0x84, 0xf3, 0xac, 0x4f, - 0x07, 0x0f, 0xc3, 0x5e, 0x16, 0x19, 0x78, 0x25, 0x9e, 0x5b, 0x8e, 0xdc, 0x74, 0x4d, - 0x90, 0x91, 0x9a, 0xa7, 0x70, 0xbb, 0x36, 0x21, 0x51, 0x28, 0xe5, 0x82, 0xb5, 0x96, - 0x41, 0xe2, 0x38, 0x52, 0xe9, 0x58, 0xeb, 0x8f, 0xc3, 0xc0, 0xaa, 0x96, 0x15, 0x2b, - 0xa4, 0xf7, 0x7f, 0x13, 0x8d, 0x6a, 0x67, 0x12, 0xa3, 0xae, 0x32, 0x26, 0x01, 0x58, - 0x83, 0xf8, 0x1d, 0xb2, 0x3e, 0x58, 0x3c, 0x86, 0x9c, 0x4c, 0x71, 0x14, 0x3a, 0x6f, - 0xff, 0xd6, 0x5e, 0x8d, 0xfd, 0xc5, 0x0c, 0x99, 0xa2, 0xf1, 0xf3, 0x14, 0xcd, 0xcc, - 0x71, 0x35, 0x9e, 0x23, 0x5f, 0x1d, 0x7d, 0xc2, 0xb5, 0xf3, 0x8e, 0xf7, 0xb9, 0x70, - 0x84, 0x31, 0x63, 0xc0, 0x3f, 0x9d, 0xd4, 0x0a, 0x80, 0x15, 0xef, 0xdc, 0x87, 0x91, - 0x95, 0x6a, 0x3f, 0x3c, 0xed, 0xd9, 0xea, 0x64, 0xf8, 0xef, 0xa7, 0xa0, 0x81, 0x5a, - 0x70, 0x38, 0x1d, 0x71, 0x46, 0x78, 0x17, 0xbd, 0x04, 0xca, 0x52, 0x9a, 0xed, 0xe0, - 0x7f, 0xf6, 0x0d, 0x17, 0x6a, 0xed, 0x0f, 0x85, 0x5a, 0x2e, 0xae, 0xa8, 0x9e, 0xae, - 0xac, 0xa8, 0x93, 0x58, 0xc0, 0x81, 0x82, 0x6a, 0x08, 0x12, 0xa5, 0xbc, 0xa2, 0x8b, - 0xe1, 0x37, 0x3f, 0x08, 0x6d, 0xbd, 0xba, 0x7e, 0x43, 0xe2, 0x03, 0x21, 0x2c, 0x9f, - 0xed, 0x21, 0x47, 0x4b, 0xa1, 0x9a, 0x05, 0x5f, 0xfc, 0xc1, 0x79, 0x41, 0x2e, 0x89, - 0x3a, 0x74, 0x48, 0x32, 0x29, 0x8c, 0x5f, 0xe2, 0x4c, 0xc6, 0xb1, 0x86, 0x67, 0xf4, - 0x9b, 0x34, 0xdf, 0xb1, 0x23, 0x79, 0x26, 0x74, 0x19, 0xa9, 0xcb, 0x94, 0x03, 0xd8, - 0x16, 0x7d, 0x8d, 0x1e, 0x91, 0xd2, 0x81, 0x1a, 0x04, 0x3b, 0x29, 0x24, 0x3b, 0x06, - 0x9b, 0x37, 0x58, 0x78, 0x47, 0xdc, 0x6f, 0xcd, 0xdb, 0x18, 0x31, 0xbd, 0x1c, 0xc2, - 0x56, 0x7c, 0xa0, 0x33, 0xac, 0x40, 0xf7, 0x4a, 0xb6, 0x95, 0x5f, 0x68, 0x3b, 0x12, - 0xe4, 0xe8, 0x25, 0x4e, 0x4e, 0xa7, 0x60, 0xd3, 0x8b, 0x3f, 0x46, 0x79, 0x1c, 0x5c, - 0x4c, 0xb1, 0x2b, 0xc7, 0xcc, 0xb0, 0xed, 0x18, 0x65, 0xf2, 0x5d, 0x60, 0x1c, 0x30, - 0x3f, 0x81, 0xfb, 0x1f, 0xa1, 0xdb, 0x48, 0x53, 0x3d, 0x3d, 0x6b, 0x28, 0x8e, 0x4d, - 0x9a, 0x4d, 0xff, 0x8e, 0xc2, 0x1c, 0x96, 0xf5, 0x78, 0x39, 0x97, 0x10, 0xc8, 0x25, - 0xfe, 0x7e, 0x32, 0xf9, 0x3a, 0x8c, 0x07, 0x43, 0xf9, 0xeb, 0xd5, 0x4c, 0xc1, 0x51, - 0xc7, 0x61, 0x03, 0x37, 0xae, 0xbf, 0x7e, 0x9b, 0x91, 0x57, 0x20, 0xa5, 0x43, 0x51, - 0xd4, 0x9a, 0xb8, 0xc2, 0x2f, 0xa3, 0x49, 0x98, 0xdc, 0xf5, 0x83, 0xd4, 0x38, 0x73, - 0x61, 0xef, 0x3f, 0xf8, 0x6f, 0x50, 0xec, 0x53, 0xf4, 0x92, 0x49, 0xe4, 0xad, 0x34, - 0x96, 0x03, 0x06, 0x6f, 0xc9, 0xc6, 0x61, 0xd6, 0x9f, 0x91, 0x1d, 0xfa, 0x72, 0x41, - 0xc8, 0xd5, 0x79, 0x2d, 0x43, 0xc4, 0x57, 0xd5, 0xde, 0x96, 0x52, 0x3a, 0x53, 0xd6, - 0x67, 0xec, 0x5c, 0x4e, 0xf9, 0xd5, 0x02, 0xa1, 0x6f, 0x15, 0x22, 0x47, 0x58, 0x96, - 0xd7, 0x9b, 0xc5, 0x78, 0x33, 0xe9, 0x77, 0x17, 0x1c, 0x32, 0x4d, 0xce, 0x2a, 0x1e, - 0xa1, 0xe4, 0x30, 0x4f, 0x49, 0xe4, 0x3a, 0xe0, 0x65, 0xe3, 0xfb, 0x19, 0x6f, 0x76, - 0xd9, 0xb8, 0x79, 0xc7, 0x20, 0x08, 0x62, 0xea, 0xd1, 0x8d, 0xea, 0x5f, 0xb6, 0xa1, - 0x7a, 0xce, 0xa3, 0x33, 0x86, 0xeb, 0x4c, 0xa1, 0xb5, 0x14, 0x86, 0xa9, 0x14, 0x8f, - 0xbd, 0xf9, 0xa9, 0x53, 0x32, 0xaa, 0x60, 0x5c, 0x5d, 0x54, 0x83, 0xce, 0x4b, 0xa8, - 0xec, 0xe0, 0x1a, 0x8f, 0xf2, 0xb7, 0xef, 0x82, 0xd0, 0x5c, 0x0b, 0x6e, 0x86, 0x1b, - 0x91, 0x5f, 0x13, 0xca, 0x0e, 0xb3, 0xea, 0x13, 0xd5, 0x07, 0x08, 0x07, 0xa2, 0xcb, - 0x66, 0x80, 0xa2, 0x49, 0xea, 0x9c, 0x72, 0x24, 0x39, 0x2c, 0xbc, 0x8a, 0xb8, 0x25, - 0x01, 0xb2, 0x6f, 0x11, 0x2a, 0xc7, 0x89, 0xa1, 0x2a, 0x31, 0xad, 0x13, 0x14, 0xe2, - 0xed, 0xe0, 0x8f, 0xad, 0x31, 0x43, 0xaf, 0x30, 0xc2, 0x7f, 0x40, 0x3b, 0xc8, 0x66, - 0xc7, 0x55, 0x17, 0x78, 0x52, 0xaf, 0xd0, 0xab, 0xb9, 0x0a, 0xde, 0x1d, 0x68, 0x27, - 0x26, 0xf4, 0x20, 0x08, 0xb4, 0x6a, 0xd7, 0xf8, 0xab, 0xdb, 0x18, 0x11, 0x7f, 0x72, - 0x64, 0x13, 0x90, 0xf0, 0x86, 0xb6, 0xe1, 0x49, 0x8b, 0xe6, 0x95, 0x48, 0x52, 0x7e, - 0x6a, 0xda, 0x2b, 0x38, 0xb9, 0xfe, 0x12, 0x1e, 0xf6, 0x70, 0xaf, 0x74, 0x37, 0xd3, - 0x25, 0x36, 0xd5, 0xcf, 0x5c, 0x4a, 0xb1, 0x9d, 0xd9, 0x97, 0x71, 0x58, 0x2d, 0x03, - 0x81, 0x04, 0xb7, 0xe0, 0x39, 0xa3, 0x76, 0xf7, 0xac, 0xbb, 0xea, 0xdb, 0x34, 0xf9, - 0x45, 0xbe, 0xb9, 0xd7, 0xca, 0x0e, 0x4e, 0x3d, 0x5c, 0x5e, 0x4e, 0xb1, 0xd8, 0x52, - 0x6e, 0xbd, 0x13, 0xda, 0xcb, 0x1b, 0xa3, 0x57, 0x35, 0xc6, 0xd0, 0x4a, 0x45, 0x55, - 0xac, 0xf4, 0xbf, 0x11, 0x76, 0x26, 0x50, 0x0d, 0x77, 0xb3, 0x81, 0x89, 0xdd, 0x48, - 0x88, 0x04, 0x12, 0x25, 0xac, 0xbe, 0x38, 0x74, 0xa4, 0xc0, 0xf6, 0x07, 0xfe, 0x67, - 0x45, 0xf9, 0x35, 0x5b, 0x3f, 0xa1, 0x88, 0xf1, 0xd6, 0x5c, 0x09, 0xf3, 0x89, 0xaf, - 0x1b, 0x9d, 0x62, 0x32, 0xaa, 0x79, 0x44, 0x79, 0x19, 0xc5, 0x50, 0xf6, 0xf3, 0x1f, - 0xec, 0x35, 0x48, 0x1c, 0xb9, 0x22, 0xde, 0x2d, 0xb5, 0xb4, 0xda, 0x2f, 0x81, 0x94, - 0x86, 0x17, 0x02, 0x8e, 0x32, 0x17, 0x06, 0xa3, 0xa7, 0x78, 0xc1, 0x93, 0x8c, 0x44, - 0x3b, 0xb0, 0x0e, 0x5b, 0x0f, 0xf0, 0x6a, 0xd8, 0xab, 0x9b, 0x1a, 0xb0, 0xc1, 0x14, - 0x77, 0x67, 0x3f, 0x85, 0xdf, 0x95, 0x61, 0xdb, 0xea, 0x45, 0xd5, 0xf9, 0x78, 0x1e, - 0xbe, 0x31, 0x7a, 0x07, 0x10, 0xae, 0x54, 0x61, 0xe3, 0x4f, 0xe6, 0xf1, 0xb1, 0xaa, - 0x9b, 0x4e, 0x67, 0xb1, 0x49, 0x10, 0x98, 0x48, 0x02, 0xc2, 0xa7, 0xe3, 0x81, 0x93, - 0xbc, 0x7b, 0xdc, 0x8b, 0xa3, 0xe4, 0xe3, 0xd1, 0xd9, 0x33, 0xbf, 0xb5, 0x80, 0xf5, - 0xb3, 0xe8, 0x7a, 0x2a, 0x06, 0x51, 0x70, 0x51, 0x41, 0x0f, 0xe1, 0xb4, 0xff, 0x1e, - 0xa0, 0xad, 0xe8, 0x24, 0xf3, 0x38, 0x51, 0x54, 0x56, 0xa5, 0x7c, 0x7a, 0x91, 0x6a, - 0x74, 0x38, 0x8e, 0xe8, 0xf1, 0x28, 0x1f, 0x9a, 0xde, 0x0a, 0xe2, 0xa2, 0x61, 0x3a, - 0x06, 0x12, 0xc4, 0x69, 0xdf, 0x79, 0x2b, 0x8d, 0xf4, 0xca, 0xe4, 0xfc, 0x25, 0xc1, - 0xca, 0xdb, 0xa9, 0x5a, 0x80, 0x7c, 0xe6, 0x1e, 0x5a, 0x53, 0x03, 0xfa, 0xaf, 0x9e, - 0x14, 0x65, 0x39, 0x96, 0xb5, 0xa8, 0xad, 0xc3, 0x4f, 0xd4, 0x75, 0xef, 0x14, 0x99, - 0x09, 0x4b, 0xab, 0xaf, 0x1f, 0x3f, 0x07, 0xda, 0x9a, 0x39, 0x0b, 0x1d, 0x9f, 0xc9, - 0xa0, 0x83, 0x27, 0x98, 0x7a, 0xdf, 0xe9, 0x56, 0x48, 0x63, 0xfb, 0xdf, 0xa8, 0xf6, - 0xb4, 0x6a, 0x88, 0x41, 0x58, 0x30, 0x99, 0xaf, 0xb7, 0x87, 0x01, 0x18, 0xfa, 0xce, - 0x76, 0x34, 0x7e, 0x40, 0xb6, 0xfd, 0x8c, 0xd1, 0x55, 0x82, 0xae, 0x8e, 0x23, 0xbe, - 0x9a, 0x02, 0x19, 0xbc, 0x3e, 0x4e, 0x45, 0x46, 0xa3, 0x0d, 0x3b, 0xbb, 0xbd, 0x16, - 0x86, 0x08, 0x68, 0x76, 0xbe, 0x0e, 0x4c, 0x85, 0x9b, 0xe7, 0x1f, 0xb5, 0x8f, 0x4f, - 0xab, 0x3d, 0x28, 0xc0, 0xb4, 0xf7, 0xe7, 0x5a, 0xd1, 0xed, 0xb7, 0xf8, 0x89, 0x46, - 0xfb, 0x40, 0xcf, 0xa5, 0x78, 0x6a, 0x0f, 0xcb, 0xa1, 0x30, 0x3c, 0x83, 0x47, 0xec, - 0xee, 0x93, 0xd4, 0x6d, 0x14, 0x0b, 0xb5, 0xf6, 0x95, 0x31, 0xd6, 0x66, 0x54, 0x8b, - 0x10, 0x9c, 0xe7, 0x64, 0xbe, 0xad, 0x7c, 0x87, 0xbd, 0x4c, 0x87, 0x64, 0x94, 0xde, - 0x82, 0xdb, 0x6e, 0x50, 0x73, 0xa6, 0xc9, 0x4f, 0x7c, 0x09, 0x9a, 0x40, 0xd7, 0xa3, - 0x1c, 0x4a, 0x04, 0xb6, 0x9c, 0x9f, 0xcc, 0xf3, 0xc7, 0xdd, 0x56, 0xf5, 0x54, 0x47, - 0x76, 0xc5, 0x3b, 0x4d, 0xf7, 0x95, 0x39, 0x81, 0xd5, 0x5a, 0x96, 0xa6, 0xdc, 0xff, - 0x99, 0x04, 0xa9, 0x08, 0x42, 0xe5, 0xba, 0xfe, 0xc8, 0x84, 0x0c, 0x2d, - ], - script_code: Script(vec![0x53, 0x63, 0x63, 0x51, 0xac, 0x00, 0x51]), - transparent_input: None, - hash_type: 1, - amount: 1345602751504862, - consensus_branch_id: 1991772603, - sighash: [ - 0x15, 0x53, 0xd4, 0xf1, 0x07, 0x45, 0x10, 0x71, 0x81, 0x99, 0x00, 0x5f, 0xef, 0xaa, - 0xa8, 0x3e, 0x29, 0xd1, 0x63, 0xee, 0xbd, 0xf3, 0xc0, 0x33, 0x82, 0x79, 0x08, 0xac, - 0xb4, 0x6f, 0xa2, 0x4b, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x01, 0x13, 0xf9, 0x12, 0xa5, - 0xdc, 0x0e, 0x00, 0x00, 0x09, 0x53, 0x53, 0xac, 0x63, 0x6a, 0x53, 0x63, 0xac, 0x6a, - 0x2f, 0x2c, 0x3b, 0x86, 0x0e, 0x40, 0xe3, 0x1c, 0x61, 0x8c, 0xa1, 0x7d, 0xf7, 0x15, - 0x04, 0x00, 0x01, 0x21, 0xbf, 0x07, 0x11, 0x5b, 0x3a, 0x39, 0xbb, 0x87, 0xf7, 0x23, - 0x91, 0x52, 0x4b, 0x82, 0x0e, 0xf3, 0x5c, 0xfc, 0x09, 0x58, 0xd4, 0x19, 0x2f, 0x49, - 0x59, 0xef, 0xe4, 0xb9, 0xa7, 0xb5, 0x29, 0x98, 0x8a, 0x3f, 0x7d, 0x27, 0x37, 0x91, - 0x49, 0x0a, 0x6b, 0x48, 0x49, 0x5a, 0x80, 0x06, 0x45, 0x5e, 0x86, 0x57, 0x71, 0xbe, - 0x92, 0x06, 0xd5, 0x4b, 0x43, 0x02, 0x4a, 0xf5, 0xe6, 0xc9, 0x5b, 0x33, 0xf6, 0xda, - 0xd1, 0x66, 0x6a, 0x05, 0xf9, 0x1a, 0xd7, 0x75, 0x79, 0x65, 0xc2, 0x99, 0x36, 0xe7, - 0xfa, 0x48, 0xd7, 0x7e, 0x89, 0xee, 0x09, 0x62, 0xf5, 0x8c, 0x05, 0x1d, 0x11, 0xd0, - 0x55, 0xfc, 0xe2, 0x04, 0xa5, 0x62, 0xde, 0x68, 0x08, 0x8a, 0x1b, 0x26, 0x48, 0xb8, - 0x17, 0x4c, 0xbc, 0xfc, 0x8b, 0x5b, 0x5c, 0xd0, 0x77, 0x11, 0x5a, 0xfd, 0xe1, 0x84, - 0x05, 0x05, 0x4e, 0x5d, 0xa9, 0xa0, 0x43, 0x10, 0x34, 0x2c, 0x5d, 0x3b, 0x52, 0x6e, - 0x0b, 0x02, 0xc5, 0xca, 0x17, 0x22, 0xba, 0xde, 0xee, 0x23, 0xd1, 0x45, 0xe8, 0xeb, - 0x22, 0x13, 0xfc, 0x4a, 0xf1, 0xe4, 0x50, 0xe4, 0xd5, 0x21, 0x7c, 0x66, 0x17, 0x00, - 0x8c, 0x78, 0xf4, 0xfb, 0x11, 0x12, 0xf4, 0x02, 0x8a, 0x70, 0x4f, 0xc5, 0xa9, 0x38, - 0x2c, 0x6b, 0x03, 0xe7, 0xd8, 0x08, 0x5e, 0x90, 0x6c, 0xf8, 0x4c, 0xa2, 0xc1, 0x20, - 0x7c, 0x87, 0xa2, 0xbc, 0xe2, 0x08, 0x0a, 0x98, 0x91, 0x66, 0x8d, 0x69, 0xb0, 0x44, - 0xbe, 0xce, 0xd6, 0xcd, 0xa3, 0x2c, 0x22, 0x9c, 0x91, 0x17, 0x91, 0x7a, 0xa0, 0x7d, - 0xdf, 0xfc, 0xd3, 0x77, 0x39, 0x5c, 0xba, 0x61, 0x6d, 0x63, 0xc0, 0xb6, 0x9c, 0x01, - 0xfc, 0xc4, 0x53, 0x91, 0xfd, 0x5b, 0x87, 0x63, 0xfb, 0x96, 0xd7, 0xca, 0x33, 0x3a, - 0x12, 0xde, 0x3c, 0xef, 0xa9, 0x1c, 0x6c, 0x98, 0xf9, 0x47, 0x3b, 0x8e, 0x10, 0x4a, - 0x71, 0x29, 0x3e, 0x46, 0x37, 0x47, 0x05, 0xba, 0xf6, 0x5f, 0xa4, 0x13, 0x84, 0xba, - 0x5c, 0x8e, 0x0c, 0x88, 0xa3, 0xeb, 0x07, 0xe0, 0xbe, 0x34, 0xda, 0xdd, 0xfa, 0xbb, - 0x7b, 0x65, 0x54, 0x3b, 0x5f, 0x39, 0xcb, 0x20, 0x23, 0xd4, 0x67, 0x89, 0xeb, 0x7d, - 0x98, 0x9a, 0xf7, 0x79, 0xe5, 0xb8, 0xd2, 0x83, 0x85, 0xa8, 0x5b, 0x0d, 0xa2, 0xab, - 0xe0, 0x7f, 0x0c, 0x2b, 0xb4, 0x25, 0x5f, 0xce, 0xa0, 0x31, 0x88, 0x52, 0x7a, 0x30, - 0x7d, 0x40, 0x91, 0x59, 0xe9, 0x01, 0x66, 0xfa, 0xc6, 0xa0, 0x70, 0xba, 0x05, 0xb3, - 0xe4, 0xdb, 0xfd, 0x3a, 0x2b, 0xfc, 0xc9, 0xee, 0x6e, 0xd0, 0x16, 0xc0, 0xf6, 0x65, - 0xbe, 0x81, 0x33, 0xb7, 0xdc, 0x1d, 0x86, 0x04, 0x4d, 0xb0, 0xf9, 0xdb, 0x40, 0xfb, - 0x0e, 0x9f, 0x8b, 0xc2, 0xe4, 0xdb, 0x53, 0x82, 0xa8, 0x04, 0x53, 0xfd, 0xd8, 0x8f, - 0x52, 0xb0, 0x59, 0xc7, 0x96, 0x49, 0x43, 0xc1, 0xc0, 0xdf, 0x3b, 0x6b, 0x64, 0x10, - 0xf9, 0x5d, 0xef, 0x5c, 0x04, 0x78, 0xf6, 0x85, 0xc6, 0xb3, 0x3a, 0xa0, 0xc8, 0x8e, - 0x68, 0x82, 0x42, 0x8e, 0x8c, 0xba, 0x43, 0x64, 0xea, 0x93, 0xaf, 0x37, 0x8c, 0x50, - 0x64, 0x82, 0x27, 0x36, 0x58, 0xa2, 0xc0, 0x95, 0xdd, 0x07, 0x6c, 0x39, 0x73, 0x19, - 0x1d, 0x46, 0xa1, 0x03, 0x9e, 0x5d, 0x6b, 0x7d, 0x9c, 0x08, 0x6b, 0x25, 0x85, 0x90, - 0xd3, 0x2d, 0x76, 0xe3, 0xd4, 0x20, 0x38, 0x43, 0x57, 0x0e, 0xfe, 0xed, 0x97, 0x1c, - 0x14, 0x12, 0x2f, 0x5b, 0x21, 0x5b, 0x0f, 0x38, 0xbb, 0xc7, 0x16, 0xb6, 0xa5, 0x94, - 0x4e, 0xe7, 0x27, 0x96, 0x56, 0x90, 0xe2, 0x09, 0xb4, 0x9e, 0xb9, 0x62, 0xc0, 0x39, - 0x97, 0x5f, 0x93, 0x9e, 0xd5, 0xc6, 0xe4, 0xc4, 0x00, 0xd8, 0x87, 0x75, 0x94, 0x33, - 0xd3, 0xad, 0x71, 0x6d, 0xa0, 0xcb, 0x44, 0x61, 0x13, 0xc7, 0x72, 0x7a, 0x64, 0xb5, - 0x8c, 0x3f, 0x8a, 0x0f, 0x81, 0x18, 0x9f, 0x98, 0x00, 0x52, 0x33, 0xa8, 0x13, 0x66, - 0xae, 0xe7, 0x3c, 0xec, 0x85, 0x22, 0x8e, 0xbc, 0xfd, 0x5e, 0xe3, 0xc3, 0xfb, 0x44, - 0xdb, 0x76, 0xba, 0x24, 0x3f, 0x28, 0x42, 0xb7, 0xb5, 0xfc, 0x74, 0x6a, 0xe5, 0x1b, - 0x0b, 0xc4, 0xbd, 0x4f, 0xc9, 0xfd, 0x83, 0x35, 0x65, 0xea, 0x85, 0x2b, 0x92, 0xb2, - 0x24, 0xf6, 0x99, 0x03, 0x18, 0xad, 0x8c, 0x7d, 0x94, 0x37, 0xe2, 0x0e, 0x2a, 0x1f, - 0x20, 0xe8, 0x18, 0xf9, 0x05, 0x7c, 0x5a, 0xba, 0xaa, 0x2e, 0x5c, 0x15, 0xb9, 0x49, - 0x45, 0xcd, 0x42, 0x4c, 0x28, 0xa5, 0xfa, 0x38, 0x5d, 0xad, 0xfe, 0x49, 0x07, 0xb2, - 0x74, 0xd8, 0x42, 0x70, 0x7d, 0xb3, 0x69, 0x7a, 0x5a, 0xe6, 0xc8, 0xf5, 0x42, 0xe5, - 0xec, 0xc0, 0x7f, 0xe4, 0x73, 0x50, 0xd1, 0x01, 0x46, 0x70, 0x21, 0x2e, 0xfe, 0x81, - 0xfb, 0x7c, 0x73, 0xe8, 0x45, 0x0d, 0xf8, 0x14, 0xef, 0x62, 0x32, 0xf7, 0x49, 0x0f, - 0x63, 0xcc, 0xf0, 0x74, 0x80, 0xf8, 0x84, 0xa6, 0x6e, 0xaf, 0xfc, 0x28, 0xfe, 0xa4, - 0x48, 0xd7, 0xb4, 0x01, 0xcd, 0xae, 0x10, 0xe7, 0xc0, 0xc7, 0xf9, 0xa7, 0xb1, 0x53, - 0x31, 0x96, 0x9f, 0xc8, 0xcb, 0x36, 0x39, 0x67, 0x73, 0xde, 0x19, 0x19, 0x31, 0xc7, - 0x50, 0xf6, 0xce, 0x5c, 0xaa, 0xf2, 0x97, 0x68, 0xeb, 0xb2, 0x7d, 0xac, 0xc7, 0x38, - 0x05, 0x6a, 0x81, 0x25, 0xb4, 0x77, 0x2b, 0xf8, 0x7a, 0xe1, 0x0a, 0x8a, 0x30, 0x9b, - 0x9b, 0xd6, 0x55, 0x04, 0x3c, 0xfc, 0x31, 0x59, 0x49, 0x43, 0x68, 0xc5, 0xab, 0x8c, - 0xad, 0xb7, 0xf6, 0x71, 0xe9, 0x62, 0x6b, 0xd2, 0x63, 0xe3, 0x11, 0x81, 0xa6, 0x04, - 0xb5, 0x06, 0xa0, 0x3b, 0x43, 0x9a, 0x7f, 0xfe, 0x43, 0x55, 0x89, 0x24, 0x77, 0xe2, - 0xbd, 0xf3, 0x38, 0xc6, 0x2c, 0x39, 0x22, 0xf7, 0xd3, 0xc9, 0xa5, 0x6c, 0x71, 0x03, - 0xd9, 0x11, 0x94, 0x8a, 0x84, 0xb5, 0xae, 0x2d, 0xbb, 0x16, 0xa3, 0x76, 0x1a, 0xdd, - 0x05, 0x3a, 0x0f, 0x96, 0x7e, 0x6b, 0x5b, 0xc9, 0x42, 0x11, 0xb6, 0x54, 0x71, 0x53, - 0x26, 0x7c, 0x6e, 0xe1, 0xca, 0xd0, 0xd9, 0x74, 0xa7, 0x10, 0x88, 0x58, 0x37, 0x35, - 0xe4, 0xf6, 0x3d, 0x33, 0x15, 0x6d, 0xad, 0xd5, 0x4c, 0x2f, 0xaf, 0x89, 0x11, 0x4a, - 0x12, 0x7b, 0x97, 0xb9, 0x4c, 0xc2, 0xa2, 0x2e, 0xf3, 0x03, 0xf4, 0x59, 0xd0, 0x4f, - 0xc0, 0xb5, 0x3a, 0xce, 0x59, 0x18, 0xd4, 0x7f, 0xf3, 0x3a, 0x55, 0x8b, 0xd7, 0x1a, - 0x75, 0xf3, 0x55, 0xfb, 0xd0, 0x6b, 0xbc, 0xcf, 0x4e, 0x02, 0xc3, 0xc0, 0xa4, 0xb6, - 0x3d, 0x0c, 0xc9, 0x49, 0x80, 0x1d, 0x63, 0xa6, 0x4c, 0xb2, 0xd3, 0x23, 0x73, 0xb2, - 0xc7, 0xb2, 0x74, 0xab, 0x2d, 0xb4, 0x68, 0x21, 0x42, 0xc8, 0xb2, 0x1d, 0x84, 0xc4, - 0x81, 0xf5, 0xef, 0x21, 0xe4, 0xb5, 0xe3, 0x60, 0x34, 0x51, 0xbf, 0x94, 0x77, 0x4d, - 0x0e, 0xf4, 0x7f, 0x63, 0xfa, 0x6a, 0xbb, 0x78, 0xd2, 0x1c, 0x19, 0x3c, 0xbe, 0x65, - 0xb6, 0x95, 0xfe, 0x67, 0x42, 0x3c, 0x1e, 0x2d, 0x31, 0x2e, 0x27, 0x76, 0xfa, 0x24, - 0xec, 0xe8, 0x46, 0x83, 0xe7, 0x48, 0x76, 0xc5, 0x5e, 0xa0, 0x36, 0x9e, 0x4e, 0xa0, - 0xe8, 0x64, 0x94, 0xe0, 0x0d, 0xde, 0x23, 0x6a, 0x16, 0x89, 0x73, 0x1f, 0x0a, 0x5d, - 0x82, 0x03, 0xaf, 0xde, 0x5c, 0x42, 0x36, 0x40, 0xb8, 0x1e, 0x4f, 0x63, 0x1c, 0x98, - 0x1c, 0x11, 0xa2, 0xe1, 0xd1, 0x84, 0xc6, 0x7c, 0x52, 0x8d, 0xf9, 0x2d, 0x53, 0xae, - 0xc4, 0x4a, 0x40, 0xa4, 0xea, 0x2a, 0x13, 0x1b, 0x47, 0x33, 0xcf, 0xe4, 0x5c, 0x6b, - 0x00, 0x12, 0xc3, 0xe9, 0xe2, 0x09, 0x75, 0xba, 0xae, 0xcb, 0x02, 0x32, 0xdf, 0x88, - 0x0b, 0xd7, 0xd1, 0xde, 0x13, 0xe1, 0x34, 0x94, 0x62, 0xec, 0x8d, 0x5d, 0xf3, 0xe7, - 0x80, 0xff, 0xa7, 0x2e, 0xba, 0x8a, 0x8d, 0xf7, 0xfc, 0xf3, 0x98, 0xec, 0x23, 0x05, - 0x13, 0xca, 0x9d, 0x61, 0x23, 0xf8, 0xb9, 0xd8, 0x17, 0x85, 0x60, 0xda, 0xf9, 0x75, - 0x11, 0x19, 0x55, 0xa2, 0xbc, 0xa3, 0x42, 0x3e, 0xee, 0xfc, 0x52, 0x7b, 0xe3, 0xa8, - 0x54, 0x3e, 0xb9, 0x0a, 0x5e, 0xc0, 0x2f, 0x35, 0xa7, 0xc6, 0x4b, 0x7d, 0xd5, 0x9a, - 0x72, 0xda, 0x00, 0x74, 0x63, 0x4e, 0x01, 0xd2, 0xab, 0xf3, 0x63, 0x7a, 0xdd, 0x77, - 0xc7, 0x35, 0x0f, 0x12, 0xb0, 0x11, 0xb2, 0x94, 0x16, 0x8e, 0xc7, 0x55, 0x76, 0xe4, - 0x7d, 0x16, 0x9e, 0x39, 0x38, 0xbf, 0x6a, 0xe2, 0xaa, 0x8f, 0xf7, 0xcf, 0xba, 0x7c, - 0xac, 0xb1, 0xf9, 0x2b, 0x6e, 0x4c, 0x24, 0x97, 0xbf, 0xfa, 0x9f, 0x17, 0xca, 0xd2, - 0x42, 0xfa, 0x9c, 0x31, 0x79, 0xc1, 0xa3, 0xaa, 0x81, 0xf7, 0x36, 0x16, 0x49, 0x57, - 0x2c, 0x71, 0x5c, 0x25, 0xa1, 0xf6, 0xcd, 0x5a, 0xce, 0x82, 0xc0, 0x0a, 0xb2, 0x34, - 0x2b, 0x9c, 0x3c, 0xb4, 0xff, 0xfd, 0xda, 0x16, 0x0c, 0xa5, 0xab, 0x9e, 0x9b, 0xaf, - 0x21, 0x39, 0xef, 0x9a, 0xfb, 0xe1, 0xb1, 0xf3, 0x09, 0x46, 0x2a, 0xfc, 0xe4, 0x62, - 0xa7, 0x9b, 0xb9, 0x69, 0x8e, 0x22, 0xc9, 0x57, 0xc5, 0x90, 0xa7, 0x53, 0xa7, 0x6b, - 0x87, 0xe0, 0x09, 0x12, 0x1e, 0x06, 0xf6, 0xa1, 0xbf, 0x62, 0xa0, 0x8b, 0xf4, 0x35, - 0xd9, 0x2e, 0x2f, 0xff, 0xe8, 0x6e, 0x2a, 0x9c, 0xbb, 0xa9, 0x13, 0x3a, 0x68, 0xe4, - 0xae, 0xbf, 0x33, 0xc3, 0x84, 0x36, 0xf2, 0x54, 0x5f, 0xc2, 0xd5, 0x28, 0x32, 0xd1, - 0x65, 0xaf, 0x41, 0x5b, 0x24, 0x4a, 0xdc, 0x5f, 0x57, 0x37, 0x7d, 0xee, 0xdf, 0x46, - 0x0a, 0xa3, 0xbe, 0xb4, 0x34, 0x19, 0xc6, 0xb0, 0x82, 0xe8, 0x35, 0xce, 0x84, 0xca, - 0x13, 0xb6, 0x90, 0x8a, 0x88, 0x13, 0xc0, 0x21, 0xde, 0x9f, 0xa9, 0xa4, 0x4e, 0x4c, - 0x18, 0xdc, 0xb3, 0xd2, 0x1f, 0xaa, 0x8b, 0x86, 0xc8, 0x70, 0x28, 0xd0, 0xb5, 0x53, - 0x21, 0x07, 0xf9, 0xf6, 0xfd, 0x49, 0x00, 0x22, 0x7d, 0x0d, 0x8f, 0xf2, 0xbf, 0x9d, - 0x28, 0xcb, 0xcc, 0x99, 0x6c, 0x47, 0x3c, 0xe6, 0x16, 0x41, 0xf4, 0x88, 0x4e, 0x6e, - 0xd3, 0xfd, 0x5e, 0x4b, 0x7c, 0xb8, 0x35, 0xb8, 0x33, 0x08, 0x96, 0x4e, 0x3c, 0x46, - 0x87, 0x3f, 0xd6, 0x13, 0x31, 0x7b, 0x91, 0xd2, 0x92, 0x36, 0xea, 0x90, 0xe3, 0x65, - 0x32, 0xec, 0x5d, 0x6b, 0x42, 0x32, 0xe8, 0xbc, 0xcc, 0x36, 0x75, 0xb9, 0x8b, 0x35, - 0xf8, 0xde, 0x4d, 0x08, 0x88, 0x84, 0x14, 0x6f, 0x3d, 0xb8, 0x97, 0x0b, 0x38, 0xd1, - 0xe5, 0x01, 0xae, 0xe9, 0xc1, 0xf3, 0xfd, 0x78, 0x25, 0x49, 0xd3, 0xf3, 0x24, 0x57, - 0x59, 0x60, 0x6d, 0x9f, 0x92, 0xd5, 0x54, 0x8a, 0xcf, 0xea, 0xdb, 0xaf, 0x9c, 0xaa, - 0x6b, 0x93, 0xdc, 0x08, 0x82, 0x8d, 0x74, 0xf6, 0xd5, 0xfd, 0xd8, 0x33, 0x31, 0xf0, - 0x96, 0x91, 0x45, 0x95, 0x52, 0x97, 0xe6, 0x9f, 0x00, 0xfd, 0x29, 0x87, 0xf2, 0xda, - 0x2b, 0x94, 0xb9, 0x95, 0xfe, 0xcb, 0xe6, 0x22, 0xa7, 0x35, 0xef, 0x7f, 0x12, 0x07, - 0xf6, 0x71, 0x62, 0x94, 0x89, 0x20, 0x2b, 0xea, 0x0b, 0x47, 0x5e, 0x51, 0x68, 0x1a, - 0xa1, 0x67, 0x78, 0xb3, 0x9b, 0xd9, 0x23, 0xc9, 0x8d, 0xc6, 0xff, 0x83, 0x73, 0xc7, - 0x9b, 0xb1, 0x70, 0x30, 0x41, 0x7b, 0xc2, 0x00, 0xc8, 0xf0, 0xb8, 0x55, 0xac, 0xfe, - 0xc1, 0x79, 0xf7, 0x67, 0x4c, 0xec, 0x27, 0x21, 0xa1, 0x0f, 0xca, 0x69, 0x3d, 0x83, - 0xcf, 0xe5, 0xb8, 0xcd, 0xcc, 0x18, 0xf8, 0x1a, 0xd6, 0x17, 0xfa, 0x26, 0xf0, 0xdf, - 0xb8, 0x36, 0x55, 0xb8, 0xa2, 0x9a, 0x7f, 0x83, 0x42, 0x32, 0x42, 0x5e, 0x8c, 0x47, - 0x45, 0x88, 0xf1, 0x8d, 0xd3, 0x26, 0xaa, 0x39, 0x6c, 0x3e, 0x47, 0x75, 0xe0, 0x02, - 0x05, 0xfc, 0x9e, 0x45, 0xf7, 0xb7, 0xd2, 0xe6, 0xd5, 0x5d, 0xcb, 0x90, 0xe2, 0x3f, - 0xf6, 0xb5, 0x08, 0x45, 0x9a, 0xa6, 0x99, 0xbf, 0xcb, 0xd5, 0x6f, 0x10, 0x99, 0x77, - 0x64, 0xd0, 0x87, 0x40, 0x89, 0x86, 0xe7, 0x3d, 0x6e, 0x28, 0x4f, 0xea, 0x9a, 0x23, - 0xc3, 0x93, 0x11, 0x78, 0x2f, 0x86, 0xca, 0xbf, 0xf9, 0x45, 0x5e, 0x4c, 0xf6, 0x99, - 0xe5, 0xf5, 0xd4, 0xbc, 0x0b, 0x39, 0x05, 0xa4, 0xe3, 0xbd, 0x01, 0xc5, 0x4d, 0xf8, - 0x64, 0x34, 0x43, 0xbe, 0x0f, 0x88, 0x90, 0x32, 0xea, 0x32, 0x5b, 0xf0, 0x71, 0x07, - 0xfd, 0x41, 0xd6, 0x73, 0xee, 0xba, 0xe6, 0xfa, 0x63, 0x7b, 0x70, 0xcc, 0x0e, 0xd3, - 0xf0, 0x09, 0x58, 0xdf, 0xb8, 0xdc, 0xf0, 0x0e, 0x85, 0xa1, 0xd0, 0xa6, 0xa8, 0x90, - 0x81, 0x40, 0xc2, 0xf4, 0x34, 0xc2, 0xe2, 0x60, 0xef, 0xb0, 0xbc, 0xa2, 0x00, 0x35, - 0x04, 0xc9, 0x99, 0x93, 0xa9, 0xe1, 0xc0, 0xff, 0x9c, 0xef, 0xe6, 0xa6, 0x65, 0xd7, - 0x91, 0x42, 0x86, 0x90, 0xe4, 0x7e, 0xf8, 0xc1, 0x31, 0xa8, 0xe9, 0xbf, 0xb4, 0xc3, - 0x08, 0x02, 0x35, 0x03, 0x2d, 0x73, 0x1b, 0x0d, 0x38, 0x41, 0x22, 0x5f, 0x1c, 0x11, - 0xe2, 0xc2, 0x8e, 0xe8, 0x4d, 0x35, 0xf9, 0x22, 0x61, 0x00, 0x56, 0x59, 0x72, 0xeb, - 0x26, 0x9d, 0x27, 0x8e, 0xf6, 0x49, 0x79, 0xbf, 0x65, 0x15, 0xed, 0x4a, 0x68, 0x40, - 0xb0, 0x88, 0x3a, 0x9e, 0x6e, 0xf6, 0x4a, 0x0e, 0xfc, 0xae, 0x1c, 0xf2, 0x1d, 0xfe, - 0x74, 0x85, 0x4e, 0x84, 0xc2, 0x74, 0x9f, 0xac, 0x03, 0x82, 0x52, 0x75, 0xc9, 0xb6, - 0x30, 0x21, 0x84, 0xc7, 0x2d, 0xf4, 0xc4, 0xbb, 0x28, 0x62, 0xe4, 0xe8, 0xa7, 0xd9, - 0xa4, 0xa2, 0x82, 0x86, 0x6f, 0x9a, 0x7b, 0x2c, 0xfc, 0x9a, 0x56, 0x31, 0x3d, 0xa0, - 0xc4, 0x7a, 0x34, 0xb7, 0xb9, 0xcd, 0xa3, 0xac, 0xe8, 0x18, 0x5f, 0x07, 0xdf, 0x36, - 0xe4, 0x48, 0xa7, 0x6a, 0xa4, 0x77, 0xf2, 0x24, 0xd8, 0x7a, 0x07, 0x4f, 0x43, 0xaf, - 0x5d, 0x5f, 0x79, 0xb3, 0xab, 0x11, 0x28, 0xf0, 0x81, 0x91, 0x44, 0x7f, 0xa6, 0x46, - 0xbf, 0xdd, 0xe5, 0xb5, 0x1e, 0x23, 0x3c, 0xa6, 0x15, 0x5d, 0x10, 0x15, 0x85, 0xbc, - 0x2c, 0x40, 0x15, 0x8a, 0xc2, 0x10, 0x6e, 0x66, 0xa2, 0x6e, 0x46, 0x42, 0x33, 0x70, - 0x63, 0x68, 0x76, 0xb4, 0x34, 0xa7, 0x4f, 0x8c, 0xe8, 0x06, 0x00, 0x50, 0xb0, 0x82, - 0xa7, 0x9b, 0x61, 0xbb, 0x5d, 0x34, 0x4e, 0xb5, 0xa1, 0x15, 0x83, 0x26, 0xce, 0xd9, - 0xa9, 0xd9, 0xf5, 0x4f, 0xb2, 0xfe, 0x8f, 0x9f, 0x05, 0xcd, 0x11, 0x1e, 0xe4, 0x6c, - 0x47, 0x10, 0xf6, 0xf6, 0x3a, 0x62, 0x69, 0x45, 0x57, 0xef, 0x1b, 0x12, 0xc8, 0x80, - 0x06, 0xb6, 0x78, 0x72, 0x50, 0x5f, 0x4e, 0x88, 0x3b, 0x58, 0x59, 0x07, 0x92, 0x9a, - 0x2f, 0x3f, 0xdb, 0x0d, 0x8f, 0x79, 0x14, 0xc4, 0x2d, 0xde, 0x2d, 0x20, 0x00, 0xf5, - 0xae, 0x02, 0xd4, 0x18, 0x21, 0xc8, 0xe1, 0xee, 0x01, 0x38, 0xeb, 0xcb, 0x72, 0x8d, - 0x7c, 0x6c, 0x3c, 0x80, 0x02, 0x7e, 0x43, 0x75, 0x94, 0xc6, 0x70, 0xfd, 0x6f, 0x39, - 0x08, 0x22, 0x2e, 0xe7, 0xa1, 0xb9, 0x17, 0xf8, 0x27, 0x1a, 0xbe, 0x66, 0x0e, 0x39, - 0xe0, 0x51, 0xaa, 0xa6, 0xfc, 0xa1, 0x86, 0x22, 0x76, 0xe2, 0xba, 0xa0, 0xfe, 0x0b, - 0x16, 0x2a, 0xeb, 0xcf, 0xe3, 0xd9, 0x34, 0x9c, 0x8d, 0x15, 0x4b, 0xb7, 0xee, 0x28, - 0x21, 0x2c, 0x1b, 0xaa, 0x70, 0x5d, 0x82, 0x07, 0x0d, 0x70, 0x32, 0xf2, 0x69, 0x5d, - 0x17, 0x96, 0x80, 0x9f, 0xab, 0x41, 0x24, 0x69, 0x26, 0xaf, 0x99, 0x2b, 0x6e, 0xee, - 0x95, 0xa9, 0xa0, 0x6b, 0xc4, 0x56, 0x2c, 0x5f, 0x2f, 0x1b, 0x19, 0x54, 0x95, 0x00, - 0x37, 0x2e, 0x7a, 0xd5, 0x79, 0xa6, 0xd6, 0xd7, 0x8b, 0x33, 0x15, 0x31, 0x30, 0xfb, - 0x44, 0x8f, 0xb7, 0x9e, 0x8a, 0x66, 0x9d, 0xb8, 0xa0, 0xf3, 0x5c, 0xdf, 0x9a, 0xe5, - 0xd3, 0x2d, 0x73, 0x2f, 0xc7, 0x94, 0x18, 0xe2, 0x3b, 0x45, 0x1d, 0xdc, 0x95, 0xa2, - 0x2a, 0xba, 0xbb, 0x05, 0x6e, 0xc6, 0xb5, 0xe8, 0xba, 0x4f, 0x52, 0x4d, 0xfa, 0xfe, - 0x87, 0x52, 0x62, 0xdd, 0x7b, 0xe4, 0x1c, 0xbb, 0xc6, 0x24, 0x20, 0xd4, 0xad, 0x6d, - 0xf5, 0xc9, 0xb7, 0x13, 0x60, 0x4f, 0x65, 0x60, 0x88, 0xa4, 0x48, 0x5e, 0x93, 0xbe, - 0x19, 0x07, 0xd2, 0x7a, 0xc6, 0xec, 0x3c, 0x57, 0x25, 0x9b, 0xd6, 0x98, 0x1d, 0x42, - 0xc1, 0xb7, 0x8a, 0x29, 0xad, 0x96, 0x85, 0xe6, 0x3c, 0x49, 0x4d, 0x41, 0x29, 0x62, - 0x3e, 0xa1, 0xa7, 0xff, 0xec, 0x85, 0xfa, 0x29, 0x41, 0x10, 0x73, 0xed, 0xb2, 0x97, - 0x8e, 0xf4, 0xe4, 0x69, 0xdd, 0xd5, 0xcd, 0xa9, 0x86, 0x18, 0x99, 0x95, 0xf8, 0x8d, - 0x6a, 0xb3, 0x66, 0xdb, 0x01, 0x90, 0x01, 0xf5, 0xb2, 0x52, 0x88, 0xcf, 0x86, 0x0f, - 0xd9, 0x98, 0xee, 0x57, 0x3c, 0x8c, 0xc4, 0x8a, 0xa9, 0xef, 0xcf, 0x9b, 0x61, 0x7e, - 0x04, 0x3c, 0x99, 0x00, 0x8e, 0x35, 0x00, 0x96, 0xfd, 0xa4, 0xeb, 0x24, 0xc2, 0x0f, - 0x46, 0x90, 0xf1, 0xe2, 0xc5, 0xef, 0x86, 0x6c, 0x0e, 0xe5, 0xdd, 0xa1, 0x19, 0xee, - 0xea, 0xf1, 0x19, 0xdb, 0xdc, 0xae, 0x8d, 0xc7, 0x6c, 0x84, 0x6c, 0xc2, 0x27, 0x27, - 0x2b, 0xfc, 0x54, 0x17, 0xdc, 0x4c, 0xf4, 0xc0, 0x87, 0xba, 0x34, 0xec, 0xf3, 0xa5, - 0x5b, 0x00, 0x1f, 0xf3, 0x09, 0xa8, 0x1c, 0x05, 0x2d, 0x69, 0x26, 0xa9, 0xdd, 0xf0, - 0xf7, 0x8c, 0x5f, 0xc0, 0x64, 0xc6, 0xa6, 0x40, 0x16, 0x21, 0xb3, 0x8a, 0xa5, 0x49, - 0x44, 0x19, 0x81, 0x99, 0x21, 0x0d, 0x2b, 0x42, 0xe6, 0x1d, 0xde, 0x1d, 0x08, 0xaf, - 0x55, 0x07, 0x3b, 0xbf, 0x06, 0x15, 0xf6, 0x7b, 0x11, 0x00, 0xcc, 0x2e, 0xa3, 0xba, - 0x3d, 0x6c, 0x1a, 0x1a, 0x90, 0x87, 0xb1, 0x19, 0xba, 0xee, 0xbf, 0xa6, 0x2b, 0xc9, - 0xf0, 0xec, 0x47, 0x9d, 0x99, 0xc1, 0xa3, 0xb1, 0x58, 0xb5, 0x14, 0xd1, 0x62, 0x9d, - 0xb3, 0x99, 0x3f, 0x11, 0x67, 0x2a, 0x26, 0x70, 0x8e, 0x5a, 0xd8, 0x16, 0xb5, 0x47, - 0xab, 0x7e, 0x82, 0x7d, 0x07, 0x1b, 0xa7, 0x84, 0x2b, 0x3e, 0x90, 0x30, 0x53, 0x83, - 0x89, 0x6e, 0xc4, 0x90, 0x5f, 0x70, 0xc7, 0x8b, 0x69, 0x4e, 0x6a, 0x5a, 0x3e, 0x43, - 0x12, 0xcd, 0x82, 0x08, 0x13, 0x2b, 0x84, 0x0f, 0x05, 0xc7, 0x14, 0x52, 0x3c, 0xa8, - 0x19, 0x72, 0x0a, 0xe2, 0x27, 0xfd, 0x1a, 0xcb, 0xa7, 0x14, 0xfa, 0x4f, 0xc4, 0x5f, - 0xc5, 0x39, 0x88, 0x57, 0xb4, 0x0d, 0xc1, 0x48, 0x79, 0x85, 0x6f, 0x35, 0x4b, 0xa4, - 0xd2, 0x58, 0x1d, 0x0c, 0xda, 0x54, 0xb6, 0x38, 0xba, 0x9d, 0x76, 0xf9, 0xb5, 0x2d, - 0x17, 0xc8, 0xf8, 0x8e, 0xe6, 0x3f, 0x58, 0x45, 0xb5, 0xdc, 0xef, 0xa4, 0xc3, 0x47, - 0x9b, 0xce, 0x9a, 0xca, 0xd1, 0x8b, 0x4a, 0xea, 0xe0, 0x3c, 0x0e, 0xae, 0x22, 0x5d, - 0x42, 0x84, 0x8b, 0xde, 0xaa, 0x53, 0x6d, 0x7d, 0x8d, 0xd3, 0xbc, 0x97, 0x9f, 0x06, - 0x58, 0x66, 0x73, 0xbc, 0x6f, 0xf1, 0xc5, 0xd3, 0xb3, 0x20, 0xf3, 0x49, 0xa5, 0xb3, - 0xa8, 0xb3, 0x55, 0x59, 0x22, 0x96, 0xaa, 0xf6, 0x1c, 0x5b, 0x72, 0x52, 0xf7, 0x3e, - 0xc0, 0xa9, 0x46, 0x6a, 0x1b, 0x85, 0x76, 0x4f, 0xb0, 0x83, 0x1b, 0x4a, 0x1a, 0x36, - 0x89, 0x0e, 0x22, 0x4c, 0x01, 0xac, 0xfc, 0xe4, 0x8e, 0xe3, 0xed, 0x93, 0x87, 0x73, - 0x98, 0xe0, 0x72, 0x6d, 0x02, 0x93, 0x6d, 0x0d, 0x03, 0x2e, 0x18, 0xe3, 0x28, 0x8b, - 0x26, 0x70, 0xe1, 0x36, 0x2c, 0x32, 0xd6, 0xe4, 0x73, 0x3b, 0x9d, 0xd2, 0xd5, 0xf2, - 0x6e, 0x1f, 0xe3, 0x06, 0xf7, 0x3c, 0x00, 0x7f, 0xdd, 0xca, 0xe9, 0xd9, 0xc0, 0xaa, - 0xf1, 0x87, 0xd7, 0x42, 0x8b, 0x1e, 0x9d, 0x47, 0x9c, 0x18, 0x23, 0x7b, 0x98, 0x28, - 0xbc, 0xa8, 0xb9, 0x8c, 0x9d, 0x9b, 0xec, 0x7d, 0x82, 0x70, 0xb5, 0xd8, 0xee, 0xc3, - 0xcc, 0x4f, 0x43, 0xfa, 0x01, 0x88, 0x52, 0x1b, 0xc6, 0x1b, 0x21, 0xdd, 0x04, 0xe3, - 0x7a, 0x83, 0xec, 0xe6, 0x8c, 0xa7, 0xa2, 0xfa, 0x6c, 0x8f, 0x9e, 0x34, 0xa6, 0x29, - 0x03, 0x35, 0xaa, 0x1f, 0xbd, 0x83, 0xd5, 0x4a, 0xaf, 0x44, 0x1e, 0x31, 0x9e, 0xa4, - 0x7a, 0x86, 0x2a, 0xd0, 0x29, 0x3c, 0xed, 0xf5, 0xdd, 0x9e, 0xda, 0xde, 0xee, 0x33, - 0xcb, 0x52, 0x2c, 0xd0, 0x11, 0x8b, 0xbd, 0x81, 0x1a, 0xce, 0x9a, 0x23, 0xbd, 0xa3, - 0x9a, 0xba, 0x72, 0xf1, 0x56, 0x6f, 0xc1, 0x68, 0x84, 0x97, 0xd2, 0xa7, 0x92, 0x8c, - 0x36, 0x70, 0x15, 0x25, 0x67, 0x8b, 0xc9, 0x72, 0x14, 0xb3, 0x1b, 0x37, 0xba, 0xb4, - 0x6b, 0x88, 0xf2, 0x7f, 0x04, 0x48, 0xde, 0xcb, 0x31, 0x62, 0x2d, 0x0f, 0x0f, 0x87, - 0xa8, 0x55, 0xba, 0x54, 0x00, 0x03, 0x32, 0x03, 0x1f, 0x73, 0xab, 0xff, 0xd4, 0x65, - 0x91, 0xda, 0x0b, 0x88, 0x72, 0x35, 0x04, 0xed, 0xb2, 0x33, 0x72, 0x30, 0xda, 0xd2, - 0xac, 0xc0, 0xd8, 0xbb, 0x68, 0xbc, 0x83, 0x7a, 0x2f, 0xf9, 0x30, 0xbf, 0xf0, 0x6f, - 0xde, 0x74, 0xeb, 0x90, 0xaa, 0xe4, 0xf6, 0x0d, 0xbb, 0x6e, 0xb8, 0x27, 0xea, 0x99, - 0x88, 0x4a, 0xcd, 0x62, 0x85, 0xa9, 0x88, 0x92, 0x80, 0x2c, 0xf5, 0x9d, 0x5d, 0x60, - 0xd0, 0x16, 0x63, 0x38, 0x7b, 0x3e, 0xd2, 0x72, 0x3b, 0xd6, 0x48, 0x9e, 0x9c, 0x2c, - 0x10, 0x6d, 0x4a, 0xa2, 0xde, 0x23, 0xce, 0xd1, 0x6c, 0x72, 0x04, 0x29, 0xc7, 0x75, - 0x3a, 0x77, 0x38, 0xec, 0x7d, 0x9d, 0xb8, 0x62, 0x42, 0x29, 0xed, 0xd2, 0x17, 0xb8, - 0x0d, 0x74, 0x87, 0x5a, 0x14, 0xca, 0xe4, 0x86, 0x3f, 0x13, 0x9e, 0x9c, 0x0b, 0x13, - 0x1b, 0x2a, 0x4c, 0x28, 0x07, 0x1a, 0x38, 0xec, 0x61, 0xf6, 0x68, 0x01, 0xaa, 0x59, - 0x56, 0xfc, 0xb2, 0xa4, 0x6b, 0x95, 0x87, 0x66, 0x5b, 0x75, 0x71, 0xaa, 0x03, 0x48, - 0x1f, 0xd8, 0xd9, 0xd5, 0x69, 0x8f, 0x83, 0x6f, 0xc8, 0x63, 0x5e, 0x69, 0xe3, 0xbd, - 0xe4, 0x2f, 0x4a, 0xc0, 0x71, 0x32, 0x8b, 0x54, 0x09, 0xf6, 0xe4, 0x2d, 0x79, 0x0a, - 0xed, 0xd7, 0x3b, 0xc1, 0xa2, 0x35, 0x47, 0x23, 0xb3, 0xb8, 0x19, 0xd0, 0x63, 0x7a, - 0x6f, 0xa4, 0x66, 0x39, 0x46, 0xa3, 0x0a, 0xc5, 0xaf, 0xdd, 0x30, 0xce, 0x83, 0x0f, - 0x67, 0x91, 0xb4, 0x57, 0x52, 0x70, 0xa1, 0x72, 0x0f, 0x91, 0x86, 0x6e, 0x2b, 0x86, - 0xf4, 0x78, 0x88, 0x94, 0xc8, 0xda, 0x62, 0xd8, 0xb9, 0x1f, 0xaf, 0x52, 0x0e, 0x3b, - 0xed, 0xbc, 0x12, 0x06, 0xa5, 0xa5, 0xe6, 0xef, 0xd3, 0xdf, 0xde, 0x08, 0x43, 0xc3, - 0xb0, 0x67, 0x57, 0x64, 0x3f, 0xc0, 0x06, 0x00, 0x88, 0x38, 0xca, 0x47, 0x30, 0x87, - 0xf8, 0x97, 0x79, 0x18, 0xcc, 0x1b, 0x81, 0xc9, 0xe6, 0x8e, 0x3b, 0x88, 0x8f, 0xe6, - 0xf7, 0xc6, 0x30, 0xf1, 0xbc, 0x7a, 0xe1, 0x88, 0xf5, 0x12, 0x84, 0x20, 0x41, 0xca, - 0xda, 0x1e, 0x05, 0xf8, 0x66, 0xd2, 0x56, 0x2d, 0xbe, 0x09, 0xc4, 0xb4, 0x30, 0x68, - 0xf7, 0x54, 0xda, 0xd3, 0x4d, 0xf0, 0xfc, 0xfc, 0x18, 0x1f, 0x31, 0x80, 0x1a, 0x79, - 0x92, 0xd2, 0xf1, 0x6b, 0xe0, 0x21, 0x1b, 0x4a, 0x22, 0xf6, 0x2a, 0xab, 0x64, 0x70, - 0x1b, 0xf4, 0xa4, 0xe6, 0xd6, 0x66, 0xfc, 0x30, 0x4a, 0x5c, 0x79, 0xc6, 0x09, 0xac, - 0xc4, 0x3b, 0x00, 0xb4, 0x86, 0x48, 0x93, 0xd3, 0x7d, 0x50, 0x07, 0xf0, 0xc3, 0x29, - 0xa4, 0x75, 0x50, 0x52, 0x57, 0x75, 0x70, 0xdd, 0x38, 0xfa, 0xc0, 0x43, 0xcd, 0x91, - 0xc1, 0x2e, 0xe3, 0x4e, 0x9c, 0xfa, 0xe3, 0x92, 0xa7, 0x8b, 0xda, 0xbd, 0x4e, 0xe3, - 0x1d, 0xc0, 0xde, 0xb0, 0x2f, 0xe7, 0xb1, 0xd8, 0xb0, 0x17, 0x8a, 0xc9, 0x51, 0x31, - 0x05, 0xfc, 0xc7, 0xe3, 0x0b, 0xa8, 0xe0, 0x16, 0xaa, 0x36, 0xa6, 0xb5, 0xdf, 0x5e, - 0x5a, 0x19, 0x09, 0xf6, 0x3a, 0xba, 0x09, 0x5d, 0x98, 0x77, 0xa8, 0xf2, 0x6e, 0x40, - 0x3d, 0xc2, 0x54, 0x76, 0xad, 0xd8, 0x3d, 0xb1, 0xca, 0x15, 0x8f, 0x0b, 0x42, 0x2b, - 0x5f, 0xf4, 0xdb, 0x69, 0xb1, 0x24, 0x4b, 0xc0, 0x90, 0x2e, 0xd0, 0x30, 0x3f, 0xec, - 0x73, 0xa5, 0xbf, 0xfe, 0xc7, 0x3a, 0xc3, 0x4c, 0x1a, 0x73, 0x16, 0x0f, 0x2c, 0xea, - 0x1e, 0x05, 0x10, 0xf8, 0x4d, 0x2f, 0xe2, 0xf7, 0x3b, 0x6e, 0x92, 0x19, 0x07, 0xa1, - 0xb7, 0xb3, 0x75, 0x12, 0x13, 0x24, 0x30, 0x11, 0x76, 0xb0, 0x9b, 0xc0, 0x41, 0xe4, - 0x68, 0x42, 0x3e, 0x93, 0xd5, 0xdc, 0xa3, 0x3e, 0x67, 0x1a, 0x78, 0x6d, 0x23, 0x1f, - 0x16, 0x43, 0xea, 0x66, 0x43, 0x8b, 0xa7, 0x85, 0xb8, 0x1e, 0x6c, 0x2b, 0xc7, 0x3f, - 0xf0, 0x0d, 0x89, 0x3b, 0xc1, 0x28, 0x5e, 0xfc, 0xa8, 0x25, 0x99, 0xd1, 0x81, 0xf1, - 0x23, 0x51, 0xf9, 0x39, 0xa9, 0x4e, 0xa8, 0xb9, 0x75, 0xc0, 0x65, 0xa9, 0x1f, 0xf2, - 0x57, 0xca, 0xc7, 0xa9, 0x23, 0x85, 0xfc, 0x8f, 0xa9, 0x21, 0xb1, 0x06, 0xba, 0x86, - 0x60, 0xc6, 0x0a, 0xc8, 0xba, 0x5e, 0xce, 0x45, 0x60, 0x6f, 0x04, 0xf3, 0x6a, 0x3a, - 0x90, 0xbb, 0x38, 0x38, 0xc4, 0x2a, 0xbf, 0x62, 0xdd, 0x2d, 0x84, 0xba, 0xbe, 0xf3, - 0xe1, 0x88, 0xe9, 0x17, 0x1a, 0xff, 0x9b, 0xc1, 0x16, 0x66, 0x90, 0x09, 0xd8, 0x87, - 0x13, 0x0a, 0xc9, 0xf7, 0x39, 0x6a, 0x62, 0x7a, 0x84, 0x74, 0xc1, 0x81, 0x1b, 0x69, - 0x6f, 0x99, 0x55, 0x2b, 0x14, 0xc4, 0x84, 0xdf, 0xe4, 0x2c, 0x24, 0xd5, 0x7c, 0x3a, - 0x9c, 0x3f, 0xea, 0x13, 0x76, 0xcd, 0xcb, 0x63, 0x42, 0x1c, 0x31, 0x4a, 0x62, 0x2a, - 0x9a, 0xef, 0x0b, 0xc0, 0x57, 0xcb, 0x11, 0xbc, 0x5e, 0x30, 0x66, 0xe3, 0x3a, 0x3b, - 0x9b, 0x31, 0xdf, 0x25, 0x75, 0xcd, 0x51, 0x85, 0xa4, 0xf3, 0xfc, 0x4e, 0x4c, 0x3d, - 0x40, 0x2e, 0xd4, 0x20, 0x46, 0xf8, 0x1f, 0x97, 0x48, 0x16, 0xd2, 0x79, 0xb1, 0x51, - 0x3a, 0xb8, 0x1d, 0x3f, 0x0a, 0x3c, 0x7f, 0x7f, 0xcf, 0x2f, 0xbb, 0x4e, 0x26, 0x32, - 0x19, 0x93, 0xa5, 0x13, 0xad, 0x3d, 0x7f, 0x4a, 0xfe, 0x6c, 0x1b, 0xbd, 0xc6, 0x57, - 0x58, 0x50, 0x80, 0xbb, 0x5a, 0x0f, 0x25, 0x97, 0x3d, 0x63, 0xeb, 0x20, 0xad, 0xa0, - 0x16, 0x6b, 0xbd, 0x8a, 0x39, 0xff, 0x93, 0x24, 0x6f, 0x27, 0x89, 0x73, 0x2a, 0xd0, - 0x55, 0x87, 0xf8, 0xdb, 0x7b, 0xc8, 0x7c, 0x24, 0x2c, 0xfd, 0x36, 0xce, 0x68, 0x5a, - 0x4b, 0x65, 0x69, 0x86, 0xc3, 0x9f, 0xd7, 0xfc, 0xb2, 0x3c, 0x91, 0x91, 0x3e, 0x46, - 0x11, 0x19, 0x1e, 0xdc, 0xc8, 0x8b, 0x78, 0xf1, 0x45, 0xea, 0x29, 0xd2, 0x71, 0xb9, - 0x40, 0xc6, 0x99, 0x41, 0xe4, 0xc3, 0xfd, 0x2d, 0x71, 0xf3, 0xb1, 0x90, 0x69, 0x0e, - 0xe1, 0x6f, 0x5d, 0x14, 0xac, 0x22, 0x24, 0xe6, 0xfc, 0x89, 0x59, 0x76, 0x54, 0x52, - 0x7d, 0xab, 0xe7, 0x2e, 0x75, 0xd2, 0xd2, 0xa1, 0x3a, 0x9f, 0xba, 0xa6, 0x37, 0x8e, - 0x8a, 0x26, 0x43, 0x21, 0x08, 0x7a, 0x19, 0x00, 0xef, 0xe3, 0xca, 0xd1, 0x4a, 0x57, - 0x96, 0x86, 0xaa, 0x36, 0x36, 0xbd, 0x37, 0x5b, 0xd3, 0x13, 0x6b, 0xee, 0x0b, 0xda, - 0xab, 0xcf, 0xac, 0x88, 0x1b, 0xc7, 0x01, 0x81, 0x27, 0x21, 0xe6, 0xfb, 0x75, 0xaa, - 0x07, 0x2d, 0x2d, 0x18, 0x7e, 0x62, 0x25, 0x8d, 0x65, 0xa1, 0x92, 0x15, 0x7c, 0xdf, - 0x2e, 0xc3, 0x21, 0x40, 0x7f, 0x68, 0x2f, 0x5e, 0xec, 0x6a, 0x32, 0x97, 0xab, 0x20, - 0xb7, 0x06, 0x1c, 0x62, 0x24, 0x57, 0x16, 0xa4, 0x4f, 0x71, 0xfb, 0xfc, 0x34, 0xc7, - 0x9b, 0x44, 0xe0, 0x9e, 0x42, 0x12, 0xac, 0x26, 0x53, 0xf6, 0xc4, 0x03, 0x64, 0x3e, - 0x1c, 0x5b, 0x9a, 0xd1, 0x34, 0xd8, 0x9c, 0x68, 0x0b, 0x70, 0x72, 0x83, 0xaf, 0x54, - 0x32, 0x6f, 0xc4, 0xf8, 0x4d, 0x6a, 0x58, 0x29, 0xa0, 0xad, 0x48, 0x30, 0x80, 0x6c, - 0x05, 0x75, 0x84, 0x92, 0xcd, 0x6a, 0xc4, 0x6b, 0xa0, 0x1a, 0x2b, 0x37, 0x22, 0xb5, - 0xe4, 0xcd, 0xaf, 0xbb, 0x3f, 0x36, 0x78, 0x5f, 0x42, 0x4a, 0xf0, 0x44, 0xda, 0xc5, - 0xdb, 0x5f, 0x7d, 0xf8, 0x39, 0xeb, 0x63, 0xc0, 0xc1, 0x7d, 0x8b, 0x0c, 0x79, 0xdb, - 0x86, 0x30, 0x94, 0x20, 0x15, 0xbe, 0x13, 0xf7, 0x9a, 0xf6, 0xf4, 0x3e, 0x5a, 0xb0, - 0x77, 0x81, 0x14, 0x79, 0x8f, 0x44, 0x22, 0x58, 0xee, 0xdc, 0x43, 0x6f, 0xcc, 0x38, - 0x6b, 0x36, 0xb5, 0x7e, 0x19, 0x17, 0xd7, 0x20, 0x17, 0x73, 0x66, 0xf4, 0x24, 0xb0, - 0xa5, 0x4b, 0x0b, 0x60, 0xf4, 0xfb, 0x13, 0x58, 0xc2, 0x0a, 0xa4, 0x1d, 0xc5, 0x02, - 0xe1, 0xdd, 0x8a, 0x16, 0x33, 0xf3, 0xd8, 0xe3, 0x27, 0x6b, 0x59, 0xe7, 0xd2, 0xc4, - 0xe6, 0x24, 0xa6, 0xf5, 0x36, 0x95, 0xbc, 0xaf, 0x24, 0x7e, 0x36, 0x48, 0x3f, 0x13, - 0xb2, 0x04, 0x42, 0x22, 0x37, 0xfc, 0x6a, 0xb3, 0xeb, 0xa0, 0x2f, 0xc4, 0x14, 0x2b, - 0x42, 0x97, 0xeb, 0xb5, 0x68, 0x3d, 0xb8, 0xd2, 0x43, 0x19, 0x70, 0x6a, 0xd2, 0x6a, - 0xaf, 0xd8, 0x1c, 0x53, 0xb7, 0x40, 0xf3, 0x45, 0x43, 0xa6, 0xb3, 0xe9, 0xf5, 0xbb, - 0x7d, 0x5c, 0x49, 0xe8, 0xc3, 0x7f, 0x61, 0x49, 0x21, 0x25, 0x4f, 0x32, 0x12, 0x39, - 0x4c, 0x79, 0x7d, 0x1c, 0xee, 0x78, 0x99, 0xb7, 0xb4, 0xb6, 0x5b, 0x59, 0xb7, 0x34, - 0x2f, 0x92, 0x53, 0x1c, 0x1d, 0x59, 0xe1, 0x79, 0x70, 0xb7, 0x31, 0x74, 0x14, 0x43, - 0x8c, 0xd8, 0x0b, 0xd0, 0xf9, 0xa6, 0x7c, 0x9b, 0x9e, 0x55, 0x2f, 0x01, 0x3c, 0x11, - 0x5a, 0x95, 0x4f, 0x35, 0xe0, 0x61, 0x6c, 0x68, 0xd4, 0x31, 0x63, 0xd3, 0x34, 0xda, - 0xc3, 0x82, 0x70, 0x33, 0xe5, 0xad, 0x84, 0x88, 0xbf, 0xd9, 0xc4, 0xbb, 0xbe, 0x8f, - 0x59, 0x35, 0xc6, 0xc5, 0xea, 0x04, 0xc3, 0xad, 0x49, 0xc7, 0x47, 0xa9, 0xe7, 0x23, - 0x1b, 0xcd, 0x7d, 0x16, 0x21, 0x5e, 0x6e, 0x80, 0x73, 0x7d, 0x6b, 0x54, 0xfe, 0xc8, - 0xb8, 0x84, 0x02, 0xf0, 0x47, 0x52, 0x45, 0xe1, 0x74, 0xa7, 0x45, 0xb8, 0x31, 0xf8, - 0xfe, 0x03, 0xa7, 0x6f, 0xb9, 0xce, 0xca, 0x4d, 0x22, 0xb7, 0x83, 0xc3, 0x28, 0xc6, - 0x91, 0x5c, 0x43, 0x40, 0x50, 0x64, 0xae, 0x56, 0xbc, 0x89, 0xe6, 0x4d, 0x15, 0x78, - 0xe4, 0xd3, 0xa3, 0x4b, 0xb9, 0x55, 0x91, 0xea, 0xf1, 0xd3, 0xda, 0x02, 0xa4, 0x54, - 0x9f, 0xa8, 0x0d, 0xb0, 0xff, 0x7c, 0xb0, 0x39, 0x93, 0xb6, 0x8a, 0xe1, 0x5a, 0x30, - 0xe8, 0x79, 0x49, 0xaa, 0x08, 0x0e, 0x94, 0xab, 0xde, 0x68, 0x89, 0x8c, 0x33, 0x92, - 0xa2, 0x17, 0xd6, 0x49, 0x61, 0x6b, 0xbe, 0x73, 0x9b, 0x13, 0xd1, 0x4d, 0xf0, 0x3f, - 0xf2, 0x76, 0x71, 0x48, 0x9b, 0xe0, 0xb4, 0xbe, 0xba, 0xaf, 0xa7, 0xd1, 0xe6, 0x39, - 0xd5, 0xb3, 0xe9, 0x94, 0xff, 0xb6, 0xb7, 0xa2, 0x09, 0xf6, 0xad, 0xfe, 0x8d, 0x1e, - 0x5c, 0xcf, 0x01, 0x0c, 0x19, 0x16, 0x8a, 0xeb, 0x00, 0xaa, 0x9d, 0x68, 0x7e, 0x24, - 0xad, 0xc0, 0xb1, 0x13, 0x5c, 0x70, 0xc9, 0x70, 0xe0, 0x90, 0x3a, 0xf6, 0xe1, 0x70, - 0x81, 0xd5, 0x81, 0x8e, 0x88, 0xb1, 0x4e, 0x4f, 0x60, 0x1b, 0x8c, 0x06, 0x3e, 0x3f, - 0x43, 0x87, 0xff, 0xa2, 0x32, 0x2a, 0x51, 0x81, 0x90, 0x9f, 0x09, 0x80, 0xd6, 0x89, - 0xde, 0x7f, 0x8e, 0x6a, 0x5c, 0x62, 0xa7, 0x77, 0xd1, 0x75, 0x00, 0x2a, 0x13, 0x7d, - 0xe8, 0x5b, 0x88, - ], - script_code: Script(vec![]), - transparent_input: None, - hash_type: 1, - amount: 1039204199089370, - consensus_branch_id: 1991772603, - sighash: [ - 0x6c, 0x4e, 0x32, 0x44, 0xc2, 0xd2, 0xbf, 0xb8, 0xd6, 0xf6, 0x69, 0x97, 0x77, 0xa1, - 0x1a, 0x64, 0xad, 0xfe, 0xe4, 0x9b, 0x2f, 0xc7, 0x81, 0xe6, 0x95, 0x15, 0x34, 0xf9, - 0x73, 0x44, 0x0d, 0xdb, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xdc, 0xf7, 0x58, 0x76, 0xdc, - 0xa6, 0x09, 0xf9, 0xd2, 0x84, 0x71, 0xf9, 0x97, 0xfa, 0x11, 0xf9, 0x9d, 0x42, 0x3f, - 0x9c, 0xf1, 0x73, 0x4b, 0xe8, 0xa5, 0xff, 0x99, 0x7d, 0x45, 0x1e, 0xb3, 0xcf, 0x4b, - 0x3d, 0xfd, 0xd9, 0x06, 0xac, 0xac, 0x63, 0x52, 0x63, 0x6a, 0xdc, 0x17, 0xa8, 0x36, - 0xb1, 0x2b, 0x43, 0xbe, 0xfc, 0x0b, 0xe0, 0xa1, 0xbd, 0x36, 0x97, 0x72, 0x33, 0x80, - 0x78, 0xb4, 0xff, 0x7d, 0x8e, 0x2d, 0x97, 0x9a, 0x34, 0x41, 0xe1, 0xc8, 0xf5, 0xaf, - 0xe4, 0x7b, 0x1e, 0x7d, 0xa5, 0x6c, 0xf0, 0x06, 0x02, 0x00, 0x53, 0x11, 0x0c, 0x05, - 0xcf, 0x00, 0xfd, 0xa3, 0xe6, 0xcc, 0xe3, 0x60, 0x69, 0x04, 0x1f, 0xaf, 0xfd, 0x2f, - 0x77, 0xff, 0x06, 0x00, 0x02, 0xef, 0x12, 0xc3, 0x67, 0xf2, 0x1d, 0xea, 0x65, 0xc6, - 0xea, 0xaf, 0xb8, 0xaf, 0x58, 0x42, 0x8f, 0x6c, 0x54, 0x8e, 0x50, 0x17, 0x0f, 0x9e, - 0x6f, 0xcd, 0xdf, 0xe7, 0x51, 0xe0, 0xb6, 0x80, 0x12, 0xcb, 0x59, 0xdd, 0x46, 0x27, - 0xef, 0xc3, 0xea, 0x75, 0xdc, 0xd1, 0x5c, 0x8e, 0x0c, 0x3b, 0x8d, 0x8d, 0x7d, 0x6b, - 0x23, 0x31, 0xc8, 0xe4, 0x80, 0x16, 0x6b, 0x5a, 0xa7, 0x48, 0x5c, 0x9f, 0x0f, 0x83, - 0xe1, 0x9b, 0xc3, 0x0e, 0x64, 0x03, 0x82, 0x8c, 0xdb, 0x65, 0x2a, 0x55, 0x6b, 0x12, - 0x04, 0x09, 0x31, 0x40, 0x2a, 0xa6, 0xac, 0x34, 0xfc, 0x19, 0xfd, 0xc0, 0x6e, 0x2e, - 0x77, 0x87, 0xf5, 0x58, 0xd1, 0x42, 0xd9, 0x06, 0xea, 0xdb, 0x75, 0x90, 0xc9, 0x41, - 0x36, 0xda, 0x6a, 0x06, 0x35, 0x14, 0xd6, 0xa2, 0x5f, 0x7b, 0x37, 0xd7, 0x66, 0x4f, - 0x9b, 0x97, 0x09, 0x43, 0x3e, 0x6e, 0x70, 0x21, 0x18, 0xa4, 0xab, 0x9e, 0x7a, 0x7a, - 0x3e, 0x62, 0x59, 0x12, 0x99, 0x37, 0xd2, 0x9d, 0x0d, 0xb2, 0x60, 0x70, 0x52, 0x3e, - 0x8b, 0x06, 0x43, 0x13, 0x0a, 0xbe, 0xfe, 0x94, 0x3b, 0x40, 0x12, 0x98, 0xae, 0x01, - 0xa3, 0xab, 0x00, 0xab, 0xbc, 0x60, 0xd7, 0xdb, 0x93, 0x3c, 0x7f, 0x07, 0xa8, 0xbf, - 0x0f, 0x7c, 0xe1, 0x66, 0x0b, 0xcc, 0xb4, 0x5e, 0x04, 0x2b, 0x45, 0x1b, 0x93, 0x50, - 0x02, 0xce, 0xce, 0x27, 0xf3, 0x6a, 0xba, 0x56, 0x47, 0xac, 0x28, 0xd8, 0x18, 0x6c, - 0xdd, 0x1f, 0xb9, 0x5d, 0xc1, 0x35, 0xd4, 0x89, 0x92, 0xf6, 0x8d, 0xa1, 0x2a, 0xd6, - 0x1a, 0xc7, 0x56, 0x68, 0x0d, 0xd7, 0xf8, 0xd0, 0x77, 0x4a, 0xbd, 0x6c, 0xfd, 0xa2, - 0xf0, 0x32, 0xaf, 0x3b, 0xe1, 0x39, 0xa6, 0x33, 0xd6, 0x73, 0x3c, 0x75, 0xd1, 0xab, - 0xa8, 0x90, 0x18, 0xc8, 0x57, 0x2b, 0x99, 0xcd, 0x30, 0xc5, 0x37, 0x06, 0x79, 0x41, - 0xdf, 0x1c, 0x4b, 0xc1, 0xfd, 0x57, 0x0f, 0x7b, 0x4d, 0xdc, 0x97, 0x51, 0x86, 0x23, - 0xe3, 0xae, 0x4a, 0x87, 0xbd, 0xb9, 0x66, 0xc9, 0x4d, 0x86, 0x1e, 0x80, 0xde, 0x88, - 0xc2, 0x92, 0xae, 0xe9, 0x38, 0x71, 0x94, 0xe2, 0x56, 0xc6, 0x70, 0x07, 0x52, 0x30, - 0x1c, 0x73, 0xfc, 0x95, 0x65, 0xa4, 0x04, 0x80, 0xd8, 0x12, 0x6e, 0x9d, 0x08, 0x58, - 0x79, 0xe2, 0x4b, 0x16, 0xe9, 0xc4, 0x85, 0xd8, 0xf0, 0xd6, 0x18, 0xca, 0x0d, 0xd1, - 0x21, 0xb5, 0x1a, 0x7c, 0xab, 0x23, 0x0c, 0x5b, 0x45, 0x67, 0x2b, 0xdb, 0x8e, 0xa3, - 0xa0, 0x40, 0xf7, 0xaa, 0xa0, 0x98, 0xba, 0x26, 0x02, 0x5d, 0x2e, 0xab, 0x79, 0x48, - 0x69, 0x3d, 0xd5, 0xf6, 0xd3, 0x09, 0x65, 0x01, 0xe9, 0xe0, 0x71, 0x25, 0xd7, 0xeb, - 0x29, 0x3b, 0x3a, 0xba, 0xd5, 0x7f, 0xd5, 0xf0, 0x11, 0x64, 0x70, 0x02, 0xd6, 0x26, - 0xae, 0x88, 0xdc, 0x61, 0xe6, 0x47, 0xff, 0x46, 0x8d, 0xfa, 0x7a, 0x03, 0x07, 0x72, - 0x78, 0x79, 0x32, 0x75, 0xf1, 0x95, 0xa9, 0x75, 0x30, 0x28, 0x91, 0x78, 0x51, 0x61, - 0x80, 0xc5, 0xff, 0x99, 0x93, 0x53, 0x6b, 0xda, 0x15, 0x04, 0xba, 0x8b, 0xb4, 0x89, - 0x19, 0x88, 0xc1, 0x33, 0x4f, 0x31, 0xfb, 0x27, 0x6a, 0x03, 0x8a, 0xa8, 0xe9, 0x67, - 0xcb, 0x62, 0xa4, 0x92, 0x1b, 0xeb, 0x22, 0xb2, 0x08, 0xb0, 0x64, 0x58, 0x18, 0x47, - 0xb2, 0xf6, 0x4c, 0xa6, 0x48, 0x37, 0x00, 0x72, 0x16, 0xde, 0x6e, 0xca, 0xff, 0xeb, - 0x4b, 0x69, 0xe6, 0x33, 0x47, 0xf8, 0x4a, 0xbc, 0xad, 0x8f, 0x2e, 0x75, 0x7d, 0x58, - 0x61, 0xce, 0x77, 0xee, 0x46, 0x51, 0x3d, 0xa7, 0x41, 0x68, 0x37, 0xdc, 0xb2, 0x3d, - 0x33, 0xea, 0x72, 0xaf, 0x23, 0xd0, 0xad, 0x8c, 0x93, 0x07, 0xd0, 0xb5, 0x85, 0x8d, - 0xa9, 0x5b, 0x77, 0xff, 0xf9, 0x02, 0x7b, 0x88, 0x59, 0xe1, 0x1d, 0xcb, 0xd5, 0x98, - 0x35, 0x0e, 0xee, 0x50, 0x93, 0x94, 0x81, 0x70, 0x8e, 0xa7, 0x08, 0xeb, 0x9f, 0x66, - 0x43, 0x88, 0xb9, 0xc6, 0x4d, 0x6a, 0xf0, 0xf9, 0x66, 0x90, 0x34, 0x24, 0x00, 0x34, - 0x8e, 0x92, 0x9e, 0x07, 0x46, 0x02, 0x53, 0xf3, 0x83, 0x90, 0xf8, 0x7b, 0xd6, 0xc0, - 0x53, 0x08, 0xc3, 0xbd, 0xe2, 0x52, 0x28, 0xe0, 0xfa, 0x08, 0x80, 0xb0, 0x8e, 0xf3, - 0x4a, 0x5a, 0x9c, 0xc0, 0xea, 0x0a, 0x67, 0xca, 0x65, 0xb6, 0xff, 0xd0, 0x05, 0x57, - 0x29, 0x09, 0xf1, 0xc4, 0x2d, 0xd7, 0x45, 0xee, 0xee, 0x9d, 0xd6, 0xb4, 0x43, 0x9c, - 0x9f, 0x3f, 0x98, 0xa1, 0x18, 0xfe, 0x16, 0x69, 0x8e, 0x9c, 0xef, 0xf5, 0x58, 0xf1, - 0x60, 0x66, 0x97, 0x5f, 0xe3, 0x95, 0x83, 0xe9, 0xb5, 0x85, 0x3b, 0x13, 0x11, 0x39, - 0x15, 0x80, 0x01, 0x9f, 0xe5, 0x5d, 0x59, 0xd1, 0xc8, 0x28, 0xd3, 0xfe, 0xb6, 0xa3, - 0xb9, 0xce, 0x92, 0xd0, 0x89, 0xae, 0x4b, 0x40, 0x8e, 0x23, 0xd6, 0xa4, 0x37, 0xd4, - 0x98, 0x9b, 0x51, 0x9b, 0x7a, 0x9e, 0xb0, 0x8a, 0xe6, 0xd4, 0x48, 0xa7, 0xa1, 0x6e, - 0x8a, 0xed, 0x26, 0xa2, 0xec, 0xd0, 0xca, 0xd8, 0x08, 0x44, 0xfd, 0x06, 0x50, 0xd8, - 0xc4, 0xe4, 0xd2, 0xaf, 0x90, 0x65, 0x67, 0x48, 0xd8, 0x09, 0x9a, 0x0c, 0x75, 0x6f, - 0xc1, 0x6c, 0xca, 0x06, 0xa3, 0x34, 0x43, 0x07, 0x02, 0xae, 0x19, 0x61, 0x66, 0x5b, - 0x48, 0x45, 0xac, 0xd1, 0xa8, 0xe3, 0x41, 0x01, 0xe6, 0x8b, 0xb6, 0x44, 0xac, 0x03, - 0x4d, 0xc6, 0x3e, 0x6e, 0x34, 0x4c, 0x3d, 0x63, 0x76, 0x2a, 0x7a, 0x5b, 0xf5, 0x9f, - 0x13, 0x09, 0x54, 0x10, 0x98, 0x1d, 0x6b, 0x6b, 0x16, 0xbc, 0xd4, 0xc9, 0xfa, 0x68, - 0xaf, 0x6e, 0x53, 0x01, 0xef, 0x19, 0xbf, 0x3a, 0x43, 0x2e, 0x40, 0x6f, 0x85, 0x67, - 0xeb, 0xd9, 0x77, 0x2e, 0x92, 0xb5, 0xca, 0x5a, 0x59, 0x96, 0x71, 0xcb, 0xfd, 0x7d, - 0xdf, 0xa3, 0x63, 0xa5, 0x36, 0xb7, 0xac, 0x45, 0xf5, 0x7c, 0xc3, 0x7d, 0x09, 0x89, - 0x6f, 0xa9, 0x06, 0x97, 0x2e, 0x55, 0x71, 0x80, 0xa4, 0xab, 0x5a, 0xd0, 0x9d, 0x88, - 0x46, 0xdd, 0x6d, 0xa7, 0x48, 0x76, 0x54, 0x36, 0xe0, 0x16, 0x02, 0x40, 0xbd, 0x5c, - 0x92, 0x16, 0x66, 0xa1, 0xee, 0xaa, 0xce, 0x04, 0xa7, 0x1b, 0x50, 0x3a, 0x1c, 0xad, - 0xf8, 0x0b, 0x39, 0x24, 0x26, 0x6c, 0x59, 0x50, 0x4f, 0x8f, 0x21, 0x5f, 0x61, 0x8b, - 0x05, 0xd5, 0x45, 0x43, 0xb6, 0xe2, 0x6d, 0x82, 0x59, 0x6f, 0xc5, 0x3b, 0x52, 0x31, - 0x2c, 0x77, 0x6d, 0x12, 0xeb, 0x2b, 0x65, 0x9b, 0x4f, 0xb0, 0x98, 0xdf, 0x87, 0xd6, - 0x83, 0xcf, 0x9e, 0x54, 0x12, 0xee, 0x56, 0xc3, 0xfe, 0x98, 0x41, 0xd7, 0x3f, 0xd0, - 0x70, 0xdf, 0xa5, 0x1f, 0x5b, 0xaf, 0xed, 0xf2, 0x06, 0xf1, 0x3c, 0x52, 0x4e, 0x5c, - 0x50, 0xca, 0xc9, 0x90, 0x6e, 0xfa, 0x39, 0x32, 0x90, 0x04, 0x2e, 0x3b, 0xc5, 0x9f, - 0x96, 0x0b, 0x7d, 0x24, 0x0a, 0xe4, 0x43, 0xfc, 0x49, 0x26, 0x9c, 0xe0, 0x00, 0x61, - 0xe6, 0x5c, 0x6d, 0x74, 0x81, 0x2a, 0x30, 0xdd, 0x5f, 0x5f, 0xe7, 0x4e, 0xff, 0x61, - 0xe0, 0xcb, 0xab, 0x3c, 0xec, 0x75, 0xd0, 0xae, 0xf9, 0x50, 0x83, 0x18, 0x94, 0x52, - 0xdd, 0x3d, 0x9e, 0xdf, 0x44, 0x87, 0xbc, 0x73, 0x4c, 0x8b, 0x24, 0xf2, 0x12, 0x96, - 0xe4, 0xe9, 0xef, 0x11, 0x7d, 0x7f, 0xb9, 0x77, 0xe3, 0xb0, 0xe6, 0x40, 0x6e, 0x63, - 0x08, 0x59, 0x06, 0x33, 0x1a, 0x93, 0x03, 0x3d, 0x1c, 0xb8, 0x36, 0x0f, 0xe6, 0xfe, - 0xa6, 0x1a, 0x68, 0x26, 0xdf, 0x36, 0x25, 0x57, 0x89, 0xf9, 0x2e, 0x40, 0xba, 0xfc, - 0xb2, 0xeb, 0xcb, 0x9e, 0x55, 0x6f, 0x6c, 0x0c, 0xca, 0xdc, 0x6a, 0xf0, 0x8e, 0x31, - 0xec, 0x4a, 0xd5, 0x28, 0x80, 0x34, 0xe1, 0x6d, 0x15, 0x5c, 0xfd, 0xca, 0xda, 0x7b, - 0xab, 0x59, 0x9c, 0x2f, 0xa4, 0xad, 0x2e, 0x62, 0x93, 0xf9, 0xfe, 0x09, 0x71, 0x69, - 0x14, 0x82, 0x76, 0xb6, 0xa9, 0xea, 0xa7, 0x2f, 0x14, 0x8b, 0x0c, 0x95, 0x65, 0xc3, - 0xc2, 0xdd, 0x63, 0x12, 0x5e, 0x0f, 0xa5, 0x30, 0x86, 0x1a, 0x71, 0x0d, 0xf8, 0xe4, - 0x81, 0xf2, 0x71, 0x29, 0x20, 0xf8, 0x78, 0x7e, 0x0a, 0xed, 0xfe, 0x61, 0x8a, 0xff, - 0x50, 0xa3, 0xb5, 0x62, 0x13, 0x88, 0x4d, 0x62, 0x62, 0xc1, 0x1d, 0xeb, 0xf2, 0xba, - 0x7e, 0x8a, 0xd6, 0x69, 0x2c, 0xb1, 0x70, 0x78, 0x33, 0x14, 0x18, 0xda, 0x4b, 0xe0, - 0x64, 0xff, 0x52, 0x70, 0x07, 0x39, 0x34, 0xab, 0xcd, 0x2a, 0xb0, 0x46, 0x9e, 0xca, - 0xf7, 0x27, 0x5b, 0x4b, 0xd7, 0x2b, 0xc6, 0xed, 0x34, 0x47, 0x8e, 0xa4, 0x08, 0x9b, - 0x73, 0x6a, 0x16, 0xdd, 0x90, 0x6d, 0x49, 0xf2, 0x5c, 0x33, 0x82, 0x7c, 0x57, 0x1c, - 0xe0, 0xb5, 0xd7, 0x21, 0x77, 0xaa, 0x35, 0x08, 0x80, 0x4b, 0xc0, 0xf8, 0xfa, 0xa9, - 0x47, 0x12, 0x22, 0x31, 0x40, 0x2d, 0x2f, 0x5c, 0xc9, 0xa0, 0xeb, 0x0e, 0x09, 0xd4, - 0x27, 0xb4, 0x27, 0x28, 0x8d, 0x93, 0x7d, 0x9d, 0x72, 0xb7, 0x74, 0x56, 0xf8, 0x86, - 0x59, 0x4c, 0xd8, 0xc6, 0xa4, 0x62, 0xf7, 0x7f, 0xd8, 0x30, 0x76, 0x46, 0x9c, 0xc0, - 0xec, 0xba, 0x3c, 0xc4, 0x0c, 0xad, 0x69, 0xe5, 0xb5, 0x41, 0x12, 0xea, 0xb3, 0x33, - 0x96, 0xae, 0xcf, 0xbc, 0x21, 0x1f, 0x1f, 0x79, 0xcf, 0x33, 0x10, 0x8e, 0x93, 0xd9, - 0x53, 0x78, 0xba, 0xe6, 0x95, 0x82, 0x74, 0xb3, 0x10, 0x88, 0xfb, 0xd8, 0xb3, 0xa3, - 0xa0, 0xd1, 0x54, 0xa7, 0x89, 0x73, 0x5b, 0x03, 0x49, 0xc4, 0xd5, 0x1c, 0x88, 0x9d, - 0x08, 0x95, 0x2d, 0xdd, 0x54, 0x88, 0xbe, 0x95, 0x56, 0x05, 0x94, 0xe6, 0x73, 0xfa, - 0x05, 0x1b, 0xf9, 0xb6, 0x14, 0xa1, 0x5e, 0x10, 0x0b, 0x60, 0xa0, 0xfe, 0x9a, 0x7e, - 0x12, 0xa9, 0xb2, 0x56, 0xdf, 0x58, 0x9b, 0x3e, 0x48, 0xe5, 0xb8, 0x0f, 0xb8, 0xcf, - 0xf0, 0x3e, 0x86, 0xf6, 0x0c, 0xc0, 0x70, 0xfb, 0x23, 0xc9, 0x7d, 0x4c, 0x14, 0xfa, - 0x3a, 0x73, 0x46, 0xff, 0x55, 0x6b, 0xc6, 0x85, 0x5a, 0x5f, 0x83, 0xe3, 0xdc, 0xd9, - 0xf6, 0xea, 0xb3, 0xda, 0xbc, 0xd4, 0x77, 0x50, 0xe3, 0x4e, 0x7c, 0x09, 0x38, 0xf6, - 0x4d, 0x45, 0x1e, 0x39, 0x50, 0x9e, 0x90, 0x27, 0x47, 0xa7, 0x07, 0x55, 0x12, 0x20, - 0x95, 0x08, 0x2a, 0xb7, 0x98, 0x59, 0x19, 0x07, 0x31, 0x41, 0xb6, 0xd3, 0x70, 0x20, - 0x91, 0xab, 0x71, 0x72, 0x80, 0xbd, 0xc5, 0x5e, 0x79, 0x9c, 0x01, 0xad, 0x86, 0x41, - 0x90, 0x4e, 0x3b, 0x1d, 0xd2, 0x9e, 0x1a, 0x96, 0x4c, 0x73, 0x7d, 0x3c, 0x15, 0x5a, - 0xfb, 0x30, 0x7b, 0x74, 0x8e, 0x41, 0x12, 0xb4, 0x8b, 0x77, 0xd5, 0xed, 0x57, 0x00, - 0xe6, 0x00, 0x2b, 0x18, 0xb0, 0xfe, 0xd2, 0xcf, 0xfd, 0xf6, 0x1f, 0xd9, 0x93, 0x4b, - 0x60, 0x73, 0x2f, 0x4d, 0x37, 0x81, 0x0a, 0x91, 0xac, 0xef, 0x1e, 0x03, 0x8b, 0x81, - 0xd7, 0x36, 0xd9, 0x8e, 0xad, 0xa9, 0xcd, 0x7e, 0x0c, 0x2b, 0xe2, 0x7a, 0xb8, 0x50, - 0x32, 0x06, 0x60, 0x91, 0x22, 0x4e, 0xdf, 0x87, 0x2f, 0x79, 0x63, 0x7d, 0xda, 0x39, - 0x16, 0x79, 0x6a, 0x5c, 0x62, 0xf5, 0x7f, 0x1d, 0xe3, 0x76, 0x78, 0xb6, 0xde, 0xa0, - 0x08, 0x69, 0x93, 0x36, 0x74, 0xf8, 0x8e, 0x41, 0xa9, 0x18, 0x08, 0x07, 0x3b, 0x0f, - 0x43, 0x6e, 0xbe, 0x25, 0xa5, 0xf4, 0x4a, 0x60, 0x10, 0x33, 0xe2, 0x18, 0x4b, 0x88, - 0xdb, 0x79, 0xe9, 0x68, 0xca, 0x6d, 0x89, 0xb7, 0x49, 0x01, 0xbe, 0x6c, 0x6d, 0xb3, - 0x63, 0x65, 0x80, 0x18, 0x2e, 0x65, 0x8d, 0xfc, 0x68, 0x67, 0x67, 0xd6, 0xd8, 0x19, - 0xfa, 0x92, 0x3e, 0x0c, 0xdf, 0x3e, 0xa3, 0x65, 0x76, 0xf8, 0x52, 0xbc, 0xd4, 0xe1, - 0x96, 0xa7, 0x1a, 0x13, 0x29, 0xf6, 0xc3, 0xff, 0x8e, 0x42, 0xe3, 0x09, 0x5a, 0xbd, - 0x8e, 0xc1, 0x97, 0x99, 0x07, 0x13, 0xee, 0x89, 0x39, 0x4c, 0x57, 0x19, 0xb2, 0x76, - 0xde, 0x8f, 0x81, 0x8a, 0x34, 0xa7, 0xbe, 0xc1, 0xf2, 0x68, 0x68, 0x2e, 0x91, 0x42, - 0xc7, 0xd3, 0x87, 0x89, 0xf6, 0x76, 0xcc, 0x12, 0xb7, 0x1a, 0xb6, 0x66, 0x35, 0xc5, - 0x02, 0xe6, 0x9d, 0x05, 0xb9, 0xc7, 0xef, 0x01, 0x52, 0x97, 0x75, 0xc6, 0x23, 0xa4, - 0x8e, 0x4c, 0xc5, 0xc4, 0x15, 0xc9, 0xfd, 0x56, 0x53, 0x65, 0xa4, 0x16, 0x37, 0x68, - 0x78, 0x51, 0x53, 0x88, 0x7f, 0xb5, 0xf9, 0x63, 0xe7, 0xac, 0xc1, 0x62, 0xf2, 0x80, - 0x5f, 0x45, 0xf4, 0x44, 0x87, 0xf8, 0x5e, 0x19, 0x9c, 0x1d, 0xf4, 0xa0, 0xfc, 0xa4, - 0xd4, 0x4b, 0xaa, 0x62, 0xda, 0x7a, 0xf5, 0xed, 0x69, 0x68, 0x41, 0x12, 0xd3, 0x5f, - 0x00, 0x73, 0x73, 0x2f, 0x5a, 0x1a, 0xc3, 0xe4, 0xf0, 0x21, 0xba, 0x5c, 0x2c, 0x32, - 0xf0, 0x6e, 0x6b, 0x90, 0xfa, 0xe2, 0xd2, 0x54, 0xcf, 0x09, 0xe7, 0x69, 0x0c, 0xf4, - 0xe3, 0xaa, 0x70, 0x30, 0x98, 0x74, 0x48, 0xe1, 0x47, 0xf9, 0x43, 0xba, 0xb5, 0xca, - 0xb5, 0x58, 0x02, 0x9a, 0x36, 0x02, 0x4d, 0x2e, 0x79, 0x0f, 0xc6, 0xfd, 0x66, 0x7f, - 0x17, 0x6e, 0x0a, 0xa9, 0x9d, 0xd1, 0xd7, 0x2b, 0x57, - ], - script_code: Script(vec![0x6a, 0x51, 0x65, 0xac]), - transparent_input: None, - hash_type: 1, - amount: 691732482992802, - consensus_branch_id: 1991772603, - sighash: [ - 0x5d, 0x40, 0x5a, 0x1c, 0x4d, 0xed, 0x19, 0x87, 0x98, 0x8a, 0x10, 0x03, 0x64, 0xa3, - 0xcd, 0x6f, 0xe0, 0xba, 0x22, 0x20, 0xa6, 0xab, 0xce, 0x08, 0xc5, 0x17, 0x13, 0x59, - 0x55, 0x30, 0x65, 0xe9, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0xa4, 0x96, 0x69, 0x60, 0x21, - 0x82, 0x08, 0x46, 0x69, 0x61, 0x12, 0x94, 0x90, 0xa7, 0xd8, 0xb6, 0x5c, 0x14, 0x70, - 0xba, 0xd8, 0xdb, 0x08, 0x28, 0xef, 0x06, 0xc1, 0xcb, 0x55, 0x70, 0x0e, 0x85, 0xe2, - 0x4f, 0xde, 0xa9, 0x08, 0x52, 0x00, 0x65, 0x63, 0x52, 0x51, 0xac, 0x51, 0x87, 0x1f, - 0x88, 0xfb, 0x02, 0x57, 0x2c, 0x4f, 0x50, 0xa0, 0xf8, 0x01, 0x00, 0x06, 0x63, 0x00, - 0x63, 0x63, 0x51, 0xac, 0xcb, 0x37, 0x9c, 0x68, 0xc8, 0x7d, 0x04, 0x00, 0x03, 0x00, - 0x6a, 0x63, 0x53, 0x3c, 0x92, 0xcf, 0x4c, 0x1c, 0xac, 0x18, 0x99, 0x41, 0x99, 0xa8, - 0xec, 0x8e, 0x01, 0x00, 0x04, 0x1b, 0x31, 0xeb, 0xfb, 0xf8, 0x18, 0xa3, 0x99, 0x2b, - 0xf3, 0x68, 0xc2, 0x4e, 0x9a, 0xcc, 0x83, 0x14, 0x2b, 0x24, 0x0f, 0xec, 0x55, 0x4c, - 0xed, 0xa1, 0xd3, 0xfc, 0x04, 0x32, 0xc5, 0x72, 0x51, 0x34, 0x19, 0xaf, 0x1d, 0xe6, - 0x56, 0xfd, 0xd0, 0x39, 0x07, 0x22, 0xa7, 0xf4, 0x6a, 0x1f, 0xc0, 0x56, 0x3f, 0x0a, - 0xda, 0xb8, 0xbc, 0xbb, 0xb0, 0xd1, 0xb2, 0x29, 0xf5, 0xa5, 0xb9, 0x23, 0x03, 0x77, - 0x5a, 0x90, 0x4d, 0xec, 0x82, 0x7f, 0xd8, 0x7a, 0x18, 0x86, 0x0d, 0x6e, 0x8a, 0x4a, - 0x52, 0xb5, 0xcf, 0x44, 0xbe, 0x28, 0xa6, 0x2d, 0x41, 0x59, 0x02, 0x09, 0x3a, 0x0c, - 0x36, 0x5d, 0x29, 0x24, 0x12, 0x01, 0xb8, 0x26, 0x1a, 0x49, 0xd4, 0x91, 0xaf, 0x04, - 0x9b, 0x39, 0xe2, 0x6d, 0x13, 0x57, 0xc3, 0x06, 0x92, 0x64, 0x16, 0x77, 0x6d, 0x7d, - 0x13, 0xf8, 0x40, 0xbd, 0x82, 0xac, 0xa0, 0x1c, 0x83, 0x1c, 0x98, 0x3f, 0x19, 0x85, - 0xee, 0x0a, 0xda, 0xe8, 0xdb, 0x84, 0x47, 0xc0, 0xe5, 0x1c, 0x09, 0xdf, 0xe3, 0xde, - 0xe3, 0x88, 0x0a, 0x97, 0x13, 0xce, 0xb7, 0x45, 0xab, 0xfd, 0xd9, 0xf1, 0xc7, 0xea, - 0xd7, 0x63, 0x08, 0xcd, 0xee, 0xa2, 0x1c, 0x8b, 0x09, 0x57, 0x02, 0x7c, 0x5d, 0x00, - 0xe5, 0x0a, 0x43, 0x88, 0xc7, 0xaf, 0x2b, 0xd6, 0x43, 0xcb, 0x5e, 0xae, 0x49, 0x27, - 0x4d, 0x12, 0x30, 0xa4, 0xcd, 0x49, 0x23, 0x7a, 0xe3, 0x7b, 0x38, 0x10, 0xc2, 0xc3, - 0x95, 0x8a, 0x7d, 0xee, 0x02, 0x34, 0x30, 0x1b, 0x89, 0xa2, 0xdf, 0x2a, 0x78, 0xef, - 0x0b, 0xfb, 0x4b, 0xf6, 0xb3, 0x87, 0xdf, 0x2c, 0x6c, 0x86, 0xe6, 0x1c, 0xd1, 0x0c, - 0xa1, 0x1f, 0x81, 0x13, 0x01, 0x26, 0x07, 0xf1, 0x5b, 0x28, 0x56, 0x24, 0x0f, 0xdc, - 0x52, 0x06, 0x5a, 0x10, 0x28, 0xc8, 0xa2, 0xdd, 0xfd, 0xd1, 0x5c, 0xf5, 0x26, 0x5f, - 0x87, 0x38, 0x8a, 0xb9, 0xbf, 0x21, 0xc9, 0xa7, 0x8c, 0x59, 0x03, 0x8a, 0x98, 0xab, - 0x64, 0xfd, 0x67, 0x10, 0x77, 0xd4, 0x72, 0xc2, 0x09, 0xdd, 0x72, 0x9b, 0xd7, 0xf8, - 0x48, 0x09, 0x45, 0xfb, 0xa7, 0x52, 0x09, 0x8a, 0x94, 0xcc, 0xb2, 0x4c, 0xf3, 0xbc, - 0x09, 0x2d, 0x42, 0x36, 0x46, 0x11, 0xa2, 0x93, 0xaf, 0xf3, 0xc5, 0x79, 0x37, 0x2c, - 0x12, 0xe1, 0x50, 0x90, 0xaa, 0x27, 0x23, 0x20, 0x57, 0xf2, 0xed, 0xde, 0x4e, 0x1d, - 0xb2, 0x92, 0xf7, 0xb1, 0x86, 0x47, 0x22, 0x67, 0x35, 0x17, 0x6d, 0x90, 0xf1, 0x26, - 0x5b, 0x37, 0x98, 0xcc, 0xab, 0xac, 0x0b, 0x8d, 0x79, 0xb1, 0x77, 0x20, 0xb2, 0xba, - 0x71, 0xd7, 0x85, 0x0c, 0xc2, 0xa0, 0x87, 0x2b, 0xf0, 0xf4, 0xb8, 0x14, 0x36, 0x78, - 0x59, 0xf8, 0x99, 0x48, 0xf0, 0xa1, 0xa3, 0x83, 0x60, 0x4b, 0x9e, 0x1a, 0xa4, 0xc7, - 0xea, 0x28, 0x92, 0x05, 0x6f, 0x81, 0x28, 0x5b, 0xc2, 0x6f, 0x30, 0x08, 0x5d, 0xd0, - 0xef, 0x3b, 0x14, 0xd1, 0x7d, 0xda, 0x57, 0x30, 0x6a, 0xe4, 0xf6, 0x6c, 0x45, 0x9a, - 0xee, 0x8a, 0x4e, 0xd9, 0x02, 0xc6, 0x6e, 0x49, 0x18, 0xfa, 0xee, 0x8d, 0xc0, 0x06, - 0x72, 0x46, 0x96, 0x0d, 0xb1, 0xf8, 0xcd, 0x07, 0xbf, 0x90, 0xd7, 0x53, 0x7c, 0xc2, - 0x7b, 0xbb, 0x8c, 0x9d, 0x5b, 0x29, 0x62, 0xc4, 0x7e, 0xd1, 0x82, 0xa2, 0xfc, 0xe0, - 0x5f, 0x8e, 0x03, 0xc4, 0xe2, 0x5e, 0x49, 0x6d, 0xd5, 0x7d, 0x6a, 0xb3, 0x45, 0x8f, - 0xac, 0xbd, 0x91, 0xea, 0x22, 0x72, 0xff, 0xda, 0x47, 0xbf, 0xd0, 0x17, 0x39, 0x20, - 0xd7, 0x17, 0x51, 0x30, 0xf0, 0xe4, 0xd0, 0x93, 0x74, 0x41, 0xbc, 0xe9, 0x8c, 0xfa, - 0x5b, 0x33, 0x3b, 0x66, 0x19, 0x0f, 0x2b, 0x44, 0x71, 0x38, 0xe8, 0xc2, 0x6d, 0x84, - 0x12, 0xca, 0xc8, 0x20, 0x86, 0xd6, 0x1b, 0x5d, 0x2c, 0x8c, 0xf0, 0xbb, 0xeb, 0xac, - 0x5b, 0x89, 0xbf, 0xe8, 0x2b, 0x58, 0x91, 0x76, 0x64, 0xba, 0xb9, 0x1c, 0xe2, 0xec, - 0xe2, 0x90, 0xb2, 0x7b, 0x60, 0x52, 0xd4, 0xbf, 0x99, 0x1a, 0x33, 0xf4, 0x58, 0x1a, - 0x63, 0x36, 0x25, 0x78, 0x79, 0x58, 0x89, 0x7f, 0xca, 0x4b, 0x98, 0xb7, 0xe7, 0x27, - 0x7c, 0x5e, 0x6a, 0x1d, 0x88, 0x59, 0x48, 0xc9, 0xd4, 0x84, 0xdd, 0x0c, 0xef, 0xef, - 0x85, 0x4e, 0x81, 0x76, 0xc3, 0x97, 0xdc, 0xfa, 0x77, 0x2e, 0x71, 0x14, 0x72, 0xe7, - 0x90, 0xba, 0x8d, 0x39, 0x35, 0xd5, 0x7c, 0xa3, 0x13, 0x49, 0x37, 0x9e, 0x62, 0x83, - 0xa6, 0xaa, 0x8f, 0xc9, 0x91, 0xef, 0xc7, 0xd3, 0xb7, 0xef, 0x66, 0xb9, 0x2f, 0xe0, - 0x9d, 0x35, 0x16, 0x27, 0x0a, 0xe1, 0x9a, 0x99, 0x92, 0x16, 0xee, 0xae, 0x16, 0x21, - 0x44, 0xac, 0xea, 0x56, 0x0d, 0x17, 0x72, 0x05, 0xf2, 0x6c, 0x97, 0x03, 0xb5, 0x4e, - 0x80, 0xaf, 0x1a, 0x87, 0x94, 0xd6, 0xd3, 0xf1, 0xc5, 0xee, 0xad, 0x22, 0x0b, 0x11, - 0x9f, 0x06, 0xb2, 0x00, 0x98, 0x6c, 0x91, 0x21, 0x32, 0xcb, 0x08, 0xa9, 0x8e, 0x0f, - 0xee, 0x35, 0xe7, 0xf7, 0x7f, 0xc8, 0x52, 0x1d, 0x38, 0x77, 0x3e, 0x61, 0x4e, 0xee, - 0xb8, 0xa3, 0xea, 0xd8, 0x6a, 0x02, 0x48, 0x32, 0xe6, 0x4a, 0x4c, 0x75, 0x72, 0x0c, - 0xdc, 0xdd, 0xf9, 0xd0, 0x77, 0x09, 0xa1, 0x68, 0xd0, 0x10, 0x12, 0xc2, 0xe4, 0xf3, - 0x34, 0x30, 0xf2, 0x99, 0x70, 0xc6, 0x0b, 0xe8, 0xc5, 0xe2, 0xc8, 0xcc, 0x8a, 0x86, - 0xed, 0xcd, 0x51, 0x2d, 0xa7, 0x0d, 0xd7, 0xbb, 0x40, 0xe2, 0x7b, 0x32, 0xdf, 0x3d, - 0x77, 0x6a, 0x4a, 0x7b, 0x00, 0xe3, 0xbd, 0x8f, 0x69, 0x7f, 0x1f, 0x4e, 0x5c, 0x9f, - 0xbe, 0xbe, 0xb4, 0xe6, 0xfa, 0xd9, 0x1e, 0x09, 0x3d, 0xd5, 0xba, 0xc9, 0x92, 0xac, - 0xbc, 0xb8, 0x38, 0x3f, 0x9a, 0x8d, 0x8c, 0x04, 0xea, 0x6e, 0x2e, 0x0d, 0x03, 0xa2, - 0xdf, 0x83, 0xd4, 0xf4, 0x94, 0x59, 0x5b, 0x2c, 0xa1, 0x0b, 0x70, 0x79, 0x25, 0x9c, - 0x50, 0x7d, 0xf1, 0xec, 0xe4, 0x4d, 0xea, 0x4e, 0x9a, 0x4a, 0xe4, 0x0e, 0xc8, 0x33, - 0x1e, 0xeb, 0x03, 0x94, 0x73, 0xbd, 0x39, 0xc0, 0x9d, 0x01, 0x4b, 0x0d, 0x7b, 0xb9, - 0x01, 0x61, 0x66, 0x55, 0x4f, 0xf3, 0x8a, 0x1d, 0x77, 0xf2, 0xfd, 0xa4, 0xe7, 0xeb, - 0xa7, 0xa7, 0x8a, 0xb3, 0x1f, 0x38, 0x29, 0x42, 0x52, 0xa2, 0xb1, 0x0f, 0xd2, 0x86, - 0x5b, 0x57, 0x05, 0x05, 0x5d, 0xfe, 0x9b, 0x3e, 0x9e, 0x8f, 0x7a, 0xd5, 0xf4, 0x00, - 0x7d, 0xbe, 0x42, 0x2b, 0x3a, 0xa0, 0xbe, 0xb9, 0xd1, 0xc8, 0x9d, 0x37, 0x46, 0x08, - 0x54, 0xff, 0x6e, 0x5f, 0x03, 0xe5, 0xff, 0x3d, 0x4f, 0x18, 0x48, 0xf4, 0xcc, 0x64, - 0x21, 0x8a, 0x01, 0xf2, 0x47, 0x2b, 0xb0, 0x55, 0x80, 0x2f, 0x97, 0xf3, 0x20, 0x41, - 0xa7, 0x92, 0x79, 0x0b, 0x7c, 0x22, 0x6b, 0x04, 0xa6, 0xea, 0xe8, 0x5f, 0x1b, 0x71, - 0xca, 0x19, 0xa1, 0x71, 0x89, 0x02, 0xb4, 0xc3, 0xa3, 0xb5, 0x06, 0xd8, 0xc1, 0xb7, - 0xae, 0x72, 0x8c, 0x9b, 0x6c, 0xc3, 0x17, 0xe5, 0xe0, 0xde, 0xe5, 0x33, 0xe2, 0xe9, - 0x99, 0x73, 0xd8, 0x83, 0xa4, 0x0c, 0x6e, 0x68, 0xf2, 0x31, 0xd2, 0xcb, 0x01, 0x2f, - 0x60, 0xc1, 0x43, 0xcc, 0xab, 0xdd, 0x40, 0x45, 0x59, 0x0d, 0x9e, 0x43, 0xfb, 0xa3, - 0x6f, 0xe4, 0xcf, 0xd9, 0x7b, 0x4b, 0xdd, 0x0c, 0x4d, 0x2c, 0x93, 0xc5, 0x72, 0x8b, - 0x12, 0x87, 0xfd, 0x25, 0x41, 0x72, 0x2c, 0x69, 0x9b, 0xc1, 0xa0, 0x05, 0x83, 0xdb, - 0xc9, 0x48, 0xd5, 0x32, 0x4a, 0xc5, 0xbd, 0x7a, 0x68, 0x09, 0x64, 0x67, 0x3e, 0xdf, - 0x2c, 0x6d, 0xeb, 0xb1, 0xc8, 0xe1, 0xd0, 0x24, 0x16, 0xe6, 0xbd, 0xb2, 0xa7, 0x68, - 0x1b, 0xf4, 0x29, 0x92, 0x25, 0xc2, 0x1b, 0x5d, 0xb6, 0xa8, 0x45, 0xad, 0x10, 0x4d, - 0x34, 0x29, 0xcd, 0xc5, 0x9e, 0x3b, 0xca, 0xcf, 0x6d, 0xbc, 0x88, 0xaf, 0x0f, 0x67, - 0xdc, 0xbd, 0xf3, 0xa0, 0x72, 0x3e, 0x4d, 0x4b, 0xce, 0x32, 0x85, 0x1b, 0xb5, 0x19, - 0x7a, 0x8f, 0x43, 0x30, 0xb2, 0x72, 0x27, 0xf0, 0xb7, 0x71, 0xd0, 0xaf, 0x17, 0x5e, - 0x9c, 0x3f, 0x6e, 0x1f, 0x68, 0x46, 0x2e, 0xe7, 0xfe, 0x17, 0x97, 0xd9, 0x28, 0x40, - 0x6f, 0x92, 0x38, 0xa3, 0xf3, 0xfd, 0x83, 0x6a, 0x27, 0x56, 0xdd, 0x0a, 0x11, 0xe1, - 0xab, 0x94, 0x9d, 0x5e, 0x30, 0x89, 0x4f, 0x56, 0x29, 0x95, 0x25, 0xe6, 0x5d, 0x95, - 0x0f, 0x2e, 0xb5, 0x0b, 0x3a, 0x8e, 0xa7, 0xac, 0xad, 0x82, 0xde, 0x26, 0x2f, 0xa3, - 0x44, 0x80, 0xa2, 0x9c, 0x26, 0x19, 0xba, 0x45, 0x90, 0x3d, 0xf9, 0xa7, 0xf9, 0x86, - 0x2d, 0xc0, 0x49, 0xce, 0xf3, 0x97, 0xf7, 0x73, 0xbe, 0xed, 0xd3, 0x22, 0x6a, 0x8c, - 0xab, 0x1c, 0x86, 0x4d, 0x00, 0xb8, 0xfd, 0x37, 0xea, 0xf1, 0xd5, 0x93, 0x5a, 0x5b, - 0xbb, 0x6a, 0xd9, 0xf2, 0x7a, 0x1d, 0x8b, 0xaf, 0xc0, 0xac, 0x5f, 0x58, 0x02, 0x36, - 0x93, 0x82, 0x2a, 0x1d, 0xd4, 0xa7, 0xca, 0x1c, 0x49, 0xec, 0x81, 0x4e, 0x8f, 0xe6, - 0xe0, 0xe0, 0xde, 0x54, 0x6a, 0x4f, 0xbe, 0x7d, 0x25, 0x67, 0x0b, 0x2f, 0xc6, 0x8a, - 0x8f, 0xb2, 0xc4, 0xa6, 0x3d, 0xef, 0xec, 0x79, 0xc9, 0x0c, 0x63, 0xff, 0x96, 0xe5, - 0x40, 0xb7, 0x61, 0x5d, 0x43, 0xa6, 0x26, 0x1d, 0x57, 0x73, 0x03, 0x06, 0xb6, 0x63, - 0x2c, 0x8e, 0xe6, 0x1b, 0xaa, 0x4a, 0xb4, 0xd3, 0x08, 0x4d, 0x65, 0x9c, 0xab, 0xcf, - 0xc4, 0x06, 0x4c, 0x09, 0xd2, 0x42, 0x69, 0xb3, 0x03, 0x17, 0x10, 0xb6, 0x7d, 0x3b, - 0x0b, 0x73, 0x6f, 0xac, 0xbc, 0x18, 0x1e, 0xb1, 0xdc, 0x8c, 0x49, 0x3f, 0x10, 0xdb, - 0xe6, 0xfe, 0x45, 0xfd, 0xd4, 0xab, 0x60, 0x22, 0xfa, 0xbd, 0xd3, 0x4c, 0x09, 0xf7, - 0x51, 0x04, 0xc3, 0x85, 0xc9, 0x26, 0x83, 0x41, 0xc1, 0x6e, 0xbe, 0x80, 0xf8, 0xc8, - 0x0e, 0x8e, 0x06, 0x23, 0x06, 0x03, 0x99, 0x5a, 0xde, 0x55, 0x61, 0xfe, 0xd4, 0x5c, - 0xf8, 0xd1, 0x14, 0xd4, 0xcf, 0x02, 0x42, 0x0c, 0x4b, 0x96, 0x2d, 0xc2, 0x02, 0xf8, - 0xa5, 0x07, 0xf3, 0xd8, 0xe8, 0xa3, 0x44, 0xfb, 0xa1, 0x0a, 0x32, 0x7f, 0xf2, 0x22, - 0x54, 0xf6, 0xc3, 0xac, 0x8f, 0x3c, 0xf9, 0x70, 0x0b, 0x1f, 0xd2, 0xec, 0xbe, 0x9f, - 0x4e, 0x91, 0xe4, 0x3a, 0x65, 0x4f, 0xff, 0x02, 0x7c, 0xd9, 0x17, 0x4b, 0x63, 0x8e, - 0x6e, 0xfe, 0xc4, 0xab, 0xfb, 0xa1, 0x87, 0xf8, 0xf3, 0xdb, 0xa0, 0x45, 0x9d, 0xa6, - 0xc3, 0xf8, 0x00, 0xcb, 0x6b, 0x61, 0x33, 0xa8, 0xb4, 0xac, 0x1e, 0xf6, 0x58, 0xd1, - 0x11, 0xc0, 0x3f, 0x07, 0x22, 0x08, 0xdc, 0xc2, 0x07, 0xa2, 0x22, 0x3a, 0x70, 0x22, - 0x92, 0x43, 0x2e, 0x83, 0x06, 0xfc, 0x03, 0x04, 0x63, 0xe7, 0x54, 0xff, 0x0f, 0x15, - 0x3d, 0x97, 0xbc, 0x9c, 0xe9, 0x6d, 0xff, 0x4b, 0xed, 0x2f, 0x1e, 0xa5, 0xb8, 0xea, - 0x87, 0x6d, 0x2e, 0xe4, 0xe4, 0xf6, 0xe4, 0x9a, 0x4a, 0x85, 0xa9, 0xcf, 0x4a, 0x33, - 0xdc, 0xd9, 0x36, 0x60, 0xa4, 0x25, 0x43, 0xe5, 0x34, 0x22, 0x39, 0x0d, 0x66, 0x5b, - 0xdd, 0x30, 0x24, 0x78, 0xb3, 0x3c, 0x8d, 0x57, 0x47, 0x92, 0x41, 0x4c, 0x5f, 0xe5, - 0xb7, 0x4f, 0xe1, 0xd1, 0x69, 0x52, 0x5c, 0x99, 0x30, 0x1a, 0x3a, 0x68, 0xa0, 0xc8, - 0x5f, 0x02, 0x0f, 0xd5, 0x8f, 0x6d, 0x9f, 0x3a, 0xcb, 0x13, 0x9c, 0x96, 0x65, 0x38, - 0x56, 0xa3, 0x2e, 0x21, 0x02, 0x7a, 0xa2, 0xba, 0x18, 0x60, 0x10, 0xd5, 0x3c, 0xdd, - 0x4c, 0x41, 0x50, 0xcb, 0x2b, 0xb2, 0x42, 0x44, 0x65, 0x42, 0xb0, 0x17, 0x84, 0x40, - 0x1f, 0xa2, 0xcb, 0xf1, 0x22, 0xc9, 0xf1, 0x1d, 0x8c, 0x81, 0x36, 0x98, 0x7b, 0x67, - 0x86, 0x29, 0x93, 0x84, 0x58, 0x5f, 0x9c, 0xa2, 0x93, 0x53, 0x7b, 0x4b, 0xe5, 0x72, - 0x6f, 0x94, 0xd4, 0x77, 0x60, 0x5a, 0x8a, 0x6c, 0x53, 0x06, 0x02, 0xbb, 0x46, 0xc4, - 0xde, 0x20, 0x7f, 0xc5, 0x9e, 0x91, 0xe4, 0xa9, 0x0a, 0x91, 0x11, 0x77, 0x74, 0x69, - 0xf1, 0xe2, 0x87, 0x82, 0x76, 0x7d, 0x9d, 0xe5, 0x7d, 0xea, 0xde, 0xad, 0xcb, 0x4a, - 0xf5, 0x19, 0x3e, 0x09, 0xc9, 0xbb, 0x74, 0x73, 0x77, 0x3a, 0x8c, 0xa5, 0x6d, 0x76, - 0x51, 0x1d, 0x65, 0x99, 0x20, 0xdb, 0x99, 0x64, 0xd3, 0x2b, 0xad, 0xb6, 0x1f, 0x4c, - 0xf6, 0xb0, 0x22, 0xd7, 0xc1, 0x53, 0x93, 0x18, 0x49, 0x64, 0x3e, 0x8b, 0x99, 0xea, - 0xe0, 0x28, 0x4f, 0x8b, 0x01, 0x15, 0xb4, 0x23, 0x7a, 0x7c, 0x5d, 0x81, 0x97, 0x0f, - 0xe8, 0x7c, 0x6f, 0x84, 0xb6, 0x68, 0x6c, 0x46, 0x25, 0xdb, 0xdd, 0x9d, 0x79, 0xd2, - 0xc5, 0x55, 0xdd, 0x4f, 0xce, 0xed, 0x2c, 0x5e, 0x5e, 0x89, 0x6f, 0x63, 0x1a, 0xe4, - 0x59, 0x7e, 0x9c, 0xc0, 0xbe, 0xe7, 0xb3, 0x02, 0x5f, 0x95, 0x56, 0x10, 0x6a, 0x84, - 0x3a, 0x18, 0x22, 0x7f, 0x5a, 0xb9, 0x61, 0x7d, 0x7b, 0xcb, 0x1a, 0xf5, 0x28, 0xfa, - 0xa7, 0xa0, 0x52, 0xea, 0x4f, 0x52, 0xca, 0x59, 0x45, 0x57, 0xfd, 0xad, 0x33, 0x05, - 0x2b, 0xc8, 0x2b, 0x39, 0xc6, 0xa6, 0x09, 0xa0, 0x70, 0x75, 0x3d, 0x78, 0x8b, 0x2c, - 0x4a, 0x2c, 0xae, 0xbb, 0xe7, 0x9f, 0xf0, 0x12, 0x07, 0x1c, 0x07, 0x08, 0x10, 0x94, - 0xad, 0x60, 0x59, 0xc2, 0x8f, 0x48, 0xe5, 0x56, 0xc4, 0xe8, 0xd8, 0xc5, 0x37, 0x8b, - 0xc2, 0x93, 0x07, 0x6b, 0xb4, 0x97, 0x07, 0x5f, 0x9c, 0xa0, 0xba, 0x13, 0x11, 0x55, - 0x0f, 0xa2, 0x17, 0x3d, 0x0e, 0xb1, 0xf0, 0xbd, 0xdd, 0xf3, 0xb3, 0xd5, 0xc2, 0x43, - 0xff, 0xea, 0xbe, 0xe8, 0x23, 0xcd, 0x63, 0xb4, 0x39, 0x39, 0xce, 0x95, 0x46, 0xed, - 0x4c, 0x41, 0xe6, 0x0c, 0xcc, 0x7e, 0x1c, 0x54, 0x3c, 0xb3, 0xe2, 0xd3, 0x50, 0xe2, - 0xe2, 0xe9, 0x74, 0x21, 0x5c, 0xf7, 0xaa, 0x96, 0x9b, 0x66, 0x81, 0x14, 0xac, 0xdb, - 0x29, 0xf4, 0xcd, 0xcf, 0xdc, 0xec, 0x2a, 0x8c, 0xe4, 0xf5, 0x95, 0xf4, 0xff, 0x5f, - 0x70, 0x7e, 0x7f, 0xa4, 0xde, 0xe8, 0xbf, 0x8f, 0x39, 0x52, 0xae, 0x32, 0xe7, 0x7f, - 0x34, 0xf8, 0xb3, 0xab, 0xaa, 0xe9, 0x69, 0x28, 0xba, 0x4a, 0x6c, 0x0f, 0xbf, 0x5b, - 0x29, 0x19, 0x2d, 0xae, 0x80, 0x0d, 0xfa, 0x79, 0x57, 0x0c, 0xaf, 0x0b, 0xb8, 0x33, - 0xbd, 0x37, 0xa3, 0xd4, 0xbe, 0xaf, 0x09, 0x1f, 0x6b, 0x3e, 0x55, 0xaa, 0xe5, 0x25, - 0xf4, 0x13, 0xac, 0x80, 0x4c, 0x34, 0x7d, 0x54, 0x1d, 0x2c, 0x09, 0xec, 0x6e, 0x54, - 0x03, 0x5d, 0xf1, 0xd8, 0x30, 0x28, 0x4d, 0x9b, 0x46, 0xff, 0xd2, 0xb2, 0xeb, 0x04, - 0x0b, 0x61, 0x77, 0xd0, 0xa0, 0x9c, 0x16, 0x60, 0x34, 0xa9, 0x57, 0xb1, 0x8f, 0xf6, - 0x2e, 0x43, 0x4a, 0x3e, 0xc7, 0x32, 0x62, 0xe4, 0xb2, 0x3f, 0xec, 0x9d, 0x29, 0x0a, - 0x81, 0xc5, 0xb1, 0xf7, 0x3c, 0xb4, 0xcd, 0x1c, 0x47, 0x2b, 0x86, 0xe5, 0x34, 0xab, - 0x9e, 0x65, 0x53, 0x29, 0x5d, 0xb0, 0xcf, 0x34, 0xe1, 0x39, 0x2a, 0xad, 0x5a, 0xbc, - 0xf3, 0x98, 0x64, 0x16, 0xa7, 0x0a, 0x9d, 0xbe, 0x59, 0xbb, 0x95, 0x8e, 0xbc, 0x71, - 0x1c, 0x3a, 0xe0, 0x8c, 0xaf, 0x52, 0xec, 0xa9, 0xcb, 0x54, 0xc4, 0x58, 0xbe, 0x7f, - 0x5e, 0x62, 0x14, 0xec, 0xa0, 0xf0, 0xa3, 0x81, 0x52, 0x62, 0x20, 0x01, 0x32, 0xe6, - 0x14, 0x54, 0x37, 0xec, 0xd2, 0x1f, 0xc8, 0x03, 0x6c, 0xb0, 0x0a, 0x49, 0x13, 0x84, - 0xc3, 0x41, 0xd8, 0x72, 0xdc, 0xda, 0x31, 0xb1, 0x42, 0x96, 0x73, 0xd9, 0xc4, 0xf5, - 0x7b, 0x81, 0xa0, 0x23, 0x6d, 0xa5, 0xec, 0x55, 0x02, 0xee, 0x29, 0x63, 0x15, 0x0a, - 0x00, 0x26, 0xbd, 0x63, 0xef, 0x67, 0x9e, 0x8c, 0x25, 0xb8, 0xec, 0xee, 0x06, 0x56, - 0x4a, 0xf3, 0xb0, 0x2d, 0xea, 0xb1, 0x06, 0x97, 0xa2, 0x4d, 0xe6, 0x7d, 0x4f, 0x65, - 0x04, 0xae, 0x27, 0x37, 0xb8, 0xe1, 0x73, 0x25, 0xc2, 0xff, 0x15, 0x0c, 0x62, 0xe3, - 0x79, 0x83, 0x44, 0xa1, 0xad, 0x3c, 0xbb, 0x75, 0xb7, 0xf2, 0xa1, 0x57, 0x38, 0xf6, - 0x01, 0xcf, 0x00, 0xf7, 0xe8, 0xbc, 0x08, 0xb6, 0x89, 0x56, 0x7e, 0x4c, 0x7c, 0x01, - 0x05, 0x8b, 0xee, 0xc2, 0x90, 0x3c, 0x5c, 0xa6, 0xb4, 0xc4, 0xa5, 0x71, 0xf4, 0x60, - 0xd6, 0x05, 0x87, 0x36, 0x29, 0x96, 0xc6, 0xe1, 0x25, 0x54, 0xe8, 0xe3, 0x4e, 0x68, - 0x3a, 0x27, 0xf8, 0xa5, 0xff, 0x97, 0x1d, 0x5a, 0x0d, 0xc2, 0xf3, 0xef, 0xd3, 0x88, - 0x99, 0x87, 0xc1, 0xcc, 0x39, 0xce, 0x5d, 0x4b, 0x6b, 0x54, 0x4c, 0xe0, 0x4c, 0x71, - 0xee, 0x4b, 0xfa, 0xe5, 0x04, 0x0d, 0x61, 0xf0, 0x57, 0xe4, 0xf7, 0x70, 0x17, 0x28, - 0xf1, 0x20, 0x04, 0xa7, 0xf7, 0xed, 0xeb, 0x3a, 0xb2, 0x26, 0x09, 0xed, 0x33, 0xb0, - 0xab, 0x5d, 0x69, 0xb1, 0x2d, 0x45, 0x76, 0x57, 0x77, 0x14, 0xdf, 0xc6, 0xdd, 0xa7, - 0x1f, 0xf6, 0x01, 0x7b, 0x55, 0xb3, 0x35, 0x4d, 0x11, 0xe9, 0x21, 0x67, 0x92, 0xe5, - 0x60, 0x9f, 0xc0, 0x67, 0x88, 0xec, 0x66, 0x8e, 0xef, 0x64, 0x5e, 0x63, 0xb3, 0x7e, - 0x2d, 0x0c, 0xd2, 0x63, 0x04, 0x08, 0x00, 0xbc, 0x8a, 0xa2, 0x80, 0x15, 0x6a, 0x79, - 0x4f, 0x62, 0xa5, 0xf6, 0x93, 0xeb, 0xd9, 0x07, 0x4b, 0x5d, 0x35, 0x4a, 0x71, 0xc8, - 0xe3, 0x36, 0xde, 0x04, 0x08, 0xac, 0x70, 0x80, 0xa2, 0xae, 0xee, 0x36, 0x6c, 0x58, - 0x14, 0x6f, 0x32, 0xe3, 0x49, 0xa9, 0xbc, 0x65, 0x7e, 0xc9, 0xe5, 0x7a, 0x89, 0xa0, - 0x4c, 0xce, 0xee, 0x21, 0xbd, 0xf3, 0x79, 0x3e, 0x49, 0xa5, 0xcf, 0x71, 0x3a, 0x42, - 0xd0, 0x29, 0xdd, 0xdb, 0x3d, 0xb4, 0x95, 0x09, 0x2c, 0x37, 0xce, 0x81, 0x4b, 0xe7, - 0x3e, 0xf4, 0xec, 0x8d, 0x70, 0xe8, 0x69, 0xbd, 0x2b, 0x78, 0x8f, 0x15, 0x00, 0xfe, - 0x5e, 0xe5, 0x6c, 0x0c, 0xe7, 0x04, 0xeb, 0xa2, 0xc1, 0xa3, 0xa3, 0x29, 0x0d, 0xe6, - 0xec, 0x68, 0xcc, 0xb5, 0xef, 0x7c, 0xd0, 0x21, 0x2a, 0x3f, 0x09, 0x96, 0x92, 0xcf, - 0x00, 0x04, 0x8d, 0xe5, 0x01, 0x26, 0x19, 0xe7, 0x41, 0x69, 0x2b, 0xfc, 0x74, 0x05, - 0xba, 0x3e, 0x87, 0x5e, 0x98, 0xb7, 0xca, 0x31, 0xe9, 0x65, 0xa1, 0x6f, 0xdd, 0xb5, - 0xb0, 0xb7, 0x72, 0xa3, 0xf5, 0xd0, 0x50, 0xd8, 0xad, 0x7f, 0x60, 0x7f, 0x55, 0xc0, - 0xdc, 0x52, 0xb4, 0x8f, 0xb0, 0x2a, 0x8b, 0x1d, 0xef, 0xc6, 0xc3, 0x10, 0xb2, 0x47, - 0x55, 0x59, 0xb4, 0x7e, 0x84, 0x4e, 0xd3, 0x77, 0x60, 0xd7, 0xd1, 0x6f, 0x27, 0xcb, - 0x48, 0xbf, 0x36, 0x16, 0xc4, 0x6f, 0xb0, 0xcf, 0x3c, 0x8c, 0x28, 0xb9, 0x39, 0x27, - 0x80, 0x0a, 0x29, 0x16, 0xa4, 0x07, 0xa6, 0x0d, 0x68, 0x99, 0x7b, 0x10, 0x50, 0x51, - 0x32, 0xad, 0x33, 0xf9, 0xce, 0x26, 0xb4, 0xac, 0xba, 0x27, 0xa2, 0xa0, 0xc2, 0x18, - 0xdb, 0x15, 0xa5, 0xd7, 0xaa, 0xed, 0x4f, 0x6a, 0x72, 0x00, 0x36, 0x72, 0xca, 0x70, - 0x49, 0x8b, 0x05, 0x49, 0x4a, 0x93, 0x34, 0x1f, 0xcf, 0x96, 0xc0, 0x99, 0x4e, 0x42, - 0x7b, 0xeb, 0xd3, 0x56, 0xe4, 0x17, 0x6d, 0xec, 0x83, 0xe6, 0xfe, 0x80, 0x02, 0x9c, - 0xfc, 0x47, 0x8b, 0x88, 0xb6, 0xfd, 0x38, 0xc0, 0x39, 0xe0, 0x8b, 0x6f, 0xd9, 0x5d, - 0xab, 0xcf, 0xb2, 0x5f, 0x23, 0x8b, 0x26, 0x62, 0x06, 0xb0, 0xa2, 0xf9, 0xa2, 0xee, - 0xa1, 0xc0, 0x83, 0xfa, 0xc8, 0x08, 0xaa, 0xfa, 0x03, 0x65, 0x66, 0xcc, 0xd2, 0x02, - 0xbc, 0xfa, 0x41, 0x4e, 0x71, 0xc8, 0xb4, 0x89, 0x33, 0xc8, 0xed, 0x45, 0x28, 0x7e, - 0x1b, 0x43, 0x9b, 0x61, 0x06, 0xa5, 0x50, 0x94, 0x73, 0xf5, 0x7b, 0x87, 0x88, 0xaf, - 0x52, 0x7c, 0xf9, 0xa7, 0xab, 0xa5, 0x93, 0xdc, 0x9f, 0x5e, 0x5a, 0xca, 0x1a, 0x64, - 0x8e, 0xe4, 0x88, 0xf3, 0x6d, 0xeb, 0x4a, 0x3f, 0xdb, 0x0f, 0xf6, 0xf5, 0xa3, 0x04, - 0x4a, 0x63, 0xe1, 0x7f, 0x70, 0xa4, 0x30, 0x38, 0x24, 0x60, 0x3a, 0xb5, 0x0e, 0x9b, - 0xf7, 0x5b, 0xae, 0xb5, 0x7b, 0xfd, 0xc8, 0x9b, 0xfd, 0xbc, 0x27, 0x27, 0x9d, 0x10, - 0x73, 0xbf, 0x7f, 0x95, 0x05, 0xfb, 0x31, 0x68, 0xd2, 0x06, 0xe2, 0xbf, 0x41, 0x02, - 0xbf, 0x15, 0x9c, 0xff, 0x61, 0xe6, 0xd6, 0x6c, 0x80, 0x37, 0x50, 0xda, 0x25, 0x4c, - 0xd6, 0xb8, 0x1a, 0xed, 0x42, 0x09, 0x97, 0x94, 0xb8, 0x4e, 0xce, 0x90, 0x42, 0x18, - 0xe6, 0xf6, 0x6e, 0xc6, 0x34, 0xe9, 0x2e, 0xef, 0xf4, 0x5f, 0x52, 0xe0, 0x4b, 0x4b, - 0x79, 0x5a, 0x15, 0x25, 0xaa, 0xf9, 0xc5, 0x1d, 0x62, 0x60, 0xfb, 0xd6, 0x4e, 0x8d, - 0x8a, 0xc2, 0x66, 0xdc, 0x6e, 0x7d, 0xf6, 0x15, 0x3a, 0xd9, 0x73, 0x55, 0x83, 0x79, - 0x28, 0x40, 0x4c, 0xd5, 0x81, 0xbc, 0x9c, 0xf9, 0xdc, 0xd6, 0x67, 0x47, 0xdc, 0x97, - 0x0a, 0x9f, 0x00, 0xde, 0xb4, 0x4b, 0xd6, 0x34, 0xab, 0x04, 0x2e, 0x01, 0x04, 0xc1, - 0xce, 0x74, 0x7f, 0x53, 0x75, 0x1b, 0xc3, 0x3e, 0x38, 0x4c, 0x6b, 0x55, 0x76, 0x39, - 0x9e, 0x16, 0xf8, 0xf0, 0xcb, 0x08, 0xde, 0x35, 0x08, 0x37, 0x33, 0x95, 0x45, 0x87, - 0xc1, 0xc2, 0x4d, 0xf2, 0xae, 0x66, 0x30, 0xff, 0xfe, 0x99, 0x62, 0x15, 0xef, 0xe4, - 0xd2, 0x62, 0x6d, 0xeb, 0x20, 0x56, 0x6a, 0x8f, 0x5e, 0xad, 0x2f, 0x04, 0xdb, 0x5d, - 0x08, 0x77, 0x9c, 0x9c, 0x65, 0x9e, 0xa3, 0x43, 0xcd, 0x78, 0x46, 0x34, 0xc9, 0x9d, - 0x8c, 0x8b, 0xad, 0xa9, 0x3b, 0xe8, 0xe6, 0xda, 0x84, 0x15, 0x94, 0xba, 0xcf, 0x7c, - 0xb3, 0xe6, 0x92, 0xc7, 0x4b, 0x5f, 0xfe, 0x95, 0x78, 0x73, 0x11, 0x3a, 0x1a, 0xb0, - 0x64, 0x02, 0x6f, 0x6d, 0xee, 0x8b, 0x48, 0xa3, 0x84, 0xa1, 0x33, 0x83, 0x18, 0x36, - 0x07, 0x86, 0x50, 0x27, 0x84, 0xd1, 0x7d, 0x40, 0x0c, 0xe3, 0xd7, 0x21, 0x78, 0x7e, - 0xdc, 0x4c, 0x6b, 0x39, 0x35, 0x66, 0x25, 0x10, 0x77, 0x10, 0x00, 0x68, 0x0d, 0x78, - 0xbb, 0x49, 0xc5, 0x66, 0xef, 0x27, 0xdf, 0x61, 0xc9, 0xfe, 0xb9, 0x2c, 0x08, 0x97, - 0x59, 0x44, 0x87, 0x27, 0xa9, 0x34, 0xe3, 0x57, 0x95, 0x3d, 0xe1, 0xe9, 0xe9, 0x0f, - 0xd8, 0xdf, 0xfe, 0x40, 0xb8, 0x73, 0xbc, 0xd5, 0xb9, 0x82, 0x08, 0xdf, 0x4b, 0x2c, - 0xa2, 0x89, 0x7a, 0xf9, 0x0d, 0x8c, 0x8a, 0x23, 0x62, 0x30, 0x02, 0xa9, 0xd8, 0xbc, - 0x02, 0xe8, 0x06, 0x25, 0x4f, 0x41, 0x0e, 0x3b, 0x02, 0x40, 0x9c, 0xbe, 0xbf, 0xce, - 0x8a, 0xcf, 0x65, 0xcf, 0x39, 0x42, 0x6b, 0x64, 0xa6, 0xba, 0x93, 0x74, 0xa1, 0x3d, - 0x72, 0x59, 0x62, 0x3f, 0x65, 0xe9, 0x3e, 0x10, 0xbf, 0x1f, 0x16, 0xba, 0x7a, 0xe0, - 0x7d, 0xa9, 0x20, 0x58, 0x1c, 0x70, 0x40, 0x9e, 0xdc, 0x7b, 0x9e, 0x21, 0x4e, 0x95, - 0x91, 0x92, 0x82, 0x4c, 0x1d, 0xa6, 0x5d, 0x33, 0x7b, 0x73, 0x75, 0xf5, 0x03, 0x2f, - 0xea, 0xd3, 0xb4, 0xf3, 0x28, 0x48, 0x11, 0x95, 0x0c, 0x7a, 0x90, 0xae, 0xc9, 0x75, - 0xd4, 0xe3, 0x62, 0x9f, 0x52, 0xd1, 0x9a, 0x16, 0x4e, 0x51, 0x16, 0xef, 0x3a, 0xd0, - 0x22, 0x44, 0x2d, 0x1e, 0xec, 0x76, 0xb8, 0x88, 0x73, 0x8b, 0x53, 0xe5, 0x05, 0x58, - 0xa7, 0x0f, 0x20, 0xc8, 0xac, 0xb5, 0x8d, 0xee, 0x63, 0x27, 0x15, 0xe4, 0x78, 0xe2, - 0xbc, 0x21, 0xbc, 0xfb, 0xe3, 0x15, 0x59, 0x96, 0xca, 0xe7, 0xbd, 0x97, 0xf0, 0x2b, - 0x51, 0x6d, 0x32, 0x00, 0xfb, 0x3c, 0x17, 0x39, 0x7c, 0xc1, 0x2b, 0xb7, 0xa1, 0x9f, - 0xd4, 0x36, 0xe6, 0x7a, 0xbc, 0xe6, 0x6d, 0x30, 0xfe, 0xc0, 0x47, 0xfb, 0x27, 0x70, - 0x82, 0x0e, 0x47, 0x6f, 0x3e, 0x32, 0xbc, 0x48, 0x3b, 0xf5, 0x31, 0x64, 0xae, 0x49, - 0x70, 0xf1, 0x1b, 0x9c, 0xae, 0xe4, 0xed, 0x6c, 0xb8, 0xd2, 0xd7, 0x0f, 0x69, 0x13, - 0xd8, 0xe0, 0x2a, 0xf8, 0xfb, 0xb1, 0xe4, 0x09, 0xb4, 0xef, 0x08, 0x04, 0x48, 0xe5, - 0x3b, 0xe6, 0xe5, 0xe6, 0x05, 0x75, 0xdf, 0xde, 0x94, 0x28, 0xb0, 0x06, 0x96, 0x61, - 0x1a, 0x2f, 0x72, 0x33, 0x2a, 0xe2, 0x90, 0x23, 0xdd, 0x88, 0xae, 0x77, 0xf1, 0x5b, - 0x8a, 0xe2, 0xc2, 0x4b, 0x86, 0xcf, 0x3d, 0x57, 0x43, 0x9c, 0xaf, 0x17, 0xf2, 0x8e, - 0xda, 0x94, 0x93, 0x2e, 0xef, 0x28, 0x53, 0x4e, 0x16, 0x49, 0xce, 0xf8, 0x85, 0x40, - 0xfc, 0xb1, 0xa6, 0x3e, 0x11, 0x5c, 0x58, 0x22, 0xaf, 0xa4, 0x40, 0xc8, 0xd7, 0x9d, - 0x66, 0xf9, 0xbb, 0x1f, 0x48, 0xe1, 0x14, 0x0b, 0x06, 0xec, 0x87, 0x18, 0x3c, 0xbc, - 0x6e, 0x95, 0xf6, 0xcd, 0x5f, 0x7e, 0xbc, 0xad, 0xb8, 0x97, 0xc7, 0x7b, 0x4a, 0xfb, - 0x36, 0x7b, 0x95, 0x2d, 0xbb, 0x71, 0x7f, 0x75, 0x18, 0x90, 0xc8, 0xac, 0x30, 0x36, - 0xda, 0xcd, 0xbd, 0x78, 0x4a, 0x0d, 0x83, 0xab, 0xb8, 0x44, 0x6b, 0x3f, 0x93, 0x96, - 0x33, 0x5f, 0xbf, 0x0b, 0x44, 0xed, 0xc9, 0x9e, 0x1c, 0x67, 0xc5, 0xc3, 0x81, 0x6a, - 0xce, 0x76, 0x29, 0xe6, 0xe7, 0xb0, 0x28, 0xd6, 0xc8, 0x62, 0x74, 0x9e, 0x86, 0xeb, - 0xc5, 0x11, 0x7e, 0x21, 0xf4, 0x23, 0xe1, 0x8d, 0x09, 0x76, 0xa1, 0xf5, 0x1d, 0x45, - 0x47, 0x6d, 0xa5, 0x60, 0xff, 0x23, 0x15, 0x42, 0xbb, 0x21, 0xc3, 0xde, 0xd2, 0xf2, - 0x3b, 0x2a, 0x50, 0xe0, 0xb8, 0x22, 0x56, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x1d, 0x11, - 0x65, 0xd7, 0x60, 0x70, 0x2e, 0xf1, 0x03, 0xd2, 0x23, 0x67, 0x26, 0x90, 0x23, 0x59, - 0xbe, 0x8d, 0x79, 0x73, 0x52, 0xf9, 0x6d, 0x22, 0x46, 0xa2, 0xee, 0x0a, 0xf8, 0x0a, - 0x2a, 0x2d, 0x89, 0xa5, 0x85, 0x30, 0xd6, 0xe3, 0x6b, 0xd3, 0x3a, 0x00, 0xc1, 0xb8, - 0x93, 0xd6, 0xff, 0x8f, 0x90, 0x01, 0x44, 0x15, 0x1b, 0xee, 0x34, 0xc7, 0x94, 0x4b, - 0x99, 0xed, 0x6e, 0x79, 0x45, 0xe7, 0xf0, 0xde, 0x87, 0x26, 0x3d, 0x0b, 0xba, 0x6e, - 0x55, 0xac, 0x96, 0xa9, 0x6d, 0x49, 0x95, 0x12, 0x9b, 0xcf, 0xa9, 0xd9, 0xda, 0x6d, - 0xe6, 0xdd, 0x48, 0x26, 0x39, 0x15, 0x3a, 0x81, 0x69, 0xa4, 0xab, 0x46, 0x4e, 0x39, - 0x0b, 0x7f, 0x0a, 0x96, 0xd1, 0x4a, 0x73, 0xf7, 0x69, 0x7f, 0x7e, 0xce, 0x3c, 0xd7, - 0x81, 0xd3, 0x5d, 0xd2, 0x2a, 0xdd, 0xdd, 0x2f, 0x5d, 0x34, 0x52, 0x04, 0xe4, 0xbb, - 0x55, 0x7e, 0x88, 0x45, 0x3f, 0x18, 0x8c, 0xac, 0xbe, 0x92, 0x29, 0x87, 0xbb, 0xe3, - 0xb3, 0xd9, 0x76, 0x82, 0x61, 0x35, 0xc1, 0x03, 0xb6, 0xca, 0x18, 0x2b, 0x63, 0xe9, - 0xe6, 0x7f, 0x83, 0xdc, 0x9f, 0x48, 0x93, 0x33, 0xd5, 0x2a, 0x7f, 0xd7, 0x68, 0x8a, - 0x58, 0xd6, 0x62, 0x0b, 0x67, 0xe9, 0xc7, 0xb0, 0x91, 0x6f, 0xef, 0x90, 0xf1, 0x5d, - 0x8e, 0x4e, 0xb8, 0x0c, 0xf5, 0x99, 0x68, 0x2f, 0x95, 0x4f, 0xf4, 0xe0, 0xb3, 0x71, - 0x83, 0x13, 0x0c, 0xa2, 0xee, 0xd0, 0x91, 0x3f, 0x46, 0xa4, 0xdb, 0x99, 0x2a, 0x1c, - 0x3b, 0xf3, 0x19, 0xdc, 0x86, 0x75, 0x94, 0x01, 0x01, 0x53, 0x7c, 0xff, 0xc4, 0xa8, - 0x2d, 0x59, 0x9b, 0xbe, 0xa0, 0xd4, 0x7e, 0x7a, 0xbf, 0xa9, 0x92, 0xb4, 0x99, 0x8c, - 0xb2, 0x50, 0x09, 0x55, 0xe6, 0x1c, 0x0d, 0x46, 0xb3, 0x21, 0x17, 0xfb, 0xb9, 0x7f, - 0x7a, 0x76, 0x32, 0xd8, 0x72, 0x4b, 0x5d, 0xff, 0x67, 0xf7, 0x5e, 0x2d, 0x31, 0x74, - 0x06, 0xa0, 0xce, 0xc2, 0x89, 0xed, 0x08, 0x3b, 0x7c, 0x58, 0x19, 0x81, 0x8c, 0x50, - 0x47, 0x93, 0xde, 0x53, 0xb6, 0xbf, 0xdb, 0x51, 0x0e, 0x7c, 0xa7, 0x29, 0xba, 0x74, - 0x3d, 0x10, 0xb3, 0xe9, 0x95, 0x7e, 0xfa, 0x84, 0x20, 0x13, 0x39, 0x47, 0x7c, 0xf3, - 0x5f, 0xbb, 0x6a, 0x27, 0x9b, 0xad, 0x9e, 0x8f, 0x42, 0xb9, 0xb3, 0xfd, 0x6f, 0x3b, - 0xc7, 0x70, 0x67, 0x1d, 0x9c, 0x19, 0x12, 0x2f, 0xa3, 0x25, 0x6d, 0x09, 0x07, 0x36, - 0xb6, 0xd6, 0x4e, 0xb9, 0xcc, 0x03, 0x20, 0xf1, 0xea, 0xaa, 0x27, 0x1b, 0xa2, 0x86, - 0x1e, 0xc4, 0xb3, 0xf3, 0xf6, 0xc8, 0x40, 0xb6, 0x19, 0xff, 0x38, 0x8d, 0x81, 0xfc, - 0x40, 0x44, 0xa0, 0xd5, 0x31, 0xa4, 0xbb, 0x44, 0xc9, 0x3d, 0x09, 0x9d, 0xb0, 0x8a, - 0x9b, 0xc3, 0x46, 0xa0, 0xb6, 0x2f, 0x16, 0x8f, 0xfb, 0xdb, 0x73, 0x93, 0x66, 0xbb, - 0x53, 0x5d, 0xde, 0x66, 0xc2, 0xc1, 0x28, 0x7b, 0x3b, 0x27, 0x85, 0xae, 0xd6, 0x4c, - 0xc4, 0x0c, 0xbc, 0x7d, 0x33, 0xcb, 0xa4, 0xa9, 0xf3, 0xfc, 0xf5, 0xf8, 0x31, 0x36, - 0xa4, 0x39, 0x2d, 0x21, 0xa7, 0xf9, 0xeb, 0x1c, 0xe4, 0xb6, 0xe1, 0x7e, 0x6f, 0x4a, - 0x85, 0xa5, 0x79, 0x66, 0x9e, 0xfd, 0x0f, 0xb0, 0x98, 0x78, 0xe0, 0x88, 0xe3, 0x22, - 0xe9, 0x06, 0xe8, 0x0d, 0x27, 0xf8, 0xd0, 0xca, 0x7e, 0x79, 0x15, 0xab, 0x40, 0x96, - 0x59, 0xa6, 0xd8, 0x0f, 0xde, 0xd1, 0x0a, 0xff, 0x9f, 0xb7, 0x73, 0x74, 0x9d, 0x79, - 0x28, 0x57, 0xf6, 0x8c, 0x7e, 0x8c, 0xf5, 0x18, 0x26, 0x0a, 0x61, 0x08, 0x6d, 0xe3, - 0x2f, 0xff, 0x82, 0x39, 0xf4, 0x53, 0x61, 0x7a, 0x19, 0xf6, 0xfe, 0xc2, 0x20, 0x67, - 0x60, 0x65, 0xeb, 0xe2, 0x75, 0x7e, 0xfc, 0xac, 0xcb, 0x77, 0xfc, 0x61, 0xe5, 0x9b, - 0x97, 0x63, 0x7e, 0x92, 0x0d, 0xee, 0x5e, 0x7e, 0x7a, 0x12, 0xe9, 0xd6, 0xd2, 0x28, - 0xb2, 0x6b, 0x2f, 0xa8, 0x36, 0xf4, 0x72, 0x83, 0x69, 0xad, 0xcd, 0xfc, 0xd0, 0x04, - 0xdc, 0xf1, 0x9e, 0x27, 0xc0, 0xc0, 0x84, 0x44, 0xd2, 0x9a, 0x12, 0x2b, 0x23, 0x09, - 0xf7, 0x16, 0x3c, 0x99, 0x0e, 0xb9, 0x26, 0x1f, 0xd4, 0x15, 0xc0, 0x45, 0x4a, 0x56, - 0xaa, 0x3e, 0xaf, 0x9c, 0x1f, 0x9b, 0xff, 0xf6, 0x04, 0x77, 0x6a, 0x4d, 0x25, 0xe7, - 0xd3, 0xcd, 0xc5, 0xc5, 0xf1, 0x9c, 0xd2, 0xa8, 0x79, 0x4a, 0x4f, 0x57, 0x16, 0x7f, - 0xbc, 0x7e, 0xaa, 0x06, 0x16, 0x4d, 0x51, 0xc4, 0x53, 0x06, 0x14, 0xbc, 0xf5, 0x20, - 0xb2, 0x63, 0x82, 0x0a, 0xa1, 0x7b, 0x20, 0xb4, 0x8c, 0xbf, 0x59, 0xd8, 0xe3, 0x09, - 0x32, 0x2e, 0xbe, 0x56, 0x6f, 0xbe, 0x46, 0xe0, 0xaa, 0x29, 0x76, 0x6a, 0xdf, 0xdf, - 0x01, 0x7a, 0x71, 0x05, 0x10, 0x3c, 0x7f, 0xca, 0xb7, 0xb0, 0x76, 0x48, 0xc7, 0xc1, - 0x16, 0x04, 0x84, 0xf7, 0x7a, 0x6c, 0x70, 0xa5, 0x38, 0x1b, 0x82, 0x56, 0x40, 0xa1, - 0xbe, 0x48, 0xe4, 0x15, 0xa1, 0xe6, 0xa2, 0x7d, 0x78, 0x02, 0x2a, 0x8a, 0x2f, 0xf0, - 0x70, 0xab, 0xf1, 0x23, 0x94, 0xe3, 0xae, 0x5a, 0x8c, 0x23, 0xe3, 0x73, 0x3e, 0xa4, - 0x7a, 0x44, 0xcb, 0x2c, 0x96, 0x8b, 0xca, 0x24, 0x98, 0x37, 0xde, 0x1d, 0x39, 0xa5, - 0xa1, 0xdc, 0xae, 0x71, 0x0c, 0xe0, 0x43, 0x01, 0x69, 0xbd, 0x6e, 0x9f, 0x64, 0xab, - 0xf1, 0xe6, 0x4e, 0xc4, 0x9e, 0xd0, 0x80, 0x4e, 0xb6, 0x47, 0x74, 0x3a, 0xce, 0xa9, - 0x29, 0xed, 0x0f, 0x7c, 0x90, 0x15, 0xb0, 0xe8, 0x1e, 0x21, 0x29, 0xdb, 0x05, 0x0d, - 0x5e, 0x78, 0xe6, 0x82, 0xc8, 0x19, 0x93, 0xea, 0x87, 0x53, 0xc9, 0x91, 0xb0, 0x2e, - 0x61, 0x81, 0x0e, 0x74, 0x61, 0xed, 0x87, 0xb3, 0x80, 0xdb, 0x96, 0xab, 0xe3, 0xbe, - 0xad, 0x0f, 0x4b, 0x22, 0x12, 0xdb, 0x65, 0x8c, 0x11, 0xb8, 0x3f, 0x53, 0x11, 0x47, - 0x85, 0x27, 0x65, 0x98, 0xb0, 0x19, 0x7a, 0x7f, 0x1c, 0x25, 0x62, 0x7d, 0x79, 0x62, - 0x4d, 0xac, 0xee, 0x97, 0x7d, 0x9f, 0x4e, 0x1a, 0x35, 0xed, 0x2e, 0xaa, 0xd3, 0xcb, - 0x68, 0x25, 0x0a, 0xa9, 0xb3, 0xab, 0x1a, 0x83, 0x45, 0x72, 0x8e, 0x7d, 0x1a, 0x78, - 0xbe, 0x1f, 0xe4, 0x62, 0xce, 0x8e, 0xad, 0x52, 0x8f, 0x7c, 0x05, 0x0f, 0x1f, 0x6e, - 0x02, 0x2b, 0xa8, 0xb0, 0xce, 0xdf, 0x6e, 0x29, 0x7a, 0xb5, 0x64, 0xca, 0x1a, 0x1f, - 0xaa, 0xf4, 0xcf, 0xf1, 0xe4, 0x20, 0x32, 0xfb, 0xbb, 0x38, 0x9d, 0x3f, 0x66, 0xd5, - 0x75, 0x55, 0xef, 0x3f, 0x3e, 0x9e, 0x49, 0xc2, 0xac, 0x4e, 0x85, 0xbb, 0x75, 0x1d, - 0x62, 0x66, 0xc9, 0x03, 0x5b, 0x77, 0x9d, 0x76, 0x9d, 0x49, 0x5c, 0x91, 0x8a, 0x05, - 0x5e, 0x77, 0x67, 0xfb, 0xb4, 0xbb, 0xac, 0x3f, 0x96, 0x3d, 0xe9, 0x97, 0x46, 0xec, - 0x4d, 0xfb, 0x64, 0x2d, 0x9c, 0x2b, 0x86, 0x38, 0xe1, 0x6c, 0x16, 0xe7, 0x27, 0x70, - 0x79, 0x3b, 0x7e, 0xa1, 0xd0, 0x70, 0xc4, 0xe1, 0x1c, 0xbc, 0x20, 0xd8, 0xff, 0x3b, - 0xea, 0xd1, 0x0d, 0xb9, 0xc9, 0x4a, 0xe0, 0x48, 0x27, 0x21, 0xe1, 0xf2, 0x2c, 0xef, - 0xe0, 0xdf, 0x7c, 0x57, 0x7a, 0xa3, 0x8e, 0xc0, 0xe6, 0xc7, 0x8c, 0x9b, 0xa1, 0x64, - 0xe9, 0xdd, 0x00, 0x55, 0xdd, 0xe8, 0x3e, 0x8a, 0xd2, 0x40, 0xe6, 0xdf, 0xdb, 0xfb, - 0xe1, 0x76, 0xe4, 0x55, 0x1f, 0xdd, 0xe9, 0x2d, 0xb1, 0x67, 0x27, 0x42, 0x04, 0x41, - 0x70, 0x06, 0x58, 0xb5, 0x0e, 0xbb, 0x5a, 0x16, 0x13, 0x26, 0x7e, 0xac, 0x51, 0xc8, - 0x0b, 0x19, 0xec, 0xb7, 0x86, 0xab, 0x3b, 0xb9, 0x37, 0xf0, 0xd9, 0x8e, 0x08, 0xb9, - 0xc9, 0xcd, 0x4d, 0xf1, 0x53, 0x4e, 0xfe, 0xe3, 0x8a, 0x8f, 0x87, 0x8c, 0x9f, 0x3b, - 0xdc, 0x7e, 0xfb, 0x2d, 0x53, 0xff, 0x84, 0xfb, 0x83, 0xea, 0xe7, 0xc9, 0x9e, 0xff, - 0xa6, 0x3c, 0x96, 0x49, 0xa1, 0xf1, 0x70, 0xd2, 0x9a, 0xf0, 0x3a, 0x3b, 0x45, 0x58, - 0x9f, 0xae, 0x81, 0xeb, 0x0b, 0x5d, 0x8e, 0x0d, 0x38, 0x02, 0x1d, 0x3b, 0x5f, 0x07, - 0xe8, 0x8c, 0x99, 0x04, 0x37, 0x6d, 0x27, 0xf1, 0x3e, 0x44, 0x41, 0xd5, 0x38, 0x74, - 0x42, 0xc5, 0xea, 0x0a, 0xf5, 0xa2, 0x0a, 0x38, 0x32, 0xbc, 0x3b, 0x9c, 0x59, 0xb8, - 0x4b, 0xca, 0x39, 0xb5, 0x2c, 0xd6, 0xb1, 0xfa, 0x29, 0x32, 0xba, 0x9d, 0x66, 0xc4, - 0x12, 0xf5, 0xcd, 0x39, 0x35, 0x1e, 0x13, 0x33, 0xef, 0x85, 0xd0, 0xee, 0xe5, 0x45, - 0xa7, 0xe4, 0x06, 0xf6, 0xeb, 0x3b, 0xf8, 0x93, 0xf3, 0xed, 0xac, 0x94, 0x64, 0x33, - 0x92, 0xa2, 0x8b, 0x0e, 0x49, 0x0c, 0x51, 0xe4, 0xb7, 0x16, 0x3c, 0x1c, 0xf7, 0x57, - 0xd2, 0x24, 0x18, 0xdd, 0x63, 0x38, 0x1b, 0xa2, 0xf2, 0x98, 0x28, 0x83, 0x6f, 0xe9, - 0x78, 0xda, 0xb5, 0x20, 0x1b, 0x2d, 0xb0, 0x8c, 0x3b, 0x38, 0x9b, 0xa4, 0xb6, 0xac, - 0xf7, 0x78, 0xc2, 0xbf, 0x91, 0x02, 0xbe, 0x0c, 0x3e, 0x12, 0xd7, 0x7a, 0xea, 0x6d, - 0xf7, 0x53, 0x8e, 0x8c, 0xf3, 0x62, 0xba, 0xaa, 0xad, 0x1d, 0xc5, 0x60, 0x42, 0xc6, - 0xf2, 0x4c, 0xaf, 0x46, 0xbe, 0xd6, 0x6a, 0xbf, 0x4c, 0x40, 0x2a, 0x74, 0x92, 0x4e, - 0xcf, 0xd0, 0xa0, 0x8d, 0xed, 0xee, 0xa0, 0xef, 0xce, 0xcd, 0x35, 0x2c, 0x27, 0x5f, - 0x13, 0xed, 0x20, 0x76, 0x03, 0x82, 0x2b, 0x1e, 0xf9, 0x97, 0xb7, 0xed, 0x42, 0xf4, - 0xa5, 0x76, 0xb9, 0xe4, 0xc0, 0x07, 0x38, 0x56, 0x3f, 0x82, 0xa7, 0x62, 0x85, 0x46, - 0x7d, 0xa2, 0x95, 0xc2, 0x3b, 0xa1, 0xc5, 0x87, 0xeb, 0xef, 0xaf, 0x13, 0xcd, 0x4d, - 0x50, 0xf2, 0x3c, 0xa5, 0x74, 0x3c, 0x22, 0x5c, 0x38, 0x6d, 0x46, 0xd4, 0xac, 0x70, - 0x83, 0x79, 0xef, 0x99, 0x96, 0x74, 0x4b, 0x39, 0x12, 0x04, 0x4b, 0x35, 0x5f, 0x92, - 0x7a, 0x67, 0xaf, 0x1e, 0xf2, 0x6a, 0x71, 0x7f, 0xb5, 0xa8, 0x46, 0xac, 0x9d, 0xa1, - 0x5e, 0xa3, 0xf1, 0x8f, 0x8c, 0x36, 0x18, 0x3f, 0x87, 0x9b, 0xb9, 0xa3, 0xb2, 0x98, - 0xff, 0xf9, 0xa4, 0x89, 0x64, 0x6e, 0x77, 0x8e, 0x6d, 0x67, 0x01, 0xf9, 0xad, 0xac, - 0x7a, 0xe8, 0x82, 0x09, 0xa8, 0x43, 0xba, 0x8a, 0x55, 0xd1, 0x19, 0x2b, 0xbe, 0xef, - 0x31, 0xd0, 0x71, 0x45, 0x37, 0xf7, 0xa0, 0x35, 0xb0, 0x79, 0xc6, 0xad, 0xd4, 0xab, - 0x50, 0x61, 0x2d, 0x35, 0x89, 0x7a, 0x93, 0x3d, 0x49, 0xe8, 0xef, 0x08, 0x6c, 0xdf, - 0x96, 0xc8, 0x0d, 0x28, 0x56, 0xcc, 0xc7, 0xe4, 0x5f, 0xc4, 0xef, 0xd4, 0xbf, 0x1b, - 0x98, 0xab, 0x28, 0x89, 0x1b, 0x4a, 0xea, 0x7e, 0xf8, 0x4c, 0xf7, 0x36, 0x93, 0x5c, - 0x46, 0x6b, 0x24, 0x97, 0x4d, 0xf8, 0xf5, 0x35, 0x5b, 0x8b, 0xa3, 0x20, 0xac, 0x5f, - 0xbc, 0x47, 0x5a, 0xa2, 0xcf, 0x5a, 0xd3, 0x77, 0x80, 0xbd, 0x9f, 0x9d, 0x46, 0x42, - 0xcf, 0x6c, 0x2d, 0xc6, 0xb8, 0x2f, 0x91, 0x7d, 0x09, 0xc4, 0xf7, 0x28, 0x88, 0xf9, - 0x15, 0x53, 0x44, 0x7f, 0xc5, 0x70, 0x26, 0x6d, 0xaa, 0xfd, 0x4b, 0x96, 0xcf, 0xe2, - 0xa0, 0xb0, 0x67, 0x92, 0x46, 0x9a, 0x72, 0x7d, 0xbe, 0xd0, 0x55, 0x91, 0xea, 0x60, - 0x57, 0x32, 0x20, 0x5e, 0x26, 0x05, 0x97, 0x8a, 0x3a, 0x90, 0x2c, 0x3c, 0xd6, 0x5f, - 0x94, 0x83, 0x00, 0xf7, 0x37, 0x51, 0x88, 0x15, 0xf4, 0x63, 0xd3, 0xc6, 0x1a, 0x18, - 0x9b, 0xc3, 0xbc, 0x84, 0xb0, 0x22, 0xf6, 0x3d, 0x65, 0x4f, 0x52, 0x0e, 0x3a, 0x7a, - 0xd8, 0x8e, 0x5d, 0x8d, 0xa1, 0x50, 0x14, 0xbe, 0x4b, 0xb9, 0x67, 0x99, 0x27, 0xdc, - 0x7e, 0x0f, 0xba, 0xf0, 0x58, 0xd9, 0x3f, 0x37, 0xc7, 0x2b, 0x28, 0x6b, 0x02, 0xb7, - 0x5f, 0x3c, 0xdb, 0xfb, 0x85, 0x0e, 0xed, 0x90, 0xcb, 0x23, 0x39, 0x24, 0x32, 0xeb, - 0xc3, 0x6b, 0xd2, 0x47, 0x54, 0x46, 0x9c, 0x03, 0x73, 0x1a, 0x7e, 0xbb, 0xed, 0x28, - 0x57, 0x78, 0x49, 0x81, 0xa0, 0x71, 0x67, 0x05, 0xd9, 0xcb, 0x47, 0xd9, 0x87, 0xf8, - 0x3d, 0x34, 0x21, 0xb1, 0x07, 0xd1, 0x55, 0xdb, 0xb6, 0x61, 0xed, 0x08, 0xf2, 0xfc, - 0x2e, 0x6b, 0x4a, 0x5b, 0x09, 0x77, 0x64, 0x51, 0xd8, 0x73, 0xb2, 0xfc, 0x63, 0x68, - 0x1c, 0xe3, 0x08, 0xc8, 0x08, 0xf5, 0x38, 0x8c, 0xb1, 0xaa, 0x55, 0x89, 0xa1, 0x87, - 0x73, 0xdb, 0x39, 0x07, 0xa0, 0x6b, 0xef, 0x62, 0xd1, 0x29, 0x60, 0xaa, 0xe7, 0x2a, - 0x2b, 0x89, 0x7e, 0x26, 0xb5, 0x75, 0xfd, 0x04, 0x8a, 0x57, 0x22, 0x2c, 0x7c, 0x68, - 0x0d, 0x54, 0xdc, 0x73, 0x28, 0xd0, 0xf0, 0xf2, 0xd7, 0x0b, 0x43, 0x10, 0x8c, 0xb2, - 0x0c, 0x5c, 0x31, 0x16, 0x46, 0x31, 0xb0, 0xe5, 0xb3, 0xbd, 0x31, 0xb7, 0xdf, 0x8f, - 0x4c, 0x1f, 0xe1, 0x43, 0x4f, 0xa7, 0x47, 0x56, 0x70, 0x6f, 0x83, 0x10, 0x60, 0xa5, - 0xb7, 0x03, 0xdf, 0x9c, 0xd4, 0x2e, 0x24, 0x96, 0x0e, 0x50, 0x8a, 0x04, 0x36, 0x11, - 0x8d, 0x4a, 0x92, 0x07, 0xb6, 0xd8, 0x50, 0x59, 0x6d, 0xde, 0xbe, 0x30, 0xf9, 0x28, - 0xee, 0xea, 0xe7, 0x35, 0x98, 0xfb, 0x3d, 0x86, 0x9d, 0x2d, 0x18, 0x15, 0xa9, 0xe1, - 0x4d, 0x12, 0x79, 0xf7, 0xb4, 0xb6, 0x3f, 0x4b, 0xca, 0x0f, 0x56, 0x68, 0x9b, 0xf8, - 0x73, 0x3b, 0x03, 0x06, 0x49, 0x64, 0xa4, 0xb0, 0x20, 0xb0, 0x60, 0xdc, 0xf4, 0x54, - 0x71, 0xfa, 0x1d, 0x41, 0xe5, 0xee, 0x03, 0xf9, 0xbd, 0x90, 0x65, 0x2b, 0x53, 0x72, - 0x30, 0x3a, 0x3a, 0xb9, 0xbb, 0x2e, 0xe3, 0x79, 0xb9, 0xaf, 0xcd, 0x1f, 0x6a, 0x3c, - 0xb9, 0x00, 0x0b, 0xb1, 0x4e, - ], - script_code: Script(vec![0x53, 0x63, 0x63, 0xac, 0x63, 0x52]), - transparent_input: None, - hash_type: 1, - amount: 1152393991505765, - consensus_branch_id: 1991772603, - sighash: [ - 0x58, 0x11, 0x0e, 0x23, 0x19, 0xad, 0x85, 0x50, 0x4a, 0x69, 0x8f, 0x73, 0xe7, 0xac, - 0x31, 0xa7, 0x23, 0xa0, 0x29, 0xec, 0x07, 0xb7, 0x72, 0xfb, 0xb3, 0x2f, 0xba, 0x17, - 0xff, 0xe2, 0xcc, 0x8d, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xb5, 0xcb, 0x96, 0x49, 0x97, - 0x9e, 0x3c, 0xcf, 0x75, 0xa8, 0xda, 0xd0, 0x54, 0x60, 0x26, 0x1f, 0xcd, 0xcb, 0x00, - 0x7a, 0xeb, 0xc1, 0x5e, 0x11, 0x67, 0x5c, 0x2d, 0xb4, 0xa6, 0xcb, 0x79, 0x38, 0xe1, - 0xfe, 0xb5, 0xcd, 0x04, 0x6a, 0x65, 0x00, 0x63, 0x44, 0x1e, 0x16, 0xc7, 0x07, 0xf0, - 0x97, 0x14, 0x47, 0x4c, 0x96, 0x16, 0x0a, 0xa6, 0x8e, 0xaa, 0x12, 0x31, 0x79, 0x06, - 0x9c, 0xd2, 0x20, 0x44, 0x06, 0x26, 0xcd, 0xfe, 0xed, 0x65, 0xf9, 0xfa, 0xbd, 0xaa, - 0x6d, 0xb1, 0x76, 0x0d, 0xa5, 0xd8, 0x06, 0x63, 0x00, 0x53, 0x6a, 0x65, 0x52, 0xfd, - 0xd0, 0xd2, 0xa9, 0x01, 0xfd, 0xc8, 0x17, 0x1c, 0x9b, 0x0e, 0x06, 0x00, 0x01, 0x65, - 0x26, 0x27, 0xba, 0x0e, 0x87, 0xb5, 0xcd, 0x0f, 0xc8, 0x7b, 0xa3, 0xa2, 0x3c, 0x78, - 0x02, 0x00, 0x00, 0x02, 0x59, 0xb1, 0xb2, 0x59, 0xc5, 0xa2, 0xd8, 0xb7, 0xa6, 0x03, - 0x9b, 0x0e, 0x12, 0xac, 0xd8, 0x89, 0xb5, 0x1b, 0x47, 0x2d, 0xd5, 0x33, 0xa4, 0x61, - 0xfb, 0x0c, 0x3f, 0x96, 0xa9, 0xc0, 0x0a, 0x0b, 0x38, 0x39, 0xfa, 0x89, 0x77, 0x6f, - 0xf0, 0x98, 0xae, 0xef, 0xc7, 0x40, 0x34, 0xff, 0x8c, 0x1f, 0x0d, 0xae, 0x63, 0x68, - 0x32, 0x4c, 0xe5, 0xda, 0x68, 0xd7, 0x71, 0x35, 0x08, 0xae, 0x6d, 0x01, 0x1a, 0xd0, - 0x5f, 0xea, 0xf2, 0x03, 0x56, 0x5c, 0x71, 0xa0, 0x48, 0x66, 0x21, 0xbd, 0xc4, 0x3c, - 0x2a, 0x8e, 0xbb, 0x82, 0x61, 0xd8, 0x47, 0x42, 0x4a, 0x4c, 0xfd, 0x0d, 0xad, 0xcf, - 0x95, 0x9d, 0xb4, 0x37, 0x2b, 0x58, 0xa0, 0xde, 0x19, 0x78, 0x9c, 0x91, 0xfc, 0x99, - 0x31, 0xec, 0xbc, 0xac, 0x64, 0x19, 0xca, 0x0e, 0x5d, 0x97, 0xa3, 0xb4, 0x1c, 0x76, - 0xc8, 0xa1, 0x96, 0xc7, 0xa3, 0xad, 0xf5, 0x5b, 0xdb, 0xe6, 0x0e, 0x85, 0x59, 0x26, - 0x4b, 0x6d, 0x8e, 0xf7, 0x5d, 0x26, 0xdc, 0x72, 0x0f, 0xe5, 0xec, 0x1f, 0x59, 0x66, - 0x2d, 0x95, 0xd0, 0x8e, 0x78, 0x9e, 0x3a, 0xd1, 0x82, 0x9e, 0x40, 0x11, 0x9a, 0xa7, - 0x89, 0x7d, 0x89, 0x40, 0x4d, 0xc4, 0x96, 0x60, 0x46, 0x68, 0xf5, 0x59, 0xca, 0x67, - 0x43, 0x7d, 0x2b, 0xfb, 0xb7, 0xf5, 0x1f, 0x36, 0xe0, 0xa5, 0xb7, 0x22, 0x8f, 0x05, - 0xb6, 0xec, 0x57, 0x89, 0xc1, 0x3f, 0xc2, 0x71, 0x95, 0x56, 0x15, 0x52, 0x63, 0x96, - 0x6e, 0x81, 0xf5, 0x21, 0x51, 0xe2, 0xf6, 0xe3, 0x68, 0x69, 0xd8, 0xa3, 0xc4, 0xc4, - 0x96, 0xa5, 0x13, 0x63, 0x2c, 0xaa, 0x8a, 0xbe, 0x1f, 0x27, 0x35, 0xeb, 0x60, 0xfc, - 0x12, 0x85, 0x82, 0x8e, 0xad, 0xdc, 0x54, 0x41, 0xa4, 0x02, 0xa3, 0xbf, 0x5b, 0xcd, - 0x22, 0x7c, 0xd8, 0x04, 0xe3, 0xc8, 0xca, 0x21, 0x24, 0x3c, 0xdf, 0xcd, 0x53, 0xd8, - 0x66, 0x05, 0xf3, 0xf8, 0xaf, 0x1a, 0x9c, 0xc5, 0x69, 0x33, 0x15, 0x53, 0x28, 0x28, - 0x01, 0x43, 0xfa, 0xdb, 0x3a, 0x1f, 0xc3, 0x3d, 0x76, 0x9f, 0x07, 0xff, 0xc0, 0x1e, - 0x35, 0x79, 0xe1, 0x18, 0x1f, 0x19, 0x15, 0xdb, 0x89, 0xd8, 0x2e, 0x50, 0xbd, 0x74, - 0x24, 0x08, 0x7c, 0x79, 0x7d, 0x9b, 0x7b, 0x3b, 0x7d, 0x2a, 0x53, 0xb8, 0xff, 0xf9, - 0xf2, 0xd9, 0x28, 0xab, 0x99, 0x6d, 0xce, 0x5e, 0xd2, 0x71, 0x58, 0x98, 0xe4, 0x85, - 0x8e, 0xec, 0x60, 0x78, 0xa9, 0x48, 0x8d, 0x2d, 0xa6, 0xd1, 0x73, 0x05, 0xd0, 0xa3, - 0x47, 0x18, 0x62, 0xa2, 0x22, 0x38, 0xb9, 0xbe, 0xc2, 0x3e, 0xf2, 0xe2, 0x04, 0x1d, - 0x50, 0x08, 0x73, 0x3e, 0x9e, 0xa5, 0x66, 0x2c, 0x9f, 0xea, 0x0e, 0x4a, 0xfd, 0xf3, - 0x27, 0x0c, 0x11, 0x32, 0x3b, 0xa4, 0x8b, 0x35, 0x50, 0x85, 0x74, 0x40, 0x97, 0xf3, - 0xf6, 0xc5, 0x2e, 0xe4, 0x04, 0x31, 0x73, 0x9c, 0x5c, 0xa8, 0xdb, 0x2b, 0xda, 0x13, - 0xda, 0x9b, 0x33, 0x0b, 0x62, 0x00, 0x0b, 0x79, 0xfd, 0x35, 0x44, 0xb1, 0x31, 0x83, - 0x15, 0x9d, 0x17, 0x4f, 0xfe, 0xd2, 0x54, 0x85, 0x40, 0xa5, 0x2e, 0xe4, 0xb6, 0x2d, - 0x35, 0xaa, 0x5a, 0x58, 0x63, 0xf2, 0xba, 0xa4, 0x47, 0x5f, 0x3e, 0xb6, 0xc7, 0x35, - 0x9d, 0xc8, 0x39, 0xdb, 0xc8, 0x68, 0x90, 0xd1, 0x99, 0xd8, 0xea, 0x6c, 0x9d, 0x97, - 0xf1, 0x9e, 0x79, 0x2c, 0x7b, 0xcb, 0x66, 0x25, 0xff, 0x32, 0xb7, 0x31, 0x57, 0x5f, - 0x62, 0xd9, 0x44, 0xc8, 0x06, 0xb3, 0xf9, 0x3c, 0x04, 0xb7, 0x3a, 0x98, 0xb2, 0x73, - 0x43, 0xeb, 0x25, 0xa0, 0x6c, 0x87, 0x53, 0x60, 0xde, 0x1a, 0x14, 0x38, 0x84, 0x0a, - 0xd0, 0x66, 0x1d, 0xeb, 0xdc, 0x9b, 0x82, 0x8a, 0xd0, 0xcb, 0xc0, 0x01, 0x1b, 0x32, - 0x35, 0xb2, 0xc7, 0x53, 0x77, 0x78, 0xf4, 0x58, 0x82, 0x1b, 0x83, 0xaa, 0x4c, 0xb3, - 0xe5, 0x4e, 0xd0, 0x61, 0x3e, 0x32, 0xe6, 0x3e, 0xf9, 0x85, 0xf9, 0x35, 0xbd, 0x7f, - 0xf8, 0xc7, 0x70, 0x5c, 0x89, 0xc0, 0xbb, 0xcc, 0xda, 0x9e, 0x66, 0x5e, 0x3b, 0x06, - 0xba, 0x87, 0x9f, 0xdd, 0xf3, 0x5e, 0x0b, 0x2f, 0x60, 0xc2, 0xa7, 0x0c, 0xb8, 0xeb, - 0x9d, 0xe2, 0xf5, 0xd7, 0x38, 0xc0, 0x5e, 0x34, 0xe5, 0x0f, 0x1f, 0x26, 0x19, 0x25, - 0x8b, 0x89, 0xe5, 0x73, 0xda, 0x55, 0x75, 0x46, 0x3d, 0x2e, 0x3b, 0xce, 0x39, 0xf7, - 0x0e, 0xb4, 0x55, 0x26, 0xcd, 0x99, 0xfa, 0xd9, 0x0f, 0x97, 0x92, 0xd0, 0xcd, 0x59, - 0x3b, 0xa8, 0x6a, 0xa1, 0xae, 0xa5, 0x03, 0xdd, 0xca, 0x5e, 0x3e, 0x57, 0x37, 0xe6, - 0xfc, 0x7b, 0xab, 0x27, 0x85, 0x12, 0x69, 0x20, 0xc4, 0x47, 0xd5, 0xe5, 0x6a, 0x75, - 0xdb, 0xe8, 0x9d, 0x68, 0x8b, 0xc0, 0xda, 0xa7, 0x9a, 0xa6, 0x2d, 0xe9, 0xea, 0x29, - 0x55, 0xf7, 0x1e, 0x1a, 0x61, 0x68, 0x2a, 0x61, 0x78, 0xf8, 0x0b, 0xca, 0xda, 0x3b, - 0x97, 0xae, 0xec, 0x77, 0xd9, 0xc8, 0x56, 0x3b, 0x06, 0x9e, 0xa0, 0x13, 0x2f, 0x72, - 0x3f, 0xbe, 0x75, 0x60, 0x2d, 0xd6, 0x29, 0xac, 0x48, 0x09, 0x93, 0xd3, 0x71, 0x4f, - 0xf0, 0x2c, 0x97, 0x0e, 0xbd, 0x83, 0xe6, 0xd6, 0xcb, 0xbe, 0x39, 0x08, 0x6b, 0x03, - 0x54, 0x20, 0xe0, 0xc2, 0x75, 0x62, 0x86, 0x58, 0xa3, 0xba, 0x92, 0x30, 0x5c, 0xc0, - 0x76, 0x98, 0xf1, 0x2e, 0xe1, 0xe4, 0x17, 0x13, 0x70, 0xac, 0x39, 0xdf, 0x0e, 0x46, - 0x6d, 0xc8, 0xec, 0xc3, 0x9d, 0xa5, 0xee, 0x47, 0xb6, 0x82, 0x9d, 0xbb, 0xa9, 0x97, - 0x0f, 0x03, 0x58, 0xed, 0x68, 0x26, 0x49, 0x60, 0x5c, 0x7b, 0xfe, 0xe6, 0x93, 0x1a, - 0x29, 0x5b, 0x14, 0xa3, 0x40, 0x76, 0x00, 0x07, 0x4e, 0xdc, 0x79, 0xfa, 0x61, 0xe6, - 0x80, 0x6f, 0x11, 0x08, 0xd3, 0x34, 0xb4, 0xa5, 0x90, 0xf7, 0xa0, 0x26, 0xb0, 0xeb, - 0x02, 0x80, 0x4d, 0x39, 0x17, 0x46, 0x6e, 0x99, 0x91, 0x20, 0x64, 0x1c, 0xe0, 0x7e, - 0xbc, 0xdc, 0x99, 0x42, 0x60, 0x82, 0xe0, 0x77, 0x1f, 0x15, 0x9c, 0x82, 0x6a, 0x9b, - 0xe6, 0xce, 0xd7, 0x2d, 0x0e, 0x9c, 0xfa, 0x5b, 0x4b, 0x8a, 0x86, 0x40, 0xca, 0x34, - 0x88, 0xa1, 0xeb, 0x2b, 0x6e, 0x37, 0x4e, 0x8c, 0x2e, 0x00, 0x3c, 0xdf, 0xa2, 0x32, - 0x10, 0x37, 0x48, 0xb5, 0xc9, 0xdc, 0x11, 0xbb, 0x30, 0xf6, 0x46, 0xb9, 0x73, 0xd7, - 0x83, 0xf5, 0x99, 0x14, 0x17, 0x4e, 0x48, 0xbd, 0x6a, 0x84, 0xfa, 0xd8, 0x9d, 0xbc, - 0xa5, 0xc7, 0x6d, 0x0a, 0xb4, 0x14, 0x5a, 0xbd, 0x08, 0xe4, 0xd0, 0xf2, 0xc7, 0x60, - 0x25, 0xfc, 0x85, 0xfc, 0x11, 0x6c, 0xca, 0x8d, 0x30, 0x2c, 0x8a, 0x3b, 0xeb, 0x26, - 0x60, 0x3a, 0x1a, 0xf1, 0xb5, 0x93, 0x91, 0xea, 0xf4, 0x71, 0x75, 0x9a, 0xdf, 0x19, - 0x4c, 0x40, 0xc2, 0x09, 0x29, 0x8c, 0xc0, 0x51, 0xfc, 0x79, 0x03, 0xfe, 0x40, 0x90, - 0x2c, 0x35, 0x6f, 0x28, 0x27, 0x9f, 0x27, 0x94, 0xbb, 0xb9, 0xe0, 0x0b, 0x1e, 0x22, - 0x1b, 0x0a, 0x26, 0x41, 0x06, 0xea, 0x50, 0x4f, 0xb8, 0x90, 0x6a, 0x20, 0x84, 0x5a, - 0x05, 0x9a, 0x60, 0x3b, 0x4f, 0x00, 0xe7, 0x83, 0x6d, 0x40, 0x67, 0xa6, 0x04, 0x19, - 0x5f, 0x24, 0x6a, 0x0f, 0x3b, 0x31, 0x82, 0x3f, 0xdf, 0x69, 0x57, 0x8c, 0x47, 0xdb, - 0x5b, 0x3d, 0xda, 0x86, 0xaa, 0xb1, 0xec, 0x9f, 0x58, 0xd9, 0x62, 0x26, 0xc6, 0xb9, - 0x1d, 0xc0, 0xf0, 0x3f, 0xe8, 0xd7, 0xdf, 0x23, 0xcf, 0x53, 0xca, 0x8e, 0xa2, 0xa9, - 0x09, 0x4f, 0xc0, 0x28, 0x65, 0x26, 0x7c, 0x88, 0xfa, 0x8c, 0x01, 0x0e, 0xb5, 0x66, - 0x13, 0x06, 0x6e, 0x50, 0xf1, 0x55, 0x4a, 0xa4, 0x10, 0x8e, 0x25, 0xa9, 0xe9, 0x67, - 0xd3, 0x4a, 0x9c, 0xf1, 0x02, 0x8c, 0x17, 0x05, 0xfa, 0x37, 0x67, 0xf4, 0x6d, 0x4b, - 0xab, 0x70, 0x28, 0xb0, 0x9b, 0x20, 0x38, 0xfc, 0x1b, 0x72, 0x7f, 0x61, 0x9e, 0x61, - 0xc4, 0xfc, 0x16, 0xbf, 0xfe, 0x65, 0x7e, 0x99, 0x12, 0x6a, 0xc5, 0x18, 0x4f, 0xc8, - 0x7f, 0x5e, 0x53, 0x01, 0x88, 0x64, 0x23, 0xb3, 0x56, 0x87, 0x59, 0x09, 0xec, 0x92, - 0xb3, 0x2d, 0x33, 0x08, 0x42, 0x53, 0xa1, 0xb9, 0x7c, 0x5d, 0x2e, 0xd6, 0x6c, 0x7e, - 0x22, 0xd1, 0x85, 0x58, 0xfe, 0x82, 0xb5, 0xec, 0x88, 0xc6, 0x07, 0x05, 0x82, 0xfa, - 0xcf, 0x75, 0x6d, 0x70, 0x32, 0x38, 0xd9, 0xaf, 0x94, 0x19, 0x96, 0x6b, 0xe4, 0x62, - 0xdf, 0xbd, 0x31, 0x5c, 0x5b, 0xfa, 0xf0, 0x44, 0xaa, 0x69, 0x5a, 0x05, 0xe6, 0x9d, - 0x3d, 0x41, 0xe7, 0x73, 0x78, 0x75, 0x1d, 0x4e, 0x02, 0xc2, 0x66, 0xdf, 0xb5, 0xcb, - 0x6a, 0x7c, 0x40, 0x08, 0xf9, 0x44, 0x88, 0x83, 0x11, 0xe6, 0xde, 0x37, 0xdc, 0x7b, - 0xdf, 0x65, 0xd7, 0x0c, 0xab, 0x3e, 0x07, 0x8a, 0xb4, 0x4e, 0x23, 0x2b, 0x41, 0x1c, - 0xaf, 0xb2, 0x88, 0x4e, 0x26, 0x45, 0x95, 0xbe, 0xed, 0xf9, 0xd4, 0x9a, 0x79, 0x36, - 0xbb, 0x28, 0x7f, 0xe2, 0x8e, 0x1c, 0x29, 0x63, 0x5e, 0xae, 0xca, 0x74, 0x7d, 0x06, - 0x87, 0xcf, 0x46, 0x59, 0x02, 0xd2, 0x5f, 0x5e, 0x51, 0x58, 0x48, 0x1d, 0xaa, 0xcd, - 0xd3, 0x00, 0xb4, 0x77, 0x40, 0xbc, 0x0c, 0x62, 0x77, 0xb4, 0x47, 0xcc, 0x26, 0x64, - 0x04, 0x42, 0x43, 0xdd, 0x48, 0x11, 0x40, 0x4e, 0xcb, 0xd7, 0xc7, 0xa6, 0x3c, 0x9f, - 0xb7, 0xd9, 0x37, 0xbc, 0xd8, 0x12, 0xc2, 0x34, 0x59, 0x23, 0xb5, 0x90, 0x26, 0x83, - 0xbd, 0x2e, 0xd5, 0x4c, 0x01, 0xae, 0x04, 0x19, 0xa7, 0xf5, 0x4e, 0x8a, 0x3a, 0x59, - 0xc6, 0xa6, 0xda, 0xcf, 0x89, 0xc7, 0x37, 0x0e, 0x79, 0xb5, 0x60, 0x13, 0x6a, 0x2b, - 0x00, 0xdd, 0xb6, 0x07, 0x4d, 0x74, 0xff, 0xc5, 0xc5, 0xdf, 0xd0, 0x6b, 0x6c, 0x51, - 0x9a, 0xbe, 0xc3, 0x59, 0x6a, 0x47, 0x61, 0x13, 0xbe, 0x41, 0x38, 0xee, 0xad, 0x5f, - 0xfd, 0xe8, 0x6b, 0x1e, 0x32, 0x40, 0x1f, 0xa3, 0x84, 0x62, 0x32, 0xd0, 0xb3, 0xc9, - 0xbd, 0x56, 0x88, 0xb6, 0x4a, 0x33, 0x09, 0x38, 0x16, 0x2a, 0x8b, 0x89, 0x29, 0xd7, - 0x0c, 0x1b, 0x67, 0x53, 0x62, 0xf4, 0xc2, 0xa9, 0xbb, 0x6b, 0x7f, 0x91, 0xeb, 0xd4, - 0x7d, 0x26, 0x3c, 0xf0, 0xa4, 0x05, 0xa2, 0x8b, 0xa7, 0x41, 0x56, 0x44, 0xf9, 0x3b, - 0x6c, 0xdf, 0xa3, 0xec, 0xeb, 0xb7, 0xb8, 0xd4, 0xee, 0x8b, 0x94, 0xb2, 0x7b, 0x61, - 0xe4, 0x03, 0x5e, 0xd6, 0xa4, 0x77, 0x46, 0x7f, 0x4a, 0x32, 0x0b, 0x8a, 0x4e, 0xba, - 0x0a, 0xb5, 0x6c, 0x26, 0x3e, 0x4b, 0xfb, 0xe2, 0x6a, 0x41, 0x8e, 0xd1, 0xcd, 0xe6, - 0x18, 0x4b, 0x89, 0x50, 0xfe, 0x7a, 0xac, 0x7f, 0x20, 0xa4, 0x7b, 0xa1, 0xbf, 0xf9, - 0x80, 0x4f, 0x53, 0xf6, 0x93, 0x23, 0xdb, 0x84, 0x75, 0x20, 0xa6, 0x58, 0x47, 0xb3, - 0x03, 0x4c, 0x4e, 0x08, 0x1b, 0xb4, 0xb8, 0x69, 0x26, 0x3b, 0x5f, 0x9b, 0x3a, 0x7a, - 0x83, 0x3b, 0x6e, 0x4c, 0xa7, 0x90, 0xcc, 0xf9, 0xfd, 0xae, 0x80, 0x79, 0xe5, 0x56, - 0x09, 0x27, 0x2c, 0x63, 0xb5, 0x49, 0xb0, 0xc8, 0x5f, 0x11, 0x0c, 0xc9, 0xc9, 0x58, - 0x68, 0x01, 0x14, 0xb3, 0x11, 0x74, 0x80, 0xaf, 0x57, 0xcb, 0x15, 0x9e, 0xdf, 0xbe, - 0x5c, 0xb9, 0xc6, 0x2b, 0xce, 0x2c, 0xf2, 0xab, 0x29, 0xb6, 0x67, 0x11, 0xac, 0x7a, - 0xa5, 0x3a, 0x74, 0x9f, 0xfa, 0x83, 0x90, 0x7e, 0xcb, 0x69, 0x12, 0xaa, 0x56, 0x96, - 0x38, 0xde, 0xa1, 0x9e, 0x54, 0x41, 0x61, 0x1e, 0xfc, 0xa3, 0x20, 0x99, 0x65, 0x3e, - 0x8a, 0x5c, 0xa1, 0xfb, 0xbd, 0xba, 0xb1, 0xd6, 0x44, 0x71, 0xec, 0x32, 0x0e, 0xc3, - 0x8e, 0xa4, 0x88, 0x40, 0x0c, 0x9b, 0x1f, 0x4e, 0x8c, 0xb5, 0x48, 0x0c, 0x0e, 0x92, - 0x42, 0xb0, 0x86, 0xa8, 0x0e, 0xee, 0xd4, 0x90, 0xae, 0x32, 0x00, 0x0c, 0x80, 0x09, - 0xec, 0xb7, 0x1f, 0xfa, 0x39, 0xf4, 0xf3, 0xb5, 0x74, 0x9c, 0xfd, 0x1b, 0xef, 0xe0, - 0xd9, 0x66, 0x7a, 0xb3, 0x02, 0x20, 0xc2, 0xdc, 0x04, 0x39, 0x36, 0x98, 0xb2, 0xcf, - 0xa2, 0x04, 0x92, 0xf2, 0x50, 0xce, 0x14, 0x32, 0x35, 0x81, 0x58, 0x70, 0x3d, 0xf7, - 0xb1, 0x39, 0xd7, 0x45, 0xce, 0x1f, 0xc3, 0x40, 0x78, 0x77, 0x01, 0xfb, 0x51, 0xdd, - 0x5e, 0x48, 0xb8, 0x95, 0x09, 0x41, 0x7d, 0x88, 0x89, 0x00, 0x80, 0x63, 0xf9, 0xba, - 0x01, 0x5a, 0x07, 0xd8, 0xd3, 0x9b, 0xbd, 0x00, 0x76, 0x2f, 0x59, 0x5a, 0xfa, 0xd8, - 0xd8, 0x59, 0xea, 0xab, 0xf0, 0xd8, 0x2d, 0x46, 0x33, 0xcf, 0x82, 0x98, 0xb0, 0x9b, - 0xea, 0x3f, 0x22, 0x28, 0x55, 0xa9, 0x2a, 0x08, 0x43, 0xf5, 0x2f, 0xa5, 0x8d, 0xb3, - 0xa1, 0x75, 0xc3, 0x0d, 0x2a, 0xbe, 0x64, 0x82, 0x64, 0x90, 0xcb, 0xe6, 0xca, 0x14, - 0x88, 0xfe, 0x3a, 0x01, 0x5a, 0x94, 0x6d, 0xc9, 0xc4, 0x5a, 0xc3, 0x09, 0x25, 0x72, - 0x7a, 0x13, 0xe0, 0x89, 0x78, 0xf7, 0x24, 0x03, 0x47, 0x20, 0x8a, 0x4d, 0x25, 0x38, - 0xc2, 0xd5, 0x61, 0x24, 0x37, 0x8c, 0x22, 0xc0, 0x4e, 0x23, 0xdc, 0x28, 0xb1, 0x50, - 0x19, 0xbe, 0x77, 0x6d, 0x70, 0xbf, 0xc1, 0xd2, 0x64, 0x5b, 0x5e, 0x80, 0xd1, 0xfd, - 0x84, 0x19, 0xdf, 0x72, 0x90, 0x43, 0x80, 0xe2, 0xe1, 0xfc, 0x4d, 0xd1, 0xdf, 0x1b, - 0xa3, 0xdf, 0xe4, 0x80, 0xcc, 0x84, 0x6d, 0x51, 0x51, 0x4a, 0x06, 0x5e, 0xd7, 0x62, - 0x78, 0x7a, 0xfd, 0x6e, 0xb9, 0x0b, 0xdf, 0x8f, 0xbb, 0xad, 0x5e, 0xb3, 0xd2, 0x3f, - 0xdc, 0x8c, 0x54, 0xcc, 0xa1, 0x0f, 0xa1, 0xfe, 0x54, 0x64, 0x82, 0xf5, 0xe1, 0x42, - 0x4b, 0xfd, 0xa8, 0x7a, 0xa7, 0xfb, 0x78, 0x6e, 0x26, 0x0f, 0x26, 0x14, 0xbe, 0x08, - 0x11, 0xee, 0x16, 0xb8, 0xd2, 0x9d, 0xf9, 0xa0, 0xf3, 0x30, 0xe9, 0x70, 0x9f, 0x63, - 0xc9, 0x50, 0xfb, 0xd9, 0x03, 0xff, 0x7d, 0x5b, 0x0c, 0xa2, 0x9f, 0xd6, 0x3b, 0x0f, - 0x97, 0x51, 0x77, 0x69, 0x02, 0x5c, 0xc3, 0x6a, 0x52, 0xe0, 0x00, 0x15, 0x93, 0x4a, - 0x3c, 0xa2, 0x58, 0xb8, 0xba, 0xb9, 0x00, 0x16, 0xa4, 0x01, 0xd5, 0xd8, 0xd7, 0xc3, - 0xb9, 0x44, 0x92, 0x5b, 0x35, 0xa9, 0x34, 0x9a, 0x1a, 0xc7, 0xd9, 0x85, 0x21, 0x61, - 0x0c, 0x2f, 0xad, 0x8b, 0x5c, 0x8b, 0x31, 0x9c, 0xd6, 0xe0, 0x5f, 0x9b, 0xbe, 0xd3, - 0x53, 0xf1, 0xd0, 0xc8, 0x65, 0xa9, 0x4a, 0xa4, 0x56, 0xdc, 0xd1, 0x8a, 0x39, 0xe2, - 0xf5, 0x85, 0xd9, 0xbe, 0xa8, - ], - script_code: Script(vec![0x63, 0x00, 0x6a, 0x53, 0x63, 0x6a, 0xac, 0x00]), - transparent_input: None, - hash_type: 1, - amount: 1788797765223798, - consensus_branch_id: 1991772603, - sighash: [ - 0xcb, 0xfa, 0x22, 0x69, 0x9b, 0x04, 0xbe, 0xb7, 0x67, 0x07, 0xb5, 0x1d, 0x62, 0x5e, - 0x94, 0xd2, 0x6c, 0x0d, 0xf8, 0xad, 0xa7, 0xcf, 0x68, 0xfc, 0xde, 0xd9, 0x60, 0x65, - 0x4b, 0x20, 0xf3, 0x60, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x02, 0x12, 0x9a, 0x03, 0xd5, - 0x7d, 0x32, 0x07, 0x00, 0x07, 0x52, 0x51, 0xac, 0x51, 0x65, 0xac, 0x00, 0x91, 0xdb, - 0xbd, 0x6f, 0xf3, 0x8f, 0x01, 0x00, 0x05, 0x53, 0x6a, 0x63, 0x63, 0x53, 0x6f, 0x34, - 0xc4, 0x99, 0x32, 0x68, 0xc7, 0x09, 0xef, 0xf1, 0x20, 0x1b, 0x50, 0x92, 0x05, 0x00, - 0x02, 0x3b, 0x5c, 0x8b, 0x5b, 0x80, 0xe7, 0x7b, 0x87, 0xf1, 0xeb, 0x73, 0xaf, 0x77, - 0x60, 0xed, 0xae, 0x0e, 0x19, 0x3e, 0x38, 0x96, 0xb1, 0x5c, 0x55, 0x8f, 0x00, 0x4e, - 0x7c, 0x7d, 0x93, 0x24, 0xd3, 0x85, 0xb4, 0x50, 0xcd, 0x4b, 0x98, 0x2a, 0xba, 0x8d, - 0x2e, 0x91, 0xf4, 0x1f, 0x22, 0xee, 0xe7, 0xf3, 0x6d, 0x79, 0xcc, 0xa9, 0xc0, 0xe0, - 0x1b, 0x26, 0xc4, 0x65, 0x11, 0x18, 0xea, 0x77, 0x15, 0x14, 0xc7, 0x7e, 0xd6, 0x0c, - 0xd5, 0x24, 0x51, 0x94, 0x2d, 0xc8, 0x5b, 0x3f, 0xba, 0x44, 0x8b, 0x2d, 0x63, 0x10, - 0xf2, 0x77, 0x79, 0x42, 0x83, 0x2e, 0x21, 0xcf, 0x3d, 0x44, 0x87, 0x4f, 0x8d, 0xca, - 0x98, 0x2b, 0x68, 0x7c, 0x9e, 0xd7, 0xe0, 0xb2, 0x32, 0x77, 0x07, 0x3c, 0x19, 0x30, - 0xa4, 0x73, 0xd1, 0x66, 0x8e, 0xf2, 0xe9, 0xae, 0x96, 0x63, 0xcf, 0xf0, 0x58, 0x16, - 0x62, 0x6c, 0xd3, 0xc5, 0xbf, 0x77, 0x16, 0x53, 0xd7, 0x78, 0x51, 0x81, 0x35, 0x5c, - 0x05, 0xae, 0xd2, 0x4a, 0x99, 0xc4, 0xb6, 0x74, 0xd2, 0x4a, 0x0f, 0x08, 0xf4, 0xb0, - 0xcf, 0xbe, 0x90, 0xf2, 0xfd, 0xba, 0xb4, 0x24, 0x82, 0xe9, 0x8f, 0x13, 0xff, 0xfc, - 0xd1, 0xad, 0x33, 0xf4, 0xf4, 0xc0, 0x4d, 0xeb, 0xc8, 0x9f, 0x40, 0xb5, 0xdb, 0xf6, - 0x45, 0x46, 0xc5, 0x20, 0xdc, 0xa5, 0xd0, 0xec, 0xf3, 0xf6, 0x5d, 0x3a, 0x77, 0xd0, - 0x12, 0x9f, 0x60, 0x03, 0x71, 0x10, 0x8a, 0xac, 0x30, 0xa9, 0xec, 0xa8, 0xbe, 0xe5, - 0x52, 0x4f, 0xab, 0x67, 0x1f, 0xc0, 0x86, 0x58, 0x76, 0x2c, 0x87, 0x38, 0xab, 0xc9, - 0xfa, 0x76, 0x93, 0xe3, 0x9d, 0x39, 0xd7, 0x03, 0xd5, 0xcd, 0x94, 0x2b, 0x5a, 0x55, - 0xfe, 0xda, 0xfe, 0xcc, 0xae, 0xf7, 0x02, 0x17, 0x69, 0xe9, 0x2c, 0xc9, 0xd3, 0xac, - 0x7b, 0x4c, 0x23, 0xb3, 0x3f, 0xc2, 0x23, 0x21, 0x85, 0x4b, 0xa3, 0x3f, 0x49, 0xee, - 0xba, 0xdd, 0xca, 0x29, 0xb3, 0x56, 0x40, 0xe4, 0xf0, 0xc2, 0xfd, 0x8c, 0x12, 0xb9, - 0x84, 0x52, 0x97, 0x60, 0xe0, 0x65, 0xfe, 0xcb, 0xa1, 0x21, 0x86, 0xd2, 0x0a, 0xee, - 0xc3, 0xda, 0x58, 0xfc, 0x35, 0x9b, 0xa8, 0x25, 0xe5, 0xb8, 0xe2, 0xe1, 0x8f, 0x12, - 0xcf, 0x29, 0x49, 0xc3, 0x12, 0xf6, 0x3c, 0x4d, 0xd7, 0xa7, 0x9b, 0x0e, 0x66, 0xb9, - 0xc8, 0xb6, 0x6f, 0xe8, 0x9a, 0xd7, 0xed, 0xc6, 0x2a, 0xc4, 0xd2, 0x07, 0xe2, 0x77, - 0xb9, 0x33, 0xb0, 0xc2, 0x06, 0xdd, 0x7c, 0x22, 0xd2, 0xdb, 0x26, 0x33, 0xfc, 0x01, - 0xa8, 0x3c, 0x24, 0xfc, 0xad, 0x40, 0x9c, 0xee, 0xd5, 0x36, 0xa6, 0xd3, 0xe8, 0xe0, - 0x8d, 0x42, 0xb5, 0x13, 0x48, 0x97, 0xb4, 0x36, 0xbf, 0xf3, 0xa1, 0xbc, 0xef, 0xc5, - 0x3a, 0xec, 0x30, 0xed, 0x89, 0x11, 0x0f, 0x89, 0x60, 0x88, 0x8a, 0x1c, 0xf2, 0x41, - 0x5c, 0xc6, 0x93, 0xa8, 0x52, 0x97, 0xd6, 0xb7, 0x89, 0x14, 0x1e, 0x04, 0x1a, 0x3c, - 0x14, 0xa5, 0xf9, 0xc6, 0x46, 0x33, 0xbe, 0x06, 0x56, 0x45, 0xe9, 0xca, 0x36, 0x37, - 0xf3, 0x73, 0x83, 0x04, 0xec, 0x3b, 0x16, 0x51, 0x31, 0x46, 0x83, 0xa0, 0x27, 0x5e, - 0x73, 0x36, 0x79, 0x70, 0x01, 0x06, 0x78, 0x23, 0x17, 0x79, 0x3e, 0x86, 0x6c, 0xed, - 0x59, 0x89, 0x21, 0x3f, 0x3b, 0xac, 0xfc, 0xfd, 0x20, 0x02, 0xea, 0x86, 0x6f, 0x3f, - 0x17, 0x07, 0x35, 0x12, 0x64, 0xb6, 0x67, 0x88, 0xf4, 0xeb, 0x7f, 0x68, 0xc5, 0xa5, - 0x36, 0xfa, 0x9c, 0x13, 0x0d, 0x20, 0x26, 0xea, 0x80, 0x97, 0x94, 0xd3, 0xb7, 0x4d, - 0x78, 0x01, 0x7e, 0xe0, 0xfb, 0xca, 0x83, 0xcc, 0x7e, 0x5c, 0xbd, 0x52, 0x7a, 0xcd, - 0xe7, 0x46, 0x53, 0x73, 0x51, 0x2c, 0x07, 0x64, 0x6a, 0x62, 0xc6, 0x0f, 0x5c, 0x16, - 0xc2, 0xef, 0x9f, 0x41, 0x8d, 0x8c, 0x7d, 0x18, 0x8f, 0x7b, 0x13, 0xdd, 0x45, 0x38, - 0xa5, 0x5d, 0x18, 0x6a, 0xd6, 0x36, 0x2a, 0x58, 0x9a, 0x9f, 0x52, 0xb2, 0x5e, 0x61, - 0x6f, 0xb2, 0xa3, 0x57, 0xac, 0xca, 0xde, 0x63, 0x57, 0xfa, 0x5a, 0x42, 0xa7, 0x98, - 0xe4, 0x17, 0x13, 0x11, 0xad, 0xe9, 0xcc, 0xfd, 0x15, 0xf2, 0x7c, 0x8c, 0x19, 0x72, - 0x17, 0x9d, 0x26, 0x1f, 0xb9, 0xb0, 0x9b, 0xc7, 0xa0, 0x36, 0xc1, 0x05, 0x55, 0x9b, - 0x04, 0x38, 0x9d, 0xfd, 0x8a, 0x7b, 0xe2, 0xa3, 0xae, 0x2b, 0xba, 0x2a, 0xfb, 0xd1, - 0xe9, 0xbf, 0x90, 0x05, 0xc8, 0xb3, 0x66, 0x35, 0x4f, 0x90, 0x9b, 0xe7, 0x1e, 0x52, - 0xc0, 0x90, 0x80, 0xfb, 0xa7, 0x45, 0x23, 0x77, 0xe8, 0xf1, 0x2c, 0x18, 0x4f, 0xe7, - 0xed, 0x46, 0x5b, 0x32, 0xc9, 0xf9, 0xb2, 0x81, 0x9e, 0xa1, 0xd1, 0x19, 0xfc, 0x26, - 0x7c, 0x8a, 0x75, 0x33, 0x81, 0xeb, 0x51, 0xac, 0xf8, 0x54, 0xc1, 0x9e, 0x8d, 0x58, - 0xff, 0x42, 0x74, 0xeb, 0xa8, 0xc6, 0x3f, 0x0f, 0xa1, 0x70, 0xa6, 0x3c, 0xbf, 0xce, - 0x2c, 0xf8, 0x7b, 0xdc, 0xdf, 0x32, 0xb7, 0xe1, 0x98, 0x04, 0x54, 0x1c, 0x2c, 0x58, - 0x97, 0x24, 0xef, 0xc6, 0x9b, 0xc4, 0x65, 0xd0, 0x90, 0x8e, 0x09, 0xb8, 0x4d, 0x1f, - 0x50, 0x41, 0x2b, 0xb0, 0x7f, 0x47, 0xfb, 0x9f, 0x0d, 0x47, 0x29, 0x28, 0x16, 0x14, - 0xca, 0xca, 0xb6, 0x14, 0xef, 0x65, 0xce, 0xba, 0x13, 0x96, 0xb5, 0x24, 0x9d, 0x2c, - 0x61, 0x70, 0x4f, 0xb6, 0xf3, 0x48, 0x44, 0x71, 0x83, 0xf9, 0x88, 0x2a, 0x98, 0xae, - 0x9c, 0x71, 0xa7, 0x66, 0x33, 0xe0, 0x5b, 0x33, 0x3a, 0x1b, 0xce, 0xee, 0xc9, 0xbd, - 0x44, 0xb8, 0x87, 0x6f, 0xab, 0x6c, 0xd7, 0x2a, 0x5e, 0x33, 0x5c, 0x97, 0x7a, 0x04, - 0x55, 0xc5, 0x36, 0x5f, 0xe8, 0x7f, 0x17, 0xa0, 0x5c, 0x0f, 0x8c, 0x23, 0x3b, 0x97, - 0x29, 0xc1, 0x09, 0x3b, 0xae, 0xb8, 0x57, 0x5c, 0xe5, 0xfd, 0xfb, 0xfd, 0x6e, 0x6a, - 0x8e, 0xbf, 0x76, 0x46, 0x47, 0x81, 0xf9, 0xb2, 0x10, 0xed, 0xb3, 0x9d, 0x20, 0x6a, - 0x68, 0x5d, 0x0d, 0xc7, 0xec, 0x06, 0x8e, 0x3c, 0x6b, 0x13, 0xd2, 0xf2, 0xaa, 0x74, - 0x11, 0x92, 0xbc, 0x86, 0x25, 0xab, 0xd3, 0x0d, 0xb6, 0xe6, 0x64, 0xfb, 0x33, 0x30, - 0xf9, 0x5c, 0xb3, 0x1b, 0xa1, 0x29, 0x0b, 0xd7, 0xf8, 0x30, 0x31, 0xc7, 0x89, 0xc2, - 0x4f, 0xd5, 0x73, 0x93, 0x46, 0x90, 0xa7, 0x3b, 0x54, 0xa9, 0x05, 0xdf, 0x8e, 0x1d, - 0x59, 0x32, 0x2f, 0x26, 0x2b, 0xbf, 0xbe, 0x95, 0xcc, 0x5b, 0x9b, 0x1e, 0x20, 0x31, - 0x0b, 0x76, 0x35, 0x0b, 0x4d, 0x60, 0x4c, 0xd1, 0xa4, 0x58, 0x66, 0x1d, 0xc4, 0x74, - 0xfe, 0x4c, 0x58, 0x79, 0x04, 0xc0, 0x53, 0x47, 0x5e, 0x17, 0x61, 0xb8, 0x0a, 0x60, - 0xcc, 0x48, 0xed, 0xd9, 0x54, 0x34, 0xdf, 0x02, 0x3b, 0x94, 0xa5, 0x8a, 0x99, 0xd6, - 0x25, 0x66, 0xe0, 0x0f, 0x67, 0x77, 0x90, 0xdc, 0xa0, 0x76, 0xa4, 0xf1, 0x67, 0x47, - 0x0c, 0x43, 0xa8, 0x1e, 0x6c, 0x32, 0xf0, 0xd0, 0x0d, 0x23, 0x65, 0x6b, 0xa7, 0x48, - 0x28, 0xb8, 0xe4, 0xd4, 0x75, 0x38, 0xe5, 0x0c, 0x0e, 0xce, 0xe2, 0xcd, 0xfe, 0x0d, - 0x59, 0x43, 0xe2, 0x3e, 0x3f, 0x17, 0x33, 0x82, 0x9d, 0x3e, 0x1b, 0x80, 0x53, 0x93, - 0x30, 0xe0, 0x6c, 0x6a, 0xe3, 0xd0, 0xec, 0xe7, 0x38, 0xc0, 0xdd, 0x74, 0x2a, 0xa5, - 0x86, 0x0f, 0x43, 0xb5, 0x30, 0xf0, 0x3d, 0xc5, 0x5d, 0xeb, 0xf7, 0x20, 0x12, 0x3f, - 0x8f, 0xba, 0xf2, 0xe5, 0x68, 0x59, 0xa5, 0x34, 0x3d, 0x46, 0x12, 0xee, 0x21, 0x46, - 0x4d, 0xb2, 0x50, 0x1d, 0x4f, 0x35, 0x31, 0x47, 0xf3, 0xe1, 0xa5, 0xab, 0xb8, 0x93, - 0x85, 0x08, 0x16, 0xc8, 0x0a, 0xf2, 0x9d, 0x88, 0x92, 0x48, 0xc9, 0x2a, 0x72, 0x9a, - 0x0e, 0x2b, 0xe2, 0xb6, 0x6c, 0xc1, 0x3a, 0xc5, 0xd9, 0x96, 0xb2, 0x50, 0x14, 0x66, - 0x6d, 0xdc, 0x63, 0x8a, 0x1f, 0xd2, 0xa0, 0xaf, 0xee, 0x93, 0xd9, 0x8e, 0x31, 0xdc, - 0x1e, 0xa8, 0x58, 0xd7, 0x2b, 0x84, 0xbb, 0xd3, 0x2f, 0xc0, 0xc6, 0x16, 0xe7, 0xd4, - 0xab, 0xda, 0xf3, 0xc1, 0x8f, 0xf9, 0x60, 0x13, 0x24, 0x5d, 0x83, 0xb3, 0xbd, 0xf9, - 0x21, 0xf4, 0x03, 0xf1, 0xae, 0xcf, 0xdd, 0xd8, 0x85, 0xfd, 0xcf, 0xc7, 0x33, 0x87, - 0x0f, 0x76, 0x0c, 0xb8, 0x7e, 0xd4, 0xfc, 0xd9, 0xcc, 0xa9, 0x33, 0x2e, 0x8e, 0x1c, - 0x85, 0x62, 0x3b, 0x20, 0x66, 0x09, 0xf8, 0x87, 0xeb, 0xdb, 0xcf, 0x9d, 0xa1, 0x0f, - 0x38, 0x14, 0x19, 0x7a, 0x9f, 0x82, 0x07, 0x05, 0xea, 0xa1, 0x28, 0x3a, 0xc7, 0x93, - 0x16, 0x83, 0x08, 0x3f, 0x22, 0xfc, 0x4d, 0xc7, 0xff, 0x68, 0x1a, 0xb8, 0x46, 0x18, - 0x6f, 0x22, 0xd5, 0x73, 0x08, 0x43, 0xde, 0x71, 0x00, 0xf0, 0x31, 0x17, 0xa3, 0xbb, - 0xa0, 0x64, 0xca, 0x3c, 0xea, 0x93, 0xf3, 0xab, 0xd3, 0x0b, 0xe6, 0xdb, 0x09, 0x35, - 0x52, 0x9d, 0xed, 0x0b, 0x50, 0xec, 0xef, 0x9f, 0x59, 0x6d, 0xb0, 0x1a, 0x87, 0xa8, - 0xda, 0xdb, 0x82, 0x7a, 0x1b, 0xe8, 0xb5, 0x79, 0x9b, 0x33, 0xc9, 0x9a, 0x82, 0x2b, - 0x73, 0xf7, 0xe6, 0x62, 0xed, 0x6f, 0x86, 0x03, 0x45, 0xa2, 0x62, 0x83, 0xc1, 0xb4, - 0x08, 0x0e, 0xcd, 0xf5, 0x79, 0xd7, 0x0e, 0x7b, 0x0c, 0x0a, 0xb7, 0x1e, 0x11, 0x6e, - 0xe2, 0xd9, 0xda, 0x27, 0x46, 0x1e, 0x28, 0x12, 0x2a, 0x09, 0xca, 0x04, 0xde, 0x38, - 0x76, 0x50, 0x2f, 0xd2, 0x4d, 0xff, 0x92, 0x09, 0x55, 0x2f, 0x91, 0x13, 0x87, 0x70, - 0x78, 0xa0, 0x94, 0xe0, 0xe5, 0xf8, 0xce, 0xbb, 0x41, 0x54, 0xe0, 0x3a, 0x6b, 0x56, - 0xf6, 0x04, 0xdf, 0x98, 0x4b, 0xd2, 0x9e, 0xfd, 0x4f, 0x88, 0xc3, 0xf6, 0x29, 0xea, - 0x2b, 0xba, 0x91, 0x27, 0xea, 0x5a, 0x6c, 0xc5, 0xa3, 0x9d, 0x74, 0x1e, 0xdd, 0x71, - 0x1a, 0x24, 0x44, 0x7f, 0xe0, 0x6c, 0xf8, 0x45, 0x5a, 0x44, 0x06, 0x5e, 0x24, 0x52, - 0x76, 0x3b, 0x0d, 0x93, 0xf8, 0x6a, 0x31, 0x47, 0xbd, 0x08, 0x75, 0x7a, 0x4f, 0x7a, - 0xa7, 0x79, 0x3c, 0x97, 0x82, 0x1c, 0x2b, 0x57, 0x22, 0xc9, 0xdb, 0xad, 0x20, 0xf6, - 0xa1, 0xe7, 0xad, 0xf6, 0x8b, 0xf2, 0x22, 0x7b, 0xe5, 0x12, 0x04, 0xe9, 0xde, 0xca, - 0x8d, 0x9e, 0xb6, 0x26, 0x6f, 0x65, 0x9b, 0x33, 0x55, 0xc8, 0x97, 0x7e, 0xae, 0x7e, - 0x9e, 0xd5, 0x39, 0xd1, 0x79, 0x39, 0xf0, 0xc6, 0x16, 0x6b, 0x01, 0x13, 0x2d, 0xb0, - 0x01, 0x66, 0x25, 0x0e, 0xa9, 0x64, 0xe3, 0x9d, 0x9d, 0x55, 0xab, 0x43, 0x9a, 0x29, - 0xbb, 0x0b, 0xcf, 0xd3, 0xa9, 0x99, 0xb3, 0x1f, 0xe7, 0xa9, 0x51, 0x00, 0x2e, 0xe5, - 0xdc, 0x01, 0x27, 0x03, 0x24, 0xb1, 0x10, 0x10, 0x37, 0x89, 0x29, 0x42, 0x90, 0x7c, - 0x6e, 0x19, 0x50, 0x9a, 0x6c, 0x5f, 0x66, 0x59, 0xba, 0xf7, 0xf4, 0x36, 0x3c, 0x49, - 0x15, 0xe6, 0x1b, 0xda, 0x34, 0x06, 0x9b, 0xd9, 0x86, 0xb6, 0x37, 0x7f, 0xf6, 0x04, - 0xed, 0xe5, 0xa7, 0x42, 0x5d, 0xb2, 0x88, 0x86, 0xb1, 0xa2, 0x61, 0x36, 0x6d, 0xa8, - 0xa1, 0x39, 0x86, 0x65, 0xbe, 0xed, 0x3b, 0xe9, 0xbc, 0x2e, 0x05, 0x5e, 0x71, 0x1b, - 0x7d, 0x36, 0xdd, 0xbd, 0xd3, 0x65, 0xcc, 0xdc, 0xd7, 0xfc, 0xba, 0xfe, 0x71, 0x29, - 0x66, 0x95, 0x08, 0xda, 0xc0, 0xad, 0x2d, 0x55, 0xee, 0x7f, 0xc6, 0x0b, 0xce, 0x22, - 0x88, 0x50, 0xba, 0x7b, 0x94, 0x3a, 0x8d, 0x50, 0xff, 0xcb, 0x2a, 0x67, 0x06, 0x51, - 0xd3, 0x15, 0xd8, 0x71, 0x9c, 0x7b, 0x57, 0xf6, 0x37, 0xa3, 0x7e, 0xdd, 0x32, 0x6a, - 0xbc, 0x76, 0xf0, 0xa7, 0x69, 0x0c, 0x23, 0x68, 0x80, 0x16, 0x01, 0x07, 0xc2, 0xb4, - 0xc8, 0x5e, 0xcf, 0x2a, 0xd9, 0xf5, 0xdd, 0x26, 0x45, 0x62, 0x6e, 0x40, 0x90, 0xf1, - 0x00, 0x47, 0xcc, 0x13, 0x15, 0x40, 0xca, 0x58, 0x03, 0x04, 0x5a, 0x6a, 0xee, 0x91, - 0xea, 0x0b, 0x3f, 0x9b, 0x77, 0xc4, 0x43, 0x40, 0x69, 0xc5, 0x32, 0x0c, 0xf5, 0xb7, - 0x01, 0x82, 0xd9, 0xfb, 0xbf, 0x30, 0x98, 0x30, 0x60, 0x11, 0x75, 0x9d, 0x0d, 0x64, - 0xa8, 0x84, 0x14, 0x1e, 0xa0, 0x21, 0xcd, 0xd9, 0x5e, 0xfa, 0x32, 0x63, 0xa5, 0x05, - 0xb8, 0x52, 0x29, 0xd1, 0x54, 0xec, 0xaa, 0x23, 0x5e, 0x8f, 0xa1, 0x07, 0x95, 0xc9, - 0xda, 0x27, 0x41, 0xcd, 0x98, 0x71, 0x90, 0x16, 0xa9, 0x01, 0x17, 0xa7, 0x6f, 0x84, - 0xf0, 0x0b, 0x5c, 0x3d, 0x4b, 0xce, 0xd7, 0x9a, 0x73, 0xbf, 0xb3, 0xa1, 0xc7, 0x8a, - 0xd1, 0xad, 0xea, 0x50, 0x78, 0xf2, 0xf1, 0xb0, 0x0f, 0x81, 0x5b, 0xc7, 0xa3, 0x0e, - 0xf8, 0x58, 0x40, 0x07, 0x77, 0x32, 0xdc, 0xb1, 0xa6, 0x1e, 0xd4, 0xbc, 0xbd, 0x66, - 0x35, 0x28, 0x50, 0x29, 0x77, 0x94, 0xad, 0x67, 0xd2, 0x93, 0xdc, 0xe9, 0x10, 0x61, - 0x13, 0x0c, 0xa4, 0x8b, 0xab, 0xca, 0xaa, 0xd6, 0x0b, 0x1f, 0x7c, 0xed, 0x07, 0xac, - 0x3f, 0xf3, 0x32, 0xd5, 0xc8, 0xd3, 0x2b, 0xa2, 0xf1, 0xe7, 0x8a, 0x23, 0xb0, 0x66, - 0x29, 0xb8, 0x89, 0x06, 0x6f, 0x07, 0x9b, 0xcd, 0xa2, 0x9f, 0xb5, 0xc8, 0x3b, 0xbb, - 0xd3, 0x7e, 0xc6, 0x17, 0x3e, 0x8a, 0x74, 0x81, 0x22, 0x12, 0x86, 0x6b, 0xcb, 0x58, - 0x80, 0x5e, 0x9e, 0x70, 0x17, 0x96, 0xa7, 0x23, 0xd5, 0x15, 0xe6, 0x15, 0x33, 0x20, - 0x0b, 0xe0, 0x6b, 0x01, 0x5f, 0xa0, 0x22, 0x35, 0xc8, 0xcb, 0xc9, 0xf3, 0x61, 0x7e, - 0xe8, 0x19, 0x5f, 0xe1, 0xbc, 0xf5, 0xbb, 0x1b, 0x63, 0x4c, 0xd4, 0x3f, 0x62, 0xea, - 0x93, 0xa4, 0x6d, 0x88, 0xf2, 0xfc, 0xbc, 0x3e, 0x28, 0x40, 0x84, 0xe7, 0x04, 0xfb, - 0x1d, 0x7d, 0x0d, 0x9a, 0xcb, 0x91, 0x96, 0x1e, 0x2e, 0xeb, 0xe2, 0xdc, 0x9e, 0xbe, - 0x36, 0x5b, 0x25, 0xb5, 0x66, 0x75, 0x97, 0x3d, 0x0c, 0x38, 0xf4, 0x76, 0x30, 0x57, - 0x47, 0x23, 0xcd, 0x3e, 0xc6, 0x6c, 0x8f, 0x3b, 0x12, 0x82, 0x21, 0xa7, 0x90, 0xd9, - 0x2c, 0x89, 0x5b, 0x94, 0x27, 0x0f, 0xe9, 0x40, 0x51, 0xa1, 0x70, 0xe9, 0x5b, 0x8b, - 0xe7, 0x16, 0x34, 0x86, 0xec, 0x8c, 0x0b, 0xee, 0xbe, 0xf6, 0x5e, 0x16, 0x26, 0xb0, - 0x46, 0xd7, 0xe7, 0xf8, 0x26, 0x37, 0x2b, 0x6a, 0xa1, 0x0b, 0xae, 0xfb, 0x84, 0x8f, - 0xa1, 0xdf, 0x6b, 0xb1, 0xdc, 0x43, 0x95, 0x40, 0xf6, 0x3c, 0x9c, 0x7a, 0x9d, 0x5f, - 0x88, 0x13, 0x40, 0x29, 0x62, 0x65, 0x1e, 0xe9, 0x84, 0x39, 0x02, 0xb6, 0xc3, 0x98, - 0x2d, 0xce, 0x50, 0xa6, 0x17, 0x8a, 0x55, 0xa1, 0xad, 0xc0, 0x1c, 0xe7, 0xdc, 0x6c, - 0x83, 0x38, 0xe1, 0xa9, 0xce, 0xef, 0xc1, 0x78, 0xdc, 0x43, 0x14, 0xf6, 0x74, 0x9a, - 0x81, 0xa7, 0x31, 0xee, 0x3c, 0x7f, 0xc0, 0xc3, 0x5d, 0x1c, 0xe3, 0x63, 0xce, 0xf1, - 0x13, 0x28, 0xf3, 0x87, 0xc4, 0x01, 0xfe, 0xf2, 0x7a, 0x67, 0xa6, 0x29, 0x2f, 0x6f, - 0x72, 0xb0, 0xa1, 0xd6, 0xc3, 0x89, 0x16, 0x2d, 0x16, 0x2e, 0xf0, 0x50, 0xae, 0x5f, - 0x3d, 0xdb, 0xb5, 0x5c, 0xaa, 0xbc, 0xa9, 0xa1, 0xbe, 0x89, 0xb4, 0x63, 0x49, 0x4d, - 0x74, 0x39, 0xfb, 0x56, 0x47, 0xa9, 0x18, 0x12, 0x8b, 0x96, 0x25, 0xd3, 0x3e, 0xac, - 0xa6, 0x19, 0xd5, 0x2f, 0x03, 0x5f, 0xe6, 0x08, 0x9c, 0xe8, 0xd8, 0xb9, 0x0f, 0xe3, - 0x67, 0x0d, 0x8c, 0x5a, 0x2e, 0x3e, 0x05, 0x49, 0x69, 0xa3, 0xd9, 0x7e, 0x61, 0xb5, - 0xe6, 0x30, 0x67, 0x4f, 0xc7, 0x08, 0x57, 0xf1, 0xbb, 0xf1, 0x0f, 0xdc, 0x40, 0x49, - 0xef, 0xf5, 0x60, 0xeb, 0xa5, 0xf2, 0x2a, 0xcc, 0x8d, 0x77, 0xdb, 0xee, 0x0b, 0x20, - 0x55, 0x7f, 0xa4, 0xd0, 0x33, 0x31, 0x72, 0xcb, 0xb5, 0xcb, 0xcc, 0x2b, 0x13, 0x5f, - 0x2c, 0xcd, 0xe0, 0x14, 0xe6, 0x3e, 0xbe, 0x4e, 0xdf, 0x92, 0x5e, 0x61, 0xba, 0x2a, - 0x32, 0x0c, 0xd3, 0x99, 0x91, 0x5a, 0xdd, 0xfc, 0xeb, 0x1a, 0xd0, 0x69, 0xa9, 0xfd, - 0x5b, 0x62, 0x10, 0xa4, 0xb6, 0xe5, 0x04, 0x52, 0xb1, 0xf9, 0x06, 0xdd, 0x16, 0xf0, - 0x16, 0x68, 0xf0, 0xaf, 0x56, 0x6a, 0x28, 0x7c, 0xce, 0xfc, 0xd8, 0x94, 0x73, 0x41, - 0x85, 0x9a, 0xe7, 0xdc, 0x3a, 0x06, 0xf6, 0xbf, 0x15, 0x74, 0xfe, 0xb9, 0x31, 0xf9, - 0x27, 0xe2, 0xd5, 0x05, 0xf6, 0x08, 0x59, 0x9e, 0x23, 0xb0, 0x5a, 0xf7, 0xc3, 0x23, - 0x69, 0x83, 0x97, 0xa8, 0x01, 0xdc, 0x7f, 0x78, 0x82, 0x5c, 0xc7, 0xeb, 0x9f, 0xcc, - 0xe6, 0xc6, 0xc4, 0xf8, 0xf6, 0x88, 0x39, 0xd3, 0x0a, 0xc5, 0x67, 0x14, 0x8e, 0x70, - 0x84, 0xdb, 0x2b, 0x37, 0x58, 0x30, 0xa0, 0x7b, 0x30, 0x5f, 0xed, 0xd6, 0x07, 0xa3, - 0x47, 0xfa, 0x65, 0xde, 0xf0, 0x1d, 0x4e, 0x1f, 0xd6, 0xc1, 0x6b, 0x4b, 0x47, 0xf5, - 0xb0, 0x1b, 0x43, 0x65, 0xb7, 0x72, 0x26, 0xe6, 0x0f, 0xdd, 0x40, 0xf2, 0x2a, 0x39, - 0x5a, 0xa2, 0x35, 0xf0, 0xdf, 0xda, 0x8f, 0xb4, 0xd3, 0xde, 0x65, 0xb0, 0xcf, 0x4f, - 0x4c, 0x22, 0x0b, 0x3b, 0x4a, 0x9e, 0x32, 0xbc, 0x0d, 0xb6, 0x4f, 0x16, 0x2c, 0x07, - 0xdf, 0x42, 0xa1, 0x01, 0x99, 0x03, 0xa6, 0x7c, 0xda, 0x69, 0x3d, 0xde, 0xb5, 0xca, - 0x39, 0xa0, 0xfe, 0x50, 0x08, 0x50, 0xec, 0x7c, 0x06, 0xbe, 0xe7, 0x18, 0x66, 0xb3, - 0x55, 0xcc, 0xbc, 0x07, 0x8c, 0xd4, 0xdc, 0x03, 0x6f, 0xda, 0xa8, 0x1c, 0xb2, 0xde, - 0x99, 0xcc, 0x88, 0xf6, 0x0a, 0x49, 0x46, 0x42, 0x87, 0xf5, 0x9f, 0xc7, 0x14, 0x8b, - 0x1a, 0xfb, 0x4a, 0x2f, 0x9b, 0xb8, 0x97, 0x14, 0xe1, 0xeb, 0x8c, 0x03, 0x61, 0xe5, - 0x99, 0x2a, 0x5b, 0x79, 0xcd, 0xbb, 0x91, 0xd9, 0xbf, 0x29, 0xeb, 0x59, 0x8c, 0xbb, - 0x4b, 0xda, 0x92, 0x3d, 0x26, 0x7f, 0xea, 0xcb, 0x91, 0xce, 0x72, 0xd6, 0x1a, 0xb1, - 0xea, 0x00, 0xf5, 0x6a, 0xa6, 0x76, 0x6e, 0xab, 0xc4, 0x7d, 0xca, 0xa6, 0x9a, 0x02, - 0x4b, 0xbf, 0xf2, 0xf2, 0x96, 0x91, 0x7f, 0x17, 0xa3, 0xf8, 0xc9, 0x3e, 0x1b, 0xf2, - 0x9c, 0x3c, 0xfc, 0x99, 0x1a, 0x2b, 0xe8, 0xcf, 0xa7, 0x0e, 0x5d, 0xe3, 0xf2, 0xdd, - 0x52, 0xa7, 0x55, 0x01, 0x38, 0x68, 0x7a, 0xec, 0x28, 0x92, 0x6f, 0xa1, 0x68, 0xb1, - 0x81, 0xdb, 0x72, 0x82, 0xbd, 0x60, 0xda, 0xd3, 0x31, 0x0d, 0xfe, 0x54, 0x2c, 0xeb, - 0xe6, 0x94, 0x74, 0x00, 0x25, 0xc7, 0xec, 0x2a, 0x20, 0x43, 0xfe, 0xbb, 0x77, 0x9f, - 0x7f, 0x37, 0x89, 0xa5, 0xe2, 0x42, 0xdb, 0x48, 0x03, 0xee, 0x36, 0x72, 0x52, 0xc4, - 0x63, 0xc9, 0xa8, 0x8b, 0x41, 0x7b, 0x70, 0x86, 0x6d, 0x9a, 0xfb, 0x7a, 0x08, 0x27, - 0x68, 0x01, 0xf9, 0x22, 0x7c, 0x63, 0x81, 0xf1, 0x5c, 0xc0, 0x94, 0xac, 0x7b, 0xd1, - 0x54, 0xa4, 0xce, 0xf9, 0x0b, 0x48, 0x47, 0xdc, 0x16, 0x8a, 0x01, 0xf1, 0xe3, 0x1e, - 0xec, 0x74, 0xa7, 0xef, 0xce, 0xba, 0x11, 0xf5, 0x07, 0x69, 0xf5, 0xd8, 0xf5, 0x4d, - 0x36, 0x20, 0xc2, 0x3e, 0xc8, 0x99, 0x3f, 0x7a, 0xef, 0x27, 0xc1, 0xd3, 0x51, 0x96, - 0xb1, 0x02, 0xb3, 0xcf, 0x3f, 0xed, 0x8b, 0xf8, 0x5d, 0x8a, 0x45, 0xf6, 0x96, 0x83, - 0xec, 0xdd, 0x1a, 0x23, 0x44, 0xef, 0xb8, 0x48, 0x07, 0xd9, 0x0f, 0x18, 0x35, 0xb4, - 0xf2, 0xf2, 0x4d, 0x8f, 0xf8, 0x12, 0x30, 0x47, 0xeb, 0x9f, 0x7d, 0x30, 0x62, 0x3e, - 0x14, 0x29, 0x0d, 0x56, 0x17, 0x96, 0x3b, 0x42, 0x21, 0x40, 0x4a, 0xe7, 0x61, 0xc8, - 0x6b, 0xec, 0x7a, 0x07, 0xbf, 0x81, 0xa0, 0xb9, 0xa7, 0xf7, 0xd0, 0x87, 0xac, 0x26, - 0xce, 0x3d, 0xfa, 0x9c, 0x93, 0xfe, 0xea, 0xeb, 0xd1, 0x0d, 0xc1, 0x88, 0xc6, 0x27, - 0xd4, 0xb9, 0x1d, 0x2a, 0x79, 0x01, 0xee, 0x5a, 0x1b, 0x38, 0x4d, 0xa3, 0x6e, 0x78, - 0x95, 0xd5, 0xb7, 0x78, 0x21, 0xfe, 0x6b, 0xca, 0xa3, 0xaf, 0xe8, 0xf2, 0x3a, 0x96, - 0x8f, 0xc9, 0xab, 0xa3, 0x7b, 0x1a, 0x4e, 0x25, 0xf5, 0xdb, 0xa1, 0xd1, 0x42, 0xff, - 0x11, 0xff, 0xd7, 0xa1, 0xba, 0x41, 0xef, 0x82, 0xc6, 0x2a, 0x95, 0x94, 0x66, 0xe7, - 0x11, 0x2a, 0xf7, 0x79, 0x6d, 0x47, 0x67, 0x12, 0x69, 0xad, 0xd3, 0xee, 0x2b, 0x17, - 0x21, 0x3e, 0xc3, 0xbd, 0x51, 0x30, 0x24, 0x4c, 0xb9, 0x07, 0x0e, 0xa8, 0xcf, 0xa4, - 0x6d, 0x44, 0xf2, 0xfc, 0xd9, 0x64, 0x06, 0x37, 0xf7, 0xde, 0xfd, 0x50, 0xb6, 0xdc, - 0x93, 0x60, 0x19, 0x45, 0xb9, 0x31, 0xe8, 0xbb, 0x72, 0x67, 0x1f, 0xe4, 0xb4, 0xb5, - 0x88, 0xc9, 0x0a, 0xd5, 0xc0, 0x0b, 0x55, 0xdc, 0x8c, 0x8a, 0xf9, 0xb0, 0xf6, 0xa3, - 0xca, 0x1e, 0x07, 0xef, 0xf1, 0x58, 0x11, 0x39, 0x1c, 0x53, 0xf7, 0xe4, 0x3b, 0x1b, - 0x81, 0x16, 0xda, 0xdc, 0x01, 0x6d, 0x19, 0x26, 0xc8, 0x48, 0x0d, 0x4e, 0xe3, 0x4e, - 0x76, 0x19, 0x1b, 0x79, 0xbe, 0xd0, 0xce, 0x95, 0x97, 0x3a, 0x4c, 0x7c, 0xf2, 0xf0, - 0x57, 0xc7, 0x14, 0x7e, 0xdb, 0x01, 0x3d, 0x20, 0x5d, 0x81, 0xe2, 0x36, 0x08, 0x88, - 0xa2, 0xab, 0xdd, 0xcc, 0xf0, 0xf6, 0xf3, 0xd8, 0xf8, 0xba, 0x11, 0x1d, 0x64, 0x2c, - 0x52, 0xd0, 0x4e, 0xbd, 0x3c, 0xe1, 0x7c, 0x60, 0xd9, 0x22, 0x57, 0xea, 0x58, 0x69, - 0x09, 0x45, 0x01, 0xbb, 0x67, 0x12, 0x68, 0xb2, 0x24, 0x47, 0x7a, 0x8e, 0x01, 0x41, - 0xd6, 0xff, 0x37, 0xe2, 0x4f, 0xf1, 0xc7, 0x65, 0xe8, 0x4d, 0x26, 0x4d, 0xb8, 0x8f, - 0x00, 0x92, 0x8e, 0x64, 0xc4, 0x12, 0xbd, 0x59, 0x15, 0x1a, 0x65, 0x71, 0xc6, 0x67, - 0x09, 0x16, 0xb0, 0x70, 0x6b, 0x04, 0x4f, 0xc5, 0xc2, 0xbd, 0x93, 0xad, 0xe3, 0x96, - 0x79, 0x57, 0xcd, 0xb9, 0x41, 0x27, 0x4c, 0xc6, 0xbd, 0xb4, 0xe0, 0x36, 0xb7, 0x67, - 0xb9, 0x50, 0xc0, 0x9e, 0x46, 0x26, 0xa1, 0xd0, 0x05, 0xbc, 0xf4, 0x83, 0x6e, 0xf6, - 0xa1, 0xde, 0x48, 0x09, 0x5d, 0xcb, 0x46, 0x12, 0x78, 0xb1, 0x6c, 0x45, 0x68, 0x90, - 0xb2, 0x3d, 0x40, 0xbd, 0x36, 0x04, 0x10, 0xf0, 0x01, 0x0a, 0x55, 0xf5, 0x05, 0xfe, - 0x5e, 0x2d, 0xb2, 0x01, 0xc7, 0x52, 0xe9, 0xb5, 0xb1, 0x5b, 0xf8, 0xaa, 0x9e, 0x82, - 0xd6, 0x49, 0xab, 0x11, 0x73, 0xba, 0x2a, 0x51, 0x32, 0xe0, 0xcc, 0x50, 0x51, 0xcc, - 0xf7, 0x4c, 0x7a, 0x6a, 0x37, 0x07, 0xab, 0x59, 0x83, 0xf7, 0xcc, 0x27, 0x5c, 0x99, - 0x1a, 0xbe, 0x4d, 0x7c, 0xee, 0x5f, 0x28, 0x9e, 0xfe, 0x72, 0x7e, 0xb3, 0xda, 0x86, - 0xfa, 0x21, 0xa2, 0x8d, 0x6b, 0x8a, 0x2a, 0xff, 0xd4, 0x2d, 0xb9, 0x8b, 0xb2, 0xa4, - 0x6c, 0xd8, 0xa3, 0x29, 0x31, 0x2f, 0xa9, 0x45, 0x39, 0xd9, 0xcb, 0x35, 0xdc, 0xb6, - 0x04, 0x67, 0x8b, 0x63, 0x90, 0x64, 0xd9, 0x20, 0x05, 0xdf, 0x2d, 0x10, 0x68, 0x1c, - 0x64, 0xb9, 0xed, 0x8c, 0xe4, 0x7d, 0x7e, 0xba, 0x0f, 0x2b, 0x50, 0x2b, 0x20, 0x6a, - 0xd4, 0xb2, 0xe9, 0x2b, 0xbe, 0x45, 0x86, 0xf6, 0xd7, 0x50, 0x9e, 0x57, 0xa6, 0x37, - 0x7f, 0xea, 0xbe, 0x38, 0xb3, 0xcc, 0x6c, 0x95, 0x5d, 0x5e, 0x7b, 0xdf, 0x7e, 0xb1, - 0x32, 0xd8, 0x6b, 0xc0, 0x7a, 0x30, 0x98, 0xb4, 0x13, 0xe4, 0x40, 0x5d, 0xaa, 0xa2, - 0x55, 0x29, 0x1d, 0x55, 0x2b, 0x2c, 0x80, 0x07, 0xbe, 0xd4, 0x1e, 0x22, 0xf1, 0xcf, - 0x79, 0x11, 0x82, 0x12, 0x00, 0x55, 0x5e, 0x9c, 0x4f, 0xfb, 0x09, 0xef, 0xc1, 0x22, - 0x38, 0x11, 0x75, 0x03, 0x1c, 0x38, 0x28, 0x0b, 0x53, 0x26, 0xeb, 0xbe, 0xaf, 0x33, - 0x4f, 0xdc, 0xf0, 0xdc, 0x44, 0x4e, 0x62, 0x9f, 0x93, 0x95, 0x51, 0x54, 0x0b, 0xcb, - 0xbb, 0xb1, 0xab, 0x9c, 0x23, 0x1a, 0x86, 0x6b, 0x32, 0x9e, 0x85, 0x24, 0xab, 0x25, - 0xf9, 0x3e, 0x5e, 0x33, 0x4a, 0x05, 0x27, 0x2a, 0x3f, 0x82, 0x6f, 0x9d, 0x05, 0xa4, - 0x50, 0x58, 0xdf, 0xcd, 0xf6, 0x88, 0x43, 0xa8, 0xb9, 0x36, 0xa0, 0xcf, 0x5e, 0x6a, - 0xa8, 0xae, 0x1b, 0x80, 0xf6, 0x01, 0x61, 0xbf, 0x41, 0x4f, 0x28, 0x02, 0x11, 0x11, - 0x09, 0x21, 0xa9, 0xc8, 0x5f, 0x51, 0x04, 0xa0, 0x16, 0x8e, 0x8e, 0x72, 0xde, 0x4f, - 0x8a, 0xa0, 0x41, 0x32, 0xeb, 0x25, 0x88, 0x76, 0xf1, 0x9d, 0x7b, 0xe5, 0xf2, 0xdd, - 0x2b, 0x0b, 0x30, 0x4b, 0x92, 0x3b, 0x29, 0x52, 0xd9, 0x1f, 0xde, 0xe7, 0xe5, 0x52, - 0x05, 0xdb, 0xb1, 0x94, 0xeb, 0xba, 0x32, 0x2f, 0xdc, 0x67, 0xb2, 0x52, 0x2c, 0x92, - 0x61, 0x21, 0xc7, 0xfa, 0x1a, 0xf1, 0x7e, 0xd0, 0x6c, 0x47, 0x27, 0x8f, 0x96, 0x08, - 0x92, 0x96, 0x08, 0x7a, 0x70, 0x4b, 0x7d, 0x0f, 0x84, 0x7d, 0x51, 0xd6, 0xcc, 0x68, - 0xac, 0xc5, 0x22, 0x07, 0x74, 0x73, 0x41, 0xf6, 0xb9, 0x8c, 0xb1, 0xcd, 0x4f, 0xaf, - 0xcd, 0x2b, 0xb0, 0xd0, 0x5b, 0xc7, 0x9b, 0xb8, 0x0d, 0x7c, 0x4b, 0x8a, 0x1a, 0x11, - 0xbc, 0x0a, 0x3b, 0xde, 0xca, 0x45, 0x41, 0x86, 0x9b, 0x4d, 0xc9, 0xd6, 0xb4, 0x8c, - 0xd7, 0x86, 0x9b, 0xf7, 0x63, 0xb9, 0xdc, 0x42, 0x45, 0x27, 0x3c, 0x70, 0x4b, 0x0d, - 0x8d, 0xec, 0x4b, 0x85, 0xd1, 0x6d, 0xd4, 0x38, 0xce, 0xd6, 0x22, 0x0f, 0xa6, 0x69, - 0x26, 0x66, 0x3f, 0xcc, 0x22, 0x8f, 0xc6, 0xc4, 0xd2, 0x7e, 0x17, 0xe3, 0x27, 0x83, - 0x4b, 0x67, 0x57, 0x91, 0x4d, 0x1b, 0xcb, 0xf3, 0x4b, 0x65, 0xd8, 0x58, 0xab, 0x8b, - 0x5c, 0x12, 0x0c, 0xb0, 0x85, 0x05, 0x22, 0xf5, 0x42, 0x89, 0x3f, 0xdd, 0xb1, 0x79, - 0xe8, 0x7f, 0x83, 0x2d, 0xaa, 0xa1, 0x52, 0xc8, 0x31, 0xf1, 0x35, 0x64, 0x00, 0x9c, - 0x41, 0x81, 0x23, 0x53, 0x3d, 0xe2, 0xc6, 0x79, 0x49, 0xe3, 0xaf, 0x2d, 0xcb, 0x60, - 0xd6, 0xbd, 0xbd, 0xda, 0xda, 0x63, 0xa3, 0x0b, 0x4b, 0x54, 0xcd, 0x1c, 0xe5, 0xa5, - 0xa0, 0x0f, 0x8e, 0x85, 0x57, 0xeb, 0xa9, 0x23, 0x4e, 0x81, 0x17, 0x8d, 0x0f, 0xca, - 0xb5, 0x61, 0x0f, 0xba, 0x96, 0x69, 0xcf, 0xeb, 0x1b, 0xd0, 0x8c, 0xd9, 0x65, 0x33, - 0x49, 0x8b, 0x27, 0x2c, 0x57, 0x79, 0xa9, 0xf9, 0x39, 0x69, 0x1d, 0xe1, 0xad, 0x88, - 0x1c, 0x80, 0x87, 0x8d, 0x6c, 0x29, 0x42, 0x15, 0x23, 0x0b, 0xbb, 0x61, 0x90, 0x69, - 0xb4, 0xdc, 0x17, 0xb3, 0xe5, 0x9d, 0xbd, 0x24, 0x2c, 0xd8, 0x8e, 0xcc, 0x3b, 0xe3, - 0xa2, 0x69, 0x6b, 0xf7, 0xf2, 0xd9, 0xe5, 0xb8, 0xc1, 0x52, 0xcc, 0x0d, 0x99, 0xa0, - 0xa5, 0xe9, 0xa3, 0x8b, 0x1b, 0x8e, 0xb1, 0xa0, 0x13, 0xeb, 0x76, 0x51, 0x33, 0x37, - 0xa7, 0xb0, 0xda, 0xdb, 0x4e, 0x81, 0x7b, 0x6f, 0x49, 0x78, 0x02, 0xbd, 0x47, 0xe9, - 0x3a, 0x82, 0x0c, 0x4f, 0xad, 0x6c, 0x65, 0x09, 0x74, 0x42, 0xb9, 0xca, 0xc1, 0x61, - 0xb6, 0x4d, 0x0f, 0xcb, 0xfb, 0xf5, 0x4f, 0xc3, 0x04, 0xc9, 0xb7, 0x0c, 0x74, 0xfb, - 0xb0, 0xd7, 0x05, 0xc7, 0x4d, 0x56, 0xac, 0xb2, 0xfe, 0x6c, 0x91, 0x74, 0xcc, 0x00, - 0xea, 0xbe, 0xa0, 0xe5, 0x97, 0x0d, 0xff, 0xe3, 0x2c, 0xb6, 0x12, 0x92, 0x05, 0x3d, - 0xb8, 0x6d, 0x36, 0x6b, 0x7e, 0x6b, 0x30, 0x13, 0xd1, 0x4b, 0x20, 0x5f, 0xb4, 0x5d, - 0x06, 0x7e, 0x37, 0x50, 0x2e, 0x37, 0x9c, 0x4a, 0xa1, 0x38, 0xbe, 0xc2, 0xc6, 0xbd, - 0x33, 0x1f, 0x58, 0xe9, 0xaa, 0x10, 0x09, 0xb0, 0x66, 0xdc, 0xe9, 0x9a, 0xcc, 0x1d, - 0xc5, 0xa6, 0x3a, 0x8f, 0x75, 0xd1, 0x98, 0x22, 0x7c, 0x2f, 0xbd, 0x20, 0xd5, 0x34, - 0xf1, 0x20, 0x30, 0xc4, 0x00, 0x99, 0xd8, 0x77, 0xca, 0xbe, 0x81, 0xb0, 0x87, 0x50, - 0xe3, 0xfb, 0xfe, 0x63, 0x12, 0xf6, 0x38, 0x0b, 0x98, 0xfb, 0x85, 0x0a, 0x2a, 0x14, - 0x2b, 0x91, 0x4a, 0xdc, 0x71, 0x54, 0x47, 0xc5, 0x79, 0x1a, 0x1b, 0x67, 0xae, 0x65, - 0x6c, 0xad, 0xdd, 0x21, 0xe1, 0xb4, 0x6d, 0xc9, 0xa7, 0x64, 0x12, 0x7b, 0xc0, 0xa3, - 0x01, 0xb4, 0x80, 0x04, 0xa9, 0xc5, 0x27, 0x6b, 0xcf, 0x08, 0xe7, 0xfe, 0x4a, 0xe5, - 0x2d, 0x76, 0xe4, 0x31, 0x48, 0x8a, 0x5b, 0x9d, 0x43, 0x1f, 0xa1, 0x36, 0x34, 0x6e, - 0x5a, 0x53, 0xab, 0x3f, 0x68, 0x12, 0xf2, 0xd9, 0x70, 0xf7, 0xb3, 0x98, 0x98, 0xcf, - 0x8b, 0x62, 0xf2, 0xdb, 0xf6, 0x1e, 0x99, 0xa2, 0x91, 0x5d, 0xfb, 0x75, 0xae, 0x22, - 0xb7, 0x9f, 0x84, 0xcf, 0x25, 0x97, 0xeb, 0x34, 0xec, 0x3d, 0x29, 0x2e, 0x6b, 0x5d, - 0x84, 0xeb, 0xac, 0x4d, 0x92, 0xde, 0x52, 0xe1, 0xf8, 0xbf, 0x6b, 0xfd, 0xba, 0xda, - 0x63, 0x44, 0x09, 0xf2, 0x0e, 0xf2, 0xcc, 0x6e, 0x3c, 0x39, 0x0e, 0x43, 0x5f, 0x47, - 0xe3, 0x47, 0x23, 0x8d, 0xb4, 0x86, 0x90, 0x84, 0x04, 0x73, 0xb0, 0xa0, 0x83, 0x1a, - 0x5a, 0x8a, 0x58, 0xc4, 0xdc, 0xfc, 0x4e, 0xab, 0x7b, 0x41, 0x8c, 0xba, 0x2a, 0x41, - 0x4f, 0x95, 0x57, 0x71, 0x90, 0xff, 0x88, 0xd7, 0x27, 0xf7, 0x3e, 0x2f, 0xff, 0x97, - 0xaa, 0xbd, 0x11, 0x14, 0xb7, 0x64, 0xe3, 0xed, 0xbc, 0x18, 0x3e, 0x60, 0x3a, 0xcf, - 0xb7, 0xc0, 0x9b, 0xf1, 0x32, 0xbb, 0x01, 0xef, 0xc7, 0x17, 0x8d, 0x4f, 0x9a, 0x2d, - 0xba, 0xf4, 0x92, 0x4f, 0xd8, 0x0f, 0xbe, 0x0e, 0x60, 0x4f, 0x60, 0x39, 0x08, 0x32, - 0xeb, 0x98, 0x04, 0x79, 0xe0, 0x4e, 0x9c, 0x9a, 0x2b, 0xb2, 0xfb, 0x36, 0x84, 0xd8, - 0xf8, 0x06, 0x48, 0xd5, 0x80, 0x78, 0x38, 0x54, 0x58, 0x4f, 0x62, 0xbe, 0x0c, 0xc9, - 0x21, 0x88, 0x32, 0x38, 0x56, 0x10, 0xd9, 0x62, 0x36, 0x5f, 0x50, 0x71, 0xfa, 0x3d, - 0x36, 0x8f, 0xfb, 0x67, 0x1b, 0xa2, 0xc2, 0xf9, 0xa0, 0xfc, 0x68, 0xd8, 0x07, 0x22, - 0x19, 0xa7, 0x7b, 0xef, 0x2d, 0x6b, 0x4a, 0x19, 0xf1, 0x6d, 0xd5, 0x30, 0x74, 0x22, - 0x47, 0x46, 0xbb, 0xa5, 0xf1, 0x72, 0x82, 0x20, 0xb1, 0x96, 0xe4, 0x0f, 0x93, 0x7c, - 0x47, 0x05, 0x42, 0x9d, 0x04, 0xaa, 0x3c, 0x50, 0x5c, 0x95, 0x60, 0x3e, 0x05, 0xff, - 0x55, 0x2e, 0xc1, 0x86, 0x42, 0xd5, 0x67, 0x05, 0x02, 0x67, 0xb9, 0xf9, 0x92, 0x9c, - 0x2e, 0x13, 0x80, 0x14, 0xb5, 0xef, 0x1b, 0xa7, 0x1d, 0x9a, 0x71, 0x86, 0xe3, 0xd1, - 0x3c, 0x8a, 0x8e, 0x40, 0x8c, 0x2a, 0x9d, 0x12, 0x01, 0xa7, 0xfe, 0xbb, 0x83, 0x34, - 0x51, 0x2b, 0x44, 0xb8, 0x2b, 0xb2, 0x01, 0x78, 0x9f, 0x63, 0x58, 0x04, 0x89, 0x6e, - 0x3e, 0xb2, 0x1b, 0x5b, 0xd8, 0xc4, 0x21, 0xf0, 0xb4, 0xcf, 0xba, 0x04, 0xde, 0x92, - 0x52, 0x8f, 0x04, 0xfb, 0x4b, 0x52, 0x6b, 0x73, 0x7e, 0xe3, 0x2d, 0xa8, 0x63, 0xf5, - 0x98, 0x45, 0x61, 0x31, 0x98, 0x3a, 0x01, 0x35, 0x8f, 0xb0, 0x7d, 0xe6, 0x75, 0x21, - 0x11, 0x58, 0x5a, 0x86, 0x25, 0x6c, 0xe0, 0x34, 0xc0, 0xd8, 0x57, 0x5a, 0x42, 0x76, - 0x13, 0x61, 0xb1, 0x18, 0x77, 0x05, 0x0b, 0xc6, 0xaf, 0xc3, 0x16, 0x15, 0x64, 0xe9, - 0x6f, 0xd8, 0xcf, 0x04, 0x8f, 0xeb, 0xeb, 0x2a, 0x92, 0x20, 0x07, 0x1c, 0xff, 0x18, - 0x2d, 0x6c, 0xa0, 0x37, 0xce, 0x2c, 0x2d, 0xed, 0x91, 0x6b, 0xd7, 0xb8, 0x4d, 0xe2, - 0x8a, 0xc0, 0x17, 0x1d, 0x97, 0xfc, 0x24, 0x95, 0x6c, 0x26, 0x66, 0x69, 0xc1, 0x03, - 0x6b, 0x2b, 0x1a, 0x23, 0xda, 0xbc, 0xf3, 0x4e, 0x38, 0xf3, 0x51, 0x45, 0x12, 0xae, - 0x8a, 0x47, 0xb3, 0x53, 0xb4, 0x16, 0x69, 0x96, 0x75, 0xe4, 0xd3, 0x1a, 0x2f, 0xe0, - 0x34, 0x08, 0xe4, 0x24, 0xa7, 0x82, 0x9a, 0x06, 0xad, 0xe6, 0x36, 0x53, 0x61, 0xd8, - 0xa9, 0x61, 0x25, 0x7c, 0xbe, 0x25, 0xb0, 0xcd, 0xe3, 0x3e, 0x96, 0x48, 0x77, 0xdf, - 0x5e, 0x57, 0xc5, 0x3d, 0xb2, 0x83, 0x51, 0x77, 0x34, 0x3e, 0x2d, 0x87, 0x6d, 0x51, - 0x4c, 0x62, 0xfb, 0xb3, 0xb4, 0xa7, 0x08, 0xce, 0x62, 0x62, 0x05, 0xcc, 0xf9, 0x2f, - 0x24, 0x0d, 0x60, 0x2c, 0xdb, 0x5d, 0x68, 0x41, 0xfd, 0x29, 0xda, 0x63, 0x08, 0xb6, - 0xca, 0x40, 0x97, 0xd8, 0x52, 0x54, 0x10, 0x46, 0x54, 0x52, 0x23, 0x9b, 0x04, 0x51, - 0xa8, 0xdb, 0xed, 0xac, 0x1e, 0x41, 0xed, 0xdd, 0x0f, 0x6b, 0xe0, 0xe3, 0xd8, 0x89, - 0x69, 0x07, 0x03, 0xa3, 0x14, 0x57, 0x07, 0xe0, 0xb3, 0xf5, 0xdb, 0x91, 0xb8, 0x19, - 0x37, 0x56, 0xe0, 0xe3, 0x47, 0xb6, 0x64, 0xa1, 0xcc, 0xcb, 0xd7, 0x86, 0x9a, 0x40, - 0x22, 0xea, 0xdf, 0x3f, 0x87, 0x3c, 0x10, 0xec, 0xab, 0x9a, 0x93, 0xf2, 0xca, 0xdc, - 0xa7, 0xa3, 0x33, 0xb8, 0x1b, 0xb6, 0x10, 0x4e, 0x82, 0xea, 0x14, 0xfe, 0x74, 0x1e, - 0xb0, 0x62, 0x08, 0x0d, 0xc8, 0x5a, 0xcb, 0xc8, 0xcc, 0x3a, 0x9b, 0xc8, 0x0c, 0x03, - 0xd9, 0x1f, 0xfb, 0x3c, 0x25, 0xf9, 0xe4, 0x2b, 0xc2, 0x5c, 0xf7, 0x7d, 0x73, 0x90, - 0xc3, 0xab, 0xaf, 0x26, 0x10, 0xf4, 0xec, 0xdb, 0x01, 0x9b, 0x15, 0x8d, 0xa2, 0x15, - 0x5b, 0xef, 0xec, 0xb9, 0xc2, 0x29, 0x6d, 0x03, 0xf8, 0x23, 0xea, 0xac, 0x0c, 0x74, - 0x0d, 0x2a, 0x44, 0x89, 0xb8, 0x28, 0x4c, 0x7e, 0x7b, 0x3a, 0x72, 0x9a, 0xfb, 0x69, - 0xbd, 0x5b, 0xfa, 0x5f, 0x62, 0xf9, 0xb5, 0x27, 0x37, 0x97, 0xdd, 0x24, 0xa0, 0x18, - 0x30, 0x7f, 0xc6, 0x20, 0xe6, 0x42, 0xaa, 0x27, 0xe7, 0x50, 0x6e, 0x17, 0xb1, 0x98, - 0xdc, 0xa4, 0x79, 0x0e, 0x8d, 0xe1, 0xbf, 0xb6, 0x71, 0xd8, 0xdc, 0x75, 0x13, 0x91, - 0x0e, 0x95, 0x43, 0x10, 0x72, 0x1b, 0x4f, 0xb5, 0x37, 0x33, 0xc9, 0x18, 0xf0, 0xd1, - 0x89, 0x85, 0x18, 0x89, 0x62, 0x73, 0x22, 0xd5, 0x20, 0xca, 0xcc, 0x9d, 0xd7, 0x03, - 0x6b, 0xb4, 0x39, 0xa1, 0x69, 0xef, 0x2c, 0xdd, 0x6c, 0xdb, 0xae, 0xa5, 0xa9, 0x1b, - 0xc2, 0x4a, 0xb3, 0xfc, 0xa1, 0x57, 0x4c, 0x12, 0xc9, 0x31, 0xe7, 0xaa, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd3, 0xc6, 0x49, 0x66, 0xc0, 0x6b, 0x62, 0x2d, 0x23, 0xc8, 0x8d, 0xb2, 0xfd, - 0x4b, 0x8f, 0xa5, 0x0b, 0xe3, 0x61, 0x94, 0x3b, 0x79, 0x6d, 0x14, 0x85, 0x5f, 0x20, - 0x71, 0xd3, 0x20, 0xd4, 0x3d, 0x6c, 0x49, 0x4c, 0x9e, 0xda, 0x35, 0xcf, 0x9b, 0xf3, - 0x7d, 0xc5, 0x4b, 0x40, 0x2e, 0xb2, 0x87, 0x64, 0xa0, 0xb9, 0x17, 0x6c, 0xf9, 0x49, - 0xb2, 0xa7, 0x78, 0x64, 0x19, 0x83, 0x89, 0x2f, 0xfb, 0x5c, 0x7b, 0xfa, 0x68, 0xe6, - 0x36, 0xde, 0xfe, 0xfc, 0xb2, 0xfa, 0x07, 0x94, 0x45, 0xec, 0xd3, 0xad, 0xdf, 0x0c, - 0x22, 0xb2, 0x61, 0x72, 0x49, 0x92, 0xe2, 0xf0, 0xd2, 0x7c, 0xff, 0x23, 0xa6, 0x46, - 0x15, 0x30, 0xdc, 0x05, 0xf4, 0x9e, 0x97, 0x2d, 0xa3, 0x71, 0x6f, 0x41, 0x91, 0xbf, - 0xf4, 0xed, 0x29, 0x02, 0x67, 0x46, 0xf0, 0x9e, 0xfa, 0x9d, 0xfc, 0xbc, 0xde, 0xc5, - 0xa6, 0x95, 0xb1, 0xf7, 0x31, 0x36, 0x14, 0x64, 0xec, 0x42, 0xe3, 0xb5, 0x26, 0x7e, - 0xb6, 0x5f, 0x55, 0x6b, 0x26, 0x7a, 0xf3, 0x59, 0x71, 0xb4, 0x14, 0x9b, 0xb3, 0xe5, - 0xaa, 0x03, 0xa4, 0x95, 0xfb, 0xeb, 0x90, 0x15, 0xac, 0x3f, 0xf1, 0x3a, 0x5c, 0x1c, - 0x2a, 0x5f, 0x81, 0x96, 0x47, 0x3d, 0x5b, 0xfe, 0x70, 0x48, 0xdf, 0x27, 0x7f, 0x0b, - 0x5c, 0xf4, 0xe6, 0xc7, 0x1c, 0xa9, 0x36, 0x6e, 0xca, 0x3b, 0x9c, 0xf1, 0xe6, 0x06, - 0x9d, 0x53, 0x9e, 0x5c, 0xe4, 0x3f, 0xd9, 0xaa, 0x25, 0xc2, 0x11, 0xd3, 0x79, 0x92, - 0xc3, 0x40, 0xad, 0xea, 0x8b, 0x24, 0x9f, 0x28, 0xab, 0x23, 0x49, 0x39, 0x17, 0xc4, - 0x9d, 0xeb, 0x28, 0x3b, 0x4c, 0x8a, 0x64, 0x90, 0x41, 0x88, 0x7e, 0x66, 0x83, 0x8d, - 0x1c, 0x42, 0x9d, 0xec, 0xdb, 0x31, 0x59, 0xcb, 0x30, 0xaf, 0xe4, 0xfb, 0x31, 0x68, - 0xcc, 0xec, 0x44, 0x98, 0x2e, 0x05, 0xf8, 0x71, 0x13, 0x2e, 0xfa, 0x63, 0xd6, 0x5a, - 0x24, 0x93, 0xcd, 0xf2, 0x39, 0xe8, 0xb2, 0xc8, 0x09, 0x05, 0xe8, 0x04, 0xa8, 0x4d, - 0xd7, 0x6a, 0xfe, 0xaa, 0x68, 0x94, 0x79, 0x1d, 0x49, 0xb1, 0xe4, 0x00, 0xb3, 0xfc, - 0xaa, 0x82, 0x73, 0x99, 0x60, 0xad, 0xda, 0x36, 0x45, 0xbb, 0x85, 0x75, 0x6c, 0x63, - 0x00, 0x5c, 0x01, 0x6f, 0x65, 0x8b, 0xa6, 0xab, 0x52, 0x57, 0xc4, 0x86, 0xaf, 0x13, - 0xed, 0xc9, 0xb4, 0x6b, 0xf6, 0x29, 0x34, 0xaa, 0x71, 0x4f, 0x00, 0x36, 0x05, 0x96, - 0x5a, 0xc5, 0x4d, 0x82, 0x50, 0xa5, 0x53, 0x52, 0x00, 0xd1, 0x20, 0x2a, 0xcc, 0xca, - 0xaa, 0x9e, 0x42, 0xea, 0x98, 0x2a, 0x21, 0x61, 0x8e, 0xdb, 0xb1, 0x34, 0xc3, 0x3b, - 0xc8, 0x4e, 0x35, 0xfc, 0x76, 0x56, 0x05, 0x86, 0xa3, 0xc3, 0x43, 0x8e, 0x8f, 0x2b, - 0x0c, 0xe7, 0x0d, 0x86, 0x31, 0x71, 0xdf, 0x23, 0x8e, 0x12, 0x60, 0xd5, 0x9f, 0x82, - 0x40, 0x37, 0xa7, 0x71, 0x7b, 0x2e, 0x21, 0xa9, 0x6e, 0x4d, 0x79, 0x9b, 0x8e, 0xc4, - 0xc9, 0x8b, 0x8d, 0x16, 0x83, 0x6c, 0x18, 0x22, 0xb2, 0x45, 0x62, 0x66, 0x46, 0x59, - 0x86, 0x85, 0x0d, 0x23, 0x31, 0xc7, 0x29, 0x34, 0xbd, 0xb6, 0x71, 0x54, 0xab, 0xa0, - 0xad, 0x49, 0xbe, 0x0e, 0x52, 0xd8, 0xb0, 0x78, 0x41, 0x11, 0x7c, 0x0e, 0xb7, 0x6a, - 0x39, 0x54, 0x96, 0x39, 0xf7, 0xad, 0xe7, 0x6a, 0x90, 0x71, 0x0e, 0x79, 0x83, 0x97, - 0x8e, 0x9b, 0x23, 0x34, 0x9b, 0xee, 0x22, 0xcd, 0x0c, 0x71, 0xa1, 0xf0, 0x72, 0x70, - 0xe2, 0xce, 0x8b, 0x36, 0x05, 0x1b, 0x00, 0x55, 0xba, 0x97, 0x05, 0xab, 0x22, 0x2e, - 0x8e, 0x85, 0x8d, 0xc4, 0x5b, 0x66, 0xc1, 0xef, 0x3f, 0xe2, 0x66, 0x55, 0x03, 0xe7, - 0x8b, 0x30, 0x29, 0xef, 0xfb, 0xd5, 0xbb, 0x13, 0x9e, 0x85, 0x2c, 0x3b, 0xf9, 0x07, - 0x13, 0x2e, 0x54, 0xc3, 0xed, 0xad, 0x03, 0xf7, 0xe8, 0x68, 0xf5, 0x23, 0x15, 0x5f, - 0x9f, 0x6b, 0xce, 0xf4, 0x50, 0xbc, 0x9b, 0x56, 0x31, 0x0c, 0xda, 0x17, 0x3e, 0x50, - 0xe9, 0x5a, 0x6e, 0xe5, 0xf0, 0x68, 0xb2, 0x5e, 0x32, 0x9c, 0x35, 0x48, 0xfc, 0x24, - 0x99, 0x37, 0x3c, 0xde, 0x29, 0x36, 0x0f, 0xbb, 0xfa, 0x5b, 0x64, 0xb5, 0x74, 0x4a, - 0xb0, 0x3a, 0x4b, 0xd5, 0xd9, 0x48, 0xc1, 0xbe, 0xf8, 0xcf, 0x4e, 0x6b, 0xd9, 0x4c, - 0x32, 0x80, 0x9b, 0x18, 0xf1, 0x18, 0x9c, 0x32, 0xbb, 0x8f, 0xae, 0x27, 0x53, 0xe4, - 0x85, 0x1c, 0x31, 0x96, 0xf5, 0xbb, 0x1d, 0xa0, 0x78, 0x51, 0xb5, 0xd3, 0x1f, 0x20, - 0xa0, 0xfd, 0x3a, 0x7a, 0x4b, 0x45, 0x01, 0xf3, 0x18, 0x5d, 0x26, 0x7b, 0x1c, 0x8b, - 0xb3, 0x59, 0x5d, 0x85, 0xc5, 0x3c, 0xae, 0x18, 0x9e, 0xc9, 0xdb, 0x6f, 0x14, 0x53, - 0xb3, 0xc6, 0xad, 0x4f, 0x3b, 0x93, 0xdd, 0x10, 0x6a, 0x3a, 0x39, 0x0d, 0xb2, 0x7a, - 0x1a, 0x75, 0x0e, 0x7e, 0xd0, 0x89, 0x7e, 0xbb, 0x61, 0x98, 0x48, 0x4d, 0xcc, 0xdf, - 0xa7, 0xa7, 0xe1, 0xd8, 0xeb, 0x2f, 0x23, 0x66, 0x8d, 0x54, 0xe9, 0x8f, 0x9e, 0xd3, - 0xae, 0x90, 0xfe, 0x0c, 0x27, 0x5f, 0x17, 0x7e, 0xcf, 0x70, 0x1f, 0xd3, 0x0b, 0x92, - 0xf6, 0x1b, 0x3c, 0x12, 0x53, 0xcc, 0x31, 0x78, 0x95, 0xfe, 0x5e, 0x39, 0xc4, 0xea, - 0x03, 0x24, 0x8e, 0x83, 0x20, 0x2e, 0xa5, 0x89, 0xa0, 0xe8, 0xfc, 0xaf, 0xc4, 0x34, - 0x07, 0xb5, 0x71, 0x9c, 0x08, 0x6a, 0xc2, 0xf5, 0x8c, 0x1c, 0x4e, 0x05, 0x63, 0x69, - 0x56, 0xb6, 0x30, 0x4e, 0x31, 0x7f, 0x4f, 0x65, 0xb4, 0xe2, 0xb9, 0x9f, 0x25, 0xe8, - 0xd7, 0xbb, 0x53, 0x28, 0xea, 0x1f, 0x31, 0x13, 0x25, 0x6a, 0x45, 0x08, 0x01, 0x6a, - 0x3e, 0x9d, 0x01, 0x2e, 0xf8, 0x19, 0xfa, 0x36, 0xa5, 0xdb, 0xce, 0x7e, 0x3a, 0xff, - 0x47, 0x42, 0xc0, 0xcd, 0x3d, 0x5d, 0x9e, 0xb8, 0x40, 0x44, 0xa0, 0x03, 0x23, 0x39, - 0x40, 0x69, 0x9b, 0xc2, 0x79, 0x45, 0xb9, 0xac, 0x93, 0x82, 0x23, 0xc1, 0x17, 0x3f, - 0x34, 0xd1, 0x7e, 0x7e, 0x2e, 0x7b, 0xbc, 0xad, 0x2d, 0x91, 0x9d, 0x1a, 0xf5, 0x54, - 0x94, 0x0b, 0x68, 0xd7, 0x43, 0x3a, 0x6d, 0x67, 0xe8, 0x5c, 0xd3, 0x35, 0x66, 0xb0, - 0x60, 0xe4, 0x48, 0xb4, 0xa2, 0xa0, 0x52, 0xa8, 0xb7, 0x9e, 0x27, 0x57, 0x8d, 0xce, - 0x6e, 0x09, 0x88, 0x6e, 0xf0, 0x92, 0xef, 0x09, 0x67, 0x97, 0x47, 0x8b, 0xb5, 0x4b, - 0x9a, 0xbb, 0xa5, 0xae, 0x26, 0x79, 0x9b, 0x07, 0xcd, 0xc8, 0x8c, 0x80, 0x2e, 0x6a, - 0xf5, 0xcb, 0xfd, 0x41, 0x24, 0x29, 0x57, 0x00, 0xac, 0x12, 0xd9, 0x10, 0xa0, 0x2a, - 0x74, 0xc8, 0xab, 0xd2, 0x4d, 0x39, 0x88, 0x72, 0xdd, 0x9d, 0x3a, 0xb3, 0xc5, 0x4c, - 0x63, 0xa0, 0x9e, 0x51, 0xbb, 0x51, 0x62, 0x54, 0x01, 0x03, 0xab, 0x0c, 0xae, 0xfc, - 0x6e, 0x5b, 0x88, 0x05, 0x21, 0xf4, 0x9c, 0x55, 0x93, 0xa7, 0xec, 0xe1, 0xef, 0xdc, - 0x00, 0xad, 0x96, 0xc3, 0x82, 0xfe, 0xcf, 0x0f, 0x9c, 0x1c, 0x8e, 0xcd, 0xcb, 0xc2, - 0x2e, 0x89, 0x07, 0xce, 0x99, 0xdf, 0x99, 0x4a, 0x33, 0x0a, 0x90, 0x44, 0x6d, 0xae, - 0xec, 0xab, 0x71, 0xf0, 0x02, 0x35, 0xdd, 0x70, 0x23, 0x3c, 0x43, 0x17, 0xd6, 0x4e, - 0xf6, 0xba, 0x3f, 0x65, 0x76, 0x42, 0xba, 0xad, 0x97, 0x35, 0xe5, 0x48, 0x68, 0xc1, - 0x97, 0x54, 0x56, 0x89, 0xa0, 0x57, 0x0b, 0xd4, 0x58, 0x4a, 0xad, 0xe4, 0x1a, 0x59, - 0x08, 0xb8, 0xaa, 0x33, 0x54, 0x95, 0x72, 0xc7, 0x20, 0x9f, 0x63, 0xad, 0x0b, 0x80, - 0x4c, 0x76, 0x02, 0xf4, 0x8d, 0xed, 0x66, 0x8c, 0x31, 0xa0, 0x7d, 0x76, 0x02, 0xd6, - 0xf8, 0x24, 0x29, 0xc3, 0xd2, 0xde, 0xe9, 0x2f, 0x38, 0xdb, 0x5b, 0x92, 0x03, 0xac, - 0x84, 0xd0, 0xfe, 0x14, 0xba, 0x6a, 0xc1, 0x9a, 0xaf, 0x94, 0x00, 0xf2, 0xe3, 0x58, - 0x3f, 0xb1, 0x68, 0xd3, 0x03, 0xca, 0x7a, 0x88, 0x71, 0xdd, 0xd9, 0xa2, 0x95, 0x04, - 0x1b, 0x30, 0xb8, 0x1e, 0xea, 0x1e, 0x7d, 0x82, 0x24, 0x34, 0x4b, 0xd2, 0x68, 0xa9, - 0x4a, 0x11, 0x1e, 0xa7, 0xc9, 0xb0, 0x6e, 0xc5, 0x69, 0x12, 0x45, 0x2e, 0xeb, 0x01, - 0xcf, 0x88, 0x87, 0xa3, 0xe2, 0x6e, 0x14, 0x40, 0x6f, 0xfe, 0xec, 0x4b, 0xfd, 0x7a, - 0x9f, 0xd8, 0x77, 0xce, 0x52, 0x03, 0xfe, 0x6b, 0x05, 0x8d, 0x23, 0x1e, 0xc7, 0x1a, - 0xf9, 0xca, 0x18, 0xed, 0x5c, 0x73, 0x55, 0x06, 0xd7, 0xba, 0x28, 0xee, 0x68, 0xee, - 0x66, 0x58, 0x7c, 0x99, 0x8c, 0x8f, 0xec, 0xa7, 0xae, 0x06, 0x8c, 0x8e, 0xd0, 0x79, - 0xe5, 0xa9, 0xa4, 0x36, 0x72, 0x8c, 0xce, 0xe1, 0x0c, 0x8f, 0x12, 0x6f, 0x7b, 0x2f, - 0xa0, 0xd0, 0xff, 0x91, 0xcc, 0x41, 0xee, 0x28, 0xa1, 0x96, 0x23, 0x03, 0x37, 0xc6, - 0x1f, 0x42, 0xe9, 0x52, 0x2b, 0xf6, 0xde, 0x64, 0xfc, 0x5a, 0x57, 0xe3, 0x74, 0x77, - 0x06, 0x07, 0x63, 0x0b, 0xc1, 0x96, 0xed, 0x05, 0x2d, 0xff, 0x00, 0x83, 0x61, 0xfc, - 0x59, 0xfd, 0x9c, 0x48, 0xd2, 0x62, 0xb9, 0x3a, 0xee, 0x45, 0x65, 0x2c, 0x78, 0x78, - 0x05, 0xdf, 0xac, 0xe8, 0x3d, 0x04, 0xe5, 0x24, 0x40, 0x3a, 0x25, 0xa1, 0x66, 0xa1, - 0xf4, 0x8e, 0xcc, 0x8f, 0xff, 0x84, 0x4f, 0x09, 0xde, 0x67, 0x48, 0x04, 0x52, 0xa6, - 0x78, 0x9d, 0x48, 0xb7, 0xbd, 0xbd, 0x81, 0x1f, 0x0e, 0xda, 0xda, 0xa8, 0xee, 0x8e, - 0xb9, 0x16, 0x17, 0x99, 0x2e, 0xad, 0x6f, 0x8a, 0x8b, 0x9e, 0xf4, 0xc5, 0xad, 0xb6, - 0xf2, 0x52, 0x48, 0xb2, 0x13, 0xf3, 0xd6, 0x93, 0xf6, 0x3c, 0x0d, 0x5d, 0x15, 0xab, - 0x54, 0x32, 0x88, 0x07, 0x14, 0x27, 0x35, 0x79, 0x37, 0x3c, 0x49, 0xcb, 0xf1, 0x47, - 0xf9, 0x4a, 0x84, 0xad, 0xe6, 0x48, 0x49, 0xeb, 0x5a, 0x94, 0x04, 0x40, 0x13, 0x38, - 0x96, 0xa2, 0x45, 0x55, 0xe4, 0x01, 0x55, 0x99, 0xc0, 0x46, 0xdf, 0xa6, 0xf1, 0x4a, - 0x28, 0x70, 0x53, 0x3a, 0xe4, 0x7d, 0x33, 0xff, 0x81, 0x6b, 0x8e, 0x46, 0x63, 0xf0, - 0x70, 0xc8, 0x0d, 0x8d, 0xb0, 0x1b, 0x43, 0xc6, 0x0f, 0x5f, 0xc0, 0x2c, 0x85, 0xac, - 0xf5, 0xe1, 0x06, 0xd3, 0xba, 0x71, 0xea, 0x69, 0x3b, 0xa4, 0x65, 0xdd, 0x61, 0xff, - 0x1d, 0x80, 0xfe, 0xee, 0xa1, 0xb6, 0xd5, 0xa1, 0x63, 0xd0, 0xc9, 0x62, 0x43, 0x16, - 0x36, 0xe1, 0xed, 0x62, 0x19, 0x66, 0xfe, 0x28, 0x5b, 0xc9, 0x70, 0xa2, 0x66, 0xbb, - 0x40, 0x8d, 0x4d, 0x48, 0xd5, 0x5e, 0xf7, 0x17, 0x04, 0xf5, 0xb7, 0x98, 0x62, 0xbd, - 0x80, 0x6a, 0x6a, 0x33, 0xe1, 0x13, 0xb1, 0x88, 0x32, 0xb3, 0xd5, 0x9e, 0x3a, 0x69, - 0x84, 0xe1, 0x4f, 0xd5, 0x2a, 0xc9, 0xd2, 0xbe, 0x3a, 0xea, 0xaa, 0xbf, 0x38, 0x29, - 0xcb, 0xf4, 0xdf, 0xca, 0x68, 0x03, 0xaf, 0xcd, 0x1f, 0xc4, 0xcd, 0x02, 0x44, 0xd7, - 0xb6, 0x3b, 0x4c, 0x9d, 0x4a, 0xa1, 0xa2, 0x27, 0xad, 0xda, 0x80, 0x6a, 0x46, 0x24, - 0xa0, 0x79, 0x65, 0xb9, 0xfd, 0xa1, 0x73, 0xa2, 0xd9, 0x9a, 0x62, 0x4f, 0x4a, 0x78, - 0xe9, 0xc7, 0x17, 0x63, 0x01, 0x2b, 0x77, 0xaf, 0x32, 0x6c, 0x75, 0x22, 0x6b, 0x7d, - 0xe8, 0x29, 0x74, 0x4b, 0x6d, 0x39, 0x72, 0xe4, 0x7f, 0x6a, 0x14, 0x5b, 0x81, 0x34, - 0x0d, 0x27, 0x16, 0x20, 0x1e, 0x07, 0x1e, 0x47, 0x1a, 0x85, 0x5e, 0x9c, 0xc3, 0x6d, - 0x39, 0x49, 0x97, 0x15, 0x74, 0xbf, 0x3a, 0x06, 0x0f, 0xc0, 0xd8, 0x82, 0xd0, 0xa9, - 0x86, 0x5c, 0x24, 0xe0, 0x94, 0x03, 0x17, 0x30, 0xcb, 0xe1, 0x88, 0xe6, 0xfd, 0xaf, - 0xcb, 0xba, 0xf7, 0x51, 0xbe, 0x87, 0xaf, 0x96, 0x5c, 0xd9, 0x8d, 0x99, 0x31, 0x04, - 0xca, 0x6e, 0xdd, 0x29, 0x28, 0x0c, 0xda, 0x86, 0x55, 0x67, 0xbd, 0xd4, 0xb4, 0xba, - 0x47, 0x37, 0xe6, 0x1c, 0x3f, 0x0a, 0xd8, 0x75, 0xa8, 0xde, 0xe6, 0xe6, 0xcd, 0xff, - 0x26, 0x81, 0x88, 0x08, 0xff, 0x9b, 0x2d, 0x55, 0x87, 0x95, 0xd6, 0x5d, 0x2a, 0x95, - 0xb4, 0x56, 0x56, 0x19, 0xf7, 0xb2, 0x41, 0x62, 0xcc, 0x47, 0x59, 0x9a, 0x33, 0x13, - 0x06, 0xe3, 0x65, 0x2f, 0xfb, 0xc3, 0xb3, 0xfd, 0x06, 0xc1, 0x46, 0x0c, 0x80, 0x6f, - 0x4e, 0x61, 0xbe, 0xc2, 0xa2, 0xa7, 0xb6, 0xc7, 0x96, 0xf6, 0x5d, 0xcf, 0x36, 0xa4, - 0xaf, 0xc6, 0xd8, 0x10, 0x09, 0x35, 0x21, 0x0a, 0x86, 0x38, 0x9f, 0x24, 0x9e, 0x2f, - 0x82, 0x32, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x8b, 0x33, 0x6b, 0x5f, 0x55, 0x40, 0x0b, 0x06, - 0x79, 0xba, 0x0c, 0x1e, 0xf0, 0x20, 0xc9, 0x26, 0x85, 0xa4, 0x24, 0x91, 0x79, 0x95, - 0xea, 0x63, 0xad, 0x1d, 0x5e, 0x28, 0xdd, 0x63, 0x99, 0x83, 0x82, 0xc7, 0xb3, 0x9d, - 0x26, 0xdb, 0x80, 0xb4, 0x3e, 0x32, 0x4f, 0xaf, 0x5d, 0x63, 0x60, 0x4a, 0x84, 0xf2, - 0x13, 0x5c, 0xbf, 0xf5, 0x98, 0xeb, 0x50, 0xe1, 0xd3, 0xa4, 0xb9, 0x9c, 0xd6, 0x6c, - 0x7a, 0xfd, 0xe6, 0x7f, 0xac, 0x47, 0xf0, 0x35, 0x8e, 0xc7, 0x83, 0xbe, 0x35, 0x95, - 0x47, 0x96, 0xe5, 0x97, 0x3a, 0xcf, 0xf6, 0x31, 0x98, 0xa3, 0x55, 0x94, 0x18, 0x7e, - 0xf8, 0x17, 0x00, 0x0b, 0x08, 0x88, 0x1e, 0x70, 0xe0, 0xb2, 0xcd, 0xe2, 0x31, 0x51, - 0x79, 0xc0, 0x84, 0x15, 0x51, 0xe8, 0xbd, 0x92, 0x8e, 0xb6, 0x24, 0x87, 0x6e, 0x86, - 0xb0, 0xb3, 0x3a, 0xab, 0x0c, 0xde, 0x87, 0xeb, 0x8f, 0xd4, 0x78, 0x8d, 0xe9, 0xfb, - 0x37, 0xea, 0xb3, 0xb4, 0x7f, 0xd5, 0xdf, 0xe9, 0xb3, 0x7b, 0xcb, 0xb6, 0xe1, 0xf2, - 0x25, 0xfd, 0x29, 0xab, 0x07, 0xfc, 0x9f, 0xf5, 0xa0, 0x8f, 0x48, 0x66, 0x9e, 0x1c, - 0x99, 0x68, 0xf5, 0x21, 0x7a, 0xd3, 0x45, 0x2d, 0xad, 0x04, 0x78, 0x39, 0x07, 0x44, - 0xe9, 0xd1, 0x67, 0x85, 0xcd, 0x54, 0xa5, 0x03, 0x98, 0xb0, 0x14, 0xa0, 0x7b, 0x78, - 0x45, 0x99, 0x7a, 0x5b, 0x11, 0x6b, 0xb2, 0xc2, 0xf4, 0xc4, 0xe5, 0x64, 0x6e, 0x63, - 0x08, 0x2c, 0x5e, 0x3f, 0xee, 0x50, 0x92, 0xff, 0x2f, 0xa8, 0x9a, 0xe3, 0x2a, 0xd6, - 0x99, 0x07, 0x50, 0x4d, 0x68, 0x85, 0xb5, 0xbd, 0x72, 0xc8, 0x23, 0xd4, 0xc7, 0x0d, - 0x5e, 0xd4, 0x5c, 0xb0, 0x0c, 0x3e, 0x04, 0x05, 0x89, 0x2c, 0x88, 0x83, 0x74, 0x53, - 0xfe, 0xf2, 0xef, 0xb7, 0x51, 0x37, 0xf3, 0xc2, 0xab, 0xbc, 0x35, 0x47, 0xdf, 0x86, - 0xee, 0x01, 0x36, 0xb6, 0xe8, 0x5f, 0x33, 0xc5, 0x25, 0x58, 0x3f, 0xfe, 0x27, 0xe6, - 0xff, 0x48, 0xa8, 0x0d, 0x12, 0x4e, 0xf8, 0x01, 0xd3, 0x24, 0x75, 0x4e, 0x16, 0x1d, - 0x8b, 0xd6, 0x77, 0x44, 0xdf, 0x8a, 0xc5, 0x84, 0x9b, 0x65, 0x5a, 0xcf, 0x9f, 0xa7, - 0xb2, 0xea, 0x84, 0x62, 0x1d, 0x8e, 0x4d, 0xd8, 0x57, 0x6d, 0xa7, 0x5e, 0xd1, 0xb4, - 0x8a, 0xcb, 0x91, 0x08, 0x03, 0x27, 0x3e, 0x48, 0x37, 0x73, 0xa9, 0x9d, 0x58, 0xcb, - 0x70, 0x40, 0x8f, 0x3f, 0x23, 0xa3, 0xea, 0x71, 0xd6, 0x73, 0x23, 0xb8, 0xf9, 0xfd, - 0x51, 0x93, 0xb8, 0xdb, 0x90, 0x6a, 0x18, 0x86, 0xe4, 0x26, 0xd0, 0xd3, 0x21, 0x6e, - 0x7f, 0x0f, 0x42, 0xa9, 0xaa, 0xe0, 0x0f, 0xc3, 0x79, 0x12, 0x20, 0xdb, 0xb1, 0x03, - 0x15, 0x19, 0xbc, 0x1e, 0xcc, 0xf8, 0x29, 0x8a, 0x22, 0xab, 0x20, 0x92, 0x71, 0x65, - 0xaa, 0x95, 0xd5, 0x46, 0x88, 0x83, 0x48, 0x17, 0x58, 0x3c, 0x64, 0x90, 0x28, 0x77, - 0x34, 0xea, 0x30, 0x0c, 0x38, 0x94, 0xf9, 0x9b, 0xaa, 0x29, 0xee, 0x97, 0x50, 0x9d, - 0x1c, 0x10, 0x71, 0xf2, 0x17, 0x42, 0xba, 0x67, 0x13, 0xed, 0xa0, 0x20, 0x38, 0x1e, - 0x60, 0x98, 0xb0, 0x5a, 0xde, 0x28, 0x09, 0x63, 0xb3, 0x98, 0xc0, 0x3b, 0xf4, 0xc4, - 0xe1, 0xf1, 0x9a, 0xd1, 0xad, 0xf1, 0xf0, 0xd6, 0x1f, 0xac, 0xbf, 0x99, 0x66, 0xbd, - 0xb0, 0x1f, 0xd1, 0x84, 0xb2, 0x00, 0xf8, 0x66, 0xc5, 0xd1, 0x2e, 0x3d, 0xc5, 0x7e, - 0xcf, 0x4f, 0xcd, 0x60, 0xc4, 0xa7, 0x56, 0x19, 0x1d, 0xcf, 0x50, 0xbb, 0x0f, 0x97, - 0x6f, 0x00, 0xe4, 0x36, 0x36, 0xa6, 0x83, 0x08, 0x69, 0x2f, 0x40, 0x24, 0x4c, 0x39, - 0x15, 0x34, 0x4b, 0x6f, 0x1f, 0x5e, 0xe7, 0x0e, 0x51, 0xe1, 0x2b, 0x28, 0x53, 0x85, - 0x53, 0x40, 0x3b, 0xe1, 0x49, 0x8e, 0x00, 0x75, 0xdb, 0xda, 0x3e, 0x66, 0x6d, 0x9e, - 0xbd, 0x18, 0xa1, 0x27, 0x21, 0xc9, 0x73, 0x49, 0xac, 0x10, 0xe8, 0xfa, 0x2d, 0x6a, - 0x59, 0xb2, 0x23, 0x56, 0xa7, 0x71, 0x96, 0x18, 0xaa, 0xb5, 0xc7, 0x57, 0xf8, 0x82, - 0x1e, 0xfc, 0x3e, 0x07, 0x1b, 0x75, 0xf2, 0x15, 0xb2, 0x00, 0xb7, 0xd2, 0x99, 0x98, - 0xed, 0x7a, 0xe0, 0x05, 0x7f, 0xb2, 0x32, 0x9c, 0xa9, 0x13, 0x6d, 0xd2, 0xbc, 0x51, - 0xa6, 0x59, 0x01, 0x71, 0xdf, 0xca, 0x3b, 0xcb, 0x93, 0x6b, 0x11, 0xc6, 0x3c, 0x03, - 0xbb, 0x7f, 0xce, 0x30, 0xa0, 0x5f, 0x9b, 0x6f, 0x8f, 0xf3, 0x54, 0x06, 0x04, 0x50, - 0xa3, 0x45, 0x2d, 0xa1, 0x86, 0xe9, 0x3d, 0x6c, 0x32, 0xda, 0x62, 0x72, 0xb8, 0x9b, - 0xc4, 0xd6, 0xd5, 0xe8, 0x47, 0x8f, 0x29, 0x91, 0x01, 0x98, 0x97, 0x11, 0xa9, 0xd2, - 0x20, 0x97, 0xcd, 0xb7, 0x0c, 0x15, 0x0e, 0xd2, 0x6d, 0xf4, 0x7b, 0x0c, 0xdd, 0xee, - 0x52, 0x1b, 0x4f, 0x1e, 0x98, 0x96, 0xa1, 0xb6, 0x97, 0x86, 0x53, 0xa4, 0xe3, 0x8b, - 0x0d, 0x28, 0x52, 0x6e, 0x1e, 0x3a, 0x87, 0x43, 0x5a, 0xc4, 0xfd, 0x30, 0x97, 0xaf, - 0xe3, 0x21, 0xe7, 0x2d, 0x40, 0xc4, 0x70, 0xf3, 0xb5, 0x3f, 0x5c, 0x35, 0x8d, 0x2e, - 0x53, 0x69, 0x7c, 0xaf, 0x66, 0x9d, 0xea, 0xa1, 0x1d, 0xe7, 0x7c, 0x98, 0x4a, 0x73, - 0x0e, 0x5b, 0xf7, 0xb3, 0x8e, 0xf6, 0x58, 0x9a, 0x5a, 0xa7, 0x55, 0x81, 0xbf, 0xd3, - 0xc0, 0x07, 0x8a, 0x63, 0xa3, 0x92, 0x96, 0x0e, 0xc3, 0xf2, 0xa0, 0x5c, 0x08, 0x1a, - 0x48, 0x4e, 0xb4, 0xf4, 0x25, 0xb7, 0x08, 0x36, 0x0f, 0x82, 0x85, 0x3c, 0xfd, 0x50, - 0xa0, 0x27, 0xfa, 0x92, 0x51, 0x76, 0x86, 0x96, 0xf3, 0x73, 0x5c, 0xd9, 0xed, 0xf7, - 0x9e, 0xcd, 0x4b, 0xe0, 0x8c, 0x57, 0x85, 0xc8, 0xae, 0xe7, 0x9a, 0x13, 0x23, 0x87, - 0x09, 0x94, 0x2f, 0x2c, 0xfd, 0x0f, 0x80, 0x7d, 0xaa, 0xb5, 0x0c, 0xc6, 0x13, 0x1b, - 0xab, 0x91, 0x25, 0x67, 0x36, 0x27, 0xf5, 0xe9, 0xa3, 0xd5, 0x3d, 0x99, 0xfa, 0x02, - 0x5c, 0x39, 0xfa, 0xb0, 0x9e, 0x2a, 0x21, 0x34, 0x6d, 0xc7, 0xf8, 0x60, 0xa6, 0x2d, - 0xd2, 0x10, 0x8e, 0x04, 0x41, 0x17, 0x8e, 0xf9, 0x76, 0x21, 0xae, 0xfc, 0xe8, 0x97, - 0x28, 0x10, 0xa4, 0xc7, 0xfc, 0x1b, 0x3c, 0x7e, 0xaa, 0x83, 0xd4, 0xa6, 0x2b, 0xd7, - 0x10, 0x98, 0x96, 0x11, 0xdd, 0x7e, 0x2f, 0x4b, 0xdf, 0x15, 0xd8, 0x31, 0x00, 0x60, - 0x11, 0xb4, 0x4e, 0xd9, 0x59, 0xdc, 0x61, 0xd8, 0xde, 0x52, 0x74, 0x5e, 0x30, 0x67, - 0x9c, 0xef, 0x04, 0x01, 0x3a, 0xc6, 0x15, 0x4e, 0xf0, 0x64, 0x69, 0x82, 0x38, 0x74, - 0x25, 0x21, 0x62, 0x26, 0x3f, 0x3a, 0x4b, 0xa5, 0x65, 0x7b, 0x8d, 0x0e, 0xcf, 0x03, - 0x86, 0x44, 0x1f, 0x87, 0x30, 0xd0, 0xf1, 0x4e, 0x86, 0x8a, 0x32, 0x46, 0x37, 0xb0, - 0xd3, 0x4a, 0x9d, 0x1d, 0xd6, 0xc3, 0x9f, 0x28, 0xfd, 0x9a, 0xf3, 0x50, 0xdc, 0x23, - 0x93, 0x79, 0x29, 0xe3, 0x79, 0x70, 0xf8, 0x87, 0x37, 0x01, 0xd3, 0xfa, 0x47, 0x10, - 0x10, 0xa7, 0x21, 0x40, 0x68, 0xad, 0x1b, 0x89, 0x02, 0x52, 0x26, 0x1d, 0xd9, 0x0d, - 0x89, 0xc5, 0xa6, 0xf2, 0x90, 0x4b, 0xc6, 0x16, 0xb0, 0x27, 0xd7, 0xbe, 0xc8, 0x79, - 0xb7, 0xa1, 0x78, 0x25, 0x4f, 0xdc, 0xaa, 0x99, 0x1b, 0x42, 0x2b, 0x7a, 0x96, 0x93, - 0xe7, 0x64, 0xa1, 0x27, 0xb1, 0x72, 0xa0, 0xdc, 0xca, 0xc4, 0x4f, 0x15, 0x27, 0x08, - 0x6c, 0x48, 0x89, 0x85, 0xf9, 0x23, 0x5e, 0x28, 0x82, 0xb4, 0x78, 0x16, 0x44, 0xeb, - 0xa9, 0xed, 0x09, 0x61, 0xca, 0x7a, 0x68, 0x45, 0xb5, 0x73, 0x65, 0xd8, 0x75, 0x4b, - 0xdc, 0x79, 0x1f, 0x81, 0xc8, 0x09, 0xd0, 0x12, 0xbd, 0x32, 0x9b, 0x6a, 0x44, 0xbd, - 0x3d, 0xfa, 0x34, 0x73, 0x5c, 0xe4, 0xc7, 0x38, 0xed, 0xef, 0xa4, 0x2d, 0x3c, 0x74, - 0x09, 0x2b, 0x5c, 0xba, 0x9c, 0x35, 0x81, 0x57, 0xd2, 0xab, 0x8a, 0x68, 0x83, 0x04, - 0x0f, 0x40, 0xce, 0xc7, 0x98, 0xa6, 0x9d, 0x7e, 0x0e, 0xa3, 0xb4, 0x76, 0xd9, 0x93, - 0xd6, 0x96, 0xdb, 0x0a, 0xdd, 0xd5, 0x43, 0x3f, 0x9e, 0x7a, 0x0f, 0xfb, 0xe0, 0x24, - 0x26, 0x1e, 0x79, 0x8d, 0xad, 0x05, 0x8e, 0xc8, 0xde, 0x26, 0x7c, 0x94, 0x78, 0xc8, - 0x01, 0xff, 0x37, 0x1e, 0x41, 0xc0, 0xbc, 0x0c, 0xf4, 0x6a, 0x4a, 0x84, 0xd0, 0xac, - 0xa4, 0x73, 0xe8, 0x80, 0xde, 0x96, 0x29, 0x69, 0xe9, 0xde, 0x23, 0x99, 0xa2, 0x99, - 0x56, 0x80, 0xdd, 0x76, 0x8f, 0xd7, 0x6b, 0xc6, 0x89, 0x6f, 0xe0, 0x2a, 0xa4, 0x82, - 0xf7, 0x6c, 0x72, 0x52, 0xe6, 0x65, 0x04, 0xe8, 0x80, 0xd2, 0x76, 0xbf, 0x7d, 0x55, - 0x7b, 0x39, 0x6a, 0xde, 0x3b, 0xb4, 0x7a, 0x6b, 0x0e, 0x0d, 0xcf, 0x06, 0x3b, 0x1a, - 0xd8, 0x56, 0x69, 0x4f, 0x8e, 0xef, 0x54, 0xca, 0x7d, 0xf4, 0x2b, 0x41, 0xf9, 0xc6, - 0x15, 0x3e, 0xa7, 0x47, 0x1c, 0xd5, 0x4f, 0x90, 0x54, 0x7c, 0xc4, 0xd4, 0xef, 0x5f, - 0xb1, 0xbf, 0xe5, 0x82, 0x88, 0x22, 0x59, 0xc7, 0x77, 0xef, 0xc4, 0xeb, 0x8f, 0x5d, - 0x75, 0x53, 0x1c, 0x1b, 0x80, 0x1b, 0x72, 0x12, 0xc6, 0xf1, 0x45, 0x09, 0x78, 0x40, - 0x20, 0xcb, 0xc3, 0xb0, 0x0e, 0xb5, 0x31, 0xc5, 0x62, 0x44, 0x36, 0x89, 0x28, 0xa8, - 0x51, 0xae, 0x53, 0x7c, 0x74, 0x80, 0xee, 0x6e, 0x45, 0x1b, 0x29, 0x74, 0x32, 0xee, - 0x17, 0x58, 0x22, 0x99, 0x50, 0xcf, 0x78, 0x08, 0x49, 0x32, 0x6c, 0x3f, 0x28, 0xdd, - 0x53, 0xd6, 0x81, 0x19, 0xd2, 0x96, 0x95, 0x50, 0x12, 0xa2, 0x6f, 0x83, 0x3c, 0xdd, - 0x29, 0xc6, 0xf4, 0xc7, 0x16, 0xf1, 0xd3, 0x37, 0xd3, 0xf4, 0xd2, 0x1c, 0x7a, 0x63, - 0xf8, 0x54, 0xc9, 0xf4, 0xc1, 0xc4, 0xcc, 0xf1, 0x81, 0xad, 0x43, 0x16, 0xca, 0xb1, - 0x36, 0x46, 0x7c, 0x01, 0xd9, 0x6d, 0x36, 0xe2, 0x98, 0x1c, 0x86, 0xc4, 0x76, 0x56, - 0x7d, 0x83, 0x77, 0x6b, 0x73, 0x37, 0x35, 0xd5, 0x65, 0x8a, 0x48, 0xf9, 0x89, 0x7c, - 0xf1, 0xe5, 0x05, 0x2b, 0x37, 0xec, 0x1c, 0x88, 0x91, 0x47, 0x36, 0xd9, 0xf9, 0x7c, - 0x54, 0x99, 0xd7, 0x3d, 0x92, 0x3b, 0x45, 0x00, 0x69, 0x4f, 0xfa, 0x57, 0x35, 0xc9, - 0x3c, 0xdb, 0x87, 0xb3, 0x5d, 0x82, 0x95, 0x49, 0xb1, 0xc6, 0x38, 0x3e, 0x95, 0xfd, - 0x19, 0x02, 0xad, 0x29, 0x80, 0xf2, 0xa3, 0xa2, 0x48, 0x3a, 0xce, 0x74, 0xb7, 0x64, - 0x3d, 0x8e, 0xae, 0x8d, 0x07, 0x9a, 0xa0, 0x06, 0x75, 0x41, 0x00, 0x6b, 0x94, 0xa6, - 0xf9, 0x13, 0xdc, 0xff, 0x13, 0xd6, 0x7c, 0xd9, 0xa8, 0xcf, 0xdf, 0x30, 0xb0, 0xc3, - 0xd1, 0x5a, 0xaa, 0x47, 0x0b, 0x3f, 0x89, 0x56, 0x10, 0x51, 0x42, 0xfa, 0x26, 0x11, - 0xfe, 0xda, 0xa4, 0x3f, 0xac, 0xbb, 0x3f, 0x05, 0x96, 0xf6, 0x78, 0x87, 0xcd, 0xee, - 0x91, 0x42, 0xc5, 0x09, 0x0a, 0x84, 0xe6, 0x25, 0x29, 0x31, 0xff, 0xcf, 0x61, 0xa5, - 0x0a, 0x4b, 0x92, 0x85, 0x30, 0x60, 0xe8, 0xb8, 0x7e, 0x10, 0xce, 0xa8, 0xce, 0x00, - 0xe4, 0x66, 0x5e, 0x5f, 0x93, 0x1f, 0x0e, 0x08, 0xdc, 0x52, 0x47, 0xbe, 0x1a, 0xed, - 0xc7, 0x9e, 0xbb, 0x7c, 0x20, 0x16, 0x2f, 0xca, 0x7b, 0xf9, 0x0e, 0x58, 0x83, 0x02, - 0x5f, 0xc9, 0x24, 0x36, 0x8d, 0x42, 0x45, 0x0b, 0x4f, 0xb7, 0xa7, 0xe1, 0x91, 0x0e, - 0xdd, 0x8d, 0x29, 0x5f, 0x03, 0xd4, 0xde, 0x03, 0xde, 0x60, 0x51, 0xd1, 0xfc, 0xf2, - 0x87, 0xf5, 0x4f, 0x38, 0x24, 0x41, 0xdd, 0xe0, 0x0c, 0xb6, 0x83, 0xa4, 0x04, 0x8c, - 0xe5, 0x4d, 0x42, 0x20, 0x90, 0x57, 0x24, 0xb3, 0x09, 0xc7, 0x99, 0x92, 0x4b, 0x85, - 0x4a, 0xfa, 0x37, 0x7b, 0x80, 0x1a, 0x03, 0x52, 0xfc, 0x44, 0x50, 0xb3, 0x35, 0x27, - 0x7a, 0xda, 0xd7, 0x61, 0xe4, 0x8a, 0x1d, 0x1d, 0xd3, 0x78, 0x93, 0x6a, 0x49, 0x1e, - 0x28, 0x6c, 0xaf, 0xc7, 0x00, 0xb4, 0x8e, 0xdf, 0x15, 0xf1, 0xc2, 0xd6, 0xed, 0xf1, - 0xa2, 0x4e, 0x0e, 0x51, 0xb3, 0x98, 0x55, 0x64, 0xeb, 0xa9, 0x69, 0xcd, 0x6e, 0xe6, - 0x59, 0xba, 0xae, 0xf7, 0x46, 0xe1, 0x3a, 0xba, 0x64, 0xaf, 0xad, 0x58, 0xaf, 0x52, - 0xf4, 0x28, 0x17, 0x36, 0x45, 0x75, 0x7a, 0x40, 0x7e, 0x1f, 0xdf, 0xd9, 0x89, 0x38, - 0x0c, 0x02, 0xbc, 0xc3, 0xc3, 0x7f, 0x48, 0x90, 0xc0, 0x8e, 0xb9, 0x31, 0x62, 0xcf, - 0x78, 0xbc, 0x3c, 0x74, 0x53, 0xf3, 0xf9, 0x92, 0xa7, 0x94, 0x53, 0x4c, 0x07, 0xe3, - 0x96, 0x8d, 0x82, 0x70, 0xaa, 0x19, 0x1f, 0x67, 0x80, 0x0a, 0x0b, 0xb3, 0xe7, 0xbf, - 0xa5, 0x4b, 0x0f, 0x6f, 0xa5, 0x3e, 0xe8, 0xfb, 0x13, 0x69, 0x82, 0xce, 0x71, 0xf4, - 0x08, 0x64, 0xb5, 0x4d, 0x00, 0x45, 0x1a, 0xf3, 0xf5, 0x32, 0x74, 0x22, 0x42, 0x16, - 0x06, 0xea, 0x10, 0xc0, 0xd6, 0x12, 0x7c, 0x02, 0xf9, 0x1a, 0xd3, 0xae, 0xb9, 0xff, - 0xd6, 0x11, 0x12, 0x25, 0x14, 0x14, 0x48, 0xbe, 0x82, 0x40, 0xc4, 0x29, 0x73, 0xac, - 0x52, 0xd7, 0x1b, 0x01, 0x2f, 0xe8, 0xef, 0x41, 0xf0, 0x0e, 0xc1, 0x96, 0xc7, 0x57, - 0x89, 0x9e, 0xf8, 0xc0, 0x0e, 0xf8, 0xdf, 0x44, 0x5c, 0x56, 0x54, 0x69, 0xd8, 0x4b, - 0xd0, 0x2c, 0x7f, 0xc4, 0x1b, 0xfc, 0xdf, 0x98, 0x95, 0x1f, 0x50, 0xe8, 0x3f, 0x19, - 0xa0, 0x00, 0xa9, 0xe4, 0x53, 0xf6, 0x21, 0x67, 0xe7, 0x35, 0x0f, 0x92, 0x36, 0x08, - 0x31, 0xbd, 0x7c, 0x52, 0x22, 0xb6, 0x70, 0x61, 0x6e, 0x4b, 0x6c, 0xa8, 0xa2, 0x35, - 0x50, 0xca, 0xd8, 0xac, 0x0d, 0xdb, 0x76, 0x45, 0xe2, 0xb9, 0x71, 0x3b, 0xe7, - ], - script_code: Script(vec![0x6a, 0x00, 0x00, 0x65, 0x53, 0xac, 0x63, 0x53, 0x63]), - transparent_input: None, - hash_type: 1, - amount: 1871432121379810, - consensus_branch_id: 1991772603, - sighash: [ - 0x36, 0x77, 0xa9, 0x48, 0x4f, 0x04, 0x04, 0xfb, 0x50, 0x64, 0x58, 0x56, 0xf4, 0xd4, - 0xa7, 0x0b, 0x2e, 0x2b, 0x1c, 0x2d, 0x86, 0x2f, 0x1d, 0x4e, 0xf6, 0x8d, 0x52, 0x09, - 0x60, 0xa1, 0x2a, 0x2b, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xa0, 0x1e, 0x64, 0x49, 0xae, - 0x43, 0x5c, 0x24, 0xbe, 0x7b, 0x9d, 0x28, 0x8a, 0xd7, 0x57, 0x12, 0xc9, 0x2a, 0xa5, - 0x06, 0x18, 0xdf, 0xba, 0x18, 0xe8, 0x4e, 0x88, 0xd4, 0x60, 0x68, 0xdf, 0x0b, 0x42, - 0xaf, 0x89, 0x29, 0x07, 0x00, 0x6a, 0x52, 0xac, 0x65, 0x51, 0x63, 0x6e, 0x99, 0x51, - 0xd0, 0x09, 0xa9, 0x39, 0xf7, 0x59, 0xa8, 0xa2, 0xc0, 0x49, 0xde, 0xf0, 0x97, 0x7f, - 0x61, 0xea, 0x11, 0x23, 0x14, 0x06, 0xcd, 0x10, 0x95, 0x6d, 0x16, 0x55, 0x78, 0xbb, - 0x29, 0xe4, 0x76, 0x96, 0x76, 0x9a, 0x58, 0x0e, 0x07, 0x01, 0x00, 0x15, 0xaf, 0x3b, - 0x50, 0x00, 0x13, 0x58, 0xd0, 0x37, 0xe5, 0x70, 0xfe, 0x0b, 0x50, 0x0e, 0xe2, 0x99, - 0x8c, 0xdf, 0x06, 0x00, 0x03, 0x7e, 0x28, 0x30, 0x34, 0x34, 0x96, 0x2f, 0x03, 0x92, - 0x48, 0x3d, 0xec, 0xad, 0x2f, 0x9f, 0x4e, 0xbc, 0x99, 0x05, 0x4b, 0xbc, 0xf1, 0x55, - 0xff, 0xae, 0x67, 0x76, 0x34, 0xc3, 0xfb, 0x98, 0x0d, 0xc5, 0xe8, 0xec, 0x67, 0xa4, - 0x65, 0x7e, 0x80, 0xa2, 0x9a, 0x79, 0x6f, 0x39, 0x62, 0xae, 0x0c, 0xb9, 0xc7, 0x86, - 0x82, 0xb3, 0xf4, 0xf9, 0x2e, 0x5a, 0x1e, 0xd1, 0xda, 0x2b, 0xbf, 0xc1, 0x71, 0x07, - 0x7e, 0xef, 0x83, 0x65, 0xbb, 0x38, 0xce, 0x94, 0xca, 0xb0, 0x28, 0x33, 0xce, 0x47, - 0xd4, 0xa0, 0x98, 0x65, 0x72, 0x94, 0xec, 0x10, 0xb2, 0x99, 0x74, 0x22, 0x22, 0xd0, - 0xbf, 0x74, 0x3f, 0x40, 0xc8, 0xea, 0x97, 0x14, 0x32, 0x5c, 0x8a, 0x37, 0x05, 0x08, - 0x24, 0xfa, 0x75, 0x62, 0xd2, 0xc9, 0x25, 0x2c, 0x34, 0xa9, 0x84, 0x50, 0x27, 0xd6, - 0x63, 0x90, 0xe9, 0x56, 0xb2, 0x5e, 0x16, 0x6c, 0x44, 0x95, 0xd3, 0xde, 0xd3, 0xf7, - 0xac, 0xcf, 0x74, 0x76, 0x38, 0x99, 0x47, 0x35, 0x11, 0x34, 0x12, 0x98, 0xfe, 0xb1, - 0x89, 0xb7, 0xed, 0x34, 0xe5, 0x67, 0xd7, 0x2f, 0x1d, 0xf4, 0xbf, 0x69, 0x7f, 0x71, - 0x46, 0x49, 0x3f, 0xa5, 0xc2, 0x36, 0x91, 0x22, 0x7b, 0x90, 0xb2, 0x51, 0x22, 0xc5, - 0x40, 0xdf, 0x0a, 0x6f, 0x2e, 0xc0, 0x6f, 0x9d, 0x89, 0xa3, 0xf7, 0x71, 0xe9, 0xb8, - 0xed, 0x74, 0x79, 0x40, 0x85, 0x51, 0x06, 0xd5, 0xea, 0x71, 0xba, 0x89, 0xe8, 0xf2, - 0x0c, 0xde, 0xa6, 0x9a, 0x77, 0x8a, 0x59, 0xe4, 0xdf, 0x79, 0x28, 0xc0, 0x35, 0x56, - 0x23, 0x31, 0xc8, 0xe1, 0x62, 0xb8, 0xfd, 0x5e, 0xbb, 0xd5, 0xe2, 0xb3, 0x7b, 0xea, - 0x7a, 0xf0, 0x69, 0x07, 0x10, 0x40, 0xc3, 0x7c, 0x1a, 0x1c, 0x37, 0xf0, 0x76, 0x0f, - 0xed, 0x7d, 0xb7, 0xfa, 0x70, 0xa9, 0x48, 0x94, 0x03, 0x00, 0x45, 0x76, 0xa2, 0xcc, - 0xe9, 0x0a, 0x39, 0x4b, 0x5e, 0xc5, 0x8b, 0x2e, 0x5d, 0x0e, 0x1a, 0xf8, 0xb0, 0x29, - 0x6d, 0x0b, 0xf0, 0x2c, 0x55, 0x97, 0xa4, 0x33, 0x54, 0x14, 0x43, 0x35, 0xe0, 0x6a, - 0x80, 0x1c, 0x6e, 0x7c, 0x73, 0x29, 0x7d, 0xfe, 0x0b, 0x32, 0xfc, 0xb8, 0x75, 0x33, - 0x81, 0x71, 0xdd, 0x1e, 0xeb, 0xeb, 0x12, 0x3f, 0xea, 0xfa, 0x32, 0xa5, 0xd8, 0xc7, - 0xce, 0x58, 0x39, 0x0e, 0xa2, 0xdf, 0x26, 0xc6, 0x88, 0x88, 0xda, 0xf3, 0x81, 0x6b, - 0x7d, 0x02, 0x97, 0xa1, 0x7b, 0x5f, 0x5d, 0x20, 0x8d, 0xe9, 0x22, 0xe7, 0x73, 0x97, - 0x2b, 0x95, 0xe6, 0x96, 0x5e, 0x58, 0xfb, 0xf6, 0x4f, 0xae, 0x06, 0xf0, 0xc3, 0x89, - 0x6e, 0x0b, 0x57, 0x89, 0x0d, 0xd7, 0xf3, 0xc6, 0x4c, 0x3d, 0x5c, 0xeb, 0xb6, 0xa7, - 0x44, 0xc5, 0x93, 0x38, 0x61, 0x22, 0x71, 0x82, 0x08, 0x04, 0x95, 0xce, 0x9a, 0xc2, - 0xe1, 0x73, 0x09, 0x9c, 0xdc, 0x35, 0x8d, 0xa8, 0x7d, 0xd7, 0x4a, 0x77, 0x34, 0xff, - 0xff, 0xc4, 0x5f, 0xb6, 0xad, 0x1f, 0x38, 0x9c, 0x6a, 0x4d, 0x49, 0x86, 0x62, 0x64, - 0x60, 0x56, 0x08, 0x4d, 0x09, 0xb7, 0x84, 0x88, 0xa3, 0xba, 0x1d, 0x8a, 0x3d, 0x6b, - 0x48, 0x9a, 0xfd, 0xf2, 0x32, 0xd6, 0xd0, 0x70, 0xa1, 0xb5, 0x06, 0x0c, 0xaa, 0x44, - 0x3d, 0x0c, 0x7e, 0xe5, 0x19, 0x04, 0x54, 0x7f, 0xaf, 0x53, 0x95, 0xcb, 0xd0, 0xba, - 0x99, 0x48, 0x0a, 0xd0, 0x4a, 0xe0, 0xe1, 0x91, 0x5b, 0xd7, 0x7f, 0xa2, 0x6d, 0x04, - 0x17, 0x5b, 0x00, 0xfd, 0xc8, 0x1e, 0xf6, 0xf3, 0x79, 0x23, 0x72, 0x49, 0x27, 0xf0, - 0x82, 0x66, 0xb6, 0x86, 0x40, 0x93, 0x13, 0xdc, 0x13, 0xbc, 0x39, 0x9d, 0x19, 0x77, - 0xb8, 0xf6, 0x58, 0x8c, 0x0e, 0x08, 0x72, 0x10, 0xf0, 0x51, 0xcf, 0x6e, 0x36, 0xe1, - 0x4e, 0x32, 0xaa, 0x23, 0xba, 0x6a, 0xe4, 0x33, 0x1f, 0x22, 0x39, 0xe7, 0x05, 0xf6, - 0x79, 0x54, 0x2f, 0xbd, 0x4e, 0xd2, 0xbf, 0x31, 0x91, 0x24, 0x36, 0x81, 0xf8, 0x27, - 0x89, 0x6b, 0x1b, 0xb1, 0xc4, 0xb7, 0x8b, 0x34, 0xc4, 0x87, 0xa4, 0xed, 0xfa, 0x97, - 0xd3, 0x6d, 0x62, 0xee, 0x32, 0x49, 0xef, 0xe0, 0x94, 0xc3, 0x87, 0x8a, 0xde, 0xdf, - 0x9f, 0x2b, 0x17, 0xd5, 0x11, 0x99, 0x80, 0x4f, 0x42, 0x9c, 0xd7, 0x04, 0xa7, 0xc8, - 0x6c, 0x85, 0x0c, 0xe1, 0x5d, 0x3c, 0x5f, 0x01, 0xd1, 0xad, 0x17, 0xeb, 0xb6, 0xc2, - 0x88, 0x3f, 0x28, 0xe8, 0x15, 0xbc, 0x45, 0x2a, 0x56, 0x07, 0x98, 0x05, 0xa5, 0xdd, - 0x69, 0x00, 0xe5, 0x5f, 0x47, 0x7e, 0xca, 0xc2, 0x14, 0x3f, 0x02, 0xee, 0x98, 0xc8, - 0xd9, 0xb1, 0xb7, 0x03, 0x93, 0xa1, 0x70, 0xba, 0x25, 0x48, 0x06, 0xb4, 0x08, 0x5b, - 0x8d, 0xf9, 0xca, 0x04, 0x07, 0x18, 0x42, 0xa3, 0xaf, 0x93, 0x33, 0x16, 0x83, 0x0d, - 0x53, 0xa7, 0xcb, 0x88, 0xd2, 0xa9, 0x82, 0x3b, 0xcd, 0xfb, 0xec, 0x8f, 0x18, 0xc8, - 0x6a, 0xc3, 0xdf, 0x89, 0x42, 0x38, 0x00, 0x1b, 0xa8, 0xfa, 0x31, 0x3f, 0x80, 0xcf, - 0xe7, 0x5f, 0x7c, 0xb5, 0xd9, 0x73, 0xcc, 0x77, 0xf3, 0x21, 0xf1, 0x95, 0x2f, 0x30, - 0x50, 0x18, 0xc0, 0xbf, 0x23, 0x8b, 0x80, 0xe3, 0x21, 0x19, 0x90, 0x60, 0x66, 0xf6, - 0x4e, 0x64, 0x5e, 0x2b, 0xca, 0xd7, 0xe4, 0xcd, 0xbe, 0xf0, 0x07, 0xf7, 0xe9, 0xad, - 0x8a, 0x31, 0x83, 0x8b, 0x9e, 0xae, 0xc3, 0x85, 0xe3, 0xf2, 0x5e, 0x16, 0x04, 0xa6, - 0xd4, 0x64, 0x99, 0x87, 0x5a, 0xc1, 0x4a, 0x6c, 0xb3, 0x55, 0xa3, 0xd4, 0x32, 0x91, - 0x45, 0x80, 0x3c, 0x6b, 0xfb, 0x82, 0xe2, 0x9a, 0xb0, 0x29, 0x9f, 0x91, 0x7a, 0x74, - 0x02, 0x81, 0x57, 0x71, 0x7c, 0x08, 0x48, 0x68, 0x63, 0x94, 0x5c, 0x5a, 0x02, 0x36, - 0x58, 0xee, 0xe4, 0xa8, 0xb2, 0x89, 0x56, 0x4c, 0x22, 0xa9, 0x67, 0x1c, 0x56, 0x91, - 0x33, 0x5e, 0xb1, 0x25, 0x89, 0x88, 0x51, 0x67, 0x8f, 0x54, 0x93, 0x45, 0x10, 0xbf, - 0x30, 0x91, 0xc6, 0x02, 0xe1, 0x2a, 0x32, 0x03, 0xa2, 0xf3, 0x2f, 0x34, 0x7d, 0x4b, - 0xdc, 0x9d, 0x44, 0x92, 0x4d, 0xc8, 0x67, 0x5c, 0x9f, 0x24, 0x06, 0x4d, 0x35, 0xb0, - 0x09, 0xb6, 0xdd, 0xbd, 0xb2, 0x37, 0x20, 0x75, 0x33, 0xd5, 0xbb, 0xad, 0x3b, 0xa1, - 0xa3, 0xd6, 0xb0, 0x89, 0x32, 0x9b, 0xe1, 0x47, 0x23, 0x4e, 0x75, 0x1a, 0x49, 0x27, - 0x9d, 0x74, 0xdb, 0x88, 0xdb, 0x5c, 0xa1, 0x02, 0xd5, 0xe0, 0xe1, 0xaa, 0xc7, 0xcc, - 0xf9, 0x66, 0xb0, 0xa8, 0x13, 0x67, 0x09, 0x5d, 0xa2, 0x1d, 0xc4, 0xb7, 0x36, 0x55, - 0x95, 0x30, 0x80, 0xe3, 0x54, 0xbd, 0x22, 0x09, 0xf2, 0x66, 0x82, 0x10, 0xe9, 0x47, - 0x41, 0x27, 0x31, 0x1d, 0x93, 0x45, 0xce, 0x1e, 0xbd, 0x3a, 0xe5, 0x24, 0x24, 0x5b, - 0xbb, 0x44, 0x7a, 0x44, 0x50, 0x80, 0xb5, 0xfa, 0x23, 0xcd, 0xfe, 0x98, 0xb3, 0xf6, - 0xf6, 0x3c, 0x44, 0xeb, 0xe7, 0x22, 0xb9, 0x7a, 0x79, 0x10, 0xdf, 0x7e, 0xa6, 0x22, - 0x5e, 0xd9, 0xdc, 0xb4, 0x49, 0x84, 0x93, 0xe8, 0xef, 0x55, 0x31, 0xf9, 0xf9, 0x77, - 0x31, 0x84, 0xd7, 0xb4, 0xf5, 0x36, 0x77, 0xb1, 0xd0, 0x44, 0xf6, 0xf1, 0x44, 0x07, - 0xde, 0x5d, 0x67, 0xe0, 0x77, 0xd2, 0x0f, 0x2e, 0x9d, 0x7f, 0xd7, 0x15, 0xbf, 0x9b, - 0x19, 0x9b, 0x93, 0xb9, 0x84, 0x02, 0x46, 0xef, 0x9c, 0x07, 0x35, 0xe4, 0x88, 0xff, - 0x7c, 0x80, 0xb9, 0x41, 0x78, 0xac, 0xa3, 0x1b, 0x13, 0xc3, 0x7c, 0x9a, 0xeb, 0x7f, - 0x62, 0xe2, 0xd8, 0x58, 0x97, 0xea, 0x2e, 0x2a, 0x23, 0x28, 0xee, 0x03, 0xc9, 0x7f, - 0x2f, 0x3f, 0x4d, 0x20, 0xa8, 0xe7, 0x30, 0x24, 0xc5, 0x50, 0x8e, 0xee, 0xbd, 0x3a, - 0x12, 0x67, 0x31, 0xcd, 0xbf, 0x21, 0xfd, 0xad, 0xb1, 0x4b, 0x4e, 0x59, 0x1c, 0xba, - 0xb1, 0x44, 0xbe, 0xc3, 0x5a, 0x72, 0xac, 0xbf, 0x94, 0x84, 0xf4, 0x7a, 0x10, 0xb9, - 0x1e, 0xfc, 0x04, 0x27, 0xfe, 0xcf, 0x3f, 0xfc, 0xf1, 0x69, 0xd7, 0x00, 0x59, 0xb4, - 0x02, 0x79, 0xff, 0xa0, 0x2c, 0x51, 0x06, 0x74, 0x27, 0xa0, 0xda, 0xea, 0xd6, 0xf9, - 0x4b, 0xaf, 0xe4, 0xc1, 0x23, 0x3a, 0x22, 0x25, 0xeb, 0x56, 0x00, 0x3f, 0xc3, 0x85, - 0x42, 0x0d, 0x5a, 0x9f, 0xf3, 0xd5, 0x91, 0x55, 0x23, 0xa0, 0x8c, 0x87, 0xeb, 0x2e, - 0xa6, 0x69, 0x17, 0x23, 0x3a, 0x73, 0x25, 0xfe, 0x79, 0x3f, 0x41, 0x07, 0x6d, 0x64, - 0x25, 0x5a, 0xbd, 0x15, 0x21, 0x47, 0x66, 0x60, 0xe9, 0x04, 0x91, 0x60, 0x2c, 0x69, - 0xa4, 0xab, 0xb1, 0x38, 0x84, 0x43, 0x10, 0x72, 0xef, 0x96, 0xa0, 0x95, 0xbe, 0x41, - 0x1f, 0xfc, 0xff, 0xb7, 0x86, 0x3f, 0xef, 0x7d, 0xab, 0x4d, 0x4a, 0x72, 0xa2, 0xd0, - 0xbb, 0xd3, 0x6f, 0x9f, 0xdf, 0x0b, 0x35, 0x38, 0xb3, 0x9c, 0xae, 0x5f, 0xf6, 0x0e, - 0x5a, 0xc6, 0xb6, 0x09, 0x70, 0x72, 0x43, 0x14, 0x6e, 0xb5, 0x36, 0x0a, 0xe7, 0xf9, - 0x3f, 0x79, 0x9b, 0x6c, 0x27, 0xe6, 0x5a, 0x0a, 0x06, 0x39, 0x87, 0x38, 0x66, 0x0f, - 0xda, 0xd2, 0xcf, 0xb3, 0x1a, 0xa5, 0x40, 0xd5, 0xe8, 0x90, 0x06, 0x78, 0xb9, 0xda, - 0xb5, 0x24, 0x79, 0xbd, 0x0c, 0xd6, 0xf1, 0xa5, 0x98, 0x67, 0x3e, 0xed, 0x9c, 0x76, - 0xe3, 0x38, 0x10, 0x49, 0x47, 0x18, 0xd0, 0x5d, 0xdf, 0xdc, 0x00, 0x7a, 0x54, 0xbc, - 0xd1, 0xcc, 0x4c, 0x97, 0x40, 0xf7, 0xe5, 0x3a, 0x31, 0x68, 0x1d, 0x2b, 0x2c, 0x6e, - 0xde, 0x79, 0x28, 0x11, 0x49, 0xea, 0xc3, 0x0f, 0x6e, 0xe5, 0x83, 0x60, 0x5a, 0xc2, - 0xff, 0xae, 0xc1, 0x55, 0x00, 0x35, 0xdc, 0x5a, 0xbb, 0x35, 0x89, 0x44, 0x68, 0xf1, - 0x2d, 0x5d, 0x08, 0xd7, 0x34, 0x36, 0xa8, 0x59, 0xe5, 0x50, 0x7f, 0xdd, 0x1a, 0x46, - 0x38, 0xfb, 0xe6, 0x81, 0xb0, 0xa0, 0xef, 0xfb, 0xbb, 0xf7, 0x4c, 0x99, 0x39, 0x9d, - 0xca, 0x69, 0x02, 0xa0, 0x74, 0xc8, 0x33, 0x35, 0x60, 0x7a, 0x0c, 0x0d, 0xb0, 0x1c, - 0xa3, 0xca, 0x2f, 0xa8, 0x18, 0x57, 0x24, 0x02, 0xe2, 0xfa, 0xef, 0xb3, 0x07, 0xbe, - 0x22, 0xc7, 0xd5, 0x61, 0x1f, 0xf6, 0xfb, 0x5a, 0x31, 0xb4, 0x62, 0x16, 0x59, 0xd8, - 0x4d, 0x8a, 0x7a, 0x1a, 0xdc, 0xa2, 0xfc, 0x4e, 0xb8, 0xb8, 0x97, 0x04, 0x43, 0x93, - 0x27, 0x64, 0x46, 0x31, 0xa7, 0xbb, 0xc1, 0xa8, 0x41, 0xf3, 0x65, 0x83, 0x0d, 0x27, - 0xc8, 0xaa, 0x4d, 0x75, 0xc8, 0x07, 0x87, 0xbd, 0x10, 0xb7, 0x14, 0xcb, 0x97, 0x9c, - 0x1b, 0x0f, 0x3f, 0x0b, 0x41, 0xee, 0x94, 0x22, 0x94, 0x24, 0x8c, 0x48, 0x5c, 0xf9, - 0x9c, 0x6b, 0xc4, 0x63, 0x20, 0x7a, 0xf3, 0x83, 0x61, 0x97, 0x83, 0x57, 0x41, 0x41, - 0x5d, 0xe6, 0x1f, 0xf2, 0x9f, 0xad, 0x30, 0x01, 0x82, 0x71, 0x4c, 0x20, 0xca, 0x34, - 0x04, 0x7b, 0xcc, 0xb7, 0x05, 0x81, 0x0f, 0xfa, 0xe5, 0x3a, 0x34, 0x16, 0xa5, 0x3f, - 0x28, 0xaf, 0xc0, 0x08, 0xe8, 0xbf, 0xf9, 0x49, 0xe4, 0x3a, 0x54, 0x10, 0xe6, 0xad, - 0xb6, 0x65, 0xf9, 0x9f, 0xa4, 0xca, 0xfa, 0xc2, 0xe0, 0xf2, 0xc0, 0xf1, 0x34, 0xbd, - 0xba, 0x83, 0x81, 0xc2, 0xbb, 0xac, 0x43, 0x33, 0x2a, 0xcd, 0xcb, 0x10, 0x08, 0x2e, - 0xf3, 0x43, 0xa3, 0x5a, 0xc6, 0x4f, 0x4b, 0xa1, 0x6e, 0x49, 0x57, 0xc7, 0x1e, 0x9a, - 0x2b, 0xd9, 0xa5, 0xcd, 0x6a, 0x92, 0x25, 0x8a, 0x9e, 0x58, 0x8e, 0x02, 0x1a, 0x06, - 0x65, 0x09, 0x04, 0x67, 0x0d, 0xa2, 0xc0, 0xe5, 0x2c, 0x52, 0x4f, 0x6e, 0x5c, 0xe3, - 0xee, 0x27, 0x5a, 0x0a, 0x63, 0x10, 0x3b, 0x5f, 0x92, 0x64, 0x16, 0xc0, 0xbd, 0x5d, - 0xa1, 0xae, 0x65, 0x69, 0xd3, 0xa4, 0xee, 0x4d, 0xbc, 0x5e, 0xc0, 0x8b, 0x29, 0x72, - 0x02, 0xc9, 0xd7, 0x13, 0xab, 0xc3, 0x47, 0x4d, 0xe4, 0x94, 0x0f, 0x59, 0xb1, 0xf3, - 0xfe, 0x0e, 0x92, 0x76, 0xa1, 0x76, 0x3b, 0x2d, 0xea, 0x39, 0x40, 0xb0, 0xc1, 0xf7, - 0xab, 0x5d, 0xa3, 0xf4, 0x55, 0x62, 0x3e, 0x04, 0x96, 0x82, 0xd0, 0x92, 0x18, 0x9c, - 0xb7, 0x9e, 0xcf, 0xd4, 0x3c, 0x3b, 0xf1, 0x0e, 0x7f, 0x2c, 0x8d, 0x4d, 0xe3, 0xa7, - 0x36, 0xf8, 0x69, 0xf0, 0x87, 0x03, 0xc4, 0xe5, 0x9f, 0x57, 0x4f, 0x77, 0xaa, 0x86, - 0x1c, 0xbf, 0xdd, 0xd0, 0x7f, 0x77, 0xdc, 0x24, 0xa9, 0x74, 0x10, 0xaf, 0xc7, 0xcf, - 0xbe, 0x3c, 0xe1, 0xff, 0xd2, 0x24, 0x53, 0x5c, 0xf3, 0x05, 0xce, 0xcc, 0x78, 0x56, - 0xa4, 0xd4, 0x8a, 0x6d, 0xec, 0x17, 0xa2, 0x4b, 0x6d, 0x27, 0xfe, 0x26, 0x64, 0xbc, - 0x2b, 0x2b, 0x71, 0x1d, 0x67, 0x13, 0x90, 0x6c, 0xed, 0x8a, 0x80, 0x66, 0x62, 0x18, - 0x40, 0xd9, 0x0c, 0x23, 0xae, 0x33, 0x77, 0x30, 0x67, 0x9d, 0x2c, 0xde, 0x32, 0x69, - 0xab, 0x1f, 0x42, 0xac, 0x03, 0xff, 0xdb, 0xa0, 0x32, 0xd3, 0x2c, 0xa8, 0x79, 0x63, - 0x82, 0x56, 0x56, 0x5d, 0xe1, 0xd2, 0xde, 0x39, 0xf5, 0x6f, 0x94, 0x57, 0x95, 0xd6, - 0xe9, 0x58, 0xe6, 0x93, 0xdc, 0x8c, 0xbf, 0x6d, 0x04, 0x30, 0x00, 0xcc, 0x7a, 0x40, - 0x15, 0xf0, 0x2d, 0x0f, 0xe3, 0x97, 0xec, 0x57, 0xf8, 0xfe, 0x29, 0x2e, 0x85, 0x14, - 0x24, 0xe8, 0x40, 0x6d, 0x38, 0xdd, 0xb8, 0xd1, 0xde, 0x9d, 0xef, 0x67, 0x2e, 0x92, - 0x7d, 0x3d, 0xc1, 0xf4, 0x11, 0xdc, 0x78, 0xad, 0xa7, 0x61, 0x00, 0x91, 0xbf, 0xe2, - 0x63, 0xcd, 0x79, 0x96, 0xd1, 0x80, 0x5e, 0xe4, 0x91, 0xe9, 0x95, 0x91, 0xd6, 0xef, - 0xdb, 0x2e, 0x3c, 0x79, 0x71, 0x57, 0x41, 0xd0, 0xd4, 0x72, 0xac, 0x11, 0xdb, 0x78, - 0x64, 0x4f, 0x3d, 0x23, 0xe5, 0x8f, 0x0b, 0x01, 0xa8, 0x61, 0xe0, 0x85, 0x65, 0x53, - 0x52, 0x07, 0xcd, 0x5e, 0x71, 0x0f, 0xc3, 0x3e, 0xb2, 0xf8, 0x92, 0x8b, 0xc7, 0xd4, - 0x01, 0x7e, 0x4e, 0x56, 0xc0, 0xc2, 0xeb, 0x95, 0x85, 0xd6, 0x99, 0x74, 0x5e, 0x3b, - 0xb9, 0x61, 0x8b, 0x2c, 0x1b, 0x90, 0xf2, 0x35, 0x1b, 0xaf, 0x27, 0x6a, 0x70, 0x17, - 0xb0, 0xfc, 0xfa, 0xcb, 0x52, 0xea, 0x27, 0x31, 0x95, 0xa8, 0xde, 0xe1, 0x67, 0x79, - 0x13, 0xc7, 0x86, 0xcc, 0x3a, 0xcb, 0x06, 0xa9, 0xec, 0x7a, 0x37, 0xb0, 0x58, 0x98, - 0x0c, 0xeb, 0x3c, 0x82, 0xaa, 0xb0, 0x3e, 0xaf, 0xc1, 0xbb, 0x88, 0xcf, 0x7a, 0xb7, - 0x98, 0xf1, 0x65, 0x1d, 0x67, 0xbf, 0x22, 0x30, 0xd5, 0x34, 0xec, 0x55, 0x23, 0x1d, - 0x21, 0x31, 0x7b, 0x1c, 0xb3, 0x0b, 0x3c, 0x38, 0xff, 0x8d, 0x21, 0x1b, 0x76, 0x36, - 0x70, 0x2a, 0x25, 0xca, 0x7c, 0xa1, 0xbf, 0xf1, 0xf2, 0xc1, 0x58, 0xc6, 0xef, 0x22, - 0x13, 0xff, 0xab, 0xb9, 0xc0, 0x9f, 0x5c, 0x47, 0xe7, 0x3b, 0xbe, 0xbb, 0xd3, 0x7f, - 0x3d, 0x3e, 0xbc, 0x24, 0xa6, 0x65, 0xb2, 0x9f, 0x10, 0xde, 0x8b, 0x9c, 0xf1, 0x94, - 0x2d, 0x90, 0xb4, 0xc3, 0x1d, 0x89, 0xa9, 0x88, 0x3b, 0xf5, 0xa0, 0x27, 0xe9, 0x20, - 0xd1, 0xb8, 0x51, 0x19, 0xf2, 0xf2, 0xf9, 0x5f, 0xd5, 0x5e, 0xda, 0x85, 0x75, 0xa4, - 0xdb, 0x62, 0x69, 0x05, 0x68, 0x1c, 0x29, 0xe8, 0xd8, 0xe7, 0x41, 0xd4, 0x20, 0xa8, - 0x34, 0x42, 0xa9, 0xd3, 0x8a, 0xf4, 0x19, 0x9e, 0xf9, 0x5c, 0xb3, 0x0b, 0xc4, 0x4e, - 0x93, 0xfe, 0x4d, 0x0e, 0xb7, 0x42, 0x22, 0xfc, 0x10, 0xac, 0x8d, 0x40, 0x0e, 0x10, - 0xed, 0x4e, 0x56, 0xfa, 0x39, 0xda, 0x01, 0x2a, 0xc1, 0x8d, 0xee, 0x4d, 0x99, 0x42, - 0x5c, 0x8f, 0x71, 0x4c, 0x51, 0xac, 0x1b, 0xa5, 0x6e, 0x0e, 0x81, 0x47, 0x4b, 0xad, - 0x3e, 0x74, 0x18, 0xed, 0x4c, 0x82, 0xb4, 0xd7, 0x75, 0x12, 0x0b, 0x19, 0x3e, 0xdc, - 0x66, 0x76, 0x30, 0x32, 0x66, 0xe3, 0x1e, 0xcf, 0x55, 0x1e, 0xb9, 0x13, 0xa6, 0x41, - 0x15, 0xbc, 0xcb, 0xbb, 0x2e, 0xcc, 0x89, 0x81, 0x55, 0x21, 0xe5, 0x6e, 0x07, 0xc8, - 0x8b, 0xbb, 0x4a, 0x55, 0xe9, 0x94, 0x5d, 0x03, 0xdb, 0x2d, 0xa0, 0xfc, 0xae, 0x3c, - 0x08, 0xf1, 0xd7, 0x7c, 0x57, 0x26, 0x1e, 0x98, 0x23, 0x66, 0x03, 0xa8, 0xc5, 0x2c, - 0x6c, 0x27, 0x98, 0xb5, 0x45, 0x61, 0xaf, 0xfe, 0x07, 0x61, 0xe6, 0xab, 0x24, 0x72, - 0x07, 0xad, 0xfc, 0x3c, 0x43, 0x22, 0xbe, 0x0f, 0xb2, 0x49, 0xbf, 0xd3, 0xc5, 0xe7, - 0xfb, 0x38, 0x37, 0xe9, 0xff, 0x21, 0x35, 0x07, 0x3a, 0xe1, 0x36, 0x0d, 0xcf, 0xaf, - 0x5f, 0xb6, 0x78, 0x56, 0x8f, 0xd8, 0x4d, 0x99, 0xa5, 0x1f, 0x32, 0xeb, 0x94, 0xcc, - 0xf5, 0xf2, 0x39, 0x02, 0x5b, 0x2b, 0x97, 0xbe, 0xf6, 0x25, 0xdb, 0xb6, 0x7f, 0x20, - 0xc3, 0xe0, 0xd9, 0x51, 0x73, 0x12, 0x9c, 0x06, 0x37, 0x50, 0x39, 0x52, 0x13, 0x41, - 0x49, 0x24, 0xe0, 0xa3, 0xfd, 0xd3, 0x66, 0xff, 0xd4, 0x69, 0xc9, 0xeb, 0xea, 0x79, - 0xfb, 0x76, 0xaf, 0x10, 0xea, 0x45, 0xb5, 0x66, 0xf1, 0xfc, 0x92, 0xaf, 0x48, 0xce, - 0xe2, 0x11, 0xf8, 0xe1, 0xb0, 0x58, 0xfb, 0x72, 0x1a, 0x8b, 0x22, 0xce, 0x43, 0x0c, - 0x54, 0x94, 0x0e, 0x24, 0xb3, 0x30, 0x8e, 0x57, 0x0a, 0xb8, 0x57, 0x25, 0x0d, 0x10, - 0xcd, 0xec, 0xe1, 0x05, 0x07, 0x1b, 0xc8, 0x66, 0xea, 0x4d, 0x6d, 0x5c, 0x69, 0xf9, - 0x59, 0x28, 0xf3, 0x9f, 0x7f, 0x1f, 0xcd, 0xf1, 0x5a, 0xcd, 0xbb, 0xec, 0x67, 0xd8, - 0x48, 0xf7, 0xc1, 0xb2, 0xef, 0x57, 0x7f, 0x48, 0xa7, 0x0b, 0x4b, 0xf3, 0xd8, 0xa7, - 0x88, 0x14, 0x31, 0x6b, 0x3d, 0x7f, 0xa3, 0xe3, 0xc9, 0x8c, 0xdf, 0xa1, 0x78, 0xb9, - 0x89, 0xbc, 0x78, 0xde, 0x8d, 0x24, 0xc1, 0xbb, 0xc0, 0x9d, 0x20, 0x7e, 0x11, 0x18, - 0x1e, 0x59, 0x1a, 0x60, 0x9a, 0xbf, 0xf9, 0xa2, 0x00, 0xd3, 0x4e, 0x1a, 0xc6, 0x3a, - 0x38, 0xf0, 0x40, 0x05, 0x3a, 0x32, 0x01, 0x68, 0xb8, 0x23, 0xac, 0x76, 0x6e, 0x02, - 0x6c, 0xbe, 0x1a, 0xbf, 0x27, 0x55, 0xbe, 0x0c, 0x73, 0xc8, 0xfd, 0x98, 0x62, 0x55, - 0x56, 0x40, 0x6c, 0x14, 0x99, 0x3f, 0x6a, 0x28, 0xae, 0x4b, 0xb3, 0xa4, 0x73, 0xa1, - 0x8d, 0xd3, 0x74, 0x3d, 0x88, 0x7e, 0xac, 0x54, 0x8e, 0xb7, 0xca, 0x4d, 0x46, 0x15, - 0x7c, 0x62, 0xb7, 0x29, 0xf3, 0x66, 0xa9, 0x56, 0x02, 0x28, 0x7c, 0x8c, 0x56, 0x33, - 0x5b, 0x78, 0xbc, 0x68, 0x9f, 0xc5, 0x38, 0x9c, 0x39, 0x79, 0xb8, 0xe7, 0x5d, 0xaf, - 0x31, 0xbd, 0x60, 0xa9, 0xcc, 0x2a, 0x92, 0x0d, 0xbc, 0xc6, 0x71, 0xdd, 0xe2, 0x7e, - 0xb4, 0x60, 0x0f, 0x12, 0xdc, 0x2a, 0xb3, 0x94, 0x4a, 0xa1, 0x9c, 0x71, 0xa9, 0x87, - 0xd8, 0x71, 0x3d, 0x99, 0xa4, 0xba, 0x9b, 0x9a, 0x19, 0xa9, 0x21, 0x60, 0x6c, 0x56, - 0x20, 0xc1, 0x67, 0xd4, 0xc7, 0xf4, 0xa2, 0x8a, 0x46, 0x4a, 0x9d, 0x16, 0xc4, 0xb0, - 0xd7, 0x4e, 0x0e, 0x75, 0xdf, 0x6d, 0xba, 0x0e, 0x1d, 0xfe, 0x60, 0x1c, 0x04, 0xc8, - 0xeb, 0x37, 0x01, 0x0e, 0x13, 0x92, 0x1d, 0x5b, 0x6c, 0x93, 0xb9, 0xf0, 0xc3, 0xdd, - 0xd3, 0x2f, 0x7b, 0xec, 0xb2, 0xd7, 0x7d, 0x79, 0xa1, 0x61, 0x8a, 0x79, 0xf7, 0x3c, - 0x45, 0x9b, 0x0d, 0xf5, 0x29, 0x7f, 0x8e, 0xab, 0xd6, 0xed, 0x06, 0xfd, 0x23, 0x40, - 0xe8, 0x60, 0x0a, 0x95, 0xd7, 0x2c, 0xef, 0xd1, 0x2e, 0x62, 0x2c, 0x57, 0xb4, 0x57, - 0xa4, 0xe8, 0x39, 0x75, 0x93, 0x74, 0x6a, 0x6b, 0xcf, 0x04, 0xc4, 0x9c, 0x6d, 0xd4, - 0xa3, 0x36, 0x68, 0xda, 0x53, 0x8d, 0x90, 0x93, 0xa4, 0x50, 0xa4, 0xd8, 0x24, 0x51, - 0xb6, 0x12, 0xff, 0x54, 0x70, 0x73, 0x8e, 0x62, 0xbf, 0xdf, 0xc7, 0x9b, 0x3e, 0x31, - 0xbb, 0x47, 0xfc, 0xa1, 0xe9, 0x87, 0x22, 0xa5, 0x98, 0x3a, 0xff, 0xe5, 0xf6, 0x32, - 0x84, 0x0b, 0x92, 0x3a, 0xb5, 0x6b, 0x1d, 0xa1, 0x53, 0xd3, 0x5d, 0x82, 0x23, 0x24, - 0xe7, 0xd5, 0x6d, 0x61, 0x3c, 0x73, 0xeb, 0xc6, 0x34, 0x1e, 0xa0, 0x3b, 0xee, 0x3a, - 0xb9, 0x73, 0xe8, 0x4d, 0x8f, 0xfc, 0x4a, 0x7c, 0x58, 0x13, 0x83, 0xe2, 0x14, 0x2d, - 0x29, 0x2a, 0x58, 0x0b, 0x6d, 0x30, 0x83, 0x43, 0xdc, 0xf1, 0xef, 0x49, 0x29, 0xa9, - 0xe3, 0xe6, 0x15, 0x32, 0xfc, 0xff, 0xb7, 0x4d, 0x30, 0x19, 0xf4, 0xe2, 0xd6, 0xd3, - 0x11, 0x78, 0x57, 0x5a, 0xca, 0x94, 0x12, 0x99, 0x22, 0x50, 0x44, 0xe1, 0xd3, 0x7b, - 0xab, 0x9f, 0x10, 0xe2, 0x9f, 0xd9, 0x6f, 0x9c, 0xf6, 0x84, 0xaf, 0x98, 0xed, 0x64, - 0x8b, 0x83, 0xd6, 0x1e, 0x52, 0x5b, 0xe3, 0x2c, 0xdb, 0x45, 0x3d, 0x2d, 0x38, 0x93, - 0x5f, 0xee, 0xb3, 0x22, 0xce, 0xb9, 0xd2, 0xa2, 0xe9, 0x5e, 0xb7, 0xfc, 0x61, 0x2d, - 0x89, 0xf4, 0xcf, 0xe8, 0x93, 0x22, 0x8e, 0x88, 0x28, 0xb1, 0x89, 0x00, 0x90, 0x45, - 0x62, 0x90, 0x75, 0xc0, 0xc2, 0x03, 0x9d, 0x5a, 0x73, 0x32, 0xfd, 0xbc, 0xd7, 0xc7, - 0xb0, 0x91, 0x01, 0x5c, 0x45, 0x69, 0xa3, 0x00, 0x53, 0x23, 0x56, 0xbb, 0xad, 0x08, - 0xff, 0xa3, 0xbb, 0x16, 0x7a, 0x3e, 0xbe, 0xb4, 0x62, 0x66, 0xb7, 0x06, 0x06, 0x49, - 0x4a, 0xda, 0xe9, 0x14, 0x9e, 0x1a, 0x64, 0xc0, 0xa0, 0xaa, 0x5d, 0xaa, 0x53, 0x62, - 0xd3, 0xc7, 0xa8, 0x96, 0xfd, 0x52, 0x78, 0x08, 0xd0, 0xa3, 0xc1, 0xcf, 0x70, 0x61, - 0xba, 0x67, 0x89, 0x39, 0x80, 0x78, 0x85, 0x0b, 0xe4, 0xb9, 0x94, 0x0e, 0x01, 0xae, - 0xbb, 0x93, 0x6d, 0xd8, 0x1a, 0x31, 0x82, 0x04, 0x28, 0x1d, 0x43, 0x97, 0x6f, 0x4e, - 0x0f, 0xa2, 0x07, 0xe4, 0xbe, 0x1f, 0xb8, 0x2c, 0x91, 0xbb, 0x26, 0x42, 0xf7, 0x36, - 0x85, 0x6d, 0xcd, 0x5a, 0xeb, 0x75, 0xc5, 0x0a, 0xf2, 0x00, 0xe1, 0x4b, 0xe5, 0xb7, - 0x8c, 0xe6, 0x9a, 0x88, 0x51, 0x54, 0xef, 0xe3, 0x0e, 0xdd, 0x09, 0xae, 0x8c, 0x5e, - 0xb5, 0x3f, 0x4b, 0x8b, 0x7c, 0x75, 0x35, 0x37, 0x3c, 0x0f, 0xe6, 0xcf, 0xe4, 0x48, - 0xa9, 0xb9, 0xf4, 0xd9, 0xe3, 0x10, 0x93, 0x03, 0xd6, 0xce, 0xe9, 0x10, 0x6a, 0xa2, - 0x2b, 0xd5, 0x9a, 0xe0, 0xe0, 0x27, 0xd3, 0x25, 0x6a, 0x75, 0xb9, 0xc5, 0xd6, 0x07, - 0x09, 0x09, 0x97, 0x53, 0xce, 0x57, 0x2c, 0x9e, 0x29, 0xdc, 0x92, 0x56, 0x2d, 0x1c, - 0x3f, 0x4a, 0x0b, 0x4d, 0x36, 0xa6, 0xfe, 0xc2, 0x1b, 0xa4, 0x94, 0x17, 0x3e, 0x44, - 0xd7, 0x9b, 0xc2, 0x34, 0x18, 0x95, 0xbd, 0x0c, 0x70, 0x96, 0xf0, 0x97, 0x4f, 0x12, - 0x67, 0xfe, 0xf6, 0x72, 0x1d, 0x58, 0xb8, 0xc4, 0xe3, 0x34, 0xf1, 0x4d, 0x86, 0xc0, - 0xee, 0x3b, 0x1a, 0xb5, 0x88, 0x0c, 0xa4, 0x29, 0x8d, 0x7f, 0x84, 0x76, 0x3b, 0xdc, - 0x71, 0x09, 0xbc, 0x82, 0x0f, 0x45, 0xc5, 0x04, 0x53, 0xe3, 0x3d, 0x96, 0x8e, 0xf9, - 0xd8, 0x6c, 0xd6, 0xeb, 0xe7, 0x15, 0xe8, 0x9d, 0x5d, 0xe3, 0x24, 0x09, 0x10, 0xc5, - 0x9c, 0x36, 0xec, 0x8f, 0xe9, 0x9b, 0x32, 0x49, 0x16, 0x30, 0xab, 0x35, 0xb1, 0x24, - 0x53, 0x1d, 0x9c, 0x29, 0xe0, 0x46, 0xc4, 0x78, 0xe6, 0x2a, 0xd1, 0x8b, 0x25, 0x39, - 0xa5, 0x09, 0x6e, 0xe2, 0x9a, 0x4d, 0x4b, 0x4b, 0x53, 0xa1, 0xcf, 0xfa, 0x93, 0x23, - 0xbc, 0x73, 0x21, 0x81, 0x7d, 0x96, 0xfd, 0x02, 0x05, 0xea, 0x9c, 0xbc, 0x4e, 0x15, - 0x88, 0xb7, 0x61, 0x3d, 0x4c, 0x39, 0x3c, 0xac, 0x21, 0x05, 0xb2, 0x8f, 0xd0, 0x46, - 0x7a, 0x0b, 0xf0, 0x23, 0xf0, 0x0d, 0x1a, 0x17, 0xf6, 0x53, 0xcd, 0xb6, 0xb5, 0xa8, - 0x3e, 0x4c, 0xf1, 0x5c, 0x34, 0x7b, 0x34, 0xb9, 0x7f, 0xbf, 0xe6, 0xea, 0xee, 0x13, - 0xbb, 0x90, 0x15, 0x3a, 0xfd, 0xc9, 0x11, 0x26, 0x37, 0xfa, 0xd1, 0xcf, 0xe1, 0x7e, - 0xdd, 0xcb, 0x0c, 0x81, 0x9e, 0x60, 0xd3, 0x50, 0x39, 0x34, 0x9b, 0x69, 0xf7, 0xca, - 0x9b, 0xa6, 0x4d, 0xf9, 0xf5, 0xe4, 0x71, 0x11, 0x5c, 0xd6, 0x79, 0x26, 0xbd, 0xf1, - 0x6e, 0x30, 0x12, 0x39, 0x8d, 0xae, 0x59, 0x5b, 0xfd, 0x25, 0xf3, 0xae, 0xe5, 0x8a, - 0xcf, 0xfe, 0x2f, 0x3e, 0xd7, 0x48, 0xfd, 0xf9, 0x3a, 0x6e, 0xd2, 0x1e, 0x87, 0x2d, - 0x94, 0x97, 0xa9, 0xf3, 0xb7, 0xb1, 0x6b, 0x7e, 0xa9, 0xea, 0x19, 0xf2, 0x47, 0x9e, - 0x4f, 0x8b, 0x6d, 0x42, 0x3f, 0xa1, 0x5f, 0xbc, 0xdf, 0xa3, 0xc9, 0x9b, 0x9a, 0x39, - 0x70, 0xee, 0x74, 0xa8, 0xd8, 0x5e, 0xc2, 0x15, 0x96, 0x52, 0xda, 0xa7, 0x67, 0x03, - 0x12, 0x63, 0xbb, 0x4b, 0x49, 0x28, 0x5d, 0x70, 0x5e, 0x24, 0xe8, 0x19, 0x26, 0x86, - 0xeb, 0xc8, 0xff, 0x85, 0x98, 0xd2, 0x4b, 0x51, 0x23, 0x2a, 0x99, 0x38, 0x56, 0x5d, - 0x0f, 0x68, 0xbe, 0x7f, 0x3a, 0x53, 0x36, 0x4a, 0xcc, 0x69, 0x21, 0xa3, 0x5b, 0xc5, - 0x99, 0x10, 0xbb, 0x71, 0xfb, 0x58, 0xb8, 0x67, 0x37, 0x3c, 0xe9, 0x5f, 0x19, 0x84, - 0x09, 0xaa, 0xef, 0x97, 0xf4, 0x01, 0xe4, 0x33, 0x00, 0x4b, 0x99, 0x19, 0x04, 0x9f, - 0x93, 0x7f, 0xd7, 0x76, 0xc4, 0xb6, 0x31, 0xa5, 0x91, 0x2a, 0x08, 0xd4, 0x9f, 0xdf, - 0x65, 0x28, 0xf8, 0x1a, 0x6f, 0x32, 0x00, 0x09, 0x37, 0x67, 0xbb, 0x77, 0x89, 0xd9, - 0x5a, 0x75, 0x03, 0x0a, 0xc1, 0xd2, 0x4c, 0x2c, 0x75, 0xbd, 0x60, 0x38, 0x25, 0x52, - 0x86, 0x3f, 0x09, 0x8d, 0x36, 0xbd, 0x48, 0x33, 0x28, 0x3d, 0x3a, 0x2d, 0x21, 0x5d, - 0x10, 0xc7, 0xff, 0xe9, 0xc8, 0x40, 0x37, 0x23, 0x14, 0x45, 0x58, 0x33, 0x29, 0x26, - 0x16, 0x74, 0x19, 0x3b, 0xdd, 0x1c, 0x64, 0x81, 0xbe, 0xf9, 0xf2, 0x26, 0xe1, 0xe6, - 0x0b, 0xb1, 0xc7, 0x76, 0xa4, 0xbe, 0x7d, 0xc6, 0x9b, 0x44, 0x30, 0xa7, 0x5a, 0x0c, - 0xbd, 0x55, 0x86, 0x7a, 0x6f, 0x46, 0xff, 0x93, 0x03, 0xf9, 0xa2, 0x9b, 0x6f, 0x3f, - 0x7c, 0x7a, 0x9c, 0x9f, 0xbc, 0xf7, 0x47, 0xb2, 0x3f, 0x86, 0x45, 0xf4, 0xda, 0x3d, - 0x9f, 0x72, 0xd0, 0xd8, 0x76, 0xa7, 0x5e, 0x54, 0x8a, 0x49, 0xdb, 0x37, 0x5b, 0x40, - 0xeb, 0xe1, 0xbb, 0xe0, 0x81, 0x7a, 0x99, 0x49, 0xde, 0xc1, 0x15, 0x7d, 0x62, 0xa7, - 0x1d, 0xbf, 0xbd, 0x9b, 0xb1, 0xd6, 0x55, 0x17, 0x53, 0xdf, 0xf5, 0xbb, 0x7f, 0xc9, - 0x36, 0x48, 0xd4, 0xeb, 0x6c, 0xad, 0x41, 0x67, 0x33, 0xad, 0xfd, 0xcc, 0x87, 0x08, - 0xdd, 0xe8, 0xbe, 0x87, 0x34, 0xd0, 0x5d, 0xec, 0x9e, 0x45, 0xdf, 0x3f, 0xa4, 0x5a, - 0xda, 0xc4, 0x1a, 0x6d, 0x23, 0xa2, 0x24, 0xa0, 0x4f, 0xdc, 0x0d, 0x96, 0x73, 0x87, - 0x98, 0x0f, 0x95, 0xe6, 0x27, 0xe6, 0xb3, 0xdc, 0xe1, 0x9c, 0xaf, 0x01, 0x09, 0x84, - 0x8c, 0xa9, 0xda, 0xea, 0x2e, 0x24, 0x6e, 0x62, 0xc2, 0x85, 0x07, 0xd2, 0x56, 0xeb, - 0xab, 0xe1, 0x18, 0xf1, 0xf6, 0xef, 0x97, 0x6e, 0x4a, 0x31, 0xa0, 0xe4, 0x14, 0x3c, - 0x43, 0x60, 0xd8, 0xb1, 0x79, 0xb3, 0x0e, 0x4b, 0xfa, 0x7e, 0x16, 0x1b, 0x1e, 0x6c, - 0x70, 0x7d, 0x8e, 0xae, 0x76, 0x28, 0x71, 0x59, 0x21, 0x94, 0x1e, 0x78, 0x54, 0xe1, - 0x0d, 0x11, 0x99, 0x12, 0x58, 0xc4, 0x3f, 0xe6, 0xc4, 0x45, 0x29, 0xf6, 0x61, 0x4b, - 0x58, 0x41, 0x61, 0x5d, 0x3e, 0x4e, 0x77, 0xfb, 0x09, 0xa6, 0xf0, 0x20, 0xe0, 0xb8, - 0x32, 0x28, 0xac, 0x17, 0x55, 0xad, 0x47, 0x71, 0x16, 0xde, 0xca, 0xac, 0x51, 0x7b, - 0xfb, 0xcf, 0x67, 0x37, 0xf5, 0xbb, 0x99, 0xe0, 0x07, 0xeb, 0x64, 0x00, 0x76, 0x6b, - 0x6c, 0xfd, 0xd7, 0x37, 0xe2, 0x08, 0x57, 0xdf, 0x3c, 0x85, 0xca, 0x16, 0xab, 0x21, - 0x17, 0x7b, 0x53, 0x1e, 0x55, 0x32, 0xc4, 0x45, 0xde, 0xd0, 0x0c, 0x1e, 0x96, 0x63, - 0x5e, 0x9f, 0x50, 0x0b, 0xa8, 0x76, 0x44, 0xb8, 0xc1, 0xd5, 0x33, 0x25, 0x37, 0xab, - 0xf2, 0x9f, 0xcc, 0xab, 0x8a, 0xe3, 0xe3, 0x88, 0x27, 0x18, 0x82, 0x6b, 0xdb, 0x8d, - 0xbd, 0xb8, 0x51, 0xa4, 0x77, 0x05, 0xeb, 0x0d, 0xec, 0x2d, 0x5e, 0xe9, 0x39, 0xdc, - 0x79, 0x87, 0x25, 0x6f, 0xee, 0xe6, 0x7f, 0x09, 0x90, 0x28, 0xf1, 0x45, 0xe2, 0x0b, - 0xf4, 0x88, 0x94, 0x98, 0x24, 0x30, 0x14, 0x35, 0x13, 0x73, 0xfd, 0xf6, 0x33, 0x01, - 0x8d, 0x21, 0x7c, 0x58, 0x8c, 0x52, 0x98, 0x6f, 0xc5, 0x24, 0xe7, 0x97, 0x97, 0xab, - 0x65, 0x58, 0x43, 0xc2, 0x61, 0xae, 0x7f, 0xc9, 0xcc, 0x3f, 0x47, 0x05, 0x46, 0x00, - 0xe4, 0xcd, 0x38, 0x5c, 0x46, 0x7a, 0x78, 0x8a, 0x9f, 0xff, 0xc3, 0x7e, 0x9d, 0xdb, - 0xb5, 0xd3, 0xe8, 0xa4, 0xbd, 0x0c, 0x4e, 0x8f, 0x56, 0xe5, 0x69, 0x5a, 0xfa, 0x90, - 0xfe, 0x50, 0xce, 0x0a, 0x30, 0x04, 0xfe, 0xd7, 0x12, 0xb4, 0xde, 0x15, 0xad, 0x5f, - 0x01, 0x71, 0xad, 0x51, 0xed, 0xfa, 0x54, 0xdb, 0xd4, 0x8b, 0x1f, 0xcc, 0x5e, 0xf6, - 0xac, 0x73, 0xcf, 0x0a, 0x28, 0xe9, 0xd9, 0x3e, 0x0c, 0xaf, 0xad, 0x88, 0x16, 0x76, - 0x1b, 0x3b, 0xe6, 0x38, 0x39, 0x8c, 0x00, 0x14, 0x33, 0x38, 0xea, 0x27, 0xa9, 0xff, - 0xf2, 0x2e, 0xc4, 0x73, 0x16, 0x36, 0x96, 0x12, 0x25, 0xca, 0x49, 0xe0, 0x13, 0xa6, - 0xdc, 0x80, 0x2b, 0xc7, 0xfb, 0x77, 0xca, 0xd1, 0x0a, 0xca, 0xfe, 0xfc, 0xe5, 0xfa, - 0x9a, 0x37, 0x35, 0x63, 0xb3, 0x91, 0x7a, 0x3a, 0x37, 0x39, 0xcc, 0x97, 0x80, 0xea, - 0x81, 0x50, 0x73, 0xde, 0x8e, 0xb4, 0x2e, 0x3f, 0x66, 0x93, 0xe8, 0x52, 0xbe, 0xfd, - 0xde, 0xdd, 0x61, 0x91, 0x29, 0xd0, 0xaa, 0x13, 0xc4, 0xbd, 0x83, 0x86, 0x22, 0xb5, - 0xe3, 0x28, 0x56, 0x35, 0x8e, 0x6d, 0x82, 0x78, 0x78, 0x95, 0x7e, 0x5d, 0xc8, 0x2c, - 0xd4, 0x37, 0x0b, 0x66, 0x10, 0x84, 0x9e, 0x95, 0x6d, 0x0a, 0x7c, 0xdf, 0xf5, 0x61, - 0x8f, 0x5c, 0x2c, 0xea, 0x61, 0x23, 0x0b, 0x47, 0x00, 0x1c, 0x30, 0xe5, 0xa8, 0xf9, - 0x37, 0xca, 0x7f, 0x9f, 0x9e, 0x66, 0x0f, 0xfa, 0xa7, 0x71, 0x80, 0xcb, 0xa2, 0x6f, - 0x90, 0xda, 0x00, 0x7c, 0xda, 0x40, 0x57, 0xa6, 0xce, 0xa2, 0xe2, 0x6b, 0xfd, 0xe5, - 0x0c, 0x7f, 0x90, 0x79, 0x88, 0x00, 0x53, 0xd0, 0x5d, 0xaa, 0xaa, 0xb3, 0xd7, 0xe4, - 0xdc, 0x9d, 0x81, 0xd0, 0x99, 0x0d, 0x2b, 0xc3, 0x69, 0xa6, 0x6b, 0x55, 0xac, 0x8b, - 0x63, 0x97, 0xbd, 0x47, 0xdb, 0x42, 0x89, 0xc5, 0x45, 0x22, 0x85, 0x55, 0x1a, 0xaa, - 0x7f, 0xa6, 0x7b, 0x01, 0x36, 0xcd, 0x11, 0x9f, 0x87, 0xd8, 0x21, 0x9e, 0x00, 0x02, - 0x97, 0xf0, 0x2c, 0x0c, 0xe6, 0xe3, 0x7b, 0x62, 0x0f, 0x5e, 0x47, 0xfc, 0xa0, 0x3a, - 0xcd, 0xd6, 0x54, 0x4a, 0x47, 0xf4, 0xde, 0xef, 0x19, 0x4f, 0x95, 0x9a, 0xdc, 0x36, - 0x8b, 0x3b, 0x5d, 0x27, 0xd3, 0x83, 0xfe, 0x2f, 0x2b, 0x52, 0x5d, 0xae, 0x04, 0x50, - 0x55, 0x06, 0x35, 0xaa, 0x21, 0x58, 0x18, 0xf7, 0xf5, 0x03, 0x78, 0x90, 0xf0, 0x53, - 0x23, 0x3f, 0x9a, 0xa5, 0x0a, 0xe2, 0x9c, 0x05, 0x56, 0xc3, 0x6d, 0x67, 0xb2, 0x64, - 0x7e, 0x54, 0xeb, 0xe7, 0x58, 0x8e, 0x1f, 0x02, 0xb3, 0xc7, 0x17, 0xdf, 0x02, 0x98, - 0x43, 0x0e, 0xc9, 0xd2, 0xbb, 0x11, 0x4b, 0x35, 0x42, 0xb7, 0x5d, 0x01, 0x0d, 0x93, - 0x4e, 0x58, 0x96, 0xe1, 0xd2, 0xd1, 0x0a, 0x09, 0x20, 0x11, 0x9d, 0xf7, 0x29, 0x2c, - 0x8c, 0x28, 0x47, 0x65, 0x0f, 0xbf, 0x42, 0x80, 0x57, 0x12, 0x8a, 0x02, 0x04, 0x0e, - 0xb3, 0xe3, 0x2d, 0xb5, 0x0c, 0xa7, 0xd8, 0xda, 0x7f, 0xf4, 0xc4, 0xa7, 0xa0, 0xe9, - 0xcf, 0x4b, 0x65, 0x2b, 0x65, 0x3d, 0x42, 0x8f, 0x83, 0xf4, 0x85, 0x33, 0x57, 0x84, - 0x1b, 0x28, 0x13, 0x80, 0x55, 0xb9, 0x13, 0x81, 0x17, 0x79, 0x0a, 0x91, 0xe2, 0x8f, - 0xaa, 0x41, 0x2f, 0xd7, 0xd0, 0x73, 0x32, 0x56, 0x73, 0x44, 0x85, 0xd1, 0xd6, 0xd1, - 0xa9, 0x8c, 0xc2, 0xd7, 0xc8, 0x2b, 0x37, 0x9e, 0x60, 0x72, 0x5d, 0x31, 0x8c, 0x14, - 0x77, 0xce, 0x49, 0x6c, 0x95, 0x86, 0x31, 0x08, 0xa1, 0xc7, 0xe4, 0xf0, 0x20, 0x0b, - 0x7a, 0x3c, 0x08, 0x8d, 0xe7, 0x7e, 0xb4, 0xbc, 0x95, 0xa1, 0xc6, 0xc8, 0x39, 0xd7, - 0x5f, 0xab, 0x59, 0x40, 0xd3, 0x07, 0x94, 0x24, 0xd5, 0x23, 0xd6, 0xd9, 0xa4, 0x6b, - 0xe5, 0x4e, 0x18, 0xf5, 0x29, 0xdc, 0x9e, 0x56, 0x77, 0x6c, 0x5e, 0xc4, 0x51, 0xce, - 0x28, 0x07, 0x9d, 0x37, 0x82, 0x6a, 0xec, 0x40, 0x97, 0xca, 0x7a, 0xee, 0xc8, 0x08, - 0x3f, 0xf5, 0xc4, 0x29, 0x56, 0x9f, 0x91, 0x53, 0xf6, 0x96, 0xbe, 0x62, 0xbd, 0x38, - 0xa3, 0xe7, 0x27, 0xa6, 0x8a, 0xcc, 0xdf, 0xab, 0x02, 0x9b, 0x0b, 0x21, 0xe6, 0xd0, - 0xcd, 0x46, 0x0a, 0x57, 0xd5, 0xf9, 0x03, 0xda, 0x18, 0xa6, 0x07, 0x86, 0xb3, 0x91, - 0xdd, 0x1f, 0x5b, 0xe9, 0x49, 0x82, 0x7e, 0x0c, 0xe7, 0xdf, 0xd1, 0xe0, 0x84, 0x27, - 0xf0, 0xd3, 0xc2, 0x86, 0x53, 0x78, 0xc7, 0x3d, 0x46, 0xa7, 0x3c, 0x55, 0x4a, 0x12, - 0x99, 0x86, 0x02, 0x2a, 0x4f, 0x38, 0x36, 0x0c, 0x39, 0xeb, 0x9c, 0xdd, 0x05, 0x0f, - 0x56, 0xec, 0x05, 0x95, 0x68, 0x65, 0x3c, 0x78, 0x29, 0xe0, 0xa4, 0x4f, 0x2c, 0x70, - 0x10, 0xad, 0xb6, 0x73, 0xe8, 0xde, 0x77, 0x04, 0xe5, 0x4c, 0x03, 0xa7, 0x7a, 0xb7, - 0x8e, 0x85, 0xb6, 0x3f, 0x2b, 0x91, 0x18, 0x5c, 0xa5, 0xda, 0x67, 0xd3, 0x28, 0x30, - 0x65, 0x8b, 0x54, 0xbb, 0x33, 0x58, 0x75, 0x13, 0xc4, 0x2e, 0x03, 0xb5, 0x2c, 0xeb, - 0x9a, 0x19, 0x57, 0xa9, 0xe9, 0x05, 0x84, 0x72, 0x37, 0xce, 0x44, 0x56, 0xe5, 0x33, - 0x50, 0x68, 0x26, 0x49, 0x0e, 0xc5, 0x55, 0x2b, 0x39, 0x12, 0xdb, 0x1c, 0x88, 0x0e, - 0xd4, 0x71, 0xb1, 0x09, 0x29, 0x98, 0xdc, 0xc1, 0x6f, 0xa9, 0x8d, 0x5a, 0xe9, 0xe7, - 0x6f, 0xd2, 0x9d, 0x17, 0x9f, 0xd7, 0x36, 0x59, 0x78, 0xc0, 0x80, 0x44, 0x51, 0x18, - 0x80, 0x1a, 0xc1, 0x0d, 0xc0, 0xf5, 0x78, 0x8f, 0x47, 0x86, 0x69, 0x34, 0xb9, 0x8a, - 0xad, 0xb9, 0xc6, 0x8d, 0xd8, 0x84, 0x83, 0xc1, 0x5d, 0x47, 0xaf, 0x8f, 0xf4, 0x2e, - 0x6b, 0xfb, 0xb8, 0xe0, 0xe5, 0x3a, 0x04, 0x7e, 0x58, 0xe5, 0xba, 0x90, 0xd1, 0xdb, - 0x1e, 0xa1, 0x26, 0x01, 0x7c, 0x65, 0x6d, 0x01, 0x1c, 0x68, 0x7b, 0xb0, 0x4f, 0x47, - 0xa5, 0x60, 0xef, 0x7c, 0xed, 0x23, 0x1b, 0x24, 0x38, 0x7f, 0xf4, 0x01, 0x90, 0x43, - 0xcf, 0xfd, 0x67, 0xfb, 0x9d, 0x89, 0x20, 0x06, 0xc3, 0x91, 0x7f, 0xd7, 0xa9, 0x6f, - 0xe0, 0x3d, 0x7b, 0xea, 0xa2, 0x17, 0x12, 0x8d, 0x71, 0xf0, 0xa2, 0x8a, 0x83, 0x78, - 0x7a, 0x86, 0xcf, 0xc9, 0x33, 0x69, 0xd0, 0xdd, 0x54, 0x65, 0x32, 0x7f, 0xc4, 0x29, - 0x4d, 0xae, 0x81, 0xc4, 0x35, 0x1c, 0x42, 0xa6, 0xf0, 0xa8, 0x0e, 0xef, 0xa6, 0x1d, - 0xb6, 0xa4, 0x0b, 0xb6, 0x81, 0xf5, 0x58, 0xf8, 0x1b, 0x10, 0x1e, 0xb6, 0x57, 0xf6, - 0x57, 0x27, 0xd6, 0x17, 0x69, 0x1b, 0x8b, 0xee, 0x3a, 0xa7, 0xe5, 0x75, 0xb4, 0x11, - 0xa0, 0x12, 0x8a, 0x3f, 0x24, 0x75, 0x3e, 0x52, 0xee, 0x34, 0x90, 0x04, 0xcf, 0x6d, - 0x25, 0xfa, 0xd6, 0xc4, 0x68, 0x1b, 0x02, 0xa2, 0xe1, 0x96, 0x14, 0xe8, 0x0c, 0x95, - 0x83, 0x81, 0x36, 0x2a, 0x91, 0xd3, 0xcd, 0x3b, 0x4e, 0x76, 0x58, 0x32, 0x94, 0x31, - 0x0c, 0x82, 0x41, 0x11, 0x29, 0xac, 0x97, 0xf2, 0xad, 0x5a, 0x5b, 0x9f, 0xa8, 0x64, - 0xa9, 0xc5, 0xd0, 0x2d, 0x8c, 0x92, 0xd6, 0x42, 0x44, 0xfa, 0x6c, 0x40, 0x9c, 0x21, - 0x69, 0x48, 0x62, 0xc4, 0x42, 0x7d, 0xc5, 0x1a, 0xec, 0x57, 0x7f, 0x6e, 0xa3, 0x38, - 0x05, 0x03, 0x13, 0x99, 0x91, 0xe6, 0xe8, 0x89, 0x09, 0x87, 0x64, 0x9f, 0xa7, 0xc4, - 0x3a, 0xc8, 0x03, 0xf6, 0x89, 0xb6, 0x9d, 0x70, 0xab, 0xd7, 0xef, 0xa7, 0x1c, 0xf9, - 0xa0, 0xf2, 0xa4, 0x1d, 0xf9, 0x41, 0x89, 0x76, 0xa4, 0xff, 0xa4, 0x4f, 0x43, 0x75, - 0x92, 0xf1, 0x9c, 0x09, 0xcb, 0x49, 0x31, 0xb3, 0xd3, 0xcd, 0x01, 0x59, 0x31, 0xcf, - 0xfa, 0xe1, 0x71, 0xe0, 0x8a, 0xc5, 0x92, 0x88, 0x61, 0xfc, 0xc3, 0x2e, 0x08, 0x81, - 0x15, 0x59, 0x76, 0x49, 0x66, 0xbe, 0xbc, 0x14, 0x14, 0x36, 0xb9, 0x17, 0xc5, 0x27, - 0x1b, 0x2c, 0x68, 0x0c, 0xdc, 0x50, 0x2c, 0xba, 0xd5, 0x27, 0xac, 0x08, 0x7b, 0x34, - 0x65, 0x6f, 0x75, 0x5d, 0xfb, 0xf0, 0xae, 0x5a, 0xed, 0xc8, 0x09, 0x85, 0xf6, 0x3d, - 0x0c, 0xa4, 0x4a, 0x76, 0x2f, 0x9b, 0x31, 0x1f, 0x15, 0x6d, 0xe6, 0x27, 0x74, 0x19, - 0x19, 0x99, 0x8e, 0x67, 0x44, 0x66, 0xc7, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x04, 0xc4, - 0x9e, 0xb1, 0x87, 0xfb, 0xf7, 0x5e, 0x5f, 0x7c, 0xee, 0x26, 0x1e, 0x30, 0x75, 0xc2, - 0xb2, 0xc2, 0x81, 0x2f, 0xe8, 0x32, 0x32, 0xc4, 0x1a, 0x5f, 0x10, 0xf4, 0x0b, 0x91, - 0x1e, 0xbc, 0xeb, 0xb7, 0x8c, 0x91, 0xc2, 0x0b, 0x82, 0xc0, 0x05, 0x0f, 0xe2, 0xee, - 0x10, 0x4b, 0x39, 0x20, 0xed, 0x0a, 0x05, 0xd1, 0x7b, 0x06, 0x0d, 0x99, 0xd5, 0x87, - 0x01, 0x98, 0xe6, 0x3c, 0xcf, 0x51, 0xb1, 0x5d, 0xf8, 0x0e, 0x87, 0xac, 0xbd, 0x30, - 0x12, 0x6c, 0xda, 0x2a, 0xff, 0xb8, 0xf1, 0xce, 0xcb, 0x1b, 0xaa, 0x6a, 0x91, 0x9e, - 0x0a, 0x97, 0x87, 0x91, 0x39, 0x69, 0x04, 0x44, 0x9a, 0xde, 0x4b, 0x0b, 0x02, 0x92, - 0x0f, 0xb8, 0xc0, 0xbf, 0x7f, 0xc0, 0x82, 0xeb, 0x74, 0x98, 0x73, 0xc1, 0x0d, 0x17, - 0xdb, 0xd9, 0x1f, 0xfe, 0xa9, 0x36, 0x10, 0xee, 0xea, 0x62, 0x57, 0x90, 0xad, 0xa2, - 0x8e, 0x3a, 0x2c, 0xf2, 0x2c, 0x0d, 0x4e, 0xa2, 0xb9, 0x26, 0x41, 0xf2, 0x16, 0xd3, - 0x92, 0x2c, 0x1f, 0xc3, 0x2d, 0xbc, 0x1e, 0x0e, 0x99, 0x00, 0x38, 0x6c, 0xf8, 0x98, - 0xcb, 0x8e, 0xd5, 0x6c, 0x06, 0x4e, 0x5b, 0x12, 0xb0, 0x26, 0xbf, 0x03, 0x5d, 0xfb, - 0xc4, 0xeb, 0x92, 0xce, 0x33, 0xf8, 0x2b, 0xbe, 0x48, 0xca, 0x94, 0x5f, 0x12, 0x44, - 0x83, 0x10, 0xd7, 0xb9, 0xdb, 0x85, 0xf1, 0xb0, 0x46, 0xdc, 0x9c, 0x56, 0x51, 0x2f, - 0x61, 0xe0, 0xa3, 0x96, 0x6f, 0xa4, 0xab, 0x71, 0xd1, 0x5f, 0x4e, 0x23, 0xe4, 0xe3, - 0x1c, 0xb9, 0x62, 0x10, 0x60, 0x14, 0xc4, 0xc2, 0x9e, 0xc3, 0xb9, 0x10, 0xe0, 0x72, - 0x2d, 0xac, 0x38, 0xaa, 0x4d, 0xc8, 0x1e, 0x17, 0x6d, 0x72, 0xfe, 0xaf, 0x2f, 0x93, - 0xf9, 0xec, 0xd5, 0x04, 0xcb, 0xaf, 0x95, 0x59, 0x83, 0x30, 0x09, 0xd9, 0x2c, 0x9d, - 0x2f, 0x81, 0x68, 0x7b, 0xf5, 0x89, 0xa4, 0x93, 0x66, 0xcd, 0x0a, 0xba, 0xe7, 0xa1, - 0x74, 0xa4, 0x8f, 0xf7, 0x6c, 0xd7, 0x2f, 0x02, 0xb1, 0x8a, 0xf8, 0x18, 0x75, 0x26, - 0xd4, 0x70, 0x94, 0x9c, 0xb8, 0xd9, 0x3e, 0xfe, 0x6c, 0x5b, 0xc7, 0x91, 0xca, 0x93, - 0xb1, 0x10, 0xc1, 0x82, 0x5b, 0x6a, 0xfb, 0x04, 0x5d, 0x9d, 0x8c, 0xa3, 0x51, 0xf7, - 0xad, 0xa3, 0x28, 0xfd, 0xd5, 0x2a, 0xec, 0x29, 0x77, 0xd2, 0x94, 0x0e, 0x2c, 0xdc, - 0xb2, 0x66, 0x4d, 0x78, 0xb7, 0x6a, 0xc0, 0xe0, 0x6d, 0x78, 0x8e, 0x57, 0xf8, 0x24, - 0x4f, 0x44, 0x2c, 0x88, 0x6a, 0x8f, 0x31, 0x13, 0x7c, 0xd7, 0xf1, 0x9e, 0x82, 0x21, - 0xa3, 0x85, 0xcb, 0xfb, 0x3f, 0x7f, 0x2a, 0x1e, 0x79, 0x50, 0x4b, 0xcf, 0x1a, 0xe0, - 0x83, 0xb1, 0x29, 0x02, 0xa5, 0x01, 0x2c, 0xd5, 0xea, 0x2f, 0xc8, 0x56, 0x43, 0xdd, - 0xec, 0xee, 0xf4, 0xab, 0x95, 0x93, 0x43, 0x21, 0x9b, 0x0c, 0x63, 0xdd, 0x0a, 0x8b, - 0x0e, 0x23, 0x3e, 0xfc, 0x68, 0xfc, 0x63, 0x30, 0x73, 0xe6, 0x6c, 0x59, 0x97, 0x5f, - 0x23, 0x52, 0x4b, 0x6a, 0xa1, 0xab, 0x9a, 0xe7, 0xb1, 0x33, 0xd5, 0xf3, 0x0c, 0xf9, - 0xe1, 0xd0, 0xf9, 0xba, 0xd7, 0x1f, 0x67, 0x3f, 0x5b, 0x75, 0x4c, 0xf4, 0x00, 0x99, - 0x77, 0x57, 0xa6, 0x45, 0x8a, 0xd3, 0xb9, 0xdc, 0x8e, 0xc0, 0xc6, 0x9c, 0x66, 0x09, - 0x66, 0x3b, 0x42, 0xbb, 0xb0, 0xca, 0x1a, 0x55, 0x73, 0x37, 0x42, 0x81, 0x1f, 0x0d, - 0x71, 0x30, 0xe0, 0x13, 0xfe, 0x2f, 0x88, 0x05, 0x8e, 0xe8, 0x9b, 0x90, 0xa7, 0x5c, - 0xd0, 0x69, 0xda, 0xf1, 0x00, 0x37, 0x25, 0x4d, 0x10, 0x16, 0xd3, 0xac, 0xf7, 0xe6, - 0x2f, 0x18, 0x3b, 0x2c, 0x55, 0x1a, 0x59, 0x90, 0xe4, 0xed, 0x73, 0xdc, 0xd8, 0x94, - 0xf7, 0x85, 0x70, 0xfd, 0x19, 0x56, 0xcb, 0x22, 0x7c, 0x65, 0x00, 0x01, 0xf2, 0x7f, - 0x94, 0x23, 0xf4, 0xed, 0x12, 0x56, 0x0b, 0x2e, 0x1c, 0x8d, 0xbc, 0xb4, 0xc3, 0x02, - 0x15, 0xb2, 0x16, 0x7f, 0x02, 0xef, 0xeb, 0x70, 0x7a, 0xf1, 0xb5, 0xc7, 0x84, 0xb7, - 0xf5, 0x8b, 0x2e, 0x51, 0x73, 0x03, 0xf3, 0xaf, 0x71, 0xb1, 0xee, 0x39, 0xa9, 0xae, - 0x06, 0xb9, 0x77, 0x28, 0xe6, 0x4f, 0x67, 0x6d, 0xed, 0x50, 0xa3, 0xf5, 0x1b, 0xc9, - 0xe0, 0x17, 0x07, 0xbf, 0x57, 0x95, 0x6f, 0x01, 0xb7, 0xda, 0x7c, 0x23, 0xe6, 0x93, - 0x52, 0x06, 0x57, 0x28, 0x6f, 0xe7, 0x3e, 0xee, 0x9e, 0xb1, 0xd5, 0x83, 0x75, 0x22, - 0x03, 0xf3, 0xd9, 0x2b, 0xd4, 0x04, 0x7b, 0x83, 0xfd, 0x38, 0xf5, 0x66, 0xdd, 0x25, - 0xb9, 0x6d, 0x11, 0xb7, 0x22, 0x2b, 0x67, 0x82, 0xda, 0xde, 0xf5, 0xee, 0x78, 0x82, - 0x14, 0x7c, 0xbb, 0x4f, 0xcf, 0xe7, 0x0d, 0x2c, 0xa7, 0xf3, 0x9a, 0x29, 0x7b, 0x21, - 0xd5, 0x6d, 0x66, 0x10, 0xe9, 0xda, 0x9d, 0x8e, 0xef, 0xdc, 0x69, 0x9e, 0x4a, 0x30, - 0x06, 0x8a, 0x14, 0x57, 0xcf, 0x5e, 0xaf, 0x69, 0x87, 0x78, 0x21, 0xd3, 0x9e, 0xa0, - 0x85, 0x94, 0xc2, 0xfb, 0x9e, 0xb9, 0xd8, 0x04, 0x64, 0x50, 0xe4, 0x13, 0x03, 0xf1, - 0x95, 0xbd, 0xc9, 0x05, 0xe4, 0xf2, 0x58, 0x3c, 0x6a, 0xe3, 0x86, 0x1b, 0x87, 0x19, - 0xbb, 0xce, 0xd1, 0xce, 0x58, 0xc4, 0x68, 0x81, 0x6d, 0x45, 0x15, 0xe6, 0x09, 0x7b, - 0x3e, 0x2e, 0x81, 0x82, 0x21, 0x0f, 0x6c, 0x1b, 0xb3, 0xaa, 0xa6, 0x2a, 0xe0, 0xf6, - 0x9f, 0x79, 0xfc, 0xc5, 0x47, 0xba, 0xab, 0x31, 0x1d, 0x99, 0x7c, 0x84, 0x95, 0xd6, - 0xab, 0xe3, 0xa5, 0x1f, 0x56, 0x53, 0xf3, 0x1c, 0x5a, 0x2e, 0xea, 0x8d, 0x31, 0x90, - 0x97, 0xf3, 0x04, 0x5e, 0x6c, 0x3c, 0x3d, 0x8c, 0x87, 0xc9, 0xbd, 0x55, 0xb4, 0x19, - 0x2e, 0xbf, 0x00, 0xff, 0x8f, 0xc7, 0xf4, 0x1e, 0x18, 0x93, 0x0a, 0x99, 0x72, 0xa3, - 0x4d, 0x9e, 0x6a, 0xa9, 0xd9, 0x1d, 0x2e, 0x28, 0x17, 0xeb, 0x6d, 0xe9, 0xba, 0x38, - 0x9e, 0x69, 0xaa, 0x51, 0x2f, 0x3f, 0xb4, 0xdf, 0xf8, 0xca, 0x1c, 0xe7, 0xc9, 0xca, - 0x39, 0x6e, 0x8a, 0x9d, 0x99, 0xd4, 0x96, 0x51, 0xb0, 0x58, 0x2f, 0xc5, 0x86, 0xce, - 0x92, 0x7e, 0xa2, 0x64, 0x5b, 0xda, 0xa3, 0x79, 0x28, 0x6f, 0x95, 0xd3, 0x9b, 0x95, - 0x81, 0xde, 0xb2, 0xc5, 0x37, 0x75, 0xae, 0xef, 0x20, 0xe7, 0xbd, 0xbc, 0x3b, 0x19, - 0xd8, 0x9b, 0xac, 0xee, 0xa1, 0x3b, 0x74, 0xe6, 0xc7, 0xf5, 0x20, 0x89, 0x39, 0x7d, - 0x11, 0x6e, 0xbf, 0xac, 0x6a, 0x30, 0xed, 0x27, 0xd6, 0x27, 0x81, 0xa0, 0x3b, 0x66, - 0xb0, 0x52, 0xf7, 0x51, 0xfb, 0x36, 0x88, 0x2b, 0x9a, 0x14, 0x34, 0x23, 0xad, 0x02, - 0xf3, 0x36, 0x0a, 0xfa, 0x54, 0xc4, 0xcf, 0x23, 0x53, 0x0c, 0x68, 0xd6, 0x0e, 0x99, - 0x56, 0x1c, 0xce, 0x0d, 0x6a, 0x9c, 0x32, 0xef, 0xc7, 0x1f, 0xef, 0xaf, 0x23, 0x57, - 0x86, 0x3f, 0xa0, 0xb9, 0xf7, 0xbe, 0x76, 0xc2, 0xd1, 0xd3, 0x88, 0x49, 0xa0, 0x0a, - 0xb0, 0x41, 0xf1, 0x82, 0xad, 0x63, 0x35, 0xe9, 0x55, 0xcc, 0x65, 0xcd, 0xfd, 0x3b, - 0x69, 0x1a, 0x3d, 0x96, 0xc4, 0xbd, 0x56, 0xf5, 0x25, 0xce, 0xdb, 0x7f, 0xdc, 0xb7, - 0x33, 0xe7, 0x67, 0x06, 0x2f, 0xd8, 0xa4, 0xef, 0x1a, 0x4b, 0x71, 0x5e, 0x5e, 0xdf, - 0x76, 0x26, 0x14, 0x4e, 0x28, 0x5f, 0x2b, 0x3c, 0x4e, 0x2c, 0xb4, 0x1b, 0x7d, 0xb9, - 0x66, 0x35, 0x82, 0xad, 0x65, 0xa5, 0x41, 0x6e, 0x57, 0xf7, 0x48, 0x5f, 0x39, 0xc0, - 0x5e, 0x8e, 0x7a, 0xf9, 0x6b, 0x36, 0x78, 0xc8, 0x0a, 0x8d, 0x4b, 0xa2, 0xf9, 0x5d, - 0x5f, 0xeb, 0x0c, 0xcb, 0x0f, 0x71, 0x7b, 0x9d, 0xb7, 0x24, 0xab, 0xf4, 0xcc, 0xd4, - 0x10, 0x49, 0x00, 0x18, 0x6f, 0x4a, 0x93, 0x0d, 0x4b, 0x2a, 0xcb, 0x9f, 0x9a, 0x16, - 0xaf, 0x89, 0x77, 0x27, 0x7d, 0x6f, 0x0b, 0xc9, 0x0a, 0xb8, 0x59, 0xc3, 0x33, 0x3b, - 0x3d, 0xe8, 0x6f, 0x41, 0xfa, 0x85, 0xd5, 0x70, 0xf1, 0x6c, 0x74, 0x82, 0x0a, 0x70, - 0x41, 0xfe, 0xa1, 0x5e, 0xe9, 0x50, 0xc3, 0x30, 0xac, 0xa3, 0xf1, 0xe5, 0x1c, 0x69, - 0x44, 0x74, 0x72, 0xf2, 0x6a, 0x3d, 0x67, 0x41, 0xbc, 0x67, 0xe9, 0x2e, 0x00, 0xa0, - 0x83, 0xb6, 0x95, 0x33, 0x03, 0xb3, 0x73, 0x1c, 0xf2, 0x84, 0x8d, 0x81, 0x7c, 0xeb, - 0x77, 0xf1, 0xcc, 0xa7, 0x1e, 0xc9, 0x13, 0x91, 0x20, 0x2b, 0x73, 0x4d, 0x54, 0x8f, - 0xa3, 0x14, 0x2c, 0x37, 0xe6, 0xfc, 0xac, 0x51, 0x92, 0xfc, 0xa2, 0x8d, 0x63, 0x98, - 0x1f, 0x67, 0xdd, 0xdc, 0x28, 0xb3, 0x1f, 0xd0, 0xb9, 0x3a, 0x7f, 0x21, 0x88, 0xc1, - 0xec, 0xa2, 0xc1, 0xef, 0xa4, 0x61, 0xd2, 0xdd, 0x73, 0x38, 0xdf, 0x07, 0x05, 0xae, - 0x70, 0x10, 0x62, 0xfb, 0xcd, 0x8d, 0x50, 0x29, 0x98, 0x85, 0xd8, 0xe3, 0xd4, 0xfb, - 0xd6, 0xa4, 0xf2, 0x15, 0x5d, 0xc8, 0xd8, 0xfd, 0x0b, 0x05, 0x8f, 0x3c, 0x77, 0x50, - 0x83, 0xf5, 0x96, 0x12, 0xac, 0x66, 0x02, 0xd9, 0xad, 0xfa, 0x49, 0xe2, 0x60, 0x2a, - 0x12, 0xf2, 0x90, 0x0d, 0x22, 0xb9, 0x9c, 0x0b, 0x8a, 0x32, 0x68, 0xa0, 0x19, 0xc0, - 0xdd, 0xf3, 0x14, 0x3e, 0x8a, 0xf4, 0x13, 0x07, 0xd9, 0x26, 0x74, 0x02, 0x13, 0x08, - 0x59, 0xee, 0x92, 0x43, 0x4d, 0x23, 0x79, 0xe9, 0x4b, 0xcb, 0xbe, 0x56, 0x1d, 0xe0, - 0x42, 0x92, 0xb5, 0x32, 0xab, 0xc3, 0x5d, 0xde, 0x53, 0xd2, 0xad, 0x86, 0x7f, 0x7a, - 0xd9, 0x42, 0x00, 0xe4, 0x8e, 0x50, 0x3e, 0x7d, 0x41, 0x6b, 0xcf, 0x98, 0x29, 0x9f, - 0x82, 0xfc, 0xba, 0xe2, 0xdc, 0x42, 0xae, 0xc1, 0x8a, 0x29, 0x3b, 0x63, 0x79, 0x5b, - 0x68, 0x63, 0xf3, 0x22, 0x49, 0xcd, 0x20, 0x5e, 0x54, 0xd7, 0xcb, 0x7c, 0x82, 0x3b, - 0x00, 0x74, 0x77, 0x35, 0x96, 0xc1, 0xc5, 0x33, 0x92, 0x1d, 0x3b, 0xae, 0x11, 0xfe, - 0x1c, 0x6b, 0xfb, 0x77, 0x74, 0xe1, 0x49, 0x88, 0x64, 0xf3, 0xb6, 0x26, 0xd4, 0xcb, - 0x14, 0x47, 0x95, 0xd8, 0xf3, 0x59, 0xf5, 0xc5, 0x5d, 0xa3, 0xd7, 0x11, 0x70, 0x4e, - 0x74, 0x29, 0x58, 0x95, 0x5e, 0xaf, 0xa4, 0xb7, 0xd0, 0x31, 0xb2, 0xd6, 0xda, 0x0c, - 0x52, 0x9d, 0x41, 0xf3, 0x16, 0x93, 0xe4, 0xe5, 0x10, 0xb6, 0xb1, 0xe4, 0xab, 0xb6, - 0x01, 0x5f, 0x0d, 0x6d, 0x12, 0x61, 0x5e, 0xc1, 0xea, 0xf2, 0x75, 0xd4, 0x62, 0x96, - 0x2f, 0x17, 0x68, 0x4a, 0x7a, 0x25, 0x30, 0x1a, 0x99, 0x55, 0x5d, 0xef, 0x47, 0x15, - 0xff, 0x62, 0xce, 0x3c, 0xa6, 0x2f, 0x82, 0xe1, 0xf0, 0xec, 0x3b, 0x76, 0xd9, 0xea, - 0x82, 0x5a, 0xbc, 0x46, 0xfa, 0x2c, 0xf2, 0xb7, 0xa9, 0x64, 0x3e, 0xf2, 0x11, 0x0d, - 0x16, 0xef, 0x7a, 0x37, 0x0a, 0x5a, 0x99, 0xc0, 0xf7, 0x3d, 0xd2, 0x07, 0xb7, 0xba, - 0xc5, 0x2f, 0x36, 0x7d, 0xc4, 0xba, 0x9f, 0x52, 0x1c, 0x2d, 0x48, 0x77, 0xba, 0x68, - 0x98, 0xb8, 0xc9, 0x0c, 0x6d, 0xa7, 0x33, 0x64, 0x5c, 0xfb, 0x78, 0xc6, 0xf4, 0x09, - 0x92, 0xc7, 0x20, 0x96, 0x8f, 0xe4, 0x3c, 0x32, 0xbb, 0x59, 0x39, 0x9b, 0xa8, 0x82, - 0x36, 0x06, 0x4a, 0xa0, 0xa6, 0x8f, 0x1a, 0x5a, 0xfa, 0xae, 0xd0, 0xf5, 0x39, 0xc2, - 0x4e, 0xf9, 0xe6, 0x9d, 0x37, 0xdd, 0xba, 0x2d, 0x15, 0x86, 0xc0, 0x3b, 0x52, 0x45, - 0x48, 0xd8, 0x20, 0x7d, 0xa9, 0x58, 0x92, 0xc0, 0x0a, 0xcf, 0xee, 0x51, 0xb2, 0x42, - 0x4c, 0x2d, 0x1a, 0x4d, 0x7d, 0x4d, 0xd9, 0x8a, 0x1c, 0x6f, 0x2a, 0x5f, 0x6b, 0x39, - 0x20, 0x64, 0x30, 0xf1, 0x84, 0x37, 0x3a, 0x96, 0xc6, 0xaa, 0x58, 0xcc, 0xe2, 0xe1, - 0xc5, 0x04, 0xd4, 0x0e, 0xe9, 0xef, 0xda, 0x58, 0x8d, 0x43, 0xef, 0xb1, 0xda, 0x53, - 0xc7, 0x3d, 0x53, 0x0f, 0xa7, 0x6b, 0x11, 0x2f, 0x33, 0xb4, 0xaf, 0xa9, 0x41, 0xcd, - 0x1e, 0x20, 0x5a, 0xcd, 0x72, 0xca, 0x86, 0x84, 0xad, 0xe8, 0x33, 0x3d, 0x46, 0x32, - 0xab, 0x94, 0x3d, 0x69, 0x0f, 0x13, 0x2b, 0xc9, 0x9f, 0x5c, 0x5a, 0x1d, 0x3e, 0xa4, - 0xe0, 0xca, 0x8f, 0x43, 0x23, 0x8c, 0xd9, 0xeb, 0x09, 0xc8, 0xbf, 0x11, 0xe9, 0x18, - 0xa9, 0xc7, 0xf8, 0x83, 0xbe, 0x94, 0x89, 0x06, 0x56, 0x33, 0x66, 0x67, 0x95, 0x4a, - 0x51, 0xa8, 0xae, 0xcd, 0xc4, 0xcb, 0xd3, 0x9a, 0xca, 0xc7, 0x52, 0x05, 0x6e, 0x71, - 0xcc, 0x96, 0x91, 0x55, 0xdd, 0x65, 0x6d, 0x79, 0x59, 0x00, 0x8c, 0x0e, 0xcf, 0x61, - 0x83, 0x2a, 0x5c, 0x44, 0xe2, 0xe0, 0xde, 0x68, 0xf5, 0x04, 0xc1, 0x77, 0xdf, 0x68, - 0x8b, 0xee, 0x55, 0x8c, 0x6f, 0x4e, 0x5e, 0xa5, 0xf9, 0xad, 0x78, 0x26, 0x73, 0x40, - 0xe2, 0xc8, 0x35, 0xb9, 0x74, 0xdd, 0x12, 0xcd, 0xb9, 0x05, 0x08, 0x87, 0x60, 0x34, - 0xdd, 0xde, 0x0d, 0x97, 0xea, 0xfa, 0xf9, 0x70, 0x18, 0x34, 0x90, 0xcd, 0x22, 0xea, - 0x57, 0xb9, 0x8a, 0xbd, 0x1a, 0x7f, 0x79, 0xe2, 0xcf, 0x23, 0xcf, 0x8d, 0x1b, 0x0e, - 0x9b, 0x7c, 0x93, 0x8a, 0xcc, 0x6b, 0x14, 0x4b, 0x54, 0x13, 0xd3, 0x2f, 0x50, 0xcd, - 0x09, 0x61, 0x8f, 0xa9, 0x74, 0x10, 0x3a, 0x72, 0x8e, 0x2b, 0x71, 0x76, 0x63, 0xd4, - 0xbd, 0x9b, 0x07, 0x20, 0xb7, 0x75, 0xf5, 0xee, 0x25, 0xa6, 0xd7, 0x4d, 0x12, 0x8c, - 0x49, 0xb4, 0x0a, 0x19, 0x74, 0x1c, 0x37, 0xdd, 0x34, 0x61, 0x6d, 0xb3, 0x1e, 0xac, - 0x0b, 0xe7, 0xf5, 0x3f, 0xfa, 0x61, 0x9f, 0x45, 0x18, 0x1f, 0x5a, 0x4d, 0xbe, 0x5b, - 0x1b, 0x48, 0x09, 0x8e, 0xba, 0x2c, 0x2e, 0xc2, 0x0a, 0x0a, 0xc0, 0x44, 0x3b, 0xa8, - 0xe9, 0x48, 0x7b, 0xcf, 0x7d, - ], - script_code: Script(vec![0xac, 0x53, 0x63, 0x52, 0x6a, 0x51, 0xac]), - transparent_input: None, - hash_type: 1, - amount: 1501997449504444, - consensus_branch_id: 1991772603, - sighash: [ - 0xa1, 0xcf, 0x50, 0xcf, 0xfe, 0x59, 0xbe, 0x5f, 0x31, 0x5f, 0xfe, 0x51, 0x6e, 0x28, - 0x9e, 0xe8, 0x02, 0x5e, 0x59, 0x38, 0xf1, 0xe8, 0xe1, 0x88, 0x53, 0x7f, 0xf1, 0xa8, - 0x93, 0xac, 0x71, 0x14, - ], - }, - TestVector { - tx: vec![ - 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0x88, 0x1d, 0xdf, 0x4f, 0x95, - 0x78, 0x97, 0x34, 0xfc, 0xc1, 0x65, 0xee, 0x1e, 0x04, 0x40, 0x85, 0xb6, 0xe7, 0xa1, - 0x77, 0x50, 0x8c, 0x29, 0xda, 0x0c, 0xe7, 0x7d, 0xed, 0x75, 0x08, 0x98, 0xde, 0x89, - 0xd2, 0x60, 0xd3, 0x02, 0x63, 0x52, 0x44, 0xcc, 0x75, 0xe1, 0x98, 0x34, 0x52, 0x5f, - 0xba, 0x56, 0x90, 0x0d, 0xe9, 0x93, 0x85, 0x44, 0x2e, 0xb9, 0xec, 0x9a, 0x5f, 0x18, - 0x2b, 0x87, 0x5d, 0x70, 0xb5, 0xb1, 0x53, 0x79, 0x0a, 0x1e, 0xe7, 0x9c, 0x0e, 0x86, - 0x78, 0x37, 0x95, 0xfa, 0x06, 0x6a, 0x00, 0x63, 0x00, 0x00, 0x63, 0xfc, 0x92, 0x29, - 0x92, 0x00, 0x83, 0x64, 0xff, 0xfc, 0x7c, 0x00, 0xc0, 0x0e, 0x0f, 0x99, 0xde, 0x47, - 0x42, 0x89, 0x06, 0x00, 0x01, 0x39, 0x21, 0x97, 0xd6, 0x23, 0xf7, 0xeb, 0xda, 0x07, - 0xcd, 0x00, 0x58, 0xd9, 0xa1, 0xd1, 0x72, 0x04, 0x3c, 0x2f, 0xc9, 0x4f, 0x14, 0x19, - 0x3e, 0x27, 0x0e, 0xef, 0xe8, 0x3c, 0x3f, 0x01, 0xb2, 0x65, 0x05, 0x4c, 0x3f, 0x6a, - 0x60, 0xe2, 0xb7, 0x6e, 0x17, 0x56, 0x08, 0x8b, 0x87, 0xda, 0x83, 0x9f, 0x77, 0x2c, - 0xbd, 0x0f, 0x27, 0x5c, 0x92, 0x28, 0x38, 0x5a, 0x04, 0xbb, 0x50, 0xec, 0x3c, 0xfa, - 0x9e, 0xe2, 0xe1, 0x5b, 0x15, 0x3d, 0x4c, 0x85, 0xfe, 0x50, 0xb6, 0x00, 0x62, 0x58, - 0xe9, 0xe8, 0xc2, 0x52, 0x99, 0xc0, 0x9d, 0xf8, 0xb4, 0x55, 0x46, 0x6b, 0xa2, 0x5f, - 0x7e, 0x4c, 0x8f, 0xe7, 0xe2, 0x50, 0xed, 0xba, 0x60, 0x69, 0x5d, 0xa4, 0x7f, 0xaa, - 0xfd, 0xd6, 0x26, 0xba, 0x7e, 0x9d, 0x48, 0x96, 0xe4, 0xb8, 0xa8, 0xa1, 0xa1, 0xdc, - 0x21, 0x5b, 0x0a, 0x25, 0xee, 0xb0, 0x4e, 0xd1, 0xbe, 0xfb, 0x5b, 0x31, 0x38, 0xc6, - 0x9f, 0xe5, 0x28, 0xe7, 0x29, 0x11, 0x23, 0xfc, 0xdf, 0x8a, 0x36, 0x6c, 0x25, 0x7d, - 0x32, 0x95, 0x38, 0x25, 0x0a, 0x0c, 0xb7, 0xf5, 0x4e, 0x1c, 0x01, 0x6c, 0xe1, 0xc6, - 0x23, 0xb2, 0xe2, 0x76, 0xa5, 0x2c, 0x6e, 0x41, 0x24, 0x1b, 0x2a, 0xc5, 0x09, 0x37, - 0x3c, 0x18, 0x81, 0x40, 0xe8, 0x36, 0x5c, 0x94, 0xf5, 0x8c, 0x63, 0xf2, 0x7f, 0xf8, - 0xe6, 0xe8, 0x69, 0xa9, 0x85, 0xaf, 0xb6, 0x1e, 0x97, 0xd8, 0xce, 0xec, 0x2a, 0x78, - 0x24, 0xa5, 0xc1, 0x07, 0xb0, 0xba, 0xa4, 0xd6, 0xe7, 0x9a, 0x6c, 0x71, 0x87, 0x2a, - 0x7b, 0x3b, 0x17, 0xef, 0x91, 0x8a, 0xe4, 0xe2, 0x5f, 0x98, 0xa7, 0x2d, 0xb5, 0x3b, - 0xa7, 0xf2, 0x6e, 0x40, 0x8b, 0xd4, 0xd1, 0xf9, 0xe3, 0x47, 0x4d, 0xdc, 0xa5, 0x83, - 0x3f, 0xf5, 0xff, 0x8d, 0x11, 0xb1, 0xbf, 0x1e, 0x2b, 0xb4, 0xd1, 0x96, 0x8a, 0x82, - 0x38, 0x88, 0xbd, 0x91, 0xa2, 0x1a, 0x76, 0x79, 0x6b, 0xca, 0x44, 0x53, 0xe2, 0x89, - 0x2d, 0x1b, 0x6e, 0x13, 0x63, 0xed, 0x10, 0x7a, 0x9e, 0x7e, 0xd9, 0x3f, 0xb1, 0xda, - 0x99, 0x4a, 0x9d, 0x4e, 0x7e, 0xc9, 0x2e, 0x29, 0xa6, 0x87, 0xf2, 0x18, 0xd2, 0x8a, - 0x76, 0x46, 0x06, 0x9b, 0xca, 0xcb, 0x4d, 0xa7, 0xba, 0xdf, 0x4e, 0xb1, 0x33, 0x1a, - 0xab, 0x21, 0x2b, 0x92, 0xc6, 0xea, 0x64, 0x76, 0xa0, 0xa0, 0x9d, 0x6b, 0xd2, 0xe0, - 0xf7, 0x6f, 0xa8, 0x73, 0x79, 0xab, 0xfd, 0x17, 0x58, 0x2f, 0x3e, 0xb2, 0x3b, 0x86, - 0xc9, 0x66, 0x9f, 0x86, 0x73, 0x70, 0x48, 0xd7, 0x71, 0x84, 0x9b, 0x8f, 0x70, 0xbd, - 0x87, 0x99, 0x01, 0x3b, 0xe0, 0xbf, 0xbd, 0x7b, 0x57, 0xbe, 0xa1, 0xa4, 0x9a, 0x4a, - 0x39, 0x14, 0x79, 0x12, 0xd7, 0xba, 0xf6, 0x80, 0x04, 0xd4, 0x15, 0x02, 0x6b, 0xbc, - 0x6f, 0x69, 0x32, 0x5f, 0x4f, 0xf7, 0x87, 0x28, 0x77, 0x5a, 0x67, 0xaa, 0xdd, 0x72, - 0x2c, 0x73, 0x31, 0x1d, 0xba, 0x5c, 0x2c, 0xf1, 0x4c, 0xcb, 0xd5, 0x7e, 0xab, 0xed, - 0x71, 0x92, 0x0f, 0xf9, 0x62, 0x32, 0x89, 0xbb, 0x76, 0x05, 0x1c, 0x73, 0xa2, 0x06, - 0xa3, 0xc2, 0xb4, 0x0c, 0xac, 0x01, 0xd5, 0xf1, 0x1f, 0xa6, 0x4c, 0x1b, 0x7d, 0xed, - 0x70, 0xea, 0x17, 0x42, 0x9c, 0x66, 0x21, 0xca, 0x9b, 0x92, 0x3c, 0x48, 0x11, 0x85, - 0x0c, 0x3d, 0xf4, 0x01, 0x3d, 0x17, 0xbd, 0xc5, 0x10, 0x1c, 0x8d, 0x80, 0xb3, 0xa0, - 0x4a, 0x4c, 0xc2, 0x3d, 0x13, 0xfe, 0x31, 0x84, 0xe8, 0xb1, 0xad, 0xe6, 0x35, 0x17, - 0x59, 0x3f, 0x7b, 0xe6, 0x69, 0x48, 0xc0, 0x85, 0x7a, 0xec, 0xe0, 0x1b, 0xc2, 0x72, - 0x29, 0x5e, 0x60, 0xb1, 0x80, 0x69, 0x46, 0xc9, 0x3b, 0xc8, 0xc7, 0xd2, 0xa2, 0xed, - 0xc3, 0x7f, 0xa3, 0x7c, 0x47, 0x7a, 0x69, 0xa9, 0x0b, 0x59, 0xb4, 0xc6, 0x91, 0x2e, - 0x91, 0x3a, 0x57, 0xef, 0xa9, 0xd5, 0x4c, 0x7e, 0x80, 0xd5, 0xac, 0x8a, 0x42, 0x94, - 0xd0, 0xfd, 0x31, 0xa4, 0x02, 0xe4, 0xb4, 0x7e, 0xc7, 0xbf, 0x03, 0x31, 0xb2, 0xc9, - 0xa4, 0x8f, 0x44, 0x57, 0x3f, 0xc7, 0xe7, 0xf1, 0x02, 0xed, 0x48, 0xc9, 0x75, 0x08, - 0xcb, 0xe4, 0x30, 0x65, 0xa9, 0xe9, 0x9f, 0xb4, 0xce, 0x13, 0x62, 0xbb, 0x8a, 0x76, - 0xb1, 0x41, 0x9d, 0x95, 0x03, 0x0e, 0x9c, 0x24, 0xee, 0xba, 0x9f, 0xf8, 0xcf, 0xda, - 0x95, 0x7b, 0x17, 0x09, 0x8c, 0xdf, 0x8c, 0x9a, 0x91, 0x9e, 0x47, 0xa1, 0x3a, 0x5b, - 0x33, 0x46, 0xe3, 0x7e, 0x82, 0x7c, 0xc8, 0x3b, 0x3c, 0x9a, 0xab, 0xf2, 0xd0, 0xba, - 0x17, 0xff, 0x3d, 0x9e, 0x0d, 0x22, 0x3c, 0x41, 0xc8, 0x8e, 0xc2, 0x39, 0x1c, 0x76, - 0x62, 0x2d, 0x7b, 0xd6, 0x21, 0x17, 0x33, 0x1e, 0x21, 0xff, 0xec, 0x32, 0x72, 0xc1, - 0xe1, 0x42, 0x39, 0x82, 0xc6, 0xb6, 0x3a, 0xec, 0x8d, 0xbf, 0x5c, 0xa2, 0xdd, 0x15, - 0x81, 0x0f, 0x53, 0x42, 0xaf, 0x49, 0xfa, 0xd2, 0x79, 0xb7, 0xca, 0x23, 0xde, 0xd3, - 0x08, 0x24, 0x79, 0x96, 0x30, 0xde, 0xdc, 0x6d, 0xb7, 0x24, 0xbc, 0xe1, 0x11, 0x36, - 0x21, 0xc4, 0xa6, 0x47, 0x9d, 0xd5, 0x55, 0xf4, 0x85, 0x21, 0x7c, 0xb5, 0x67, 0x13, - 0x9e, 0xea, 0xdd, 0x7e, 0xe8, 0xdc, 0x5b, 0x26, 0x62, 0xf1, 0x06, 0x6a, 0x7c, 0x60, - 0xde, 0xe0, 0x09, 0x3c, 0x92, 0x46, 0xde, 0x7a, 0x05, 0xe8, 0xb0, 0xf6, 0xbe, 0xf0, - 0x03, 0x3d, 0xde, 0x2e, 0x87, 0xcb, 0xa6, 0x8d, 0x23, 0x6e, 0xf6, 0x6a, 0x23, 0xd5, - 0x5e, 0x7b, 0xd2, 0x8d, 0x02, 0x59, 0x9c, 0xca, 0x0d, 0xf7, 0xa9, 0x00, 0x63, 0x7b, - 0xb3, 0x46, 0x4d, 0x62, 0x2b, 0x7c, 0x9c, 0x9c, 0x8c, 0x91, 0x46, 0x89, 0x74, 0x88, - 0x01, 0x64, 0xde, 0xf7, 0x99, 0x90, 0x8a, 0x11, 0xa5, 0x91, 0xab, 0xb3, 0xc8, 0xd8, - 0xbd, 0x9c, 0x12, 0xb1, 0xf6, 0xf3, 0xcd, 0xc9, 0xed, 0x8e, 0x16, 0xe5, 0x7d, 0x23, - 0x34, 0xb2, 0x17, 0x79, 0x7d, 0xf1, 0x90, 0x52, 0xfe, 0xeb, 0xed, 0x6c, 0xdb, 0x99, - 0xac, 0x44, 0xea, 0x13, 0xaf, 0xea, 0xc4, 0x37, 0x7d, 0x0f, 0xa3, 0x7e, 0xf5, 0x16, - 0xdd, 0xac, 0xea, 0xb0, 0xd9, 0x39, 0x5b, 0xd4, 0x40, 0x46, 0x0e, 0x28, 0xb5, 0xf5, - 0x7a, 0x6e, 0xfd, 0x37, 0xd2, 0x68, 0xa8, 0x64, 0xcb, 0x5c, 0xa3, 0x4b, 0xe2, 0x87, - 0xe1, 0x04, 0x8e, 0xfc, 0x1e, 0x40, 0xcd, 0xf4, 0xfc, 0xfc, 0x02, 0x4c, 0xf1, 0x82, - 0x03, 0x8b, 0x9d, 0x80, 0xed, 0x1c, 0x07, 0x63, 0x62, 0x00, 0xc8, 0x19, 0xa7, 0xe7, - 0xc2, 0x40, 0xc3, 0xc4, 0xf7, 0xa9, 0x17, 0x32, 0xe3, 0xff, 0x13, 0xe2, 0xa5, 0x6a, - 0x64, 0x66, 0x66, 0x10, 0xca, 0xd9, 0x84, 0x1c, 0x1a, 0x93, 0x4f, 0xe9, 0x33, 0xb0, - 0xf1, 0x9f, 0xb7, 0x1d, 0x06, 0x1c, 0x58, 0xf2, 0x1a, 0x49, 0x81, 0xce, 0x3e, 0x68, - 0xc5, 0x02, 0x39, 0x03, 0x60, 0x8d, 0xe5, 0x83, 0x02, 0xc6, 0xc8, 0xde, 0xf4, 0xe5, - 0x61, 0x9e, 0xc0, 0xd9, 0x1c, 0xf9, 0x35, 0x44, 0x75, 0x97, 0x2b, 0xfe, 0x0d, 0x75, - 0x75, 0x60, 0x2a, 0xaf, 0x0e, 0x9e, 0x88, 0x5c, 0x6b, 0xaf, 0x9d, 0x56, 0x7b, 0x1f, - 0xcb, 0x63, 0x19, 0x0c, 0xb7, 0x92, 0xf1, 0xd8, 0x71, 0x61, 0x1a, 0xdb, 0x4f, 0x3d, - 0x1e, 0xd3, 0x28, 0x02, 0x69, 0x18, 0xe2, 0x8d, 0x2f, 0xd4, 0x5a, 0xb9, 0xd3, 0x70, - 0xe7, 0x29, 0x2e, 0xd7, 0x54, 0xce, 0x29, 0xfb, 0x78, 0x7f, 0xd5, 0xd0, 0x9e, 0x6d, - 0x47, 0xcb, 0xc8, 0x00, 0x21, 0xab, 0xf7, 0xd2, 0xef, 0xeb, 0xdb, 0xe0, 0xad, 0xd8, - 0x70, 0x16, 0x8f, 0x51, 0xdc, 0xc4, 0x09, 0x57, 0xa4, 0xa3, 0xc8, 0xe1, 0x92, 0x60, - 0x13, 0x83, 0xb7, 0x68, 0x41, 0x36, 0xdc, 0xa2, 0x82, 0x62, 0x3f, 0x31, 0xba, 0x7a, - 0xe5, 0x36, 0x6b, 0x45, 0x3c, 0x6a, 0x26, 0xf6, 0x8a, 0x14, 0xdb, 0x65, 0x59, 0xbc, - 0xb1, 0x02, 0x37, 0x37, 0x9a, 0x27, 0xa9, 0x50, 0x2f, 0xf9, 0xd6, 0x4a, 0x33, 0x83, - 0x20, 0x75, 0x15, 0x30, 0xf1, 0xf8, 0x92, 0xa6, 0xd4, 0x6f, 0x50, 0x31, 0x1b, 0x5e, - 0x18, 0xf0, 0x33, 0x6f, 0xc4, 0x77, 0x21, 0x56, 0x66, 0xe1, 0x88, 0x93, 0x3c, 0x69, - 0x39, 0x98, 0x9f, 0x6e, 0x6a, 0x3a, 0xdb, 0xa2, 0x29, 0x96, 0xaa, 0xe6, 0xa0, 0xfe, - 0x1b, 0xdd, 0xcb, 0xe1, 0x49, 0x6d, 0x96, 0x8d, 0xe0, 0x93, 0xdf, 0x44, 0xa3, 0x30, - 0x0f, 0x75, 0x15, 0xa1, 0x2c, 0x9d, 0x82, 0x22, 0x6d, 0x6b, 0x4d, 0x62, 0xc4, 0x6a, - 0x21, 0x3d, 0x5f, 0x01, 0x07, 0x10, 0x6f, 0xd2, 0xa2, 0x2d, 0x3b, 0x59, 0x86, 0x13, - 0xdb, 0x49, 0x1f, 0x70, 0xcc, 0xb1, 0xf0, 0x3b, 0x86, 0x59, 0x66, 0x9e, 0xd7, 0x44, - 0x34, 0xe4, 0x3b, 0x77, 0x1f, 0x22, 0x78, 0x07, 0x10, 0xfb, 0xd8, 0xf2, 0xf2, 0x0e, - 0x98, 0x97, 0xdf, 0x5c, 0xc2, 0x35, 0x48, 0x77, 0x9c, 0x6c, 0x08, 0x30, 0x83, 0x9d, - 0x23, 0x1c, 0x3f, 0xf9, 0xac, 0x54, 0x40, 0x7d, 0xfd, 0xfc, 0xc5, 0x90, 0x14, 0xbf, - 0x67, 0xd9, 0x68, 0x57, 0x06, 0xa5, 0x62, 0x2e, 0x38, 0xf7, 0xa9, 0x33, 0xc3, 0x4a, - 0xfb, 0xb6, 0xaa, 0x8c, 0xdf, 0xd9, 0x3b, 0xd2, 0xec, 0x91, 0xad, 0x37, 0x90, 0x4c, - 0xe1, 0x3b, 0x8a, 0xb8, 0xef, 0x77, 0x23, 0x66, 0xfa, 0xd3, 0xc3, 0xeb, 0xee, 0x8f, - 0x26, 0x11, 0xee, 0x7b, 0x6c, 0x2a, 0xf7, 0xe6, 0x53, 0xef, 0xbe, 0xc4, 0xdc, 0x4c, - 0xbf, 0x13, 0xac, 0xf3, 0x7e, 0x39, 0x9e, 0x2b, 0x0b, 0x05, 0xb6, 0x1c, 0xb7, 0xe1, - 0x7b, 0x15, 0x62, 0x7b, 0x62, 0x96, 0x2e, 0x21, 0x00, 0xb1, 0x95, 0xfe, 0xfe, 0x94, - 0xbc, 0x48, 0x4e, 0x88, 0x13, 0x97, 0x00, 0x73, 0x7d, 0xe1, 0xa5, 0xec, 0x7d, 0x9c, - 0xc8, 0x5d, 0x53, 0x3b, 0x61, 0xec, 0xad, 0x86, 0x53, 0xce, 0xdb, 0xb7, 0x71, 0xf6, - 0x75, 0xaf, 0x61, 0xe4, 0xc6, 0xf7, 0xef, 0xaa, 0xcc, 0x9f, 0x7e, 0x42, 0x4c, 0x16, - 0x71, 0x5b, 0x0a, 0x98, 0xc4, 0x46, 0x05, 0x9a, 0x27, 0x1a, 0x27, 0xbd, 0x56, 0x9d, - 0x1b, 0x5d, 0xbf, 0xae, 0x8f, 0x53, 0x89, 0x85, 0x24, 0xca, 0xe8, 0x70, 0x59, 0xff, - 0x34, 0xfb, 0x2a, 0x53, 0x32, 0x26, 0xbd, 0x29, 0xa0, 0xba, 0x6f, 0x8d, 0x08, 0x36, - 0xfd, 0x0a, 0x4c, 0x0d, 0x60, 0x9a, 0x72, 0xe1, 0x05, 0x39, 0xa4, 0x4f, 0x8c, 0x39, - 0xf6, 0x27, 0x9b, 0xe3, 0x96, 0xe4, 0x1c, 0xa9, 0xf2, 0x9a, 0x28, 0xce, 0x9f, 0xa0, - 0xdd, 0x51, 0xa3, 0x02, 0xe7, 0x70, 0xe1, 0xe3, 0xdb, 0x70, 0x6a, 0x34, 0xcb, 0x90, - 0x4e, 0xf0, 0x8d, 0x9c, 0x82, 0xc5, 0x5b, 0xc7, 0x28, 0xc9, 0x55, 0xb1, 0x20, 0xbb, - 0x2e, 0xc3, 0x73, 0xfc, 0xff, 0xff, 0x3c, 0x46, 0xd6, 0x03, 0xab, 0x38, 0x78, 0x96, - 0xd4, 0x9c, 0xd2, 0x1b, 0x2f, 0x77, 0xec, 0xfb, 0xbb, 0x02, 0xa5, 0xe1, 0x53, 0xb1, - 0x71, 0xaf, 0xed, 0x98, 0x6c, 0x15, 0xda, 0x6f, 0x2d, 0x4c, 0xf7, 0x45, 0xd1, 0x99, - 0x5f, 0x51, 0x36, 0xe1, 0xb3, 0xe6, 0x8a, 0x67, 0xa8, 0x99, 0x6f, 0xe7, 0x65, 0x61, - 0x6d, 0x8a, 0xa1, 0x1b, 0xcd, 0x9f, 0x8b, 0x59, 0x1d, 0xb8, 0x7e, 0xfc, 0xda, 0xaf, - 0xfd, 0x41, 0x00, 0x3e, 0xc7, 0x29, 0x36, 0x05, 0x42, 0x62, 0x08, 0x54, 0xfb, 0x04, - 0xb8, 0x0c, 0xb8, 0x61, 0xa6, 0x36, 0xa4, 0x71, 0x7d, 0x66, 0x68, 0x94, 0xc3, 0x2f, - 0x1f, 0x2b, 0xf2, 0x24, 0x7c, 0xc4, 0x15, 0xde, 0x1d, 0x0c, 0x4e, 0x71, 0x2b, 0x95, - 0x88, 0x42, 0xd6, 0xa4, 0xb2, 0x76, 0xde, 0xa5, 0xdb, 0x88, 0x42, 0x3f, 0x2b, 0x4c, - 0x66, 0x4b, 0x1d, 0x2b, 0x18, 0x77, 0xba, 0xf3, 0x37, 0x47, 0x34, 0x36, 0x14, 0xe5, - 0xeb, 0xe9, 0xb7, 0xe1, 0x2e, 0xd0, 0x15, 0x3f, 0x9c, 0xa7, 0x45, 0x8e, 0x4d, 0xa4, - 0x97, 0x63, 0x9d, 0xff, 0x13, 0x52, 0xff, 0x0e, 0xfa, 0xe0, 0x1d, 0x14, 0x03, 0x21, - 0xc2, 0x8d, 0xd0, 0xb6, 0x7b, 0x06, 0x98, 0x90, 0xf6, 0x13, 0x0f, 0x82, 0x46, 0xab, - 0x85, 0x44, 0x71, 0x75, 0x32, 0xd3, 0xa5, 0xf6, 0x36, 0x39, 0xa9, 0x9d, 0x7f, 0x8e, - 0x98, 0x31, 0xc6, 0x48, 0x51, 0xb7, 0xef, 0x68, 0x93, 0xb3, 0xc9, 0x74, 0x0f, 0x98, - 0x44, 0xd1, 0x8a, 0x61, 0x3b, 0x5f, 0x9a, 0x6a, 0xb4, 0xbd, 0x6e, 0x6a, 0x93, 0xe8, - 0xe4, 0xbe, 0xa5, 0x57, 0x5d, 0x2c, 0xb4, 0x33, 0x0c, 0x0a, 0xf8, 0x55, 0x83, 0x19, - 0xa9, 0x09, 0xa5, 0x98, 0x8a, 0x99, 0x2e, 0x40, 0x63, 0x43, 0xdd, 0x1c, 0x74, 0x2d, - 0x64, 0xcd, 0x4a, 0x17, 0xa2, 0xf3, 0x79, 0x5e, 0x8d, 0xb4, 0xd3, 0x0c, 0xcd, 0xf4, - 0x41, 0x56, 0x55, 0xed, 0xa7, 0xb4, 0x37, 0xe3, 0x39, 0x73, 0x23, 0x89, 0x6b, 0x11, - 0xb1, 0xbe, 0xd7, 0x2d, 0x63, 0xe3, 0x10, 0xaa, 0x49, 0x67, 0x1d, 0x85, 0x53, 0x4f, - 0x6d, 0xbc, 0x18, 0x1f, 0xeb, 0xb5, 0xbd, 0xc0, 0x8a, 0xc0, 0xd1, 0x23, 0x82, 0x9d, - 0x10, 0x8c, 0xd2, 0x69, 0xf3, 0xb0, 0xa3, 0x96, 0xf4, 0x24, 0x1e, 0x7d, 0xda, 0x72, - 0xf5, 0x48, 0x62, 0xbe, 0xde, 0xf0, 0x1c, 0x12, 0xe3, 0xc6, 0xcf, 0xdf, 0x75, 0xf6, - 0x76, 0xc2, 0xdd, 0xef, 0x91, 0xaf, 0x7f, 0x8a, 0x8a, 0x76, 0x9c, 0x25, 0xe1, 0x77, - 0xcd, 0x43, 0x0b, 0xed, 0xe7, 0x4b, 0x57, 0x69, 0x05, 0x19, 0xa9, 0x8d, 0xb1, 0xfb, - 0x5c, 0x36, 0x12, 0x80, 0xf7, 0x54, 0x0a, 0xc8, 0x27, 0xa9, 0x1b, 0x2d, 0x08, 0x75, - 0x2d, 0xec, 0xfb, 0x71, 0x56, 0xfc, 0xdb, 0x61, 0x75, 0x78, 0xb0, 0x53, 0xee, 0xe4, - 0x1f, 0x66, 0xa6, 0x0e, 0x04, 0x5c, 0x3a, 0x56, 0x9f, 0x3f, 0x7e, 0xdb, 0x76, 0x31, - 0x68, 0x2f, 0xde, 0x9e, 0xf9, 0x1e, 0xa8, 0x81, 0x1f, 0xc2, 0xc7, 0x8f, 0x64, 0x6a, - 0xf6, 0xb4, 0x71, 0x0e, 0xdb, 0xb8, 0xbf, 0x23, 0x28, 0xbd, 0x32, 0x73, 0xa2, 0xcb, - 0x72, 0xff, 0xcc, 0xa7, 0xc2, 0x17, 0xb8, 0x27, 0x19, 0x2d, 0xd2, 0xea, 0x92, 0x9e, - 0x97, 0x6d, 0x13, 0x1c, 0x9d, 0x20, 0x2e, 0xc5, 0x06, 0xa3, 0x5d, 0x93, 0xab, 0x21, - 0x6f, 0x64, 0xbd, 0x73, 0xfe, 0x5d, 0x8a, 0xba, 0xe4, 0x57, 0x1f, 0x85, 0xbe, 0xb8, - 0x4a, 0x7f, 0x93, 0xa3, 0xde, 0x37, 0xa4, 0x51, 0xf3, 0x08, 0xf7, 0xde, 0x6c, 0xcd, - 0x1a, 0x6e, 0xef, 0xef, 0x24, 0x69, 0x9f, 0x21, 0x58, 0xd1, 0x26, 0x1f, 0xe2, 0x51, - 0x82, 0xb5, 0x02, 0xda, 0x3e, 0x74, 0x61, 0x1a, 0x61, 0x16, 0xfc, 0x30, 0x64, 0xfa, - 0x72, 0x3c, 0x5a, 0x81, 0xad, 0xc0, 0xa3, 0x2f, 0x1e, 0xd6, 0x29, 0x91, 0x57, 0xd1, - 0xc1, 0x1c, 0x0a, 0xd9, 0x90, 0x41, 0x89, 0x46, 0x96, 0x30, 0x1d, 0x5b, 0x3f, 0x1b, - 0xf4, 0x32, 0x05, 0xd7, 0xdc, 0xcf, 0xa6, 0x8b, 0xbb, 0x4a, 0x1f, 0x5e, 0x24, 0x2b, - 0x3e, 0x69, 0x0b, 0xfc, 0x97, 0xb9, 0x43, 0x66, 0xa3, 0x43, 0xf5, 0xdd, 0x16, 0xdf, - 0x67, 0xb2, 0xed, 0x2b, 0xe2, 0x1c, 0x74, 0x71, 0x18, 0x87, 0x2b, 0x46, 0x2e, 0xe2, - 0x0c, 0x77, 0x8c, 0xed, 0x85, 0x6f, 0xa9, 0x80, 0x40, 0x3f, 0xb2, 0x4b, 0x78, 0x61, - 0x37, 0xd0, 0xef, 0x02, 0x78, 0x53, 0x9b, 0x00, 0xce, 0x6e, 0x23, 0xc0, 0x7e, 0xf2, - 0xa0, 0x7c, 0xb2, 0x4c, 0x51, 0xc5, 0xb4, 0x85, 0xe4, 0x54, 0xed, 0xf6, 0x61, 0xdb, - 0x4b, 0x93, 0x1a, 0xb8, 0xcb, 0x49, 0x4e, 0xb3, 0x94, 0xfd, 0x13, 0xc1, 0xb3, 0x20, - 0x85, 0xf2, 0x7b, 0x20, 0x4a, 0x4b, 0x87, 0xee, 0x6c, 0x80, 0x63, 0x45, 0xd7, 0x58, - 0x4c, 0xb1, 0x61, 0x00, 0x6a, 0xd9, 0x84, 0x8a, 0x24, 0xa2, 0x2a, 0x57, 0x71, 0xe3, - 0xa2, 0xab, 0x65, 0x46, 0x3f, 0x55, 0x3d, 0x52, 0xcd, 0x53, 0x5e, 0xf1, 0x0b, 0xdd, - 0x40, 0xd8, 0x87, 0x73, 0x72, 0xa5, 0x32, 0xe3, 0x73, 0x1b, 0x0e, 0xe9, 0x0c, 0x04, - 0xe8, 0xe4, 0x37, 0x47, 0xcc, 0x3e, 0xb9, 0x6b, 0xb8, 0x79, 0xbd, 0x94, 0xd7, 0x01, - 0x2a, 0xf4, 0x6a, 0x93, 0xba, 0x17, 0x70, 0x37, 0xf0, 0x62, 0x74, 0x4d, 0x3f, 0xdf, - 0xcc, 0xd3, 0x6a, 0xab, 0xe0, 0xf8, 0xcc, 0xca, 0x19, 0xdc, 0xf7, 0x84, 0x1b, 0x1e, - 0xe2, 0xf4, 0xfe, 0xb1, 0x80, 0x0e, 0x75, 0x44, 0x1c, 0x51, 0xe9, 0x5c, 0xce, 0x94, - 0xce, 0xee, 0xcd, 0x85, 0x87, 0xfb, 0xf5, 0x74, 0x30, 0x8d, 0xd7, 0x63, 0x63, 0x1b, - 0x73, 0x35, 0x78, 0x30, 0x91, 0xf4, 0xc8, 0xb3, 0xc8, 0xfb, 0x3c, 0xd9, 0x39, 0x70, - 0xce, 0xf0, 0xed, 0xa4, 0xca, 0x08, 0x44, 0x75, 0x68, 0x23, 0x9c, 0x02, 0xfe, 0x8f, - 0x67, 0x5e, 0x15, 0xc4, 0x9b, 0x51, 0x21, 0xb1, 0x00, 0xcc, 0x19, 0xfc, 0xc2, 0xb2, - 0x91, 0x3d, 0xf7, 0x4f, 0x75, 0x8f, 0x70, 0xbd, 0x6e, 0xeb, 0x73, 0x39, 0x51, 0x6e, - 0x5f, 0x1e, 0xff, 0x97, 0x00, 0xf8, 0xee, 0x13, 0x0e, 0x5c, 0x84, 0xce, 0xd7, 0xb1, - 0xce, 0xd6, 0x6b, 0xe9, 0xa0, 0x55, 0x96, 0xbe, 0x8e, 0x55, 0xf6, 0xd9, 0xfd, 0xf7, - 0xcf, 0x0f, 0xa6, 0x22, 0x90, 0xec, 0x67, 0x0b, 0x6b, 0xdd, 0x67, 0x38, 0xbb, 0x5c, - 0xfb, 0x34, 0x1e, 0xf5, 0xff, 0xb4, 0x2b, 0xc2, 0xab, 0xc5, 0x08, 0xff, 0x23, 0x12, - 0x48, 0xf2, 0xc2, 0xdc, 0x15, 0x77, 0x0d, 0x33, 0x72, 0x2b, 0x9c, 0x9d, 0xae, - ], - script_code: Script(vec![0xac, 0x65]), - transparent_input: Some(0), - hash_type: 3, - amount: 391892287957268, - consensus_branch_id: 1991772603, - sighash: [ - 0x6a, 0x3b, 0x2b, 0xcc, 0x15, 0x57, 0x89, 0xa2, 0x74, 0x39, 0xaa, 0x27, 0x5c, 0xa9, - 0x9e, 0xc6, 0x48, 0xdd, 0xd5, 0x88, 0xe8, 0x2e, 0xfa, 0xe4, 0xac, 0x46, 0xba, 0x3f, - 0xd0, 0xe3, 0xbb, 0xa0, - ], - }, - ]; - - for tv in test_vectors { + for tv in self::data::zip_0243::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = if let Some(n) = tv.transparent_input { - Some(( + let transparent_input = tv.transparent_input.map(|n| { + ( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - )) - } else { - None - }; + ) + }); assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input,), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), tv.sighash ); } diff --git a/zcash_primitives/src/transaction/tests/data.rs b/zcash_primitives/src/transaction/tests/data.rs new file mode 100644 index 0000000..28dd054 --- /dev/null +++ b/zcash_primitives/src/transaction/tests/data.rs @@ -0,0 +1,5686 @@ +pub mod tx_read_write { + // TxID: 64f0bd7fe30ce23753358fe3a2dc835b8fba9c0274c4e2c54a6f73114cb55639 + // From testnet block 280003. + pub const TX_READ_WRITE: [u8; 2005] = [ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0x8f, 0x64, 0x29, 0x96, 0xdf, 0x1e, + 0x93, 0xa6, 0xd7, 0x9a, 0xe5, 0xba, 0xae, 0x34, 0x93, 0xf4, 0x23, 0xca, 0x6c, 0x82, 0xe9, + 0x9f, 0x3e, 0x8d, 0x95, 0x24, 0xfa, 0x78, 0xbc, 0xf1, 0x61, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x6b, 0x48, 0x30, 0x45, 0x02, 0x21, 0x00, 0xb6, 0x5e, 0x37, 0x22, 0x97, 0x07, 0xd9, 0xcd, + 0x48, 0x39, 0x40, 0xd2, 0xab, 0x8b, 0xdc, 0x0b, 0x74, 0xb1, 0x2d, 0xda, 0x66, 0xd0, 0x2d, + 0xbd, 0xf3, 0x6f, 0xd3, 0x83, 0xb9, 0x60, 0x2a, 0x51, 0x02, 0x20, 0x4b, 0xe7, 0xfd, 0x7a, + 0x39, 0xa4, 0xa4, 0x2d, 0xff, 0x07, 0x1a, 0x5a, 0x2b, 0xc5, 0x1b, 0x49, 0x2d, 0x33, 0xf0, + 0xbc, 0x39, 0x4b, 0xc8, 0x78, 0x61, 0xe1, 0xbc, 0xaa, 0xf2, 0xba, 0xc9, 0x3b, 0x01, 0x21, + 0x02, 0x48, 0xe7, 0x8b, 0xdc, 0x18, 0xf1, 0xa8, 0x31, 0x10, 0xc1, 0x2e, 0x40, 0x08, 0xb7, + 0x64, 0x02, 0x69, 0x61, 0xb1, 0x68, 0xfe, 0x8d, 0x5a, 0x8d, 0x94, 0x7e, 0xfe, 0x6a, 0xf8, + 0x3c, 0xc8, 0x8e, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf0, 0xf2, 0x70, 0x18, 0x02, 0x00, 0x00, + 0x00, 0x19, 0x76, 0xa9, 0x14, 0xa2, 0x84, 0xd0, 0x51, 0x1d, 0x0e, 0x52, 0x0d, 0x36, 0xf4, + 0x44, 0xa3, 0x6c, 0x10, 0xbf, 0x54, 0xb4, 0xb0, 0x17, 0xcd, 0x88, 0xac, 0x00, 0x00, 0x00, + 0x00, 0xd7, 0x45, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xca, 0x9a, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x31, 0xa3, 0x05, 0x9e, 0x66, 0xaa, 0x6c, 0xa9, 0x7a, 0x62, 0xf5, 0x6e, + 0xa2, 0x34, 0x20, 0x75, 0x68, 0x56, 0x6f, 0x69, 0x71, 0xb3, 0x72, 0x2a, 0xe0, 0xdd, 0x82, + 0xc0, 0x03, 0x99, 0x69, 0x2a, 0xac, 0xb5, 0xfb, 0x12, 0xac, 0x58, 0x0a, 0xc2, 0x66, 0x24, + 0xa8, 0xcf, 0x0a, 0x90, 0x4c, 0xd6, 0xf4, 0xbf, 0xea, 0x55, 0x62, 0x52, 0x05, 0xcb, 0x58, + 0xf0, 0x6b, 0x1c, 0x19, 0x74, 0x23, 0x28, 0x0d, 0xea, 0xc7, 0x4e, 0xea, 0x97, 0x59, 0x8c, + 0x43, 0x14, 0xd8, 0x99, 0xa4, 0xfd, 0x85, 0x31, 0x1e, 0x04, 0x62, 0x57, 0xd2, 0xd4, 0xc2, + 0x97, 0xf1, 0x40, 0x6c, 0xf7, 0x09, 0xd9, 0x2a, 0x86, 0x07, 0xf7, 0x69, 0x8d, 0x45, 0xfe, + 0x9f, 0x41, 0xde, 0xa3, 0xa0, 0x57, 0x1c, 0x5d, 0xa5, 0xcf, 0xa7, 0x8e, 0x18, 0xeb, 0xf5, + 0x80, 0xc3, 0x61, 0x79, 0xd9, 0xd6, 0xe6, 0x32, 0x0a, 0x34, 0x8f, 0x14, 0x6c, 0x40, 0x7a, + 0xda, 0xb4, 0xcb, 0x31, 0x03, 0x92, 0xa5, 0xf5, 0xb5, 0xab, 0x28, 0x3b, 0x78, 0x34, 0x3b, + 0xa9, 0x1a, 0xbc, 0x7c, 0x4b, 0xfe, 0x23, 0xa3, 0xdb, 0xaf, 0x80, 0x37, 0xc6, 0x76, 0xe5, + 0x95, 0xa2, 0x65, 0x74, 0xb1, 0x81, 0x3b, 0xc2, 0xbf, 0x2d, 0x2e, 0x91, 0x1f, 0x6f, 0x3a, + 0xbb, 0x0b, 0xa6, 0xbc, 0xac, 0x7a, 0x29, 0x01, 0xfb, 0xdc, 0xe6, 0x5f, 0xb0, 0x7b, 0x56, + 0x36, 0x01, 0x7e, 0xf1, 0x4d, 0xff, 0x44, 0xcd, 0xee, 0xa7, 0x30, 0x47, 0x72, 0x94, 0xf2, + 0xf8, 0x61, 0x9b, 0xd3, 0xd5, 0xe6, 0xbe, 0x48, 0x98, 0xbf, 0x8d, 0x39, 0xc0, 0xe0, 0xea, + 0xe5, 0xa3, 0x68, 0x64, 0x62, 0x52, 0x06, 0xb9, 0xa8, 0xf9, 0x94, 0x0b, 0xf1, 0x66, 0x50, + 0xde, 0xf7, 0x92, 0x6e, 0xb0, 0xdb, 0x43, 0xb7, 0xd7, 0x61, 0x5e, 0x47, 0x74, 0xcf, 0x10, + 0x94, 0x82, 0xf2, 0xe8, 0x07, 0xfe, 0xe6, 0xc0, 0xc8, 0x84, 0xe8, 0x31, 0x4c, 0x67, 0xc5, + 0xd8, 0x5f, 0x4c, 0x22, 0x9c, 0xde, 0xab, 0x1e, 0x96, 0x4c, 0xf0, 0xc1, 0xad, 0xcb, 0x47, + 0xce, 0xbf, 0xc7, 0xc0, 0x67, 0xa0, 0xf3, 0xc8, 0x06, 0x81, 0x4a, 0x28, 0x5e, 0xdb, 0xb6, + 0x24, 0xf4, 0x71, 0x06, 0x29, 0x09, 0x89, 0x44, 0xac, 0x75, 0xe7, 0xc9, 0xcb, 0xc5, 0x6b, + 0xd0, 0xa0, 0x29, 0xe1, 0x11, 0x0e, 0xac, 0x60, 0xcb, 0x40, 0x77, 0xeb, 0xf1, 0x08, 0xfe, + 0x3e, 0x67, 0xcd, 0x06, 0x13, 0x91, 0xe5, 0xd6, 0x91, 0x6d, 0x5f, 0x41, 0xc0, 0x2b, 0x89, + 0x14, 0xc1, 0x2c, 0xf6, 0x05, 0xdb, 0x7d, 0x95, 0x92, 0x26, 0xe2, 0xe8, 0xff, 0x71, 0x26, + 0x3b, 0x9a, 0xf4, 0xc5, 0x9b, 0x0f, 0x4d, 0xb3, 0x15, 0xb7, 0x4c, 0xa2, 0xb0, 0xb7, 0xd2, + 0x52, 0x13, 0xd5, 0x29, 0x39, 0x54, 0xc3, 0xe5, 0x11, 0x72, 0x37, 0x0f, 0xb6, 0xc3, 0x5a, + 0xbe, 0x9c, 0xe3, 0x6e, 0xf2, 0x53, 0xe3, 0xa7, 0x2e, 0x19, 0xda, 0xc9, 0xbd, 0x73, 0x62, + 0xc4, 0x49, 0x92, 0x97, 0x42, 0x15, 0xc8, 0x2c, 0xb9, 0x0c, 0x99, 0x48, 0x8d, 0xbd, 0xe1, + 0x19, 0x63, 0xe8, 0x57, 0xce, 0xa6, 0xb8, 0x1b, 0x8e, 0xaa, 0xe3, 0x4b, 0x7c, 0xf5, 0xa9, + 0x7d, 0x6b, 0x60, 0xd4, 0x9f, 0xdf, 0xa2, 0x0f, 0x5f, 0x3c, 0x12, 0x0e, 0xf3, 0x82, 0xca, + 0x24, 0x69, 0x60, 0x4f, 0xb0, 0xc6, 0x84, 0x2c, 0x6d, 0x4f, 0xae, 0x96, 0x61, 0x66, 0x5b, + 0x5c, 0xbc, 0x61, 0x2c, 0xef, 0x13, 0x2f, 0x88, 0xfb, 0x7d, 0xa3, 0x93, 0xf3, 0x56, 0xe3, + 0xad, 0x13, 0xfc, 0x35, 0x57, 0x98, 0x0a, 0x77, 0x34, 0x23, 0x14, 0x53, 0xe4, 0x40, 0x79, + 0x04, 0x2f, 0xb4, 0x32, 0xf5, 0x5e, 0x75, 0x14, 0x84, 0xd5, 0xd6, 0xd3, 0x0f, 0xbc, 0x4f, + 0x99, 0x90, 0x13, 0xd5, 0xd4, 0xf2, 0xfb, 0x62, 0xf7, 0x14, 0x4e, 0x8d, 0xcd, 0x2a, 0xe5, + 0x95, 0x46, 0xcc, 0x43, 0x79, 0xad, 0x9f, 0x18, 0x59, 0xef, 0x80, 0xde, 0xc6, 0x6b, 0x1a, + 0x9b, 0x0b, 0x7f, 0xd2, 0xc4, 0x7b, 0xd3, 0x83, 0x02, 0xd2, 0x9c, 0x31, 0x99, 0x03, 0x29, + 0xa8, 0x95, 0x87, 0x6e, 0xd1, 0xd8, 0x4d, 0xb7, 0x57, 0x85, 0x6e, 0x75, 0xce, 0x9a, 0x1d, + 0xc7, 0xc7, 0x47, 0x2b, 0xc2, 0x18, 0xfb, 0x8d, 0x7c, 0x7d, 0x02, 0x8b, 0xb0, 0x2f, 0x10, + 0xef, 0xe7, 0xfe, 0x6a, 0x8c, 0x9c, 0xe0, 0x34, 0xfe, 0xa6, 0x6b, 0x90, 0x9c, 0x8d, 0x41, + 0x26, 0x25, 0x1c, 0x7d, 0x6e, 0x54, 0xf4, 0xcf, 0xc7, 0x78, 0xcd, 0x4f, 0x0e, 0x0b, 0xad, + 0x10, 0x96, 0x17, 0x6f, 0x2d, 0xd4, 0x5c, 0x45, 0xcb, 0xe1, 0x5e, 0x11, 0x8f, 0x90, 0xff, + 0x25, 0x45, 0xf8, 0x32, 0xf2, 0x36, 0x98, 0xf2, 0xc9, 0x53, 0x1b, 0x52, 0x65, 0x5a, 0x4c, + 0x0c, 0x89, 0x53, 0x55, 0x99, 0x28, 0xee, 0xdf, 0xc7, 0x56, 0xc3, 0x65, 0xcf, 0x92, 0x9b, + 0x84, 0x47, 0xdc, 0xdc, 0x7d, 0x82, 0x38, 0x49, 0xe0, 0x2f, 0xf6, 0x8b, 0x62, 0x78, 0xd7, + 0x54, 0x2c, 0xe0, 0xf1, 0x07, 0x0b, 0xb1, 0xad, 0x91, 0x3c, 0x1a, 0x35, 0x36, 0x25, 0xf5, + 0xd3, 0x5b, 0x14, 0xcf, 0xec, 0x84, 0xa6, 0x33, 0xd7, 0xfe, 0x25, 0x25, 0x6d, 0xcf, 0xfe, + 0x92, 0xf9, 0xa6, 0xf0, 0xfe, 0x00, 0xca, 0xaa, 0xa5, 0xb3, 0x9c, 0xc2, 0xab, 0x06, 0x76, + 0x8a, 0x42, 0xa5, 0xb4, 0x00, 0x83, 0xce, 0xa0, 0x1c, 0x96, 0xb3, 0xe6, 0x8d, 0x0f, 0x6a, + 0x58, 0x7e, 0xaf, 0x2d, 0xa6, 0xfd, 0xad, 0xc8, 0x25, 0x27, 0xf1, 0x86, 0xa6, 0x04, 0x71, + 0xce, 0x98, 0xe2, 0x7d, 0x2b, 0x11, 0xef, 0xc4, 0x79, 0x98, 0xf3, 0x03, 0x0a, 0x7a, 0x2e, + 0x5d, 0x0b, 0x0a, 0x7e, 0xb8, 0x0f, 0x6b, 0xd0, 0xe4, 0xb9, 0xc8, 0x36, 0x7c, 0x6c, 0x52, + 0x2d, 0x94, 0x15, 0xf8, 0xca, 0xec, 0x7b, 0x0a, 0x73, 0x18, 0xd5, 0x3d, 0xce, 0x39, 0x1c, + 0xf7, 0xe7, 0x38, 0x9c, 0x9a, 0x74, 0xaa, 0x6a, 0x4c, 0x21, 0x7c, 0x28, 0x85, 0x19, 0xaf, + 0x81, 0xba, 0x21, 0x22, 0xca, 0x0c, 0x58, 0x40, 0xcc, 0x02, 0xcf, 0x1b, 0xcf, 0x15, 0x0c, + 0xd3, 0xdf, 0x33, 0xc0, 0xac, 0xfd, 0x00, 0x53, 0xe6, 0x68, 0xb9, 0x26, 0x56, 0x1b, 0x92, + 0x40, 0x98, 0xd9, 0x7a, 0xaa, 0xb5, 0x7e, 0xe1, 0x11, 0x3d, 0xf9, 0x66, 0xa4, 0x22, 0xef, + 0x9b, 0x01, 0x46, 0x17, 0xbc, 0xee, 0xf0, 0x5f, 0xb6, 0x46, 0x8e, 0x33, 0x0e, 0x2d, 0xec, + 0xe3, 0xf3, 0x75, 0xe9, 0x8e, 0xf0, 0x3e, 0x5b, 0x18, 0xa9, 0x53, 0xe2, 0x30, 0x1f, 0xcc, + 0xec, 0x86, 0x20, 0x0a, 0xe4, 0x32, 0xc9, 0xc1, 0x2c, 0x30, 0x77, 0x54, 0x37, 0xf3, 0x62, + 0x97, 0x14, 0xa9, 0xfa, 0xbe, 0xb5, 0x32, 0x89, 0x40, 0x2b, 0x7f, 0xd3, 0x86, 0xce, 0xf2, + 0xb1, 0x14, 0x67, 0x23, 0xa8, 0x9d, 0x0f, 0x81, 0x65, 0x1e, 0x00, 0xca, 0xea, 0x2f, 0x3a, + 0xc9, 0xee, 0xfe, 0xfb, 0x86, 0x8d, 0x85, 0xed, 0x23, 0x54, 0xf5, 0x30, 0xfe, 0x38, 0xfe, + 0x3a, 0x3a, 0x6a, 0xab, 0x47, 0xd4, 0x2d, 0xc2, 0x13, 0x29, 0xe3, 0xad, 0x1b, 0x9d, 0x06, + 0xc0, 0xc8, 0xd6, 0x53, 0x74, 0x56, 0xf5, 0x4a, 0xd0, 0x45, 0x3f, 0x44, 0x41, 0x75, 0xd8, + 0x7e, 0xf5, 0xcd, 0xd1, 0x69, 0x46, 0x62, 0xe0, 0xa1, 0xe6, 0xe3, 0x63, 0x2e, 0xd7, 0xa8, + 0xe7, 0x6b, 0xc7, 0xb1, 0xb5, 0xa4, 0x18, 0xf0, 0x86, 0xd3, 0x40, 0x81, 0x5e, 0xc3, 0x98, + 0xf0, 0x92, 0xe9, 0x78, 0x69, 0xf5, 0xe2, 0x01, 0xc2, 0x2c, 0x87, 0x91, 0x8f, 0x76, 0x6a, + 0x35, 0x32, 0xeb, 0x9a, 0x4f, 0xc9, 0xac, 0xf1, 0x96, 0xcb, 0xc2, 0xd0, 0x28, 0x51, 0x19, + 0xa4, 0x21, 0x6d, 0x25, 0x81, 0xcd, 0x2d, 0x91, 0xbc, 0xdc, 0xe8, 0x68, 0xc4, 0x68, 0xf6, + 0xf3, 0x4c, 0xf4, 0x9e, 0x3a, 0x56, 0xce, 0x24, 0x9a, 0x2f, 0xd8, 0xcf, 0x36, 0xb0, 0x1b, + 0x0f, 0x77, 0xde, 0x72, 0x2b, 0xbc, 0xe2, 0x67, 0xe3, 0xe5, 0x52, 0x16, 0x88, 0xe6, 0x52, + 0x22, 0x23, 0x5c, 0x91, 0xc2, 0x63, 0xd8, 0x0e, 0x28, 0x29, 0x7e, 0x92, 0x9d, 0x88, 0x5b, + 0x7b, 0x9c, 0x1a, 0x16, 0x54, 0xb2, 0xd0, 0xb8, 0x75, 0x77, 0xc9, 0xa1, 0xc7, 0x25, 0xf5, + 0x44, 0x15, 0xdc, 0x5f, 0x52, 0xdd, 0xe0, 0x69, 0x5f, 0x9f, 0x6d, 0xcb, 0x4b, 0x6e, 0xe3, + 0xe3, 0xea, 0x70, 0x29, 0x04, 0xc1, 0x1f, 0xf9, 0x2f, 0x55, 0x53, 0x4c, 0x7e, 0xf9, 0x8c, + 0xe7, 0x93, 0xd7, 0x47, 0x56, 0xa4, 0x5d, 0x4e, 0x32, 0x0a, 0x42, 0x5e, 0x98, 0x2d, 0x5b, + 0x37, 0x2d, 0x6a, 0x8d, 0x41, 0xfb, 0x86, 0xba, 0x51, 0x64, 0x81, 0x68, 0x32, 0xa4, 0x81, + 0x82, 0x5c, 0x8c, 0x6a, 0xd7, 0x27, 0x09, 0x69, 0x85, 0x9e, 0x55, 0xd2, 0x36, 0x75, 0x35, + 0x06, 0x0f, 0x99, 0x85, 0x70, 0x65, 0x17, 0x04, 0x66, 0xbd, 0xb7, 0x0c, 0xb9, 0x3a, 0xb2, + 0xf9, 0xc0, 0xe2, 0x93, 0xa0, 0xa9, 0x19, 0x84, 0x3b, 0xbf, 0x34, 0xc2, 0xfe, 0x61, 0xb0, + 0xc3, 0xe3, 0x2a, 0xa7, 0x07, 0x8e, 0x83, 0xd4, 0xc1, 0x92, 0x9e, 0x1e, 0x1d, 0x86, 0x14, + 0x1c, 0xde, 0xb1, 0x89, 0x20, 0x91, 0x09, 0x75, 0xdb, 0x3a, 0x76, 0x26, 0x82, 0x05, 0x99, + 0x63, 0x0c, 0x42, 0x3a, 0xde, 0x23, 0x3d, 0x5d, 0x60, 0x68, 0x55, 0x24, 0xe8, 0xd8, 0x03, + 0x2b, 0x86, 0x1b, 0x4a, 0xad, 0x20, 0x02, 0xa8, 0xfd, 0x17, 0xc9, 0x28, 0x2b, 0x82, 0x5f, + 0x02, 0xd3, 0x53, 0xe2, 0x91, 0x37, 0x9c, 0xed, 0x00, 0xeb, 0xaa, 0x3c, 0x03, 0xe0, 0x1d, + 0x9c, 0x59, 0xf4, 0x05, 0x09, 0x9d, 0x1c, 0x34, 0x32, 0xba, 0xd0, 0x63, 0x58, 0xd6, 0xb1, + 0x94, 0x2f, 0x0b, 0xaf, 0x71, 0x09, 0x98, 0xd1, 0x0a, 0x22, 0xd1, 0x55, 0xb0, 0xfe, 0x84, + 0x99, 0x52, 0x89, 0x31, 0x26, 0x94, 0x9f, 0xf9, 0x2d, 0xe3, 0xa4, 0xc2, 0xee, 0xaf, 0xdf, + 0x68, 0x84, 0x35, 0xe3, 0x25, 0xd8, 0x1c, 0x2c, 0xe0, 0x08, 0xcf, 0x6c, 0x76, 0x03, 0x0d, + 0x4d, 0x46, 0x34, 0x2a, 0xc3, 0x37, 0x2c, 0x73, 0x98, 0x65, 0x60, 0xc4, 0xec, 0x35, 0xa6, + 0xf6, 0x49, 0xef, 0x02, 0xc1, 0x19, 0x36, 0xb7, 0x03, 0x9b, 0xc6, 0xf5, 0xd0, 0x94, 0x38, + 0xdb, 0xe4, 0x76, 0x25, 0x1b, 0x59, 0x64, 0xb6, 0x8f, 0x02, 0xee, 0xdf, 0xf7, 0xa9, 0xe0, + 0xed, 0x3e, 0x30, 0x90, 0x96, 0x5a, 0x22, 0xf2, 0xc5, 0x52, 0xce, 0x3b, 0x2b, 0x47, 0x4f, + 0xd2, 0xfc, 0x06, 0xb5, 0x09, 0x27, 0x83, 0x0a, 0x05, 0xa3, 0x03, 0xfa, 0xff, 0xd6, 0x84, + 0x82, 0xd7, 0xb7, 0x85, 0x38, 0x43, 0x25, 0x40, 0xdd, 0x32, 0x61, 0xab, 0x75, 0x9b, 0x65, + 0x82, 0x12, 0x9a, 0x7f, 0x18, 0xd8, 0x01, 0xc5, 0x43, 0x19, 0xca, 0x52, 0xa3, 0xc6, 0xa3, + 0xdb, 0x63, 0x50, 0x44, 0xd6, 0x25, 0xe2, 0x40, 0x38, 0xad, 0x42, 0x77, 0xf8, 0xd5, 0xbf, + 0x01, 0x60, 0x35, 0x16, 0x5f, 0x21, 0xb0, 0x70, 0xe8, 0x16, 0x9d, 0x65, 0x7d, 0x6e, 0xd1, + 0xfa, 0x7f, 0x8e, 0xd0, 0x9b, 0x4e, 0x1d, 0x9c, 0xa2, 0xe5, 0x1a, 0x24, 0xda, 0x55, 0xe4, + 0x3b, 0x3f, 0xca, 0x98, 0x59, 0xb2, 0x40, 0x8c, 0x26, 0xaa, 0xcb, 0xad, 0x74, 0x9e, 0xbe, + 0x88, 0x2c, 0x31, 0xe7, 0x20, 0x5e, 0x63, 0x8b, 0xb7, 0xe2, 0xbf, 0xc8, 0xa3, 0xf1, 0xc0, + 0x2c, 0x0c, 0xa7, 0xbb, 0x9d, 0xaa, 0xab, 0x7f, 0xcb, 0xf8, 0x45, 0xd8, 0x00, 0x2c, 0x3d, + 0xe7, 0x99, 0x24, 0xdc, 0xaa, 0xdc, 0x24, 0xbd, 0xc0, 0x08, 0x2f, 0x4a, 0x6b, 0x61, 0x87, + 0x6f, 0x31, 0x92, 0xa8, 0x81, 0xf5, 0x9a, 0x68, 0x2d, 0x27, 0x36, 0x85, 0xd4, 0x79, 0x5c, + 0x9b, 0xd7, 0xcc, 0xcf, 0x49, 0xde, 0x34, 0x44, 0x3a, 0x9f, 0x9c, 0xb3, 0x5b, 0xbf, 0x25, + 0x4c, 0x50, 0x61, 0x1b, 0x7c, 0x13, 0x24, 0xb1, 0x10, 0x94, 0x66, 0x7b, 0x6b, 0x60, 0x8c, + 0x39, 0xd1, 0x25, 0x2c, 0xeb, 0xcc, 0x48, 0x77, 0xce, 0xea, 0x76, 0xe1, 0x9b, 0x84, 0x2b, + 0x67, 0xf6, 0x26, 0x74, 0x3f, 0xab, 0x29, 0x77, 0x76, 0xcc, 0x9c, 0xf7, 0x9e, 0x90, 0xe8, + 0xfc, 0xe1, 0x00, 0x17, 0x90, 0xc2, 0xe7, 0xd5, 0xc9, 0x58, 0x64, 0x7c, 0xca, 0x5d, 0x33, + 0x97, 0xd2, 0x0a, 0xfc, 0xf2, 0x9b, 0xa4, 0x4f, 0x62, 0xa7, 0xc6, 0x2e, 0x90, 0x8d, 0x84, + 0x8d, 0x81, 0xa7, 0x9f, 0xad, 0xbb, 0x37, 0x0a, 0xba, 0x93, 0xb0, 0x3e, 0x41, 0xd4, 0xbc, + 0x49, 0xe2, 0x99, 0xd6, 0xd3, 0x3f, 0xaf, 0x86, 0x9f, 0x36, 0x37, 0x14, 0x14, 0xce, 0x64, + 0x6f, 0xc2, 0xca, 0x6d, 0xcf, 0xf5, 0x5a, 0x6e, 0x06, 0x39, 0xd5, 0x0c, 0xae, 0xb1, 0x14, + 0xc4, 0x18, 0xc6, 0x26, 0xb8, 0x67, 0x15, 0x43, 0x64, 0x81, 0xd1, 0x92, 0x8d, 0x55, 0xa7, + 0x56, 0xa6, 0x03, 0xe7, 0x11, 0x0c, 0x3a, 0xfe, 0x96, 0x3c, 0x2b, 0x29, 0xa4, 0x78, 0xf9, + 0xd4, 0x39, 0x7b, 0x88, 0x5a, 0x67, 0xb0, 0x93, 0xa3, 0x45, 0x79, 0x62, 0x19, 0xc1, 0x11, + 0xb7, 0xe9, 0x4d, 0xb3, 0x90, 0xaa, 0x4b, 0xb7, 0x6b, 0x66, 0xa5, 0x34, 0xe5, 0xe2, 0x67, + 0x9b, 0x27, 0xdb, 0x5f, 0x95, 0xfd, 0x09, 0xa3, 0x6b, 0x05, + ]; +} + +pub mod zip_0143 { + use crate::{consensus, legacy::Script}; + + pub struct Test0143Vector { + pub tx: Vec, + pub script_code: Script, + pub transparent_input: Option, + pub hash_type: u32, + pub amount: i64, + pub consensus_branch_id: consensus::BranchId, + pub sighash: [u8; 32], + } + + pub fn make_test_vectors() -> Vec { + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0143.py + vec![ + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0xe7, 0x71, 0x98, + 0x11, 0x89, 0x3e, 0x00, 0x00, 0x09, 0x52, 0x00, 0xac, 0x65, 0x51, 0xac, 0x63, + 0x65, 0x65, 0xb2, 0x83, 0x5a, 0x08, 0x05, 0x75, 0x02, 0x00, 0x02, 0x51, 0x51, + 0x48, 0x1c, 0xdd, 0x86, 0xb3, 0xcc, 0x43, 0x18, 0x00, + ], + script_code: Script(vec![0x6a, 0x00, 0x00, 0x00, 0x63, 0xac, 0x53]), + transparent_input: None, + hash_type: 1, + amount: 1672704339313879, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0xa1, 0xf1, 0xa4, 0xe5, 0xcd, 0x9b, 0xd5, 0x22, 0x32, 0x2d, 0x66, 0x1e, 0xdd, + 0x2a, 0xf1, 0xbf, 0x2a, 0x70, 0x19, 0xcf, 0xab, 0x94, 0xec, 0xe1, 0x8f, 0x4b, + 0xa9, 0x35, 0xb0, 0xa1, 0x90, 0x73, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x42, 0x01, 0xcf, 0xb1, + 0xcd, 0x8d, 0xbf, 0x69, 0xb8, 0x25, 0x0c, 0x18, 0xef, 0x41, 0x29, 0x4c, 0xa9, + 0x79, 0x93, 0xdb, 0x54, 0x6c, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, + 0xa5, 0xe2, 0x9d, 0x4e, 0x30, 0xa7, 0x03, 0xac, 0x6a, 0x00, 0x98, 0x42, 0x1c, + 0x69, 0x37, 0x8a, 0xf1, 0xe4, 0x0f, 0x64, 0xe1, 0x25, 0x94, 0x6f, 0x62, 0xc2, + 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69, 0x68, 0x91, 0x2a, 0x63, 0x81, + 0xce, 0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x05, 0x63, + 0x63, 0x63, 0x53, 0x53, 0xe8, 0xc7, 0x20, 0x3d, 0x02, 0xd2, 0xda, 0x86, 0x38, + 0x7a, 0xe6, 0x01, 0x00, 0x08, 0x00, 0x63, 0x65, 0x6a, 0x63, 0xac, 0x52, 0x00, + 0xa7, 0x62, 0x29, 0x97, 0xf4, 0xff, 0x04, 0x00, 0x07, 0x51, 0x51, 0x00, 0x53, + 0x53, 0x65, 0x65, 0x97, 0xb0, 0xe4, 0xe4, 0xc7, 0x05, 0xfc, 0x05, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, 0xfa, + 0x3d, 0x5a, 0x57, 0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, + 0xfb, 0x1a, 0x38, 0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c, 0x3e, 0x0a, 0xd3, 0x36, + 0x0c, 0x1d, 0x37, 0x10, 0xac, 0xd2, 0x0b, 0x18, 0x3e, 0x31, 0xd4, 0x9f, 0x25, + 0xc9, 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, + 0x98, 0x51, 0xa7, 0xaf, 0x9d, 0xb6, 0x99, 0x0e, 0xd8, 0x3d, 0xd6, 0x4a, 0xf3, + 0x59, 0x7c, 0x04, 0x32, 0x3e, 0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, + 0xb9, 0xda, 0x94, 0x8d, 0x32, 0x0d, 0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, + 0xdf, 0x65, 0x8d, 0x24, 0xae, 0x67, 0xc2, 0x2c, 0x8d, 0x13, 0x09, 0x13, 0x1f, + 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76, 0xd3, 0x8d, 0x47, 0xf1, 0xe1, + 0x91, 0xe0, 0x0c, 0x7a, 0x1d, 0x48, 0xaf, 0x04, 0x68, 0x27, 0x59, 0x1e, 0x97, + 0x33, 0xa9, 0x7f, 0xa6, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, + 0xed, 0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0xfc, 0x1b, 0xe4, 0xaa, 0xc0, 0x0f, 0xf2, + 0x71, 0x1e, 0xbd, 0x93, 0x1d, 0xe5, 0x18, 0x85, 0x68, 0x78, 0xf7, 0x34, 0x76, + 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, 0xf7, 0x39, 0x3c, 0x94, + 0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, + 0x0f, 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, + 0xf4, 0x73, 0xf4, 0x68, 0xa0, 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88, 0x0d, + 0x78, 0x0c, 0xb0, 0x7f, 0xcf, 0xaa, 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5, + 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d, 0x2b, 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, + 0x94, 0x94, 0xc6, 0xac, 0x89, 0x3c, 0x49, 0x72, 0x38, 0x33, 0xec, 0x89, 0x26, + 0xc1, 0x03, 0x95, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, + 0x5d, 0x99, 0x58, 0x9c, 0x03, 0xb8, 0x38, 0xe8, 0xaa, 0xf7, 0x45, 0x53, 0x3e, + 0xd9, 0xe8, 0xae, 0x3a, 0x1c, 0xd0, 0x74, 0xa5, 0x1a, 0x20, 0xda, 0x8a, 0xba, + 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, 0xed, 0x42, 0x43, 0x5e, 0x02, 0x47, + 0x69, 0x30, 0xd0, 0x69, 0x89, 0x6c, 0xff, 0x30, 0xeb, 0x41, 0x4f, 0x72, 0x7b, + 0x89, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75, 0xa4, 0xa6, + 0xf2, 0x65, 0x72, 0x50, 0x4b, 0x0b, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, + 0x11, 0xe5, 0x25, 0x96, 0xbc, 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, + 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11, 0x7d, 0x41, 0x7a, + 0xdb, 0x3d, 0x15, 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50, 0x0c, 0x6b, + 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, 0xcc, + 0x40, 0xa9, 0x8d, 0x5f, 0x29, 0x03, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, + 0xfd, 0xbb, 0x5d, 0x47, 0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, + 0x57, 0xb9, 0x27, 0xb7, 0xfa, 0xe2, 0xdb, 0x58, 0x71, 0x05, 0x41, 0x5d, 0x02, + 0x42, 0x78, 0x9d, 0x38, 0xf5, 0x0b, 0x8d, 0xbc, 0xc1, 0x29, 0xca, 0xb3, 0xd1, + 0x7d, 0x19, 0xf3, 0x35, 0x5b, 0xcf, 0x73, 0xce, 0xcb, 0x8c, 0xb8, 0xa5, 0xda, + 0x01, 0x30, 0x71, 0x52, 0xf1, 0x39, 0x02, 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, + 0x82, 0xd3, 0x90, 0x26, 0xc6, 0xcb, 0x4c, 0xd4, 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, + 0x4f, 0x5a, 0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15, 0x40, 0x6f, 0x2f, 0xdd, 0x2a, + 0x02, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, 0x2a, 0x1b, 0xaf, 0xce, + 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, 0x79, 0xf8, 0x80, + 0x08, 0xe3, 0x15, 0xdc, 0x7d, 0x83, 0x88, 0x03, 0x6c, 0x17, 0x82, 0xfd, 0x27, + 0x95, 0xd1, 0x8a, 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, 0x48, + 0x9c, 0xe7, 0x57, 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, + 0xdf, 0x73, 0xca, 0xec, 0x65, 0x60, 0x40, 0x37, 0x31, 0x4f, 0xaa, 0xce, 0xb5, + 0x62, 0x18, 0xc6, 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f, + 0x21, 0xa9, 0xfb, 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, + 0x00, 0xe1, 0xb1, 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, + 0x70, 0x96, 0x49, 0xe9, 0x50, 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, + 0x1f, 0xe3, 0x89, 0x5b, 0x8c, 0xc3, 0xd1, 0x4d, 0x2c, 0xf6, 0x55, 0x6d, 0xf6, + 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a, 0x69, 0xf5, 0x33, 0x57, 0xd7, 0x76, 0x7f, + 0x4f, 0x5c, 0xcb, 0xdb, 0xc5, 0x96, 0x63, 0x12, 0x77, 0xf8, 0xfe, 0xcd, 0x08, + 0xcb, 0x05, 0x6b, 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2, 0x44, + 0xfc, 0x71, 0x62, 0x69, 0xb9, 0x26, 0xd6, 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, + 0x6b, 0xf2, 0x1a, 0xff, 0x9e, 0x68, 0x62, 0x5a, 0x19, 0x24, 0x40, 0xea, 0x06, + 0x82, 0x81, 0x23, 0xd9, 0x78, 0x84, 0x80, 0x6f, 0x15, 0xfa, 0x08, 0xda, 0x52, + 0x75, 0x4a, 0x10, 0x95, 0xe3, 0xff, 0x1a, 0xbd, 0x5c, 0xe4, 0xfd, 0xdf, 0xcc, + 0xfc, 0x3a, 0x61, 0x28, 0xae, 0xf7, 0x84, 0xa6, 0x46, 0x10, 0xa8, 0x9d, 0x1a, + 0x70, 0x99, 0x21, 0x6d, 0x08, 0x14, 0xd3, 0xa2, 0xd4, 0x52, 0x43, 0x1c, 0x32, + 0xd4, 0x11, 0xac, 0x1c, 0xce, 0x82, 0xad, 0x02, 0x29, 0x40, 0x7b, 0xbc, 0x48, + 0x98, 0x56, 0x75, 0xe3, 0xf8, 0x74, 0xa4, 0x53, 0x3f, 0x1d, 0x63, 0xa8, 0x4d, + 0xfa, 0x3e, 0x0f, 0x46, 0x0f, 0xe2, 0xf5, 0x7e, 0x34, 0xfb, 0xc7, 0x54, 0x23, + 0xc3, 0x73, 0x7f, 0x5b, 0x2a, 0x06, 0x15, 0xf5, 0x72, 0x2d, 0xb0, 0x41, 0xa3, + 0xef, 0x66, 0xfa, 0x48, 0x3a, 0xfd, 0x3c, 0x2e, 0x19, 0xe5, 0x94, 0x44, 0xa6, + 0x4a, 0xdd, 0x6d, 0xf1, 0xd9, 0x63, 0xf5, 0xdd, 0x5b, 0x50, 0x10, 0xd3, 0xd0, + 0x25, 0xf0, 0x28, 0x7c, 0x4c, 0xf1, 0x9c, 0x75, 0xf3, 0x3d, 0x51, 0xdd, 0xdd, + 0xba, 0x5d, 0x65, 0x7b, 0x43, 0xee, 0x8d, 0xa6, 0x45, 0x44, 0x38, 0x14, 0xcc, + 0x73, 0x29, 0xf3, 0xe9, 0xb4, 0xe5, 0x4c, 0x23, 0x6c, 0x29, 0xaf, 0x39, 0x23, + 0x10, 0x17, 0x56, 0xd9, 0xfa, 0x4b, 0xd0, 0xf7, 0xd2, 0xdd, 0xaa, 0xcb, 0x6b, + 0x0f, 0x86, 0xa2, 0x65, 0x8e, 0x0a, 0x07, 0xa0, 0x5a, 0xc5, 0xb9, 0x50, 0x05, + 0x1c, 0xd2, 0x4c, 0x47, 0xa8, 0x8d, 0x13, 0xd6, 0x59, 0xba, 0x2a, 0x46, 0xca, + 0x18, 0x30, 0x81, 0x6d, 0x09, 0xcd, 0x76, 0x46, 0xf7, 0x6f, 0x71, 0x6a, 0xbe, + 0xc5, 0xde, 0x07, 0xfe, 0x9b, 0x52, 0x34, 0x10, 0x80, 0x6e, 0xa6, 0xf2, 0x88, + 0xf8, 0x73, 0x6c, 0x23, 0x35, 0x7c, 0x85, 0xf4, 0x57, 0x91, 0xe1, 0x70, 0x80, + 0x29, 0xd9, 0x82, 0x4d, 0x90, 0x70, 0x46, 0x07, 0xf3, 0x87, 0xa0, 0x3e, 0x49, + 0xbf, 0x98, 0x36, 0x57, 0x44, 0x31, 0x34, 0x5a, 0x78, 0x77, 0xef, 0xaa, 0x8a, + 0x08, 0xe7, 0x30, 0x81, 0xef, 0x8d, 0x62, 0xcb, 0x78, 0x0a, 0xb6, 0x88, 0x3a, + 0x50, 0xa0, 0xd4, 0x70, 0x19, 0x0d, 0xfb, 0xa1, 0x0a, 0x85, 0x7f, 0x82, 0x84, + 0x2d, 0x38, 0x25, 0xb3, 0xd6, 0xda, 0x05, 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, + 0xc0, 0xb7, 0x16, 0xc4, 0x8f, 0xbd, 0x46, 0x7f, 0x75, 0xb7, 0x80, 0x14, 0x9a, + 0xe8, 0x80, 0x8f, 0x4e, 0x68, 0xf5, 0x0c, 0x05, 0x36, 0xac, 0xdd, 0xf6, 0xf1, + 0xae, 0xab, 0x01, 0x6b, 0x6b, 0xc1, 0xec, 0x14, 0x4b, 0x4e, 0x55, 0x3a, 0xcf, + 0xd6, 0x70, 0xf7, 0x7e, 0x75, 0x5f, 0xc8, 0x8e, 0x06, 0x77, 0xe3, 0x1b, 0xa4, + 0x59, 0xb4, 0x4e, 0x30, 0x77, 0x68, 0x95, 0x8f, 0xe3, 0x78, 0x9d, 0x41, 0xc2, + 0xb1, 0xff, 0x43, 0x4c, 0xb3, 0x0e, 0x15, 0x91, 0x4f, 0x01, 0xbc, 0x6b, 0xc2, + 0x30, 0x7b, 0x48, 0x8d, 0x25, 0x56, 0xd7, 0xb7, 0x38, 0x0e, 0xa4, 0xff, 0xd7, + 0x12, 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, 0x40, 0x59, 0xf3, + 0x96, 0xbf, 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, 0xa9, 0x44, + 0xf7, 0x2d, 0x43, 0x6a, 0x10, 0x2f, 0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, 0xb0, + 0x86, 0xfe, 0x9d, 0x2e, 0x71, 0x62, 0x47, 0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, + 0xec, 0x95, 0x12, 0xbf, 0xb3, 0xf3, 0x83, 0x27, 0x29, 0x6e, 0xfa, 0xa7, 0x43, + 0x28, 0xb1, 0x18, 0xc2, 0x74, 0x02, 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4, + 0xbb, 0xc6, 0x8e, 0x37, 0xc0, 0xaa, 0x7d, 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, + 0x3b, 0x84, 0x1e, 0x75, 0x17, 0x13, 0xa0, 0x29, 0x43, 0x90, 0x5a, 0xae, 0x08, + 0x03, 0xfd, 0x69, 0x44, 0x2e, 0xb7, 0x68, 0x1e, 0xc2, 0xa0, 0x56, 0x00, 0x05, + 0x4e, 0x92, 0xee, 0xd5, 0x55, 0x02, 0x8f, 0x21, 0xb6, 0xa1, 0x55, 0x26, 0x8a, + 0x2d, 0xd6, 0x64, 0x0a, 0x69, 0x30, 0x1a, 0x52, 0xa3, 0x8d, 0x4d, 0x9f, 0x9f, + 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71, 0x18, 0x14, 0x1c, 0xe4, 0xc9, 0xbe, + 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81, 0xa1, 0x55, 0xfa, 0x3a, 0x2b, + 0x9d, 0xaf, 0xd8, 0x2e, 0x65, 0x0b, 0x38, 0x6a, 0xd3, 0xa0, 0x8c, 0xb6, 0xb8, + 0x31, 0x31, 0xac, 0x30, 0x0b, 0x08, 0x46, 0x35, 0x4a, 0x7e, 0xef, 0x9c, 0x41, + 0x0e, 0x4b, 0x62, 0xc4, 0x7c, 0x54, 0x26, 0x90, 0x7d, 0xfc, 0x66, 0x85, 0xc5, + 0xc9, 0x9b, 0x71, 0x41, 0xac, 0x62, 0x6a, 0xb4, 0x76, 0x1f, 0xd3, 0xf4, 0x1e, + 0x72, 0x8e, 0x1a, 0x28, 0xf8, 0x9d, 0xb8, 0x9f, 0xfd, 0xec, 0xa3, 0x64, 0xdd, + 0x2f, 0x0f, 0x07, 0x39, 0xf0, 0x53, 0x45, 0x56, 0x48, 0x31, 0x99, 0xc7, 0x1f, + 0x18, 0x93, 0x41, 0xac, 0x9b, 0x78, 0xa2, 0x69, 0x16, 0x42, 0x06, 0xa0, 0xea, + 0x1c, 0xe7, 0x3b, 0xfb, 0x2a, 0x94, 0x2e, 0x73, 0x70, 0xb2, 0x47, 0xc0, 0x46, + 0xf8, 0xe7, 0x5e, 0xf8, 0xe3, 0xf8, 0xbd, 0x82, 0x1c, 0xf5, 0x77, 0x49, 0x18, + 0x64, 0xe2, 0x0e, 0x6d, 0x08, 0xfd, 0x2e, 0x32, 0xb5, 0x55, 0xc9, 0x2c, 0x66, + 0x1f, 0x19, 0x58, 0x8b, 0x72, 0xa8, 0x95, 0x99, 0x71, 0x0a, 0x88, 0x06, 0x12, + 0x53, 0xca, 0x28, 0x5b, 0x63, 0x04, 0xb3, 0x7d, 0xa2, 0xb5, 0x29, 0x4f, 0x5c, + 0xb3, 0x54, 0xa8, 0x94, 0x32, 0x28, 0x48, 0xcc, 0xbd, 0xc7, 0xc2, 0x54, 0x5b, + 0x7d, 0xa5, 0x68, 0xaf, 0xac, 0x87, 0xff, 0xa0, 0x05, 0xc3, 0x12, 0x24, 0x1c, + 0x2d, 0x57, 0xf4, 0xb4, 0x5d, 0x64, 0x19, 0xf0, 0xd2, 0xe2, 0xc5, 0xaf, 0x33, + 0xae, 0x24, 0x37, 0x85, 0xb3, 0x25, 0xcd, 0xab, 0x95, 0x40, 0x4f, 0xc7, 0xae, + 0xd7, 0x05, 0x25, 0xcd, 0xdb, 0x41, 0x87, 0x2c, 0xfc, 0xc2, 0x14, 0xb1, 0x32, + 0x32, 0xed, 0xc7, 0x86, 0x09, 0x75, 0x3d, 0xbf, 0xf9, 0x30, 0xeb, 0x0d, 0xc1, + 0x56, 0x61, 0x2b, 0x9c, 0xb4, 0x34, 0xbc, 0x4b, 0x69, 0x33, 0x92, 0xde, 0xb8, + 0x7c, 0x53, 0x04, 0x35, 0x31, 0x2e, 0xdc, 0xed, 0xc6, 0xa9, 0x61, 0x13, 0x33, + 0x38, 0xd7, 0x86, 0xc4, 0xa3, 0xe1, 0x03, 0xf6, 0x01, 0x10, 0xa1, 0x6b, 0x13, + 0x37, 0x12, 0x97, 0x04, 0xbf, 0x47, 0x54, 0xff, 0x6b, 0xa9, 0xfb, 0xe6, 0x59, + 0x51, 0xe6, 0x10, 0x62, 0x0f, 0x71, 0xcd, 0xa8, 0xfc, 0x87, 0x76, 0x25, 0xf2, + 0xc5, 0xbb, 0x04, 0xcb, 0xe1, 0x22, 0x8b, 0x1e, 0x88, 0x6f, 0x40, 0x50, 0xaf, + 0xd8, 0xfe, 0x94, 0xe9, 0x7d, 0x2e, 0x9e, 0x85, 0xc6, 0xbb, 0x74, 0x8c, 0x00, + 0x42, 0xd3, 0x24, 0x9a, 0xbb, 0x13, 0x42, 0xbb, 0x0e, 0xeb, 0xf6, 0x20, 0x58, + 0xbf, 0x3d, 0xe0, 0x80, 0xd9, 0x46, 0x11, 0xa3, 0x75, 0x09, 0x15, 0xb5, 0xdc, + 0x6c, 0x0b, 0x38, 0x99, 0xd4, 0x12, 0x22, 0xba, 0xce, 0x76, 0x0e, 0xe9, 0xc8, + 0x81, 0x8d, 0xed, 0x59, 0x9e, 0x34, 0xc5, 0x6d, 0x73, 0x72, 0xaf, 0x1e, 0xb8, + 0x68, 0x52, 0xf2, 0xa7, 0x32, 0x10, 0x4b, 0xdb, 0x75, 0x07, 0x39, 0xde, 0x6c, + 0x2c, 0x6e, 0x0f, 0x9e, 0xb7, 0xcb, 0x17, 0xf1, 0x94, 0x2b, 0xfc, 0x9f, 0x4f, + 0xd6, 0xeb, 0xb6, 0xb4, 0xcd, 0xd4, 0xda, 0x2b, 0xca, 0x26, 0xfa, 0xc4, 0x57, + 0x8e, 0x9f, 0x54, 0x34, 0x05, 0xac, 0xc7, 0xd8, 0x6f, 0xf5, 0x91, 0x58, 0xbd, + 0x0c, 0xba, 0x3a, 0xef, 0x6f, 0x4a, 0x84, 0x72, 0xd1, 0x44, 0xd9, 0x9f, 0x8b, + 0x8d, 0x1d, 0xed, 0xaa, 0x90, 0x77, 0xd4, 0xf0, 0x1d, 0x4b, 0xb2, 0x7b, 0xbe, + 0x31, 0xd8, 0x8f, 0xbe, 0xfa, 0xc3, 0xdc, 0xd4, 0x79, 0x75, 0x63, 0xa2, 0x6b, + 0x1d, 0x61, 0xfc, 0xd9, 0xa4, 0x64, 0xab, 0x21, 0xed, 0x55, 0x0f, 0xe6, 0xfa, + 0x09, 0x69, 0x5b, 0xa0, 0xb2, 0xf1, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x64, 0x68, + 0xcc, 0x6e, 0x20, 0xa6, 0x6f, 0x82, 0x6e, 0x3d, 0x14, 0xc5, 0x00, 0x6f, 0x05, + 0x63, 0x88, 0x7f, 0x5e, 0x12, 0x89, 0xbe, 0x1b, 0x20, 0x04, 0xca, 0xca, 0x8d, + 0x3f, 0x34, 0xd6, 0xe8, 0x4b, 0xf5, 0x9c, 0x1e, 0x04, 0x61, 0x9a, 0x7c, 0x23, + 0xa9, 0x96, 0x94, 0x1d, 0x88, 0x9e, 0x46, 0x22, 0xa9, 0xb9, 0xb1, 0xd5, 0x9d, + 0x5e, 0x31, 0x90, 0x94, 0x31, 0x8c, 0xd4, 0x05, 0xba, 0x27, 0xb7, 0xe2, 0xc0, + 0x84, 0x76, 0x2d, 0x31, 0x45, 0x3e, 0xc4, 0x54, 0x9a, 0x4d, 0x97, 0x72, 0x9d, + 0x03, 0x34, 0x60, 0xfc, 0xf8, 0x9d, 0x64, 0x94, 0xf2, 0xff, 0xd7, 0x89, 0xe9, + 0x80, 0x82, 0xea, 0x5c, 0xe9, 0x53, 0x4b, 0x3a, 0xcd, 0x60, 0xfe, 0x49, 0xe3, + 0x7e, 0x4f, 0x66, 0x69, 0x31, 0x67, 0x73, 0x19, 0xed, 0x89, 0xf8, 0x55, 0x88, + 0x74, 0x1b, 0x31, 0x28, 0x90, 0x1a, 0x93, 0xbd, 0x78, 0xe4, 0xbe, 0x02, 0x25, + 0xa9, 0xe2, 0x69, 0x2c, 0x77, 0xc9, 0x69, 0xed, 0x01, 0x76, 0xbd, 0xf9, 0x55, + 0x59, 0x48, 0xcb, 0xd5, 0xa3, 0x32, 0xd0, 0x45, 0xde, 0x6b, 0xa6, 0xbf, 0x44, + 0x90, 0xad, 0xfe, 0x74, 0x44, 0xcd, 0x46, 0x7a, 0x09, 0x07, 0x54, 0x17, 0xfc, + 0xc0, 0x06, 0x2e, 0x49, 0xf0, 0x08, 0xc5, 0x1a, 0xd4, 0x22, 0x74, 0x39, 0xc1, + 0xb4, 0x47, 0x6c, 0xcd, 0x8e, 0x97, 0x86, 0x2d, 0xab, 0x7b, 0xe1, 0xe8, 0xd3, + 0x99, 0xc0, 0x5e, 0xf2, 0x7c, 0x6e, 0x22, 0xee, 0x27, 0x3e, 0x15, 0x78, 0x6e, + 0x39, 0x4c, 0x8f, 0x1b, 0xe3, 0x16, 0x82, 0xa3, 0x01, 0x47, 0x96, 0x3a, 0xc8, + 0xda, 0x8d, 0x41, 0xd8, 0x04, 0x25, 0x84, 0x26, 0xa3, 0xf7, 0x02, 0x89, 0xb8, + 0xad, 0x19, 0xd8, 0xde, 0x13, 0xbe, 0x4e, 0xeb, 0xe3, 0xbd, 0x4c, 0x8a, 0x6f, + 0x55, 0xd6, 0xe0, 0xc3, 0x73, 0xd4, 0x56, 0x85, 0x18, 0x79, 0xf5, 0xfb, 0xc2, + 0x82, 0xdb, 0x9e, 0x13, 0x48, 0x06, 0xbf, 0xf7, 0x1e, 0x11, 0xbc, 0x33, 0xab, + 0x75, 0xdd, 0x6c, 0xa0, 0x67, 0xfb, 0x73, 0xa0, 0x43, 0xb6, 0x46, 0xa7, 0x03, + 0x39, 0xca, 0xb4, 0x92, 0x83, 0x86, 0x78, 0x6d, 0x2f, 0x24, 0x14, 0x1e, 0xe1, + 0x20, 0xfd, 0xc3, 0x4d, 0x67, 0x64, 0xea, 0xfc, 0x66, 0x88, 0x0e, 0xe0, 0x20, + 0x4f, 0x53, 0xcc, 0x11, 0x67, 0xed, 0x02, 0xb4, 0x3a, 0x52, 0xde, 0xa3, 0xca, + 0x7c, 0xff, 0x8e, 0xf3, 0x5c, 0xd8, 0xe6, 0xd7, 0xc1, 0x11, 0xa6, 0x8e, 0xf4, + 0x4b, 0xcd, 0x0c, 0x15, 0x13, 0xad, 0x47, 0xca, 0x61, 0xc6, 0x59, 0xcc, 0x5d, + 0x0a, 0x5b, 0x44, 0x0f, 0x6b, 0x9f, 0x59, 0xaf, 0xf6, 0x68, 0x79, 0xbb, 0x66, + 0x88, 0xfd, 0x28, 0x59, 0x36, 0x2b, 0x18, 0x2f, 0x20, 0x7b, 0x31, 0x75, 0x96, + 0x1f, 0x64, 0x11, 0xa4, 0x93, 0xbf, 0xfd, 0x04, 0x8e, 0x7d, 0x0d, 0x87, 0xd8, + 0x2f, 0xe6, 0xf9, 0x90, 0xa2, 0xb0, 0xa2, 0x5f, 0x5a, 0xa0, 0x11, 0x1a, 0x6e, + 0x68, 0xf3, 0x7b, 0xf6, 0xf3, 0xac, 0x2d, 0x26, 0xb8, 0x46, 0x86, 0xe5, 0x69, + 0x03, 0x8d, 0x99, 0xc1, 0x38, 0x35, 0x97, 0xfa, 0xd8, 0x11, 0x93, 0xc4, 0xc1, + 0xb1, 0x6e, 0x6a, 0x90, 0xe2, 0xd5, 0x07, 0xcd, 0xfe, 0x6f, 0xbd, 0xaa, 0x86, + 0x16, 0x3e, 0x9c, 0xf5, 0xde, 0x31, 0x00, 0x03, 0xca, 0x7e, 0x8d, 0xa0, 0x47, + 0xb0, 0x90, 0xdb, 0x9f, 0x37, 0x95, 0x2f, 0xbf, 0xee, 0x76, 0xaf, 0x61, 0x66, + 0x81, 0x90, 0xbd, 0x52, 0xed, 0x49, 0x0e, 0x67, 0x7b, 0x51, 0x5d, 0x01, 0x43, + 0x84, 0x03, 0x07, 0x21, 0x9c, 0x7c, 0x0e, 0xe7, 0xfc, 0x7b, 0xfc, 0x79, 0xf3, + 0x25, 0x64, 0x4e, 0x4d, 0xf4, 0xc0, 0xd7, 0xdb, 0x08, 0xe9, 0xf0, 0xbd, 0x02, + 0x49, 0x43, 0xc7, 0x05, 0xab, 0xff, 0x89, 0x94, 0x03, 0xa6, 0x05, 0xcf, 0xbc, + 0x7e, 0xd7, 0x46, 0xa7, 0xd3, 0xf7, 0xc3, 0x7d, 0x9e, 0x8b, 0xdc, 0x43, 0x3b, + 0x7d, 0x79, 0xe0, 0x8a, 0x12, 0xf7, 0x38, 0xa8, 0xf0, 0xdb, 0xdd, 0xfe, 0xf2, + 0xf2, 0x65, 0x02, 0xf3, 0xe4, 0x7d, 0x1b, 0x0f, 0xd1, 0x1e, 0x6a, 0x13, 0x31, + 0x1f, 0xb7, 0x99, 0xc7, 0x9c, 0x64, 0x1d, 0x9d, 0xa4, 0x3b, 0x33, 0xe7, 0xad, + 0x01, 0x2e, 0x28, 0x25, 0x53, 0x98, 0x78, 0x92, 0x62, 0x27, 0x5f, 0x11, 0x75, + 0xbe, 0x84, 0x62, 0xc0, 0x14, 0x91, 0xc4, 0xd8, 0x42, 0x40, 0x6d, 0x0e, 0xc4, + 0x28, 0x2c, 0x95, 0x26, 0x17, 0x4a, 0x09, 0x87, 0x8f, 0xe8, 0xfd, 0xde, 0x33, + 0xa2, 0x96, 0x04, 0xe5, 0xe5, 0xe7, 0xb2, 0xa0, 0x25, 0xd6, 0x65, 0x0b, 0x97, + 0xdb, 0xb5, 0x2b, 0xef, 0xb5, 0x9b, 0x1d, 0x30, 0xa5, 0x74, 0x33, 0xb0, 0xa3, + 0x51, 0x47, 0x44, 0x44, 0x09, 0x9d, 0xaa, 0x37, 0x10, 0x46, 0x61, 0x32, 0x60, + 0xcf, 0x33, 0x54, 0xcf, 0xcd, 0xad, 0xa6, 0x63, 0xec, 0xe8, 0x24, 0xff, 0xd7, + 0xe4, 0x43, 0x93, 0x88, 0x6a, 0x86, 0x16, 0x5d, 0xdd, 0xdf, 0x2b, 0x4c, 0x41, + 0x77, 0x35, 0x54, 0xc8, 0x69, 0x95, 0x26, 0x94, 0x08, 0xb1, 0x1e, 0x67, 0x37, + 0xa4, 0xc4, 0x47, 0x58, 0x6f, 0x69, 0x17, 0x34, 0x46, 0xd8, 0xe4, 0x8b, 0xf8, + 0x4c, 0xbc, 0x00, 0x0a, 0x80, 0x78, 0x99, 0x97, 0x3e, 0xb9, 0x3c, 0x5e, 0x81, + 0x9a, 0xad, 0x66, 0x94, 0x13, 0xf8, 0x38, 0x79, 0x33, 0xad, 0x15, 0x84, 0xaa, + 0x35, 0xe4, 0x3f, 0x4e, 0xcd, 0x1e, 0x2d, 0x04, 0x07, 0xc0, 0xb1, 0xb8, 0x99, + 0x20, 0xff, 0xdf, 0xdb, 0x9b, 0xea, 0x51, 0xac, 0x95, 0xb5, 0x57, 0xaf, 0x71, + 0xb8, 0x9f, 0x90, 0x3f, 0x5d, 0x98, 0x48, 0xf1, 0x4f, 0xcb, 0xeb, 0x18, 0x37, + 0x57, 0x0f, 0x54, 0x4d, 0x63, 0x59, 0xeb, 0x23, 0xfa, 0xf3, 0x8a, 0x08, 0x22, + 0xda, 0x36, 0xce, 0x42, 0x6c, 0x4a, 0x2f, 0xbe, 0xff, 0xeb, 0x0a, 0x8a, 0x2e, + 0x29, 0x7a, 0x9d, 0x19, 0xba, 0x15, 0x02, 0x45, 0x90, 0xe3, 0x32, 0x9d, 0x9f, + 0xa9, 0x26, 0x1f, 0x99, 0x38, 0xa4, 0x03, 0x2d, 0xd3, 0x46, 0x06, 0xc9, 0xcf, + 0x9f, 0x3d, 0xd3, 0x3e, 0x57, 0x6f, 0x05, 0xcd, 0x1d, 0xd6, 0x81, 0x1c, 0x62, + 0x98, 0x75, 0x7d, 0x77, 0xd9, 0xe8, 0x10, 0xab, 0xdb, 0x22, 0x6a, 0xfc, 0xaa, + 0x43, 0x46, 0xa6, 0x56, 0x0f, 0x89, 0x32, 0xb3, 0x18, 0x1f, 0xd3, 0x55, 0xd5, + 0xd3, 0x91, 0x97, 0x61, 0x83, 0xf8, 0xd9, 0x93, 0x88, 0x83, 0x96, 0x32, 0xd6, + 0x35, 0x4f, 0x66, 0x6d, 0x09, 0xd3, 0xe5, 0x62, 0x9e, 0xa1, 0x97, 0x37, 0x38, + 0x86, 0x13, 0xd3, 0x8a, 0x34, 0xfd, 0x0f, 0x6e, 0x50, 0xee, 0x5a, 0x0c, 0xc9, + 0x67, 0x71, 0x77, 0xf5, 0x00, 0x28, 0xc1, 0x41, 0x37, 0x81, 0x87, 0xbd, 0x28, + 0x19, 0x40, 0x3f, 0xc5, 0x34, 0xf8, 0x00, 0x76, 0xe9, 0x38, 0x0c, 0xb4, 0x96, + 0x4d, 0x3b, 0x6b, 0x45, 0x81, 0x9d, 0x3b, 0x8e, 0x9c, 0xaf, 0x54, 0xf0, 0x51, + 0x85, 0x2d, 0x67, 0x1b, 0xf8, 0xc1, 0xff, 0xde, 0x2d, 0x15, 0x10, 0x75, 0x64, + 0x18, 0xcb, 0x48, 0x10, 0x93, 0x6a, 0xa5, 0x7e, 0x69, 0x65, 0xd6, 0xfb, 0x65, + 0x6a, 0x76, 0x0b, 0x7f, 0x19, 0xad, 0xf9, 0x6c, 0x17, 0x34, 0x88, 0x55, 0x21, + 0x93, 0xb1, 0x47, 0xee, 0x58, 0x85, 0x80, 0x33, 0xda, 0xc7, 0xcd, 0x0e, 0xb2, + 0x04, 0xc0, 0x64, 0x90, 0xbb, 0xde, 0xdf, 0x5f, 0x75, 0x71, 0xac, 0xb2, 0xeb, + 0xe7, 0x6a, 0xce, 0xf3, 0xf2, 0xa0, 0x1e, 0xe9, 0x87, 0x48, 0x6d, 0xfe, 0x6c, + 0x3f, 0x0a, 0x5e, 0x23, 0x4c, 0x12, 0x72, 0x58, 0xf9, 0x7a, 0x28, 0xfb, 0x5d, + 0x16, 0x4a, 0x81, 0x76, 0xbe, 0x94, 0x6b, 0x80, 0x97, 0xd0, 0xe3, 0x17, 0x28, + 0x7f, 0x33, 0xbf, 0x9c, 0x16, 0xf9, 0xa5, 0x45, 0x40, 0x9c, 0xe2, 0x9b, 0x1f, + 0x42, 0x73, 0x72, 0x5f, 0xc0, 0xdf, 0x02, 0xa0, 0x4e, 0xba, 0xe1, 0x78, 0xb3, + 0x41, 0x4f, 0xb0, 0xa8, 0x2d, 0x50, 0xde, 0xb0, 0x9f, 0xcf, 0x4e, 0x6e, 0xe9, + 0xd1, 0x80, 0xff, 0x4f, 0x56, 0xff, 0x3b, 0xc1, 0xd3, 0x60, 0x1f, 0xc2, 0xdc, + 0x90, 0xd8, 0x14, 0xc3, 0x25, 0x6f, 0x49, 0x67, 0xd3, 0xa8, 0xd6, 0x4c, 0x83, + 0xfe, 0xa3, 0x39, 0xc5, 0x1f, 0x5a, 0x8e, 0x58, 0x01, 0xfb, 0xb9, 0x78, 0x35, + 0x58, 0x1b, 0x60, 0x24, 0x65, 0xde, 0xe0, 0x4b, 0x59, 0x22, 0xc2, 0x76, 0x1b, + 0x54, 0x24, 0x5b, 0xec, 0x0c, 0x9e, 0xef, 0x2d, 0xb9, 0x7d, 0x22, 0xb2, 0xb3, + 0x55, 0x6c, 0xc9, 0x69, 0xfb, 0xb1, 0x3d, 0x06, 0x50, 0x97, 0x65, 0xa5, 0x2b, + 0x3f, 0xac, 0x54, 0xb9, 0x3f, 0x42, 0x1b, 0xf0, 0x8e, 0x18, 0xd5, 0x2d, 0xdd, + 0x52, 0xcc, 0x1c, 0x8c, 0xa8, 0xad, 0xfa, 0xcc, 0xab, 0x7e, 0x5c, 0xc2, 0xf4, + 0x57, 0x3f, 0xbb, 0xf8, 0x23, 0x9b, 0xb0, 0xb8, 0xae, 0xdb, 0xf8, 0xda, 0xd1, + 0x62, 0x82, 0xda, 0x5c, 0x91, 0x25, 0xdb, 0xa1, 0xc0, 0x59, 0xd0, 0xdf, 0x8a, + 0xbf, 0x62, 0x10, 0x78, 0xf0, 0x2d, 0x6c, 0x4b, 0xc8, 0x6d, 0x40, 0x84, 0x5a, + 0xc1, 0xd5, 0x97, 0x10, 0xc4, 0x5f, 0x07, 0xd5, 0x85, 0xeb, 0x48, 0xb3, 0x2f, + 0xc0, 0x16, 0x7b, 0xa2, 0x56, 0xe7, 0x3c, 0xa3, 0xb9, 0x31, 0x1c, 0x62, 0xd1, + 0x09, 0x49, 0x79, 0x57, 0xd8, 0xdb, 0xe1, 0x0a, 0xa3, 0xe8, 0x66, 0xb4, 0x0c, + 0x0b, 0xaa, 0x2b, 0xc4, 0x92, 0xc1, 0x9a, 0xd1, 0xe6, 0x37, 0x2d, 0x96, 0x22, + 0xbf, 0x16, 0x3f, 0xbf, 0xfe, 0xae, 0xee, 0x79, 0x6a, 0x3c, 0xd9, 0xb6, 0xfb, + 0xbf, 0xa4, 0xd7, 0x92, 0xf3, 0x4d, 0x7f, 0xd6, 0xe7, 0x63, 0xcd, 0x58, 0x59, + 0xdd, 0x26, 0x83, 0x3d, 0x21, 0xd9, 0xbc, 0x54, 0x52, 0xbd, 0x19, 0x51, 0x5d, + 0xff, 0x9f, 0x49, 0x95, 0xb3, 0x5b, 0xc0, 0xc1, 0xf8, 0x76, 0xe6, 0xad, 0x11, + 0xf2, 0x45, 0x2d, 0xc9, 0xae, 0x85, 0xae, 0xc0, 0x1f, 0xc5, 0x6f, 0x8c, 0xbf, + 0xda, 0x75, 0xa7, 0x72, 0x7b, 0x75, 0xeb, 0xbd, 0x6b, 0xbf, 0xfb, 0x43, 0xb6, + 0x3a, 0x3b, 0x1b, 0x67, 0x1e, 0x40, 0xfe, 0xb0, 0xdb, 0x00, 0x29, 0x74, 0xa3, + 0xc3, 0xb1, 0xa7, 0x88, 0x56, 0x72, 0x31, 0xbf, 0x63, 0x99, 0xff, 0x89, 0x23, + 0x69, 0x81, 0x14, 0x9d, 0x42, 0x38, 0x02, 0xd2, 0x34, 0x1a, 0x3b, 0xed, 0xb9, + 0xdd, 0xcb, 0xac, 0x1f, 0xe7, 0xb6, 0x43, 0x5e, 0x14, 0x79, 0xc7, 0x2e, 0x70, + 0x89, 0xd0, 0x29, 0xe7, 0xfb, 0xba, 0xf3, 0xcf, 0x37, 0xe9, 0xb9, 0xa6, 0xb7, + 0x76, 0x79, 0x1e, 0x4c, 0x5e, 0x6f, 0xda, 0x57, 0xe8, 0xd5, 0xf1, 0x4c, 0x8c, + 0x35, 0xa2, 0xd2, 0x70, 0x84, 0x6b, 0x9d, 0xbe, 0x00, 0x5c, 0xda, 0x16, 0xaf, + 0x44, 0x08, 0xf3, 0xab, 0x06, 0xa9, 0x16, 0xee, 0xeb, 0x9c, 0x95, 0x94, 0xb7, + 0x04, 0x24, 0xa4, 0xc1, 0xd1, 0x71, 0x29, 0x5b, 0x67, 0x63, 0xb2, 0x2f, 0x47, + 0xf8, 0x0b, 0x53, 0xcc, 0xbb, 0x90, 0x4b, 0xd6, 0x8f, 0xd6, 0x5f, 0xbd, 0x3f, + 0xbd, 0xea, 0x10, 0x35, 0xe9, 0x8c, 0x21, 0xa7, 0xdb, 0xc9, 0x1a, 0x9b, 0x5b, + 0xc7, 0x69, 0x0f, 0x05, 0xec, 0x31, 0x7c, 0x97, 0xf8, 0x76, 0x4e, 0xb4, 0x8e, + 0x91, 0x1d, 0x42, 0x8e, 0xc8, 0xd8, 0x61, 0xb7, 0x08, 0xe8, 0x29, 0x8a, 0xcb, + 0x62, 0x15, 0x51, 0x45, 0x15, 0x5a, 0xe9, 0x5f, 0x0a, 0x1d, 0x15, 0x01, 0x03, + 0x47, 0x53, 0x14, 0x6e, 0x22, 0xd0, 0x5f, 0x58, 0x6d, 0x7f, 0x6b, 0x4f, 0xe1, + 0x2d, 0xad, 0x9a, 0x17, 0xf5, 0xdb, 0x70, 0xb1, 0xdb, 0x96, 0xb8, 0xd9, 0xa8, + 0x3e, 0xda, 0xdc, 0x96, 0x6c, 0x8a, 0x54, 0x66, 0xb6, 0x1f, 0xc9, 0x98, 0xc3, + 0x1f, 0x10, 0x70, 0xd9, 0xa5, 0xc9, 0xa6, 0xd2, 0x68, 0xd3, 0x04, 0xfe, 0x6b, + 0x8f, 0xd3, 0xb4, 0x01, 0x03, 0x48, 0x61, 0x1a, 0xbd, 0xcb, 0xd4, 0x9f, 0xe4, + 0xf8, 0x5b, 0x62, 0x3c, 0x78, 0x28, 0xc7, 0x13, 0x82, 0xe1, 0x03, 0x4e, 0xa6, + 0x7b, 0xc8, 0xae, 0x97, 0x40, 0x4b, 0x0c, 0x50, 0xb2, 0xa0, 0x4f, 0x55, 0x9e, + 0x49, 0x95, 0x0a, 0xfc, 0xb0, 0xef, 0x46, 0x2a, 0x2a, 0xe0, 0x24, 0xb0, 0xf0, + 0x22, 0x4d, 0xfd, 0x73, 0x68, 0x4b, 0x88, 0xc7, 0xfb, 0xe9, 0x2d, 0x02, 0xb6, + 0x8f, 0x75, 0x9c, 0x47, 0x52, 0x66, 0x3c, 0xd7, 0xb9, 0x7a, 0x14, 0x94, 0x36, + 0x49, 0x30, 0x55, 0x21, 0x32, 0x6b, 0xde, 0x08, 0x56, 0x30, 0x86, 0x46, 0x29, + 0x29, 0x1b, 0xae, 0x25, 0xff, 0x88, 0x22, 0xa1, 0x4c, 0x4b, 0x66, 0x6a, 0x92, + 0x59, 0xad, 0x0d, 0xc4, 0x2a, 0x82, 0x90, 0xac, 0x7b, 0xc7, 0xf5, 0x3a, 0x16, + 0xf3, 0x79, 0xf7, 0x58, 0xe5, 0xde, 0x75, 0x0f, 0x04, 0xfd, 0x7c, 0xad, 0x47, + 0x70, 0x1c, 0x85, 0x97, 0xf9, 0x78, 0x88, 0xbe, 0xa6, 0xfa, 0x0b, 0xf2, 0x99, + 0x99, 0x56, 0xfb, 0xfd, 0x0e, 0xe6, 0x8e, 0xc3, 0x6e, 0x46, 0x88, 0x80, 0x9a, + 0xe2, 0x31, 0xeb, 0x8b, 0xc4, 0x36, 0x9f, 0x5f, 0xe1, 0x57, 0x3f, 0x57, 0xe0, + 0x99, 0xd9, 0xc0, 0x99, 0x01, 0xbf, 0x39, 0xca, 0xac, 0x48, 0xdc, 0x11, 0x95, + 0x6a, 0x8a, 0xe9, 0x05, 0xea, 0xd8, 0x69, 0x54, 0x54, 0x7c, 0x44, 0x8a, 0xe4, + 0x3d, 0x31, 0x5e, 0x66, 0x9c, 0x42, 0x42, 0xda, 0x56, 0x59, 0x38, 0xf4, 0x17, + 0xbf, 0x43, 0xce, 0x7b, 0x2b, 0x30, 0xb1, 0xcd, 0x40, 0x18, 0x38, 0x8e, 0x1a, + 0x91, 0x0f, 0x0f, 0xc4, 0x1f, 0xb0, 0x87, 0x7a, 0x59, 0x25, 0xe4, 0x66, 0x81, + 0x9d, 0x37, 0x5b, 0x0a, 0x91, 0x2d, 0x4f, 0xe8, 0x43, 0xb7, 0x6e, 0xf6, 0xf2, + 0x23, 0xf0, 0xf7, 0xc8, 0x94, 0xf3, 0x8f, 0x7a, 0xb7, 0x80, 0xdf, 0xd7, 0x5f, + 0x66, 0x9c, 0x8c, 0x06, 0xcf, 0xfa, 0x43, 0xeb, 0x47, 0x56, 0x5a, 0x50, 0xe3, + 0xb1, 0xfa, 0x45, 0xad, 0x61, 0xce, 0x9a, 0x1c, 0x47, 0x27, 0xb7, 0xaa, 0xa5, + 0x35, 0x62, 0xf5, 0x23, 0xe7, 0x39, 0x52, + ], + script_code: Script(vec![0x53]), + transparent_input: Some(1), + hash_type: 3, + amount: 365293780364847, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x23, 0x65, 0x2e, 0x76, 0xcb, 0x13, 0xb8, 0x5a, 0x0e, 0x33, 0x63, 0xbb, 0x5f, + 0xca, 0x06, 0x1f, 0xa7, 0x91, 0xc4, 0x0c, 0x53, 0x3e, 0xcc, 0xee, 0x89, 0x93, + 0x64, 0xe6, 0xe6, 0x0b, 0xb4, 0xf7, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x99, 0xa6, 0x9f, 0xdf, + 0x1c, 0x5a, 0xc7, 0x73, 0x21, 0x46, 0xee, 0x5e, 0x1d, 0x6b, 0x6c, 0xa9, 0xb9, + 0x18, 0x0f, 0x96, 0x4c, 0xc9, 0xd0, 0x87, 0x8a, 0xe1, 0x37, 0x35, 0x24, 0xd7, + 0xd5, 0x10, 0xe5, 0x82, 0x27, 0xdf, 0x09, 0x51, 0x53, 0x63, 0x6a, 0x00, 0x6a, + 0xac, 0x51, 0x6a, 0xb0, 0xf1, 0x85, 0x6e, 0x28, 0xd5, 0xc8, 0xaf, 0xb0, 0x95, + 0xef, 0x61, 0x84, 0xfe, 0xd6, 0x51, 0x58, 0x90, 0x22, 0xee, 0xae, 0xa4, 0xc0, + 0xce, 0x1f, 0xa6, 0xf0, 0x85, 0x09, 0x2b, 0x04, 0x97, 0x94, 0x89, 0x17, 0x2b, + 0x3e, 0xf8, 0x19, 0x4a, 0x01, 0x63, 0xf5, 0x72, 0x4d, 0x6b, 0x02, 0xde, 0xe1, + 0x36, 0xf3, 0xa9, 0xaa, 0x02, 0x00, 0x03, 0x52, 0x52, 0xac, 0x17, 0xb7, 0x3f, + 0x8d, 0x38, 0x3e, 0x00, 0x00, 0x06, 0xac, 0x63, 0x00, 0x53, 0xac, 0x51, 0x04, + 0xb4, 0x75, 0x56, 0xaf, 0x73, 0xb6, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x32, + 0x1d, 0x14, 0x60, 0x71, 0x78, 0x9d, 0x23, 0x35, 0x93, 0x4a, 0x68, 0x06, 0x14, + 0xe8, 0x35, 0x62, 0xf8, 0x2d, 0xfd, 0x40, 0x5b, 0x54, 0xa4, 0x5e, 0xb3, 0x2c, + 0x16, 0x54, 0x48, 0xd4, 0xd5, 0xd6, 0x1c, 0xa2, 0x85, 0x95, 0x85, 0x36, 0x9f, + 0x53, 0xf1, 0xa1, 0x37, 0xe9, 0xe8, 0x2b, 0x67, 0xb8, 0xfd, 0xaf, 0x01, 0xbd, + 0xa5, 0x4a, 0x31, 0x73, 0x11, 0x89, 0x6a, 0xe1, 0x02, 0x80, 0xa0, 0x32, 0x44, + 0x0c, 0x42, 0x0a, 0x42, 0x1e, 0x94, 0x4d, 0x1e, 0x95, 0x2b, 0x70, 0xd5, 0x82, + 0x6c, 0xd3, 0xb0, 0x8b, 0x7d, 0xb9, 0x63, 0x0f, 0xe4, 0xfd, 0x5f, 0x22, 0x12, + 0x5d, 0xe8, 0x40, 0xfc, 0xc4, 0x0b, 0x98, 0x03, 0x8a, 0xf1, 0x1d, 0x55, 0xbe, + 0x25, 0x43, 0x25, 0x97, 0xb4, 0xb6, 0x5b, 0x9e, 0xc1, 0xc7, 0xa8, 0xbb, 0xfd, + 0x05, 0x2c, 0xbf, 0x7e, 0x1c, 0x17, 0x85, 0x31, 0x49, 0x34, 0xb2, 0x62, 0xd5, + 0x85, 0x37, 0x54, 0xf1, 0xf1, 0x77, 0x71, 0xcf, 0xb7, 0x50, 0x30, 0x72, 0x65, + 0x57, 0x53, 0xfa, 0x3f, 0x54, 0xec, 0xc5, 0x87, 0xe9, 0xf8, 0x3b, 0x58, 0x19, + 0x16, 0x09, 0x2d, 0xf2, 0x6e, 0x63, 0xe1, 0x89, 0x94, 0xcb, 0x0d, 0xb9, 0x1a, + 0x0b, 0xbd, 0xc7, 0xb6, 0x11, 0x9b, 0x32, 0x22, 0x2a, 0xdf, 0x5e, 0x61, 0xd8, + 0xd8, 0xae, 0x89, 0xda, 0xe4, 0x95, 0x4b, 0x54, 0x81, 0x3b, 0xb3, 0x3f, 0x08, + 0xd5, 0x62, 0xba, 0x51, 0x3f, 0xee, 0x1b, 0x09, 0xc0, 0xfc, 0xd5, 0x16, 0x05, + 0x54, 0x19, 0x47, 0x4d, 0xd7, 0xfd, 0xa0, 0x38, 0xa8, 0x9c, 0x84, 0xea, 0x7b, + 0x94, 0x68, 0x28, 0x7f, 0x0e, 0xb0, 0xc1, 0x0c, 0x4b, 0x13, 0x25, 0x20, 0x19, + 0x4d, 0x3d, 0x8d, 0x53, 0x51, 0xfc, 0x10, 0xd0, 0x9c, 0x15, 0xc8, 0xcc, 0x10, + 0x1a, 0xa1, 0x66, 0x3b, 0xbf, 0x17, 0xb8, 0x41, 0x11, 0xf3, 0x8b, 0xb4, 0x39, + 0xf0, 0x73, 0x53, 0xbd, 0xea, 0x35, 0x96, 0xd1, 0x5e, 0x71, 0x3e, 0x1e, 0x2e, + 0x7d, 0x3f, 0x1c, 0x38, 0x31, 0x35, 0xb4, 0x7f, 0xa7, 0xf8, 0x1f, 0x46, 0xdf, + 0x02, 0x90, 0x2a, 0x40, 0x46, 0x99, 0xec, 0x91, 0x2f, 0x56, 0x56, 0xc3, 0x5b, + 0x85, 0x76, 0x3e, 0x4d, 0xe5, 0x83, 0xae, 0xca, 0xa1, 0xdf, 0xd5, 0xd2, 0x67, + 0x7d, 0x9c, 0x8f, 0xfe, 0xe8, 0x77, 0xf6, 0x03, 0x40, 0xa5, 0xca, 0x0d, 0x67, + 0xf6, 0xe5, 0x54, 0x12, 0x47, 0x39, 0xf8, 0x05, 0xaf, 0x87, 0x6a, 0xee, 0xde, + 0x53, 0xaa, 0x8b, 0x0f, 0x8e, 0x56, 0x04, 0xa7, 0x3c, 0x30, 0xcb, 0xd0, 0x9d, + 0xad, 0x0a, 0x3d, 0x6f, 0x8a, 0x5d, 0xcc, 0x40, 0xde, 0xf4, 0x07, 0x97, 0x34, + 0x21, 0x13, 0xba, 0x20, 0x6f, 0xae, 0x8e, 0xbe, 0x4f, 0x3b, 0xc3, 0xca, 0xf6, + 0x92, 0x59, 0xe4, 0x62, 0xef, 0xf9, 0xba, 0x8b, 0x3f, 0x4b, 0xfa, 0xa1, 0x30, + 0x0c, 0x26, 0x92, 0x5a, 0x87, 0x29, 0xcd, 0x32, 0x91, 0x5b, 0xfc, 0x96, 0x60, + 0x86, 0xf0, 0xd5, 0x56, 0x0b, 0xbe, 0x32, 0xa5, 0x98, 0xc2, 0x2a, 0xdf, 0xb4, + 0x8c, 0x03, 0x72, 0xba, 0x5d, 0x42, 0x87, 0xc0, 0xce, 0xfb, 0xac, 0xfd, 0x8c, + 0xe1, 0x95, 0xb4, 0x96, 0x3c, 0x34, 0xa9, 0x4b, 0xba, 0x7a, 0x17, 0x5d, 0xae, + 0x4b, 0xbe, 0x3e, 0xf4, 0x86, 0x3d, 0x53, 0x70, 0x03, 0x15, 0x09, 0x0f, 0x47, + 0xa0, 0x68, 0xe2, 0x27, 0x43, 0x3f, 0x9e, 0x49, 0xd3, 0xaa, 0x09, 0xe3, 0x56, + 0xd8, 0xd6, 0x6d, 0x0c, 0x01, 0x21, 0xe9, 0x1a, 0x3c, 0x4a, 0xa3, 0xf2, 0x7f, + 0xa1, 0xb6, 0x03, 0x96, 0xe2, 0xb4, 0x1d, 0xb9, 0x08, 0xfd, 0xab, 0x8b, 0x18, + 0xcc, 0x73, 0x04, 0xe9, 0x4e, 0x97, 0x05, 0x68, 0xf9, 0x42, 0x1c, 0x0d, 0xbb, + 0xba, 0xf8, 0x45, 0x98, 0xd9, 0x72, 0xb0, 0x53, 0x4f, 0x02, 0xa5, 0xe5, 0x26, + 0x70, 0x43, 0x6a, 0xaa, 0x77, 0x6e, 0xd2, 0x48, 0x2a, 0xd7, 0x03, 0x43, 0x02, + 0x01, 0xe5, 0x34, 0x43, 0xc3, 0x6d, 0xcf, 0xd3, 0x4a, 0x0c, 0xb6, 0x63, 0x78, + 0x76, 0x10, 0x5e, 0x03, 0xbf, 0x3b, 0xd5, 0x8e, 0xc1, 0x48, 0xcb, 0x64, 0x97, + 0x0e, 0x32, 0x23, 0xa9, 0x1f, 0x71, 0xdf, 0xcf, 0xd5, 0xa0, 0x4b, 0x66, 0x7f, + 0xba, 0xf3, 0xd4, 0xb3, 0xb9, 0x08, 0xb9, 0x82, 0x88, 0x20, 0xdf, 0xec, 0xdd, + 0x75, 0x37, 0x50, 0xb5, 0xf9, 0xd2, 0x21, 0x6e, 0x56, 0xc6, 0x15, 0x27, 0x2f, + 0x85, 0x44, 0x64, 0xc0, 0xca, 0x4b, 0x1e, 0x85, 0xae, 0xdd, 0x03, 0x82, 0x92, + 0xc4, 0xe1, 0xa5, 0x77, 0x44, 0xeb, 0xba, 0x01, 0x0b, 0x9e, 0xbf, 0xbb, 0x01, + 0x1b, 0xd6, 0xf0, 0xb7, 0x88, 0x05, 0x02, 0x5d, 0x27, 0xf3, 0xc1, 0x77, 0x46, + 0xba, 0xe1, 0x16, 0xc1, 0x5d, 0x9f, 0x47, 0x1f, 0x0f, 0x62, 0x88, 0xa1, 0x50, + 0x64, 0x7b, 0x2a, 0xfe, 0x9d, 0xf7, 0xcc, 0xcf, 0x01, 0xf5, 0xcd, 0xe5, 0xf0, + 0x46, 0x80, 0xbb, 0xfe, 0xd8, 0x7f, 0x6c, 0xf4, 0x29, 0xfb, 0x27, 0xad, 0x6b, + 0xab, 0xe7, 0x91, 0x76, 0x66, 0x11, 0xcf, 0x5b, 0xc2, 0x0e, 0x48, 0xbe, 0xf1, + 0x19, 0x25, 0x9b, 0x9b, 0x8a, 0x0e, 0x39, 0xc3, 0xdf, 0x28, 0xcb, 0x95, 0x82, + 0xea, 0x33, 0x86, 0x01, 0xcd, 0xc4, 0x81, 0xb3, 0x2f, 0xb8, 0x2a, 0xde, 0xeb, + 0xb3, 0xda, 0xde, 0x25, 0xd1, 0xa3, 0xdf, 0x20, 0xc3, 0x7e, 0x71, 0x25, 0x06, + 0xb5, 0xd9, 0x96, 0xc4, 0x9a, 0x9f, 0x0f, 0x30, 0xdd, 0xcb, 0x91, 0xfe, 0x90, + 0x04, 0xe1, 0xe8, 0x32, 0x94, 0xa6, 0xc9, 0x20, 0x3d, 0x94, 0xe8, 0xdc, 0x2c, + 0xbb, 0x44, 0x9d, 0xe4, 0x15, 0x50, 0x32, 0x60, 0x4e, 0x47, 0x99, 0x70, 0x16, + 0xb3, 0x04, 0xfd, 0x43, 0x7d, 0x82, 0x35, 0x04, 0x5e, 0x25, 0x5a, 0x19, 0xb7, + 0x43, 0xa0, 0xa9, 0xf2, 0xe3, 0x36, 0xb4, 0x4c, 0xae, 0x30, 0x7b, 0xb3, 0x98, + 0x7b, 0xd3, 0xe4, 0xe7, 0x77, 0xfb, 0xb3, 0x4c, 0x0a, 0xb8, 0xcc, 0x3d, 0x67, + 0x46, 0x6c, 0x0a, 0x88, 0xdd, 0x4c, 0xca, 0xd1, 0x8a, 0x07, 0xa8, 0xd1, 0x06, + 0x8d, 0xf5, 0xb6, 0x29, 0xe5, 0x71, 0x8d, 0x0f, 0x6d, 0xf5, 0xc9, 0x57, 0xcf, + 0x71, 0xbb, 0x00, 0xa5, 0x17, 0x8f, 0x17, 0x5c, 0xac, 0xa9, 0x44, 0xe6, 0x35, + 0xc5, 0x15, 0x9f, 0x73, 0x8e, 0x24, 0x02, 0xa2, 0xd2, 0x1a, 0xa0, 0x81, 0xe1, + 0x0e, 0x45, 0x6a, 0xfb, 0x00, 0xb9, 0xf6, 0x24, 0x16, 0xc8, 0xb9, 0xc0, 0xf7, + 0x22, 0x8f, 0x51, 0x07, 0x29, 0xe0, 0xbe, 0x3f, 0x30, 0x53, 0x13, 0xd7, 0x7f, + 0x73, 0x79, 0xdc, 0x2a, 0xf2, 0x48, 0x69, 0xc6, 0xc7, 0x4e, 0xe4, 0x47, 0x14, + 0x98, 0x86, 0x1d, 0x19, 0x2f, 0x0f, 0xf0, 0xf5, 0x08, 0x28, 0x5d, 0xab, 0x6b, + 0x6a, 0x36, 0xcc, 0xf7, 0xd1, 0x22, 0x56, 0xcc, 0x76, 0xb9, 0x55, 0x03, 0x72, + 0x0a, 0xc6, 0x72, 0xd0, 0x82, 0x68, 0xd2, 0xcf, 0x77, 0x73, 0xb6, 0xba, 0x2a, + 0x5f, 0x66, 0x48, 0x47, 0xbf, 0x70, 0x7f, 0x2f, 0xc1, 0x0c, 0x98, 0xf2, 0xf0, + 0x06, 0xec, 0x22, 0xcc, 0xb5, 0xa8, 0xc8, 0xb7, 0xc4, 0x0c, 0x7c, 0x2d, 0x49, + 0xa6, 0x63, 0x9b, 0x9f, 0x2c, 0xe3, 0x3c, 0x25, 0xc0, 0x4b, 0xc4, 0x61, 0xe7, + 0x44, 0xdf, 0xa5, 0x36, 0xb0, 0x0d, 0x94, 0xba, 0xdd, 0xf4, 0xf4, 0xd1, 0x40, + 0x44, 0xc6, 0x95, 0xa3, 0x38, 0x81, 0x47, 0x7d, 0xf1, 0x24, 0xf0, 0xfc, 0xf2, + 0x06, 0xa9, 0xfb, 0x2e, 0x65, 0xe3, 0x04, 0xcd, 0xbf, 0x0c, 0x4d, 0x23, 0x90, + 0x17, 0x0c, 0x13, 0x0a, 0xb8, 0x49, 0xc2, 0xf2, 0x2b, 0x5c, 0xdd, 0x39, 0x21, + 0x64, 0x0c, 0x8c, 0xf1, 0x97, 0x6a, 0xe1, 0x01, 0x0b, 0x0d, 0xfd, 0x9c, 0xb2, + 0x54, 0x3e, 0x45, 0xf9, 0x97, 0x49, 0xcc, 0x4d, 0x61, 0xf2, 0xe8, 0xaa, 0xbf, + 0xe9, 0x8b, 0xd9, 0x05, 0xfa, 0x39, 0x95, 0x1b, 0x33, 0xea, 0x76, 0x9c, 0x45, + 0xab, 0x95, 0x31, 0xc5, 0x72, 0x09, 0x86, 0x2a, 0xd1, 0x2f, 0xd7, 0x6b, 0xa4, + 0x80, 0x7e, 0x65, 0x41, 0x7b, 0x6c, 0xd1, 0x2f, 0xa8, 0xec, 0x91, 0x6f, 0x01, + 0x3e, 0xbb, 0x87, 0x06, 0xa9, 0x6e, 0xff, 0xed, 0xa0, 0x6c, 0x4b, 0xe2, 0x4b, + 0x04, 0x84, 0x63, 0x92, 0xe9, 0xd1, 0xe6, 0x93, 0x0e, 0xae, 0x01, 0xfa, 0x21, + 0xfb, 0xd7, 0x00, 0x58, 0x3f, 0xb5, 0x98, 0xb9, 0x2c, 0x8f, 0x4e, 0xb8, 0xa6, + 0x1a, 0xa6, 0x23, 0x5d, 0xb6, 0x0f, 0x28, 0x41, 0xcf, 0x3a, 0x1c, 0x6a, 0xb5, + 0x4c, 0x67, 0x06, 0x68, 0x44, 0x71, 0x1d, 0x09, 0x1e, 0xb9, 0x31, 0xa1, 0xbd, + 0x62, 0x81, 0xae, 0xdf, 0x2a, 0x0e, 0x8f, 0xab, 0x18, 0x81, 0x72, 0x02, 0xa9, + 0xbe, 0x06, 0x40, 0x2e, 0xd9, 0xcc, 0x72, 0x0c, 0x16, 0xbf, 0xe8, 0x81, 0xe4, + 0xdf, 0x42, 0x55, 0xe8, 0x7a, 0xfb, 0x7f, 0xc6, 0x2f, 0x38, 0x11, 0x6b, 0xbe, + 0x03, 0xcd, 0x8a, 0x3c, 0xb1, 0x1a, 0x27, 0xd5, 0x68, 0x41, 0x47, 0x82, 0xf4, + 0x7b, 0x1a, 0x44, 0xc9, 0x7c, 0x68, 0x04, 0x67, 0x69, 0x4b, 0xc9, 0x70, 0x9d, + 0x32, 0x91, 0x6c, 0x97, 0xe8, 0x00, 0x6c, 0xbb, 0x07, 0xba, 0x0e, 0x41, 0x80, + 0xa3, 0x73, 0x80, 0x38, 0xc3, 0x74, 0xc4, 0xcc, 0xe8, 0xf3, 0x29, 0x59, 0xaf, + 0xb2, 0x5f, 0x30, 0x3f, 0x58, 0x15, 0xc4, 0x53, 0x31, 0x24, 0xac, 0xf9, 0xd1, + 0x89, 0x40, 0xe7, 0x75, 0x22, 0xac, 0x5d, 0xc4, 0xb9, 0x57, 0x0a, 0xae, 0x8f, + 0x47, 0xb7, 0xf5, 0x7f, 0xd8, 0x76, 0x7b, 0xea, 0x1a, 0x24, 0xae, 0x7b, 0xed, + 0x65, 0xb4, 0xaf, 0xdc, 0x8f, 0x12, 0x78, 0xc3, 0x0e, 0x2d, 0xb9, 0x8f, 0xd1, + 0x72, 0x73, 0x0a, 0xc6, 0xbb, 0xed, 0x4f, 0x11, 0x27, 0xcd, 0x32, 0xb0, 0x4a, + 0x95, 0xb2, 0x05, 0x52, 0x6c, 0xfc, 0xb4, 0xc4, 0xe1, 0xcc, 0x95, 0x51, 0x75, + 0xb3, 0xe8, 0xde, 0x1f, 0x5d, 0x81, 0xb1, 0x86, 0x69, 0x69, 0x23, 0x50, 0xaa, + 0xa1, 0xa1, 0xd7, 0x97, 0x61, 0x75, 0x82, 0xe5, 0x4d, 0x7a, 0x5b, 0x57, 0xa6, + 0x83, 0xb3, 0x2f, 0xb1, 0x09, 0x80, 0x62, 0xda, 0xd7, 0xb0, 0xc2, 0xeb, 0x51, + 0x8f, 0x68, 0x62, 0xe8, 0x3d, 0xb2, 0x5e, 0x3d, 0xba, 0xf7, 0xae, 0xd5, 0x04, + 0xde, 0x93, 0x2a, 0xcb, 0x99, 0xd7, 0x35, 0x99, 0x2c, 0xe6, 0x2b, 0xae, 0x9e, + 0xf8, 0x93, 0xff, 0x6a, 0xcc, 0x0f, 0xfc, 0xf8, 0xe3, 0x48, 0x3e, 0x14, 0x6b, + 0x9d, 0x49, 0xdd, 0x8c, 0x78, 0x35, 0xf4, 0x3a, 0x37, 0xdc, 0xa0, 0x78, 0x7e, + 0x3e, 0xc9, 0xf6, 0x60, 0x52, 0x23, 0xd5, 0xba, 0x7a, 0xe0, 0xab, 0x90, 0x25, + 0xb7, 0x3b, 0xc0, 0x3f, 0x7f, 0xac, 0x36, 0xc0, 0x09, 0xa5, 0x6d, 0x4d, 0x95, + 0xd1, 0xe8, 0x1d, 0x3b, 0x3e, 0xbc, 0xa7, 0xe5, 0x4c, 0xc1, 0xa1, 0x2d, 0x12, + 0x7b, 0x57, 0xc8, 0x13, 0x89, 0x76, 0xe7, 0x91, 0x01, 0x3b, 0x01, 0x5f, 0x06, + 0xa6, 0x24, 0xf5, 0x21, 0xb6, 0xee, 0x04, 0xec, 0x98, 0x08, 0x93, 0xc7, 0xe5, + 0xe0, 0x1a, 0x33, 0x62, 0x03, 0x59, 0x40, 0x94, 0xf8, 0x28, 0x33, 0xd7, 0x44, + 0x5f, 0xe2, 0xd0, 0x91, 0x30, 0xf6, 0x35, 0x11, 0xda, 0x54, 0x83, 0x2d, 0xe9, + 0x13, 0x6b, 0x39, 0xf4, 0x59, 0x9f, 0x5a, 0xa5, 0xdf, 0xbb, 0x45, 0xda, 0x60, + 0xcd, 0xce, 0xab, 0x7e, 0xef, 0xde, 0x89, 0xbe, 0x63, 0xf3, 0xf7, 0xc0, 0xd2, + 0x32, 0x48, 0x47, 0xcc, 0xe1, 0x40, 0x5d, 0xef, 0x7c, 0x46, 0x9b, 0x0e, 0x27, + 0x24, 0x94, 0xe5, 0xdf, 0x54, 0xf5, 0x68, 0x65, 0x6c, 0xb9, 0xc8, 0x81, 0x8d, + 0x92, 0xb7, 0x2b, 0x8b, 0xc3, 0x4d, 0xb7, 0xbb, 0x31, 0x12, 0x48, 0x7e, 0x74, + 0x6e, 0xef, 0xe4, 0xe8, 0x08, 0xbb, 0xb2, 0x87, 0xd9, 0x9b, 0xf0, 0x7d, 0x00, + 0xda, 0xbe, 0xde, 0xdc, 0x5e, 0x5f, 0x07, 0x4f, 0xfe, 0xae, 0x0c, 0xba, 0x7d, + 0xa3, 0xa5, 0x16, 0xc1, 0x73, 0xbe, 0x1c, 0x51, 0x33, 0x23, 0xe1, 0x19, 0xf6, + 0x35, 0xe8, 0x20, 0x9a, 0x07, 0x4b, 0x21, 0x6b, 0x70, 0x23, 0xfa, 0xdc, 0x2d, + 0x25, 0x94, 0x9c, 0x90, 0x03, 0x7e, 0x71, 0xe3, 0xe5, 0x50, 0x72, 0x6d, 0x21, + 0x0a, 0x2c, 0x68, 0x83, 0x42, 0xe5, 0x24, 0x40, 0x63, 0x5e, 0x9c, 0xc1, 0x4a, + 0xfe, 0x10, 0x10, 0x26, 0x21, 0xa9, 0xc9, 0xac, 0xcb, 0x78, 0x2e, 0x9e, 0x4a, + 0x5f, 0xa8, 0x7f, 0x0a, 0x95, 0x6f, 0x5b, 0x85, 0x50, 0x99, 0x60, 0x28, 0x5c, + 0x22, 0x62, 0x7c, 0x59, 0x48, 0x3a, 0x5a, 0x4c, 0x28, 0xcc, 0xe4, 0xb1, 0x56, + 0xe5, 0x51, 0x40, 0x6a, 0x7e, 0xe8, 0x35, 0x56, 0x56, 0xa2, 0x1e, 0x43, 0xe3, + 0x8c, 0xe1, 0x29, 0xfd, 0xad, 0xb7, 0x59, 0xed, 0xdf, 0xa0, 0x8f, 0x00, 0xfc, + 0x8e, 0x56, 0x7c, 0xef, 0x93, 0xc6, 0x79, 0x2d, 0x01, 0xdf, 0x05, 0xe6, 0xd5, + 0x80, 0xf4, 0xd5, 0xd4, 0x8d, 0xf0, 0x42, 0x45, 0x1a, 0x33, 0x59, 0x0d, 0x3e, + 0x8c, 0xf4, 0x9b, 0x26, 0x27, 0x21, 0x8f, 0x0c, 0x29, 0x2f, 0xa6, 0x6a, 0xda, + 0x94, 0x5f, 0xa5, 0x5b, 0xb2, 0x35, 0x48, 0xe3, 0x3a, 0x83, 0xa5, 0x62, 0x95, + 0x7a, 0x31, 0x49, 0xa9, 0x93, 0xcc, 0x47, 0x23, 0x62, 0x29, 0x87, 0x36, 0xa8, + 0xb7, 0x78, 0xd9, 0x7c, 0xe4, 0x23, 0x01, 0x3d, 0x64, 0xb3, 0x2c, 0xd1, 0x72, + 0xef, 0xa5, 0x51, 0xbf, 0x7f, 0x36, 0x8f, 0x04, 0xbd, 0xae, 0xc6, 0x09, 0x1a, + 0x30, 0x04, 0xa7, 0x57, 0x59, 0x8b, 0x80, 0x1d, 0xcf, 0x67, 0x5c, 0xb8, 0x3e, + 0x43, 0xa5, 0x3a, 0xe8, 0xb2, 0x54, 0xd3, 0x33, 0xbc, 0xda, 0x20, 0xd4, 0x81, + 0x7d, 0x34, 0x77, 0xab, 0xfb, 0xa2, 0x5b, 0xb8, 0x3d, 0xf5, 0x94, 0x9c, 0x12, + 0x6f, 0x14, 0x9b, 0x1d, 0x99, 0x34, 0x1e, 0x4e, 0x6f, 0x91, 0x20, 0xf4, 0xd4, + 0x1e, 0x62, 0x91, 0x85, 0x00, 0x2c, 0x72, 0xc0, 0x12, 0xc4, 0x14, 0xd2, 0x38, + 0x2a, 0x6d, 0x47, 0xc7, 0xb3, 0xde, 0xab, 0xa7, + ], + script_code: Script(vec![0xac, 0x00]), + transparent_input: Some(0), + hash_type: 3, + amount: 711752082734717, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0xb3, 0x8e, 0x31, 0x70, 0x8c, 0xb7, 0x8e, 0xee, 0xc7, 0x66, 0x3e, 0xca, 0x1e, + 0x01, 0xb7, 0x53, 0x9e, 0x26, 0xb7, 0x30, 0xcf, 0x44, 0x6d, 0x3b, 0xf5, 0x7a, + 0x99, 0x8e, 0x9e, 0xd9, 0x2b, 0x47, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0x0d, 0x38, 0x6a, + 0xe3, 0x0d, 0xd3, 0x01, 0x00, 0x07, 0x00, 0x65, 0x51, 0x65, 0x53, 0x53, 0x6a, + 0xd5, 0x09, 0x42, 0xf7, 0x69, 0x67, 0x02, 0x00, 0x05, 0xac, 0x65, 0x63, 0x65, + 0xac, 0x61, 0xa7, 0xb8, 0xb9, 0xf6, 0x99, 0xd6, 0x01, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x49, 0x23, 0x2f, 0x32, 0x9f, 0xef, 0x95, 0xc7, 0xaf, 0x37, 0x00, 0x98, + 0xff, 0xe4, 0x91, 0x8e, 0x0c, 0xa1, 0xdf, 0x47, 0xf2, 0x75, 0x86, 0x7b, 0x73, + 0x9e, 0x0a, 0x51, 0x4d, 0x32, 0x09, 0x32, 0x5e, 0x21, 0x70, 0x45, 0x92, 0x7b, + 0x47, 0x9c, 0x1c, 0xe2, 0xe5, 0xd5, 0x4f, 0x25, 0x48, 0x8c, 0xad, 0x15, 0x13, + 0xe3, 0xf4, 0x4a, 0x21, 0x26, 0x6c, 0xfd, 0x84, 0x16, 0x33, 0x32, 0x7d, 0xee, + 0x6c, 0xf8, 0x10, 0xfb, 0xf7, 0x39, 0x3e, 0x31, 0x7d, 0x9e, 0x53, 0xd1, 0xbe, + 0x1d, 0x5a, 0xe7, 0x83, 0x9b, 0x66, 0xb9, 0x43, 0xb9, 0xed, 0x18, 0xf2, 0xc5, + 0x30, 0xe9, 0x75, 0x42, 0x23, 0x32, 0xc3, 0x43, 0x9c, 0xce, 0x49, 0xa2, 0x9f, + 0x2a, 0x33, 0x6a, 0x48, 0x51, 0x26, 0x3c, 0x5e, 0x9b, 0xd1, 0x3d, 0x73, 0x11, + 0x09, 0xe8, 0x44, 0xb7, 0xf8, 0xc3, 0x92, 0xa5, 0xc1, 0xdc, 0xaa, 0x2a, 0xe5, + 0xf5, 0x0f, 0xf6, 0x3f, 0xab, 0x97, 0x65, 0xe0, 0x16, 0x70, 0x2c, 0x35, 0xa6, + 0x7c, 0xd7, 0x36, 0x4d, 0x3f, 0xab, 0x55, 0x2f, 0xb3, 0x49, 0xe3, 0x5c, 0x15, + 0xc5, 0x02, 0x50, 0x45, 0x3f, 0xd1, 0x8f, 0x7b, 0x85, 0x59, 0x92, 0x63, 0x2e, + 0x2c, 0x76, 0xc0, 0xfb, 0xf1, 0xef, 0x96, 0x3e, 0xa8, 0x0e, 0x32, 0x23, 0xde, + 0x32, 0x77, 0xbc, 0x55, 0x92, 0x51, 0x72, 0x58, 0x29, 0xec, 0x03, 0xf2, 0x13, + 0xba, 0x89, 0x55, 0xca, 0xb2, 0x82, 0x2f, 0xf2, 0x1a, 0x9b, 0x0a, 0x49, 0x04, + 0xd6, 0x68, 0xfc, 0xd7, 0x72, 0x24, 0xbd, 0xe3, 0xdd, 0x01, 0xf6, 0xff, 0xc4, + 0x82, 0x8f, 0x6b, 0x64, 0x23, 0x0b, 0x35, 0xc6, 0xa0, 0x49, 0x87, 0x34, 0x94, + 0x27, 0x6e, 0xa1, 0xd7, 0xed, 0x5e, 0x92, 0xcb, 0x4f, 0x90, 0xba, 0x83, 0xa9, + 0xe4, 0x96, 0x01, 0xb1, 0x94, 0x04, 0x2f, 0x29, 0x00, 0xd9, 0x9d, 0x31, 0x2d, + 0x7b, 0x70, 0x50, 0x8c, 0xf1, 0x76, 0x06, 0x6d, 0x15, 0x4d, 0xbe, 0x96, 0xef, + 0x9d, 0x43, 0x67, 0xe4, 0xc8, 0x40, 0xe4, 0xa1, 0x7b, 0x5e, 0x51, 0x22, 0xe8, + 0xeb, 0xe2, 0x03, 0x8a, 0x3c, 0x5f, 0x4c, 0xba, 0xe2, 0x1e, 0xa3, 0xfa, 0x1a, + 0xe6, 0xc2, 0x5a, 0x94, 0x62, 0xeb, 0xcb, 0xb0, 0xfd, 0x5f, 0x14, 0x55, 0x4b, + 0xc9, 0x77, 0x47, 0xc3, 0x3e, 0x34, 0xda, 0x90, 0xc8, 0x02, 0xd8, 0xd0, 0xd5, + 0x0b, 0xfe, 0x37, 0x61, 0x8c, 0x58, 0x12, 0x89, 0x14, 0x84, 0xfa, 0x25, 0x93, + 0x22, 0xc1, 0x50, 0x92, 0xd4, 0x15, 0x5d, 0x86, 0x96, 0xd6, 0xf1, 0x2f, 0x24, + 0xfd, 0x36, 0x44, 0x0a, 0xb3, 0xbe, 0x08, 0x71, 0xca, 0x3d, 0xd9, 0x62, 0x53, + 0x48, 0xa6, 0x14, 0xb5, 0x9b, 0xde, 0x45, 0x88, 0x56, 0x49, 0xba, 0xe3, 0x6d, + 0xe3, 0x4d, 0xef, 0x8f, 0xce, 0xc8, 0x53, 0x43, 0x47, 0x5d, 0x97, 0x6a, 0xe1, + 0xe9, 0xb2, 0x78, 0x29, 0xce, 0x2a, 0xc5, 0xef, 0xd0, 0xb3, 0x99, 0xa8, 0xb4, + 0x48, 0xbe, 0x65, 0x04, 0x29, 0x4e, 0xe6, 0xb3, 0xc1, 0xc6, 0xa5, 0x34, 0x2d, + 0x7c, 0x01, 0xae, 0x03, 0x8a, 0xd3, 0x07, 0x0c, 0x2b, 0x1a, 0x91, 0x57, 0x3a, + 0xf5, 0xe0, 0xc5, 0xe4, 0xcb, 0xbf, 0x4a, 0xcd, 0xc6, 0xb5, 0x4c, 0x92, 0x72, + 0x20, 0x0d, 0x99, 0x70, 0x25, 0x0c, 0x17, 0xc1, 0x03, 0x6f, 0x02, 0x08, 0x5c, + 0x41, 0x85, 0x8e, 0xd3, 0xa0, 0xc4, 0x81, 0x50, 0xbc, 0x69, 0x7e, 0x4a, 0x69, + 0x5f, 0xef, 0x33, 0x5f, 0x7a, 0xd0, 0x7e, 0x1a, 0x46, 0xdc, 0x76, 0x7f, 0xf8, + 0x22, 0xdb, 0x70, 0xe6, 0x02, 0x90, 0x80, 0xb9, 0x81, 0x6b, 0x22, 0x32, 0xc8, + 0x1a, 0x4c, 0x66, 0xcc, 0x58, 0x6a, 0xbf, 0xe1, 0xea, 0xa8, 0xca, 0x6c, 0xf4, + 0x1f, 0xc3, 0xc3, 0xe6, 0xc7, 0xb8, 0x86, 0xfb, 0x6d, 0xac, 0x9f, 0x02, 0x22, + 0xb4, 0xfc, 0x6f, 0xff, 0x9d, 0x05, 0x13, 0xd6, 0x1a, 0x21, 0xc8, 0x0a, 0x37, + 0x76, 0x71, 0xd1, 0x35, 0xa6, 0x68, 0xa0, 0xae, 0x2b, 0xb9, 0x34, 0xc8, 0x2c, + 0x41, 0x42, 0xda, 0x69, 0xd1, 0x02, 0xa7, 0xde, 0x9a, 0x7d, 0xf7, 0x06, 0x40, + 0x0e, 0xc7, 0x98, 0x78, 0xd8, 0x68, 0xe1, 0x7e, 0x8f, 0x71, 0xea, 0x31, 0x49, + 0x5a, 0xf8, 0x19, 0xa0, 0x16, 0xcc, 0x41, 0x9e, 0x07, 0xc5, 0x01, 0xaa, 0x83, + 0x09, 0xb2, 0xe6, 0xc8, 0x5b, 0x79, 0xb2, 0x76, 0x37, 0x33, 0xa3, 0x7b, 0xbc, + 0x04, 0x20, 0xd4, 0x25, 0x37, 0xb8, 0x71, 0xb4, 0x29, 0x4a, 0x65, 0xd3, 0xe0, + 0x55, 0xff, 0x71, 0x8d, 0xd9, 0xdc, 0x8c, 0x75, 0xe7, 0xe5, 0xb2, 0xef, 0xe4, + 0x42, 0x63, 0x73, 0x71, 0xb7, 0xc4, 0x8f, 0x6e, 0xe9, 0x9e, 0x3e, 0xa3, 0x8a, + 0x4b, 0x0f, 0x2f, 0x67, 0xfc, 0x2b, 0x90, 0x8c, 0xda, 0x65, 0x7e, 0xae, 0x75, + 0x4e, 0x03, 0x7e, 0x26, 0x2e, 0x9a, 0x9f, 0x9b, 0xd7, 0xec, 0x42, 0x67, 0xed, + 0x8e, 0x96, 0x93, 0x0e, 0x10, 0x84, 0x78, 0x3c, 0x37, 0xd6, 0xf9, 0xdd, 0x15, + 0xfd, 0x29, 0xf4, 0xcc, 0x47, 0x7e, 0x66, 0xf1, 0x30, 0xd6, 0x30, 0x43, 0x0d, + 0xcc, 0x01, 0x04, 0x89, 0x9b, 0x4f, 0x9f, 0x46, 0xeb, 0x09, 0x0e, 0xf7, 0xfc, + 0x90, 0xb4, 0x79, 0xab, 0xf6, 0x1f, 0x93, 0x95, 0x5e, 0xe0, 0x0e, 0x6a, 0x18, + 0x48, 0xf1, 0xab, 0x14, 0xad, 0x33, 0x4f, 0x2b, 0x68, 0x03, 0x58, 0x08, 0xcd, + 0xf1, 0xbb, 0x9e, 0x9d, 0x9a, 0x81, 0x6b, 0xaf, 0x72, 0x8a, 0x95, 0x5b, 0x96, + 0x0b, 0x77, 0x01, 0xfa, 0x62, 0x66, 0x87, 0xdc, 0x3c, 0x9c, 0xba, 0x64, 0x63, + 0x37, 0xb5, 0x3e, 0x29, 0x81, 0x6e, 0x94, 0x82, 0xdd, 0xf5, 0x57, 0x8a, 0x87, + 0x68, 0xaa, 0xe4, 0x77, 0xfc, 0xe4, 0x10, 0xac, 0x2d, 0x5d, 0xe6, 0x09, 0x58, + 0x61, 0xc1, 0x11, 0xd7, 0xfe, 0xb3, 0xe6, 0xbb, 0x4f, 0xbb, 0x5a, 0x54, 0x95, + 0x54, 0x95, 0x97, 0x27, 0x98, 0x35, 0x0a, 0x25, 0x3f, 0x05, 0xf6, 0x6c, 0x2e, + 0xcf, 0xcb, 0xc0, 0xed, 0x43, 0xf5, 0xec, 0x2e, 0x6d, 0x8d, 0xba, 0x15, 0xa5, + 0x12, 0x54, 0xd9, 0x7b, 0x18, 0x21, 0x10, 0x7c, 0x07, 0xdd, 0x9a, 0x16, 0xef, + 0x84, 0x06, 0xf9, 0x43, 0xe2, 0x82, 0xb9, 0x5d, 0x4b, 0x36, 0x25, 0x30, 0xc9, + 0x13, 0xd6, 0xba, 0x42, 0x1d, 0xf6, 0x02, 0x7d, 0xe5, 0xaf, 0x1e, 0x47, 0x45, + 0xd5, 0x86, 0x81, 0x06, 0x95, 0x4b, 0xe6, 0xc1, 0x96, 0x27, 0x80, 0xa2, 0x94, + 0x10, 0x72, 0xe9, 0x51, 0x31, 0xb1, 0x67, 0x9d, 0xf0, 0x63, 0x76, 0x25, 0x04, + 0x2c, 0x37, 0xd4, 0x8f, 0xfb, 0x15, 0x2e, 0x5e, 0xbc, 0x18, 0x5c, 0x8a, 0x2b, + 0x7d, 0x43, 0x85, 0xf1, 0xc9, 0x5a, 0xf9, 0x37, 0xdf, 0x78, 0xdf, 0xd8, 0x75, + 0x7f, 0xab, 0x43, 0x49, 0x68, 0xb0, 0xb5, 0x7c, 0x66, 0x57, 0x44, 0x68, 0xf1, + 0x60, 0xb4, 0x47, 0xac, 0x82, 0x21, 0xe5, 0x06, 0x06, 0x76, 0xa8, 0x42, 0xa1, + 0xc6, 0xb7, 0x17, 0x2d, 0xd3, 0x34, 0x0f, 0x76, 0x40, 0x70, 0xab, 0x1f, 0xe0, + 0x91, 0xc5, 0xc7, 0x4c, 0x95, 0xa5, 0xdc, 0x04, 0x33, 0x90, 0x72, 0x3a, 0x4c, + 0x12, 0x7d, 0xa1, 0x4c, 0xdd, 0xe1, 0xdc, 0x26, 0x75, 0xa6, 0x23, 0x40, 0xb3, + 0xe6, 0xaf, 0xd0, 0x52, 0x2a, 0x31, 0xde, 0x26, 0xe7, 0xd1, 0xec, 0x3a, 0x9c, + 0x8a, 0x09, 0x1f, 0xfd, 0xc7, 0x5b, 0x7e, 0xcf, 0xdc, 0x7c, 0x12, 0x99, 0x5a, + 0x5e, 0x37, 0xce, 0x34, 0x88, 0xbd, 0x29, 0xf8, 0x62, 0x9d, 0x68, 0xf6, 0x96, + 0x49, 0x24, 0x48, 0xdd, 0x52, 0x66, 0x97, 0x47, 0x6d, 0xc0, 0x61, 0x34, 0x6e, + 0xbe, 0x3f, 0x67, 0x72, 0x17, 0xff, 0x9c, 0x60, 0xef, 0xce, 0x94, 0x3a, 0xf2, + 0x8d, 0xfd, 0x3f, 0x9e, 0x59, 0x69, 0x25, 0x98, 0xa6, 0x04, 0x7c, 0x23, 0xc4, + 0xc0, 0x14, 0x00, 0xf1, 0xab, 0x57, 0x30, 0xea, 0xc0, 0xae, 0x8d, 0x58, 0x43, + 0xd5, 0x05, 0x1c, 0x37, 0x62, 0x40, 0x17, 0x2a, 0xf2, 0x18, 0xd7, 0xa1, 0xec, + 0xfe, 0x65, 0xb4, 0xf7, 0x51, 0x00, 0x63, 0x89, 0x83, 0xc1, 0x4d, 0xe4, 0x97, + 0x47, 0x55, 0xda, 0xde, 0x80, 0x18, 0xc9, 0xb8, 0xf4, 0x54, 0x3f, 0xb0, 0x95, + 0x96, 0x15, 0x13, 0xe6, 0x7c, 0x61, 0xdb, 0xc5, 0x9c, 0x60, 0x7f, 0x9b, 0x51, + 0xf8, 0xd0, 0x9b, 0xdc, 0xad, 0x28, 0xbc, 0xfb, 0x9e, 0x5d, 0x27, 0x44, 0xea, + 0x88, 0x48, 0xb2, 0x62, 0x3a, 0xc0, 0x7f, 0x8e, 0xf6, 0x1a, 0x81, 0xa3, 0x59, + 0x10, 0xb8, 0xa1, 0xba, 0xf3, 0x9a, 0x91, 0x9a, 0x7b, 0x60, 0xbc, 0x60, 0x4d, + 0x63, 0x18, 0x5f, 0x75, 0x92, 0x21, 0xd8, 0x47, 0xcc, 0x54, 0xa2, 0x27, 0x65, + 0xa4, 0xc3, 0x34, 0x75, 0xb5, 0x79, 0x1e, 0x9a, 0xf3, 0x27, 0x1f, 0xc8, 0xd9, + 0x35, 0x06, 0x67, 0x09, 0x0d, 0x81, 0x84, 0xec, 0x50, 0x52, 0x2d, 0x80, 0x4f, + 0x23, 0xc4, 0xfb, 0x44, 0xff, 0xa4, 0x81, 0xbc, 0x92, 0xae, 0x40, 0x8d, 0x1b, + 0x9f, 0x2b, 0x13, 0x19, 0x04, 0xf9, 0x70, 0x5c, 0x59, 0xe2, 0xf4, 0xbd, 0xe7, + 0xa3, 0xb2, 0xc0, 0x85, 0xd9, 0x3f, 0xd2, 0xab, 0xc5, 0xe1, 0x4d, 0x16, 0x30, + 0x01, 0xa1, 0x2f, 0x51, 0x93, 0x8d, 0x02, 0x1a, 0xfa, 0x92, 0x23, 0x9b, 0x87, + 0x3d, 0xc6, 0xc3, 0x57, 0xea, 0xa8, 0xaf, 0x4e, 0xe6, 0xd0, 0x05, 0x40, 0x65, + 0x7f, 0xe3, 0x29, 0x14, 0x10, 0x3b, 0x5d, 0x98, 0xf6, 0x8b, 0xd3, 0xe2, 0xb5, + 0x35, 0x9f, 0x08, 0xcc, 0xd8, 0x8d, 0x0c, 0x81, 0x1e, 0x4c, 0x31, 0xfb, 0xb4, + 0x9f, 0x3a, 0x90, 0xbb, 0xd0, 0x5d, 0xce, 0x62, 0xf3, 0x44, 0xe7, 0x07, 0x75, + 0x93, 0x15, 0x9a, 0xe3, 0x50, 0x50, 0xb0, 0x4c, 0x9e, 0x6b, 0x86, 0xbc, 0x43, + 0x2d, 0xc8, 0xb0, 0x48, 0xc7, 0x3c, 0x00, 0x18, 0xca, 0x5b, 0x69, 0x41, 0x12, + 0x97, 0x73, 0x2a, 0x4e, 0x1a, 0xa9, 0x9a, 0x92, 0x8c, 0x71, 0xe7, 0xa2, 0x4f, + 0xd2, 0x77, 0x85, 0x6a, 0xa4, 0x25, 0x01, 0xe5, 0x1b, 0x01, 0x2a, 0xea, 0x94, + 0x46, 0xa2, 0x10, 0x4e, 0x93, 0xf8, 0x15, 0xa0, 0xb3, 0xa2, 0x9b, 0x45, 0x83, + 0x14, 0xf3, 0xd8, 0xbe, 0x2b, 0x98, 0x23, 0xd3, 0x42, 0xf4, 0x62, 0x13, 0xe9, + 0x42, 0xa7, 0xe1, 0x9a, 0x46, 0xe9, 0x70, 0xb5, 0xc5, 0x06, 0x70, 0x84, 0x30, + 0x31, 0x7b, 0x1b, 0xb3, 0xb3, 0x5d, 0xf6, 0x8a, 0xe3, 0x3a, 0x49, 0x26, 0xa0, + 0x3e, 0x6b, 0xfe, 0xb5, 0x51, 0x04, 0x16, 0xfc, 0xbb, 0x05, 0x24, 0xc9, 0xca, + 0x50, 0x74, 0x15, 0x6c, 0xc5, 0xa5, 0xd6, 0xfe, 0x1c, 0x99, 0x5e, 0xdc, 0x60, + 0xa2, 0xf5, 0x50, 0x41, 0x1a, 0xa4, 0x1e, 0x3d, 0xa3, 0xbd, 0xcf, 0x64, 0xbc, + 0xf0, 0x4a, 0x05, 0x10, 0x57, 0x1b, 0x93, 0x6d, 0x47, 0xe5, 0x5c, 0xec, 0x03, + 0x30, 0xee, 0x8d, 0xfe, 0x73, 0x56, 0x34, 0x04, 0xf0, 0x47, 0xd7, 0xf3, 0xa8, + 0xa3, 0xd7, 0x74, 0x3b, 0xc5, 0x54, 0x95, 0x52, 0x10, 0xf1, 0xeb, 0x0d, 0x08, + 0x59, 0x9e, 0xa7, 0x7d, 0x5f, 0x97, 0x4d, 0x87, 0x17, 0x6d, 0x37, 0xd9, 0x8b, + 0x9c, 0x0a, 0xd4, 0x40, 0x40, 0x72, 0x09, 0xed, 0x6a, 0x9f, 0x08, 0x46, 0x4d, + 0x56, 0x55, 0x93, 0xe1, 0xa6, 0x3b, 0x93, 0x85, 0x36, 0xb4, 0x92, 0x44, 0xe9, + 0x7d, 0x88, 0x01, 0x73, 0xb6, 0x40, 0xf2, 0xdd, 0xb7, 0x4d, 0x06, 0x8e, 0xcb, + 0x46, 0xcf, 0x28, 0x9b, 0x7d, 0x89, 0x13, 0x07, 0xbb, 0xa3, 0x70, 0x54, 0xcf, + 0x91, 0xb3, 0x1f, 0xc8, 0x2f, 0x74, 0xd5, 0xfc, 0xc0, 0x00, 0x94, 0x2e, 0xde, + 0x91, 0x18, 0x25, 0xf5, 0x3f, 0xe6, 0x09, 0x68, 0x6f, 0x46, 0x32, 0x23, 0xb1, + 0xe9, 0xbc, 0x03, 0xbd, 0xe8, 0x95, 0xd1, 0x23, 0x8f, 0xad, 0x04, 0xa3, 0xbf, + 0xce, 0x68, 0xa0, 0x75, 0xe8, 0xa3, 0x7c, 0x0e, 0x87, 0xbf, 0x46, 0xdd, 0x01, + 0x55, 0x45, 0xf9, 0xb4, 0xfb, 0x0e, 0xec, 0x64, 0x5f, 0xfc, 0xbb, 0xe0, 0xca, + 0x5f, 0x8c, 0x56, 0x1b, 0x25, 0x7d, 0x52, 0xd6, 0x02, 0xd8, 0xc9, 0x4c, 0x50, + 0x28, 0x73, 0xa0, 0x1d, 0x92, 0x51, 0xd8, 0xc8, 0x60, 0xc0, 0x41, 0x52, 0x5b, + 0x3b, 0xf4, 0xe3, 0xa2, 0xeb, 0x92, 0x72, 0x81, 0x5c, 0x75, 0x86, 0x76, 0x84, + 0x28, 0xb4, 0xc2, 0xb2, 0x5e, 0x37, 0x45, 0xf0, 0x09, 0xc5, 0xdc, 0xe2, 0x0b, + 0x69, 0xd5, 0xd7, 0xc4, 0x3c, 0xeb, 0x73, 0x6b, 0x68, 0x31, 0xe8, 0xc1, 0x10, + 0xf1, 0x6c, 0xfd, 0xb3, 0xa4, 0x67, 0xe9, 0x41, 0x4c, 0x00, 0xec, 0xf1, 0x37, + 0x31, 0x50, 0x08, 0x94, 0x55, 0x56, 0x78, 0xc4, 0x97, 0xfa, 0xba, 0x9a, 0x95, + 0xd0, 0x1c, 0xc4, 0x64, 0x39, 0x0f, 0xc4, 0xa7, 0x6b, 0xfa, 0x8b, 0x0e, 0x1c, + 0x68, 0xa5, 0x25, 0xd7, 0x06, 0xd6, 0x60, 0x4b, 0x23, 0x30, 0xb6, 0xb3, 0x48, + 0x52, 0x15, 0xf6, 0x06, 0xf1, 0x88, 0x3a, 0x75, 0x15, 0x88, 0xc7, 0xef, 0xa5, + 0x06, 0xc3, 0xe8, 0xd0, 0xc6, 0x01, 0x92, 0xe8, 0x47, 0x6b, 0xd1, 0x17, 0x5d, + 0x95, 0x62, 0x08, 0x7b, 0xdb, 0x81, 0x8e, 0x66, 0x21, 0x62, 0x86, 0xba, 0xfe, + 0x47, 0xff, 0x4d, 0xbc, 0xce, 0xd5, 0x14, 0x44, 0x48, 0x0a, 0x9a, 0x56, 0x73, + 0xec, 0xe7, 0xfa, 0xc7, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd4, 0x1a, 0xb0, 0x05, + 0x17, 0x53, 0xa7, 0xca, 0xa8, 0x9b, 0xe3, 0x13, 0x9a, 0xfd, 0x97, 0x93, 0xb3, + 0xe0, 0x2f, 0x27, 0xf0, 0x40, 0x04, 0x65, 0x95, 0xac, 0xd4, 0x7b, 0xf1, 0x3f, + 0xd0, 0xda, 0x27, 0xf0, 0x9e, 0xda, 0x48, 0x03, 0x6d, 0x3e, 0xe4, 0x37, 0xf2, + 0xee, 0x8f, 0x86, 0x06, 0xea, 0x97, 0x34, 0x3c, 0x33, 0x58, 0x46, 0x57, 0xf4, + 0x6d, 0xba, 0x99, 0xdb, 0x5c, 0xfe, 0x6c, 0xa1, 0x76, 0xfa, 0xb7, 0xb0, 0xf3, + 0xbf, 0xa0, 0xab, 0x61, 0xe3, 0x40, 0xc3, 0x4e, 0xb9, 0xf1, 0x7c, 0x7e, 0xc2, + 0xbe, 0x03, 0xb1, 0x80, 0xf0, 0xbb, 0x6f, 0x43, 0x4c, 0x2a, 0x65, 0x42, 0xe0, + 0x0e, 0x84, 0x37, 0x3f, 0x4f, 0x46, 0x49, 0xcd, 0xa3, 0x2b, 0xf6, 0x86, 0x66, + 0x61, 0x43, 0xf6, 0x22, 0xaa, 0x48, 0x04, 0x60, 0xb5, 0xaf, 0xac, 0x51, 0x86, + 0x07, 0xcd, 0x9a, 0xf8, 0xbc, 0xd6, 0xb5, 0x8c, 0x30, 0x12, 0x73, 0x16, 0xb2, + 0x5d, 0x5e, 0xa7, 0xbf, 0x6b, 0x0c, 0xab, 0x85, 0x42, 0xff, 0x69, 0xd9, 0xb2, + 0xf1, 0x80, 0xbe, 0x12, 0xed, 0x75, 0x34, 0x4a, 0x39, 0x5a, 0xa1, 0x0f, 0x85, + 0x2f, 0x08, 0x3a, 0xd6, 0x4e, 0xf4, 0x0e, 0x9c, 0x03, 0x09, 0xe9, 0xbb, 0xa5, + 0x4b, 0x8c, 0xb3, 0x3c, 0x95, 0x49, 0x8a, 0x69, 0x53, 0x8d, 0x3a, 0xe5, 0xb2, + 0x5e, 0x24, 0x70, 0x98, 0x30, 0x6f, 0xa8, 0xc7, 0x4a, 0x8e, 0xe5, 0xbc, 0xa9, + 0x41, 0x53, 0x1d, 0x61, 0xaa, 0xc2, 0x7a, 0xab, 0x3d, 0xc5, 0x61, 0x7d, 0x56, + 0x06, 0xc9, 0x57, 0x7a, 0x2a, 0x83, 0x46, 0xe8, 0xd8, 0x5b, 0x32, 0xb8, 0x50, + 0x57, 0x75, 0x10, 0x8d, 0xc8, 0x5e, 0x2a, 0xde, 0x2e, 0xac, 0x1e, 0x63, 0x6e, + 0x1a, 0xf4, 0x05, 0x4c, 0x8b, 0x6f, 0x57, 0x63, 0x2d, 0xf2, 0x69, 0xc3, 0x72, + 0x3b, 0x32, 0x08, 0x72, 0xe4, 0xc5, 0x7b, 0x21, 0x83, 0x58, 0xdc, 0x7e, 0x99, + 0x05, 0xbb, 0x04, 0xed, 0xf9, 0x2e, 0xdf, 0x0d, 0xf6, 0x35, 0xf3, 0xbf, 0x36, + 0x1e, 0x57, 0xa1, 0x32, 0x96, 0xe1, 0x44, 0x7a, 0xf5, 0x08, 0x02, 0x72, 0xd6, + 0x36, 0xe2, 0x75, 0x18, 0xa9, 0x87, 0x6e, 0x15, 0xeb, 0x01, 0xf5, 0xe8, 0xde, + 0xd8, 0x18, 0x92, 0x51, 0x1c, 0xc2, 0x85, 0x1b, 0x00, 0xb8, 0x32, 0x71, 0x2a, + 0x6d, 0x3b, 0xa5, 0x66, 0x03, 0x17, 0xbc, 0xd3, 0x56, 0x76, 0x21, 0xa7, 0xcf, + 0x84, 0x45, 0x58, 0x96, 0x53, 0x26, 0x20, 0x20, 0xc3, 0x3b, 0xf7, 0x80, 0x31, + 0xb8, 0xee, 0x07, 0x07, 0xde, 0x07, 0x20, 0x68, 0xc1, 0x70, 0x57, 0x0b, 0x27, + 0xe6, 0xd9, 0xf5, 0xc6, 0xdd, 0xc3, 0x35, 0x40, 0x2e, 0xfc, 0x54, 0x88, 0x62, + 0xf5, 0xa0, 0x70, 0x94, 0xfd, 0x42, 0x8a, 0x7b, 0xbc, 0x15, 0xd7, 0xb3, 0x8d, + 0x05, 0x36, 0x2c, 0x9c, 0xa9, 0x85, 0xf5, 0x8a, 0x76, 0x64, 0x7d, 0x2b, 0xe4, + 0xc2, 0xcd, 0x6b, 0x3d, 0x17, 0xd6, 0x87, 0x09, 0x71, 0xd7, 0xa0, 0x98, 0xba, + 0xf7, 0x2c, 0x6f, 0x6f, 0x12, 0x14, 0xcf, 0x1f, 0xaa, 0xe4, 0x88, 0x03, 0x7d, + 0xe2, 0x59, 0xd3, 0x41, 0x5c, 0x2f, 0x0d, 0xde, 0xc7, 0x45, 0x70, 0x04, 0xf3, + 0x57, 0x08, 0xd1, 0xec, 0xcc, 0xcc, 0x0d, 0xf6, 0x5a, 0x04, 0x94, 0x3a, 0xd5, + 0xcb, 0xc1, 0x3f, 0x29, 0x5f, 0x02, 0x0f, 0xe0, 0x56, 0xc4, 0x0b, 0x2d, 0x88, + 0xf2, 0x7d, 0xc3, 0x4c, 0xfe, 0xb8, 0x03, 0xbe, 0x34, 0x83, 0xa9, 0xeb, 0xf9, + 0xb5, 0xa9, 0x02, 0x60, 0x57, 0x72, 0x5d, 0x63, 0xea, 0xd2, 0xc0, 0xc0, 0x03, + 0x1f, 0xe2, 0x6a, 0xc1, 0xe7, 0xbd, 0xfc, 0xd6, 0xfa, 0xd8, 0x75, 0x84, 0x2d, + 0x19, 0x4f, 0x33, 0x17, 0x50, 0x46, 0x2c, 0x06, 0xb8, 0xd7, 0x98, 0x2d, 0x67, + 0x99, 0x5e, 0xd5, 0xd3, 0xae, 0x96, 0x02, 0x5a, 0xe0, 0x06, 0x7f, 0x4e, 0xb1, + 0xc7, 0xc9, 0x32, 0x31, 0xbd, 0x39, 0x77, 0x3c, 0xbe, 0x0a, 0x9d, 0x66, 0xb0, + 0xc9, 0xaa, 0x8c, 0xff, 0x6a, 0x37, 0x6e, 0x1f, 0x37, 0x2e, 0xac, 0x6a, 0xc4, + 0x02, 0x6c, 0xc0, 0x94, 0x22, 0x45, 0xd4, 0xc2, 0xdc, 0xf0, 0x2d, 0x76, 0x40, + 0xff, 0xcc, 0x5a, 0x6a, 0xc3, 0xa8, 0x7f, 0x5c, 0x41, 0x15, 0x51, 0xbc, 0xc2, + 0xf2, 0x6c, 0xb9, 0x49, 0x61, 0xd5, 0x3f, 0x95, 0xdd, 0xb1, 0x9a, 0xe9, 0x30, + 0xc8, 0xd7, 0x0f, 0x03, 0x1b, 0x29, 0xa5, 0xdf, 0x99, 0xff, 0x36, 0x69, 0x5e, + 0x80, 0x2c, 0xbc, 0xb6, 0xb5, 0x8c, 0x1b, 0xa7, 0xed, 0x5e, 0xac, 0xfa, 0x76, + 0x41, 0x4a, 0x41, 0xad, 0x4a, 0x44, 0xf7, 0x1f, 0x1b, 0x58, 0x0d, 0x34, 0xc3, + 0xa9, 0x52, 0x92, 0x0b, 0x25, 0x4a, 0x14, 0x5f, 0xea, 0x51, 0x7f, 0x5b, 0x42, + 0xb2, 0xf6, 0x5e, 0xcd, 0x0f, 0x82, 0x59, 0x54, 0x78, 0xd8, 0x0a, 0xe5, 0xc8, + 0xce, 0xea, 0x12, 0xa1, 0x61, 0xcc, 0xbb, 0x5e, 0xac, 0x09, 0x99, 0x0f, 0xc6, + 0x19, 0xa4, 0x60, 0x80, 0x43, 0x6d, 0xbd, 0x08, 0xd7, 0x47, 0x84, 0xaf, 0x00, + 0x2d, 0x58, 0xe0, 0x6f, 0xaf, 0x7f, 0x3c, 0xea, 0xe7, 0xd3, 0x41, 0x9b, 0x1f, + 0xca, 0x26, 0x5a, 0x55, 0x59, 0xcf, 0x9e, 0x2d, 0x3b, 0x60, 0x97, 0x8d, 0x81, + 0xa6, 0x78, 0xb9, 0xed, 0x8e, 0x44, 0x86, 0xb4, 0xd1, 0x46, 0x09, 0xd6, 0xc1, + 0x27, 0xc0, 0xc2, 0xfb, 0xff, 0xe3, 0x0a, 0x60, 0xf7, 0xbf, 0xf1, 0xd9, 0xfb, + 0x83, 0x00, 0xed, 0x00, 0x92, 0x53, 0xba, 0x9b, 0x99, 0x6f, 0xa0, 0x52, 0x41, + 0xb1, 0x0f, 0x5a, 0xc9, 0xa8, 0x40, 0x8e, 0x92, 0x5b, 0x62, 0x6b, 0xb2, 0x1a, + 0x47, 0x1f, 0xe3, 0xbe, 0xde, 0x52, 0xbb, 0xa0, 0x97, 0xb2, 0xa9, 0x9a, 0x9b, + 0xa5, 0xa8, 0x66, 0x58, 0xc3, 0xfd, 0x9e, 0xc5, 0x5b, 0xfa, 0x9b, 0x32, 0x85, + 0x67, 0x25, 0x4a, 0xb3, 0x6d, 0x2c, 0x7f, 0x44, 0xd2, 0xc7, 0xe1, 0x3e, 0xb5, + 0x4b, 0xeb, 0x70, 0xea, 0x8f, 0xa9, 0x4b, 0x6c, 0x6e, 0x01, 0x2d, 0x79, 0xe3, + 0xf5, 0x36, 0x89, 0xc2, 0xb1, 0xa1, 0x8e, 0xaf, 0x2d, 0x47, 0x1d, 0x13, 0xc1, + 0xab, 0x39, 0xd9, 0x19, 0x4a, 0xe8, 0x43, 0xab, 0x1d, 0x28, 0xff, 0xa8, 0xf6, + 0x9d, 0xc7, 0xe1, 0x5c, 0xc3, 0x8b, 0x12, 0xe8, 0xfc, 0xd7, 0x92, 0x55, 0xb7, + 0x21, 0x60, 0x56, 0xd9, 0xed, 0xb7, 0x48, 0x2f, 0xb9, 0x8a, 0xa0, 0x33, 0xb6, + 0x5e, 0x51, 0xc1, 0xa0, 0x8b, 0x8a, 0x11, 0xd8, 0x4d, 0x04, 0x09, 0xb7, 0x34, + 0xf4, 0x52, 0xaa, 0xf0, 0xd6, 0xb1, 0x8f, 0x50, 0x25, 0x86, 0x83, 0xd3, 0xf9, + 0xa7, 0x6d, 0x39, 0x9f, 0xd0, 0x47, 0xee, 0xe2, 0x88, 0xbb, 0x45, 0x85, 0x85, + 0x1d, 0xc9, 0x3e, 0xcc, 0xc6, 0x23, 0x22, 0x92, 0x4c, 0xd1, 0x3b, 0x5d, 0xd4, + 0xee, 0xd6, 0x6e, 0xd8, 0xd9, 0x97, 0x2d, 0x77, 0x26, 0x29, 0xea, 0x64, 0x74, + 0x2e, 0x54, 0x73, 0x39, 0x81, 0xb0, 0x06, 0xc0, 0x62, 0x46, 0x8e, 0x4b, 0xd8, + 0xf7, 0xdd, 0x9a, 0xf6, 0x98, 0xf5, 0x2a, 0xe8, 0x14, 0x63, 0x4e, 0x81, 0xd7, + 0xf3, 0xe0, 0xc4, 0x20, 0x31, 0x7c, 0xac, 0xa9, 0xae, 0x48, 0x11, 0xc6, 0xaf, + 0x06, 0xfe, 0x80, 0xa8, 0xc0, 0x2a, 0xb7, 0xa0, 0x0e, 0x18, 0xe4, 0xa6, 0xaa, + 0x1e, 0xa1, 0xb7, 0x69, 0x45, 0xd2, 0x61, 0x5d, 0x43, 0xac, 0x11, 0x8b, 0x56, + 0xc2, 0xf2, 0x96, 0x0f, 0xe9, 0x3a, 0x02, 0x5f, 0x13, 0xec, 0x91, 0xff, 0xc6, + 0xd2, 0xc3, 0x53, 0x69, 0x9a, 0xbb, 0x09, 0x2d, 0xed, 0xc0, 0x65, 0xdb, 0x8f, + 0xa2, 0x14, 0xdb, 0xc4, 0x64, 0x66, 0xf8, 0x97, 0xb8, 0x8c, 0x58, 0xb3, 0x01, + 0x52, 0x13, 0x3a, 0xa3, 0x83, 0x1a, 0xf3, 0x7c, 0x74, 0xd9, 0x9e, 0x9e, 0x36, + 0xff, 0x70, 0x11, 0xd3, 0x23, 0x83, 0x05, 0x69, 0x15, 0x08, 0xa2, 0xc3, 0xa4, + 0x3e, 0x75, 0x5d, 0xc0, 0x81, 0xb5, 0x11, 0xd6, 0x48, 0x2a, 0x7d, 0xb6, 0x5f, + 0xa9, 0x69, 0x9e, 0xa8, 0x7f, 0xf4, 0x70, 0x99, 0xed, 0x36, 0x37, 0xdb, 0xb0, + 0xa3, 0xd0, 0xef, 0x79, 0x79, 0x6a, 0x8e, 0xf1, 0xe4, 0xd9, 0x4d, 0x42, 0xb4, + 0xbc, 0x2b, 0x4a, 0x03, 0x8a, 0xe6, 0xe4, 0x6b, 0x24, 0xcf, 0xc8, 0x41, 0x53, + 0xd3, 0x1e, 0xaf, 0x89, 0x50, 0x63, 0xa5, 0xca, 0x95, 0x9b, 0xe6, 0x3f, 0x37, + 0xf2, 0xba, 0x0d, 0x43, 0x23, 0x66, 0x73, 0x6d, 0x86, 0x32, 0xfc, 0xe0, 0x72, + 0xb6, 0xae, 0x5b, 0x6f, 0x3f, 0xd5, 0x9d, 0x3f, 0xaf, 0xf6, 0x38, 0x27, 0x5a, + 0x99, 0x2f, 0xef, 0xc8, 0x7e, 0x60, 0xd4, 0x4c, 0x2c, 0xad, 0xc2, 0xb5, 0xc4, + 0x94, 0xe3, 0xe7, 0x2e, 0xb4, 0x59, 0x7c, 0x96, 0xb4, 0x01, 0x67, 0x79, 0x9a, + 0x90, 0x01, 0xa2, 0xed, 0x36, 0x76, 0xa8, 0xb4, 0x03, 0xae, 0x25, 0xff, 0xd7, + 0x72, 0xf7, 0x08, 0x1e, 0x9a, 0x32, 0xbc, 0xc1, 0xc5, 0xe2, 0xed, 0xd4, 0xe2, + 0xa6, 0x57, 0x6b, 0x78, 0x3c, 0xce, 0x3a, 0xae, 0x11, 0xfa, 0x43, 0x22, 0x62, + 0x54, 0x88, 0x56, 0x18, 0x3e, 0xe6, 0x82, 0xd5, 0xdc, 0x31, 0xbe, 0xb3, 0x8f, + 0x06, 0x1c, 0xbd, 0xec, 0xa7, 0x02, 0x1a, 0x44, 0x4e, 0x2d, 0xd4, 0x17, 0xdf, + 0x26, 0xdc, 0xd2, 0x20, 0xf2, 0xb7, 0x31, 0x77, 0x2b, 0x43, 0x9e, 0x96, 0xd6, + 0x14, 0xe1, 0xfa, 0xcb, 0x48, 0x6c, 0x7a, 0x7d, 0x51, 0x71, 0xb1, 0xde, 0x35, + 0x9f, 0x6a, 0xd3, 0xa9, 0x6f, 0x64, 0x9c, 0x96, 0x91, 0x02, 0xa1, 0x96, 0x4f, + 0xb4, 0xb4, 0xa1, 0xa4, 0x27, 0x9c, 0x68, 0xe6, 0xc3, 0x72, 0xe4, 0x21, 0x87, + 0xd7, 0x54, 0xe8, 0x04, 0xa6, 0x16, 0x53, 0x09, 0x20, 0x69, 0xfb, 0x9b, 0x6d, + 0x25, 0x26, 0x68, 0x90, 0x80, 0x8b, 0x01, 0x5d, 0xf2, 0x8c, 0x80, 0x10, 0x65, + 0xda, 0x6f, 0xeb, 0xdc, 0x1a, 0x56, 0xbf, 0xd0, 0x02, 0x62, 0x5a, 0xcf, 0xaa, + 0x53, 0x73, 0xfd, 0xe1, 0x49, 0xc1, 0xcf, 0xc3, 0x64, 0x9b, 0x48, 0x69, 0x69, + 0x6d, 0x44, 0xec, 0xb1, 0x24, 0x79, 0xc5, 0xeb, 0xef, 0x99, 0x5f, 0x10, 0x02, + 0x9f, 0x8b, 0x53, 0x0e, 0xeb, 0x3f, 0xdc, 0x2e, 0x50, 0xe8, 0x75, 0x7f, 0xc0, + 0xbb, 0x9e, 0x26, 0x30, 0x23, 0xdb, 0x82, 0xf8, 0x78, 0xd9, 0xac, 0x7f, 0xfb, + 0x0b, 0xd4, 0x39, 0x1d, 0xf1, 0xd8, 0x79, 0x89, 0x9a, 0x3e, 0xf5, 0x7b, 0xfd, + 0x0d, 0x1f, 0x77, 0x55, 0x64, 0x8e, 0xdd, 0x85, 0xbb, 0x05, 0x2a, 0x6e, 0xdf, + 0x71, 0xcd, 0x26, 0x28, 0xc9, 0x87, 0x42, 0x9f, 0x36, 0xdc, 0x50, 0x5c, 0xcc, + 0x43, 0xf3, 0x0e, 0x7a, 0x86, 0x9c, 0x9e, 0x25, 0x5e, 0x2a, 0xf9, 0xfc, 0xf3, + 0x0c, 0x12, 0x17, 0x96, 0xd1, 0x90, 0x00, 0x09, 0x60, 0xcb, 0x6f, 0xe2, 0xf1, + 0xbf, 0x24, 0x61, 0x18, 0xb4, 0x98, 0xf3, 0x24, 0x7f, 0x9d, 0x48, 0x4c, 0x73, + 0xcf, 0x09, 0x39, 0x30, 0x39, 0xe4, 0x53, 0x26, 0xb8, 0xff, 0xff, 0xb3, 0xe7, + 0xe6, 0x15, 0x9c, 0x46, 0x69, 0x9f, 0x10, 0x07, 0x92, 0xd4, 0x67, 0x29, 0x50, + 0x34, 0x8a, 0x90, 0x55, 0x2e, 0x45, 0x94, 0x3b, 0xee, 0xac, 0xf0, 0x3f, 0x32, + 0x16, 0xf9, 0x4e, 0x27, 0x4d, 0x63, 0xd6, 0x37, 0xd9, 0xf1, 0x90, 0xe8, 0xa2, + 0x66, 0xcd, 0xee, 0xf1, 0x53, 0x53, 0x0b, 0xee, 0x5c, 0xb8, 0x35, 0x52, 0x60, + 0x50, 0x5c, 0x2c, 0x2e, 0x5d, 0x99, 0x0f, 0xff, 0xdc, 0x34, 0xec, 0x0f, 0xf7, + 0xf1, 0xaf, 0x81, 0xb2, 0x4c, 0xed, 0x0e, 0xfa, 0x62, 0x13, 0xda, 0x6c, 0x7c, + 0x60, 0xc4, 0x87, 0xf5, 0xf7, 0xb0, 0x3f, 0x81, 0x60, 0xa0, 0x57, 0xf4, 0x6d, + 0x05, 0xbf, 0x82, 0x18, 0xb3, 0xad, 0xd9, 0xc0, 0x68, 0x93, 0xbd, 0x02, 0xdb, + 0x9b, 0x61, 0x19, 0x1d, 0xfb, 0x13, 0x3b, 0xfa, 0xbe, 0x48, 0x58, 0xe4, 0x7a, + 0x4c, 0xc3, 0x2e, 0x41, 0x6e, 0xc0, 0x8b, 0x8a, 0xc7, 0x91, 0x5a, 0x43, 0x73, + 0x3f, 0x44, 0x06, 0xe9, 0xd9, 0x67, 0xc5, 0x60, 0xf3, 0x44, 0xd7, 0xe9, 0x04, + 0xa2, 0x80, 0x45, 0xd9, 0x9f, 0x3a, 0xf8, 0xc8, 0x2e, 0x97, 0xe1, 0xb9, 0xc1, + 0xb2, 0x05, 0xe5, 0x85, 0xfb, 0xeb, 0xb4, 0x8f, 0xaf, 0x58, 0xf1, 0xb6, 0x5d, + 0xca, 0x24, 0x97, 0xe0, 0x9a, 0x70, 0xaa, 0xd4, 0x86, 0x5f, 0x85, 0x71, 0x5a, + 0x28, 0x0e, 0x18, 0x6f, 0x3f, 0xc1, 0x74, 0x0d, 0x81, 0x84, 0xd3, 0x3e, 0x83, + 0x22, 0x16, 0x95, 0x21, 0xcd, 0xc1, 0x32, 0x21, 0x29, 0x39, 0xc8, 0x4a, 0x10, + 0x89, 0x64, 0xe2, 0xde, 0x74, 0xb6, 0xea, 0x55, 0xb4, 0xcb, 0x8f, 0x6f, 0x9b, + 0xee, 0x98, 0xb1, 0x0d, 0x41, 0x51, 0x09, 0x45, 0x5f, 0x48, 0xb7, 0x76, 0x08, + 0x2d, 0xc3, 0x0b, 0x4b, 0xc7, 0x34, 0x77, 0x07, 0x55, 0x11, 0x70, 0x03, 0x08, + 0x15, 0x8c, 0xe2, 0xf2, 0xf9, 0xbf, 0x0f, 0x69, 0x1b, 0x2c, 0xe5, 0x3e, 0x61, + 0x14, 0x2c, 0xb7, 0x40, 0xc1, 0x5b, 0x7b, 0x62, 0x3c, 0xf4, 0x8b, 0x3f, 0x7b, + 0xfe, 0xfa, 0x31, 0xbc, 0xdc, 0x66, 0x5c, 0x6d, 0x71, 0x23, 0xe9, 0x53, 0x50, + 0x81, 0x13, 0x75, 0x94, 0x7b, 0x05, 0x5a, 0x43, 0xdb, 0x07, 0xe0, 0x3f, 0x33, + 0x62, 0x7d, 0xf5, 0xc6, 0x38, 0xbf, 0xad, 0x95, 0x6d, 0xdc, 0x1e, 0xa7, 0xd7, + 0x62, 0x0a, 0x20, 0xf2, 0x79, 0x2f, 0x63, 0x81, 0x7a, 0x1c, 0xf3, 0x25, 0x80, + 0xd0, 0x42, 0x74, 0x23, 0x4a, 0xf2, 0xa5, 0x1b, 0x56, 0xbb, 0x68, 0xa2, 0x9e, + 0x43, 0xa9, 0x54, 0x14, 0x2b, 0xa4, 0xca, 0x68, 0x23, 0xbd, 0xe9, 0x05, 0x3d, + 0x72, 0xfd, 0xad, 0xbc, 0x61, 0xad, 0x59, 0x36, 0xc5, 0x3f, 0xdd, 0x75, 0x79, + 0x44, 0x6d, 0x11, 0xc4, 0x46, 0x07, 0xf4, 0x16, 0x30, 0xe4, 0xc0, 0x89, 0x15, + 0xe6, 0x31, 0x77, 0x15, 0x50, 0xe9, 0xce, 0x1f, 0xca, 0x2c, 0x63, 0xfe, 0x06, + 0xb7, 0x98, 0x9d, 0x58, 0x4f, 0xa7, 0xd7, 0x82, 0xa8, 0x8c, 0x1e, 0x7d, 0x64, + 0xb6, 0xfb, 0xf5, 0x5e, 0x35, + ], + script_code: Script(vec![0x6a, 0x53, 0x53, 0x63]), + transparent_input: None, + hash_type: 1, + amount: 379068098637835, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x92, 0xe7, 0xb4, 0x8f, 0x32, 0x81, 0x87, 0x71, 0x26, 0x87, 0xaf, 0x4d, 0xc1, + 0x7a, 0x73, 0xfe, 0x0a, 0x70, 0xac, 0x07, 0x8d, 0x24, 0xcd, 0xcd, 0xd4, 0x58, + 0xa3, 0xd6, 0x86, 0x61, 0xec, 0x0a, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x24, 0x9d, 0xf0, 0x57, + 0x01, 0xda, 0xb0, 0x31, 0xc4, 0xba, 0xc1, 0xea, 0x26, 0x7a, 0x29, 0x96, 0xa2, + 0x02, 0x8d, 0x1e, 0x6a, 0x0f, 0x80, 0xa3, 0x84, 0x7c, 0x53, 0x1d, 0xba, 0x96, + 0xee, 0x65, 0xa2, 0x41, 0x89, 0xbd, 0x09, 0x52, 0xac, 0x65, 0x63, 0x65, 0xac, + 0x00, 0x65, 0x00, 0xb2, 0xa4, 0xf9, 0x51, 0xef, 0x8f, 0x49, 0x7d, 0xff, 0xf2, + 0xf2, 0xf2, 0x71, 0xea, 0xb8, 0x9c, 0x62, 0x8e, 0x18, 0xb5, 0xfc, 0xb4, 0x38, + 0x82, 0x53, 0x7e, 0xaf, 0x6a, 0xd2, 0xa6, 0xb1, 0x75, 0x46, 0x33, 0xca, 0xa8, + 0x6b, 0xf2, 0xc7, 0x6f, 0x07, 0x53, 0x63, 0x6a, 0x6a, 0x65, 0x6a, 0x53, 0xa2, + 0x21, 0x0c, 0x27, 0x01, 0xea, 0x6c, 0x54, 0x2c, 0xc8, 0xc7, 0x06, 0x00, 0x00, + 0xe0, 0x11, 0x29, 0xf0, 0x3a, 0x1e, 0x9c, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, + 0x07, 0x62, 0xc0, 0xb1, 0xc6, 0x58, 0x55, 0xde, 0xba, 0x84, 0x22, 0xca, 0x4b, + 0x88, 0xab, 0xee, 0xa6, 0xa4, 0x38, 0x2c, 0xf1, 0x6c, 0xcd, 0x6d, 0xc7, 0xc3, + 0x7c, 0x44, 0xe5, 0x49, 0xc4, 0x53, 0x48, 0x19, 0xac, 0xd8, 0xbb, 0x0a, 0x02, + 0xa5, 0xfa, 0x7a, 0x1c, 0x1d, 0x38, 0x06, 0xfb, 0xc3, 0x40, 0x7f, 0xd7, 0xda, + 0x93, 0xfd, 0x0d, 0xe6, 0x40, 0x0d, 0x3a, 0xb8, 0x97, 0x74, 0x85, 0xcd, 0xdf, + 0xbe, 0xd5, 0x93, 0x2f, 0x50, 0x7b, 0x79, 0x94, 0x7a, 0xdb, 0x2f, 0xad, 0x37, + 0x61, 0x5a, 0xa7, 0x17, 0xdb, 0x5f, 0x29, 0x80, 0x99, 0xf2, 0x0f, 0x26, 0x3b, + 0x35, 0x9a, 0x11, 0x51, 0xa6, 0xb7, 0x5c, 0x01, 0x36, 0x5e, 0xb1, 0x54, 0xae, + 0x42, 0x14, 0x0d, 0x6e, 0x10, 0x34, 0x2f, 0x14, 0xf3, 0x4d, 0xc3, 0x3e, 0x07, + 0xff, 0x0e, 0x4d, 0x1a, 0x6b, 0xe3, 0x75, 0xb3, 0x2f, 0x84, 0xb9, 0x2e, 0x5d, + 0x81, 0xeb, 0xb6, 0x39, 0xc4, 0xf2, 0x7e, 0x71, 0x5a, 0xa4, 0x2c, 0xc7, 0x57, + 0x07, 0xd4, 0xeb, 0xd1, 0xbb, 0xfb, 0xe8, 0xf9, 0x0f, 0xc7, 0xc9, 0x53, 0xe7, + 0xa9, 0x71, 0x5e, 0x65, 0xaf, 0x82, 0x67, 0x37, 0x3d, 0x34, 0x51, 0x67, 0x4f, + 0xf0, 0x84, 0xef, 0xd9, 0x2c, 0xcf, 0x3b, 0xcc, 0x7a, 0xca, 0x14, 0x67, 0xb6, + 0x32, 0x7e, 0x4f, 0x95, 0x22, 0xb2, 0xcc, 0x57, 0x9a, 0x7a, 0x8f, 0xff, 0x7c, + 0xa7, 0xcf, 0x14, 0x5d, 0xfc, 0x13, 0xea, 0xfc, 0x34, 0x15, 0x3b, 0x2c, 0x3e, + 0x8a, 0xfb, 0xe5, 0x34, 0x44, 0xd0, 0xc7, 0x3b, 0x3b, 0xd5, 0xbc, 0x87, 0x0b, + 0x01, 0xcd, 0x45, 0x79, 0x11, 0xe3, 0x56, 0x31, 0x3f, 0xd1, 0xda, 0xfb, 0x4c, + 0x81, 0x51, 0x63, 0x4a, 0x01, 0xaf, 0xf7, 0xcf, 0x11, 0x6d, 0x43, 0x3c, 0x3d, + 0x2b, 0x3a, 0xdd, 0xa9, 0xce, 0xbe, 0x18, 0xf7, 0xd1, 0x72, 0x44, 0x3e, 0x5e, + 0x7b, 0x5a, 0xc9, 0xab, 0xe8, 0xdb, 0x22, 0x56, 0xd7, 0xeb, 0xe2, 0xff, 0x28, + 0x02, 0x09, 0x39, 0x50, 0x38, 0x70, 0x59, 0x7b, 0x9a, 0x95, 0x58, 0x92, 0xc7, + 0x38, 0x02, 0x50, 0xa2, 0xd4, 0x2e, 0xc9, 0x2b, 0xe7, 0x23, 0xfe, 0xdf, 0x2f, + 0x2e, 0xde, 0x5a, 0x47, 0x2a, 0xa1, 0xe7, 0x4f, 0x33, 0xad, 0x41, 0x90, 0x15, + 0x44, 0xed, 0xbb, 0xe3, 0xac, 0x46, 0x4c, 0xf4, 0x03, 0x19, 0x60, 0x15, 0xf4, + 0xf2, 0x2a, 0xc2, 0xb8, 0xfc, 0x01, 0x49, 0x6b, 0xea, 0xb4, 0xd4, 0x59, 0x07, + 0xf4, 0x79, 0x81, 0x2a, 0x25, 0x94, 0x31, 0xa2, 0xcb, 0xc9, 0x3d, 0x4f, 0x3b, + 0x84, 0xe4, 0x0b, 0x36, 0x60, 0x20, 0x27, 0x3a, 0x67, 0x52, 0xe5, 0x01, 0xaf, + 0x6f, 0xf1, 0xb7, 0x8d, 0xdc, 0x81, 0x7e, 0x6e, 0xa3, 0x51, 0xd6, 0x00, 0x6b, + 0xec, 0xf8, 0xd2, 0xff, 0xb0, 0x39, 0x90, 0xf6, 0x77, 0x74, 0xa8, 0x1e, 0x05, + 0xb7, 0xf4, 0xbb, 0xad, 0x85, 0x77, 0xfa, 0x27, 0xc9, 0xde, 0x64, 0xe1, 0xb1, + 0x1d, 0xcf, 0x38, 0x4f, 0x59, 0x56, 0x44, 0x37, 0x48, 0x75, 0x5a, 0x9f, 0xc6, + 0xf2, 0xa0, 0x03, 0x10, 0xc3, 0x65, 0x7e, 0xba, 0xc0, 0x3b, 0xfc, 0x0b, 0x58, + 0x7b, 0xef, 0x2f, 0x45, 0xec, 0x8a, 0xcd, 0xaa, 0x51, 0xc1, 0x43, 0xb0, 0xcb, + 0x25, 0xb9, 0x14, 0x2c, 0x61, 0xbd, 0x79, 0x0a, 0x80, 0x03, 0xc2, 0x3f, 0x90, + 0xcc, 0x03, 0x49, 0x5b, 0x51, 0xe4, 0xd2, 0x84, 0x3e, 0x55, 0x7f, 0x9e, 0x25, + 0x45, 0x10, 0x8c, 0x6c, 0x6f, 0xae, 0x35, 0x9f, 0x64, 0x5c, 0x27, 0x68, 0x91, + 0xc0, 0xdc, 0xab, 0x03, 0xaf, 0x18, 0x77, 0x00, 0xc0, 0x82, 0xdc, 0x47, 0x77, + 0x40, 0xfb, 0x3f, 0x2c, 0xd7, 0xbb, 0x59, 0xfb, 0x35, 0x85, 0x54, 0xe9, 0x4c, + 0x7e, 0x67, 0x8c, 0xe0, 0x1a, 0xeb, 0xf9, 0x4e, 0x51, 0x5e, 0x03, 0x72, 0x29, + 0x67, 0x99, 0x5a, 0xea, 0x85, 0x8d, 0x64, 0xe7, 0x78, 0x9f, 0xf3, 0x06, 0x36, + 0x95, 0x77, 0x22, 0x81, 0x80, 0x32, 0x6a, 0x5b, 0x0a, 0xf4, 0x75, 0xe2, 0x7a, + 0x54, 0xb2, 0x07, 0xb4, 0x03, 0x92, 0xe3, 0x76, 0x17, 0x0e, 0x3f, 0xb0, 0x05, + 0x02, 0x82, 0x61, 0xc9, 0x9c, 0x2d, 0xbd, 0x0e, 0xed, 0xee, 0x87, 0x1c, 0x1c, + 0x0f, 0x48, 0xb8, 0xe9, 0xb8, 0xe4, 0xbe, 0x77, 0xd1, 0xb7, 0x37, 0xfe, 0x21, + 0xf0, 0xfa, 0x5a, 0x18, 0xeb, 0xb5, 0x27, 0x55, 0xb5, 0xa6, 0xcf, 0x61, 0x30, + 0xfb, 0x56, 0x94, 0x4c, 0xfa, 0xb8, 0x75, 0x27, 0xc2, 0x50, 0xd1, 0x13, 0xb2, + 0x9b, 0xca, 0xc9, 0xaa, 0xa1, 0x0c, 0x2e, 0x7d, 0xe4, 0x15, 0xed, 0xb0, 0x80, + 0x6c, 0x6d, 0xa0, 0x30, 0x20, 0xa1, 0x34, 0xca, 0x7e, 0xcd, 0xc8, 0xda, 0x1b, + 0xd5, 0x7a, 0x37, 0xf5, 0x5a, 0x46, 0x94, 0x0b, 0x45, 0xb2, 0x41, 0xb1, 0xc1, + 0x6e, 0xe1, 0x00, 0x92, 0x7d, 0x1b, 0xd8, 0x60, 0xd4, 0x45, 0xa9, 0xde, 0x50, + 0xd4, 0xc3, 0x84, 0xd6, 0xe1, 0xd0, 0x01, 0x08, 0x02, 0x6c, 0x0e, 0xa5, 0xeb, + 0xbf, 0x0b, 0x72, 0xfb, 0xf5, 0xc3, 0x70, 0xbc, 0xe1, 0x8d, 0x3a, 0xcb, 0xc4, + 0x65, 0x99, 0x09, 0x9b, 0xaa, 0xe1, 0xd8, 0x02, 0xf7, 0x73, 0x33, 0x49, 0x4a, + 0x7a, 0xe1, 0x30, 0xfe, 0x86, 0xe8, 0xf8, 0x18, 0xf9, 0x26, 0x1a, 0x2d, 0xad, + 0xb4, 0x12, 0x52, 0x29, 0xba, 0x0f, 0xfc, 0x0e, 0x70, 0x90, 0x32, 0x44, 0x30, + 0xb5, 0x21, 0xa9, 0x0d, 0x22, 0x4a, 0xb7, 0xa1, 0x02, 0x4e, 0x1d, 0x89, 0x3e, + 0x74, 0x04, 0xfe, 0xdb, 0x34, 0x8e, 0x4d, 0x5e, 0x22, 0x35, 0xc5, 0x9a, 0x78, + 0x76, 0xa0, 0xfc, 0x60, 0x14, 0x5c, 0x6a, 0x00, 0x96, 0x87, 0x68, 0x44, 0x60, + 0x27, 0x1e, 0xe1, 0x33, 0xa4, 0x37, 0xfe, 0x52, 0xfb, 0x6c, 0xfb, 0xa9, 0x7f, + 0xce, 0xc1, 0x61, 0xdf, 0x51, 0x5d, 0xde, 0x90, 0x5a, 0x24, 0xda, 0x6d, 0x37, + 0xbd, 0xc3, 0x40, 0x44, 0xa9, 0x55, 0xe6, 0x82, 0xb4, 0x74, 0x71, 0xca, 0x1e, + 0x8c, 0x78, 0xc5, 0x1e, 0xd3, 0x77, 0xcd, 0x4a, 0xfa, 0x89, 0x4b, 0xd9, 0xbd, + 0x12, 0xe7, 0x07, 0x15, 0x6d, 0xa0, 0x72, 0x6f, 0x7c, 0xf5, 0x72, 0x9f, 0xab, + 0xe3, 0x72, 0x16, 0x04, 0x63, 0xfe, 0x04, 0x29, 0x24, 0x4d, 0x06, 0x74, 0x89, + 0xba, 0x5d, 0x09, 0x47, 0x2e, 0xcd, 0x9b, 0xcd, 0xc4, 0xd5, 0xe4, 0xdf, 0x10, + 0x1e, 0x18, 0x9d, 0xb8, 0x46, 0x3e, 0xb5, 0x38, 0x30, 0x7b, 0x58, 0x7d, 0xef, + 0xf7, 0x8d, 0xe9, 0xc7, 0x3a, 0xf2, 0x80, 0x80, 0xb2, 0xfd, 0x05, 0x00, 0x3e, + 0x11, 0xd3, 0xe1, 0xb3, 0x29, 0x9d, 0xc9, 0x52, 0x1f, 0x8b, 0x51, 0x3b, 0xad, + 0xb0, 0x10, 0xe9, 0x1b, 0xfe, 0xb9, 0x1b, 0x0b, 0x2a, 0x6c, 0xb1, 0x29, 0xc2, + 0xe8, 0x25, 0xa5, 0x97, 0xb8, 0xfb, 0x75, 0xbc, 0x56, 0x2d, 0x65, 0x4d, 0x62, + 0x10, 0x46, 0x40, 0xdd, 0x74, 0xe5, 0x6c, 0xd1, 0x4b, 0xaa, 0xba, 0x56, 0x5b, + 0x84, 0xb8, 0x45, 0xe1, 0x63, 0xd1, 0xca, 0xef, 0x25, 0x33, 0xc3, 0x98, 0x16, + 0x37, 0x20, 0x4f, 0x96, 0xa5, 0x9c, 0x8e, 0x80, 0x24, 0xd9, 0x04, 0x1b, 0x20, + 0x29, 0xe9, 0x4c, 0x15, 0x24, 0x5f, 0x1a, 0x95, 0x88, 0x40, 0xba, 0x3f, 0x38, + 0x0a, 0x4d, 0x20, 0xf1, 0x18, 0x4e, 0x77, 0x82, 0x7d, 0xe3, 0xff, 0x8f, 0x3d, + 0x73, 0x45, 0x9a, 0xfe, 0x24, 0x1f, 0x72, 0x3c, 0x08, 0x48, 0x23, 0x23, 0x0e, + 0x00, 0x3d, 0x3d, 0x21, 0xe5, 0x35, 0x01, 0xec, 0x04, 0x99, 0xb0, 0x83, 0xa7, + 0xda, 0xd6, 0x85, 0xc5, 0x71, 0x27, 0xf4, 0xde, 0x64, 0x73, 0x3a, 0x88, 0x0c, + 0x2d, 0xb2, 0x8f, 0xda, 0xab, 0xf1, 0xb5, 0x42, 0xd2, 0x05, 0xf6, 0x64, 0xa3, + 0x51, 0x35, 0x71, 0x27, 0x11, 0xdc, 0xcc, 0xd9, 0x31, 0xa5, 0x0b, 0x9c, 0x56, + 0x61, 0x88, 0x23, 0x60, 0xd4, 0xca, 0xc0, 0x04, 0x76, 0x81, 0xbc, 0x2e, 0x2b, + 0x3b, 0xf6, 0xc9, 0x97, 0x60, 0xd7, 0xcf, 0xb4, 0xfa, 0x21, 0x39, 0x43, 0x77, + 0xa4, 0x55, 0x1c, 0x76, 0xd1, 0xf7, 0x5a, 0xc0, 0x3c, 0x26, 0x20, 0x54, 0xdf, + 0xfd, 0x79, 0xa9, 0xde, 0xd0, 0x5e, 0x88, 0x89, 0x58, 0x19, 0x9e, 0xea, 0x45, + 0x01, 0xe2, 0x99, 0x0a, 0x53, 0xa5, 0xcd, 0x2a, 0x46, 0xa4, 0x01, 0x57, 0x65, + 0x88, 0xfd, 0x7d, 0x05, 0x8a, 0x26, 0xf2, 0x84, 0x38, 0xe5, 0x78, 0x2f, 0x45, + 0xac, 0x1d, 0x07, 0xf6, 0xf6, 0xf5, 0xed, 0x73, 0x74, 0x1d, 0x57, 0x85, 0x83, + 0x7a, 0x6b, 0x84, 0x4b, 0x47, 0x47, 0x75, 0x71, 0x8c, 0x29, 0xdd, 0x99, 0x08, + 0x4e, 0x9f, 0x88, 0xef, 0x15, 0x3a, 0x83, 0x29, 0xf5, 0x32, 0xa6, 0x90, 0x17, + 0xdc, 0x3a, 0x97, 0xed, 0x75, 0x43, 0x67, 0x72, 0x30, 0x98, 0xe5, 0x76, 0x58, + 0x40, 0xb0, 0x22, 0x89, 0x72, 0x44, 0x74, 0x5f, 0xbb, 0xbb, 0x30, 0xa7, 0xcb, + 0x54, 0xfa, 0x05, 0x11, 0x16, 0x6e, 0x95, 0x44, 0x12, 0x20, 0x00, 0x61, 0x0b, + 0xd2, 0xaa, 0xcb, 0xd8, 0x23, 0x25, 0xa5, 0x9b, 0x95, 0x15, 0x4e, 0xcd, 0x82, + 0xc8, 0x8d, 0x23, 0xab, 0xd1, 0xe2, 0x07, 0x70, 0xff, 0xb8, 0xaa, 0xbf, 0x83, + 0xfc, 0x07, 0x34, 0x96, 0x4c, 0xcd, 0x41, 0x1d, 0x1c, 0x93, 0x57, 0x14, 0xe2, + 0x4a, 0xab, 0x56, 0x6f, 0x4f, 0x08, 0x42, 0x40, 0x14, 0xc4, 0xec, 0xa9, 0x1b, + 0x59, 0x0f, 0x08, 0x2b, 0x47, 0x3f, 0x36, 0x1c, 0x87, 0x41, 0x5d, 0x37, 0xbd, + 0x20, 0xd7, 0x0f, 0xd0, 0xb5, 0x2b, 0x6d, 0xdf, 0x18, 0x65, 0xf7, 0x66, 0x70, + 0x2e, 0x32, 0xb0, 0x5b, 0x3c, 0xf1, 0x63, 0x0e, 0xe8, 0x59, 0x7a, 0xae, 0x19, + 0x63, 0x3f, 0x35, 0x16, 0xa8, 0x55, 0x5a, 0xc5, 0xbe, 0x32, 0xc6, 0x75, 0xbe, + 0x18, 0x17, 0xef, 0xbf, 0xfd, 0x93, 0x69, 0x04, 0x1a, 0x08, 0x9c, 0x28, 0x3f, + 0x19, 0x64, 0x99, 0x68, 0xc2, 0x49, 0x8c, 0xde, 0x56, 0xf5, 0x00, 0x43, 0x4f, + 0x28, 0x0d, 0x77, 0xa9, 0xc6, 0x2e, 0x43, 0xcb, 0xd3, 0xf1, 0x36, 0xa4, 0xc6, + 0xa0, 0x0a, 0x43, 0xe6, 0xed, 0x53, 0x0c, 0xb2, 0xe8, 0xae, 0x83, 0x88, 0x60, + 0xad, 0xc8, 0x8a, 0xac, 0xc7, 0xbd, 0x6a, 0x00, 0xae, 0x0c, 0x19, 0xff, 0x45, + 0x33, 0xa4, 0x85, 0xef, 0xde, 0x08, 0x2b, 0x5f, 0x4d, 0x1f, 0x7a, 0x8e, 0xbe, + 0x7e, 0xd8, 0x2b, 0x7b, 0x05, 0xa8, 0xcf, 0xe1, 0xe3, 0x73, 0x45, 0x9f, 0x1b, + 0xdc, 0xbf, 0x95, 0x25, 0x74, 0x7e, 0x8c, 0x95, 0x08, 0xa5, 0x55, 0xfa, 0xcb, + 0x79, 0x87, 0x40, 0xe0, 0xbd, 0xf9, 0x94, 0xd9, 0x73, 0x9b, 0xbe, 0x55, 0x38, + 0xa0, 0xae, 0x0f, 0x07, 0x6c, 0x58, 0x2c, 0x0f, 0x5b, 0xa8, 0x78, 0xb9, 0x9b, + 0x82, 0x49, 0xdb, 0x1d, 0x7e, 0x95, 0x05, 0x6c, 0x98, 0xaf, 0x08, 0x3d, 0x98, + 0xcb, 0x0e, 0xd9, 0xe3, 0xf7, 0x43, 0x6e, 0x1c, 0x76, 0x43, 0x76, 0x6f, 0x96, + 0x6b, 0x83, 0xe9, 0x99, 0x20, 0x6e, 0xbd, 0x13, 0x93, 0xb9, 0xb2, 0xa7, 0xf4, + 0x14, 0x48, 0x0f, 0xa0, 0x17, 0x48, 0x00, 0x69, 0xf8, 0x5c, 0x77, 0x49, 0xc4, + 0x35, 0xae, 0x2f, 0xba, 0x2d, 0xdc, 0x10, 0x38, 0xd5, 0x47, 0xd8, 0x48, 0x54, + 0x81, 0x7e, 0xf3, 0x96, 0x35, 0xc2, 0x98, 0x27, 0xaa, 0xd8, 0x67, 0x26, 0xc9, + 0xad, 0xe3, 0xb2, 0x65, 0xb9, 0x08, 0x6c, 0x8b, 0x5b, 0x75, 0xef, 0x56, 0xfe, + 0x4b, 0xd8, 0xb4, 0xd6, 0x28, 0x93, 0x89, 0x5b, 0x3f, 0xd2, 0x73, 0x4f, 0xda, + 0xc4, 0x64, 0x15, 0x6d, 0x7e, 0x5e, 0xbc, 0x7e, 0xcf, 0x1d, 0x83, 0xb8, 0x6f, + 0x65, 0x96, 0x37, 0xe3, 0xb1, 0x42, 0xc1, 0x64, 0x96, 0x3b, 0x8c, 0xdc, 0xf4, + 0xba, 0x4f, 0x40, 0x35, 0xdf, 0xfc, 0x5a, 0x78, 0x94, 0x58, 0x84, 0x77, 0x81, + 0x91, 0x8a, 0xc7, 0x2f, 0xc1, 0x8b, 0xbb, 0xf5, 0x11, 0x00, 0x32, 0xe6, 0x6d, + 0x75, 0xb3, 0x17, 0x1e, 0xf4, 0xb5, 0x13, 0x29, 0x01, 0x64, 0xa7, 0x7b, 0x42, + 0xb0, 0xa4, 0xcf, 0xb8, 0x96, 0x39, 0xab, 0x23, 0x84, 0x5e, 0x1a, 0xa2, 0xa4, + 0x52, 0xf3, 0x73, 0x1c, 0x8c, 0xb6, 0x50, 0x82, 0xa6, 0x22, 0xa7, 0xc2, 0xe0, + 0x01, 0x3e, 0xa4, 0x7d, 0x0b, 0xdd, 0x42, 0xd6, 0x99, 0x04, 0x66, 0x64, 0x9a, + 0x90, 0x5c, 0x68, 0x4c, 0x32, 0x51, 0x71, 0x6d, 0x61, 0xf7, 0x60, 0xd5, 0x3d, + 0xe6, 0xe3, 0xf7, 0x90, 0xfb, 0xa7, 0xf5, 0xf1, 0xf4, 0xde, 0x26, 0x71, 0x13, + 0xbd, 0xfc, 0xd7, 0x42, 0x28, 0x22, 0x33, 0x0b, 0x32, 0xd5, 0x8e, 0x67, 0x77, + 0x76, 0x5f, 0x22, 0xa4, 0x11, 0x63, 0x44, 0xee, 0xb6, 0x5b, 0x2e, 0xc5, 0x16, + 0x39, 0x3a, 0xb3, 0x75, 0x1b, 0x53, 0x56, 0xd2, 0xb0, 0xc9, 0x50, 0x0c, 0x0f, + 0x3e, 0x46, 0x91, 0x81, 0x03, 0x5b, 0xc3, 0x66, 0x0f, 0x0b, 0x8f, 0x9f, 0xbe, + 0x6e, 0x40, 0xb5, 0xe8, 0x9c, 0xb7, 0x9b, 0x06, 0x37, 0x14, 0xca, 0x75, 0xe7, + 0x2e, 0x2e, 0x10, 0x0a, 0x10, 0xd6, 0x3b, 0xf7, 0x84, 0xdf, 0x08, 0x20, 0xef, + 0x25, 0xf8, 0xef, 0x40, 0xfe, 0x5f, 0x05, 0xfb, 0x95, 0x68, 0x3f, 0x91, 0x05, + 0xff, 0x3c, 0xb2, 0xd2, 0x19, 0xab, 0x76, 0x60, 0x5a, 0x06, 0x4f, 0x69, 0x21, + 0x9f, 0x1d, 0xc0, 0xd0, 0x0b, 0x3b, 0x48, 0x64, 0x2f, 0x97, 0x0d, 0xc0, 0x0c, + 0xca, 0x4b, 0x8b, 0x43, 0x30, 0x8b, 0xe1, 0x82, 0x86, 0xec, 0x5a, 0x42, 0x88, + 0xd6, 0x00, 0xa3, 0x78, 0x5c, 0xb6, 0x22, 0xd4, 0x68, 0xa4, 0xc6, 0x96, 0x9b, + 0x37, 0x92, 0xf2, 0x48, 0x50, 0x27, 0xd0, 0xad, 0x9a, 0xa4, 0xa9, 0xc2, 0xcc, + 0x97, 0x2f, 0x9e, 0xe5, 0x19, 0x0a, 0x95, 0xb1, 0xeb, 0x05, 0x8d, 0xdd, 0xd8, + 0xc0, 0x8e, 0x7d, 0x75, 0x3f, 0x5e, 0x01, 0x1b, 0x2b, 0xcf, 0xee, 0x1d, 0x52, + 0xc1, 0xc4, 0xf2, 0xca, 0xcd, 0xa3, 0x0b, 0xdb, 0x69, 0x30, 0x65, 0x3c, 0x0c, + 0xc4, 0x48, 0x6e, 0x60, 0xe8, 0x9f, 0xa8, 0x49, 0xb3, + ], + script_code: Script(vec![0x53, 0x52]), + transparent_input: Some(0), + hash_type: 3, + amount: 1437866676382615, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0xd8, 0xe9, 0xb9, 0x72, 0xb2, 0x89, 0x8e, 0xfc, 0xca, 0x8e, 0x96, 0xbc, 0x98, + 0x70, 0x00, 0x8c, 0xdb, 0xc1, 0x9d, 0x45, 0xb7, 0x8d, 0x09, 0xef, 0xb1, 0x02, + 0xf2, 0xd7, 0x0d, 0xba, 0x01, 0xad, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x01, 0x87, 0xda, 0xa7, 0x31, + 0xf5, 0x70, 0xa7, 0xa4, 0x06, 0x0a, 0xf0, 0xce, 0x70, 0x0d, 0x31, 0xbc, 0xa7, + 0xe7, 0x4b, 0x3e, 0x3b, 0xa3, 0xd0, 0xe8, 0xa6, 0x39, 0x2a, 0x06, 0x2b, 0x8e, + 0x86, 0xd9, 0xd7, 0xd0, 0x0b, 0x21, 0x02, 0x65, 0x53, 0x06, 0x2e, 0x06, 0xb1, + 0x01, 0x30, 0x11, 0xff, 0x08, 0xf0, 0x83, 0x05, 0x00, 0x09, 0x63, 0x6a, 0x52, + 0x63, 0x51, 0x63, 0x00, 0x6a, 0xac, 0x9a, 0xbc, 0xef, 0x2a, 0x99, 0x08, 0x73, + 0x19, 0x00, + ], + script_code: Script(vec![0x63]), + transparent_input: None, + hash_type: 1, + amount: 1993227025071196, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x2b, 0x62, 0xff, 0x0c, 0x8d, 0xec, 0x4d, 0xf1, 0x8b, 0x99, 0x56, 0x61, 0x5b, + 0x57, 0x4d, 0xda, 0x39, 0x42, 0xfe, 0x45, 0x2d, 0x91, 0x78, 0xb0, 0xbb, 0xb2, + 0xea, 0xee, 0x4d, 0xe4, 0x4a, 0x8c, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x7c, 0x82, 0x97, 0x7c, + 0x0f, 0xf7, 0x97, 0x09, 0x3e, 0x2c, 0x1f, 0x3a, 0xe8, 0x55, 0xf6, 0x5a, 0xea, + 0x91, 0xe1, 0x31, 0x2f, 0xc6, 0xb8, 0xa4, 0x35, 0x1a, 0x2e, 0xc0, 0x3e, 0x02, + 0xe5, 0xd0, 0x2f, 0x53, 0x35, 0x4b, 0x05, 0x6a, 0x53, 0x52, 0x63, 0x6a, 0x82, + 0xcd, 0x1f, 0x55, 0xeb, 0xca, 0x57, 0xb6, 0x33, 0x7c, 0x85, 0x93, 0x8a, 0x79, + 0x81, 0x3d, 0x20, 0x21, 0xd6, 0x09, 0x4c, 0x68, 0xb3, 0x75, 0xe9, 0x84, 0xf6, + 0x83, 0x93, 0x30, 0x08, 0x71, 0xe3, 0x48, 0xfc, 0x52, 0x36, 0xcc, 0xa6, 0x33, + 0x05, 0xac, 0x63, 0x65, 0x51, 0x63, 0x41, 0x87, 0x01, 0xff, 0x01, 0x86, 0xd2, + 0x6f, 0xee, 0x28, 0xca, 0x06, 0x00, 0x01, 0xac, 0x5a, 0xa7, 0x27, 0xab, 0x79, + 0x85, 0xda, 0x0e, 0x00, + ], + script_code: Script(vec![0x65, 0x53, 0x51]), + transparent_input: Some(1), + hash_type: 130, + amount: 449567650863240, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x49, 0x3d, 0x49, 0xc3, 0xe2, 0x22, 0x5d, 0x11, 0xc4, 0x64, 0x05, 0x18, 0x20, + 0x14, 0x76, 0x25, 0xf3, 0x90, 0x9f, 0xa7, 0x18, 0x9f, 0x61, 0xc7, 0xea, 0xec, + 0xfc, 0x6d, 0xad, 0x2e, 0x82, 0x03, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x00, 0x02, 0xe9, 0x6a, 0xa7, + 0x3c, 0xd9, 0xd1, 0x04, 0x00, 0x02, 0x00, 0x53, 0x06, 0xf6, 0x99, 0xe0, 0xb1, + 0x9a, 0x04, 0x00, 0x06, 0xac, 0x65, 0x65, 0x51, 0xac, 0x51, 0x0e, 0x68, 0xae, + 0x38, 0x75, 0x05, 0x51, 0x13, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x92, 0xf1, 0x35, + 0xbf, 0x5f, 0x68, 0x78, 0x7d, 0x37, 0x0c, 0xa8, 0xc4, 0xc4, 0x07, 0x4d, 0xc5, + 0xd6, 0x01, 0xae, 0x90, 0x49, 0x54, 0x37, 0xc3, 0xc2, 0xd4, 0x8a, 0x3d, 0x96, + 0x66, 0x83, 0xac, 0x05, 0x16, 0x0b, 0x7a, 0x84, 0xea, 0xa7, 0xaa, 0xb7, 0x40, + 0x09, 0xe5, 0x7a, 0x85, 0xf7, 0xbf, 0x68, 0xa2, 0xe4, 0x82, 0x00, 0x0f, 0x82, + 0x9c, 0x54, 0x50, 0x73, 0xa1, 0x5d, 0x5c, 0xd0, 0xfc, 0xc5, 0x74, 0x39, 0xa4, + 0x35, 0x0e, 0xaf, 0x09, 0x8d, 0xfb, 0x82, 0xa0, 0x85, 0xea, 0x8a, 0x4a, 0xf6, + 0xfa, 0x83, 0x81, 0xf0, 0x65, 0x88, 0x19, 0xea, 0xb4, 0x83, 0xf6, 0x5b, 0x32, + 0x5d, 0x5a, 0xed, 0xa1, 0x52, 0x32, 0xcf, 0xad, 0xec, 0x75, 0xab, 0x18, 0x66, + 0xe4, 0xc0, 0x15, 0x5a, 0x9c, 0x74, 0xa7, 0xa5, 0x7c, 0xcf, 0x34, 0xc4, 0x83, + 0xac, 0x7d, 0xa1, 0x58, 0x8a, 0x1b, 0x6b, 0x99, 0x41, 0xf1, 0x10, 0x40, 0xf9, + 0x4c, 0xf7, 0x8f, 0xad, 0x89, 0xbf, 0x11, 0xfe, 0xd6, 0x9a, 0xa0, 0xd8, 0x31, + 0x05, 0xad, 0xac, 0xdd, 0x4e, 0x5f, 0x04, 0xa6, 0x24, 0x24, 0x02, 0x3c, 0x9b, + 0x9e, 0x33, 0xc4, 0xfb, 0x7f, 0x12, 0xbd, 0xf2, 0x1f, 0x07, 0xf2, 0x65, 0xc5, + 0x37, 0xd5, 0x1c, 0x65, 0x51, 0xf4, 0x61, 0x7b, 0x91, 0x5d, 0x21, 0x99, 0x18, + 0x39, 0xc3, 0xd0, 0xd3, 0x63, 0x93, 0xd6, 0x46, 0xe0, 0xa8, 0xa4, 0x15, 0x09, + 0x21, 0x7d, 0x0e, 0x7d, 0x2c, 0xa1, 0xa0, 0xa0, 0xd6, 0x77, 0xa3, 0xea, 0xca, + 0x23, 0xed, 0xeb, 0x07, 0xb7, 0x4e, 0x65, 0x2a, 0x0b, 0xc5, 0x0c, 0x6c, 0x08, + 0x3a, 0x55, 0xd6, 0xc7, 0x30, 0x6e, 0x74, 0x08, 0x6f, 0x47, 0x68, 0x93, 0x3a, + 0xa2, 0x48, 0x73, 0x68, 0x18, 0x67, 0xa7, 0x89, 0x3d, 0x77, 0xcb, 0x7f, 0x29, + 0xb8, 0xc8, 0x47, 0xc5, 0x83, 0xf2, 0xd0, 0x71, 0xa6, 0x86, 0x61, 0x6e, 0x20, + 0x67, 0x19, 0xf7, 0x61, 0xae, 0x39, 0xc1, 0x10, 0x44, 0x2e, 0x06, 0x16, 0x3d, + 0x2b, 0x84, 0x59, 0x03, 0x60, 0x69, 0x5d, 0x4e, 0x19, 0x84, 0x9e, 0x03, 0x4f, + 0x24, 0xd9, 0xad, 0x39, 0x6c, 0x19, 0xff, 0x83, 0xce, 0x74, 0xf4, 0x6e, 0x64, + 0x5f, 0x93, 0x2e, 0x14, 0x1a, 0x41, 0x19, 0x59, 0x36, 0xc8, 0x5d, 0x51, 0x44, + 0x14, 0xf1, 0x12, 0xe6, 0x0b, 0x02, 0x25, 0x37, 0xc3, 0x8d, 0x6d, 0xc6, 0xc4, + 0x63, 0x83, 0x05, 0xc9, 0xbd, 0x6c, 0x62, 0xe3, 0x66, 0xbc, 0x63, 0x12, 0x3e, + 0x3e, 0x6d, 0xd3, 0x6e, 0xed, 0xd3, 0x13, 0x6f, 0xce, 0x8d, 0xee, 0xca, 0x0a, + 0xa0, 0x9a, 0x32, 0x98, 0xa3, 0x9d, 0x83, 0x85, 0x9e, 0xfc, 0x9b, 0x2b, 0x69, + 0xcf, 0x9a, 0x7d, 0xee, 0x08, 0xa9, 0x8e, 0x4b, 0xe5, 0x58, 0xac, 0x79, 0x12, + 0xfd, 0xcb, 0x42, 0x20, 0x90, 0x75, 0x42, 0x02, 0x60, 0xf7, 0xca, 0xd0, 0xf2, + 0xc0, 0x1f, 0x2a, 0xfe, 0x33, 0x07, 0x3f, 0x26, 0x24, 0x9d, 0x94, 0x4f, 0x7a, + 0x50, 0xdd, 0x84, 0x83, 0x9b, 0xc3, 0xea, 0x7f, 0xde, 0xe4, 0xed, 0x71, 0x02, + 0x9c, 0xf0, 0x75, 0x33, 0xd2, 0x6e, 0x1e, 0x27, 0xa3, 0xef, 0xb0, 0x32, 0xc3, + 0xa3, 0xb3, 0x4b, 0xd3, 0x09, 0x26, 0x22, 0xd2, 0x06, 0x2a, 0xe5, 0x36, 0xef, + 0x51, 0x49, 0xc4, 0x9b, 0x5b, 0xc9, 0x03, 0x5e, 0xaf, 0xab, 0x6e, 0x67, 0x57, + 0x61, 0x00, 0x8b, 0x0d, 0xad, 0xde, 0xec, 0xaa, 0x60, 0x44, 0x70, 0xbb, 0xe0, + 0xfa, 0xda, 0x25, 0x5d, 0x29, 0x0e, 0x92, 0xb1, 0x90, 0xc2, 0xc2, 0xd8, 0xc2, + 0x02, 0xe5, 0x45, 0x5d, 0x1f, 0xa9, 0xa9, 0xf3, 0xdb, 0x77, 0x79, 0xb5, 0x84, + 0x64, 0x34, 0x64, 0xaa, 0x80, 0x14, 0xba, 0x66, 0x99, 0x4d, 0xe2, 0x55, 0x17, + 0xf8, 0x39, 0x80, 0xe6, 0x6e, 0xe4, 0xf6, 0x03, 0x14, 0xae, 0x6d, 0xbe, 0xf4, + 0x52, 0xd5, 0xd3, 0x8b, 0x0a, 0x16, 0xf3, 0x99, 0x1f, 0x36, 0xd8, 0xa8, 0xb3, + 0x9d, 0xdc, 0x0d, 0x55, 0x95, 0xee, 0xd9, 0x87, 0x62, 0x87, 0x8c, 0xdf, 0x3f, + 0x4a, 0x02, 0xdc, 0x5c, 0xda, 0x77, 0xd5, 0xfe, 0x4f, 0xaf, 0x63, 0xa1, 0x5f, + 0x56, 0x8a, 0x54, 0x0d, 0xa5, 0x7d, 0xd9, 0xbe, 0xb6, 0xfb, 0x1a, 0x97, 0x7c, + 0xcb, 0x91, 0xb4, 0xd7, 0x9c, 0xb3, 0x9b, 0x28, 0x91, 0x1a, 0x29, 0xe7, 0xbf, + 0x02, 0x8a, 0xc6, 0x10, 0x37, 0x96, 0xdf, 0xb6, 0xb2, 0x09, 0x67, 0x23, 0x9a, + 0xd3, 0x73, 0xc3, 0x8c, 0x53, 0xf6, 0xdf, 0x18, 0x23, 0xd4, 0x95, 0x0a, 0x02, + 0x83, 0xe9, 0x9b, 0x9c, 0x06, 0xab, 0x29, 0x66, 0x66, 0x7c, 0x9d, 0xf6, 0x77, + 0x71, 0x6b, 0x0c, 0xad, 0xed, 0x81, 0x8d, 0xf9, 0xe4, 0x49, 0xc0, 0x72, 0xe2, + 0x2f, 0x9d, 0x98, 0xbb, 0x0f, 0x9b, 0x03, 0xbd, 0x5f, 0xd0, 0x13, 0xfc, 0xef, + 0x3e, 0xd6, 0xa4, 0x9a, 0xeb, 0x98, 0x72, 0x02, 0x54, 0x08, 0x7e, 0xf7, 0x28, + 0xe3, 0x19, 0x47, 0xff, 0xe8, 0xf7, 0x66, 0xe6, 0x3e, 0xe4, 0x6f, 0xf2, 0x08, + 0x16, 0xd5, 0xfa, 0x8f, 0xf5, 0x5a, 0x26, 0x39, 0x89, 0x61, 0x49, 0x0a, 0xb9, + 0xae, 0x36, 0x6f, 0xc5, 0xa2, 0xd1, 0x99, 0x6e, 0xd6, 0x93, 0xcc, 0xca, 0x82, + 0x35, 0x6f, 0x60, 0x0a, 0xb0, 0x99, 0xf6, 0xec, 0xa8, 0xbf, 0xe6, 0x45, 0x27, + 0x0d, 0x3f, 0x95, 0xed, 0xba, 0x5b, 0x0d, 0xe7, 0xa3, 0x28, 0x19, 0x23, 0x3b, + 0xcc, 0x75, 0x4a, 0x5c, 0xe2, 0xe5, 0xea, 0x07, 0x84, 0x2e, 0x5f, 0xf2, 0xce, + 0xbe, 0x62, 0xad, 0x76, 0xe8, 0xef, 0xf8, 0xd1, 0x5e, 0xa4, 0xc2, 0x4a, 0x5f, + 0x20, 0x78, 0x68, 0x31, 0x9a, 0x5a, 0xf6, 0xb0, 0x35, 0xbe, 0x3f, 0x44, 0xf4, + 0x34, 0x09, 0x4f, 0x6e, 0x52, 0x5b, 0xe6, 0x14, 0xda, 0xc9, 0x20, 0xa3, 0x30, + 0xbd, 0xfb, 0x26, 0xd7, 0x5f, 0xe7, 0xb4, 0xb3, 0x65, 0xd0, 0x94, 0x45, 0x92, + 0x50, 0xaa, 0xa5, 0x54, 0x44, 0x89, 0xfb, 0x1d, 0x99, 0x25, 0x81, 0x80, 0x0a, + 0x77, 0xb8, 0x91, 0x21, 0x57, 0xfc, 0x97, 0x13, 0xaa, 0xac, 0x25, 0xb4, 0xc2, + 0x6e, 0xb0, 0x3f, 0x71, 0x66, 0x46, 0x61, 0x9a, 0xf0, 0x24, 0x56, 0xae, 0x69, + 0x59, 0x62, 0xfe, 0x5e, 0x93, 0x1a, 0x63, 0xb5, 0xc7, 0x90, 0x52, 0xec, 0xd3, + 0x33, 0xe1, 0x84, 0x12, 0xdb, 0x91, 0xe1, 0x5f, 0x7c, 0xbc, 0x70, 0xb4, 0xcd, + 0x7e, 0x8e, 0x3c, 0x95, 0x1f, 0x35, 0x85, 0x72, 0xe3, 0x77, 0x67, 0xe7, 0xd5, + 0x27, 0x04, 0xa6, 0x72, 0x1b, 0x30, 0xef, 0xc4, 0x10, 0x17, 0xae, 0x4d, 0x23, + 0x15, 0x58, 0xc5, 0xc8, 0x2c, 0xc7, 0xdd, 0x7e, 0x33, 0x56, 0xc0, 0x9d, 0xc2, + 0x49, 0x06, 0xf0, 0x43, 0x8d, 0xfc, 0xc3, 0x00, 0x85, 0x6a, 0xc2, 0xce, 0xd8, + 0xf7, 0x7f, 0xa8, 0x01, 0x57, 0x36, 0xc6, 0x61, 0xe8, 0x02, 0x48, 0xae, 0xeb, + 0x77, 0x48, 0x74, 0xaa, 0x79, 0xd2, 0x90, 0xb8, 0xf5, 0x02, 0x7a, 0x0a, 0x50, + 0x95, 0x37, 0xfc, 0x7c, 0x68, 0x9b, 0x7a, 0xd8, 0x61, 0x16, 0xcf, 0xec, 0x26, + 0x47, 0xcc, 0xaa, 0xe1, 0xc7, 0x4b, 0x41, 0x6f, 0x3e, 0x6a, 0xe8, 0xf7, 0xcc, + 0x60, 0xea, 0xaf, 0x7b, 0x6a, 0x59, 0x0d, 0x51, 0x54, 0x41, 0x38, 0xe1, 0x73, + 0x29, 0x45, 0x60, 0x3a, 0x53, 0x46, 0x2c, 0x60, 0xe1, 0xf6, 0xcb, 0x0c, 0x9c, + 0xa0, 0x39, 0x0c, 0x48, 0x82, 0x24, 0xc3, 0x13, 0x26, 0x9f, 0xcd, 0x59, 0xfc, + 0xb6, 0x11, 0xfb, 0x2d, 0x9b, 0x4c, 0x8f, 0xa6, 0x01, 0xbb, 0x1c, 0xb8, 0xd0, + 0x7d, 0x79, 0x7b, 0xf5, 0xde, 0x52, 0xbc, 0xee, 0xb0, 0x23, 0x01, 0xc8, 0x96, + 0x2a, 0xc1, 0xfc, 0x04, 0x91, 0xdc, 0x81, 0xaf, 0xfd, 0x6c, 0x1e, 0xbf, 0x89, + 0xa1, 0x3d, 0x6f, 0x29, 0x0e, 0xda, 0x5d, 0x5c, 0xef, 0x38, 0x22, 0x15, 0xc5, + 0xe9, 0x51, 0xd7, 0x13, 0x05, 0xef, 0x33, 0xd9, 0x73, 0x71, 0x26, 0xd0, 0xe6, + 0x62, 0x90, 0x5f, 0x12, 0x50, 0x92, 0x6f, 0x6a, 0x22, 0x99, 0x90, 0xe3, 0x8f, + 0x69, 0xad, 0x9a, 0x91, 0x92, 0xb3, 0x02, 0xf2, 0x6b, 0xdd, 0xa4, 0x65, 0xd9, + 0x0b, 0x94, 0xb1, 0x2c, 0x57, 0xfa, 0x3f, 0xd6, 0x93, 0x00, 0x83, 0xf1, 0x84, + 0x43, 0x8d, 0x8a, 0x88, 0x9d, 0x3f, 0x5e, 0xce, 0xa2, 0xc6, 0xd2, 0x3d, 0x67, + 0x36, 0xf2, 0xa0, 0xf1, 0x8e, 0x26, 0xf4, 0xfa, 0x45, 0xd1, 0xbe, 0x8f, 0x3d, + 0xc4, 0xa7, 0x07, 0x13, 0x7e, 0x95, 0xd2, 0xad, 0x59, 0x4f, 0x6c, 0x03, 0xd2, + 0x49, 0x23, 0x06, 0x7a, 0xe4, 0x7f, 0xd6, 0x42, 0x5e, 0xfb, 0x9c, 0x1d, 0x50, + 0x4e, 0x6f, 0xd5, 0x57, 0x53, 0x40, 0x94, 0x56, 0x01, 0xfe, 0x80, 0x6f, 0x57, + 0x56, 0xac, 0xb5, 0x62, 0xf1, 0x3c, 0x0c, 0xa1, 0xd8, 0x03, 0xa1, 0x95, 0xc2, + 0xeb, 0xb2, 0xef, 0x02, 0xac, 0x33, 0xe6, 0xa8, 0x8d, 0xea, 0x07, 0x5b, 0xa9, + 0x96, 0xd3, 0xc3, 0x36, 0x64, 0x8e, 0x86, 0x94, 0xd3, 0xa1, 0x9d, 0x3d, 0xca, + 0x53, 0x1b, 0xeb, 0x50, 0xd4, 0x32, 0x7c, 0x5c, 0x0c, 0x23, 0xcb, 0x7c, 0xfd, + 0xb0, 0x8c, 0xa7, 0xcf, 0x2c, 0xac, 0x6b, 0xc1, 0x39, 0xd0, 0x74, 0x14, 0x73, + 0xd3, 0x76, 0x02, 0x9c, 0xb4, 0xab, 0x6b, 0xf0, 0x54, 0x55, 0x7c, 0xe2, 0x94, + 0xc7, 0x28, 0xa4, 0x68, 0x7d, 0x57, 0xec, 0x89, 0x09, 0xff, 0x51, 0xa4, 0xd0, + 0x2f, 0x9d, 0xcd, 0x11, 0x19, 0x3d, 0x7d, 0x1c, 0x9f, 0xda, 0xe6, 0xa1, 0x73, + 0x96, 0xa1, 0xbf, 0x57, 0xa9, 0x94, 0x93, 0x4f, 0x5e, 0x7a, 0x59, 0xf0, 0x45, + 0xde, 0xbe, 0xaf, 0xf6, 0x2e, 0xf3, 0x26, 0xb9, 0x47, 0xf2, 0xa8, 0xb4, 0x95, + 0x55, 0xe4, 0xd9, 0x9b, 0x3b, 0xf5, 0xc8, 0x1f, 0xf9, 0xfe, 0x31, 0x4e, 0x04, + 0x7a, 0xf1, 0x52, 0x50, 0x8f, 0x57, 0x01, 0x5c, 0xa4, 0x02, 0xc6, 0x7d, 0x92, + 0x5c, 0x99, 0xac, 0xea, 0x3e, 0xe8, 0xcc, 0x4b, 0x00, 0x8c, 0x5c, 0xb4, 0x39, + 0x66, 0xe7, 0x14, 0xef, 0x48, 0x0f, 0xd0, 0x5e, 0x07, 0xc7, 0xb2, 0xdd, 0xa9, + 0xaa, 0x39, 0x66, 0x11, 0x3e, 0xaa, 0x29, 0x3d, 0x3f, 0x62, 0x2b, 0x30, 0x9d, + 0x64, 0x80, 0x3c, 0xe1, 0xe6, 0x37, 0x8b, 0x6a, 0xac, 0x4f, 0xab, 0x52, 0x7c, + 0x43, 0xcd, 0x45, 0xed, 0x0a, 0x3c, 0x1a, 0x4b, 0x9f, 0xb1, 0x8d, 0xcc, 0xcf, + 0xcd, 0xb6, 0xac, 0x0c, 0x24, 0x21, 0x63, 0x9c, 0xda, 0x00, 0x75, 0xa2, 0x0d, + 0xc5, 0x11, 0x1b, 0x8d, 0x3d, 0x31, 0x99, 0x49, 0x5b, 0xd9, 0x13, 0x3d, 0xba, + 0xb9, 0x45, 0x41, 0x41, 0x0e, 0x4f, 0xba, 0x92, 0xc7, 0xb6, 0x06, 0xa5, 0xcb, + 0x12, 0x2f, 0x14, 0x0c, 0xf1, 0xa3, 0x59, 0x6f, 0x27, 0x88, 0xf3, 0xc8, 0xb9, + 0x26, 0x60, 0xf1, 0x4c, 0xb6, 0x5a, 0xf5, 0xdd, 0x23, 0xdf, 0xdb, 0xac, 0x13, + 0x71, 0xec, 0xf4, 0xb3, 0x37, 0x12, 0xfe, 0xd2, 0x29, 0x2c, 0x44, 0xf7, 0x08, + 0x34, 0xcf, 0x96, 0xc0, 0x5d, 0x58, 0x82, 0x7e, 0x69, 0xbf, 0xc2, 0xe6, 0x96, + 0xfa, 0x08, 0x74, 0x86, 0x9c, 0x02, 0xf3, 0xdc, 0xa1, 0x1c, 0x3b, 0x90, 0xcb, + 0x21, 0x4e, 0x68, 0xbc, 0x1c, 0xae, 0x03, 0x9d, 0x7a, 0x14, 0x6c, 0xdc, 0x1d, + 0x60, 0x9d, 0x7a, 0x6b, 0x3f, 0xd5, 0xd4, 0x61, 0xb0, 0x95, 0x1c, 0x82, 0xcf, + 0xb3, 0xe7, 0x63, 0xfa, 0xd2, 0xd1, 0xbc, 0x76, 0x78, 0xcd, 0xf8, 0x27, 0x79, + 0xf8, 0xfd, 0x5a, 0x1c, 0xe2, 0x2a, 0x8d, 0x3c, 0x45, 0x47, 0xab, 0xd9, 0x59, + 0x83, 0x8a, 0x46, 0xfb, 0x80, 0xaf, 0xe0, 0x1f, 0x8e, 0xcc, 0x99, 0x31, 0x51, + 0x3b, 0x19, 0x62, 0xec, 0x54, 0x08, 0x56, 0xcb, 0x18, 0x93, 0x87, 0xcf, 0xbf, + 0xcc, 0x0f, 0x7c, 0x68, 0x22, 0x3c, 0xba, 0x47, 0xfb, 0x0c, 0x9b, 0x48, 0x6e, + 0x4d, 0x99, 0x17, 0x19, 0x41, 0xf7, 0x67, 0x5a, 0x8b, 0x46, 0x32, 0x8a, 0x3b, + 0xc1, 0x09, 0xbf, 0x07, 0xc6, 0x6d, 0x5e, 0xde, 0x77, 0x1c, 0xc4, 0xc7, 0x4c, + 0xe8, 0x03, 0x33, 0x82, 0x91, 0x91, 0xee, 0xdc, 0x49, 0x35, 0x08, 0xa6, 0x44, + 0x53, 0x0a, 0x61, 0x44, 0xf2, 0x2d, 0xcf, 0x97, 0x52, 0x5a, 0x4c, 0xdc, 0xa1, + 0xad, 0x71, 0x07, 0x3b, 0x08, 0x0b, 0x73, 0xea, 0x45, 0x49, 0xf5, 0x40, 0x1b, + 0xff, 0x43, 0x18, 0x26, 0x8e, 0x6a, 0xd6, 0x37, 0x36, 0x31, 0x57, 0xa1, 0x9a, + 0x53, 0xf1, 0x23, 0xa0, 0xb0, 0xe1, 0x6d, 0x0b, 0x77, 0xf0, 0x20, 0x28, 0xda, + 0x46, 0x41, 0x00, 0xfd, 0xe7, 0x6d, 0x83, 0xdd, 0x0b, 0xb2, 0x24, 0xf7, 0xb5, + 0x7a, 0x00, 0xc0, 0x2f, 0x68, 0xae, 0x64, 0x8f, 0xdc, 0x52, 0x99, 0x57, 0xa1, + 0x04, 0x90, 0xdc, 0xe1, 0xfd, 0xdb, 0xb0, 0x90, 0x4f, 0x0d, 0x51, 0x8b, 0xb3, + 0x87, 0x54, 0x40, 0x19, 0x98, 0x3b, 0x61, 0x69, 0x75, 0xa7, 0x8e, 0x74, 0xd8, + 0x54, 0xfd, 0xdc, 0x49, 0xb2, 0x55, 0x16, 0x7b, 0x55, 0xef, 0x4b, 0xee, 0x46, + 0x56, 0x68, 0xb2, 0x0e, 0xa4, 0x11, 0x8c, 0xa5, 0x69, 0xae, 0x48, 0x0e, 0x0f, + 0x6e, 0x5e, 0x04, 0x3a, 0x35, 0x7b, 0x36, 0xd3, 0xab, 0x36, 0xc8, 0x61, 0xf2, + 0x27, 0x83, 0x01, 0xdc, 0xe5, 0x76, 0x74, 0xd5, 0x07, 0x3b, 0x3a, 0x6f, 0x51, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x79, 0x3a, 0xf1, 0xb7, 0xd4, 0x6f, 0x95, 0x7e, + 0x22, 0xd8, 0xd2, 0x58, 0x3b, 0xf1, 0x81, 0x83, 0x6c, 0x3b, 0xe9, 0x93, 0x0b, + 0xac, 0x8f, 0xa4, 0x60, 0xe9, 0x68, 0xaa, 0x71, 0x09, 0x87, 0x0b, 0xbe, 0xd1, + 0x7d, 0xf5, 0xf8, 0x88, 0xc8, 0xca, 0x14, 0x67, 0xae, 0x17, 0xdb, 0xbc, 0xde, + 0x31, 0xc1, 0x10, 0x5c, 0xb5, 0xbd, 0xa8, 0x8a, 0xc6, 0xc6, 0x27, 0x00, 0x2c, + 0xe2, 0x1c, 0x02, 0x14, 0x0f, 0xfe, 0x81, 0xec, 0x58, 0xbf, 0x1e, 0x6d, 0x1b, + 0xb7, 0xaa, 0xad, 0xa4, 0x1f, 0xba, 0x0b, 0xb5, 0x88, 0x77, 0x8a, 0x7f, 0x65, + 0x20, 0x2a, 0xd8, 0x11, 0xea, 0x73, 0xd2, 0x6c, 0x74, 0x55, 0x03, 0x95, 0xaf, + 0xf7, 0x53, 0x25, 0x10, 0x7c, 0x9b, 0x3f, 0x9a, 0xe9, 0xdc, 0xdc, 0xd8, 0x6e, + 0xd0, 0x81, 0xa2, 0xe7, 0x42, 0x47, 0x19, 0xa3, 0xd1, 0x85, 0xb7, 0xe0, 0xa4, + 0x3a, 0x47, 0x2e, 0x29, 0x8a, 0xc0, 0xaf, 0xdc, 0x52, 0x87, 0xd7, 0xad, 0x12, + 0x4c, 0xd9, 0x40, 0x5a, 0x62, 0xcd, 0x1c, 0xa0, 0x8b, 0x28, 0x2e, 0xfe, 0xf7, + 0xf9, 0x28, 0xdf, 0x76, 0xe2, 0x82, 0x1a, 0x41, 0x84, 0x13, 0xeb, 0x7c, 0xea, + 0xa5, 0xff, 0x12, 0x90, 0xb0, 0x3e, 0xc9, 0x1c, 0xe6, 0xdd, 0x28, 0x13, 0x0c, + 0x3a, 0xb0, 0xb2, 0x3b, 0x60, 0x2b, 0xd5, 0xbe, 0x5d, 0xc2, 0x60, 0x03, 0xaa, + 0xe0, 0x4b, 0x33, 0xd7, 0xbd, 0x25, 0x90, 0xe9, 0x0c, 0x8c, 0x38, 0x8e, 0xa7, + 0x95, 0x51, 0x22, 0xdb, 0xac, 0xa6, 0x7b, 0x30, 0x39, 0x5a, 0x92, 0x8b, 0x57, + 0xb8, 0x57, 0x51, 0x23, 0x20, 0x5a, 0xe1, 0x91, 0x52, 0xe4, 0x1e, 0x00, 0x29, + 0x31, 0xb4, 0x57, 0x46, 0x19, 0x8e, 0x5d, 0xd9, 0x57, 0x1a, 0x56, 0xa7, 0xe0, + 0xd4, 0x23, 0xff, 0x27, 0x98, 0x9d, 0x3e, 0xb4, 0x17, 0xec, 0xd3, 0xc3, 0x09, + 0x3f, 0xb8, 0x2c, 0x56, 0x58, 0xe2, 0x96, 0x24, 0xc5, 0x32, 0x19, 0xa6, 0x0c, + 0xd0, 0xa8, 0xc4, 0xda, 0x36, 0x7e, 0x29, 0xa7, 0x17, 0x79, 0xa7, 0x30, 0x32, + 0x98, 0x5a, 0x3d, 0x1f, 0xd0, 0x3d, 0x02, 0xd0, 0x6e, 0x05, 0x56, 0x6f, 0x3b, + 0x84, 0x36, 0x7c, 0xf0, 0xfa, 0xee, 0x9b, 0xc3, 0xbd, 0x7a, 0x3a, 0x60, 0x6a, + 0x9f, 0xdb, 0x84, 0x9c, 0x5d, 0x82, 0xd0, 0xa6, 0x19, 0x23, 0xc2, 0xe5, 0xd8, + 0x02, 0x63, 0xa8, 0xa5, 0x0c, 0x38, 0xbd, 0x03, 0x87, 0x72, 0xc4, 0x14, 0x3d, + 0x8b, 0x7a, 0xcf, 0xd7, 0x4e, 0x72, 0xc0, 0x4d, 0x89, 0x24, 0x8d, 0xff, 0x20, + 0xfe, 0x8d, 0xc5, 0xec, 0x21, 0x49, 0x05, 0x0a, 0xa2, 0x41, 0x64, 0xe8, 0x5f, + 0x67, 0x44, 0xad, 0x0c, 0xac, 0xf1, 0xa8, 0xb7, 0x01, 0x26, 0xf4, 0x82, 0xc0, + 0x92, 0xed, 0x9f, 0x61, 0x27, 0xd2, 0x05, 0x0d, 0x12, 0xe8, 0x78, 0xa7, 0x96, + 0x53, 0xa1, 0xe8, 0x4d, 0xae, 0xc3, 0xeb, 0xe6, 0x2d, 0x5f, 0x6c, 0x4a, 0xbe, + 0x5c, 0xe9, 0x0a, 0x7f, 0xe2, 0xe5, 0x2a, 0x8d, 0x78, 0x46, 0xe8, 0xed, 0xf2, + 0xf2, 0xbc, 0xe0, 0x5a, 0x03, 0x7c, 0x82, 0x03, 0x22, 0xca, 0xad, 0x12, 0x61, + 0x46, 0x7d, 0xcf, 0xb7, 0xd6, 0xb6, 0x13, 0x3d, 0xc2, 0x1e, 0x80, 0x96, 0xc7, + 0xe9, 0xf8, 0xe9, 0xe1, 0x0c, 0x1e, 0x3f, 0xac, 0x40, 0x58, 0xb6, 0x82, 0xc6, + 0x8e, 0x02, 0xfa, 0xca, 0xe0, 0xf9, 0xc2, 0xdd, 0x4d, 0x64, 0xd9, 0x04, 0x61, + 0x52, 0xb4, 0x76, 0x23, 0x32, 0x93, 0x9f, 0x17, 0xe6, 0xaa, 0xf7, 0xd8, 0xb9, + 0xd3, 0x58, 0xe2, 0x21, 0x8d, 0x4e, 0x0d, 0x69, 0x02, 0xf1, 0x19, 0xe1, 0xc6, + 0x4e, 0xec, 0x4c, 0x8b, 0x53, 0x28, 0x09, 0x70, 0x71, 0x31, 0xf0, 0x1f, 0x55, + 0xc7, 0xad, 0x04, 0xcf, 0xb6, 0x3f, 0x7c, 0x4a, 0x3d, 0x0a, 0x2b, 0x0f, 0xfb, + 0x0b, 0x05, 0x02, 0xbe, 0x05, 0x5b, 0x8c, 0x94, 0xca, 0x80, 0xbb, 0x0a, 0x1d, + 0x13, 0xcd, 0x4c, 0xd6, 0x9a, 0xb9, 0x83, 0x04, 0xae, 0x25, 0x15, 0xd5, 0xf7, + 0x69, 0x9d, 0x4a, 0xbe, 0xe5, 0xc2, 0x0b, 0xe6, 0x09, 0x02, 0x73, 0x51, 0x10, + 0x12, 0xf2, 0x34, 0xbd, 0x85, 0xa7, 0xef, 0xf5, 0xfb, 0x63, 0x4c, 0xff, 0x26, + 0x58, 0xba, 0x65, 0x16, 0x04, 0x85, 0x63, 0x09, 0x5e, 0xce, 0xfb, 0x30, 0x15, + 0xee, 0x3f, 0x03, 0xca, 0x52, 0xa1, 0x77, 0xf2, 0x61, 0xec, 0xdc, 0x26, 0xbc, + 0x08, 0x9d, 0x34, 0xc6, 0x40, 0x48, 0x46, 0xe9, 0xc6, 0x47, 0xfc, 0xfe, 0x98, + 0xcc, 0x6a, 0xcd, 0xbb, 0x46, 0x4f, 0x64, 0x27, 0x8a, 0xd8, 0xce, 0x9d, 0x1a, + 0xe0, 0xd4, 0x15, 0xbc, 0x0c, 0x05, 0x24, 0x5f, 0xdd, 0xaf, 0x4e, 0xbc, 0x8d, + 0xc7, 0x03, 0xa8, 0x5c, 0xb2, 0x70, 0xf7, 0x96, 0xad, 0x2d, 0x93, 0x7e, 0x2a, + 0xc0, 0xd5, 0xe0, 0xa3, 0x48, 0x21, 0x75, 0x80, 0x00, 0xaa, 0x59, 0xc9, 0xd4, + 0x65, 0x24, 0x85, 0x29, 0x4e, 0xe0, 0xab, 0x29, 0x69, 0x6b, 0x21, 0x43, 0x0f, + 0xa5, 0x4d, 0xcf, 0xbf, 0x2b, 0x9c, 0x49, 0xd1, 0x42, 0x06, 0x42, 0x09, 0xee, + 0xee, 0xd4, 0xd4, 0x71, 0xff, 0xc0, 0x17, 0xd4, 0xe2, 0x0a, 0x79, 0x6b, 0x09, + 0x27, 0x80, 0x4c, 0x06, 0x1b, 0x9f, 0x4a, 0x70, 0x91, 0xfe, 0x01, 0x5a, 0xda, + 0x68, 0xfd, 0x84, 0x42, 0xe0, 0x18, 0x25, 0xc8, 0x8d, 0xfe, 0x55, 0xcf, 0x5d, + 0xe3, 0x89, 0x36, 0xf7, 0xce, 0x25, 0x31, 0x1b, 0x90, 0x2b, 0xa9, 0x7a, 0x3c, + 0x12, 0xa9, 0x5c, 0xfa, 0x1c, 0x3a, 0x59, 0x1b, 0x81, 0x8f, 0x60, 0x83, 0x27, + 0x09, 0xd9, 0xe4, 0x83, 0x9e, 0x41, 0x0f, 0xb3, 0x6b, 0x84, 0xf3, 0xac, 0x4f, + 0x07, 0x0f, 0xc3, 0x5e, 0x16, 0x19, 0x78, 0x25, 0x9e, 0x5b, 0x8e, 0xdc, 0x74, + 0x4d, 0x90, 0x91, 0x9a, 0xa7, 0x70, 0xbb, 0x36, 0x21, 0x51, 0x28, 0xe5, 0x82, + 0xb5, 0x96, 0x41, 0xe2, 0x38, 0x52, 0xe9, 0x58, 0xeb, 0x8f, 0xc3, 0xc0, 0xaa, + 0x96, 0x15, 0x2b, 0xa4, 0xf7, 0x7f, 0x13, 0x8d, 0x6a, 0x67, 0x12, 0xa3, 0xae, + 0x32, 0x26, 0x01, 0x58, 0x83, 0xf8, 0x1d, 0xb2, 0x3e, 0x58, 0x3c, 0x86, 0x9c, + 0x4c, 0x71, 0x14, 0x3a, 0x6f, 0xff, 0xd6, 0x5e, 0x8d, 0xfd, 0xc5, 0x0c, 0x99, + 0xa2, 0xf1, 0xf3, 0x14, 0xcd, 0xcc, 0x71, 0x35, 0x9e, 0x23, 0x5f, 0x1d, 0x7d, + 0xc2, 0xb5, 0xf3, 0x8e, 0xf7, 0xb9, 0x70, 0x84, 0x31, 0x63, 0xc0, 0x3f, 0x9d, + 0xd4, 0x0a, 0x80, 0x15, 0xef, 0xdc, 0x87, 0x91, 0x95, 0x6a, 0x3f, 0x3c, 0xed, + 0xd9, 0xea, 0x64, 0xf8, 0xef, 0xa7, 0xa0, 0x81, 0x5a, 0x70, 0x38, 0x1d, 0x71, + 0x46, 0x78, 0x17, 0xbd, 0x04, 0xca, 0x52, 0x9a, 0xed, 0xe0, 0x7f, 0xf6, 0x0d, + 0x17, 0x6a, 0xed, 0x0f, 0x85, 0x5a, 0x2e, 0xae, 0xa8, 0x9e, 0xae, 0xac, 0xa8, + 0x93, 0x58, 0xc0, 0x81, 0x82, 0x6a, 0x08, 0x12, 0xa5, 0xbc, 0xa2, 0x8b, 0xe1, + 0x37, 0x3f, 0x08, 0x6d, 0xbd, 0xba, 0x7e, 0x43, 0xe2, 0x03, 0x21, 0x2c, 0x9f, + 0xed, 0x21, 0x47, 0x4b, 0xa1, 0x9a, 0x05, 0x5f, 0xfc, 0xc1, 0x79, 0x41, 0x2e, + 0x89, 0x3a, 0x74, 0x48, 0x32, 0x29, 0x8c, 0x5f, 0xe2, 0x4c, 0xc6, 0xb1, 0x86, + 0x67, 0xf4, 0x9b, 0x34, 0xdf, 0xb1, 0x23, 0x79, 0x26, 0x74, 0x19, 0xa9, 0xcb, + 0x94, 0x03, 0xd8, 0x16, 0x7d, 0x8d, 0x1e, 0x91, 0xd2, 0x81, 0x1a, 0x04, 0x3b, + 0x29, 0x24, 0x3b, 0x06, 0x9b, 0x37, 0x58, 0x78, 0x47, 0xdc, 0x6f, 0xcd, 0xdb, + 0x18, 0x31, 0xbd, 0x1c, 0xc2, 0x56, 0x7c, 0xa0, 0x33, 0xac, 0x40, 0xf7, 0x4a, + 0xb6, 0x95, 0x5f, 0x68, 0x3b, 0x12, 0xe4, 0xe8, 0x25, 0x4e, 0x4e, 0xa7, 0x60, + 0xd3, 0x8b, 0x3f, 0x46, 0x79, 0x1c, 0x5c, 0x4c, 0xb1, 0x2b, 0xc7, 0xcc, 0xb0, + 0xed, 0x18, 0x65, 0xf2, 0x5d, 0x60, 0x1c, 0x30, 0x3f, 0x81, 0xfb, 0x1f, 0xa1, + 0xdb, 0x48, 0x53, 0x3d, 0x3d, 0x6b, 0x28, 0x8e, 0x4d, 0x9a, 0x4d, 0xff, 0x8e, + 0xc2, 0x1c, 0x96, 0xf5, 0x78, 0x39, 0x97, 0x10, 0xc8, 0x25, 0xfe, 0x7e, 0x32, + 0xf9, 0x3a, 0x8c, 0x07, 0x43, 0xf9, 0xeb, 0xd5, 0x4c, 0xc1, 0x51, 0xc7, 0x61, + 0x03, 0x37, 0xae, 0xbf, 0x7e, 0x9b, 0x91, 0x57, 0x20, 0xa5, 0x43, 0x51, 0xd4, + 0x9a, 0xb8, 0xc2, 0x2f, 0xa3, 0x49, 0x98, 0xdc, 0xf5, 0x83, 0xd4, 0x38, 0x73, + 0x61, 0xef, 0x3f, 0xf8, 0x6f, 0x50, 0xec, 0x53, 0xf4, 0x92, 0x49, 0xe4, 0xad, + 0x34, 0x96, 0x03, 0x06, 0x6f, 0xc9, 0xc6, 0x61, 0xd6, 0x9f, 0x91, 0x1d, 0xfa, + 0x72, 0x41, 0xc8, 0xd5, 0x79, 0x2d, 0x43, 0xc4, 0x57, 0xd5, 0xde, 0x96, 0x52, + 0x3a, 0x53, 0xd6, 0x67, 0xec, 0x5c, 0x4e, 0xf9, 0xd5, 0x02, 0xa1, 0x6f, 0x15, + 0x22, 0x47, 0x58, 0x96, 0xd7, 0x9b, 0xc5, 0x78, 0x33, 0xe9, 0x77, 0x17, 0x1c, + 0x32, 0x4d, 0xce, 0x2a, 0x1e, 0xa1, 0xe4, 0x30, 0x4f, 0x49, 0xe4, 0x3a, 0xe0, + 0x65, 0xe3, 0xfb, 0x19, 0x6f, 0x76, 0xd9, 0xb8, 0x79, 0xc7, 0x20, 0x08, 0x62, + 0xea, 0xd1, 0x8d, 0xea, 0x5f, 0xb6, 0xa1, 0x7a, 0xce, 0xa3, 0x33, 0x86, 0xeb, + 0x4c, 0xa1, 0xb5, 0x14, 0x86, 0xa9, 0x14, 0x8f, 0xbd, 0xf9, 0xa9, 0x53, 0x32, + 0xaa, 0x60, 0x5c, 0x5d, 0x54, 0x83, 0xce, 0x4b, 0xa8, 0xec, 0xe0, 0x1a, 0x8f, + 0xf2, 0xb7, 0xef, 0x82, 0xd0, 0x5c, 0x0b, 0x6e, 0x86, 0x1b, 0x91, 0x5f, 0x13, + 0xca, 0x0e, 0xb3, 0xea, 0x13, 0xd5, 0x07, 0x08, 0x07, 0xa2, 0xcb, 0x66, 0x80, + 0xa2, 0x49, 0xea, 0x9c, 0x72, 0x24, 0x39, 0x2c, 0xbc, 0x8a, 0xb8, 0x25, 0x01, + 0xb2, 0x6f, 0x11, 0x2a, 0xc7, 0x89, 0xa1, 0x2a, 0x31, 0xad, 0x13, 0x14, 0xe2, + 0xed, 0xe0, 0x8f, 0xad, 0x31, 0x43, 0xaf, 0x30, 0xc2, 0x7f, 0x40, 0x3b, 0xc8, + 0x66, 0xc7, 0x55, 0x17, 0x78, 0x52, 0xaf, 0xd0, 0xab, 0xb9, 0x0a, 0xde, 0x1d, + 0x68, 0x27, 0x26, 0xf4, 0x20, 0x08, 0xb4, 0x6a, 0xd7, 0xf8, 0xab, 0xdb, 0x18, + 0x11, 0x7f, 0x72, 0x64, 0x13, 0x90, 0xf0, 0x86, 0xb6, 0xe1, 0x49, 0x8b, 0xe6, + 0x95, 0x48, 0x52, 0x7e, 0x6a, 0xda, 0x2b, 0x38, 0xb9, 0xfe, 0x12, 0x1e, 0xf6, + 0x70, 0xaf, 0x74, 0x37, 0xd3, 0x25, 0x36, 0xd5, 0xcf, 0x5c, 0x4a, 0xb1, 0x9d, + 0xd9, 0x97, 0x71, 0x58, 0x2d, 0x03, 0x81, 0x04, 0xb7, 0xe0, 0x39, 0xa3, 0x76, + 0xf7, 0xac, 0xbb, 0xea, 0xdb, 0x34, 0xf9, 0x45, 0xbe, 0xb9, 0xd7, 0xca, 0x0e, + 0x4e, 0x3d, 0x5c, 0x5e, 0x4e, 0xb1, 0xd8, 0x52, 0x6e, 0xbd, 0x13, 0xda, 0xcb, + 0x1b, 0xa3, 0x57, 0x35, 0xc6, 0xd0, 0x4a, 0x45, 0x55, 0xac, 0xf4, 0xbf, 0x11, + 0x76, 0x26, 0x50, 0x0d, 0x77, 0xb3, 0x81, 0x89, 0xdd, 0x48, 0x88, 0x04, 0x12, + 0x25, 0xac, 0xbe, 0x38, 0x74, 0xa4, 0xc0, 0xf6, 0x07, 0xfe, 0x67, 0x45, 0xf9, + 0x35, 0x5b, 0x3f, 0xa1, 0x88, 0xf1, 0xd6, 0x5c, 0x09, 0xf3, 0x89, 0xaf, 0x1b, + 0x9d, 0x62, 0x32, 0xaa, 0x79, 0x44, 0x79, 0x19, 0xc5, 0x50, 0xf6, 0xf3, 0x1f, + 0xec, 0x35, 0x48, 0x1c, 0xb9, 0x22, 0xde, 0x2d, 0xb5, 0xb4, 0xda, 0x2f, 0x81, + 0x94, 0x86, 0x17, 0x02, 0x8e, 0x32, 0x17, 0x06, 0xa3, 0xa7, 0x78, 0xc1, 0x93, + 0x8c, 0x44, 0x3b, 0xb0, 0x0e, 0x5b, 0x0f, 0xf0, 0x6a, 0xd8, 0xab, 0x9b, 0x1a, + 0xb0, 0xc1, 0x14, 0x77, 0x67, 0x3f, 0x85, 0xdf, 0x95, 0x61, 0xdb, 0xea, 0x45, + 0xd5, 0xf9, 0x78, 0x1e, 0xbe, 0x31, 0x7a, 0x07, 0x10, 0xae, 0x54, 0x61, 0xe3, + 0x4f, 0xe6, 0xf1, 0xb1, 0xaa, 0x9b, 0x4e, 0x67, 0xb1, 0x49, 0x10, 0x98, 0x48, + 0x02, 0xc2, 0xa7, 0xe3, 0x81, 0x93, 0xbc, 0x7b, 0xdc, 0x8b, 0xa3, 0xe4, 0xe3, + 0xd1, 0xd9, 0x33, 0xbf, 0xb5, 0x80, 0xf5, 0xb3, 0xe8, 0x7a, 0x2a, 0x06, 0x51, + 0x70, 0x51, 0x41, 0x0f, 0xe1, 0xb4, 0xff, 0x1e, 0xa0, 0xad, 0xe8, 0x24, 0xf3, + 0x38, 0x51, 0x54, 0x56, 0xa5, 0x7c, 0x7a, 0x91, 0x6a, 0x74, 0x38, 0x8e, 0xe8, + 0xf1, 0x28, 0x1f, 0x9a, 0xde, 0x0a, 0xe2, 0xa2, 0x61, 0x3a, 0x06, 0x12, 0xc4, + 0x69, 0xdf, 0x79, 0x2b, 0x8d, 0xf4, 0xca, 0xe4, 0xfc, 0x25, 0xc1, 0xca, 0xdb, + 0xa9, 0x5a, 0x80, 0x7c, 0xe6, 0x1e, 0x5a, 0x53, 0x03, 0xfa, 0xaf, 0x9e, 0x14, + 0x65, 0x39, 0x96, 0xb5, 0xa8, 0xad, 0xc3, 0x4f, 0xd4, 0x75, 0xef, 0x14, 0x99, + 0x09, 0x4b, 0xab, 0xaf, 0x1f, 0x3f, 0x07, 0xda, 0x9a, 0x39, 0x0b, 0x1d, 0x9f, + 0xc9, 0xa0, 0x83, 0x27, 0x98, 0x7a, 0xdf, 0xe9, 0x56, 0x48, 0x63, 0xfb, 0xdf, + 0xa8, 0xf6, 0xb4, 0x6a, 0x88, 0x41, 0x58, 0x30, 0x99, 0xaf, 0xb7, 0x87, 0x01, + 0x18, 0xfa, 0xce, 0x76, 0x34, 0x7e, 0x40, 0xb6, 0xfd, 0x8c, 0xd1, 0x55, 0x82, + 0xae, 0x8e, 0x23, 0xbe, 0x9a, 0x02, 0x19, 0xbc, 0x3e, 0x4e, 0x45, 0x46, 0xa3, + 0x0d, 0x3b, 0xbb, 0xbd, 0x16, 0x86, 0x08, 0x68, 0x76, 0xbe, 0x0e, 0x4c, 0x85, + 0x9b, 0xe7, 0x1f, 0xb5, 0x8f, 0x4f, 0xab, 0x3d, 0x28, 0xc0, 0xb4, 0xf7, 0xe7, + 0x5a, 0xd1, 0xed, 0xb7, 0xf8, 0x89, 0x46, 0xfb, 0x40, 0xcf, 0xa5, 0x78, 0x6a, + 0x0f, 0xcb, 0xa1, 0x30, 0x3c, 0x83, 0x47, 0xec, 0xee, 0x93, 0xd4, 0x6d, 0x14, + 0x0b, 0xb5, 0xf6, 0x95, 0x31, 0xd6, 0x66, 0x54, 0x8b, 0x10, 0x9c, 0xe7, 0x64, + 0xbe, 0xad, 0x7c, 0x87, 0xbd, 0x4c, 0x87, 0x64, 0x94, 0xde, 0x82, 0xdb, 0x6e, + 0x50, 0x73, 0xa6, 0xc9, 0x4f, 0x7c, 0x09, 0x9a, 0x40, 0xd7, 0xa3, 0x1c, 0x4a, + 0x04, 0xb6, 0x9c, 0x9f, 0xcc, 0xf3, 0xc7, 0xdd, 0x56, 0xf5, 0x54, 0x47, 0x76, + 0xc5, 0x3b, 0x4d, 0xf7, 0x95, 0x39, 0x81, 0xd5, 0x5a, 0x96, 0xa6, 0xdc, 0xff, + 0x99, 0x04, 0xa9, 0x08, 0x42, 0xe5, 0xba, 0xfe, 0xc8, 0x84, 0x0c, 0x2d, 0x25, + 0x5b, 0xf5, 0xad, 0x61, 0xc4, 0x60, 0xf9, 0x8f, 0xeb, 0x82, 0xa1, 0x0f, 0xa1, + 0xc0, + ], + script_code: Script(vec![0x65, 0x6a, 0x65, 0x51, 0x52, 0x65, 0x63]), + transparent_input: None, + hash_type: 1, + amount: 1712463999734827, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0xbb, 0x10, 0x30, 0x0e, 0x4d, 0xaf, 0xe3, 0x0c, 0x3f, 0xf0, 0x26, 0x34, 0xd0, + 0xe0, 0x03, 0x2f, 0x17, 0x15, 0xb0, 0x0c, 0xbc, 0x77, 0x3d, 0xf6, 0xb0, 0x9e, + 0x00, 0x43, 0x38, 0x6a, 0x14, 0x18, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x02, 0x13, 0xe5, 0x6c, 0x77, + 0x2f, 0x2c, 0x3b, 0x86, 0x0e, 0xa5, 0xb0, 0x3a, 0x88, 0x54, 0xbc, 0x6e, 0x65, + 0x90, 0xd6, 0x3c, 0xc0, 0xea, 0x54, 0xf1, 0x0b, 0x73, 0xba, 0x24, 0x1b, 0xf7, + 0x4b, 0x63, 0x55, 0x51, 0xa2, 0xaa, 0x06, 0x65, 0x6a, 0xac, 0x52, 0x51, 0x63, + 0x36, 0x8b, 0x26, 0xd7, 0x0a, 0x73, 0x7f, 0x26, 0x76, 0x85, 0x99, 0x8a, 0x3f, + 0x7d, 0x26, 0x37, 0x91, 0x49, 0x09, 0xc7, 0x46, 0x49, 0x5d, 0x24, 0xc4, 0x98, + 0x63, 0x5e, 0xf9, 0x7a, 0xc6, 0x6a, 0x40, 0x08, 0x94, 0xc0, 0x9f, 0x73, 0x48, + 0x8e, 0x07, 0x6a, 0x53, 0x65, 0x52, 0x51, 0x65, 0x52, 0x05, 0xf9, 0x1a, 0xd7, + 0x00, 0x79, 0x65, 0xc2, 0x99, 0x36, 0x1d, 0x60, 0x0d, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x89, 0xee, 0x09, 0x62, 0xf5, 0x8c, 0x05, 0x1d, 0x11, 0xd0, 0x55, 0xfc, + 0xe2, 0x04, 0xa5, 0x62, 0xde, 0x68, 0x08, 0x8a, 0x1b, 0x26, 0x48, 0xb8, 0x17, + 0x4c, 0xbc, 0xfc, 0x8b, 0x5b, 0x5c, 0xd0, 0x77, 0x11, 0x5a, 0xfd, 0xe1, 0x84, + 0x05, 0x05, 0x4e, 0x5d, 0xa9, 0xa0, 0x43, 0x10, 0x34, 0x2c, 0x5d, 0x3b, 0x52, + 0x6e, 0x0b, 0x02, 0xc5, 0xca, 0x17, 0x22, 0xba, 0xde, 0xee, 0x23, 0xd1, 0x45, + 0xe8, 0xeb, 0x22, 0x13, 0xfc, 0x4a, 0xf1, 0xe4, 0x50, 0xe4, 0xd5, 0x21, 0x7c, + 0x66, 0x17, 0x00, 0x8c, 0x78, 0xf4, 0xfb, 0x11, 0x12, 0xf4, 0x02, 0x8a, 0x70, + 0x4f, 0xc5, 0xa9, 0x38, 0x2c, 0x6b, 0x03, 0xe7, 0xd8, 0x08, 0x5e, 0x90, 0x6c, + 0xf8, 0x4c, 0xa2, 0xc1, 0x20, 0x7c, 0x87, 0xa2, 0xbc, 0xe2, 0x08, 0x0a, 0x98, + 0x91, 0x66, 0x8d, 0x69, 0xb0, 0x44, 0xbe, 0xce, 0xd6, 0xcd, 0xa3, 0x2c, 0x22, + 0x9c, 0x91, 0x17, 0x91, 0x7a, 0xa0, 0x7d, 0xdf, 0xfc, 0xd3, 0x77, 0x39, 0x5c, + 0xba, 0x61, 0x6d, 0x63, 0xc0, 0xb6, 0x9c, 0x01, 0xfc, 0xc4, 0x53, 0x91, 0xfd, + 0x5b, 0x87, 0x63, 0xfb, 0x96, 0xd7, 0xca, 0x33, 0x3a, 0x12, 0xde, 0x3c, 0xef, + 0xa9, 0x1c, 0x6c, 0x98, 0xf9, 0x47, 0x3b, 0x8e, 0x10, 0x4a, 0x71, 0x29, 0x3e, + 0x46, 0x37, 0x47, 0x05, 0xba, 0xf6, 0x5f, 0xa4, 0x13, 0x84, 0xba, 0x5c, 0x8e, + 0x0c, 0x88, 0xa3, 0xeb, 0x07, 0xe0, 0xbe, 0x34, 0xda, 0xdd, 0xfa, 0xbb, 0x7b, + 0x65, 0x54, 0x3b, 0x5f, 0x39, 0xcb, 0x20, 0x23, 0xd4, 0x67, 0x89, 0xeb, 0x7d, + 0x98, 0x9a, 0xf7, 0x79, 0xe5, 0xb8, 0xd2, 0x83, 0x85, 0xa8, 0x5b, 0x0d, 0xa2, + 0xab, 0xe0, 0x7f, 0x0c, 0x2b, 0xb4, 0x25, 0x5f, 0xce, 0xa0, 0x31, 0x88, 0x52, + 0x7a, 0x30, 0x7d, 0x40, 0x91, 0x59, 0xe9, 0x01, 0x66, 0xfa, 0xc6, 0xa0, 0x70, + 0xba, 0x05, 0xb3, 0xe4, 0xdb, 0xfd, 0x3a, 0x2b, 0xfc, 0xc9, 0xee, 0x6e, 0xd0, + 0x16, 0xc0, 0xf6, 0x65, 0xbe, 0x81, 0x33, 0xb7, 0xdc, 0x1d, 0x86, 0x04, 0x4d, + 0xb0, 0xf9, 0x03, 0x40, 0xfb, 0x0e, 0x9f, 0x8b, 0xc2, 0xe4, 0xdb, 0x53, 0x82, + 0xa8, 0xb4, 0xf8, 0x15, 0xb4, 0xe8, 0x43, 0x4a, 0xd0, 0xdf, 0xbc, 0x51, 0xa5, + 0xe9, 0xb1, 0x45, 0xe1, 0x59, 0x6c, 0xbf, 0x46, 0x70, 0x03, 0xe0, 0x5d, 0xfd, + 0xaf, 0xbb, 0x0c, 0xf3, 0xdd, 0xee, 0x28, 0xd7, 0x6a, 0x82, 0x42, 0x8e, 0x8a, + 0xba, 0x43, 0x64, 0xe8, 0x4b, 0xac, 0x37, 0x92, 0x98, 0xdf, 0x29, 0x32, 0xe6, + 0x9b, 0xb5, 0xd0, 0x0b, 0x51, 0x6e, 0xfc, 0x33, 0xae, 0x6c, 0xc3, 0x94, 0x7c, + 0xeb, 0x09, 0xed, 0x37, 0x16, 0x67, 0x21, 0x2a, 0x83, 0x1b, 0x54, 0x85, 0xea, + 0xfc, 0xe8, 0x48, 0x81, 0x88, 0xea, 0x4e, 0x27, 0xd0, 0xcd, 0xf7, 0xdd, 0xd3, + 0x48, 0xab, 0xff, 0x77, 0x7f, 0x4a, 0x13, 0xbb, 0xc7, 0x16, 0xb6, 0xa5, 0x94, + 0x4e, 0xe7, 0x27, 0x96, 0x56, 0x90, 0xe2, 0x09, 0xb4, 0x9e, 0xb9, 0x62, 0xc0, + 0x39, 0x97, 0x5f, 0x03, 0x9e, 0xd5, 0xc6, 0xe4, 0xc4, 0x00, 0xd8, 0x87, 0x75, + 0x94, 0x33, 0xd3, 0xad, 0x71, 0x6d, 0xa0, 0xcb, 0x44, 0x61, 0x13, 0xc7, 0x72, + 0x7a, 0x64, 0xb5, 0x8c, 0x3f, 0x8a, 0x0f, 0x81, 0x18, 0x9f, 0x02, 0x00, 0x52, + 0x33, 0xa8, 0x13, 0x66, 0xae, 0xe7, 0x3c, 0xec, 0x85, 0x22, 0x8e, 0xbc, 0xfd, + 0x5e, 0xe3, 0xc3, 0xfb, 0x44, 0xdb, 0x76, 0xba, 0x24, 0x3f, 0x28, 0x42, 0xb7, + 0xb5, 0xfc, 0x74, 0x6a, 0x03, 0x1b, 0x0b, 0xc4, 0xbd, 0x4f, 0xc9, 0xfd, 0x83, + 0x35, 0x65, 0xea, 0x85, 0x2b, 0x92, 0xb2, 0x24, 0xf6, 0x99, 0x03, 0x18, 0xad, + 0x8c, 0x7d, 0x94, 0x37, 0xe2, 0x0e, 0x2a, 0x1f, 0x20, 0xe8, 0x18, 0x03, 0x05, + 0x7c, 0x5a, 0xba, 0xaa, 0x2e, 0x5c, 0x15, 0xb9, 0x49, 0x45, 0xcd, 0x42, 0x4c, + 0x28, 0xa5, 0xfa, 0x38, 0x5d, 0xad, 0xfe, 0x49, 0x07, 0xb2, 0x74, 0xd8, 0x42, + 0x70, 0x7d, 0xb3, 0x69, 0x7a, 0x02, 0xe6, 0xc8, 0xf5, 0x42, 0xe5, 0xec, 0xc0, + 0x7f, 0xe4, 0x73, 0x50, 0xd1, 0x01, 0x46, 0x70, 0x21, 0x2e, 0xfe, 0x81, 0xfb, + 0x7c, 0x73, 0xe8, 0x45, 0x0d, 0xf8, 0x14, 0xef, 0x62, 0x32, 0xf7, 0x49, 0x0f, + 0x63, 0xcc, 0xf0, 0x74, 0x80, 0xf8, 0x84, 0xa6, 0x6e, 0xaf, 0xfc, 0x28, 0xfe, + 0xa4, 0x48, 0xd7, 0xb4, 0x01, 0xcd, 0xae, 0x10, 0xe7, 0xc0, 0xc7, 0xf9, 0xa7, + 0xb1, 0x53, 0x31, 0x96, 0x9f, 0xc8, 0xcb, 0x36, 0x39, 0x67, 0x73, 0xde, 0x19, + 0x19, 0x31, 0xc7, 0x50, 0xf6, 0xce, 0x5c, 0xaa, 0xf2, 0x97, 0x68, 0xeb, 0xb2, + 0x7d, 0xac, 0xc7, 0x38, 0x05, 0x6a, 0x81, 0x25, 0xb4, 0x77, 0x2b, 0xf8, 0x7a, + 0xe1, 0x0a, 0x8a, 0x30, 0x9b, 0x9b, 0xd6, 0x55, 0x04, 0x3c, 0xfc, 0x31, 0x59, + 0x49, 0x43, 0x68, 0xc5, 0xab, 0x8c, 0xad, 0xb7, 0xf6, 0x71, 0xe9, 0x62, 0x6b, + 0xd2, 0x63, 0xe3, 0x11, 0x81, 0xa6, 0x04, 0xb5, 0x06, 0xa0, 0x3b, 0x43, 0x9a, + 0x7f, 0xfe, 0x43, 0x55, 0x89, 0x24, 0x77, 0xe2, 0xbd, 0xf3, 0x38, 0xc6, 0x2c, + 0x39, 0x22, 0xf7, 0xd3, 0xc9, 0xa5, 0x6c, 0x71, 0x03, 0xd9, 0x11, 0x94, 0x8a, + 0x84, 0xb5, 0xae, 0x2d, 0xbb, 0x16, 0xa3, 0x76, 0x1a, 0xdd, 0x05, 0x3a, 0x0f, + 0x96, 0x7e, 0x6b, 0x5b, 0xc9, 0x42, 0x11, 0xb6, 0x54, 0x71, 0x53, 0x26, 0x7c, + 0x6e, 0xe1, 0xca, 0xd0, 0xd9, 0x74, 0xa7, 0x10, 0x88, 0x58, 0x37, 0x35, 0xe4, + 0xf6, 0x3d, 0x33, 0x15, 0x6d, 0xad, 0xd5, 0x4c, 0x2f, 0xaf, 0x89, 0x11, 0x4a, + 0x12, 0x7b, 0x97, 0xb9, 0x4c, 0xc2, 0xa2, 0x2e, 0xf3, 0x03, 0xf4, 0x59, 0xd0, + 0x4f, 0xc0, 0xb5, 0x3a, 0xce, 0x59, 0x18, 0xd4, 0x7f, 0xf3, 0x3a, 0x55, 0x8b, + 0xd7, 0x1a, 0x75, 0xf3, 0x55, 0xfb, 0xd0, 0x6b, 0xbc, 0xcf, 0x4e, 0x02, 0xc3, + 0xc0, 0xa4, 0xb6, 0x3d, 0x0c, 0xc9, 0x49, 0x80, 0x1d, 0x63, 0xa6, 0x4c, 0xb2, + 0xd3, 0x23, 0x73, 0xb2, 0xc7, 0xb2, 0x74, 0xab, 0x2d, 0xb4, 0x68, 0x21, 0x42, + 0xc8, 0xb2, 0x1d, 0x84, 0xc4, 0x81, 0xf5, 0xef, 0x21, 0xe4, 0xb5, 0xe3, 0x60, + 0x34, 0x51, 0xbf, 0x94, 0x77, 0x4d, 0x0e, 0xf4, 0x7f, 0x63, 0xfa, 0x6a, 0xbb, + 0x78, 0xd2, 0x1c, 0x19, 0x3c, 0xbe, 0x65, 0xb6, 0x95, 0xfe, 0x67, 0x42, 0x3c, + 0x1e, 0x2d, 0x31, 0x2e, 0x27, 0x76, 0xfa, 0x24, 0xec, 0xe8, 0x46, 0x83, 0xe7, + 0x48, 0x76, 0xc5, 0x5e, 0xa0, 0x36, 0x9e, 0x4e, 0xa0, 0xe8, 0x64, 0x94, 0xe0, + 0x0d, 0xde, 0x23, 0x6a, 0x16, 0x89, 0x73, 0x1f, 0x0a, 0x5d, 0x82, 0x03, 0xaf, + 0xde, 0x5c, 0x42, 0x36, 0x40, 0xb8, 0x1e, 0x4f, 0x63, 0x1c, 0x98, 0x1c, 0x11, + 0xa2, 0xe1, 0xd1, 0x84, 0xc6, 0x7c, 0x52, 0x8d, 0xf9, 0x2d, 0x53, 0xae, 0xc4, + 0x4a, 0x40, 0xa4, 0xea, 0x2a, 0x13, 0x1b, 0x47, 0x33, 0xcf, 0xe4, 0x5c, 0x6b, + 0x00, 0x12, 0xc3, 0xe9, 0xe2, 0x09, 0x75, 0xba, 0xae, 0xcb, 0x02, 0x32, 0xdf, + 0x88, 0x0b, 0xd7, 0xd1, 0xde, 0x13, 0xe1, 0x34, 0x94, 0x62, 0xec, 0x8d, 0x5d, + 0xf3, 0xe7, 0x80, 0xff, 0xa7, 0x2e, 0xba, 0x8a, 0x8d, 0xf7, 0xfc, 0xf3, 0x98, + 0xec, 0x23, 0x05, 0x13, 0xca, 0x9d, 0x61, 0x23, 0xf8, 0xb9, 0xd8, 0x17, 0x85, + 0x60, 0xda, 0xf9, 0x75, 0x11, 0x19, 0x55, 0xa2, 0xbc, 0xa3, 0x42, 0x3e, 0xee, + 0xfc, 0x52, 0x7b, 0xe3, 0xa8, 0x54, 0x3e, 0xb9, 0x0a, 0x5e, 0xc0, 0x2f, 0x35, + 0xa7, 0xc6, 0x4b, 0x7d, 0xd5, 0x9a, 0x72, 0xda, 0x00, 0x74, 0x63, 0x4e, 0x01, + 0xd2, 0xab, 0xf3, 0x63, 0x7a, 0xdd, 0x77, 0xc7, 0x35, 0x0f, 0x12, 0xb0, 0x11, + 0xb2, 0x94, 0x16, 0x8e, 0xc7, 0x55, 0x76, 0xe4, 0x7d, 0x16, 0x9e, 0x39, 0x38, + 0xbf, 0x6a, 0xe2, 0xaa, 0x8f, 0xf7, 0xcf, 0xba, 0x7c, 0xac, 0xb1, 0xf9, 0x2b, + 0x6e, 0x4c, 0x24, 0x97, 0xbf, 0xfa, 0x9f, 0x17, 0xca, 0xd2, 0x42, 0xfa, 0x9c, + 0x31, 0x79, 0xc1, 0xa3, 0xaa, 0x81, 0xf7, 0x36, 0x16, 0x49, 0x57, 0x2c, 0x71, + 0x5c, 0x25, 0xa1, 0xf6, 0xcd, 0x5a, 0xce, 0x82, 0xc0, 0x0a, 0xb2, 0x34, 0x2b, + 0x9c, 0x3c, 0xb4, 0xff, 0xfd, 0xda, 0x16, 0x0c, 0xa5, 0xab, 0x9e, 0x9b, 0xaf, + 0x21, 0x39, 0xef, 0x9a, 0xfb, 0xe1, 0xb1, 0xf3, 0x09, 0x46, 0x2a, 0xfc, 0xe4, + 0x62, 0xa7, 0x9b, 0xb9, 0x69, 0x8e, 0x22, 0xc9, 0x57, 0xc5, 0x90, 0xa7, 0x53, + 0xa7, 0x6b, 0x87, 0xe0, 0x09, 0x12, 0x1e, 0x06, 0xf6, 0xa1, 0xbf, 0x62, 0xa0, + 0x8b, 0xf4, 0x35, 0xd9, 0x2e, 0x2f, 0xff, 0xe8, 0x6e, 0x2a, 0x9c, 0xbb, 0xa9, + 0x13, 0x3a, 0x68, 0xe4, 0xae, 0xbf, 0x33, 0xc3, 0x84, 0x36, 0xf2, 0x54, 0x5f, + 0xc2, 0xd5, 0x28, 0x32, 0xd1, 0x65, 0xaf, 0x41, 0x5b, 0x24, 0x4a, 0xdc, 0x5f, + 0x57, 0x37, 0x7d, 0xee, 0xdf, 0x46, 0x0a, 0xa3, 0xbe, 0xb4, 0x34, 0x19, 0xc6, + 0xb0, 0x82, 0xe8, 0x35, 0xce, 0x84, 0xca, 0x13, 0xb6, 0x90, 0x8a, 0x88, 0x13, + 0xc0, 0x21, 0xde, 0x9f, 0xa9, 0xa4, 0x4e, 0x4c, 0x18, 0xdc, 0xb3, 0xd2, 0x1f, + 0xaa, 0xbd, 0xb4, 0x19, 0x31, 0xb2, 0xfd, 0x49, 0x76, 0x44, 0xdc, 0x3a, 0x15, + 0x07, 0xfa, 0x5a, 0xc7, 0xc7, 0x6b, 0xee, 0xbb, 0xdb, 0xd1, 0xd4, 0x92, 0x99, + 0xa5, 0x5b, 0xd4, 0x99, 0x27, 0xe9, 0xd7, 0xf4, 0x88, 0x4e, 0x6e, 0xd3, 0xfd, + 0x5e, 0x4b, 0x7c, 0xb8, 0x35, 0xb8, 0x33, 0x08, 0x96, 0x4e, 0x3c, 0x46, 0x87, + 0x3f, 0xd6, 0x13, 0x31, 0x7b, 0x91, 0xd2, 0x92, 0x36, 0xea, 0x90, 0xe3, 0x65, + 0xd1, 0x62, 0xcc, 0x05, 0x1c, 0x84, 0x6d, 0x24, 0x21, 0x76, 0xda, 0xf6, 0xd2, + 0x86, 0x18, 0xae, 0x31, 0xfb, 0xaa, 0xe9, 0x99, 0xa9, 0x3f, 0x17, 0x5c, 0x69, + 0x38, 0xe6, 0x31, 0xa0, 0x81, 0xf2, 0xc1, 0xf3, 0xfd, 0x78, 0x25, 0x49, 0xd3, + 0xf3, 0x24, 0x57, 0x59, 0x60, 0x6d, 0x9f, 0x92, 0xd5, 0x54, 0x8a, 0xcf, 0xea, + 0xdb, 0xaf, 0x9c, 0xaa, 0x6b, 0x93, 0xdc, 0x08, 0x82, 0x8d, 0x74, 0xf6, 0xd5, + 0xfd, 0xd8, 0x33, 0x31, 0xf0, 0x96, 0x91, 0x45, 0x95, 0x52, 0x97, 0xe6, 0x9f, + 0x00, 0xfd, 0x29, 0x87, 0xf2, 0xda, 0x2b, 0x94, 0xb9, 0x95, 0xfe, 0xcb, 0xe6, + 0x22, 0xa7, 0x35, 0xef, 0x7f, 0x12, 0x07, 0xf6, 0x71, 0x62, 0x94, 0x89, 0x20, + 0x2b, 0xea, 0x0b, 0x47, 0x5e, 0x51, 0x68, 0x1a, 0xa1, 0x67, 0x78, 0xb3, 0x9b, + 0xd9, 0x23, 0xc9, 0x8d, 0xc6, 0xff, 0x83, 0x73, 0xc7, 0x9b, 0xb1, 0x70, 0x30, + 0x41, 0x7b, 0xc2, 0x00, 0xc8, 0xf0, 0xb8, 0x55, 0xac, 0xfe, 0xc1, 0x79, 0xf7, + 0x67, 0x4c, 0xec, 0x27, 0x21, 0xa1, 0x0f, 0xca, 0x69, 0x3d, 0x83, 0xcf, 0xe5, + 0xb8, 0xcd, 0xcc, 0x18, 0xf8, 0x1a, 0xd6, 0x17, 0xfa, 0x26, 0xf0, 0xdf, 0xb8, + 0x36, 0x55, 0xb8, 0xa2, 0x9a, 0x7f, 0x83, 0x42, 0x32, 0x42, 0x5e, 0x8c, 0x47, + 0x45, 0x88, 0xf1, 0x8d, 0xd3, 0x26, 0xaa, 0x39, 0x6c, 0x3e, 0x47, 0x75, 0xe0, + 0x02, 0x05, 0xfc, 0x9e, 0x45, 0xf7, 0xb7, 0xd2, 0xe6, 0xd5, 0x5d, 0xcb, 0x90, + 0xe2, 0x3f, 0xf6, 0xb5, 0x08, 0x45, 0x9a, 0xa6, 0x99, 0xbf, 0xcb, 0xd5, 0x6f, + 0x10, 0x99, 0x77, 0x64, 0xd0, 0x87, 0x40, 0x89, 0x86, 0xe7, 0x3d, 0x6e, 0x28, + 0x4f, 0xea, 0x9a, 0x23, 0xc3, 0x93, 0x11, 0x78, 0x2f, 0x86, 0xca, 0xbf, 0xf9, + 0x45, 0x5e, 0x4c, 0xf6, 0x99, 0xe5, 0xf5, 0xd4, 0xbc, 0x0b, 0x39, 0x05, 0xa4, + 0xe3, 0xbd, 0x01, 0xc5, 0x4d, 0xf8, 0x64, 0x34, 0x43, 0xbe, 0x0f, 0x88, 0x90, + 0x32, 0xea, 0x32, 0x5b, 0xf0, 0x71, 0x07, 0xfd, 0x41, 0xd6, 0x73, 0xee, 0xba, + 0xe6, 0xfa, 0x63, 0x7b, 0x70, 0xcc, 0x0e, 0xd3, 0xf0, 0x09, 0x58, 0xdf, 0xb8, + 0xdc, 0xf0, 0x0e, 0x85, 0xa1, 0xd0, 0xa6, 0xa8, 0x90, 0x81, 0x40, 0xc2, 0xf4, + 0x34, 0xc2, 0xe2, 0x60, 0xef, 0xb0, 0xbc, 0xa2, 0x00, 0x35, 0x04, 0xc9, 0x99, + 0x93, 0xa9, 0xe1, 0xc0, 0xff, 0x9c, 0xef, 0xe6, 0xa6, 0x65, 0xd7, 0x91, 0x42, + 0x86, 0x90, 0xe4, 0x7e, 0xf8, 0xc1, 0x31, 0xa8, 0xe9, 0xbf, 0xb4, 0xc3, 0x08, + 0x02, 0x35, 0x03, 0x2d, 0x73, 0x1b, 0x0d, 0x38, 0x41, 0x22, 0x5f, 0x1c, 0x11, + 0xe2, 0xc2, 0x8e, 0xe8, 0x4d, 0x35, 0xf9, 0x22, 0x61, 0x00, 0x56, 0x59, 0x72, + 0xeb, 0x26, 0x9d, 0x27, 0x8e, 0xf6, 0x49, 0x79, 0xbf, 0x65, 0x15, 0xed, 0x4a, + 0x68, 0x40, 0xb0, 0x88, 0x3a, 0x9e, 0x6e, 0xf6, 0x4a, 0x0e, 0xfc, 0xae, 0x1c, + 0xf2, 0x1d, 0xfe, 0x74, 0x85, 0x4e, 0x84, 0xc2, 0x74, 0x9f, 0xac, 0x03, 0x82, + 0x52, 0x75, 0xc9, 0xb6, 0x30, 0x21, 0x84, 0xc7, 0x2d, 0xf4, 0xc4, 0xbb, 0x28, + 0x62, 0xe4, 0xe8, 0xa7, 0xd9, 0xa4, 0xa2, 0x82, 0x86, 0x6f, 0x9a, 0x7b, 0x2c, + 0xfc, 0x9a, 0x56, 0x31, 0x3d, 0xa0, 0xc4, 0x7a, 0x34, 0xb7, 0xb9, 0xcd, 0xa3, + 0xac, 0xe8, 0x18, 0x5f, 0x07, 0xdf, 0x36, 0xe4, 0x48, 0xa7, 0x6a, 0xa4, 0x77, + 0xf2, 0x24, 0xd8, 0x7a, 0x07, 0x4f, 0x43, 0xaf, 0x5d, 0x5f, 0x79, 0xb3, 0xab, + 0x11, 0x28, 0xf0, 0x81, 0x91, 0x44, 0x7f, 0xa6, 0x46, 0xbf, 0xdd, 0xe5, 0xb5, + 0x1e, 0x23, 0x3c, 0xa6, 0x15, 0x5d, 0x10, 0x15, 0x85, 0xbc, 0x2c, 0x40, 0x15, + 0x8a, 0xc2, 0x10, 0x6e, 0x66, 0xa2, 0x6e, 0x46, 0x42, 0x33, 0x70, 0x63, 0x68, + 0x76, 0xb4, 0x34, 0xa7, 0x4f, 0x8c, 0xe8, 0x06, 0x00, 0x50, 0xb0, 0x82, 0xa7, + 0x9b, 0x61, 0xbb, 0x5d, 0x34, 0x4e, 0xb5, 0xa1, 0x15, 0x83, 0x26, 0xce, 0xd9, + 0xa9, 0xd9, 0xf5, 0x4f, 0xb2, 0xfe, 0x8f, 0x9f, 0x05, 0xcd, 0x11, 0x1e, 0xe4, + 0x6c, 0x47, 0x10, 0xf6, 0xf6, 0x3a, 0x62, 0x69, 0x45, 0x57, + ], + script_code: Script(vec![0x53, 0x52, 0x00]), + transparent_input: Some(1), + hash_type: 1, + amount: 1564816348934332, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x21, 0x46, 0x62, 0xc6, 0x74, 0x50, 0x60, 0x3d, 0x8a, 0xa7, 0x3b, 0xea, 0xbb, + 0xf7, 0x51, 0x8d, 0x03, 0x6c, 0xe9, 0x1d, 0xc8, 0x7b, 0x01, 0x81, 0xe8, 0xa0, + 0xf3, 0xfa, 0x82, 0x2c, 0x7d, 0x8a, + ], + }, + Test0143Vector { + tx: vec![ + 0x03, 0x00, 0x00, 0x80, 0x70, 0x82, 0xc4, 0x03, 0x01, 0x59, 0x07, 0x92, 0x9a, + 0x2f, 0x3f, 0xdb, 0x0d, 0x8f, 0x79, 0x14, 0xc4, 0x2d, 0xde, 0x2d, 0x20, 0x00, + 0xf5, 0xae, 0x02, 0xd4, 0x18, 0x21, 0xc8, 0xe1, 0xee, 0x01, 0x38, 0xeb, 0xcb, + 0x72, 0x8d, 0x7c, 0x6c, 0x3c, 0x80, 0x02, 0x65, 0x53, 0x75, 0x94, 0xc6, 0x70, + 0x00, 0x6f, 0x39, 0x08, 0x22, 0x2e, 0x89, 0xd1, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x27, 0x1a, 0xbe, 0x66, 0x0e, 0x39, 0xe0, 0x51, 0xaa, 0xa6, 0xfc, 0xa1, + 0x86, 0x22, 0x76, 0xe2, 0xba, 0xa0, 0xfe, 0x0b, 0x16, 0x2a, 0xeb, 0xcf, 0xe3, + 0xd9, 0x34, 0x9c, 0x8d, 0x15, 0x4b, 0xb7, 0xee, 0x28, 0x21, 0x2c, 0x1b, 0xaa, + 0x70, 0x5d, 0x82, 0x07, 0x0d, 0x70, 0x32, 0xf2, 0x69, 0x5d, 0x17, 0x96, 0x80, + 0x9f, 0xab, 0x41, 0x24, 0x69, 0x26, 0xaf, 0x99, 0x2b, 0x6e, 0xee, 0x95, 0xa9, + 0xa0, 0x6b, 0xc4, 0x56, 0x2c, 0x5f, 0x2f, 0x1b, 0x19, 0x54, 0x95, 0x00, 0x37, + 0x2e, 0x7a, 0xd5, 0x79, 0xa6, 0xd6, 0xd7, 0x8b, 0x33, 0x15, 0x31, 0x30, 0xfb, + 0x44, 0x8f, 0xb7, 0x9e, 0x8a, 0x66, 0x9d, 0xb8, 0xa0, 0xf3, 0x5c, 0xdf, 0x9a, + 0xe5, 0xd3, 0x2d, 0x73, 0x2f, 0xc7, 0x94, 0x18, 0xe2, 0x3b, 0x45, 0x1d, 0xdc, + 0x95, 0xa2, 0x2a, 0xba, 0xbb, 0x05, 0x6e, 0xc6, 0xb5, 0xe8, 0xba, 0x4f, 0x52, + 0x4d, 0xfa, 0xfe, 0x87, 0x52, 0x62, 0xdd, 0x7b, 0xe4, 0x1c, 0xbb, 0xc6, 0x24, + 0x20, 0xd4, 0xad, 0x6d, 0xf5, 0xc9, 0xb7, 0x13, 0x60, 0x4f, 0x65, 0x60, 0x88, + 0xa4, 0x48, 0x5e, 0x93, 0xbe, 0x19, 0x07, 0xd2, 0x7a, 0xc6, 0xec, 0x3c, 0x57, + 0x25, 0x9b, 0xd6, 0x98, 0x1d, 0x42, 0xc1, 0xb7, 0x8a, 0x29, 0xad, 0x96, 0x85, + 0xe6, 0x3c, 0x49, 0x4d, 0x41, 0x29, 0x62, 0x3e, 0xa1, 0xa7, 0xff, 0xec, 0x85, + 0xfa, 0x29, 0x41, 0x10, 0x73, 0xed, 0xb2, 0x97, 0x8e, 0xf4, 0xe4, 0x69, 0xdd, + 0xd5, 0xcd, 0xa9, 0x86, 0x18, 0x99, 0x95, 0xf8, 0x8d, 0x6a, 0xb3, 0x66, 0xdb, + 0x01, 0x90, 0x01, 0xf5, 0xb2, 0x52, 0x88, 0xcf, 0x86, 0x0f, 0xd9, 0x98, 0xee, + 0x57, 0x3c, 0x8c, 0xc4, 0x8a, 0xa9, 0xef, 0xcf, 0x9b, 0x61, 0x7e, 0x04, 0x3c, + 0x32, 0x9c, 0xd1, 0xaa, 0x1a, 0x0e, 0xd3, 0xa4, 0x02, 0xfb, 0x96, 0xe3, 0x36, + 0xc7, 0x19, 0xe6, 0x25, 0x3c, 0xb6, 0x91, 0xaa, 0x0d, 0xb5, 0x27, 0x36, 0x62, + 0x6e, 0xd1, 0x97, 0x88, 0x75, 0x88, 0x8e, 0xc7, 0x6c, 0x84, 0x6b, 0xc2, 0x27, + 0x27, 0x2a, 0x02, 0x53, 0x17, 0xdf, 0xf0, 0xb1, 0x14, 0x8d, 0x92, 0xd6, 0xf5, + 0xfb, 0x7d, 0x95, 0x33, 0x67, 0x70, 0xa7, 0xd1, 0x6f, 0xac, 0x1a, 0xdd, 0x86, + 0x07, 0x76, 0xcb, 0x48, 0x02, 0x21, 0xf8, 0xfb, 0x33, 0x03, 0xe4, 0xe9, 0xb0, + 0x79, 0x02, 0xd2, 0xff, 0x86, 0xfd, 0xac, 0x72, 0x09, 0x62, 0x34, 0xae, 0xd4, + 0x8d, 0xe8, 0x92, 0xff, 0x73, 0x55, 0x07, 0x3b, 0xbf, 0x06, 0x15, 0xf6, 0x7b, + 0x11, 0x00, 0xcc, 0x0a, 0xa3, 0xba, 0x3d, 0x6c, 0x1a, 0x1a, 0x90, 0x87, 0xb1, + 0x19, 0xba, 0xee, 0xbf, 0xa6, 0x2b, 0xc9, 0xf0, 0xec, 0x47, 0x9d, 0x99, 0xc1, + 0xa3, 0xb1, 0x58, 0xb5, 0x14, 0xd1, 0x62, 0x9d, 0xb3, 0x99, 0x3f, 0x11, 0x67, + 0x2a, 0x26, 0x70, 0x8e, 0x5a, 0xd8, 0x16, 0xb5, 0x47, 0xab, 0x7e, 0x82, 0x7d, + 0x07, 0x1b, 0xa7, 0x84, 0x2b, 0x3e, 0x90, 0x30, 0x53, 0x83, 0x89, 0x6e, 0xc4, + 0x90, 0x5f, 0x70, 0x03, 0x8b, 0x69, 0x4e, 0x6a, 0x5a, 0x3e, 0x43, 0x12, 0xcd, + 0x82, 0x08, 0x13, 0x2b, 0x84, 0x0f, 0x05, 0xc7, 0x14, 0x52, 0x3c, 0xa8, 0x19, + 0x72, 0x0a, 0xe2, 0x27, 0xfd, 0x1a, 0xcb, 0xa7, 0x14, 0xfa, 0x03, 0xc4, 0x5f, + 0xc5, 0x39, 0x88, 0x57, 0xb4, 0x0d, 0xc1, 0x48, 0x79, 0x85, 0x6f, 0x35, 0x4b, + 0xa4, 0xd2, 0x58, 0x1d, 0x0c, 0xda, 0x54, 0xb6, 0x38, 0xba, 0x9d, 0x76, 0xf9, + 0xb5, 0x2d, 0x17, 0xc8, 0x02, 0x8e, 0xe6, 0x3f, 0x58, 0x45, 0xb5, 0xdc, 0xef, + 0xa4, 0xc3, 0x47, 0x9b, 0xce, 0x9a, 0xca, 0xd1, 0x8b, 0x4a, 0xea, 0xe0, 0x3c, + 0x0e, 0xae, 0x22, 0x5d, 0x42, 0x84, 0x8b, 0xde, 0xaa, 0x53, 0x6d, 0x03, 0x8d, + 0xd3, 0xbc, 0x97, 0x9f, 0x06, 0x58, 0x66, 0x73, 0xbc, 0x6f, 0xf1, 0xc5, 0xd3, + 0xb3, 0x20, 0xf3, 0x49, 0xa5, 0xb3, 0xa8, 0xb3, 0x55, 0x59, 0x22, 0x96, 0xaa, + 0xf6, 0x1c, 0x5b, 0x72, 0x52, 0x03, 0x3e, 0xc0, 0xa9, 0x46, 0x6a, 0x1b, 0x85, + 0x76, 0x4f, 0xb0, 0x83, 0x1b, 0x4a, 0x1a, 0x36, 0x89, 0x0e, 0x22, 0x4c, 0x01, + 0xac, 0xfc, 0xe4, 0x8e, 0xe3, 0xed, 0x93, 0x87, 0x73, 0x98, 0xe0, 0x72, 0x6d, + 0x02, 0x93, 0x6d, 0x0d, 0x03, 0x2e, 0x18, 0xe3, 0x28, 0x8b, 0x26, 0x70, 0xe1, + 0x36, 0x2c, 0x32, 0xd6, 0xe4, 0x73, 0x3b, 0x9d, 0xd2, 0xd5, 0xf2, 0x6e, 0x1f, + 0xe3, 0x06, 0xf7, 0x3c, 0x00, 0x7f, 0xdd, 0xca, 0xe9, 0xd9, 0xc0, 0xaa, 0xf1, + 0x87, 0xd7, 0x42, 0x8b, 0x1e, 0x9d, 0x47, 0x9c, 0x18, 0x23, 0x7b, 0x98, 0x28, + 0xbc, 0xa8, 0xb9, 0x8c, 0x9d, 0x9b, 0xec, 0x7d, 0x82, 0x70, 0xb5, 0xd8, 0xee, + 0xc3, 0xcc, 0x4f, 0x43, 0xfa, 0x01, 0x88, 0x52, 0x1b, 0xc6, 0x1b, 0x21, 0xdd, + 0x04, 0xe3, 0x7a, 0x83, 0xec, 0xe6, 0x8c, 0xa7, 0xa2, 0xfa, 0x6c, 0x8f, 0x9e, + 0x34, 0xa6, 0x29, 0x03, 0x35, 0xaa, 0x1f, 0xbd, 0x83, 0xd5, 0x4a, 0xaf, 0x44, + 0x1e, 0x31, 0x9e, 0xa4, 0x7a, 0x86, 0x2a, 0xd0, 0x29, 0x3c, 0xed, 0xf5, 0xdd, + 0x9e, 0xda, 0xde, 0xee, 0x33, 0xcb, 0x52, 0x2c, 0xd0, 0x11, 0x8b, 0xbd, 0x81, + 0x1a, 0xce, 0x9a, 0x23, 0xbd, 0xa3, 0x9a, 0xba, 0x72, 0xf1, 0x56, 0x6f, 0xc1, + 0x68, 0x84, 0x97, 0xd2, 0xa7, 0x92, 0x8c, 0x36, 0x70, 0x15, 0x25, 0x67, 0x8b, + 0xc9, 0x72, 0x14, 0xb3, 0x1b, 0x37, 0xba, 0xb4, 0x6b, 0x88, 0xf2, 0x7f, 0x04, + 0x48, 0xde, 0xcb, 0x31, 0x62, 0x2d, 0x0f, 0x0f, 0x87, 0xa8, 0x55, 0xba, 0x54, + 0x00, 0x03, 0x32, 0x03, 0x1f, 0x73, 0xab, 0xff, 0xd4, 0x65, 0x91, 0xda, 0x0b, + 0x88, 0x72, 0x35, 0x04, 0xed, 0xb2, 0x33, 0x72, 0x30, 0xda, 0xd2, 0xac, 0xc0, + 0xd8, 0xbb, 0x68, 0xbc, 0x83, 0x7a, 0x2f, 0xf9, 0x30, 0xbf, 0xf0, 0x6f, 0xde, + 0x74, 0xeb, 0x90, 0xaa, 0xe4, 0xf6, 0x0d, 0xbb, 0x6e, 0xb8, 0x27, 0xea, 0x99, + 0x88, 0x4a, 0xcd, 0x62, 0x85, 0xa9, 0x88, 0x92, 0x80, 0x2c, 0xf5, 0x9d, 0x5d, + 0x60, 0xd0, 0x16, 0x63, 0x38, 0x7b, 0x3e, 0xd2, 0x72, 0x3b, 0xd6, 0x48, 0x9e, + 0x9c, 0x2c, 0x10, 0x6d, 0x4a, 0xa2, 0xde, 0x23, 0xce, 0xd1, 0x6c, 0x72, 0x04, + 0x29, 0xc7, 0x75, 0x3a, 0x77, 0x38, 0xec, 0x7d, 0x9d, 0xb8, 0x62, 0x42, 0x29, + 0xed, 0xd2, 0x17, 0xb8, 0x0d, 0x74, 0x87, 0x5a, 0x14, 0xca, 0xe4, 0x86, 0x3f, + 0x13, 0x9e, 0x9c, 0x0b, 0x13, 0x1b, 0x2a, 0x4c, 0x28, 0x07, 0x1a, 0x38, 0xec, + 0x61, 0xf6, 0x68, 0x01, 0xaa, 0x59, 0x56, 0xfc, 0xb2, 0xa4, 0x6b, 0x95, 0x87, + 0x66, 0x5b, 0x75, 0x71, 0xaa, 0x03, 0x48, 0x1f, 0xd8, 0xd9, 0xd5, 0x69, 0x8f, + 0x83, 0x6f, 0xc8, 0x63, 0x5e, 0x69, 0xe3, 0xbd, 0xe4, 0x2f, 0x4a, 0xc0, 0x71, + 0x32, 0x8b, 0x54, 0x09, 0xf6, 0xe4, 0x2d, 0x79, 0x0a, 0xed, 0xd7, 0x3b, 0xc1, + 0xa2, 0x35, 0x47, 0x23, 0xb3, 0xb8, 0x19, 0xd0, 0x63, 0x7a, 0x6f, 0xa4, 0x66, + 0x39, 0x46, 0xa3, 0x0a, 0xc5, 0xaf, 0xdd, 0x30, 0xce, 0x83, 0x0f, 0x67, 0x91, + 0xb4, 0x57, 0x52, 0x70, 0xa1, 0x72, 0x0f, 0x91, 0x86, 0x6e, 0x2b, 0x86, 0xf4, + 0x78, 0x88, 0x94, 0xc8, 0xda, 0x62, 0xd8, 0xb9, 0x1f, 0xaf, 0x52, 0x0e, 0x3b, + 0xed, 0xbc, 0x12, 0x06, 0xa5, 0xa5, 0xe6, 0xef, 0xd3, 0xdf, 0xde, 0x08, 0x43, + 0xc3, 0xb0, 0x67, 0x57, 0x64, 0x3f, 0xc0, 0x06, 0x00, 0x88, 0x38, 0xca, 0x47, + 0x30, 0x87, 0xf8, 0x97, 0x79, 0x18, 0xcc, 0x1b, 0x81, 0xc9, 0xe6, 0x8e, 0x3b, + 0x88, 0x8f, 0xe6, 0xf7, 0xc6, 0x30, 0xf1, 0xbc, 0x7a, 0xe1, 0x88, 0xf5, 0x12, + 0x84, 0x20, 0x41, 0xca, 0xda, 0x1e, 0x05, 0xf8, 0x66, 0xd2, 0x56, 0x2d, 0xbe, + 0x09, 0xc4, 0xb4, 0x30, 0x68, 0xf7, 0x54, 0xda, 0xd3, 0x4d, 0xf0, 0xfc, 0xfc, + 0x18, 0x1f, 0x31, 0x80, 0x1a, 0x79, 0x92, 0xd2, 0xf1, 0x6b, 0xe0, 0x21, 0x1b, + 0x4a, 0x22, 0xf6, 0x2a, 0xab, 0x64, 0x70, 0x1b, 0xf4, 0xa4, 0xe6, 0xd6, 0x66, + 0xfc, 0x30, 0x4a, 0x5c, 0x79, 0xc6, 0x09, 0xac, 0xc4, 0x3b, 0x00, 0xb4, 0x86, + 0x48, 0x93, 0xd3, 0x7d, 0x50, 0x07, 0xf0, 0xc3, 0x29, 0xa4, 0x75, 0x50, 0x52, + 0x57, 0x75, 0x70, 0xdd, 0x38, 0xfa, 0xc0, 0x43, 0xcd, 0x91, 0xc1, 0x2e, 0xe3, + 0x4e, 0x9c, 0xfa, 0xe3, 0x92, 0xa7, 0x8b, 0xda, 0xbd, 0x4e, 0xe3, 0x1d, 0xc0, + 0xde, 0xb0, 0x2f, 0xe7, 0xb1, 0xd8, 0xb0, 0x17, 0x8a, 0xc9, 0x51, 0x31, 0x05, + 0xfc, 0xc7, 0xe3, 0x0b, 0xa8, 0xe0, 0x16, 0xaa, 0x36, 0xa6, 0xb5, 0xdf, 0x5e, + 0x5a, 0x19, 0x09, 0xf6, 0x3a, 0xba, 0x09, 0x5d, 0x98, 0x77, 0xa8, 0xf2, 0xdc, + 0x53, 0xf4, 0x6f, 0x6c, 0x9b, 0x07, 0xad, 0xdf, 0x14, 0x6f, 0x4f, 0xfa, 0x50, + 0x1f, 0x9d, 0xd3, 0xcf, 0xf9, 0x24, 0xe3, 0x01, 0x0f, 0xaf, 0x50, 0x4e, 0x2b, + 0x8a, 0xca, 0x73, 0x57, 0xac, 0xbf, 0xfe, 0xc7, 0x3a, 0xc3, 0x4c, 0x1a, 0x73, + 0x16, 0x0f, 0x2c, 0xea, 0x1e, 0x05, 0x10, 0xf8, 0x4d, 0x2f, 0xe2, 0xf7, 0x3b, + 0x6e, 0x92, 0x19, 0x07, 0xa1, 0xb7, 0xb3, 0x75, 0x12, 0x13, 0x24, 0x1b, 0x2c, + 0xfa, 0xa5, 0x5a, 0x5e, 0xa4, 0xdd, 0x51, 0x7e, 0x7b, 0x49, 0xd2, 0xde, 0x8c, + 0x09, 0x08, 0x43, 0x73, 0x0d, 0x24, 0x08, 0xa2, 0xa3, 0x04, 0xaa, 0x1e, 0x2e, + 0x13, 0x70, 0xa6, 0xbf, 0x6c, 0x2b, 0xc7, 0x3f, 0xf0, 0x0d, 0x89, 0x3b, 0xc1, + 0x28, 0x5e, 0xfc, 0xa8, 0x25, 0x99, 0xd1, 0x81, 0xf1, 0x23, 0x51, 0xf9, 0x39, + 0xa9, 0x4e, 0xa8, 0xb9, 0x75, 0xc0, 0x65, 0xa9, 0x1f, 0xf2, 0x57, 0xca, 0xc7, + 0xa9, 0x23, 0x85, 0xfc, 0x8f, 0xa9, 0x21, 0xb1, 0x06, 0xba, 0x86, 0x60, 0xc6, + 0x0a, 0xc8, 0xba, 0x5e, 0xce, 0x45, 0x60, 0x6f, 0x04, 0xf3, 0x6a, 0x3a, 0x90, + 0xbb, 0x38, 0x38, 0xc4, 0x2a, 0xbf, 0x62, 0xdd, 0x2d, 0x84, 0xba, 0xbe, 0xf3, + 0xe1, 0x88, 0xe9, 0x17, 0x1a, 0xff, 0x9b, 0xc1, 0x16, 0x66, 0x90, 0x09, 0xd8, + 0x87, 0x13, 0x0a, 0xc9, 0xf7, 0x39, 0x6a, 0x62, 0x7a, 0x84, 0x74, 0xc1, 0x81, + 0x1b, 0x69, 0x6f, 0x99, 0x55, 0x2b, 0x14, 0xc4, 0x84, 0xdf, 0xe4, 0x2c, 0x24, + 0xd5, 0x7c, 0x3a, 0x9c, 0x3f, 0xea, 0x13, 0x76, 0xcd, 0xcb, 0x63, 0x42, 0x1c, + 0x31, 0x4a, 0x62, 0x2a, 0x9a, 0xef, 0x0b, 0xc0, 0x57, 0xcb, 0x11, 0xbc, 0x5e, + 0x30, 0x66, 0xe3, 0x3a, 0x3b, 0x9b, 0x31, 0xdf, 0x25, 0x75, 0xcd, 0x51, 0x85, + 0xa4, 0xf3, 0xfc, 0x4e, 0x4c, 0x3d, 0x40, 0x2e, 0xd4, 0x20, 0x46, 0xf8, 0x1f, + 0x97, 0x48, 0x16, 0xd2, 0x79, 0xb1, 0x51, 0x3a, 0xb8, 0x1d, 0x3f, 0x0a, 0x3c, + 0x7f, 0x7f, 0xcf, 0x2f, 0xbb, 0x4e, 0x26, 0x32, 0x19, 0x93, 0xa5, 0x13, 0xad, + 0x3d, 0x7f, 0x4a, 0xfe, 0x6c, 0x1b, 0xbd, 0xc6, 0x57, 0x58, 0x50, 0x80, 0xbb, + 0x5a, 0x0f, 0x25, 0x97, 0x3d, 0x63, 0xeb, 0x20, 0xad, 0xa0, 0x16, 0x6b, 0xbd, + 0x8a, 0x39, 0xff, 0x93, 0x24, 0x6f, 0x27, 0x89, 0x73, 0x2a, 0xd0, 0x55, 0x87, + 0xf8, 0xdb, 0x7b, 0xc8, 0x7c, 0x24, 0x2c, 0xfd, 0x36, 0xce, 0x68, 0x5a, 0x4b, + 0x65, 0x69, 0x86, 0xc3, 0x9f, 0xd7, 0xfc, 0xb2, 0x3c, 0x91, 0x91, 0x3e, 0x46, + 0x11, 0x19, 0x1e, 0xdc, 0xc8, 0x8b, 0x78, 0xf1, 0x45, 0xea, 0x29, 0xd2, 0x71, + 0xb9, 0x40, 0xc6, 0x99, 0x41, 0xe4, 0xc3, 0xfd, 0x2d, 0x71, 0xf3, 0xb1, 0x90, + 0x69, 0x0e, 0xe1, 0x6f, 0x5d, 0x14, 0xac, 0x22, 0x24, 0xe6, 0xfc, 0x89, 0x59, + 0x76, 0x54, 0x52, 0x7d, 0xab, 0xe7, 0x2e, 0x75, 0xd2, 0xd2, 0xa1, 0x3a, 0x9f, + 0xba, 0xa6, 0x37, 0x8e, 0x8a, 0x26, 0x43, 0x21, 0x08, 0x7a, 0x19, 0x00, 0xef, + 0xe3, 0xca, 0xd1, 0x4a, 0x57, 0x96, 0x86, 0xaa, 0x36, 0x36, 0xbd, 0x37, 0x5b, + 0xd3, 0x13, 0x6b, 0xee, 0x0b, 0xda, 0xab, 0xcf, 0xac, 0x88, 0x1b, 0xc7, 0x01, + 0x81, 0x27, 0x21, 0xe6, 0xfb, 0x75, 0xaa, 0x07, 0x2d, 0x2d, 0x18, 0x7e, 0x62, + 0x25, 0x8d, 0x65, 0xa1, 0x92, 0x15, 0x7c, 0xdf, 0x2e, 0xc3, 0x21, 0x40, 0x7f, + 0x68, 0x2f, 0x5e, 0xec, 0x6a, 0x32, 0x97, 0xab, 0x20, 0xb7, 0x06, 0x1c, 0x62, + 0x24, 0x57, 0x16, 0xa4, 0x4f, 0x71, 0xfb, 0xfc, 0x34, 0xc7, 0x9b, 0x44, 0xe0, + 0x9e, 0x42, 0x12, 0xac, 0x26, 0x53, 0xf6, 0xc4, 0x03, 0x64, 0x3e, 0x1c, 0x5b, + 0x9a, 0xd1, 0x34, 0xd8, 0x9c, 0x68, 0x0b, 0x70, 0x72, 0x83, 0xaf, 0x54, 0x32, + 0x6f, 0xc4, 0xf8, 0x4d, 0x6a, 0x58, 0x29, 0xa0, 0xad, 0x48, 0x30, 0x80, 0x6c, + 0x05, 0x75, 0x84, 0x92, 0xcd, 0x6a, 0xc4, 0x6b, 0xa0, 0x1a, 0x2b, 0x37, 0x22, + 0xb5, 0xe4, 0xcd, 0xaf, 0xbb, 0x3f, 0x36, 0x78, 0x5f, 0x42, 0x4a, 0xf0, 0x44, + 0xda, 0xc5, 0xdb, 0x5f, 0x7d, 0xf8, 0x39, 0xeb, 0x63, 0xc0, 0xc1, 0x7d, 0x8b, + 0x0c, 0x79, 0xdb, 0x86, 0x30, 0x94, 0x20, 0x15, 0xbe, 0x13, 0xf7, 0x9a, 0xf6, + 0xf4, 0x3e, 0x5a, 0xb0, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x14, 0x79, 0x8f, 0x44, + 0x22, 0x58, 0xee, 0xdc, 0x43, 0x6f, 0xcc, 0x38, 0x6b, 0x36, 0xb5, 0x7e, 0x19, + 0x17, 0xd7, 0x20, 0x17, 0x73, 0x66, 0xf4, 0x24, 0xb0, 0xa5, 0x4b, 0x0b, 0x60, + 0xf4, 0xfb, 0x13, 0x58, 0xc2, 0x0a, 0xa4, 0x1d, 0xc5, 0x02, 0xe1, 0xdd, 0x8a, + 0x16, 0x33, 0xf3, 0xd8, 0xe3, 0x27, 0x6b, 0x59, 0xe7, 0xd2, 0xc4, 0xe6, 0x24, + 0xa6, 0xf5, 0x36, 0x95, 0xbc, 0xaf, 0x24, 0x7e, 0x36, 0x48, 0x3f, 0x13, 0xb2, + 0x04, 0x42, 0x22, 0x37, 0xfc, 0x6a, 0xb3, 0xeb, 0xa0, 0x2f, 0xc4, 0x14, 0x2b, + 0x42, 0x97, 0xeb, 0xb5, 0x68, 0x3d, 0xb8, 0xd2, 0x43, 0x19, 0x70, 0x6a, 0xd2, + 0x6a, 0xaf, 0xd8, 0x1c, 0x53, 0xb7, 0x40, 0xf3, 0x45, 0x43, 0xa6, 0xb3, 0xe9, + 0xf5, 0xbb, 0x7d, 0x5c, 0x49, 0xe8, 0xc3, 0x7f, 0x61, 0x49, 0x21, 0x25, 0x4f, + 0x32, 0x12, 0x39, 0x4c, 0x79, 0x7d, 0x1c, 0xee, 0x78, 0x99, 0xb7, 0xb4, 0xb6, + 0x5b, 0x59, 0xb7, 0x34, 0x2f, 0x92, 0x53, 0x1c, 0x1d, 0x59, 0xe1, 0x79, 0x70, + 0xb7, 0x31, 0x74, 0x14, 0x43, 0x8c, 0xd8, 0x0b, 0xd0, 0xf9, 0xa6, 0x7c, 0x9b, + 0x9e, 0x55, 0x2f, 0x01, 0x3c, 0x11, 0x5a, 0x95, 0x4f, 0x35, 0xe0, 0x61, 0x6c, + 0x68, 0xd4, 0x31, 0x63, 0xd3, 0x34, 0xda, 0xc3, 0x82, 0x70, 0x33, 0xe5, 0xad, + 0x84, 0x88, 0xbf, 0xd9, 0xc4, 0xbb, 0xbe, 0x8f, 0x59, 0x35, 0xc6, 0xc5, 0xea, + 0x04, 0xc3, 0xad, 0x49, 0xc7, 0x47, 0xa9, 0xe7, 0x23, 0x1b, 0xcd, 0x7d, 0x16, + 0x21, 0x5e, 0x6e, 0x80, 0x73, 0x7d, 0x6b, 0x54, 0xfe, 0xc8, 0xb8, 0x84, 0x02, + 0xf0, 0x47, 0x52, 0x45, 0xe1, 0x74, 0xa7, 0x45, 0xb8, 0x31, 0xf8, 0xfe, 0x03, + 0xa7, 0x6f, 0xb9, 0xce, 0xca, 0x4d, 0x22, 0xb7, 0x83, 0xc3, 0x28, 0xc6, 0x91, + 0x5c, 0x43, 0x40, 0x50, 0x64, 0xae, 0x56, 0xbc, 0x89, 0xe6, 0x4d, 0x15, 0x78, + 0xe4, 0xd3, 0xa3, 0x4b, 0xb9, 0x55, 0x91, 0xea, 0xf1, 0xd3, 0xda, 0x02, 0xa4, + 0x54, 0x9f, 0xa8, 0x0d, 0xb0, 0xff, 0x7c, 0xb0, 0x39, 0x93, 0x02, 0x8a, 0xe1, + 0x5a, 0x30, 0xe8, 0x79, 0x49, 0xaa, 0x08, 0x0e, 0x94, 0xab, 0xde, 0x68, 0x89, + 0x8c, 0x33, 0x92, 0xa2, 0x17, 0xd6, 0x49, 0x61, 0x6b, 0xbe, 0x73, 0x9b, 0x13, + 0xd1, 0x4d, 0xf0, 0x3f, 0x02, 0x76, 0x71, 0x48, 0x9b, 0xe0, 0xb4, 0xbe, 0xba, + 0xaf, 0xa7, 0xd1, 0xe6, 0x39, 0xd5, 0xb3, 0xe9, 0x94, 0xff, 0xb6, 0xb7, 0xa2, + 0x09, 0xf6, 0xad, 0xfe, 0x8d, 0x1e, 0x5c, 0xcf, 0x01, 0x0c, 0x19, 0x0a, 0x8a, + 0xeb, 0x18, 0xaa, 0x9d, 0x68, 0x7e, 0x24, 0xad, 0xc0, 0xb1, 0x13, 0x5c, 0x70, + 0xc9, 0x70, 0xe0, 0x90, 0x3a, 0xf6, 0xe1, 0x70, 0x81, 0xd5, 0x81, 0x8e, 0x88, + 0xb1, 0x4e, 0x4f, 0x60, 0x1b, 0x8c, 0x06, 0x3e, 0x3f, 0x43, 0x87, 0xff, 0xa2, + 0x32, 0x2a, 0x51, 0x81, 0x90, 0x9f, 0x09, 0x80, 0xd6, 0x89, 0xde, 0x7f, 0x8e, + 0x6a, 0x5c, 0x62, 0xa7, 0x77, 0xd1, 0x75, 0x00, 0x2a, 0x13, 0x7d, 0x02, 0x5b, + 0x88, 0x88, 0x92, 0x91, 0x98, 0x11, 0x7a, 0xa5, 0xd6, 0x19, 0x93, 0xe1, 0xdc, + 0xf7, 0x58, 0x76, 0xdc, 0xa6, 0x09, 0xf9, 0xd2, 0x84, 0x71, 0xf9, 0x97, 0xfa, + 0x11, 0xf9, 0x9d, 0x42, 0x3f, 0x02, 0xf1, 0x73, 0x4b, 0xe8, 0xa5, 0xff, 0x99, + 0x7d, 0x45, 0x1e, 0xb3, 0xcf, 0x4b, 0x3d, 0xfd, 0xd9, 0xd4, 0x54, 0x5c, 0x35, + 0xb2, 0xb5, 0xa7, 0xdc, 0x17, 0xa8, 0x36, 0xb1, 0x2b, 0x43, 0xbe, 0xfc, 0x03, + 0xe0, 0xa1, 0xbd, 0x36, 0x97, 0x72, 0x33, 0x80, 0x78, 0xb4, 0xff, 0x7d, 0x8e, + 0x2d, 0x97, 0x9a, 0x34, 0x41, 0xe1, 0xc8, 0xf5, 0xaf, 0xe4, 0x7b, 0x1e, 0x7d, + 0xa5, 0x6c, 0xf0, 0x06, 0x02, 0xd0, 0x03, 0x11, 0x0c, 0x05, 0xcf, 0x48, 0xfd, + 0xa3, 0xe6, 0xcc, 0xe3, 0x2a, 0x04, 0x40, 0x00, 0xf4, 0x5c, 0x6d, 0x1e, 0x69, + 0x6d, 0x24, 0x5c, 0xbd, 0x31, 0x2b, 0xdc, 0x3a, 0x3a, 0x21, 0xc9, 0x92, 0xd0, + 0x03, 0xc8, 0xcc, 0x8f, 0xa6, 0x30, 0x6d, 0x7e, 0x13, 0x0a, 0x2b, 0xa4, 0x20, + 0x18, 0xfe, 0x59, 0x69, 0x49, 0xfd, 0x82, 0x26, 0x7b, 0xcc, 0x59, 0xdd, 0x46, + 0x26, 0xef, 0xc3, 0xea, 0x74, 0x38, 0xd0, 0x5c, 0x91, 0xb0, 0xf8, 0xe0, 0x92, + 0x55, 0x0d, 0x2d, 0x39, 0xa0, 0x1e, 0xb4, 0x5e, 0xe8, 0xf7, 0xd0, 0x9b, 0x03, + 0x8d, 0x83, 0x83, 0xe1, 0x9b, 0xc3, 0x0e, 0x64, 0x03, 0x82, 0x8c, 0xdb, 0x65, + 0x2a, 0x55, 0x6b, 0x12, 0x04, 0x09, 0x31, 0x40, 0x2a, 0xa6, 0xac, 0x34, 0xfc, + 0x19, 0xfd, 0xc0, 0x6e, 0x2e, 0x77, 0x87, 0xf5, 0xb7, 0x7b, 0x04, 0x5f, 0xd0, + 0x98, 0xc0, 0x31, 0xbd, 0xbd, 0x46, 0x27, 0x76, 0x09, 0xd8, 0x42, 0xf4, 0x84, + 0x24, 0xed, 0xa3, 0x1e, 0x3c, 0xf2, 0xcd, 0xd6, 0x43, 0x85, 0xba, 0xd3, 0x11, + 0x88, 0x58, 0xd1, 0x42, 0xd9, 0x06, 0xea, 0xdb, 0x75, 0x90, 0xc9, 0x41, 0x36, + 0xda, 0x6a, 0x06, 0x35, 0x14, 0xd6, 0xa2, 0x5f, 0x7b, 0x37, 0xd7, 0x66, 0x4f, + 0x9b, 0x97, 0x09, 0x43, 0x3e, 0x6e, 0x70, 0x21, 0x18, 0xa4, 0xab, 0x9e, 0x7a, + 0x7a, 0x3e, 0x62, 0x59, 0x12, 0x99, 0x37, 0xd2, 0x9d, 0x0d, 0xb2, 0x60, 0x70, + 0x52, 0x3e, 0x8b, 0x06, 0x43, 0x13, 0x0a, 0xbe, 0xfe, 0x94, 0x3b, 0x40, 0x12, + 0x98, 0xae, 0x01, 0xa3, 0xab, 0x00, 0xab, 0xbc, 0x60, 0xd7, 0xdb, 0x93, 0x3c, + 0x7f, 0x07, 0xa8, 0xbf, 0x0f, 0x7c, 0xe1, 0x66, 0x0b, 0xcc, 0xb4, 0x5e, 0x04, + 0x2b, 0x45, 0x1b, 0x93, 0x50, 0x02, 0xce, 0xce, 0x27, 0xf3, 0x6a, 0xba, 0x56, + 0x47, 0xac, 0x28, 0xd8, 0x18, 0x6c, 0xdd, 0x1f, 0xb9, 0x5d, 0xc1, 0x35, 0xd4, + 0x89, 0x92, 0xf6, 0x8d, 0xa1, 0x2a, 0xd6, 0x1a, 0xc7, 0x56, 0x68, 0x0d, 0xd7, + 0xf8, 0xd0, 0x77, 0x4a, 0xbd, 0x6c, 0xfd, 0xa2, 0xf0, 0x32, 0xaf, 0x3b, 0xe1, + 0x39, 0xa6, 0x33, 0xd6, 0x73, 0x3c, 0x75, 0xd1, 0xab, 0xa8, 0x90, 0x18, 0xc8, + 0x57, 0x2b, 0x99, 0xcd, 0x30, 0xc5, 0x37, 0x06, 0x79, 0x41, 0xdf, 0x1c, 0x4b, + 0xc1, 0xfd, 0x57, 0x0f, 0x7b, 0x4d, 0xdc, 0x97, 0x51, 0x86, 0x23, 0xe3, 0xae, + 0x4a, 0x87, 0xbd, 0xb9, 0x66, 0xc9, 0x4d, 0x86, 0x1e, 0x80, 0xde, 0x88, 0xc2, + 0x92, 0xae, 0xe9, 0x38, 0x71, 0x94, 0xe2, 0x56, 0xc6, 0x70, 0x07, 0x52, 0x30, + 0x1c, 0x73, 0xfc, 0x95, 0x65, 0xa4, 0x04, 0x80, 0xd8, 0x12, 0x6e, 0x9d, 0x08, + 0x58, 0x79, 0xe2, 0x4b, 0x16, 0xe9, 0xc4, 0x85, 0xd8, 0xf0, 0xd6, 0x18, 0xca, + 0x0d, 0xd1, 0x21, 0xb5, 0x1a, 0x7c, 0xab, 0x23, 0x0c, 0x5b, 0x45, 0x67, 0x2b, + 0xdb, 0x8e, 0xa3, 0xa0, 0x40, 0xf7, 0xaa, 0xa0, 0x98, 0xba, 0x26, 0x02, 0x5d, + 0x2e, 0xab, 0x79, 0x48, 0x69, 0x3d, 0xd5, 0xf6, 0xd3, 0x09, 0x65, 0x01, 0xe9, + 0xe0, 0x71, 0x25, 0xd7, 0xeb, 0x29, 0x3b, 0x3a, 0xba, 0xd5, 0x7f, 0xd5, 0xf0, + 0x11, 0x64, 0x70, 0x2d, 0xae, 0x64, 0xbd, 0xba, 0x8c, 0x92, 0x4f, 0xb0, 0x79, + 0x96, 0x79, 0xd7, 0x7f, 0x98, 0xd3, 0x03, 0x91, 0x9f, 0xb4, 0xa7, 0xff, 0x26, + 0xa9, 0x6f, 0x13, 0x7a, 0x5e, 0x5c, 0xb9, 0x5b, 0xc4, 0xc6, 0xff, 0x99, 0x93, + 0x52, 0x6b, 0xda, 0x15, 0x03, 0x16, 0x8a, 0xb4, 0x8c, 0xbd, 0x45, 0x15, 0x39, + 0x27, 0xd3, 0x04, 0x30, 0x42, 0x3d, 0xbd, 0xf0, 0x66, 0x05, 0xf5, 0xb5, 0x4b, + 0x80, 0x8f, 0xeb, 0x22, 0xb2, 0x08, 0xb0, 0x64, 0x58, 0x18, 0x47, 0xb2, 0xf6, + 0x4c, 0xa6, 0x48, 0x37, 0x00, 0x72, 0x16, 0xde, 0x6e, 0xca, 0xff, 0xeb, 0x4b, + 0x69, 0xe6, 0x33, 0x47, 0xf8, 0x4a, 0xbc, 0xad, 0x8f, 0x2e, 0x75, 0x7d, 0x58, + 0x61, 0xce, 0x77, 0xee, 0x46, 0x51, 0x3d, 0xa7, 0x41, 0x68, 0x37, 0xdc, 0xb2, + 0x3d, 0x33, 0xea, 0x72, 0xaf, 0x23, 0xd0, 0xad, 0x8c, 0x93, 0x07, 0xd0, 0xb5, + 0x85, 0x8d, 0xa9, 0x5b, 0x77, 0xff, 0xf9, 0x02, 0x7b, 0x88, 0x59, 0xe1, 0x1d, + 0xcb, 0xd5, 0x98, 0x35, 0x0e, 0xee, 0x50, 0x93, 0x94, 0x81, 0x70, 0x8e, 0xa7, + 0x08, 0xeb, 0x9f, 0x66, 0x43, 0x88, 0xb9, 0xc6, 0x4d, 0x6a, 0xf0, 0xf9, 0x66, + 0x90, 0x34, 0x24, 0x00, 0x34, 0x8e, 0x92, 0x9e, 0x07, 0x46, 0x02, 0x53, 0xf3, + 0x83, 0x90, 0xf8, 0x7b, 0xd6, 0xc0, 0x53, 0x08, 0xc3, 0xbd, 0xe2, 0x52, 0x28, + 0xe0, 0xfa, 0x08, 0x80, 0xb0, 0x8e, 0xf3, 0x4a, 0x5a, 0x9c, 0xc0, 0xea, 0x0a, + 0x67, 0xca, 0x65, 0xb6, 0xff, 0xd0, 0x05, 0x57, 0x29, 0x09, 0xf1, 0xc4, 0x2d, + 0xd7, 0x45, 0xee, 0xee, 0x9d, 0xd6, 0xb4, 0x43, 0x9c, 0x9f, 0x3f, 0x98, 0xa1, + 0x18, 0xfe, 0x16, 0x69, 0x8e, 0x9c, 0xef, 0xf5, 0x58, 0xf1, 0x60, 0x66, 0x97, + 0x5f, 0xe3, 0x95, 0x83, 0xe9, 0xb5, 0x85, 0x3b, 0x13, 0x11, 0x39, 0x15, 0x80, + 0x01, 0x9f, 0xe5, 0x5d, 0x59, 0xd1, 0xc8, 0x28, 0xd3, 0xfe, 0xb6, 0xa3, 0xb9, + 0xce, 0x92, 0xd0, 0x89, 0xae, 0x4b, 0x40, 0x8e, 0x23, 0xd6, 0xa4, 0x37, 0xd4, + 0x98, 0x9b, 0x51, 0x9b, 0x7a, 0x9e, 0xb0, 0x8a, 0xe6, 0xd4, 0x48, 0xa7, 0xa1, + 0x6e, 0x8a, 0xed, 0x26, 0xa2, 0xec, 0xd0, 0xca, 0xd8, 0x08, 0x44, 0xfd, 0x06, + 0x50, 0xd8, 0xc4, 0xe4, 0xd2, 0xaf, 0x90, 0x65, 0x67, 0x48, 0xd8, 0x09, 0x9a, + 0x0c, 0x75, 0x6f, 0xc1, 0x6c, 0xca, 0x06, 0xa3, 0x34, 0x43, 0x07, 0x02, 0xae, + 0x19, 0x61, 0x66, 0x5b, 0x48, 0x45, 0xac, 0xd1, 0xa8, 0xe3, 0x41, 0x01, 0xe6, + 0x8b, 0xb6, 0x44, 0xac, 0x03, 0x4d, 0xc6, 0x3e, 0x6e, 0x34, 0x4c, 0x3d, 0x63, + 0x76, 0x2a, 0x7a, 0x5b, 0xf5, 0x9f, 0x13, 0x09, 0x54, 0x10, 0x98, 0x1d, 0x6b, + 0x6b, 0x16, 0xbc, 0xd4, 0xc9, 0xfa, 0x68, 0xaf, 0x6e, 0x53, 0x65, 0xe9, 0x4e, + 0xcb, 0xe7, 0xab, 0x8b, 0x80, 0x43, 0xdf, 0xba, 0xcb, 0x23, 0xc8, 0x4d, 0x71, + 0xa8, 0xfe, 0x5d, 0x9a, 0xc5, 0x50, 0x2c, 0xe9, 0xf7, 0x3f, 0x40, 0x8e, 0x14, + 0x37, 0x6d, 0xb8, 0x6e, 0xf5, 0x7c, 0xc3, 0x7d, 0x09, 0x89, 0x6f, 0xa9, 0x06, + 0x97, 0x2e, 0x55, 0x71, 0x80, 0xa4, 0xab, 0x5a, 0xd0, 0x9d, 0x88, 0x46, 0xdd, + 0x6d, 0xa7, 0x48, 0x76, 0x54, 0x36, 0xe0, 0x16, 0x02, 0x40, 0xf8, 0xd4, 0x1c, + 0x0a, 0xc7, 0x83, 0xf9, 0x39, 0xf2, 0xd0, 0xed, 0x26, 0x2c, 0xe8, 0x59, 0xc1, + 0x31, 0xeb, 0xc9, 0x3f, 0xf2, 0xe6, 0xe4, 0x07, 0xd4, 0xe2, 0x43, 0xe1, 0xe9, + 0x31, 0xd5, 0x3a, 0x45, 0x43, 0xb6, 0xe2, 0x6d, 0x82, 0x59, 0x6f, 0xc5, 0x3b, + 0x52, 0x31, 0x2c, 0x77, 0x6d, 0x12, 0xeb, 0x2b, 0x65, 0x9b, 0x4f, 0xb0, 0x98, + 0xdf, 0x87, 0xd6, 0x83, 0xcf, 0x9e, 0x54, 0x12, 0xee, 0x56, 0xc3, 0xfe, 0x98, + 0x41, 0xd7, 0x3f, 0xd0, 0x70, 0xdf, 0xa5, 0x1f, 0x5b, 0xaf, 0xed, 0xf2, 0x06, + 0xf1, 0x3c, 0x52, 0x4e, 0x5c, 0x50, 0xca, 0xc9, 0x90, 0x6e, 0xfa, 0x39, 0x32, + 0x90, 0x04, 0x2e, 0x3b, 0xc5, 0x9f, 0x96, 0x0b, 0x7d, 0x24, 0x0a, 0xe4, 0x43, + 0xfc, 0x49, 0x26, 0x9c, 0xe0, 0x00, 0x61, 0xe6, 0x5c, 0x6d, 0x74, 0x81, 0x2a, + 0x30, 0xdd, 0x5f, 0x5f, 0xe7, 0x4e, 0xff, 0x61, 0xe0, 0xcb, 0xab, 0x3c, 0xec, + 0x75, 0xd0, 0xae, 0xf9, 0x50, 0x83, 0x18, 0x94, 0x52, 0xdd, 0x3d, 0x9e, 0xdf, + 0x44, 0x87, 0xbc, 0x73, 0x4c, 0x8b, 0x24, 0xf2, 0x12, 0x96, 0xe4, 0xe9, 0xef, + 0x11, 0x7d, 0x7f, 0xb9, 0x77, 0xe3, 0xb0, 0xe6, 0x40, 0x6e, 0x63, 0x08, 0x59, + 0x06, 0x33, 0x1a, 0x93, 0x03, 0x3d, 0x1c, 0xb8, 0x36, 0x0f, 0xe6, 0xfe, 0xa6, + 0x1a, 0x68, 0x26, 0xdf, 0x36, 0x25, 0x57, 0x89, 0xf9, 0x2e, 0x40, 0xba, 0xfc, + 0xb2, 0xeb, 0xcb, 0x9e, 0x55, 0x6f, 0x6c, 0x0c, 0xca, 0xdc, 0x6a, 0xf0, 0x8e, + 0x31, 0xec, 0x4a, 0xd5, 0x28, 0x80, 0x34, 0xe1, 0x6d, 0x15, 0x5c, 0xfd, 0xca, + 0xda, 0x7b, 0xab, 0x59, 0x9c, 0x2f, 0xa4, 0xad, 0x2e, 0x62, 0x93, 0xf9, 0xfe, + 0x09, 0x71, 0x69, 0x14, 0x82, 0x76, 0xb6, 0xa9, 0xea, 0xa7, 0x2f, 0x14, 0x8b, + 0x0c, 0x95, 0x65, 0xc3, 0xc2, 0xdd, 0x63, 0x12, 0x5e, 0x0f, 0xa5, 0x30, 0x86, + 0x1a, 0x71, 0x0d, 0xf8, 0xe4, 0x81, 0xf2, 0x71, 0x29, 0x20, 0xf8, 0x78, 0x7e, + 0x0a, 0xed, 0xfe, 0x61, 0x8a, 0xff, 0x50, 0xa3, 0xb5, 0x62, 0x13, 0x88, 0x4d, + 0x62, 0x62, 0xc1, 0x1d, 0xeb, 0xf2, 0xba, 0x7e, 0x8a, 0xd6, 0x69, 0x2c, 0xb1, + 0x70, 0x78, 0x33, 0x14, 0x18, 0xda, 0x4b, 0xe0, 0x64, 0xff, 0x52, 0x70, 0x07, + 0x39, 0x34, 0xab, 0xcd, 0x2a, 0xb0, 0x46, 0x9e, 0xca, 0xf7, 0x27, 0x5b, 0x4b, + 0xd7, 0x2b, 0xc6, 0xed, 0x34, 0x47, 0x8e, 0xa4, 0x08, 0x9b, 0x73, 0x6a, 0x16, + 0xdd, 0x90, 0x6d, 0x49, 0xf2, 0x5c, 0x33, 0x82, 0x7c, 0x57, 0x1c, 0xe0, 0xb5, + 0xd7, 0x21, 0x77, 0xaa, 0x35, 0x08, 0x80, 0x4b, 0xc0, 0xf8, 0xfa, 0xa9, 0x47, + 0x12, 0x22, 0x31, 0x40, 0x2d, 0x2f, 0x5c, 0xc9, 0xa0, 0xeb, 0x0e, 0x09, 0xd4, + 0x27, 0xb4, 0x27, 0x28, 0x8d, 0x93, 0x7d, 0x9d, 0x72, 0xb7, 0x74, 0x56, 0xf8, + 0x86, 0x59, 0x4c, 0xd8, 0xc6, 0xa4, 0x62, 0xf7, 0x7f, 0xd8, 0x30, 0x76, 0x46, + 0x9c, 0xc0, 0xec, 0xba, 0x3c, 0xc4, 0x0c, 0xad, 0x69, 0xe5, 0xb5, 0x41, 0x12, + 0xea, 0xb3, 0x33, 0x96, 0xae, 0xcf, 0xbc, 0x21, 0x1f, 0x1f, 0x79, 0xcf, 0x33, + 0x10, 0x8e, 0x93, 0xd9, 0x53, 0x78, 0xba, 0xe6, 0x95, 0x82, 0x74, 0xb3, 0x10, + 0x88, 0xfb, 0xd8, 0xb3, 0xa3, 0xa0, 0xd1, 0x54, 0xa7, 0x89, 0x73, 0x5b, 0x03, + 0x49, 0xc4, 0xd5, 0x1c, 0x88, 0x9d, 0x08, 0x95, 0x2d, 0xdd, 0x54, 0x88, 0xbe, + 0x95, 0x56, 0x05, 0x94, 0xe6, + ], + script_code: Script(vec![0x52, 0x63, 0x53, 0x51, 0x65]), + transparent_input: Some(0), + hash_type: 2, + amount: 483959951916902, + consensus_branch_id: consensus::BranchId::Overwinter, + sighash: [ + 0x29, 0x6f, 0xd7, 0x63, 0xf2, 0x54, 0x5e, 0x64, 0xfb, 0x5d, 0x7d, 0x49, 0xc0, + 0x00, 0xd2, 0xb4, 0x18, 0xb9, 0x3b, 0xde, 0x22, 0x34, 0xf8, 0x74, 0x29, 0x11, + 0xe8, 0xaf, 0xef, 0xd0, 0x6d, 0x57, + ], + }, + ] + } +} + +pub mod zip_0243 { + use crate::{consensus, legacy::Script}; + + pub struct Test0243Vector { + pub tx: Vec, + pub script_code: Script, + pub transparent_input: Option, + pub hash_type: u32, + pub amount: i64, + pub consensus_branch_id: consensus::BranchId, + pub sighash: [u8; 32], + } + pub fn make_test_vectors() -> Vec { + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0243.py + vec![ + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x02, 0xe7, 0x71, 0x98, + 0x11, 0x89, 0x3e, 0x00, 0x00, 0x09, 0x52, 0x00, 0xac, 0x65, 0x51, 0xac, 0x63, + 0x65, 0x65, 0xb2, 0x83, 0x5a, 0x08, 0x05, 0x75, 0x02, 0x00, 0x02, 0x51, 0x51, + 0x48, 0x1c, 0xdd, 0x86, 0xb3, 0xcc, 0x43, 0x18, 0x44, 0x21, 0x17, 0x62, 0x3c, + 0xeb, 0x05, 0x00, 0x03, 0x1b, 0x3d, 0x1a, 0x02, 0x7c, 0x2c, 0x40, 0x59, 0x09, + 0x58, 0xb7, 0xeb, 0x13, 0xd7, 0x42, 0xa9, 0x97, 0x73, 0x8c, 0x46, 0xa4, 0x58, + 0x96, 0x5b, 0xaf, 0x27, 0x6b, 0xa9, 0x2f, 0x27, 0x2c, 0x72, 0x1f, 0xe0, 0x1f, + 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, 0x9d, 0x4e, 0x30, 0xa7, 0x35, 0x94, + 0xbf, 0x50, 0x98, 0x42, 0x1c, 0x69, 0x37, 0x8a, 0xf1, 0xe4, 0x0f, 0x64, 0xe1, + 0x25, 0x94, 0x6f, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69, + 0x68, 0x91, 0x2a, 0x63, 0x81, 0xce, 0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, + 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5, 0xfd, 0x93, 0x13, 0x25, 0xc9, 0xa1, 0x38, + 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x51, 0xa7, + 0xaf, 0x9d, 0xb6, 0x99, 0x0e, 0xd8, 0x3d, 0xd6, 0x4a, 0xf3, 0x59, 0x7c, 0x04, + 0x32, 0x3e, 0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, + 0x8d, 0x32, 0x0d, 0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, 0xdf, 0x65, 0x8d, + 0x24, 0xae, 0x67, 0xc2, 0x2c, 0x8d, 0x13, 0x09, 0x13, 0x1f, 0xc0, 0x0f, 0xe7, + 0xf2, 0x35, 0x73, 0x42, 0x76, 0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x91, 0xe0, 0x0c, + 0x7a, 0x1d, 0x48, 0xaf, 0x04, 0x68, 0x27, 0x59, 0x1e, 0x97, 0x33, 0xa9, 0x7f, + 0xa6, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, + 0xe6, 0x9c, 0xe8, 0xfc, 0x1b, 0xe4, 0xaa, 0xc0, 0x0f, 0xf2, 0x71, 0x1e, 0xbd, + 0x93, 0x1d, 0xe5, 0x18, 0x85, 0x68, 0x78, 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, + 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, 0xf7, 0x39, 0x3c, 0x94, 0xe2, 0x88, 0x53, + 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, 0x0f, 0xe5, 0x3e, + 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, 0xf4, 0x73, 0xf4, + 0x68, 0xa0, 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88, 0x0d, 0x78, 0x0c, 0xb0, + 0x7f, 0xcf, 0xaa, 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, + 0x3d, 0x88, 0x2d, 0x2b, 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, 0x94, 0x94, 0xc6, + 0xac, 0x89, 0x3c, 0x49, 0x72, 0x38, 0x33, 0xec, 0x89, 0x26, 0xc1, 0x03, 0x95, + 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, 0x5d, 0x99, 0x58, + 0x9c, 0x8b, 0xb8, 0x38, 0xe8, 0xaa, 0xf7, 0x45, 0x53, 0x3e, 0xd9, 0xe8, 0xae, + 0x3a, 0x1c, 0xd0, 0x74, 0xa5, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, + 0xeb, 0xbc, 0x86, 0x2d, 0xed, 0x42, 0x43, 0x5e, 0x92, 0x47, 0x69, 0x30, 0xd0, + 0x69, 0x89, 0x6c, 0xff, 0x30, 0xeb, 0x41, 0x4f, 0x72, 0x7b, 0x89, 0x5a, 0x4b, + 0x7b, 0xe1, 0x76, 0x93, 0x67, 0xe1, 0xfe, 0x8a, 0xd1, 0x8d, 0xe1, 0x1e, 0x58, + 0xd8, 0x8a, 0x0a, 0xd5, 0x51, 0x1d, 0x35, 0x25, 0x12, 0x2b, 0x7b, 0x0a, 0x6f, + 0x25, 0xd2, 0x8b, 0x16, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, + 0x86, 0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11, 0x7d, 0x41, 0x7a, 0xdb, 0x3d, 0x15, + 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50, 0x0c, 0x6b, 0x8f, 0xb8, 0x6b, + 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, 0xcc, 0x40, 0xa9, 0x8d, + 0x5f, 0x29, 0x35, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, 0xfd, 0xbb, 0x5d, + 0x47, 0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, + 0xb7, 0xfa, 0xe2, 0xdb, 0x58, 0x71, 0x05, 0x41, 0x5d, 0x46, 0x42, 0x78, 0x9d, + 0x38, 0xf5, 0x0b, 0x8d, 0xbc, 0xc1, 0x29, 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, + 0x35, 0x5b, 0xcf, 0x73, 0xce, 0xcb, 0x8c, 0xb8, 0xa5, 0xda, 0x01, 0x30, 0x71, + 0x52, 0xf1, 0x39, 0x36, 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, + 0x26, 0xc6, 0xcb, 0x4c, 0xd4, 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a, 0x53, + 0x41, 0xec, 0x5d, 0xd7, 0x15, 0x40, 0x6f, 0x2f, 0xdd, 0x2a, 0xfa, 0x73, 0x3f, + 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, 0x2a, 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, + 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, 0x79, 0xf8, 0x80, 0x08, 0xe3, 0x15, + 0xdc, 0x7d, 0x83, 0x88, 0xe7, 0x6c, 0x17, 0x82, 0xfd, 0x27, 0x95, 0xd1, 0x8a, + 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, 0x48, 0x9c, 0xe7, 0x57, + 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, 0xdf, 0x73, 0xca, + 0xec, 0x65, 0x60, 0x40, 0x37, 0x31, 0x4f, 0xaa, 0xce, 0xb5, 0x62, 0x18, 0xc6, + 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f, 0x21, 0xa9, 0xfb, + 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, 0x00, 0xe1, 0xb1, + 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, 0x49, + 0xe9, 0x50, 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, 0x1f, 0xe3, 0x89, + 0x5b, 0x8c, 0xc3, 0xd1, 0x4d, 0x2c, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, + 0xdd, 0x3d, 0x9a, 0x69, 0xf5, 0x33, 0x57, 0xd7, 0x76, 0x7f, 0x4f, 0x5c, 0xcb, + 0xdb, 0xc5, 0x96, 0x63, 0x12, 0x77, 0xf8, 0xfe, 0xcd, 0x08, 0xcb, 0x05, 0x6b, + 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2, 0x44, 0xfc, 0x71, 0x62, + 0x69, 0xb9, 0x26, 0xd6, 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, + 0xff, 0x9e, 0x68, 0x62, 0x5a, 0x6b, 0x4c, 0xbc, 0x4b, 0x70, 0x0a, 0x36, 0x4f, + 0xa7, 0x6b, 0xd8, 0x29, 0x8b, 0xc3, 0xec, 0x60, 0x8d, 0x4c, 0xf7, 0xf3, 0x56, + 0x66, 0x58, 0xd5, 0x58, 0x87, 0x14, 0xec, 0x94, 0x48, 0xb0, 0xf0, 0x39, 0x61, + 0x28, 0xae, 0xf8, 0x84, 0xa6, 0x46, 0x11, 0x4c, 0x9f, 0x1a, 0x6d, 0xf5, 0x63, + 0x19, 0x03, 0x3c, 0x31, 0x99, 0xcc, 0x7a, 0x09, 0xe9, 0xe9, 0x56, 0x74, 0x82, + 0xc9, 0x26, 0x95, 0x39, 0x02, 0x29, 0x40, 0x7b, 0xbc, 0x48, 0x98, 0x56, 0x75, + 0xe3, 0xf8, 0x74, 0xa4, 0x53, 0x3f, 0x1d, 0x63, 0xa8, 0x4d, 0xfa, 0x3e, 0x0f, + 0x46, 0x0f, 0xe2, 0xf5, 0x7e, 0x34, 0xfb, 0xc7, 0x54, 0x23, 0xb6, 0x88, 0x3a, + 0x50, 0xa0, 0xd4, 0x70, 0x19, 0x0d, 0xfb, 0xa1, 0x0a, 0x85, 0x7f, 0x82, 0x84, + 0x2d, 0x38, 0x25, 0xb3, 0xd6, 0xda, 0x05, 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, + 0xc0, 0xb7, 0x16, 0xc4, 0x8f, 0xbd, 0x46, 0x7f, 0x75, 0xb7, 0x80, 0x14, 0x9a, + 0xe8, 0x80, 0x8f, 0x4e, 0x68, 0xf5, 0x0c, 0x05, 0x36, 0xac, 0xdd, 0xf6, 0xf1, + 0xae, 0xab, 0x01, 0x6b, 0x6b, 0xc1, 0xec, 0x14, 0x4b, 0x4e, 0x55, 0x3a, 0xcf, + 0xd6, 0x70, 0xf7, 0x7e, 0x75, 0x5f, 0xc8, 0x8e, 0x06, 0x77, 0xe3, 0x1b, 0xa4, + 0x59, 0xb4, 0x4e, 0x30, 0x77, 0x68, 0x95, 0x8f, 0xe3, 0x78, 0x9d, 0x41, 0xc2, + 0xb1, 0xff, 0x43, 0x4c, 0xb3, 0x0e, 0x15, 0x91, 0x4f, 0x01, 0xbc, 0x6b, 0xc2, + 0x30, 0x7b, 0x48, 0x8d, 0x25, 0x56, 0xd7, 0xb7, 0x38, 0x0e, 0xa4, 0xff, 0xd7, + 0x12, 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, 0x40, 0x59, 0xf3, + 0x96, 0xbf, 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, 0xa9, 0x44, + 0xf7, 0x2d, 0x43, 0x6a, 0x10, 0x2f, 0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, 0xb0, + 0x86, 0xfe, 0x9d, 0x2e, 0x71, 0x62, 0x47, 0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, + 0xec, 0x95, 0x12, 0xbf, 0xb3, 0xf3, 0x83, 0x27, 0x29, 0x6e, 0xfa, 0xa7, 0x43, + 0x28, 0xb1, 0x18, 0xc2, 0x74, 0x02, 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4, + 0xbb, 0xc6, 0x8e, 0x37, 0xc0, 0xaa, 0x7d, 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, + 0x3b, 0x84, 0x1e, 0x75, 0x17, 0x13, 0xa0, 0x29, 0x43, 0x90, 0x5a, 0xae, 0x08, + 0x03, 0xfd, 0x69, 0x44, 0x2e, 0xb7, 0x68, 0x1e, 0xc2, 0xa0, 0x56, 0x00, 0x05, + 0x4e, 0x92, 0xee, 0xd5, 0x55, 0x02, 0x8f, 0x21, 0xb6, 0xa1, 0x55, 0x26, 0x8a, + 0x2d, 0xd6, 0x64, 0x0a, 0x69, 0x30, 0x1a, 0x52, 0xa3, 0x8d, 0x4d, 0x9f, 0x9f, + 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71, 0x18, 0x14, 0x1c, 0xe4, 0xc9, 0xbe, + 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81, 0xa1, 0x55, 0xfa, 0x3a, 0x03, + 0x49, 0x99, 0xc5, 0x38, 0xf7, 0xa7, 0x58, 0xbb, 0x5b, 0x1d, 0x28, 0xfd, 0x21, + 0x8f, 0xba, 0x19, 0x38, 0x74, 0x4b, 0xdb, 0x77, 0xb4, 0xa4, 0xdf, 0xa7, 0xa5, + 0xfa, 0xe9, 0x6e, 0x8c, 0xd4, 0x9b, 0x26, 0x90, 0x7d, 0xfc, 0x66, 0x85, 0xc5, + 0xc9, 0x9b, 0x71, 0x41, 0xac, 0x62, 0x6a, 0xb4, 0x76, 0x1f, 0xd3, 0xf4, 0x1e, + 0x72, 0x8e, 0x1a, 0x28, 0xf8, 0x9d, 0xb8, 0x9f, 0xfd, 0xec, 0xa3, 0x64, 0xe4, + 0xb2, 0x2d, 0x81, 0xd9, 0x96, 0x8d, 0x01, 0x19, 0xe4, 0xc7, 0xa1, 0x89, 0xad, + 0xf2, 0x2a, 0xd9, 0x68, 0x30, 0xa5, 0x4e, 0x40, 0xdc, 0x73, 0xea, 0xba, 0x6b, + 0x2a, 0xaf, 0x14, 0xf7, 0xca, 0x94, 0x2e, 0x73, 0x70, 0xb2, 0x47, 0xc0, 0x46, + 0xf8, 0xe7, 0x5e, 0xf8, 0xe3, 0xf8, 0xbd, 0x82, 0x1c, 0xf5, 0x77, 0x49, 0x18, + 0x64, 0xe2, 0x0e, 0x6d, 0x08, 0xfd, 0x2e, 0x32, 0xb5, 0x55, 0xc9, 0x2c, 0x66, + 0x1f, 0x19, 0x58, 0x8b, 0x72, 0xa8, 0x95, 0x99, 0x71, 0x0a, 0x88, 0x06, 0x12, + 0x53, 0xca, 0x28, 0x5b, 0x63, 0x04, 0xb3, 0x7d, 0xa2, 0xb5, 0x29, 0x4f, 0x5c, + 0xb3, 0x54, 0xa8, 0x94, 0x32, 0x28, 0x48, 0xcc, 0xbd, 0xc7, 0xc2, 0x54, 0x5b, + 0x7d, 0xa5, 0x68, 0xaf, 0xac, 0x87, 0xff, 0xa0, 0x05, 0xc3, 0x12, 0x24, 0x1c, + 0x2d, 0x57, 0xf4, 0xb4, 0x5d, 0x64, 0x19, 0xf0, 0xd2, 0xe2, 0xc5, 0xaf, 0x33, + 0xae, 0x24, 0x37, 0x85, 0xb3, 0x25, 0xcd, 0xab, 0x95, 0x40, 0x4f, 0xc7, 0xae, + 0xd7, 0x05, 0x25, 0xcd, 0xdb, 0x41, 0x87, 0x2c, 0xfc, 0xc2, 0x14, 0xb1, 0x32, + 0x32, 0xed, 0xc7, 0x86, 0x09, 0x75, 0x3d, 0xbf, 0xf9, 0x30, 0xeb, 0x0d, 0xc1, + 0x56, 0x61, 0x2b, 0x9c, 0xb4, 0x34, 0xbc, 0x4b, 0x69, 0x33, 0x92, 0xde, 0xb8, + 0x7c, 0x53, 0x04, 0x35, 0x31, 0x2e, 0xdc, 0xed, 0xc6, 0xa9, 0x61, 0x13, 0x33, + 0x38, 0xd7, 0x86, 0xc4, 0xa3, 0xe1, 0x03, 0xf6, 0x01, 0x10, 0xa1, 0x6b, 0x13, + 0x37, 0x12, 0x97, 0x04, 0xbf, 0x47, 0x54, 0xff, 0x6b, 0xa9, 0xfb, 0xe6, 0x59, + 0x51, 0xe6, 0x10, 0x62, 0x0f, 0x71, 0xcd, 0xa8, 0xfc, 0x87, 0x76, 0x25, 0xf2, + 0xc5, 0xbb, 0x04, 0xcb, 0xe1, 0x22, 0x8b, 0x1e, 0x88, 0x6f, 0x40, 0x50, 0xaf, + 0xd8, 0xfe, 0x94, 0xe9, 0x7d, 0x2e, 0x9e, 0x85, 0xc6, 0xbb, 0x74, 0x8c, 0x00, + 0x42, 0xd3, 0x24, 0x9a, 0xbb, 0x13, 0x42, 0xbb, 0x0e, 0xeb, 0xf6, 0x20, 0x58, + 0xbf, 0x3d, 0xe0, 0x80, 0xd9, 0x46, 0x11, 0xa3, 0x75, 0x09, 0x15, 0xb5, 0xdc, + 0x6c, 0x0b, 0x38, 0x99, 0xd4, 0x12, 0x22, 0xba, 0xce, 0x76, 0x0e, 0xe9, 0xc8, + 0x81, 0x8d, 0xed, 0x59, 0x9e, 0x34, 0xc5, 0x6d, 0x73, 0x72, 0xaf, 0x1e, 0xb8, + 0x68, 0x52, 0xf2, 0xa7, 0x32, 0x10, 0x4b, 0xdb, 0x75, 0x07, 0x39, 0xde, 0x6c, + 0x2c, 0x6e, 0x0f, 0x9e, 0xb7, 0xcb, 0x17, 0xf1, 0x94, 0x2b, 0xfc, 0x9f, 0x4f, + 0xd6, 0xeb, 0xb6, 0xb4, 0xcd, 0xd4, 0xda, 0x2b, 0xca, 0x26, 0xfa, 0xc4, 0x57, + 0x8e, 0x9f, 0x54, 0x34, 0x05, 0xac, 0xc7, 0xd8, 0x6f, 0xf5, 0x91, 0x58, 0xbd, + 0x0c, 0xba, 0x3a, 0xef, 0x6f, 0x4a, 0x84, 0x72, 0xd1, 0x44, 0xd9, 0x9f, 0x8b, + 0x8d, 0x1d, 0xed, 0xaa, 0x90, 0x77, 0xd4, 0xf0, 0x1d, 0x4b, 0xb2, 0x7b, 0xbe, + 0x31, 0xd8, 0x8f, 0xbe, 0xfa, 0xc3, 0xdc, 0xd4, 0x79, 0x75, 0x63, 0xa2, 0x6b, + 0x1d, 0x61, 0xfc, 0xd9, 0xa4, 0x64, 0xab, 0x21, 0xed, 0x55, 0x0f, 0xe6, 0xfa, + 0x09, 0x69, 0x5b, 0xa0, 0xb2, 0xf1, 0x0e, 0xea, 0x64, 0x68, 0xcc, 0x6e, 0x20, + 0xa6, 0x6f, 0x82, 0x6e, 0x3d, 0x14, 0xc5, 0x00, 0x6f, 0x05, 0x63, 0x88, 0x7f, + 0x5e, 0x12, 0x89, 0xbe, 0x1b, 0x20, 0x04, 0xca, 0xca, 0x8d, 0x3f, 0x34, 0xd6, + 0xe8, 0x4b, 0xf5, 0x9c, 0x1e, 0x04, 0x61, 0x9a, 0x7c, 0x23, 0xa9, 0x96, 0x94, + 0x1d, 0x88, 0x9e, 0x46, 0x22, 0xa9, 0xb9, 0xb1, 0xd5, 0x9d, 0x5e, 0x31, 0x90, + 0x94, 0x31, 0x8c, 0xd4, 0x05, 0xba, 0x27, 0xb7, 0xe2, 0xc0, 0x84, 0x76, 0x2d, + 0x31, 0x45, 0x3e, 0xc4, 0x54, 0x9a, 0x4d, 0x97, 0x72, 0x9d, 0x03, 0x34, 0x60, + 0xfc, 0xf8, 0x9d, 0x64, 0x94, 0xf2, 0xff, 0xd7, 0x89, 0xe9, 0x80, 0x82, 0xea, + 0x5c, 0xe9, 0x53, 0x4b, 0x3a, 0xcd, 0x60, 0xfe, 0x49, 0xe3, 0x7e, 0x4f, 0x66, + 0x69, 0x31, 0x67, 0x73, 0x19, 0xed, 0x89, 0xf8, 0x55, 0x88, 0x74, 0x1b, 0x31, + 0x28, 0x90, 0x1a, 0x93, 0xbd, 0x78, 0xe4, 0xbe, 0x02, 0x25, 0xa9, 0xe2, 0x69, + 0x2c, 0x77, 0xc9, 0x69, 0xed, 0x01, 0x76, 0xbd, 0xf9, 0x55, 0x59, 0x48, 0xcb, + 0xd5, 0xa3, 0x32, 0xd0, 0x45, 0xde, 0x6b, 0xa6, 0xbf, 0x44, 0x90, 0xad, 0xfe, + 0x74, 0x44, 0xcd, 0x46, 0x7a, 0x09, 0x07, 0x54, 0x17, 0xfc, 0xc0, 0x06, 0x2e, + 0x49, 0xf0, 0x08, 0xc5, 0x1a, 0xd4, 0x22, 0x74, 0x39, 0xc1, 0xb4, 0x47, 0x6c, + 0xcd, 0x8e, 0x97, 0x86, 0x2d, 0xab, 0x7b, 0xe1, 0xe8, 0xd3, 0x99, 0xc0, 0x5e, + 0xf2, 0x7c, 0x6e, 0x22, 0xee, 0x27, 0x3e, 0x15, 0x78, 0x6e, 0x39, 0x4c, 0x8f, + 0x1b, 0xe3, 0x16, 0x82, 0xa3, 0x01, 0x47, 0x96, 0x3a, 0xc8, 0xda, 0x8d, 0x41, + 0xd8, 0x04, 0x25, 0x84, 0x26, 0xa3, 0xf7, 0x02, 0x89, 0xb8, 0xad, 0x19, 0xd8, + 0xde, 0x13, 0xbe, 0x4e, 0xeb, 0xe3, 0xbd, 0x4c, 0x8a, 0x6f, 0x55, 0xd6, 0xe0, + 0xc3, 0x73, 0xd4, 0x56, 0x85, 0x18, 0x79, 0xf5, 0xfb, 0xc2, 0x82, 0xdb, 0x9e, + 0x13, 0x48, 0x06, 0xbf, 0xf7, 0x1e, 0x11, 0xbc, 0x33, 0xab, 0x75, 0xdd, 0x6c, + 0xa0, 0x67, 0xfb, 0x73, 0xa0, 0x43, 0xb6, 0x46, 0xa7, 0xcf, 0x39, 0xca, 0xb4, + 0x92, 0x83, 0x86, 0x78, 0x6d, 0x2f, 0x24, 0x14, 0x1e, 0xe1, 0x20, 0xfd, 0xc3, + 0x4d, 0x67, 0x64, 0xea, 0xfc, 0x66, 0x88, 0x0e, 0xe0, 0x20, 0x4f, 0x53, 0xcc, + 0x11, 0x67, 0xed, 0x20, 0xb4, 0x3a, 0x52, 0xde, 0xa3, 0xca, 0x7c, 0xff, 0x8e, + 0xf3, 0x5c, 0xd8, 0xe6, 0xd7, 0xc1, 0x11, 0xa6, 0x8e, 0xf4, 0x4b, 0xcd, 0x0c, + 0x15, 0x13, 0xad, 0x47, 0xca, 0x61, 0xc6, 0x59, 0xcc, 0x5d, 0x32, 0x5b, 0x44, + 0x0f, 0x6b, 0x9f, 0x59, 0xaf, 0xf6, 0x68, 0x79, 0xbb, 0x66, 0x88, 0xfd, 0x28, + 0x59, 0x36, 0x2b, 0x18, 0x2f, 0x20, 0x7b, 0x31, 0x75, 0x96, 0x1f, 0x64, 0x11, + 0xa4, 0x93, 0xbf, 0xfd, 0x04, 0x8e, 0x7d, 0x0d, 0x87, 0xd8, 0x2f, 0xe6, 0xf9, + 0x90, 0xa2, 0xb0, 0xa2, 0x5f, 0x5a, 0xa0, 0x11, 0x1a, 0x6e, 0x68, 0xf3, 0x7b, + 0xf6, 0xf3, 0xac, 0x2d, 0x26, 0xb8, 0x46, 0x86, 0xe5, 0x69, 0xd5, 0x8d, 0x99, + 0xc1, 0x38, 0x35, 0x97, 0xfa, 0xd8, 0x11, 0x93, 0xc4, 0xc1, 0xb1, 0x6e, 0x6a, + 0x90, 0xe2, 0xd5, 0x07, 0xcd, 0xfe, 0x6f, 0xbd, 0xaa, 0x86, 0x16, 0x3e, 0x9c, + 0xf5, 0xde, 0x31, 0x00, 0xfb, 0xca, 0x7e, 0x8d, 0xa0, 0x47, 0xb0, 0x90, 0x79, + 0x36, 0x2d, 0x77, 0x92, 0xde, 0xb3, 0xca, 0x9d, 0xc1, 0x56, 0x1b, 0x87, 0xc8, + 0x2e, 0x3c, 0xb9, 0x9e, 0xb5, 0x83, 0x73, 0x19, 0x58, 0x22, 0x16, 0xa3, 0x22, + 0x67, 0x74, 0xef, 0xa9, 0x0e, 0xfb, 0x7b, 0xfc, 0x79, 0xf4, 0x25, 0x64, 0x4e, + 0x4e, 0x98, 0xc2, 0xd7, 0xd8, 0x64, 0x2b, 0x9d, 0xb8, 0x2a, 0xa7, 0x39, 0xbf, + 0x2d, 0x71, 0xcc, 0x41, 0x17, 0x22, 0x7d, 0xb2, 0x27, 0xcf, 0x0a, 0x05, 0xad, + 0x9a, 0x95, 0x83, 0x2e, 0x23, 0xc9, 0x4f, 0x27, 0x1c, 0xa0, 0xe4, 0x69, 0x4f, + 0xac, 0x63, 0x22, 0x28, 0x2e, 0xba, 0xc6, 0x98, 0x6b, 0x8f, 0xdc, 0x8a, 0xd8, + 0x63, 0x08, 0x4f, 0xf1, 0x0f, 0xd1, 0x1e, 0x6a, 0x13, 0x31, 0x1f, 0xb7, 0x99, + 0xc7, 0x9c, 0x64, 0x1d, 0x9d, 0xa4, 0x3b, 0x33, 0xe7, 0xad, 0x01, 0x2e, 0x28, + 0x25, 0x53, 0x98, 0x78, 0x92, 0x62, 0x27, 0x5f, 0x11, 0x75, 0xbe, 0x84, 0x62, + 0xc0, 0x14, 0x91, 0xc4, 0xd8, 0x42, 0x40, 0x6d, 0x0e, 0xc4, 0x28, 0x2c, 0x95, + 0x26, 0x17, 0x4a, 0x09, 0x87, 0x8f, 0xe8, 0xfd, 0xde, 0x33, 0xa2, 0x96, 0x04, + 0xe5, 0xe5, 0xe7, 0xb2, 0xa0, 0x25, 0xd6, 0x65, 0x0b, 0x97, 0xdb, 0xb5, 0x2b, + 0xef, 0xb5, 0x9b, 0x1d, 0x30, 0xa5, 0x74, 0x33, 0xb0, 0xa3, 0x51, 0x47, 0x44, + 0x44, 0x09, 0x9d, 0xaa, 0x37, 0x10, 0x46, 0x61, 0x32, 0x60, 0xcf, 0x33, 0x54, + 0xcf, 0xcd, 0xad, 0xa6, 0x63, 0xec, 0xe8, 0x24, 0xff, 0xd7, 0xe4, 0x43, 0x93, + 0x88, 0x6a, 0x86, 0x16, 0x5d, 0xdd, 0xdf, 0x2b, 0x4c, 0x41, 0x77, 0x35, 0x54, + 0xc8, 0x69, 0x95, 0x26, 0x94, 0x08, 0xb1, 0x1e, 0x67, 0x37, 0xa4, 0xc4, 0x47, + 0x58, 0x6f, 0x69, 0x17, 0x34, 0x46, 0xd8, 0xe4, 0x8b, 0xf8, 0x4c, 0xbc, 0x00, + 0x0a, 0x80, 0x78, 0x99, 0x97, 0x3e, 0xb9, 0x3c, 0x5e, 0x81, 0x9a, 0xad, 0x66, + 0x94, 0x13, 0xf8, 0x38, 0x79, 0x33, 0xad, 0x15, 0x84, 0xaa, 0x35, 0xe4, 0x3f, + 0x4e, 0xcd, 0x1e, 0x2d, 0x04, 0x07, 0xc0, 0xb1, 0xb8, 0x99, 0x20, 0xff, 0xdf, + 0xdb, 0x9b, 0xea, 0x51, 0xac, 0x95, 0xb5, 0x57, 0xaf, 0x71, 0xb8, 0x9f, 0x90, + 0x3f, 0x5d, 0x98, 0x48, 0xf1, 0x4f, 0xcb, 0xeb, 0x18, 0x37, 0x57, 0x0f, 0x54, + 0x4d, 0x63, 0x59, 0xeb, 0x23, 0xfa, 0xf3, 0x8a, 0x08, 0x22, 0xda, 0x36, 0xce, + 0x42, 0x6c, 0x4a, 0x2f, 0xbe, 0xff, 0xeb, 0x0a, 0x8a, 0x2e, 0x29, 0x7a, 0x9d, + 0x19, 0xba, 0x15, 0x02, 0x45, 0x90, 0xe3, 0x32, 0x9d, 0x9f, 0xa9, 0x26, 0x1f, + 0x99, 0x38, 0xa4, 0x03, 0x2d, 0xd3, 0x46, 0x06, 0xc9, 0xcf, 0x9f, 0x3d, 0xd3, + 0x3e, 0x57, 0x6f, 0x05, 0xcd, 0x1d, 0xd6, 0x81, 0x1c, 0x62, 0x98, 0x75, 0x7d, + 0x77, 0xd9, 0xe8, 0x10, 0xab, 0xdb, 0x22, 0x6a, 0xfc, 0xaa, 0x43, 0x46, 0xa6, + 0x56, 0x0f, 0x89, 0x32, 0xb3, 0x18, 0x1f, 0xd3, 0x55, 0xd5, 0xd3, 0x91, 0x97, + 0x61, 0x83, 0xf8, 0xd9, 0x93, 0x88, 0x83, 0x96, 0x32, 0xd6, 0x35, 0x4f, 0x66, + 0x6d, 0x09, 0xd3, 0xe5, 0x62, 0x9e, 0xa1, 0x97, 0x37, 0x38, 0x86, 0x13, 0xd3, + 0x8a, 0x34, 0xfd, 0x0f, 0x6e, 0x50, 0xee, 0x5a, 0x0c, 0xc9, 0x67, 0x71, 0x77, + 0xf5, 0x00, 0x28, 0xc1, 0x41, 0x37, 0x81, 0x87, 0xbd, 0x28, 0x19, 0x40, 0x3f, + 0xc5, 0x34, 0xf8, 0x00, 0x76, 0xe9, 0x38, 0x0c, 0xb4, 0x96, 0x4d, 0x3b, 0x6b, + 0x45, 0x81, 0x9d, 0x3b, 0x8e, 0x9c, 0xaf, 0x54, 0xf0, 0x51, 0x85, 0x2d, 0x67, + 0x1b, 0xf8, 0xc1, 0xff, 0xde, 0x2d, 0x15, 0x10, 0x75, 0x64, 0x18, 0xcb, 0x48, + 0x10, 0x93, 0x6a, 0xa5, 0x7e, 0x69, 0x65, 0xd6, 0xfb, 0x65, 0x6a, 0x76, 0x0b, + 0x7f, 0x19, 0xad, 0xf9, 0x6c, 0x17, 0x34, 0x88, 0x55, 0x21, 0x93, 0xb1, 0x47, + 0xee, 0x58, 0x85, 0x80, 0x33, 0xda, 0xc7, 0xcd, 0x0e, 0xb2, 0x04, 0xc0, 0x64, + 0x90, 0xbb, 0xde, 0xdf, 0x5f, 0x75, 0x71, 0xac, 0xb2, 0xeb, 0xe7, 0x6a, 0xce, + 0xf3, 0xf2, 0xa0, 0x1e, 0xe9, 0x87, 0x48, 0x6d, 0xfe, 0x6c, 0x3f, 0x0a, 0x5e, + 0x23, 0x4c, 0x12, 0x72, 0x58, 0xf9, 0x7a, 0x28, 0xfb, 0x5d, 0x16, 0x4a, 0x81, + 0x76, 0xbe, 0x94, 0x6b, 0x80, 0x97, 0xd0, 0xe3, 0x17, 0x28, 0x7f, 0x33, 0xbf, + 0x9c, 0x16, 0xf9, 0xa5, 0x45, 0x40, 0x9c, 0xe2, 0x9b, 0x1f, 0x42, 0x73, 0x72, + 0x5f, 0xc0, 0xdf, 0x02, 0xa0, 0x4e, 0xba, 0xe1, 0x78, 0xb3, 0x41, 0x4f, 0xb0, + 0xa8, 0x2d, 0x50, 0xde, 0xb0, 0x9f, 0xcf, 0x4e, 0x6e, 0xe9, 0xd1, 0x80, 0xff, + 0x4f, 0x56, 0xff, 0x3b, 0xc1, 0xd3, 0x60, 0x1f, 0xc2, 0xdc, 0x90, 0xd8, 0x14, + 0xc3, 0x25, 0x6f, 0x49, 0x67, 0xd3, 0xa8, 0xd6, 0x4c, 0x83, 0xfe, 0xa3, 0x39, + 0xc5, 0x1f, 0x5a, 0x8e, 0x58, 0x01, 0xfb, 0xb9, 0x78, 0x35, 0x58, 0x1b, 0x60, + 0x24, 0x65, 0xde, 0xe0, 0x4b, 0x59, 0x22, 0xc2, 0x76, 0x1b, 0x54, 0x24, 0x5b, + 0xec, 0x0c, 0x9e, 0xef, 0x2d, 0xb9, 0x7d, 0x22, 0xb2, 0xb3, 0x55, 0x6c, 0xc9, + 0x69, 0xfb, 0xb1, 0x3d, 0x06, 0x50, 0x97, 0x65, 0xa5, 0x2b, 0x3f, 0xac, 0x54, + 0xb9, 0x3f, 0x42, 0x1b, 0xf0, 0x8e, 0x18, 0xd5, 0x2d, 0xdd, 0x52, 0xcc, 0x1c, + 0x8c, 0xa8, 0xad, 0xfa, 0xcc, 0xab, 0x7e, 0x5c, 0xc2, 0xf4, 0x57, 0x3f, 0xbb, + 0xf8, 0x23, 0x9b, 0xb0, 0xb8, 0xae, 0xdb, 0xf8, 0xda, 0xd1, 0x62, 0x82, 0xda, + 0x5c, 0x91, 0x25, 0xdb, 0xa1, 0xc0, 0x59, 0xd0, 0xdf, 0x8a, 0xbf, 0x62, 0x10, + 0x78, 0xf0, 0x2d, 0x6c, 0x4b, 0xc8, 0x6d, 0x40, 0x84, 0x5a, 0xc1, 0xd5, 0x97, + 0x10, 0xc4, 0x5f, 0x07, 0xd5, 0x85, 0xeb, 0x48, 0xb3, 0x2f, 0xc0, 0x16, 0x7b, + 0xa2, 0x56, 0xe7, 0x3c, 0xa3, 0xb9, 0x31, 0x1c, 0x62, 0xd1, 0x09, 0x49, 0x79, + 0x57, 0xd8, 0xdb, 0xe1, 0x0a, 0xa3, 0xe8, 0x66, 0xb4, 0x0c, 0x0b, 0xaa, 0x2b, + 0xc4, 0x92, 0xc1, 0x9a, 0xd1, 0xe6, 0x37, 0x2d, 0x96, 0x22, 0xbf, 0x16, 0x3f, + 0xbf, 0xfe, 0xae, 0xee, 0x79, 0x6a, 0x3c, 0xd9, 0xb6, 0xfb, 0xbf, 0xa4, 0xd7, + 0x92, 0xf3, 0x4d, 0x7f, 0xd6, 0xe7, 0x63, 0xcd, 0x58, 0x59, 0xdd, 0x26, 0x83, + 0x3d, 0x21, 0xd9, 0xbc, 0x54, 0x52, 0xbd, 0x19, 0x51, 0x5d, 0xff, 0x9f, 0x49, + 0x95, 0xb3, 0x5b, 0xc0, 0xc1, 0xf8, 0x76, 0xe6, 0xad, 0x11, 0xf2, 0x45, 0x2d, + 0xc9, 0xae, 0x85, 0xae, 0xc0, 0x1f, 0xc5, 0x6f, 0x8c, 0xbf, 0xda, 0x75, 0xa7, + 0x72, 0x7b, 0x75, 0xeb, 0xbd, 0x6b, 0xbf, 0xfb, 0x43, 0xb6, 0x3a, 0x3b, 0x1b, + 0x67, 0x1e, 0x40, 0xfe, 0xb0, 0xdb, 0x00, 0x29, 0x74, 0xa3, 0xc3, 0xb1, 0xa7, + 0x88, 0x56, 0x72, 0x31, 0xbf, 0x63, 0x99, 0xff, 0x89, 0x23, 0x69, 0x81, 0x14, + 0x9d, 0x42, 0x38, 0x02, 0xd2, 0x34, 0x1a, 0x3b, 0xed, 0xb9, 0xdd, 0xcb, 0xac, + 0x1f, 0xe7, 0xb6, 0x43, 0x5e, 0x14, 0x79, 0xc7, 0x2e, 0x70, 0x89, 0xb5, 0x1b, + 0xfe, 0x2f, 0xf3, 0x45, 0x85, 0x7d, 0xa9, 0xb5, 0x45, 0xe8, 0x8e, 0x32, 0x21, + 0xf3, 0xf5, 0xf7, 0x2d, 0x1e, 0x06, 0x9c, 0x9a, 0x85, 0xdd, 0x22, 0x36, 0xd3, + 0x90, 0x98, 0x95, 0x87, 0xbe, 0x00, 0x5c, 0xda, 0x16, 0xaf, 0x44, 0x08, 0xf3, + 0xab, 0x06, 0xa9, 0x16, 0xee, 0xeb, 0x9c, 0x95, 0x94, 0xb7, 0x04, 0x24, 0xa4, + 0xc1, 0xd1, 0x71, 0x29, 0x5b, 0x67, 0x63, 0xb2, 0x2f, 0x47, 0x12, 0xba, 0x7b, + 0xef, 0xf0, 0xff, 0x27, 0x88, 0x3a, 0xfa, 0xff, 0x26, 0x03, 0x4b, 0x89, 0x57, + 0x35, 0x70, 0x9c, 0xf9, 0x37, 0xbd, 0x22, 0x31, 0x89, 0x1e, 0x70, 0xeb, 0x27, + 0x71, 0xe9, 0x92, 0x7c, 0x97, 0xf8, 0x76, 0x4e, 0xb4, 0x8e, 0x91, 0x1d, 0x42, + 0x8e, 0xc8, 0xd8, 0x61, 0xb7, 0x08, 0xe8, 0x29, 0x8a, 0xcb, 0x62, 0x15, 0x51, + 0x45, 0x15, 0x5a, 0xe9, 0x5f, 0x0a, 0x1d, 0x15, 0x01, 0x03, 0x47, 0x53, 0x14, + 0x6e, 0x22, 0xd0, 0x5f, 0x58, 0x6d, 0x7f, 0x6b, 0x4f, 0xe1, 0x2d, 0xad, 0x9a, + 0x17, 0xf5, 0xdb, 0x70, 0xb1, 0xdb, 0x96, 0xb8, 0xd9, 0xa8, 0x3e, 0xda, 0xdc, + 0x96, 0x6c, 0x8a, 0x54, 0x66, 0xb6, 0x1f, 0xc9, 0x98, 0xc3, 0x1f, 0x10, 0x70, + 0xd9, 0xa5, 0xc9, 0xa6, 0xd2, 0x68, 0xd3, 0x04, 0xfe, 0x6b, 0x8f, 0xd3, 0xb4, + 0x01, 0x03, 0x48, 0x61, 0x1a, 0xbd, 0xcb, 0xd4, 0x9f, 0xe4, 0xf8, 0x5b, 0x62, + 0x3c, 0x78, 0x28, 0xc7, 0x13, 0x82, 0xe1, 0x03, 0x4e, 0xa6, 0x7b, 0xc8, 0xae, + 0x97, 0x40, 0x4b, 0x0c, 0x50, 0xb2, 0xa0, 0x4f, 0x55, 0x9e, 0x49, 0x95, 0x0a, + 0xfc, 0xb0, 0xef, 0x46, 0x2a, 0x2a, 0xe0, 0x24, 0xb0, 0xf0, 0x22, 0x4d, 0xfd, + 0x73, 0x68, 0x4b, 0x88, 0xc7, 0xfb, 0xe9, 0x2d, 0x02, 0xb6, 0x8f, 0x75, 0x9c, + 0x47, 0x52, 0x66, 0x3c, 0xd7, 0xb9, 0x7a, 0x14, 0x94, 0x36, 0x49, 0x30, 0x55, + 0x21, 0x32, 0x6b, 0xde, 0x08, 0x56, 0x30, 0x86, 0x46, 0x29, 0x29, 0x1b, 0xae, + 0x25, 0xff, 0x88, 0x22, 0xa1, 0x4c, 0x4b, 0x66, 0x6a, 0x92, 0x59, 0xad, 0x0d, + 0xc4, 0x2a, 0x82, 0x90, 0xac, 0x7b, 0xc7, 0xf5, 0x3a, 0x16, 0xf3, 0x79, 0xf7, + 0x58, 0xe5, 0xde, 0x75, 0x0f, 0x04, 0xfd, 0x7c, 0xad, 0x47, 0x70, 0x1c, 0x85, + 0x97, 0xf9, 0x78, 0x88, 0xbe, 0xa6, 0xfa, 0x0b, 0xf2, 0x99, 0x99, 0x56, 0xfb, + 0xfd, 0x0e, 0xe6, 0x8e, 0xc3, 0x6e, 0x46, 0x88, 0x80, 0x9a, 0xe2, 0x31, 0xeb, + 0x8b, 0xc4, 0x36, 0x9f, 0x5f, 0xe1, 0x57, 0x3f, 0x57, 0xe0, 0x99, 0xd9, 0xc0, + 0x99, 0x01, 0xbf, 0x39, 0xca, 0xac, 0x48, 0xdc, 0x11, 0x95, 0x6a, 0x8a, 0xe9, + 0x05, 0xea, 0xd8, 0x69, 0x54, 0x54, 0x7c, 0x44, 0x8a, 0xe4, 0x3d, 0x31, 0x5e, + 0x66, 0x9c, 0x42, 0x42, 0xda, 0x56, 0x59, 0x38, 0xf4, 0x17, 0xbf, 0x43, 0xce, + 0x7b, 0x2b, 0x30, 0xb1, 0xcd, 0x40, 0x18, 0x38, 0x8e, 0x1a, 0x91, 0x0f, 0x0f, + 0xc4, 0x1f, 0xb0, 0x87, 0x7a, 0x59, 0x25, 0xe4, 0x66, 0x81, 0x9d, 0x37, 0x5b, + 0x0a, 0x91, 0x2d, 0x4f, 0xe8, 0x43, 0xb7, 0x6e, 0xf6, 0xf2, 0x23, 0xf0, 0xf7, + 0xc8, 0x94, 0xf3, 0x8f, 0x7a, 0xb7, 0x80, 0xdf, 0xd7, 0x5f, 0x66, 0x9c, 0x8c, + 0x06, 0xcf, 0xfa, 0x43, 0xeb, 0x47, 0x56, 0x5a, 0x50, 0xe3, 0xb1, 0xfa, 0x45, + 0xad, 0x61, 0xce, 0x9a, 0x1c, 0x47, 0x27, 0xb7, 0xaa, 0xa5, 0x35, 0x62, 0xf5, + 0x23, 0xe7, 0x39, 0x52, 0xbb, 0xf3, 0x3d, 0x8a, 0x41, 0x04, 0x07, 0x8a, 0xde, + 0x3e, 0xaa, 0xa4, 0x96, 0x99, 0xa6, 0x9f, 0xdf, 0x1c, 0x5a, 0xc7, 0x73, 0x21, + 0x46, 0xee, 0x5e, 0x1d, 0x6b, 0x6c, 0xa9, 0xb9, 0x18, 0x0f, 0x96, 0x4c, 0xc9, + 0xd0, 0x87, 0x8a, 0xe1, 0x37, 0x35, 0x24, 0xd7, 0xd5, 0x10, 0xe5, 0x82, 0x27, + 0xdf, 0x6d, 0xe9, 0xd3, 0x0d, 0x27, 0x18, 0x67, 0x64, 0x01, 0x77, 0xb0, 0xf1, + 0x85, 0x6e, 0x28, 0xd5, 0xc8, 0xaf, 0xb0, 0x95, 0xef, 0x61, 0x84, 0xfe, 0xd6, + 0x51, 0x58, 0x90, 0x22, 0xee, 0xae, 0xa4, 0xc0, 0xce, 0x1f, 0xa6, 0xf0, 0x85, + 0x09, 0x2b, 0x04, 0x97, 0x94, 0x89, 0x17, 0x2b, 0x3e, 0xf8, 0x19, 0x4a, 0x79, + 0x8d, 0xf5, 0x72, 0x4d, 0x6b, 0x05, 0xf1, 0xae, 0x00, 0x00, 0x13, 0xa0, 0x8d, + 0x61, 0x2b, 0xca, 0x8a, 0x8c, 0x31, 0x44, 0x3c, 0x10, 0x34, 0x6d, 0xbf, 0x61, + 0xde, 0x84, 0x75, 0xc0, 0xbb, 0xec, 0x51, 0x04, 0xb4, 0x75, 0x56, 0xaf, 0x3d, + 0x51, 0x44, 0x58, 0xe2, 0x32, 0x1d, 0x14, 0x60, 0x71, 0x78, 0x9d, 0x23, 0x35, + 0x93, 0x4a, 0x68, 0x06, 0x14, 0xe8, 0x35, 0x62, 0xf8, 0x2d, 0xfd, 0x40, 0x5b, + 0x54, 0xa4, 0x5e, 0xb3, 0x2c, 0x16, 0x54, 0x48, 0xd4, 0xd5, 0xd6, 0x1c, 0xa2, + 0x85, 0x95, 0x85, 0x36, 0x9f, 0x53, 0xf1, 0xa1, 0x37, 0xe9, 0xe8, 0x2b, 0x67, + 0xb8, 0xfd, 0xaf, 0x01, 0xbd, 0xa5, 0x4a, 0x31, 0x73, 0x11, 0x89, 0x6a, 0xe1, + 0x02, 0x80, 0xa0, 0x32, 0x44, 0x0c, 0x42, 0x0a, 0x42, 0x1e, 0x94, 0x4d, 0x1e, + 0x95, 0x2b, 0x70, 0xd5, 0x82, 0x6c, 0xd3, 0xb0, 0x8b, 0x7d, 0xb9, 0x63, 0x0f, + 0xe4, 0xfd, 0x5f, 0x22, 0x12, 0x5d, 0xe8, 0x40, 0xfc, 0xc4, 0x0b, 0x98, 0x03, + 0x8a, 0xf1, 0x1d, 0x55, 0xbe, 0x25, 0x43, 0x25, 0x97, 0xb4, 0xb6, 0x5b, 0x9e, + 0xc1, 0xc7, 0xa8, 0xbb, 0xfd, 0x05, 0x2c, 0xbf, 0x7e, 0x1c, 0x17, 0x85, 0x31, + 0x49, 0x34, 0xb2, 0x62, 0xd5, 0x85, 0x37, 0x54, 0xf1, 0xf1, 0x77, 0x71, 0xcf, + 0xb7, 0x50, 0x30, 0x72, 0x65, 0x57, 0x53, 0xfa, 0x3f, 0x54, 0xec, 0xc5, 0x87, + 0xe9, 0xf8, 0x3b, 0x58, 0x19, 0x16, 0x09, 0x2d, 0xf2, 0x6e, 0x63, 0xe1, 0x89, + 0x94, 0xcb, 0x0d, 0xb9, 0x1a, 0x0b, 0xbd, 0xc7, 0xb6, 0x11, 0x9b, 0x32, 0x22, + 0x2a, 0xdf, 0x5e, 0x61, 0xd8, 0xd8, 0xae, 0x89, 0xda, 0xe4, 0x95, 0x4b, 0x54, + 0x81, 0x3b, 0xb3, 0x3f, 0x08, 0xd5, 0x62, 0xba, 0x51, 0x3f, 0xee, 0x1b, 0x09, + 0xc0, 0xfc, 0xd5, 0x16, 0x05, 0x54, 0x19, 0x47, 0x4d, 0xd7, 0xfd, 0xa0, 0x38, + 0xa8, 0x9c, 0x84, 0xea, 0x7b, 0x94, 0x68, 0x28, 0x7f, 0x0e, 0xb0, 0xc1, 0x0c, + 0x4b, 0x13, 0x25, 0x20, 0x19, 0x4d, 0x3d, 0x8d, 0x53, 0x51, 0xfc, 0x10, 0xd0, + 0x9c, 0x15, 0xc8, 0xcc, 0x10, 0x1a, 0xa1, 0x66, 0x3b, 0xbf, 0x17, 0xb8, 0x41, + 0x11, 0xf3, 0x8b, 0xb4, 0x39, 0xf0, 0x73, 0x53, 0xbd, 0xea, 0x35, 0x96, 0xd1, + 0x5e, 0x71, 0x3e, 0x1e, 0x2e, 0x7d, 0x3f, 0x1c, 0x38, 0x31, 0x35, 0xb4, 0x7f, + 0xa7, 0xf8, 0x1f, 0x46, 0xdf, 0x7a, 0x90, 0x2a, 0x40, 0x46, 0x99, 0xec, 0x91, + 0x2f, 0x56, 0x56, 0xc3, 0x5b, 0x85, 0x76, 0x3e, 0x4d, 0xe5, 0x83, 0xae, 0xca, + 0xa1, 0xdf, 0xd5, 0xd2, 0x67, 0x7d, 0x9c, 0x8f, 0xfe, 0xe8, 0x77, 0xf6, 0x3f, + 0x40, 0xa5, 0xca, 0x0d, 0x67, 0xf6, 0xe5, 0x54, 0x12, 0x47, 0x00, 0xf8, 0x05, + 0xaf, 0x87, 0x6a, 0xee, 0xde, 0x53, 0xaa, 0x8b, 0x0f, 0x8e, 0x56, 0x04, 0xa7, + 0x3c, 0x30, 0xcb, 0xd0, 0x9d, 0xad, 0x96, 0x3d, 0x6f, 0x8a, 0x5d, 0xcc, 0x40, + 0xde, 0xf4, 0x07, 0x97, 0x34, 0x21, 0x13, 0xba, 0x20, 0x6f, 0xae, 0x8e, 0xbe, + 0x4f, 0x3b, 0xc3, 0xca, 0xf6, 0x92, 0x59, 0xe4, 0x62, 0xef, 0xf9, 0xba, 0x8b, + 0x3f, 0x4b, 0xfa, 0xa1, 0x30, 0x0c, 0x26, 0x92, 0x5a, 0x87, + ], + script_code: Script(vec![0x63]), + transparent_input: None, + hash_type: 1, + amount: 1969273897303781, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x63, 0xd1, 0x85, 0x34, 0xde, 0x5f, 0x2d, 0x1c, 0x9e, 0x16, 0x9b, 0x73, 0xf9, + 0xc7, 0x83, 0x71, 0x8a, 0xdb, 0xef, 0x5c, 0x8a, 0x7d, 0x55, 0xb5, 0xe7, 0xa3, + 0x7a, 0xff, 0xa1, 0xdd, 0x3f, 0xf3, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0x0b, 0xbe, 0x32, 0xa5, + 0x98, 0xc2, 0x2a, 0xdf, 0xb4, 0x8c, 0xef, 0x72, 0xba, 0x5d, 0x42, 0x87, 0xc0, + 0xce, 0xfb, 0xac, 0xfd, 0x8c, 0xe1, 0x95, 0xb4, 0x96, 0x3c, 0x34, 0xa9, 0x4b, + 0xba, 0x7a, 0x17, 0x5d, 0xae, 0x4b, 0x04, 0x65, 0xac, 0x65, 0x63, 0x53, 0x70, + 0x89, 0x15, 0x09, 0x0f, 0x47, 0xa0, 0x68, 0xe2, 0x27, 0x43, 0x3f, 0x9e, 0x49, + 0xd3, 0xaa, 0x09, 0xe3, 0x56, 0xd8, 0xd6, 0x6d, 0x0c, 0x01, 0x21, 0xe9, 0x1a, + 0x3c, 0x4a, 0xa3, 0xf2, 0x7f, 0xa1, 0xb6, 0x33, 0x96, 0xe2, 0xb4, 0x1d, 0x09, + 0x00, 0x63, 0x53, 0x53, 0x00, 0xac, 0x53, 0xac, 0x51, 0x4e, 0x97, 0x05, 0x68, + 0x02, 0xda, 0x07, 0x1b, 0x97, 0x0d, 0x48, 0x07, 0x00, 0x01, 0x52, 0xa8, 0x44, + 0x55, 0x0b, 0xdc, 0x20, 0x02, 0x00, 0x07, 0x52, 0x52, 0x6a, 0x65, 0x52, 0x00, + 0x52, 0xd7, 0x03, 0x43, 0x02, 0x01, 0x1b, 0x9a, 0x07, 0x66, 0x20, 0xed, 0xc0, + 0x67, 0xff, 0x02, 0x00, 0x00, 0x03, 0x53, 0xe3, 0xb8, 0xa7, 0x1f, 0xac, 0xe1, + 0xc9, 0xf3, 0x77, 0x45, 0xed, 0x36, 0x88, 0x35, 0x29, 0x30, 0x4b, 0xfd, 0x5a, + 0x39, 0x0b, 0x37, 0xbc, 0x5a, 0x34, 0x45, 0x24, 0x1f, 0x03, 0xf6, 0x4a, 0x81, + 0x88, 0x20, 0xdf, 0xed, 0xdd, 0x75, 0x37, 0x51, 0x59, 0xfb, 0xd2, 0x1e, 0xca, + 0x98, 0x72, 0x10, 0x4f, 0x8d, 0x7b, 0x3c, 0x8c, 0x86, 0x97, 0x03, 0xa1, 0xe7, + 0x84, 0x8a, 0x5c, 0x94, 0x1e, 0x45, 0xa9, 0xc7, 0x94, 0x34, 0x46, 0xd0, 0xdc, + 0x96, 0x27, 0xcb, 0x31, 0xf8, 0x0e, 0x7a, 0xa5, 0x96, 0xd4, 0x82, 0x1d, 0xc9, + 0x9a, 0x7d, 0x77, 0x7c, 0xd5, 0x7e, 0x19, 0x48, 0x42, 0xa0, 0x23, 0x47, 0x1f, + 0x0f, 0x62, 0x88, 0xa1, 0x50, 0x64, 0x7b, 0x2a, 0xfe, 0x9d, 0xf7, 0xcc, 0xcf, + 0x01, 0xf5, 0xcd, 0xe5, 0xf0, 0x46, 0x80, 0xbb, 0xfe, 0xd8, 0x7f, 0x6c, 0xf4, + 0x29, 0xfb, 0x27, 0xad, 0x6b, 0xab, 0xe7, 0x91, 0x76, 0x66, 0x11, 0xcf, 0x5b, + 0xc2, 0x0e, 0x48, 0xbe, 0xf1, 0x19, 0x25, 0x9b, 0x9b, 0x8a, 0x0e, 0x39, 0xc3, + 0xdf, 0x28, 0xcb, 0x95, 0x82, 0xea, 0x33, 0x86, 0x01, 0xcd, 0xc4, 0x81, 0xb3, + 0x2f, 0xb8, 0x2a, 0xde, 0xeb, 0xb3, 0xda, 0xde, 0x25, 0xd1, 0xa3, 0xdf, 0x20, + 0xc3, 0x7e, 0x71, 0x25, 0x06, 0xb5, 0xd9, 0x96, 0xc4, 0x9a, 0x9f, 0x0f, 0x30, + 0xdd, 0xcb, 0x91, 0xfe, 0x90, 0x04, 0xe1, 0xe8, 0x32, 0x94, 0xa6, 0xc9, 0x20, + 0x3d, 0x94, 0xe8, 0xdc, 0x2c, 0xbb, 0x44, 0x9d, 0xe4, 0x15, 0x50, 0x32, 0x60, + 0x4e, 0x47, 0x99, 0x70, 0x16, 0xb3, 0x04, 0xfd, 0x43, 0x7d, 0x82, 0x35, 0x04, + 0x5e, 0x25, 0x5a, 0x19, 0xb7, 0x43, 0xa0, 0xa9, 0xf2, 0xe3, 0x36, 0xb4, 0x4c, + 0xae, 0x30, 0x7b, 0xb3, 0x98, 0x7b, 0xd3, 0xe4, 0xe7, 0x77, 0xfb, 0xb3, 0x4c, + 0x0a, 0xb8, 0xcc, 0x3d, 0x67, 0x46, 0x6c, 0x0a, 0x88, 0xdd, 0x4c, 0xca, 0xd1, + 0x8a, 0x07, 0xa8, 0xd1, 0x06, 0x8d, 0xf5, 0xb6, 0x29, 0xe5, 0x71, 0x8d, 0x0f, + 0x6d, 0xf5, 0xc9, 0x57, 0xcf, 0x71, 0xbb, 0x00, 0xa5, 0x17, 0x8f, 0x17, 0x5c, + 0xac, 0xa9, 0x44, 0xe6, 0x35, 0xc5, 0x15, 0x9f, 0x73, 0x8e, 0x24, 0x02, 0xa2, + 0xd2, 0x1a, 0xa0, 0x81, 0xe1, 0x0e, 0x45, 0x6a, 0xfb, 0x00, 0xb9, 0xf6, 0x24, + 0x16, 0xc8, 0xb9, 0xc0, 0xf7, 0x22, 0x8f, 0x51, 0x07, 0x29, 0xe0, 0xbe, 0x3f, + 0x30, 0x53, 0x13, 0xd7, 0x7f, 0x73, 0x79, 0xdc, 0x2a, 0xf2, 0x48, 0x69, 0xc6, + 0xc7, 0x4e, 0xe4, 0x47, 0x14, 0x98, 0x86, 0x1d, 0x19, 0x2f, 0x0f, 0xf0, 0xf5, + 0x08, 0x28, 0x5d, 0xab, 0x6b, 0x6a, 0x36, 0xcc, 0xf7, 0xd1, 0x22, 0x56, 0xcc, + 0x76, 0xb9, 0x55, 0x03, 0x72, 0x0a, 0xc6, 0x72, 0xd0, 0x82, 0x68, 0xd2, 0xcf, + 0x77, 0x73, 0xb6, 0xba, 0x2a, 0x5f, 0x66, 0x48, 0x47, 0xbf, 0x70, 0x7f, 0x2f, + 0xc1, 0x0c, 0x98, 0xf2, 0xf0, 0x06, 0xec, 0x22, 0xcc, 0xb5, 0xa8, 0xc8, 0xb7, + 0xc4, 0x0c, 0x7c, 0x2d, 0x49, 0xa6, 0x63, 0x9b, 0x9f, 0x2c, 0xe3, 0x3c, 0x25, + 0xc0, 0x4b, 0xc4, 0x61, 0xe7, 0x44, 0xdf, 0xa5, 0x36, 0xb0, 0x0d, 0x94, 0xba, + 0xdd, 0xf4, 0xf4, 0xd1, 0x40, 0x44, 0xc6, 0x95, 0xa3, 0x38, 0x81, 0x47, 0x7d, + 0xf1, 0x24, 0xf0, 0xfc, 0xf2, 0x06, 0xa9, 0xfb, 0x2e, 0x65, 0xe3, 0x04, 0xcd, + 0xbf, 0x0c, 0x4d, 0x23, 0x90, 0x17, 0x0c, 0x13, 0x0a, 0xb8, 0x49, 0xc2, 0xf2, + 0x2b, 0x5c, 0xdd, 0x39, 0x21, 0x64, 0x0c, 0x8c, 0xf1, 0x97, 0x6a, 0xe1, 0x01, + 0x0b, 0x0d, 0xfd, 0x9c, 0xb2, 0x54, 0x3e, 0x45, 0xf9, 0x97, 0x49, 0xcc, 0x4d, + 0x61, 0xf2, 0xe8, 0xaa, 0xbf, 0xe9, 0x8b, 0xd9, 0x05, 0xfa, 0x39, 0x95, 0x1b, + 0x33, 0xea, 0x76, 0x9c, 0x45, 0xab, 0x95, 0x31, 0xc5, 0x72, 0x09, 0x86, 0x2a, + 0xd1, 0x2f, 0xd7, 0x6b, 0xa4, 0x80, 0x7e, 0x65, 0x41, 0x7b, 0x6c, 0xd1, 0x2f, + 0xa8, 0xec, 0x91, 0x6f, 0x01, 0x3e, 0xbb, 0x87, 0x06, 0xa9, 0x6e, 0xff, 0xed, + 0xa0, 0x6c, 0x4b, 0xe2, 0x4b, 0x04, 0x84, 0x63, 0x92, 0xe9, 0xd1, 0xe6, 0x93, + 0x0e, 0xae, 0x01, 0xfa, 0x21, 0xfb, 0xd7, 0x00, 0x58, 0x3f, 0xb5, 0x98, 0xb9, + 0x2c, 0x8f, 0x4e, 0xb8, 0xa6, 0x1a, 0xa6, 0x23, 0x5d, 0xb6, 0x0f, 0x28, 0x41, + 0xcf, 0x3a, 0x1c, 0x6a, 0xb5, 0x4c, 0x67, 0x06, 0x68, 0x44, 0x71, 0x1d, 0x09, + 0x1e, 0xb9, 0x31, 0xa1, 0xbd, 0x62, 0x81, 0xae, 0xdf, 0x2a, 0x0e, 0x8f, 0xab, + 0x18, 0x81, 0x72, 0x02, 0xa9, 0xbe, 0x06, 0x40, 0x2e, 0xd9, 0xcc, 0x72, 0x0c, + 0x16, 0xbf, 0xe8, 0x81, 0xe4, 0xdf, 0x42, 0x55, 0xe8, 0x7a, 0xfb, 0x7f, 0xc6, + 0x2f, 0x38, 0x11, 0x6b, 0xbe, 0x03, 0xcd, 0x8a, 0x3c, 0xb1, 0x1a, 0x27, 0xd5, + 0x68, 0x41, 0x47, 0x82, 0xf4, 0x7b, 0x1a, 0x44, 0xc9, 0x7c, 0x68, 0x04, 0x67, + 0x69, 0x4b, 0xc9, 0x70, 0x9d, 0x32, 0x91, 0x6c, 0x97, 0xe8, 0x00, 0x6c, 0xbb, + 0x07, 0xba, 0x0e, 0x41, 0x80, 0xa3, 0x73, 0x80, 0x38, 0xc3, 0x74, 0xc4, 0xcc, + 0xe8, 0xf3, 0x29, 0x59, 0xaf, 0xb2, 0x5f, 0x30, 0x3f, 0x58, 0x15, 0xc4, 0x53, + 0x31, 0x24, 0xac, 0xf9, 0xd1, 0x89, 0x40, 0xe7, 0x75, 0x22, 0xac, 0x5d, 0xc4, + 0xb9, 0x57, 0x0a, 0xae, 0x8f, 0x47, 0xb7, 0xf5, 0x7f, 0xd8, 0x76, 0x7b, 0xea, + 0x1a, 0x24, 0xae, 0x7b, 0xed, 0x65, 0xb4, 0xaf, 0xdc, 0x8f, 0x12, 0x78, 0xc3, + 0x0e, 0x2d, 0xb9, 0x8f, 0xd1, 0x72, 0x73, 0x0a, 0xc6, 0xbb, 0xed, 0x4f, 0x11, + 0x27, 0xcd, 0x32, 0xb0, 0x4a, 0x95, 0xb2, 0x05, 0x52, 0x6c, 0xfc, 0xb4, 0xc4, + 0xe1, 0xcc, 0x95, 0x51, 0x75, 0xb3, 0xe8, 0xde, 0x1f, 0x5d, 0x81, 0xb1, 0x86, + 0x69, 0x69, 0x23, 0x50, 0xaa, 0xa1, 0xa1, 0xd7, 0x97, 0x61, 0x75, 0x82, 0xe5, + 0x4d, 0x7a, 0x5b, 0x57, 0xa6, 0x83, 0xb3, 0x2f, 0xb1, 0x09, 0x80, 0x62, 0xda, + 0xd7, 0xb0, 0xc2, 0xeb, 0x51, 0x8f, 0x68, 0x62, 0xe8, 0x3d, 0xb2, 0x5e, 0x3d, + 0xba, 0xf7, 0xae, 0xd5, 0x04, 0xde, 0x93, 0x2a, 0xcb, 0x99, 0xd7, 0x35, 0x99, + 0x2c, 0xe6, 0x2b, 0xae, 0x9e, 0xf8, 0x93, 0xff, 0x6a, 0xcc, 0x0f, 0xfc, 0xf8, + 0xe3, 0x48, 0x3e, 0x14, 0x6b, 0x9d, 0x49, 0xdd, 0x8c, 0x78, 0x35, 0xf4, 0x3a, + 0x37, 0xdc, 0xa0, 0x78, 0x7e, 0x3e, 0xc9, 0xf6, 0x60, 0x52, 0x23, 0xd5, 0xba, + 0x7a, 0xe0, 0xab, 0x90, 0x25, 0xb7, 0x3b, 0xc0, 0x3f, 0x7f, 0xac, 0x36, 0xc0, + 0x09, 0xa5, 0x6d, 0x4d, 0x95, 0xd1, 0xe8, 0x1d, 0x3b, 0x3e, 0xbc, 0xa7, 0xe5, + 0x4c, 0xc1, 0xa1, 0x2d, 0x12, 0x7b, 0x57, 0xc8, 0x13, 0x89, 0x76, 0xe7, 0x91, + 0x01, 0x3b, 0x01, 0x5f, 0x06, 0xa6, 0x24, 0xf5, 0x21, 0xb6, 0xee, 0x04, 0xec, + 0x98, 0x08, 0x93, 0xc7, 0xe5, 0xe0, 0x1a, 0x33, 0x62, 0x03, 0x59, 0x40, 0x94, + 0xf8, 0x28, 0x33, 0xd7, 0x44, 0x27, 0x88, 0x00, 0x84, 0xd3, 0x58, 0x63, 0xc8, + 0xe7, 0xeb, 0xb5, 0xc9, 0xee, 0xd9, 0x8e, 0x72, 0x57, 0x2e, 0xc4, 0x0c, 0x79, + 0xb2, 0x66, 0x23, 0xb5, 0x80, 0x22, 0xf4, 0x89, 0xb0, 0x89, 0x3d, 0x88, 0xbe, + 0x63, 0xf3, 0xf8, 0xc0, 0xd2, 0x32, 0x49, 0xeb, 0xcd, 0xe1, 0x3d, 0xb9, 0x31, + 0x29, 0x41, 0xc3, 0x6c, 0x1d, 0x1c, 0xbc, 0xab, 0xac, 0x0c, 0x78, 0xcb, 0x3b, + 0x19, 0x12, 0xdb, 0x0d, 0xcb, 0xfe, 0x18, 0x93, 0xd9, 0xb5, 0x1b, 0xe4, 0xaf, + 0x1d, 0x00, 0x0b, 0xac, 0x1a, 0xd0, 0xa3, 0xae, 0x2c, 0xe1, 0xe7, 0x32, 0x25, + 0xfb, 0x11, 0x4d, 0x05, 0xaf, 0x4c, 0xef, 0xc0, 0x6e, 0x87, 0x5f, 0x07, 0x4f, + 0xfe, 0xae, 0x0c, 0xba, 0x7d, 0xa3, 0xa5, 0x16, 0xc1, 0x73, 0xbe, 0x1c, 0x51, + 0x33, 0x23, 0xe1, 0x19, 0xf6, 0x35, 0xe8, 0x20, 0x9a, 0x07, 0x4b, 0x21, 0x6b, + 0x70, 0x23, 0xfa, 0xdc, 0x2d, 0x25, 0x94, 0x9c, 0x90, 0x03, 0x7e, 0x71, 0xe3, + 0xe5, 0x50, 0x72, 0x6d, 0x21, 0x0a, 0x2c, 0x68, 0x83, 0x42, 0xe5, 0x24, 0x40, + 0x63, 0x5e, 0x9c, 0xc1, 0x4a, 0xfe, 0x10, 0x10, 0x26, 0x21, 0xa9, 0xc9, 0xac, + 0xcb, 0x78, 0x2e, 0x9e, 0x4a, 0x5f, 0xa8, 0x7f, 0x0a, 0x95, 0x6f, 0x5b, 0x85, + 0x50, 0x99, 0x60, 0x28, 0x5c, 0x22, 0x62, 0x7c, 0x59, 0x48, 0x3a, 0x5a, 0x4c, + 0x28, 0xcc, 0xe4, 0xb1, 0x56, 0xe5, 0x51, 0x40, 0x6a, 0x7e, 0xe8, 0x35, 0x56, + 0x56, 0xa2, 0x1e, 0x43, 0xe3, 0x8c, 0xe1, 0x29, 0xfd, 0xad, 0xb7, 0x59, 0xed, + 0xdf, 0xa0, 0x8f, 0x00, 0xfc, 0x8e, 0x56, 0x7c, 0xef, 0x93, 0xc6, 0x79, 0x2d, + 0x01, 0xdf, 0x05, 0xe6, 0xd5, 0x80, 0xf4, 0xd5, 0xd4, 0x8d, 0xf0, 0x42, 0x45, + 0x1a, 0x33, 0x59, 0x0d, 0x3e, 0x8c, 0xf4, 0x9b, 0x26, 0x27, 0x21, 0x8f, 0x0c, + 0x29, 0x2f, 0xa6, 0x6a, 0xda, 0x94, 0x5f, 0xa5, 0x5b, 0xb2, 0x35, 0x48, 0xe3, + 0x3a, 0x83, 0xa5, 0x62, 0x95, 0x7a, 0x31, 0x49, 0xa9, 0x93, 0xcc, 0x47, 0x23, + 0x62, 0x29, 0x87, 0x36, 0xa8, 0xb7, 0x78, 0xd9, 0x7c, 0xe4, 0x23, 0x01, 0x3d, + 0x64, 0xb3, 0x2c, 0xd1, 0x72, 0xef, 0xa5, 0x51, 0xbf, 0x7f, 0x36, 0x8f, 0x04, + 0xbd, 0xae, 0xc6, 0x09, 0x1a, 0x30, 0x04, 0xa7, 0x57, 0x59, 0x8b, 0x80, 0x1d, + 0xcf, 0x67, 0x5c, 0xb8, 0x3e, 0x43, 0xa5, 0x3a, 0xe8, 0xb2, 0x54, 0xd3, 0x33, + 0xbc, 0xda, 0x20, 0xd4, 0x81, 0x7d, 0x34, 0x77, 0xab, 0xfb, 0xa2, 0x5b, 0xb8, + 0x3d, 0xf5, 0x94, 0x9c, 0x12, 0x6f, 0x14, 0x9b, 0x1d, 0x99, 0x34, 0x1e, 0x4e, + 0x6f, 0x91, 0x20, 0xf4, 0xd4, 0x1e, 0x62, 0x91, 0x85, 0x00, 0x2c, 0x72, 0xc0, + 0x12, 0xc4, 0x14, 0xd2, 0x38, 0x2a, 0x6d, 0x47, 0xc7, 0xb3, 0xde, 0xab, 0xa7, + 0x70, 0xc4, 0x00, 0xca, 0x96, 0xb2, 0x81, 0x4f, 0x6b, 0x26, 0xc3, 0xef, 0x17, + 0x42, 0x9f, 0x1a, 0x98, 0xc8, 0x5d, 0x83, 0xdb, 0x20, 0xef, 0xad, 0x48, 0xbe, + 0x89, 0x96, 0xfb, 0x1b, 0xff, 0x59, 0x1e, 0xff, 0xf3, 0x60, 0xfe, 0x11, 0x99, + 0x05, 0x6c, 0x56, 0xe5, 0xfe, 0xec, 0x61, 0xa7, 0xb8, 0xb9, 0xf6, 0x99, 0xd6, + 0x01, 0x2c, 0x28, 0x49, 0x23, 0x2f, 0x32, 0x9f, 0xef, 0x95, 0xc7, 0xaf, 0x37, + 0x00, 0x98, 0xff, 0xe4, 0x91, 0x8e, 0x0c, 0xa1, 0xdf, 0x47, 0xf2, 0x75, 0x86, + 0x7b, 0x73, 0x9e, 0x0a, 0x51, 0x4d, 0x32, 0x09, 0x32, 0x5e, 0x21, 0x70, 0x45, + 0x92, 0x7b, 0x47, 0x9c, 0x1c, 0xe2, 0xe5, 0xd5, 0x4f, 0x25, 0x48, 0x8c, 0xad, + 0x15, 0x13, 0xe3, 0xf4, 0x4a, 0x21, 0x26, 0x6c, 0xfd, 0x84, 0x16, 0x33, 0x32, + 0x7d, 0xee, 0x6c, 0xf8, 0x10, 0xfb, 0xf7, 0x39, 0x3e, 0x31, 0x7d, 0x9e, 0x53, + 0xd1, 0xbe, 0x1d, 0x5a, 0xe7, 0x83, 0x9b, 0x66, 0xb9, 0x43, 0xb9, 0xed, 0x18, + 0xf2, 0xc5, 0x30, 0xe9, 0x75, 0x42, 0x23, 0x32, 0xc3, 0x43, 0x9c, 0xce, 0x49, + 0xa2, 0x9f, 0x2a, 0x33, 0x6a, 0x48, 0x51, 0x26, 0x3c, 0x5e, 0x9b, 0xd1, 0x3d, + 0x73, 0x11, 0x09, 0xe8, 0x44, 0xb7, 0xf8, 0xc3, 0x92, 0xa5, 0xc1, 0xdc, 0xaa, + 0x2a, 0xe5, 0xf5, 0x0f, 0xf6, 0x3f, 0xab, 0x97, 0x65, 0xe0, 0x16, 0x70, 0x2c, + 0x35, 0xa6, 0x7c, 0xd7, 0x36, 0x4d, 0x3f, 0xab, 0x55, 0x2f, 0xb3, 0x49, 0xe3, + 0x5c, 0x15, 0xc5, 0x02, 0x50, 0x45, 0x3f, 0xd1, 0x8f, 0x7b, 0x85, 0x59, 0x92, + 0x63, 0x2e, 0x2c, 0x76, 0xc0, 0xfb, 0xf1, 0xef, 0x96, 0x3e, 0xa8, 0x0e, 0x32, + 0x23, 0xde, 0x32, 0x77, 0xbc, 0x55, 0x92, 0x51, 0x72, 0x58, 0x29, 0xec, 0x03, + 0xf2, 0x13, 0xba, 0x89, 0x55, 0xca, 0xb2, 0x82, 0x2f, 0xf2, 0x1a, 0x9b, 0x0a, + 0x49, 0x04, 0xd6, 0x68, 0xfc, 0xd7, 0x72, 0x24, 0xbd, 0xe3, 0xdd, 0x01, 0xf6, + 0xff, 0xc4, 0x82, 0x8f, 0x6b, 0x64, 0x23, 0x0b, 0x35, 0xc6, 0xa0, 0x49, 0x87, + 0x34, 0x94, 0x27, 0x6e, 0xa1, 0xd7, 0xed, 0x5e, 0x92, 0xcb, 0x4f, 0x90, 0xba, + 0x83, 0xa9, 0xe4, 0x96, 0x01, 0xb1, 0x94, 0x04, 0x2f, 0x29, 0x00, 0xd9, 0x9d, + 0x31, 0x2d, 0x7b, 0x70, 0x50, 0x8c, 0xf1, 0x76, 0x06, 0x6d, 0x15, 0x4d, 0xbe, + 0x96, 0xef, 0x9d, 0x43, 0x67, 0xe4, 0xc8, 0x40, 0xe4, 0xa1, 0x7b, 0x5e, 0x51, + 0x22, 0xe8, 0xeb, 0xe2, 0x15, 0x8a, 0x3c, 0x5f, 0x4c, 0xba, 0xe2, 0x1e, 0xa3, + 0xfa, 0x1a, 0xe6, 0xc2, 0x5a, 0x94, 0x62, 0xeb, 0xcb, 0xb0, 0xfd, 0x5f, 0x14, + 0x55, 0x4b, 0xc9, 0x77, 0x47, 0xc3, 0x3e, 0x34, 0xda, 0x90, 0xc8, 0x16, 0xd8, + 0xd0, 0xd5, 0x0b, 0xfe, 0x37, 0x61, 0x8c, 0x58, 0x12, 0x89, 0x14, 0x84, 0xfa, + 0x25, 0x93, 0x22, 0xc1, 0x50, 0x92, 0xd4, 0x15, 0x5d, 0x86, 0x96, 0xd6, 0xf1, + 0x2f, 0x24, 0xfd, 0x36, 0x44, 0x96, 0xb3, 0xbe, 0x08, 0x71, 0xca, 0x3d, 0xd9, + 0x62, 0x53, 0x48, 0xa6, 0x14, 0xb5, 0x9b, 0xde, 0x45, 0x88, 0x56, 0x49, 0xba, + 0xe3, 0x6d, 0xe3, 0x4d, 0xef, 0x8f, 0xce, 0xc8, 0x53, 0x43, 0x47, 0x5d, 0x97, + 0x6a, 0xe1, 0xe9, 0xb2, 0x78, 0x29, 0xce, 0x2a, 0xc5, 0xef, 0xd0, 0xb3, 0x99, + 0xa8, 0xb4, 0x48, 0xbe, 0x65, 0x04, 0x29, 0x4e, 0xe6, 0xb3, 0xc1, 0xc6, 0xa5, + 0x34, 0x2d, 0x7c, 0x01, 0xae, 0x9d, 0x8a, 0xd3, 0x07, 0x0c, 0x2b, 0x1a, 0x91, + 0x57, 0x3a, 0xf5, 0xe0, 0xc5, 0xe4, 0xcb, 0xbf, 0x4a, 0xcd, 0xc6, 0xb5, 0x4c, + 0x92, 0x72, 0x20, 0x0d, 0x99, 0x70, 0x25, 0x0c, 0x17, 0xc1, 0x03, 0x6f, 0x06, + 0x08, 0x5c, 0x41, 0x85, 0x8e, 0xd3, 0xa0, 0xc4, 0x81, 0x50, 0xbc, 0x69, 0x7e, + 0x4a, 0x69, 0x5f, 0xef, 0x33, 0x5f, 0x7a, 0xd0, 0x7e, 0x1a, 0x46, 0xdc, 0x76, + 0x7f, 0xf8, 0x22, 0xdb, 0x70, 0xe6, 0x66, 0x90, 0x80, 0xb9, 0x81, 0x6b, 0x22, + 0x32, 0xc8, 0x1a, 0x4c, 0x66, 0xcc, 0x58, 0x6a, 0xbf, 0xe1, 0xea, 0xa8, 0xca, + 0x6c, 0xf4, 0x1f, 0xc3, 0x0e, 0xb8, 0xdc, 0x57, 0xc3, 0x7a, 0x3c, 0x39, 0xc5, + 0x9c, 0x94, 0x23, 0x2d, 0xf9, 0xd3, 0x88, 0xdb, 0xfa, 0x35, 0xc2, 0xcd, 0x5c, + 0x75, 0xf3, 0x28, 0xe9, 0xfe, 0xa7, 0x8f, 0x65, 0x56, 0x8f, 0x2b, 0xb9, 0x34, + 0xc8, 0x2c, 0x41, 0x42, 0xda, 0x69, 0xd1, 0x2c, 0xa7, 0xde, 0x9a, 0x7d, 0xf7, + 0x06, 0x40, 0x0e, 0xc7, 0x98, 0x78, 0xd8, 0x68, 0xe1, 0x7e, 0x8f, 0x71, 0xea, + 0x31, 0x49, 0x5a, 0x8b, 0xae, 0x7b, 0xdc, 0x2e, 0x48, 0xb5, 0x11, 0x87, 0x71, + 0xc2, 0xfc, 0xa0, 0x78, 0xcc, 0xa1, 0xfc, 0xe0, 0xd7, 0xef, 0x0a, 0xf3, 0x47, + 0x8c, 0xf3, 0x6f, 0x69, 0xe8, 0x5a, 0x41, 0xdd, 0x29, 0xb4, 0x29, 0x4a, 0x65, + 0xd3, 0xe0, 0x55, 0xff, 0x71, 0x8d, 0xd9, 0xdc, 0x8c, 0x75, 0xe7, 0xe5, 0xb2, + 0xef, 0xe4, 0x42, 0x63, 0x73, 0x71, 0xb7, 0xc4, 0x8f, 0x6e, 0xe9, 0x9e, 0x3e, + 0xa3, 0x8a, 0x4b, 0x0f, 0x2f, 0x67, 0xfc, 0x2b, 0x90, 0x8c, 0xda, 0x65, 0x7e, + 0xae, 0x75, 0x4e, 0x03, 0x7e, 0x26, 0x2e, 0x9a, 0x9f, 0x9b, 0xd7, 0xec, 0x42, + 0x67, 0xed, 0x8e, 0x96, 0x93, 0x0e, 0x10, 0x84, 0x78, 0x3c, 0x37, 0xd6, 0xf9, + 0xdd, 0x15, 0xfd, 0x29, 0xf4, 0xcc, 0x47, 0x7e, 0x66, 0xf1, 0x30, 0xd6, 0x30, + 0x43, 0x0d, 0xcc, 0x01, 0x04, 0x89, 0x9b, 0x4f, 0x9f, 0x46, 0xeb, 0x09, 0x0e, + 0xf7, 0xfc, 0x90, 0xb4, 0x79, 0xab, 0xf6, 0x1f, 0x93, 0x95, 0x5e, 0xe0, 0x0e, + 0x6a, 0x18, 0x48, 0xf1, 0xab, 0x14, 0xad, 0x33, 0x4f, 0x2b, 0x68, 0x03, 0x58, + 0x08, 0xcd, 0xf1, 0xbb, 0x9e, 0x9d, 0x9a, 0x81, 0x6b, 0xaf, 0x72, 0x8a, 0x95, + 0x5b, 0x96, 0x0b, 0x77, 0x01, 0xfa, 0x62, 0x66, 0x87, 0xdc, 0x3c, 0x9c, 0xba, + 0x64, 0x63, 0x37, 0xb5, 0x3e, 0x29, 0x81, 0x6e, 0x94, 0x82, 0xdd, 0xf5, 0x57, + 0x8a, 0x87, 0x68, 0xaa, 0xe4, 0x77, 0xfc, 0xe4, 0x10, 0xac, 0x2d, 0x5d, 0xe6, + 0x09, 0x58, 0x61, 0xc1, 0x11, 0xd7, 0xfe, 0xb3, 0xe6, 0xbb, 0x4f, 0xbb, 0x5a, + 0x54, 0x95, 0x54, 0x95, 0x97, 0x27, 0x98, 0x35, 0x0a, 0x25, 0x3f, 0x05, 0xf6, + 0x6c, 0x2e, 0xcf, 0xcb, 0xc0, 0xed, 0x43, 0xf5, 0xec, 0x2e, 0x6d, 0x8d, 0xba, + 0x15, 0xa5, 0x12, 0x54, 0xd9, 0x7b, 0x18, 0x21, 0x10, 0x7c, 0x07, 0xdd, 0x9a, + 0x16, 0xef, 0x84, 0x06, 0xf9, 0x43, 0xe2, 0x82, 0xb9, 0x5d, 0x4b, 0x36, 0x25, + 0x30, 0xc9, 0x13, 0xd6, 0xba, 0x42, 0x1d, 0xf6, 0x02, 0x7d, 0xe5, 0xaf, 0x1e, + 0x47, 0x45, 0xd5, 0x86, 0x81, 0x06, 0x95, 0x4b, 0xe6, 0xc1, 0x96, 0x27, 0x80, + 0xa2, 0x94, 0x10, 0x72, 0xe9, 0x51, 0x31, 0xb1, 0x67, 0x9d, 0xf0, 0x63, 0x76, + 0x25, 0x04, 0x2c, 0x37, 0xd4, 0x8f, 0xfb, 0x15, 0x2e, 0x5e, 0xbc, 0x18, 0x5c, + 0x8a, 0x2b, 0x7d, 0x43, 0x85, 0xf1, 0xc9, 0x5a, 0xf9, 0x37, 0xdf, 0x78, 0xdf, + 0xd8, 0x75, 0x7f, 0xab, 0x43, 0x49, 0x68, 0xb0, 0xb5, 0x7c, 0x66, 0x57, 0x44, + 0x68, 0xf1, 0x60, 0xb4, 0x47, 0xac, 0x82, 0x21, 0xe5, 0x06, 0x06, 0x76, 0xa8, + 0x42, 0xa1, 0xc6, 0xb7, 0x17, 0x2d, 0xd3, 0x34, 0x0f, 0x76, 0x40, 0x70, 0xab, + 0x1f, 0xe0, 0x91, 0xc5, 0xc7, 0x4c, 0x95, 0xa5, 0xdc, 0x04, 0x33, 0x90, 0x72, + 0x3a, 0x4c, 0x12, 0x7d, 0xa1, 0x4c, 0xdd, 0xe1, 0xdc, 0x26, 0x75, 0xa6, 0x23, + 0x40, 0xb3, 0xe6, 0xaf, 0xd0, 0x52, 0x2a, 0x31, 0xde, 0x26, 0xe7, 0xd1, 0xec, + 0x3a, 0x9c, 0x8a, 0x09, 0x1f, 0xfd, 0xc7, 0x5b, 0x7e, 0xcf, 0xdc, 0x7c, 0x12, + 0x99, 0x5a, 0x5e, 0x37, 0xce, 0x34, 0x88, 0xbd, 0x29, 0xf8, 0x62, 0x9d, 0x68, + 0xf6, 0x96, 0x49, 0x24, 0x48, 0xdd, 0x52, 0x66, 0x97, 0x47, 0x6d, 0xc0, 0x61, + 0x34, 0x6e, 0xbe, 0x3f, 0x67, 0x72, 0x17, 0xff, 0x9c, 0x60, 0xef, 0xce, 0x94, + 0x3a, 0xf2, 0x8d, 0xfd, 0x3f, 0x9e, 0x59, 0x69, 0x25, 0x98, 0xa6, 0x04, 0x7c, + 0x23, 0xc4, 0xc0, 0x14, 0x00, 0xf1, 0xab, 0x57, 0x30, 0xea, 0xc0, 0xae, 0x8d, + 0x58, 0x43, 0xd5, 0x05, 0x1c, 0x37, 0x62, 0x40, 0x17, 0x2a, 0xf2, 0x18, 0xd7, + 0xa1, 0xec, 0xfe, 0x65, 0xb4, 0xf7, 0x51, 0x00, 0x63, 0x89, 0x83, 0xc1, 0x4d, + 0xe4, 0x97, 0x47, 0x55, 0xda, 0xde, 0x80, 0x18, 0xc9, 0xb8, 0xf4, 0x54, 0x3f, + 0xb0, 0x95, 0x96, 0x15, 0x13, 0xe6, 0x7c, 0x61, 0xdb, 0xc5, 0x9c, 0x60, 0x7f, + 0x9b, 0x51, 0xf8, 0xd0, 0x9b, 0xdc, 0xad, 0x28, 0xbc, 0xfb, 0x9e, 0x5d, 0x27, + 0x44, 0xea, 0x88, 0x48, 0xb2, 0x62, 0x3a, 0xc0, 0x7f, 0x8e, 0xf6, 0x1a, 0x81, + 0xa3, 0x59, 0x10, 0xb8, 0xa1, 0xba, 0xf3, 0x9a, 0x91, 0x9a, 0x7b, 0x60, 0xbc, + 0x60, 0x4d, 0x63, 0x18, 0x5f, 0x75, 0x92, 0x21, 0xd8, 0x47, 0xcc, 0x54, 0xa2, + 0x27, 0x65, 0xa4, 0xc3, 0x34, 0x75, 0xb5, 0x79, 0x1e, 0x9a, 0xf3, 0x27, 0x1f, + 0xc8, 0xd9, 0x35, 0x06, 0x67, 0x09, 0x0d, 0x81, 0x84, 0xec, 0x50, 0x52, 0x2d, + 0x80, 0x4f, 0x23, 0xc4, 0xfb, 0x44, 0xff, 0xa4, 0x81, 0xbc, 0x92, 0xae, 0x40, + 0x8d, 0x1b, 0x9f, 0x2b, 0x13, 0x19, 0x04, 0xf9, 0x70, 0x5c, 0x59, 0xe2, 0xf4, + 0xbd, 0xe7, 0xa3, 0xb2, 0xc0, 0x85, 0xd9, 0x3f, 0xd2, 0xab, 0xc5, 0xe1, 0x4d, + 0x16, 0x30, 0x01, 0xa1, 0x2f, 0x51, 0x93, 0x8d, 0x02, 0x1a, 0xfa, 0x92, 0x23, + 0x9b, 0x87, 0x3d, 0xc6, 0xc3, 0x57, 0xea, 0xa8, 0xaf, 0x4e, 0xe6, 0xd0, 0x05, + 0x40, 0x65, 0x7f, 0xe3, 0x29, 0x14, 0x10, 0x3b, 0x5d, 0x98, 0xf6, 0x8b, 0xd3, + 0xe2, 0xb5, 0x35, 0x9f, 0x08, 0xcc, 0xd8, 0x8d, 0x0c, 0x81, 0x1e, 0x4c, 0x31, + 0xfb, 0xb4, 0x9f, 0x3a, 0x90, 0xbb, 0xd0, 0x5d, 0xce, 0x62, 0xf3, 0x44, 0xe7, + 0x07, 0x75, 0x93, 0x15, 0x9a, 0xe3, 0x50, 0x50, 0xb0, 0x4c, 0x9e, 0x6b, 0x86, + 0xbc, 0x43, 0x2d, 0xc8, 0xb0, 0x48, 0xc7, 0x3c, 0x00, 0x18, 0xca, 0x5b, 0x69, + 0x41, 0x12, 0x97, 0x73, 0x2a, 0x4e, 0x1a, 0xa9, 0x9a, 0x92, 0x8c, 0x71, 0xe7, + 0xa2, 0x4f, 0xd2, 0x77, 0x85, 0x6a, 0xa4, 0x25, 0x01, 0xe5, 0x1b, 0x01, 0x2a, + 0xea, 0x94, 0x46, 0xa2, 0x10, 0x4e, 0x93, 0xf8, 0x15, 0xa0, 0xb3, 0xa2, 0x9b, + 0x45, 0x83, 0x14, 0xf3, 0xd8, 0xbe, 0x2b, 0x98, 0x23, 0xd3, 0x42, 0xf4, 0x62, + 0x13, 0xe9, 0x42, 0xa7, 0xe1, 0x9a, 0x46, 0xe9, 0x70, 0xb5, 0xc5, 0x06, 0x70, + 0x84, 0x30, 0x31, 0x7b, 0x1b, 0xb3, 0xb3, 0x5d, 0xf6, 0x8a, 0xe3, 0x3a, 0x49, + 0x26, 0xa0, 0x3e, 0x6b, 0xfe, 0xb5, 0x51, 0x04, 0x16, 0xfc, 0xbb, 0x05, 0x24, + 0xc9, 0xca, 0x50, 0x74, 0x15, 0x6c, 0xc5, 0xa5, 0xd6, 0xfe, 0x1c, 0x99, 0x5e, + 0xdc, 0x60, 0xa2, 0xf5, 0x50, 0x41, 0x1a, 0xa4, 0x1e, 0x3d, 0xa3, 0xbd, 0xcf, + 0x64, 0xbc, 0xf0, 0x4a, 0x05, 0x10, 0x57, 0x1b, 0x93, 0x6d, 0x47, 0xe5, 0x5c, + 0xec, 0x03, 0x30, 0x00, 0x8d, 0xfe, 0x73, 0x56, 0x34, 0x04, 0xf0, 0x47, 0xd7, + 0xf3, 0xa8, 0xa3, 0xd7, 0x74, 0x3b, 0xc5, 0x54, 0x95, 0x52, 0x10, 0xf1, 0xeb, + 0x0d, 0x08, 0x59, 0x9e, 0xa7, 0x7d, 0x5f, 0x97, 0x4d, 0x87, 0x17, 0x6d, 0x37, + 0xd9, 0x8b, 0x9c, 0x0a, 0xd4, 0x40, 0x40, 0x72, 0x09, 0xed, 0x6a, 0x9f, 0x08, + 0x46, 0x4d, 0x56, 0x55, 0x93, 0xe1, 0xa6, 0x3b, 0x93, 0x85, 0x36, 0xb4, 0x92, + 0x44, 0xe9, 0x7d, + ], + script_code: Script(vec![]), + transparent_input: Some(1), + hash_type: 2, + amount: 652655344020909, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0xbb, 0xe6, 0xd8, 0x4f, 0x57, 0xc5, 0x6b, 0x29, 0xb9, 0x14, 0xc6, 0x94, 0xba, + 0xac, 0xcb, 0x89, 0x12, 0x97, 0xe9, 0x61, 0xde, 0x3e, 0xb4, 0x6c, 0x68, 0xe3, + 0xc8, 0x9c, 0x47, 0xb1, 0xa1, 0xdb, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0x46, 0xcf, 0x28, 0x9b, + 0x7d, 0x89, 0x13, 0x07, 0xbb, 0xa3, 0x70, 0x54, 0xcf, 0x91, 0xb3, 0x1f, 0xc8, + 0x2f, 0x74, 0xd5, 0xfc, 0xc0, 0x00, 0x94, 0x2e, 0xde, 0x91, 0x18, 0x25, 0xf5, + 0x3f, 0xe6, 0x09, 0x68, 0x6f, 0x46, 0x00, 0x23, 0xb1, 0xe9, 0xbc, 0x00, 0xbd, + 0xe8, 0x95, 0xd1, 0x23, 0x8f, 0xad, 0x04, 0xab, 0xa9, 0x88, 0x99, 0x66, 0x7d, + 0x01, 0x00, 0x04, 0xea, 0x42, 0x71, 0x76, 0x09, 0x84, 0x13, 0x90, 0x59, 0x18, + 0xee, 0x21, 0x3d, 0x4e, 0xc1, 0x27, 0x94, 0x74, 0x2d, 0x19, 0xf6, 0x7d, 0x6f, + 0x86, 0xce, 0xf7, 0xe6, 0x98, 0x2e, 0x88, 0x41, 0x71, 0x28, 0x73, 0xa0, 0x1d, + 0x92, 0x51, 0xd8, 0xc8, 0x60, 0xc0, 0x41, 0x52, 0x5b, 0x3b, 0xf4, 0xe3, 0xa2, + 0xeb, 0x92, 0x72, 0x81, 0x5c, 0x75, 0x86, 0x76, 0x84, 0x28, 0xb4, 0xc2, 0xb2, + 0x5e, 0x37, 0x45, 0xf0, 0x09, 0xc5, 0xdc, 0xe2, 0x0b, 0x69, 0xd5, 0xd7, 0xc4, + 0x3c, 0xeb, 0x73, 0x6b, 0x68, 0x31, 0xe8, 0xc1, 0x10, 0xf1, 0x6c, 0xfd, 0xb3, + 0xa4, 0x67, 0xe9, 0x41, 0x4c, 0x00, 0xec, 0xf1, 0x37, 0x31, 0x50, 0x08, 0x94, + 0x55, 0x56, 0x78, 0xc4, 0x97, 0xfa, 0xba, 0x9a, 0x95, 0xd0, 0x1c, 0xc4, 0x64, + 0x39, 0x0f, 0xc4, 0xa7, 0x6b, 0xfa, 0x8b, 0x0e, 0x1c, 0x68, 0xa5, 0x25, 0xd7, + 0x06, 0xd6, 0x60, 0x4b, 0x23, 0x30, 0xb6, 0xb3, 0x48, 0x52, 0x15, 0xf6, 0x06, + 0xf1, 0x88, 0x3a, 0x75, 0x15, 0x88, 0xc7, 0xef, 0xa5, 0x06, 0xc3, 0xe8, 0xd0, + 0xc6, 0x01, 0x92, 0xe8, 0x47, 0x6b, 0xd1, 0x17, 0x5d, 0x95, 0x62, 0x08, 0x7b, + 0xdb, 0x81, 0x8e, 0x66, 0x21, 0x62, 0x86, 0xba, 0xfe, 0x47, 0xff, 0x4d, 0xbc, + 0xce, 0xd5, 0x14, 0x44, 0x48, 0x0a, 0x9a, 0x56, 0x73, 0xec, 0xe7, 0xfa, 0xc7, + 0x3a, 0x0e, 0xd4, 0x1a, 0xb0, 0x05, 0x17, 0x53, 0xa7, 0xca, 0xa8, 0x9b, 0xe3, + 0x13, 0x9a, 0xfd, 0x97, 0x93, 0xb3, 0xe0, 0x2f, 0x27, 0xf0, 0x40, 0x04, 0x65, + 0x95, 0xac, 0xd4, 0x7b, 0xf1, 0x3f, 0xd0, 0xda, 0x27, 0xf0, 0x9e, 0xda, 0x48, + 0x03, 0x6d, 0x3e, 0xe4, 0x37, 0xf2, 0xee, 0x8f, 0x86, 0x06, 0xea, 0x97, 0x34, + 0x3c, 0x33, 0x58, 0x46, 0x57, 0xf4, 0x6d, 0xba, 0x99, 0xdb, 0x5c, 0xfe, 0x6c, + 0xa1, 0x76, 0xfa, 0xb7, 0xb0, 0xf3, 0xbf, 0xa0, 0xab, 0x61, 0xe3, 0x40, 0xc3, + 0x4e, 0xb9, 0xf1, 0x7c, 0x7e, 0xc2, 0xbe, 0x03, 0xb1, 0x80, 0xf0, 0xbb, 0x6f, + 0x43, 0x4c, 0x2a, 0x65, 0x42, 0xe0, 0x0e, 0x84, 0x37, 0x3f, 0x4f, 0x46, 0x49, + 0xcd, 0xa3, 0x2b, 0xf6, 0x86, 0x66, 0x61, 0x43, 0xf6, 0x22, 0xaa, 0x48, 0x04, + 0x60, 0xb5, 0xaf, 0xac, 0x51, 0x86, 0x07, 0xcd, 0x9a, 0xf8, 0xbc, 0xd6, 0xb5, + 0x8c, 0x30, 0x12, 0x73, 0x16, 0xb2, 0x5d, 0x5e, 0xa7, 0xbf, 0x6b, 0x0c, 0xab, + 0x85, 0x42, 0xff, 0x69, 0xd9, 0xb2, 0xf1, 0x80, 0xbe, 0x12, 0xed, 0x75, 0x34, + 0x4a, 0x39, 0x5a, 0xa1, 0x0f, 0x85, 0x2f, 0x08, 0x3a, 0xd6, 0x4e, 0xf4, 0x0e, + 0x9c, 0x03, 0x09, 0xe9, 0xbb, 0xa5, 0x4b, 0x8c, 0xb3, 0x3c, 0x95, 0x49, 0x8a, + 0x69, 0x53, 0x8d, 0x3a, 0xe5, 0xb2, 0x5e, 0x24, 0x70, 0x98, 0xe1, 0x11, 0x7c, + 0x91, 0x8a, 0xaa, 0xae, 0x9c, 0xb6, 0xef, 0x77, 0xab, 0xd1, 0xe0, 0x1c, 0xc7, + 0x43, 0xd0, 0xdd, 0xd0, 0x22, 0x75, 0x95, 0x1b, 0x92, 0x49, 0x95, 0x65, 0xce, + 0x83, 0x1f, 0x30, 0x32, 0xb8, 0x50, 0x57, 0x75, 0x10, 0x8d, 0xc8, 0x5e, 0x2a, + 0xde, 0x2e, 0xac, 0x1e, 0x63, 0x6e, 0x1a, 0xf4, 0x05, 0x4c, 0x8b, 0x6f, 0x57, + 0x63, 0x2d, 0xf2, 0x69, 0xc3, 0x72, 0x3b, 0x32, 0x08, 0x72, 0xe4, 0xc5, 0x7b, + 0x21, 0x83, 0x58, 0xdc, 0x7e, 0x99, 0x05, 0xbb, 0x04, 0xed, 0xf9, 0x2e, 0xdf, + 0x0d, 0xf6, 0x35, 0xf3, 0xbf, 0x36, 0x1e, 0x57, 0xa1, 0x32, 0x96, 0xe1, 0x44, + 0x7a, 0xf5, 0xa5, 0x66, 0x65, 0x17, 0xbc, 0xd3, 0x56, 0x76, 0x21, 0xa7, 0xcf, + 0x84, 0x45, 0x58, 0x96, 0x53, 0x26, 0x20, 0x20, 0xc3, 0x3b, 0xf7, 0x80, 0x31, + 0xb8, 0xee, 0x07, 0x07, 0xde, 0x07, 0x20, 0x68, 0xc1, 0x70, 0x57, 0x03, 0x27, + 0xe6, 0xd9, 0xf5, 0xc6, 0xdd, 0xc3, 0x35, 0x40, 0x2e, 0xfc, 0x54, 0x88, 0x62, + 0xf5, 0xa0, 0x70, 0x94, 0xfd, 0x42, 0x8a, 0x7b, 0xbc, 0x15, 0xd7, 0xb3, 0x8d, + 0x05, 0x36, 0x2c, 0x9c, 0xa9, 0x85, 0xf5, 0x8a, 0x76, 0x64, 0x7d, 0x2b, 0xe4, + 0xc2, 0xcd, 0x6b, 0x3d, 0x17, 0xd6, 0x87, 0x09, 0x71, 0xd7, 0xa0, 0x98, 0xba, + 0xf7, 0x2c, 0x6f, 0x6f, 0x12, 0x14, 0xcf, 0x1f, 0xaa, 0xe4, 0x88, 0xbd, 0x7d, + 0xe2, 0x59, 0xd3, 0x41, 0x5c, 0x2f, 0x0d, 0xde, 0xc7, 0x45, 0x70, 0x04, 0xf3, + 0x57, 0x08, 0xd1, 0xec, 0xcc, 0xcc, 0x0d, 0xf6, 0x5a, 0x04, 0x94, 0x3a, 0xd5, + 0xcb, 0xc1, 0x3f, 0x29, 0x5f, 0x00, 0x0f, 0xe0, 0x56, 0xc4, 0x0b, 0x2d, 0x88, + 0xf2, 0x7d, 0xc3, 0x4c, 0xfe, 0xb8, 0x03, 0xbe, 0x34, 0x83, 0xa9, 0xeb, 0xf9, + 0xb5, 0xa9, 0x02, 0x60, 0x57, 0x72, 0x5d, 0x63, 0xea, 0xd2, 0xc0, 0xc0, 0xff, + 0x1f, 0xe2, 0x6a, 0xc1, 0xe7, 0xbd, 0xfc, 0xd6, 0xfa, 0xd8, 0x75, 0x84, 0x2d, + 0x19, 0x4f, 0x33, 0x17, 0x50, 0x46, 0x2c, 0x06, 0xb8, 0xd7, 0x98, 0x2d, 0x67, + 0x99, 0x5e, 0xd5, 0xd3, 0xae, 0x96, 0xa0, 0x5a, 0xe0, 0x06, 0x7f, 0x4e, 0xb1, + 0xc7, 0xc9, 0x32, 0x31, 0xbd, 0x39, 0x77, 0x3c, 0xbe, 0x0a, 0x9d, 0x66, 0xb0, + 0xc9, 0xaa, 0x8c, 0xff, 0x6a, 0x37, 0x6e, 0x1f, 0x37, 0x2e, 0xac, 0x6a, 0xc4, + 0xe4, 0x6c, 0xc0, 0x94, 0x22, 0x45, 0xd4, 0xc2, 0xdc, 0xf0, 0x2d, 0x76, 0x40, + 0xff, 0xcc, 0x5a, 0x6a, 0xc3, 0xa8, 0x7f, 0x5c, 0x41, 0x15, 0x51, 0xbc, 0xc2, + 0xf2, 0x6c, 0xb9, 0x49, 0x61, 0xd5, 0x3f, 0x95, 0xdd, 0xb1, 0x9a, 0xe9, 0x30, + 0xc8, 0xd7, 0x0f, 0x03, 0x1b, 0x29, 0xa5, 0xdf, 0x99, 0xff, 0x36, 0x69, 0x5e, + 0x80, 0x2c, 0xbc, 0xb6, 0xb1, 0xcf, 0x7d, 0xc9, 0x7a, 0xbb, 0x3b, 0x25, 0x27, + 0x64, 0xc0, 0x1a, 0x62, 0x2d, 0xfb, 0x2e, 0xcb, 0x49, 0xce, 0x71, 0xf7, 0x38, + 0x6e, 0x23, 0x89, 0x5b, 0x5a, 0xfe, 0x16, 0x61, 0x98, 0xb6, 0x7f, 0x5b, 0x42, + 0xb2, 0xf6, 0x5e, 0xcd, 0x0f, 0x82, 0x59, 0x54, 0x78, 0xd8, 0x0a, 0xe5, 0xc8, + 0xce, 0xea, 0x12, 0xa1, 0x61, 0xcc, 0xbb, 0x5e, 0xac, 0x09, 0x99, 0x0f, 0xc6, + 0x19, 0xa4, 0x60, 0x80, 0x43, 0x6d, 0xbd, 0x08, 0xd7, 0x47, 0x84, 0xaf, 0x00, + 0x2d, 0x58, 0xe0, 0x6f, 0xaf, 0x7f, 0x3c, 0xea, 0xe7, 0xd3, 0x41, 0x9b, 0x1f, + 0xca, 0x26, 0x5a, 0x55, 0x59, 0xcf, 0x9e, 0x2d, 0x3b, 0x97, 0xb2, 0xa9, 0x9a, + 0x9b, 0xa5, 0xa8, 0x66, 0x58, 0xc3, 0xfd, 0x9e, 0xc5, 0x5b, 0xfa, 0x9b, 0x32, + 0x85, 0x67, 0x25, 0x4a, 0xb3, 0x6d, 0x2c, 0x7f, 0x44, 0xd2, 0xc7, 0xe1, 0x3e, + 0xb5, 0x4b, 0xeb, 0x70, 0xea, 0x8f, 0xa9, 0x4b, 0x6c, 0x6e, 0x01, 0x2d, 0x79, + 0xe3, 0xf5, 0x36, 0x89, 0xc2, 0xb1, 0xa1, 0x8e, 0xaf, 0x2d, 0x47, 0x1d, 0x13, + 0xc1, 0xab, 0x39, 0xd9, 0x19, 0x4a, 0xe8, 0x43, 0xab, 0x1d, 0x28, 0xff, 0xa8, + 0xf6, 0x9d, 0xc7, 0xe1, 0x5c, 0xc3, 0x8b, 0x12, 0xe8, 0xfc, 0xd7, 0x92, 0x55, + 0xb7, 0x21, 0x60, 0x56, 0xd9, 0xed, 0xb7, 0x48, 0x2f, 0xb9, 0x8a, 0xa0, 0x33, + 0xb6, 0x5e, 0x51, 0xc1, 0xa0, 0x8b, 0x8a, 0x11, 0xd8, 0x4d, 0x04, 0x09, 0xb7, + 0x34, 0xf4, 0x52, 0xaa, 0xf0, 0xd6, 0xb1, 0x8f, 0x50, 0x25, 0x86, 0x83, 0xd3, + 0xf9, 0xa7, 0x6d, 0x39, 0x9f, 0xd0, 0x47, 0xee, 0xe2, 0x88, 0xbb, 0x45, 0x85, + 0x85, 0x1d, 0xc9, 0x3e, 0xcc, 0xc6, 0x23, 0x22, 0x92, 0x4c, 0xd1, 0x3b, 0x5d, + 0xd4, 0xee, 0xd6, 0x6e, 0xd8, 0xd9, 0x97, 0x2d, 0x77, 0x26, 0x29, 0xea, 0x64, + 0x74, 0x2e, 0x54, 0x73, 0x39, 0x81, 0xb0, 0x06, 0xc0, 0x62, 0x46, 0x8e, 0x4b, + 0xd8, 0xf7, 0xdd, 0x9a, 0xf6, 0x98, 0xf5, 0x2a, 0xe8, 0x14, 0x63, 0x4e, 0x81, + 0xd7, 0xf3, 0xe0, 0xc4, 0x20, 0x31, 0x7c, 0xac, 0xa9, 0xae, 0x48, 0x11, 0xc6, + 0xaf, 0x06, 0xfe, 0x80, 0xa8, 0xc0, 0x2a, 0xb7, 0xa0, 0x0e, 0x18, 0xe4, 0xa6, + 0xaa, 0x1e, 0xa1, 0xb7, 0x69, 0x45, 0xd2, 0x61, 0x5d, 0x43, 0xac, 0x11, 0x8b, + 0x56, 0xc2, 0xf2, 0x96, 0x0f, 0xe9, 0x3a, 0x02, 0x5f, 0x13, 0xec, 0x91, 0xff, + 0xc6, 0xd2, 0xc3, 0x53, 0x69, 0x9a, 0xbb, 0x09, 0x2d, 0xed, 0xc0, 0x65, 0xdb, + 0x8f, 0xa2, 0x14, 0xdb, 0xc4, 0x64, 0x66, 0xf8, 0x97, 0xb8, 0x8c, 0x58, 0xb3, + 0x01, 0x52, 0x13, 0x3a, 0xa3, 0x83, 0x1a, 0xf3, 0x7c, 0x74, 0xd9, 0x9e, 0x9e, + 0x36, 0xff, 0x70, 0x11, 0xd3, 0x23, 0x83, 0x05, 0x69, 0x15, 0x08, 0xd0, 0xf1, + 0xf6, 0xaa, 0xaa, 0xa4, 0x25, 0x12, 0x30, 0xc6, 0xcc, 0xc4, 0x66, 0x68, 0xbb, + 0xcf, 0x35, 0xe5, 0xa5, 0xef, 0x2f, 0x86, 0xe6, 0x65, 0xd8, 0xcf, 0xac, 0x74, + 0x76, 0xec, 0xb2, 0x43, 0x78, 0x79, 0x6a, 0x8e, 0xf2, 0xe4, 0xd9, 0x4d, 0x43, + 0x58, 0xbe, 0x2b, 0x47, 0x5f, 0xcc, 0x92, 0xdf, 0x93, 0x82, 0xc5, 0xc0, 0x69, + 0x19, 0xa0, 0xd6, 0x31, 0xec, 0x26, 0x10, 0xfe, 0xdc, 0x21, 0x9b, 0xe6, 0x3f, + 0x37, 0xf2, 0xba, 0x0d, 0x43, 0x23, 0x66, 0x73, 0x6d, 0x86, 0x32, 0xfc, 0xe0, + 0x72, 0xb6, 0xae, 0x5b, 0x6f, 0x3f, 0xd5, 0x9d, 0x3f, 0xaf, 0xf6, 0x38, 0x27, + 0x5a, 0x99, 0x2f, 0xef, 0xc8, 0x7e, 0x60, 0xd4, 0x4c, 0x2c, 0xad, 0xc2, 0xb5, + 0xc4, 0x94, 0xe3, 0xe7, 0x2e, 0xb4, 0x59, 0x7c, 0x96, 0xb4, 0x01, 0x67, 0x79, + 0x9a, 0x90, 0x01, 0xa2, 0xed, 0x36, 0x76, 0xa8, 0xb4, 0x03, 0xae, 0x25, 0xff, + 0xd7, 0x72, 0xf7, 0x08, 0x1e, 0x9a, 0x32, 0xbc, 0xc1, 0xc5, 0xe2, 0xed, 0xd4, + 0xe2, 0xa6, 0x57, 0x6b, 0x78, 0x3c, 0xce, 0x3a, 0xae, 0x11, 0xfa, 0x43, 0x22, + 0x62, 0x54, 0x88, 0x56, 0x18, 0x3e, 0xe6, 0x82, 0xd5, 0xdc, 0x31, 0xbe, 0xb3, + 0x8f, 0x06, 0x1c, 0xbd, 0xec, 0xa7, 0x02, 0x1a, 0x44, 0x4e, 0x2d, 0xd4, 0x17, + 0xdf, 0x26, 0xdc, 0xd2, 0x20, 0xf2, 0xb7, 0x31, 0x77, 0x2b, 0x43, 0x9e, 0x96, + 0xd6, 0x14, 0xe1, 0xfa, 0xcb, 0x48, 0x6c, 0x7a, 0x7d, 0x51, 0x71, 0xb1, 0xde, + 0x35, 0x9f, 0x6a, 0xd3, 0xa9, 0x6f, 0x64, 0x9c, 0x96, 0x91, 0x02, 0xa1, 0x96, + 0x4f, 0xb4, 0xb4, 0xa1, 0xa4, 0x27, 0x9c, 0x68, 0xe6, 0xc3, 0x72, 0xe4, 0x21, + 0x87, 0xd7, 0x54, 0xe8, 0x04, 0xa6, 0x16, 0x53, 0x09, 0x20, 0x69, 0xfb, 0x9b, + 0x6d, 0x25, 0x26, 0x68, 0x90, 0x80, 0x8b, 0x01, 0x5d, 0xf2, 0x8c, 0x80, 0x10, + 0x65, 0xda, 0x6f, 0xeb, 0xdc, 0x1a, 0x56, 0xbf, 0xd0, 0x02, 0x62, 0x5a, 0xcf, + 0xaa, 0x53, 0x73, 0xfd, 0xe1, 0x49, 0xc1, 0xcf, 0xc3, 0x64, 0x9b, 0x48, 0x69, + 0x69, 0x6d, 0x44, 0xec, 0xb1, 0x24, 0x79, 0xc5, 0xeb, 0xef, 0x99, 0x5f, 0x10, + 0x02, 0x9f, 0x8b, 0x53, 0x0e, 0xeb, 0x3f, 0xdc, 0x2e, 0x50, 0xe8, 0x75, 0x7f, + 0xc0, 0xbb, 0x9e, 0x26, 0x30, 0x23, 0xdb, 0x82, 0xf8, 0x78, 0xd9, 0xac, 0x7f, + 0xfb, 0x0b, 0xd4, 0x39, 0x1d, 0xf1, 0xd8, 0x79, 0x89, 0x9a, 0x3e, 0xf5, 0x7b, + 0xfd, 0x0d, 0x1f, 0x77, 0x55, 0x64, 0x8e, 0xdd, 0x85, 0xbb, 0x05, 0x2a, 0x6e, + 0xdf, 0x71, 0xcd, 0x26, 0x28, 0xc9, 0x87, 0x42, 0x9f, 0x36, 0xdc, 0x50, 0x5c, + 0xcc, 0x43, 0xf3, 0x0e, 0x7a, 0x86, 0x9c, 0x9e, 0x25, 0x5e, 0x2a, 0xf9, 0xfc, + 0xf3, 0x0c, 0x12, 0x17, 0x96, 0x03, 0xae, 0x17, 0x57, 0x55, 0x3b, 0x5c, 0x94, + 0x60, 0x7e, 0x00, 0xd0, 0x32, 0xfb, 0xbe, 0xd2, 0x3c, 0x4c, 0xba, 0xbf, 0x74, + 0x1d, 0x68, 0xaa, 0xb3, 0x0e, 0x0b, 0x8f, 0x15, 0xf4, 0x44, 0xd5, 0x86, 0xb3, + 0xe7, 0xe6, 0x15, 0x9c, 0x46, 0x69, 0x9f, 0x10, 0x07, 0x92, 0xd4, 0x67, 0x29, + 0x50, 0x34, 0x8a, 0x90, 0x55, 0x2e, 0x45, 0x94, 0x3b, 0xee, 0xac, 0xf0, 0x3f, + 0x32, 0x16, 0xf9, 0x4e, 0x27, 0x90, 0x6e, 0xdc, 0x63, 0x23, 0x19, 0xad, 0x8d, + 0x37, 0x44, 0x7f, 0x5c, 0x59, 0xcc, 0xde, 0x35, 0x4f, 0x99, 0xff, 0x6c, 0x7a, + 0x76, 0x23, 0xf6, 0xd4, 0x15, 0x25, 0xa8, 0x09, 0xce, 0x2f, 0x41, 0xec, 0x0f, + 0xf7, 0xf1, 0xaf, 0x81, 0xb2, 0x4c, 0xed, 0x0e, 0xfa, 0x62, 0x13, 0xda, 0x6c, + 0x7c, 0x60, 0xc4, 0x87, 0xf5, 0xf7, 0xb0, 0x3f, 0x81, 0x60, 0xa0, 0x57, 0xf4, + 0x6d, 0x05, 0xbf, 0x82, 0x18, 0xb3, 0xad, 0xd9, 0xc0, 0x68, 0x93, 0xbd, 0x02, + 0xdb, 0x9b, 0x61, 0x19, 0x1d, 0xfb, 0x13, 0x3b, 0xfa, 0xbe, 0x48, 0x58, 0xe4, + 0x7a, 0x4c, 0xc3, 0x2e, 0x41, 0x6e, 0xc0, 0x8b, 0x8a, 0xc7, 0x91, 0x5a, 0x43, + 0x73, 0x3f, 0x44, 0x06, 0xe9, 0xd9, 0x67, 0xc5, 0x60, 0xf3, 0x44, 0xd7, 0xe9, + 0x04, 0xa2, 0x80, 0x45, 0xd9, 0x9f, 0x3a, 0xf8, 0xc8, 0x2e, 0x97, 0xe1, 0xb9, + 0xc1, 0xb2, 0x05, 0xe5, 0x85, 0xfb, 0xeb, 0xb4, 0x8f, 0xaf, 0x58, 0xf1, 0xb6, + 0x5d, 0xca, 0x24, 0x97, 0xe0, 0x9a, 0x70, 0xaa, 0xd4, 0x86, 0x5f, 0x85, 0x71, + 0x5a, 0x28, 0x0e, 0x18, 0x6f, 0x3f, 0xc1, 0x74, 0x0d, 0x81, 0x84, 0xd3, 0x3e, + 0x83, 0x22, 0x16, 0x95, 0x21, 0xcd, 0xc1, 0x32, 0x21, 0x29, 0x39, 0xc8, 0x4a, + 0x10, 0x89, 0x64, 0xe2, 0xde, 0x74, 0xb6, 0xea, 0x55, 0xb4, 0xcb, 0x8f, 0x6f, + 0x9b, 0xee, 0x98, 0xb1, 0x0d, 0x41, 0x51, 0x09, 0x45, 0x5f, 0x48, 0xb7, 0x76, + 0x08, 0x2d, 0xc3, 0x0b, 0x4b, 0xc7, 0x34, 0x77, 0x07, 0x55, 0x11, 0x70, 0x03, + 0x08, 0x15, 0x8c, 0xe2, 0xf2, 0xf9, 0xbf, 0x0f, 0x69, 0x1b, 0x2c, 0xe5, 0x3e, + 0x61, 0x14, 0x2c, 0xb7, 0x40, 0xc1, 0x5b, 0x7b, 0x62, 0x3c, 0xf4, 0x8b, 0x3f, + 0x7b, 0xfe, 0xfa, 0x31, 0xbc, 0xdc, 0x66, 0x5c, 0x6d, 0x71, 0x23, 0xe9, 0x53, + 0x50, 0x81, 0x13, 0x75, 0x94, 0x7b, 0x05, 0x5a, 0x43, 0xdb, 0x07, 0xe0, 0x3f, + 0x33, 0x62, 0x7d, 0xf5, 0xc6, 0x38, 0xbf, 0xad, 0x95, 0x6d, 0xdc, 0x1e, 0xa7, + 0xd7, 0x62, 0x0a, 0x20, 0xf2, 0x79, 0x2f, 0x63, 0x81, 0x7a, 0x1c, 0xf3, 0x25, + 0x80, 0xd0, 0x42, 0x74, 0x23, 0x4a, 0xf2, 0xa5, 0x1b, 0x56, 0xbb, 0x68, 0xa2, + 0x9e, 0x43, 0xa9, 0x54, 0x14, 0x2b, 0xa4, 0xca, 0x68, 0x23, 0xbd, 0xe9, 0x05, + 0x3d, 0x72, 0xfd, 0xad, 0xbc, 0x61, 0xad, 0x59, 0x36, 0xc5, 0x3f, 0xdd, 0x75, + 0x79, 0x44, 0x6d, 0x11, 0xc4, 0x46, 0x07, 0xf4, 0x16, 0x30, 0xe4, 0xc0, 0x89, + 0x15, 0xe6, 0x31, 0x77, 0x15, 0x50, 0xe9, 0xce, 0x1f, 0xca, 0x2c, 0x63, 0xfe, + 0x06, 0xb7, 0x98, 0x9d, 0x58, 0x4f, 0xa7, 0xd7, 0x82, 0xa8, 0x8c, 0x1e, 0x7d, + 0x64, 0xb6, 0xfb, 0xf5, 0x5e, 0x35, 0x96, 0xaf, 0x9b, 0xcb, 0x75, 0x85, 0xf8, + 0xc7, 0xd3, 0xaa, 0x5c, 0x20, 0x82, 0xb2, 0x65, 0x24, 0x9d, 0xf0, 0x57, 0x01, + 0xda, 0xb0, 0x31, 0xc4, 0xba, 0xc1, 0xea, 0x26, 0x7a, 0x29, 0x96, 0xa2, 0x02, + 0x8d, 0x1e, 0x6a, 0x0f, 0x80, 0xa3, 0x84, 0x7c, 0x53, 0x1d, 0xba, 0x96, 0xee, + 0x65, 0xa2, 0x41, 0x89, 0xbd, 0x27, 0x12, 0xe4, 0x0e, 0x95, 0x96, 0x64, 0x98, + 0x1e, 0x58, 0xb2, 0xa4, 0xf9, 0x51, 0xef, 0x8f, 0x49, 0x7d, 0xff, 0xf2, 0xf2, + 0xf2, 0x71, 0xea, 0xb8, 0x9c, 0x62, 0x8e, 0x18, 0xb5, 0xfc, 0xb4, 0x38, 0x82, + 0x53, 0x7e, 0xaf, 0x6a, 0xd2, 0xa6, 0xb1, 0x75, 0x46, 0x33, 0xca, 0xa8, 0x6b, + 0xf2, 0xc7, 0x6f, 0x39, 0x93, 0x15, 0x4f, 0xc7, 0x3e, 0x6f, 0xbb, 0xa2, 0x21, + 0x0c, 0x27, 0x43, 0xf5, 0x30, 0xa4, 0x27, 0x84, 0x9a, 0x30, 0x1e, 0x00, 0xe0, + 0x11, 0x29, 0xf0, 0x3a, 0x46, 0x07, 0xf8, 0x7c, 0xbe, 0x07, 0x62, 0xc0, 0xb1, + 0xc6, 0x58, 0x55, 0xde, 0xba, 0x84, 0x22, 0xca, 0x4b, 0x88, 0xab, 0xee, 0xa6, + 0xa4, 0x38, 0x2c, 0xf1, 0x6c, 0xcd, 0x6d, 0xc7, 0xc3, 0x7c, 0x44, 0xe5, 0x49, + 0xc4, 0x53, 0x48, 0x19, 0xac, 0xd8, 0xbb, 0x0a, 0x02, 0xa5, 0xfa, 0x7a, 0x1c, + 0x1d, 0x38, 0x06, 0xfb, 0xc3, 0x40, 0x7f, 0xd7, 0xda, 0x93, 0xfd, 0x0d, 0xe6, + 0x40, 0x0d, 0x3a, 0xb8, 0x97, 0x74, 0x85, 0xcd, 0xdf, 0xbe, 0xd5, 0x93, 0x2f, + 0x50, 0x7b, 0x79, 0x94, 0x7a, 0xdb, 0x2f, 0xad, 0x37, 0x61, 0x5a, 0xa7, 0x17, + 0xdb, 0x5f, 0x29, 0x80, 0x99, 0xf2, 0x0f, 0x26, 0x3b, 0x35, 0x9a, 0x11, 0x51, + 0xa6, 0xb7, 0x5c, 0x01, 0x36, 0x5e, 0xb1, 0x54, 0xae, 0x42, 0x14, 0x0d, 0x6e, + 0x10, 0x34, 0x2f, 0x14, 0xf3, 0x4d, 0xc3, 0x3e, 0x07, 0xff, 0x0e, 0x4d, 0x1a, + 0x6b, 0xe3, 0x75, 0xb3, 0x2f, 0x84, 0xb9, 0x2e, 0x5d, 0x81, 0xeb, 0xb6, 0x39, + 0xc4, 0xf2, 0x7e, 0x71, 0x5a, 0xa4, 0x2c, 0xc7, 0x57, 0x07, 0xd4, 0xeb, 0xd1, + 0xbb, 0xfb, 0xe8, 0xf9, 0x0f, 0xc7, 0xc9, 0x53, 0xe7, 0xa9, 0x71, 0x5e, 0x65, + 0xaf, 0x82, 0x67, 0x37, 0x3d, 0x34, 0x51, 0x67, 0x4f, 0xf0, 0x84, 0xef, 0xd9, + 0x2c, 0xcf, 0x3b, 0xcc, 0x7a, 0xca, 0x14, 0x67, 0xb6, 0x32, 0x7e, 0x4f, 0x95, + 0x22, 0xb2, 0xcc, 0x57, 0x9a, 0x7a, 0x8f, 0xff, 0x7c, 0xa7, 0xcf, 0x14, 0x5d, + 0xfc, 0x13, 0xea, 0xfc, 0x34, 0x15, 0x3b, 0x2c, 0x3e, 0x8a, 0xfb, 0xe5, 0x34, + 0x44, 0xd0, 0xc7, 0x3b, 0x3b, 0xd5, 0xbc, 0x87, 0x0b, 0x01, 0xcd, 0x45, 0x79, + 0x11, 0xe3, 0x56, 0x31, 0x3f, 0xd1, 0xda, 0xfb, 0x4c, 0x81, 0x51, 0x63, 0x4a, + 0x01, 0xaf, 0xf7, 0xcf, 0x11, 0x6d, 0x43, 0x3c, 0x3d, 0x2b, 0x3a, 0xdd, 0xa9, + 0xce, 0xbe, 0x18, 0xf7, 0xd1, 0x72, 0x44, 0x3e, 0x5e, 0x7b, 0x5a, 0xc9, 0xab, + 0xe8, 0xdb, 0x22, 0x56, 0xd7, 0xeb, 0xe2, 0xff, 0x28, 0x02, 0x09, 0x39, 0x50, + 0x38, 0x70, 0x59, 0x7b, 0x9a, 0x95, 0x58, 0x92, 0xc7, 0x38, 0x96, 0x50, 0xa2, + 0xd4, 0x2e, 0xc9, 0x2b, 0xe7, 0x23, 0xfe, 0xdf, 0x2f, 0x2e, 0xde, 0x5a, 0x47, + 0x2a, 0xa1, 0xe7, 0x4f, 0x33, 0xad, 0x41, 0x90, 0x15, 0x44, 0xed, 0xbb, 0xe3, + 0xac, 0x46, 0x4c, 0xf4, 0x39, 0x19, 0x60, 0x15, 0xf4, 0xf2, 0x2a, 0xc2, 0xb8, + 0xfc, 0x01, 0x49, 0x6b, 0xea, 0xb4, 0xd4, 0x59, 0x07, 0xf4, 0x79, 0x81, 0x2a, + 0x25, 0x94, 0x31, 0xa2, 0xcb, 0xc9, 0x3d, 0x4f, 0x3b, 0x84, 0xe4, 0xdd, 0x36, + 0x60, 0x20, 0x27, 0x3a, 0x67, 0x52, 0xe5, 0x01, 0xaf, 0x6f, 0xf1, 0xb7, 0x8d, + 0xdc, 0x81, 0x7e, 0x6e, 0xa3, 0xe5, 0x37, 0x5c, 0xa3, 0xfe, 0x2f, 0xb1, 0xd1, + 0xa9, 0x37, 0x15, 0xdf, 0xf9, 0x29, 0x93, 0xcc, 0xc6, 0x50, 0x4f, 0x64, 0xfc, + 0xbf, 0x22, 0x17, 0x30, 0xd9, 0xef, 0xf4, 0xf3, 0x27, 0xe0, 0xad, 0x37, 0x4f, + 0x59, 0x56, 0x45, 0x37, 0x48, 0x75, 0x5b, 0x43, 0xc8, 0xf2, 0x9d, 0x67, 0x52, + 0x6f, 0x60, 0xa6, 0x18, 0xb7, 0x33, 0x24, 0xd2, 0x24, 0x33, 0x72, 0x92, 0x1b, + 0x99, 0xe3, 0xdf, 0x36, 0x85, 0x0c, 0x60, 0xd5, 0xd9, 0x34, 0x3a, 0x48, 0x70, + 0xa0, 0xe7, 0x52, 0x8c, 0x65, 0x13, 0xc2, 0x7c, 0x56, 0x43, 0x90, 0x93, 0xf9, + 0x33, 0x68, 0x1d, 0x93, 0xa4, 0xd4, 0xbc, 0xee, 0xac, 0x2b, 0x10, 0x8c, 0x6c, + 0x6f, 0xae, 0x35, 0x9f, 0x64, 0x5c, 0x27, 0x68, 0x91, 0xc0, 0xdc, 0xab, 0x3f, + 0xaf, 0x18, 0x77, 0x00, 0xc0, 0x82, 0xdc, 0x47, 0x77, 0x40, 0xfb, 0x3f, 0x2c, + 0xd7, 0xbb, 0x59, 0xfb, 0x35, 0x85, 0x54, 0xe9, 0x4c, 0x7e, 0x67, 0x8c, 0xe0, + 0x1a, 0xeb, 0xf9, 0x4e, 0x51, 0x5e, 0x49, 0x72, 0x29, 0x67, 0x99, 0x5a, 0xea, + 0x85, 0x8d, 0x64, 0xe7, 0x78, 0x9f, 0xf3, 0x06, 0x36, 0x95, 0x77, 0x22, 0x81, + 0x80, 0x32, 0x6a, 0x5b, 0x0a, 0xf4, 0x75, 0xe2, 0x7a, 0x54, 0xb2, 0x07, 0xb4, + 0x1f, 0x92, 0xe3, 0x76, 0x17, 0x0e, 0x3f, 0xb0, 0x05, 0x02, 0x82, 0x61, 0xc9, + 0x9c, 0x2d, 0xbd, 0x0e, 0xed, 0xee, 0x87, 0x1c, 0x1c, 0x0f, 0x48, 0xb8, 0xe9, + 0xb8, 0xe4, 0xbe, 0x77, 0xd1, 0xb7, 0x37, 0xfe, 0x21, 0xf0, 0xfa, 0x5a, 0x18, + 0xeb, 0xb5, 0x27, 0x55, 0xb5, 0xa6, 0xcf, 0x61, 0x30, 0xfb, 0x56, 0x94, 0x4c, + 0xfa, 0xb8, 0x75, 0x27, 0xc2, 0x50, 0xd1, 0x13, 0xb2, 0x9b, 0xca, 0xc9, 0xaa, + 0xa1, 0x0c, 0x2e, 0x7d, 0xe4, 0x15, 0xed, 0xb0, 0x80, 0x6c, 0x6d, 0xa0, 0x30, + 0x20, 0xa1, 0x34, 0xca, 0x7e, 0xcd, 0xc8, 0xda, 0x1b, 0xd5, 0x7a, 0x37, 0xf5, + 0x5a, 0x46, 0x94, 0x0b, 0x45, 0xb2, 0x41, 0xb1, 0xc1, 0x6e, 0xe1, 0x00, 0x92, + 0x7d, 0x1b, 0xd8, 0x60, 0xd4, 0x45, 0xa9, 0xde, 0x50, 0xd4, 0xc3, 0x84, 0xd6, + 0xe1, 0xd0, 0x01, 0x08, 0x02, 0x6c, 0x0e, 0xa5, 0xeb, 0xbf, 0x0b, 0x72, 0xfb, + 0xf5, 0xc3, 0x70, 0xbc, 0xe1, 0x8d, 0x3a, 0xcb, 0xc4, 0x65, 0x99, 0x09, 0x9b, + 0xaa, 0xe1, 0xd8, 0x02, 0xf7, 0x73, 0x33, 0x49, 0x4a, 0x7a, 0xe1, 0x30, 0xfe, + 0x86, 0xe8, 0xf8, 0x18, 0xf9, 0x26, 0x1a, 0x2d, 0xad, 0xb4, 0x12, 0x52, 0x29, + 0xba, 0x0f, 0xfc, 0x0e, 0x70, 0x90, 0x32, 0x44, 0x30, 0xb5, 0x21, 0xa9, 0x0d, + 0x22, 0x4a, 0xb7, 0xa1, 0x02, 0x4e, 0x1d, 0x89, 0x3e, 0x74, 0x04, 0xfe, 0xdb, + 0x34, 0x8e, 0x4d, 0x5e, 0x22, 0x35, 0xc5, 0x9a, 0x78, 0x76, 0xa0, 0xfc, 0x60, + 0x14, 0x5c, 0x6a, 0x00, 0x96, 0x87, 0x68, 0x44, 0x60, 0x27, 0x1e, 0xe1, 0x33, + 0xa4, 0x37, 0xfe, 0x52, 0xfb, 0x6c, 0xfb, 0xa9, 0x7f, 0xce, 0xc1, 0x61, 0xdf, + 0x51, 0x5d, 0xde, 0x90, 0x5a, 0x24, 0xda, 0x6d, 0x37, 0xbd, 0xc3, 0x40, 0x44, + 0xa9, 0x55, 0xe6, 0x82, 0xb4, 0x74, 0x71, 0xca, 0x1e, 0x8c, 0x78, 0xc5, 0x1e, + 0xd3, 0x77, 0xcd, 0x4a, 0xfa, 0x89, 0x4b, 0xd9, 0xbd, 0x12, 0xe7, 0x07, 0x15, + 0x6d, 0xa0, 0x72, 0x6f, 0x7c, 0xf5, 0x72, 0x9f, 0xab, 0xe3, 0x72, 0x16, 0x04, + 0x63, 0xfe, 0x04, 0x29, 0x24, 0x4d, 0x06, 0x74, 0x89, 0xba, 0x5d, 0x09, 0x47, + 0x2e, 0xcd, 0x9b, 0xcd, 0xc4, 0xd5, 0xe4, 0xdf, 0x10, 0x1e, 0x18, 0x9d, 0xb8, + 0x46, 0x3e, 0xb5, 0x38, 0x30, 0x7b, 0x58, 0x7d, 0xef, 0xf7, 0x8d, 0xe9, 0xc7, + 0x3a, 0xf2, 0x80, 0x80, 0xb2, 0xfd, 0x05, 0x00, 0x3e, 0x11, 0xd3, 0xe1, 0xb3, + 0x29, 0x9d, 0xc9, 0x52, 0x1f, 0x8b, 0x51, 0x3b, 0xad, 0xb0, 0x10, 0xe9, 0x1b, + 0xfe, 0xb9, 0x1b, 0x0b, 0x2a, 0x6c, 0xb1, 0x29, 0xc2, 0xe8, 0x25, 0xa5, 0x97, + 0xb8, 0xfb, 0x75, 0xbc, 0x56, 0x2d, 0x65, 0x4d, 0x62, 0x10, 0x46, 0x40, 0xdd, + 0x74, 0xe5, 0x6c, 0xd1, 0x4b, 0xaa, 0xba, 0x56, 0x5b, 0x84, 0xb8, 0x45, 0xe1, + 0x63, 0xd1, 0xca, 0xef, 0x25, 0x33, 0xc3, 0x98, 0x16, 0x37, 0x20, 0x4f, 0x96, + 0xa5, 0x9c, 0x8e, 0x80, 0x24, 0xd9, 0x04, 0x1b, 0x20, 0x29, 0xe9, 0x4c, 0x15, + 0x24, 0x5f, 0x1a, 0x95, 0x88, 0x40, 0xba, 0x3f, 0x38, 0x0a, 0x4d, 0x20, 0xf1, + 0x18, 0x4e, 0x77, 0x82, 0x7d, 0xe3, 0xff, 0x8f, 0x3d, 0x73, 0x45, 0x9a, 0xfe, + 0x24, 0x1f, 0x72, 0x3c, 0x08, 0x48, 0x23, 0x23, 0x0e, 0x00, 0x3d, 0x3d, 0x21, + 0xe5, 0x35, 0x01, 0xec, 0x04, 0x99, 0xb0, 0x83, 0xa7, 0xda, 0xd6, 0x85, 0xc5, + 0x71, 0x27, 0xf4, 0xde, 0x64, 0x73, 0x3a, 0x88, 0x0c, 0x2d, 0xb2, 0x8f, 0xda, + 0xab, 0xf1, 0xb5, 0x42, 0xd2, 0x05, 0xf6, 0x64, 0xa3, 0x51, 0x35, 0x71, 0x27, + 0x11, 0xdc, 0xcc, 0xd9, 0x31, 0xa5, 0x0b, 0x9c, 0x56, 0x61, 0x88, 0x23, 0x60, + 0xd4, 0xca, 0xc0, 0x04, 0x76, 0x81, 0xbc, 0x2e, 0x2b, 0x3b, 0xf6, 0xc9, 0x97, + 0x60, 0xd7, 0xcf, 0xb4, 0xfa, 0x21, 0x39, 0x43, 0x77, 0xa4, 0x55, 0x1c, 0x76, + 0xd1, 0xf7, 0x5a, 0xc0, 0x3c, 0x26, 0x20, 0x54, 0xdf, 0xfd, 0x79, 0xa9, 0xde, + 0xd0, 0x5e, 0x88, 0x89, 0x58, 0x19, 0x9e, 0xea, 0x45, 0x01, 0xe2, 0x99, 0x0a, + 0x53, 0xa5, 0xcd, 0x2a, 0x46, 0xa4, 0x01, 0x57, 0x65, 0x88, 0xfd, 0x7d, 0x05, + 0x8a, 0x26, 0xf2, 0x84, 0x38, 0xe5, 0x78, 0x2f, 0x45, 0xac, 0x1d, 0x07, 0xf6, + 0xf6, 0xf5, 0xed, 0x73, 0x74, 0x1d, 0x57, 0x85, 0x83, 0x7a, 0x6b, 0x84, 0x4b, + 0x47, 0x47, 0x75, 0x71, 0x8c, 0x29, 0xdd, 0x99, 0x08, 0x4e, 0x9f, 0x88, 0xef, + 0x15, 0x3a, 0x83, 0x29, 0xf5, 0x32, 0xa6, 0x90, 0x17, 0xdc, 0x3a, 0x97, 0xed, + 0x75, 0x43, 0x67, 0x72, 0x30, 0x98, 0xe5, 0x76, 0x58, 0x40, 0xb0, 0x22, 0x89, + 0x72, 0x44, 0x74, 0x5f, 0xbb, 0xbb, 0x30, 0xa7, 0xcb, 0x54, 0xfa, 0x05, 0x11, + 0x16, 0x6e, 0x95, 0x44, 0x12, 0x20, 0x00, 0x61, 0x0b, 0xd2, 0xaa, 0xcb, 0xd8, + 0x23, 0x25, 0xa5, 0x9b, 0x95, 0x15, 0x4e, 0xcd, 0x82, 0xc8, 0x8d, 0x23, 0xab, + 0xd1, 0xe2, 0x07, 0x70, 0xff, 0xb8, 0xaa, 0xbf, 0x83, 0xfc, 0x07, 0x34, 0x96, + 0x4c, 0xcd, 0x41, 0x1d, 0x1c, 0x93, 0x57, 0x14, 0xe2, 0x4a, 0xab, 0x56, 0x6f, + 0x4f, 0x08, 0x42, 0x40, 0x14, 0xc4, 0xec, 0xa9, 0x1b, 0x59, 0x0f, 0x08, 0x2b, + 0x47, 0x3f, 0x36, 0x1c, 0x87, 0x41, 0x5d, 0x37, 0xbd, 0x20, 0xd7, 0x0f, 0xd0, + 0xb5, 0x2b, 0x6d, 0xdf, 0x18, 0x65, 0xf7, 0x66, 0x70, 0x2e, 0x32, 0xb0, 0x5b, + 0x3c, 0xf1, 0x63, 0x0e, 0xe8, 0x59, 0x7a, 0xae, 0x19, 0x63, 0x3f, 0x35, 0x16, + 0xa8, 0x55, 0x5a, 0xc5, 0xbe, 0x32, 0xc6, 0x75, 0xbe, 0x18, 0x17, 0xef, 0xbf, + 0xfd, 0x93, 0x69, 0x04, 0xbd, 0x73, 0x4d, 0x97, 0x23, 0x3a, 0xaa, 0x38, 0x3b, + 0x41, 0xba, 0x6d, 0x27, 0xf6, 0x42, 0x84, 0x7d, 0x1e, 0xda, 0xcb, 0x4b, 0xf8, + 0x22, 0xe6, 0x70, 0x80, 0x86, 0x75, 0x18, 0xae, 0x5a, 0x8f, 0x0a, 0x43, 0xe6, + 0xed, 0x53, 0x0c, 0xb2, 0xe8, 0xae, 0x83, 0x88, 0x60, 0xad, 0xc8, 0x8a, 0xac, + 0xc7, 0xbd, 0x6a, 0x00, 0xae, 0x0c, 0x19, 0xff, 0x45, 0x33, 0xa4, 0x85, 0xef, + 0xde, 0x08, 0x2b, 0xc5, 0x21, 0x40, 0x18, 0x2b, 0x23, 0x4d, 0x1a, 0x0d, 0x0e, + 0xeb, 0xdf, 0xb9, 0x87, 0x75, 0x98, 0xe0, 0x34, 0x7f, 0xb1, 0x00, 0x1e, 0x15, + 0xb5, 0xd4, 0x44, 0x6e, 0x76, 0x6c, 0xde, 0x25, 0xef, 0x79, 0x87, 0x40, 0xe0, + 0xbd, 0xf9, 0x94, 0xd9, 0x73, 0x9b, 0xbe, 0x55, 0x38, 0xa0, 0xae, 0x0f, 0x07, + 0x6c, 0x58, 0x2c, 0x0f, 0x5b, 0xa8, 0x78, 0xb9, 0x9b, 0x82, 0x49, 0xdb, 0x1d, + 0x7e, 0x95, 0x05, 0x6c, 0x98, 0xaf, 0x08, 0x3d, 0x98, 0xcb, 0x0e, 0xd9, 0xe3, + 0xf7, 0x43, 0x6e, 0x1c, 0x76, 0x43, 0x76, 0x6f, 0x96, 0x6b, 0x83, 0xe9, 0x99, + 0x20, 0x6e, 0xbd, 0x13, 0x93, 0xb9, 0xb2, 0xa7, 0xf4, 0x14, 0x48, 0x0f, 0xa0, + 0x17, 0x48, 0x00, 0x69, 0xf8, 0x5c, 0x77, 0x49, 0xc4, 0x35, 0xae, 0x2f, 0xba, + 0x2d, 0xdc, 0x10, 0x38, 0xd5, 0x47, 0xd8, 0x48, 0x54, 0x81, 0x7e, 0xf3, 0x96, + 0x35, 0xc2, 0x98, 0x27, 0xaa, 0xd8, 0x67, 0x26, 0xc9, 0xad, 0xe3, 0xb2, 0x65, + 0xb9, 0x08, 0x6c, 0x8b, 0x5b, 0x75, 0xef, 0x56, 0xfe, 0x4b, 0xd8, 0xb4, 0xd6, + 0x28, 0x93, 0x89, 0x5b, 0x3f, 0xd2, 0x73, 0x4f, 0xda, 0xc4, 0x64, 0x15, 0x6d, + 0x7e, 0x5e, 0xbc, 0x7e, 0xcf, 0x1d, 0x83, 0xb8, 0x6f, 0x65, 0x96, 0x37, 0xe3, + 0xb1, 0x42, 0xc1, 0x64, 0x96, 0x3b, 0x8c, 0xdc, 0xf4, 0xba, 0x4f, 0x40, 0x35, + 0xdf, 0xfc, 0x5a, 0x78, 0x94, 0x58, 0x84, 0x77, 0x81, 0x91, 0x8a, 0xc7, 0x2f, + 0xc1, 0x8b, 0xbb, 0xf5, 0x11, 0x00, 0x32, 0xe6, 0x6d, 0x75, 0xb3, 0x17, 0x1e, + 0xf4, 0xb5, 0x13, 0x29, 0x01, 0x64, 0xa7, 0x7b, 0x42, 0xb0, 0xa4, 0xcf, 0xb8, + 0x96, 0x39, 0xab, 0x23, 0x84, 0x5e, 0x1a, 0xa2, 0xa4, 0x52, 0xf3, 0x73, 0x1c, + 0x8c, 0xb6, 0x50, 0x82, 0xa6, 0x22, 0xa7, 0xc2, 0xe0, 0x01, 0x3e, 0xa4, 0x7d, + 0x0b, 0xdd, 0x42, 0xd6, 0x99, 0x04, 0x66, 0x64, 0x9a, 0x90, 0x5c, 0x68, 0x4c, + 0x32, 0x51, 0x71, 0x6d, 0x61, 0xf7, 0x60, 0xd5, 0x3d, 0xe6, 0xe3, 0xf7, 0x90, + 0xfb, 0xa7, 0xf5, 0xf1, 0xf4, 0xde, 0x26, 0x71, 0x13, 0xbd, 0xfc, 0xd7, 0x42, + 0x28, 0x22, 0x33, 0x0b, 0x32, 0xd5, 0x8e, 0x67, 0x77, 0x76, 0x5f, 0x22, 0xa4, + 0x11, 0x63, 0x44, 0xee, 0xb6, 0x5b, 0x2e, 0xc5, 0x16, 0x39, 0x3a, 0xb3, 0x75, + 0x1b, 0x53, 0x56, 0xd2, 0xb0, 0xc9, 0x50, 0x0c, 0x0f, 0x3e, 0x46, 0x91, 0x81, + 0x03, 0x5b, 0xc3, 0x66, 0x0f, 0x0b, 0x8f, 0x9f, 0xbe, 0x6e, 0x40, 0xb5, 0xe8, + 0x9c, 0xb7, 0x9b, 0x06, 0x37, 0x14, 0xca, 0x75, 0xe7, 0x2e, 0x2e, 0x10, 0x0a, + 0x10, 0xd6, 0x3b, 0xf7, 0x84, 0xdf, 0x08, 0x20, 0xef, 0x25, 0xf8, 0xef, 0x40, + 0xfe, 0x5f, 0x05, 0xfb, 0x95, 0x68, 0x3f, 0x91, 0x05, 0xff, 0x3c, 0xb2, 0xd2, + 0x19, 0xab, 0x76, 0x60, 0x5a, 0x06, 0x4f, 0x69, 0x21, 0x9f, 0x1d, 0xc0, 0xd0, + 0x0b, 0x3b, 0x48, 0x64, 0x2f, 0x97, 0x0d, 0xc0, 0x0c, 0xca, 0x4b, 0x8b, 0x43, + 0x30, 0x8b, 0xe1, 0x82, 0x86, 0xec, 0x5a, 0x42, 0x88, 0xd6, 0x00, 0xa3, 0x78, + 0x5c, 0xb6, 0x22, 0xd4, 0x68, 0xa4, 0xc6, 0x96, 0x9b, 0x37, 0x92, 0xf2, 0x48, + 0x50, 0x27, 0xd0, 0xad, 0x9a, 0xa4, 0xa9, 0xc2, 0xcc, 0x97, 0x2f, 0x9e, 0xe5, + 0x19, 0x0a, 0x95, 0xb1, 0xeb, 0x05, 0x8d, 0xdd, 0xd8, 0xc0, 0x8e, 0x7d, 0x75, + 0x3f, 0x5e, 0x01, 0x1b, 0x2b, 0xcf, 0xee, 0x1d, 0x52, 0xc1, 0xc4, 0xf2, 0xca, + 0xcd, 0xa3, 0x0b, 0xdb, 0x69, 0x30, 0x65, 0x3c, 0x0c, 0xc4, 0x48, 0x6e, 0x60, + 0xe8, 0x9f, 0xa8, 0x49, 0xb3, 0x20, 0x83, 0xba, 0x9d, 0xb4, 0x53, 0xfb, 0x8d, + 0xf6, 0x83, 0xcd, 0x68, 0x75, 0x4c, 0x87, 0xda, 0xa7, 0x31, 0xf5, 0x70, 0xa7, + 0xa4, 0x06, 0x0a, 0xf0, 0xce, 0x70, 0x0d, 0x31, 0xbc, 0xa7, 0xe7, 0x4b, 0x3e, + 0x3b, 0xa3, 0xd0, 0xe8, 0xa6, 0x39, 0x2a, 0x06, 0x2b, 0x8e, 0x86, 0xd9, 0xd7, + 0xd0, 0x0b, 0x21, 0x70, 0x1e, 0x7b, 0x06, 0x2e, 0x06, 0xb1, 0xbc, 0xd8, 0x2a, + 0x01, 0xd3, 0x75, 0x62, 0x6f, 0xbf, 0x87, 0x2d, 0x27, 0xfa, 0x45, 0x11, 0xf5, + 0xf8, 0xcf, 0x8c, 0x9a, 0xbc, 0xef, 0x2a, 0x99, 0x01, 0x76, 0xae, 0x33, 0x93, + 0x25, 0xd5, 0xa5, 0x88, 0xda, 0x57, 0x96, 0xfa, 0xae, 0x5b, 0xab, 0x7c, 0x82, + 0x97, 0x7c, 0x0f, 0xf7, 0x97, 0x09, 0x3e, 0x2c, 0x1f, 0x3a, 0xe8, 0x55, 0xf6, + 0x5a, 0xea, 0x91, 0xe1, 0x31, 0x2f, 0xc6, 0xb8, 0xa4, 0x35, 0x1a, 0x2e, 0xc0, + 0x3e, 0x02, 0xe5, 0xd0, 0x2f, 0x53, 0x35, 0x4b, 0x05, 0x2f, 0xd3, 0xda, 0x0d, + 0xff, 0x82, 0xcd, 0x1f, 0x55, 0xeb, 0xca, 0x57, 0xb6, 0x33, 0x7c, 0x85, 0x93, + 0x8a, 0x79, 0x81, 0x3d, 0x20, 0x21, 0xd6, 0x09, 0x4c, 0x68, 0xb3, 0x75, 0xe9, + 0x84, 0xf6, 0x83, 0x93, 0x30, 0x08, 0x71, 0xe3, 0x48, 0xfc, 0x52, 0x36, 0xcc, + 0xa6, 0x33, 0x05, 0x44, 0xe5, 0x46, 0x39, 0xb5, 0x41, 0x87, 0x01, 0xff, 0x4c, + 0xc4, 0x5a, 0x31, 0xf6, 0x2e, 0xdd, 0x84, 0x3d, 0xbb, 0xdc, 0x5a, 0xa7, 0x27, + 0xab, 0x79, 0xb4, 0x42, 0x68, 0x3c, 0x49, 0x56, 0xbb, 0xb1, 0x95, 0xa4, 0xfa, + 0x66, 0xdc, 0x9c, 0xd5, 0x42, 0xc7, 0x6b, 0x91, 0x50, 0xc8, 0x4b, 0xf8, 0x90, + 0x78, 0x99, 0x42, 0xf5, 0x5c, 0x20, 0x0b, 0x77, 0x3e, 0xcd, 0xd7, 0x99, 0x2c, + 0xff, 0x3e, 0xca, 0x24, 0xde, 0x3e, 0x09, 0x84, 0xe1, 0x0e, 0x68, 0xae, 0x38, + 0x75, 0x34, 0xb9, 0x6c, 0xde, 0x37, 0x92, 0xf1, 0x35, 0xbf, 0x5f, 0x68, 0x78, + 0x7d, 0x37, 0x0c, 0xa8, 0xc4, 0xc4, 0x07, 0x4d, 0xc5, 0xd6, 0x01, 0xae, 0x90, + 0x49, 0x54, 0x37, 0xc3, 0xc2, 0xd4, 0x8a, 0x3d, 0x96, 0x66, 0x83, 0xac, 0x05, + 0x16, 0x0b, 0x7a, 0x84, 0xea, 0xa7, 0xaa, 0xb7, 0x40, 0x09, 0xe5, 0x7a, 0x85, + 0xf7, 0xbf, 0x68, 0xa2, 0xe4, 0x82, 0x00, 0x0f, 0x82, 0x9c, 0x54, 0x50, 0x73, + 0xa1, 0x5d, 0x5c, 0xd0, 0xfc, 0xc5, 0x74, 0x39, 0xa4, 0x35, 0x0e, 0xaf, 0x09, + 0x8d, 0xfb, 0x82, 0xa0, 0x85, 0xea, 0x8a, 0x4a, 0xf6, 0xfa, 0x83, 0x81, 0xf0, + 0x65, 0x88, 0x19, 0xea, 0xb4, 0x83, 0xf6, 0x5b, 0x32, 0x5d, 0x5a, 0xed, 0xa1, + 0x52, 0x32, 0xcf, 0xad, 0xec, 0x75, 0xab, 0x18, 0x66, 0xe4, 0xc0, 0x15, 0x5a, + 0x9c, 0x74, 0xa7, 0xa5, 0x7c, 0xcf, 0x34, 0xc4, 0x83, 0xac, 0x7d, 0xa1, 0x58, + 0x8a, 0x1b, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xf1, 0x10, 0x40, 0xf9, 0x4c, + 0xf7, 0x8f, 0xad, 0x89, 0xbf, 0x11, 0xfe, 0xd6, 0x9a, 0xa0, 0xd8, 0x31, 0x05, + 0xad, 0xac, 0xdd, 0x4e, 0x5f, 0x04, 0xa6, 0x24, 0x24, 0x02, 0x3c, 0x9b, 0x9e, + 0x33, 0xc4, 0xfb, 0x7f, 0x12, 0xbd, 0xf2, 0x1f, 0x07, 0xf2, 0x65, 0xc5, 0x37, + 0xd5, 0x1c, 0x65, 0x51, 0xf4, 0x61, 0x7b, 0x91, 0x5d, 0x21, 0x99, 0x18, 0x39, + 0xc3, 0xd0, 0xd3, 0x63, 0x93, 0xd6, 0x46, 0xe0, 0xa8, 0xa4, 0x15, 0x09, 0x21, + 0x7d, 0x0e, 0x7d, 0x2c, 0xa1, 0xa0, 0xa0, 0xd6, 0x77, 0xa3, 0xea, 0xca, 0x23, + 0xed, 0xeb, 0x07, 0xb7, 0x4e, 0x65, 0x2a, 0x0b, 0xc5, 0x0c, 0x6c, 0x08, 0x3a, + 0x55, 0xd6, 0xc7, 0x30, 0x6e, 0x74, 0x08, 0x6f, 0x47, 0x68, 0x93, 0x3a, 0xa2, + 0x48, 0x73, 0x68, 0x18, 0x67, 0xa7, 0x89, 0x3d, 0x77, 0xcb, 0x7f, 0x29, 0xb8, + 0xc8, 0x47, 0xc5, 0x83, 0xf2, 0xd0, 0x71, 0xa6, 0x86, 0x61, 0x6e, 0x20, 0x67, + 0x19, 0xf7, 0x61, 0xae, 0x39, 0xc1, 0x10, 0x44, 0x2e, 0x06, 0x16, 0x3d, 0x2b, + 0x84, 0x59, 0x03, 0x60, 0x69, 0x5d, 0x4e, 0x19, 0x84, 0x9e, 0x63, 0x4f, 0x24, + 0xd9, 0xad, 0x39, 0x6c, 0x19, 0xff, 0x83, 0xce, 0x74, 0xf4, 0x6e, 0x64, 0x5f, + 0x93, 0x2e, 0x14, 0x1a, 0x41, 0x19, 0x59, 0x36, 0xc8, 0x5d, 0x51, 0x44, 0x14, + 0xf1, 0x12, 0xe6, 0x0b, 0x1a, 0x25, 0x37, 0xc3, 0x8d, 0x6d, 0xc6, 0xc4, 0x63, + 0x83, 0x05, 0xc9, 0xbd, 0x6c, 0x62, 0xe3, 0x66, 0xbc, 0x63, 0x12, 0x3e, 0x3e, + 0x6d, 0xd3, 0x6e, 0xed, 0xd3, 0x13, 0x6f, 0xce, 0x8d, 0xee, 0xca, 0x2a, 0xa0, + 0x9a, 0x32, 0x98, 0xa3, 0x9d, 0x83, 0x85, 0x9e, 0xfc, 0x9b, 0x2b, 0x69, 0xcf, + 0x9a, 0x7d, 0xee, 0x08, 0xa9, 0x8e, 0x4b, 0xe5, 0x58, 0xac, 0x79, 0x12, 0xfd, + 0xcb, 0x42, 0x20, 0x90, 0x75, 0x42, 0x02, 0x60, 0xf7, 0xca, 0xd0, 0xf2, 0xc0, + 0x1f, 0x2a, 0xfe, 0x33, 0x07, 0x3f, 0x26, 0x24, 0x9d, 0x94, 0x4f, 0x7a, 0x50, + 0xdd, 0x84, 0x83, 0x9b, 0xc3, 0xea, 0x7f, 0xde, 0xe4, 0xed, 0x71, 0x44, 0x9c, + 0xf0, 0x75, 0x33, 0xd2, 0x6e, 0x1e, 0x27, 0xa3, 0xef, 0xb0, 0x32, 0xc3, 0xa3, + 0xb3, 0x4b, 0xd3, 0x09, 0x26, 0x22, 0xd2, 0x06, 0x2a, 0xe5, 0x36, 0xef, 0x51, + 0x49, 0xc4, 0x9b, 0x5b, 0xc9, 0x47, 0x5e, 0xaf, 0xab, 0x6e, 0x67, 0x57, 0x61, + 0x00, 0x8b, 0x0d, 0xad, 0xde, 0xec, 0xaa, 0x60, 0x44, 0x70, 0xbb, 0xe0, 0xfa, + 0xda, 0x25, 0x5d, 0x29, 0x0e, 0x92, 0xb1, 0x90, 0xc2, 0xc2, 0xd8, 0xc2, 0xde, + 0xe5, 0x45, 0x5d, 0x1f, 0xa9, 0xa9, 0xf3, 0xdb, 0x77, 0x79, 0xb5, 0x84, 0x64, + 0x34, 0x64, 0xaa, 0x80, 0x14, 0xba, 0x66, 0x99, 0x4d, 0xe2, 0x55, 0x17, 0xf8, + 0x39, 0x80, 0xe6, 0x6e, 0xe4, 0xf6, 0x23, 0x14, 0xae, 0x6d, 0xbe, 0xf4, 0x52, + 0xd5, 0xd3, 0x8b, 0x0a, 0x16, 0xf3, 0x99, 0x1f, 0x36, 0xd8, 0xa8, 0xb3, 0x9d, + 0xdc, 0x0d, 0x55, 0x95, 0xee, 0xd9, 0x87, 0x62, 0x87, 0x8c, 0xdf, 0x3f, 0x4a, + 0x2e, 0xdc, 0x5c, 0xda, 0x77, 0xd5, 0xfe, 0x4f, 0xaf, 0x63, 0xa1, 0x5f, 0x56, + 0x8a, 0x54, 0x0d, 0xa5, 0x7d, 0xd9, 0xbe, 0xb6, 0xfb, 0x1a, 0x97, 0x7c, 0xcb, + 0x91, 0xb4, 0xd7, 0x9c, 0xb3, 0x9b, 0x28, 0x91, 0x1a, 0x29, 0xe7, 0xbf, 0x02, + 0x8a, 0xc6, 0x10, 0x37, 0x96, 0xdf, 0xb6, 0xb2, 0x09, 0x67, 0x23, 0x9a, 0xd3, + 0x73, 0xc3, 0x8c, 0x53, 0xf6, 0xdf, 0x18, 0x23, 0xd4, 0x95, 0x0a, 0x02, 0x83, + 0xe9, 0x9b, 0x9c, 0x06, 0xab, 0x29, 0x66, 0x66, 0x7c, 0x9d, 0xf6, 0x77, 0x71, + 0x6b, 0x0c, 0xad, 0xed, 0x81, 0x8d, 0xf9, 0xe4, 0x49, 0xc0, 0x72, 0xe2, 0x2f, + 0x9d, 0x98, 0xbb, 0x0f, 0x9b, 0x03, 0xbd, 0x5f, 0xd0, 0x13, 0xfc, 0xef, 0x3e, + 0xd6, 0xa4, 0x9a, 0xeb, 0x98, 0x72, 0x02, 0x54, 0x08, 0x7e, 0xf7, 0x28, 0xe3, + 0x19, 0x47, 0xff, 0xe8, 0xf7, 0x66, 0xe6, 0x3e, 0xe4, 0x6f, 0xf2, 0x08, 0x16, + 0xd5, 0xfa, 0x8f, 0xf5, 0x5a, 0x26, 0x39, 0x89, 0x61, 0x49, 0x0a, 0xb9, 0xae, + 0x36, 0x6f, 0xc5, 0xa2, 0xd1, 0x99, 0x6e, 0xd6, 0x93, 0xcc, 0xca, 0x82, 0x35, + 0x6f, 0x60, 0x0a, 0xb0, 0x99, 0xf6, 0xec, 0xa8, 0xbf, 0xe6, 0x45, 0x27, 0x0d, + 0x3f, 0x95, 0xed, 0xba, 0x5b, 0x0d, 0xe7, 0xa3, 0x28, 0x19, 0x23, 0x3b, 0xcc, + 0x75, 0x4a, 0x5c, 0xe2, 0xe5, 0xea, 0x07, 0x84, 0x2e, 0x5f, 0xf2, 0xce, 0xbe, + 0x62, 0xad, 0x76, 0xe8, 0xef, 0xf8, 0xd1, 0x5e, 0xa4, 0xc2, 0x4a, 0x5f, 0x20, + 0x78, 0x68, 0x31, 0x9a, 0x5a, 0xf6, 0xb0, 0x35, 0xbe, 0x3f, 0x44, 0xf4, 0x34, + 0x09, 0x4f, 0x6e, 0x52, 0x5b, 0xe6, 0x14, 0xda, 0xc9, 0x20, 0xa3, 0x30, 0xbd, + 0xfb, 0x26, 0xd7, 0x5f, 0xe7, 0xb4, 0xb3, 0x65, 0xd0, 0x94, 0x45, 0x92, 0x50, + 0xaa, 0xa5, 0x54, 0x44, 0x89, 0xfb, 0x1d, 0x99, 0x25, 0x81, 0x80, 0x0a, 0x77, + 0xb8, 0x91, 0x21, 0x57, 0xfc, 0x97, 0x13, 0xaa, 0xac, 0x25, 0xb4, 0xc2, 0x6e, + 0xb0, 0x3f, 0x71, 0x66, 0x46, 0x61, 0x9a, 0xf0, 0x24, 0x56, 0xae, 0x69, 0x59, + 0x62, 0xfe, 0x5e, 0x93, 0x1a, 0x63, 0xb5, 0xc7, 0x90, 0x52, 0xec, 0xd3, 0x33, + 0xe1, 0x84, 0x12, 0xdb, 0x91, 0xe1, 0x5f, 0x7c, 0xbc, 0x70, 0xb4, 0xcd, 0x7e, + 0x8e, 0x3c, 0x95, 0x1f, 0x35, 0x85, 0x72, 0xe3, 0x77, 0x67, 0xe7, 0xd5, 0x27, + 0x04, 0xa6, 0x72, 0x1b, 0x30, 0xef, 0xc4, 0x10, 0x17, 0xae, 0x4d, 0x23, 0x15, + 0x58, 0xc5, 0xc8, 0x2c, 0xc7, 0xdd, 0x7e, 0x33, 0x56, 0xc0, 0x9d, 0xc2, 0x49, + 0x06, 0xf0, 0x43, 0x8d, 0xfc, 0xc3, 0x00, 0x85, 0x6a, 0xc2, 0xce, 0xd8, 0xf7, + 0x7f, 0xa8, 0x01, 0x57, 0x36, 0xc6, 0x61, 0xe8, 0x02, 0x48, 0xae, 0xeb, 0x77, + 0x48, 0x74, 0xaa, 0x79, 0xd2, 0x90, 0xb8, 0xf5, 0x02, 0x7a, 0x0a, 0x50, 0x95, + 0x37, 0xfc, 0x7c, 0x68, 0x9b, 0x7a, 0xd8, 0x61, 0x16, 0xcf, 0xec, 0x26, 0x47, + 0xcc, 0xaa, 0xe1, 0xc7, 0x4b, 0x41, 0x6f, 0x3e, 0x6a, 0xe8, 0xf7, 0xcc, 0x60, + 0xea, 0xaf, 0x7b, 0x6a, 0x59, 0x0d, 0x51, 0x54, 0x41, 0x38, 0xe1, 0x73, 0x29, + 0x45, 0x60, 0x3a, 0x53, 0x46, 0x2c, 0x60, 0xe1, 0xf6, 0xcb, 0x0c, 0x9c, 0xa0, + 0x39, 0x0c, 0x48, 0x82, 0x24, 0xc3, 0x13, 0x26, 0x9f, 0xcd, 0x59, 0xfc, 0xb6, + 0x11, 0xfb, 0x2d, 0x9b, 0x4c, 0x8f, 0xa6, 0x01, 0xbb, 0x1c, 0xb8, 0xd0, 0x7d, + 0x79, 0x7b, 0xf5, 0xde, 0x52, 0xbc, 0xee, 0xb0, 0x23, 0x01, 0xc8, 0x96, 0x2a, + 0xc1, 0xfc, 0x04, 0x91, 0xdc, 0x81, 0xaf, 0xfd, 0x6c, 0x1e, 0xbf, 0x89, 0xa1, + 0x3d, 0x6f, 0x29, 0x0e, 0xda, 0x5d, 0x5c, 0xef, 0x38, 0x22, 0x15, 0xc5, 0xe9, + 0x51, 0xd7, 0x13, 0x05, 0xef, 0x33, 0xd9, 0x73, 0x71, 0x26, 0xd0, 0xe6, 0x62, + 0x90, 0x5f, 0x12, 0x50, 0x92, 0x6f, 0x6a, 0x22, 0x99, 0x90, 0xe3, 0x8f, 0x69, + 0xad, 0x9a, 0x91, 0x92, 0xb3, 0x02, 0xf2, 0x6b, 0xdd, 0xa4, 0x65, 0xd9, 0x0b, + 0x94, 0xb1, 0x2c, 0x57, 0xfa, 0x3f, 0xd6, 0x93, 0x00, 0x83, 0xf1, 0x84, 0x43, + 0x8d, 0x8a, 0x88, 0x9d, 0x3f, 0x5e, 0xce, 0xa2, 0xc6, 0xd2, 0x3d, 0x67, 0x36, + 0xf2, 0xa0, 0xf1, 0x8e, 0x26, 0xf4, 0xfa, 0x45, 0xd1, 0xbe, 0x8f, 0x3d, 0xc4, + 0xa7, 0x07, 0x13, 0x7e, 0x95, 0xd2, 0xad, 0x59, 0x4f, 0x6c, 0x03, 0xd2, 0x49, + 0x23, 0x06, 0x7a, 0xe4, 0x7f, 0xd6, 0x42, 0x5e, 0xfb, 0x9c, 0x1d, 0x50, 0x4e, + 0x6f, 0xd5, 0x57, 0x53, 0x40, 0x94, 0x56, 0x01, 0xfe, 0x80, 0x6f, 0x57, 0x56, + 0xac, 0xb5, 0x62, 0xf1, 0x3c, 0x0c, 0xa1, 0xd8, 0x03, 0xa1, 0x95, 0xc2, 0xeb, + 0xb2, 0xef, 0x02, 0xac, 0x33, 0xe6, 0xa8, 0x8d, 0xea, 0x07, 0x5b, 0xa9, 0x96, + 0xd3, 0xc3, 0x36, 0x64, 0x8e, 0x86, 0x94, 0xd3, 0xa1, 0x9d, 0x3d, 0xca, 0x53, + 0x1b, 0xeb, 0x50, 0xd4, 0x32, 0x7c, 0x5c, 0x0c, 0x23, 0xcb, 0x7c, 0xfd, 0xb0, + 0x8c, 0xa7, 0xcf, 0x2c, 0xac, 0x6b, 0xc1, 0x39, 0xd0, 0x74, 0x14, 0x73, 0xd3, + 0x76, 0x02, 0x9c, 0xb4, 0xab, 0x6b, 0xf0, 0x54, 0x55, 0x7c, 0xe2, 0x94, 0xc7, + 0x28, 0xa4, 0x68, 0x7d, 0x57, 0xec, 0x89, 0x09, 0xff, 0x51, 0xa4, 0xd0, 0x2f, + 0x9d, 0xcd, 0x11, 0x19, 0x3d, 0x7d, 0x1c, 0x9f, 0xda, 0xe6, 0xa1, 0x73, 0x96, + 0xa1, 0xbf, 0x57, 0xa9, 0x94, 0x93, 0x4f, 0x5e, 0x7a, 0x59, 0xf0, 0x45, 0xde, + 0xbe, 0xaf, 0xf6, 0x2e, 0xf3, 0x26, 0xb9, 0x47, 0xf2, 0xa8, 0xb4, 0x95, 0x55, + 0xe4, 0xd9, 0x9b, 0x3b, 0xf5, 0xc8, 0x1f, 0xf9, 0xfe, 0x31, 0x4e, 0x04, 0x7a, + 0xf1, 0x52, 0x50, 0x8f, 0x57, 0x01, 0x5c, 0xa4, 0x02, 0xc6, 0x7d, 0x92, 0x5c, + 0x99, 0xac, 0xea, 0x3e, 0xe8, 0xcc, 0x4b, 0x00, 0x8c, 0x5c, 0xb4, 0x39, 0x66, + 0xe7, 0x14, 0xef, 0x48, 0x0f, 0xd0, 0x5e, 0x07, 0xc7, 0xb2, 0xdd, 0xa9, 0xaa, + 0x39, 0x66, 0x11, 0x3e, 0xaa, 0x29, 0x3d, 0x3f, 0x62, 0x2b, 0x30, 0x9d, 0x64, + 0x80, 0x3c, 0xe1, 0xe6, 0x37, 0x8b, 0x6a, 0xac, 0x4f, 0xab, 0x52, 0x7c, 0x43, + 0xcd, 0x45, 0xed, 0x0a, 0x3c, 0x1a, 0x4b, 0x9f, 0xb1, 0x8d, 0xcc, 0xcf, 0xcd, + 0xb6, 0xac, 0x0c, 0x24, 0x21, 0x63, 0x9c, 0xda, 0x00, 0x75, 0xa2, 0x0d, 0xc5, + 0x11, 0x1b, 0x8d, 0x3d, 0x31, 0x99, 0x49, 0x5b, 0xd9, 0x13, 0x3d, 0xba, 0xb9, + 0x45, 0x41, 0x41, 0x0e, 0x4f, 0xba, 0x92, 0xc7, 0xb6, 0x06, 0xa5, 0xcb, 0x12, + 0x2f, 0x14, 0x0c, 0xf1, 0xa3, 0x59, 0x6f, 0x27, 0x88, 0xf3, 0xc8, 0xb9, 0x26, + 0x60, 0xf1, 0x4c, 0xb6, 0x5a, 0xf5, 0xdd, 0x23, 0xdf, 0xdb, 0xac, 0x13, 0x71, + 0xec, 0xf4, 0xb3, 0x37, 0x12, 0xfe, 0xd2, 0x29, 0x2c, 0x44, 0xf7, 0x08, 0x34, + 0xcf, 0x96, 0xc0, 0x5d, 0x58, 0x82, 0x7e, 0x69, 0xbf, 0xc2, 0xe6, 0x96, 0xfa, + 0x08, 0x74, 0x86, 0x9c, 0x02, 0xf3, 0xdc, 0xa1, 0x1c, 0x3b, 0x90, 0xcb, 0x21, + 0x4e, 0x68, 0xbc, 0x1c, 0xae, 0x03, 0x9d, 0x7a, 0x14, 0x6c, 0xdc, 0x1d, 0x60, + 0x9d, 0x7a, 0x6b, 0x3f, 0xd5, 0xd4, 0x61, 0xb0, 0x95, 0x1c, 0x82, 0xcf, 0xb3, + 0xe7, 0x63, 0xfa, 0xd2, 0xd1, 0xbc, 0x76, 0x78, 0xcd, 0xf8, 0x27, 0x79, 0xf8, + 0xfd, 0x5a, 0x1c, 0xe2, 0x2a, 0x8d, 0x3c, 0x45, 0x47, 0xab, 0xd9, 0x59, 0x83, + 0x8a, 0x46, 0xfb, 0x80, 0xaf, 0xe0, 0x1f, 0x8e, 0xcc, 0x99, 0x31, 0x51, 0x3b, + 0x19, 0x62, 0xec, 0x54, 0x08, 0x56, 0xcb, 0x18, 0x93, 0x87, 0xcf, 0xbf, 0xcc, + 0x0f, 0x7c, 0x68, 0x22, 0x3c, 0xba, 0x47, 0xfb, 0x0c, 0x9b, 0x48, 0x6e, 0x4d, + 0x99, 0x17, 0x19, 0x41, 0xf7, 0x67, 0x5a, 0x8b, 0x46, 0x32, 0x8a, 0x3b, 0xc1, + 0x09, 0xbf, 0x07, 0xc6, 0x6d, 0x5e, 0xde, 0x77, 0x1c, 0xc4, 0xc7, 0x4c, 0xe8, + 0x03, 0x33, 0x82, 0x91, 0x91, 0xee, 0xdc, 0x49, 0x35, 0x08, 0xa6, 0x44, 0x53, + 0x0a, 0x61, 0x44, 0xf2, 0x2d, 0xcf, 0x97, 0x52, 0x5a, 0x4c, 0xdc, 0xa1, 0xad, + 0x71, 0x07, 0x3b, 0x08, 0x0b, 0x73, 0xea, 0x45, 0x49, 0xf5, 0x40, 0x1b, 0xff, + 0x43, 0x18, 0x26, 0x8e, 0x6a, 0xd6, 0x37, 0x36, 0x31, 0x57, 0xa1, 0x9a, 0x53, + 0xf1, 0x23, 0xa0, 0xb0, 0xe1, 0x6d, 0x0b, 0x77, 0xf0, 0x20, 0x28, 0xda, 0x46, + 0x41, 0x00, 0xfd, 0xe7, 0x6d, 0x83, 0xdd, 0x0b, 0xb2, 0x24, 0xf7, 0xb5, 0x7a, + 0x00, 0xc0, 0x2f, 0x68, 0xae, 0x64, 0x8f, 0xdc, 0x52, 0x99, 0x57, 0xa1, 0x04, + 0x90, 0xdc, 0xe1, 0xfd, 0xdb, 0xb0, 0x90, 0x4f, 0x0d, 0x51, 0x8b, 0xb3, 0x87, + 0x54, 0x40, 0x19, 0x98, 0x3b, 0x61, 0x69, 0x75, 0xa7, 0x8e, 0x74, 0xd8, 0x54, + 0xfd, 0xdc, 0x49, 0xb2, 0x55, 0x16, 0x7b, 0x55, 0xef, 0x4b, 0xee, 0x46, 0x56, + 0x68, 0xb2, 0x0e, 0xa4, 0x11, 0x8c, 0xa5, 0x69, 0xae, 0x48, 0x0e, 0x0f, 0x6e, + 0x5e, 0x04, 0x3a, 0x35, 0x7b, 0x36, 0xd3, 0xab, 0x36, 0xc8, 0x61, 0xf2, 0x27, + 0x83, 0x01, 0xdc, 0xe5, 0x76, 0x74, 0xd5, 0x07, 0x3b, 0x3a, 0x6f, 0x51, 0x03, + 0xa0, 0x79, 0x3a, 0xf1, 0xb7, 0xd4, 0x6f, 0x95, 0x7e, 0x22, 0xd8, 0xd2, 0x58, + 0x3b, 0xf1, 0x81, 0x83, 0x6c, 0x3b, 0xe9, 0x93, 0x0b, 0xac, 0x8f, 0xa4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0xe9, 0x68, 0xaa, 0x71, 0x09, 0x87, 0x0b, 0xbe, 0xd1, 0x7d, + 0xf5, 0xf8, 0x88, 0xc8, 0xca, 0x14, 0x67, 0xae, 0x17, 0xdb, 0xbc, 0xde, 0x31, + 0xc1, 0x10, 0x5c, 0xb5, 0xbd, 0xa8, 0x8a, 0xc6, 0xc6, 0x27, 0x00, 0x2c, 0xe2, + 0x1c, 0x02, 0x14, 0x0f, 0xfe, 0x81, 0xec, 0x58, 0xbf, 0x1e, 0x6d, 0x1b, 0xb7, + 0xaa, 0xad, 0xa4, 0x1f, 0xba, 0x0b, 0xb5, 0x88, 0x77, 0x8a, 0x7f, 0x65, 0x20, + 0x2a, 0xd8, 0x11, 0xea, 0x73, 0xd2, 0x6c, 0x74, 0x55, 0x03, 0x95, 0xaf, 0xf7, + 0x53, 0x25, 0x10, 0x7c, 0x9b, 0x3f, 0x9a, 0xe9, 0xdc, 0xdc, 0xd8, 0x6e, 0xd0, + 0x81, 0xa2, 0xe7, 0x42, 0x47, 0x19, 0xa3, 0xd1, 0x85, 0xb7, 0xe0, 0xa4, 0x3a, + 0x47, 0x2e, 0x29, 0x8a, 0xc0, 0xaf, 0xdc, 0x52, 0x87, 0xd7, 0xad, 0x12, 0x4c, + 0xd9, 0x40, 0x5a, 0x62, 0xcd, 0x1c, 0xa0, 0x8b, 0x28, 0x2e, 0xfe, 0xf7, 0xf9, + 0x28, 0xdf, 0x76, 0xe2, 0x82, 0x1a, 0x41, 0x84, 0x13, 0xeb, 0x7c, 0xea, 0xa5, + 0xff, 0x12, 0x90, 0xb0, 0x3e, 0xc9, 0x1c, 0xe6, 0xdd, 0x28, 0x13, 0x0c, 0x3a, + 0xb0, 0xb2, 0x3b, 0x60, 0x2b, 0xd5, 0xbe, 0x5d, 0xc2, 0x60, 0x03, 0xaa, 0xe0, + 0x4b, 0x33, 0xd7, 0xbd, 0x25, 0x90, 0xe9, 0x0c, 0x8c, 0x38, 0x8e, 0xa7, 0x95, + 0x51, 0x22, 0xdb, 0xac, 0xa6, 0x7b, 0x30, 0x39, 0x5a, 0x92, 0x8b, 0x57, 0xb8, + 0x57, 0x51, 0x23, 0x20, 0x5a, 0xe1, 0x91, 0x52, 0xe4, 0x1e, 0x00, 0x29, 0x31, + 0xb4, 0x57, 0x46, 0x19, 0x8e, 0x5d, 0xd9, 0x57, 0x1a, 0x56, 0xa7, 0xe0, 0xd4, + 0x23, 0xff, 0x27, 0x98, 0x9d, 0x3e, 0xb4, 0x17, 0xec, 0xd3, 0xc3, 0x09, 0x3f, + 0xb8, 0x2c, 0x56, 0x58, 0xe2, 0x96, 0x24, 0xc5, 0x32, 0x19, 0xa6, 0x0c, 0xd0, + 0xa8, 0xc4, 0xda, 0x36, 0x7e, 0x29, 0xa7, 0x17, 0x79, 0xa7, 0x30, 0x32, 0x98, + 0x5a, 0x3d, 0x1f, 0xd0, 0x3d, 0xd4, 0xd0, 0x6e, 0x05, 0x56, 0x6f, 0x3b, 0x84, + 0x36, 0x7c, 0xf0, 0xfa, 0xee, 0x9b, 0xc3, 0xbd, 0x7a, 0x3a, 0x60, 0x6a, 0x9f, + 0xdb, 0x84, 0x9c, 0x5d, 0x82, 0xd0, 0xa6, 0x19, 0x23, 0xc2, 0xe5, 0xd8, 0xaa, + 0x63, 0xa8, 0xa5, 0x0c, 0x38, 0xbd, 0x03, 0x87, 0x72, 0xc4, 0x14, 0x3d, 0x8b, + 0x7a, 0xcf, 0xd7, 0x4e, 0x72, 0xc0, 0x4d, 0x89, 0x24, 0x8d, 0xff, 0x20, 0xfe, + 0x8d, 0xc5, 0xec, 0x21, 0x49, 0x05, 0x4e, 0xa2, 0x41, 0x64, 0xe8, 0x5f, 0x67, + 0x44, 0xad, 0x0c, 0xac, 0xf1, 0xa8, 0xb7, 0x01, 0x26, 0xf4, 0x82, 0xc0, 0x92, + 0xed, 0x9f, 0x61, 0x27, 0xd2, 0x05, 0x0d, 0x12, 0xe8, 0x78, 0xa7, 0x96, 0x53, + 0xa1, 0xe8, 0x4d, 0xae, 0xc3, 0xeb, 0xe6, 0x2d, 0x5f, 0x6c, 0x4a, 0xbe, 0x5c, + 0xe9, 0x0a, 0x7f, 0xe2, 0xe5, 0x2a, 0x8d, 0x78, 0x46, 0xe8, 0xed, 0xf2, 0xf2, + 0xbc, 0xe0, 0x5a, 0x03, 0x7c, 0x82, 0x6f, 0x22, 0xca, 0xad, 0x12, 0x61, 0x46, + 0x7d, 0xcf, 0xb7, 0xd6, 0xb6, 0x13, 0x3d, 0xc2, 0x1e, 0x80, 0x96, 0xc7, 0xe9, + 0xf8, 0xe9, 0xe1, 0x0c, 0x1e, 0x3f, 0xac, 0x40, 0x58, 0xb6, 0x82, 0xc6, 0x8e, + 0x54, 0xfa, 0xca, 0xe0, 0xf9, 0xc2, 0xdd, 0x4d, 0x64, 0xd9, 0x04, 0x61, 0x52, + 0xb4, 0x76, 0x23, 0x32, 0x93, 0x9f, 0x17, 0xe6, 0xaa, 0xf7, 0xd8, 0xb9, 0xd3, + 0x58, 0xe2, 0x21, 0x8d, 0x4e, 0x0d, 0x69, 0xa4, 0xf1, 0x19, 0xe1, 0xc6, 0x4e, + 0xec, 0x4c, 0x8b, 0x53, 0x28, 0x09, 0x70, 0x71, 0x31, 0xf0, 0x1f, 0x55, 0xc7, + 0xad, 0x04, 0xcf, 0xb6, 0x3f, 0x7c, 0x4a, 0x3d, 0x0a, 0x2b, 0x0f, 0xfb, 0x0b, + 0x05, 0xa6, 0xbe, 0x05, 0x5b, 0x8c, 0x94, 0xca, 0x80, 0xbb, 0x0a, 0x1d, 0x13, + 0xcd, 0x4c, 0xd6, 0x9a, 0xb9, 0x83, 0x04, 0xae, 0x25, 0x15, 0xd5, 0xf7, 0x69, + 0x9d, 0x4a, 0xbe, 0xe5, 0xc2, 0x0b, 0xe6, 0x09, 0xd8, 0x73, 0x51, 0x10, 0x12, + 0xf2, 0x34, 0xbd, 0x85, 0xa7, 0xef, 0xf5, 0xfb, 0x63, 0x4c, 0xff, 0x26, 0x58, + 0xba, 0x65, 0x16, 0x04, 0x85, 0x63, 0x09, 0x5e, 0xce, 0xfb, 0x30, 0x15, 0xee, + 0x3f, 0x03, 0xca, 0x52, 0xa1, 0x77, 0xf2, 0x61, 0xec, 0xdc, 0x26, 0xbc, 0x08, + 0x9d, 0x34, 0xc6, 0x40, 0x48, 0x46, 0xe9, 0xc6, 0x47, 0xfc, 0xfe, 0x98, 0xcc, + 0x6a, 0xcd, 0xbb, 0x46, 0x4f, 0x64, 0x27, 0x8a, 0xd8, 0xce, 0x9d, 0x1a, 0xe0, + 0xd4, 0x15, 0xbc, 0x0c, 0x05, 0x24, 0x5f, 0xdd, 0xaf, 0x4e, 0xbc, 0x8d, 0xc7, + 0x03, 0xa8, 0x5c, 0xb2, 0x70, 0xf7, 0x96, 0xad, 0x2d, 0x93, 0x7e, 0x2a, 0xc0, + 0xd5, 0xe0, 0xa3, 0x48, 0x21, 0x75, 0x80, 0x00, 0xaa, 0x59, 0xc9, 0xd4, 0x65, + 0x24, 0x85, 0x29, 0x4e, 0xe0, 0xab, 0x29, 0x69, 0x6b, 0x21, 0x43, 0x0f, 0xa5, + 0x4d, 0xcf, 0xbf, 0x2b, 0x9c, 0x49, 0xd1, 0x42, 0x06, 0x42, 0x09, 0xee, 0xee, + 0xd4, 0xd4, 0x71, 0xff, 0xc0, 0x17, 0xd4, 0xe2, 0x0a, 0x79, 0x6b, 0x09, 0x27, + 0x80, 0x4c, 0x06, 0x1b, 0x9f, 0x4a, 0x70, 0x91, 0xfe, 0x01, 0x5a, 0xda, 0x68, + 0xfd, 0x84, 0x42, 0xe0, 0x18, 0x25, 0xc8, 0x8d, 0xfe, 0x55, 0xcf, 0x5d, 0xe3, + 0x89, 0x36, 0xf7, 0xce, 0x25, 0x31, 0x1b, 0x90, 0x2b, 0xa9, 0x7a, 0x3c, 0x12, + 0xa9, 0x5c, 0xfa, 0x1c, 0x3a, 0x59, 0x1b, 0x81, 0x8f, 0x60, 0x83, 0x27, 0x09, + 0xd9, 0xe4, 0x83, 0x9e, 0x41, 0x0f, 0xb3, 0x6b, 0x84, 0xf3, 0xac, 0x4f, 0x07, + 0x0f, 0xc3, 0x5e, 0x16, 0x19, 0x78, 0x25, 0x9e, 0x5b, 0x8e, 0xdc, 0x74, 0x4d, + 0x90, 0x91, 0x9a, 0xa7, 0x70, 0xbb, 0x36, 0x21, 0x51, 0x28, 0xe5, 0x82, 0xb5, + 0x96, 0x41, 0xe2, 0x38, 0x52, 0xe9, 0x58, 0xeb, 0x8f, 0xc3, 0xc0, 0xaa, 0x96, + 0x15, 0x2b, 0xa4, 0xf7, 0x7f, 0x13, 0x8d, 0x6a, 0x67, 0x12, 0xa3, 0xae, 0x32, + 0x26, 0x01, 0x58, 0x83, 0xf8, 0x1d, 0xb2, 0x3e, 0x58, 0x3c, 0x86, 0x9c, 0x4c, + 0x71, 0x14, 0x3a, 0x6f, 0xff, 0xd6, 0x5e, 0x8d, 0xfd, 0xc5, 0x0c, 0x99, 0xa2, + 0xf1, 0xf3, 0x14, 0xcd, 0xcc, 0x71, 0x35, 0x9e, 0x23, 0x5f, 0x1d, 0x7d, 0xc2, + 0xb5, 0xf3, 0x8e, 0xf7, 0xb9, 0x70, 0x84, 0x31, 0x63, 0xc0, 0x3f, 0x9d, 0xd4, + 0x0a, 0x80, 0x15, 0xef, 0xdc, 0x87, 0x91, 0x95, 0x6a, 0x3f, 0x3c, 0xed, 0xd9, + 0xea, 0x64, 0xf8, 0xef, 0xa7, 0xa0, 0x81, 0x5a, 0x70, 0x38, 0x1d, 0x71, 0x46, + 0x78, 0x17, 0xbd, 0x04, 0xca, 0x52, 0x9a, 0xed, 0xe0, 0x7f, 0xf6, 0x0d, 0x17, + 0x6a, 0xed, 0x0f, 0x85, 0x5a, 0x2e, 0xae, 0xa8, 0x9e, 0xae, 0xac, 0xa8, 0x93, + 0x58, 0xc0, 0x81, 0x82, 0x6a, 0x08, 0x12, 0xa5, 0xbc, 0xa2, 0x8b, 0xe1, 0x37, + 0x3f, 0x08, 0x6d, 0xbd, 0xba, 0x7e, 0x43, 0xe2, 0x03, 0x21, 0x2c, 0x9f, 0xed, + 0x21, 0x47, 0x4b, 0xa1, 0x9a, 0x05, 0x5f, 0xfc, 0xc1, 0x79, 0x41, 0x2e, 0x89, + 0x3a, 0x74, 0x48, 0x32, 0x29, 0x8c, 0x5f, 0xe2, 0x4c, 0xc6, 0xb1, 0x86, 0x67, + 0xf4, 0x9b, 0x34, 0xdf, 0xb1, 0x23, 0x79, 0x26, 0x74, 0x19, 0xa9, 0xcb, 0x94, + 0x03, 0xd8, 0x16, 0x7d, 0x8d, 0x1e, 0x91, 0xd2, 0x81, 0x1a, 0x04, 0x3b, 0x29, + 0x24, 0x3b, 0x06, 0x9b, 0x37, 0x58, 0x78, 0x47, 0xdc, 0x6f, 0xcd, 0xdb, 0x18, + 0x31, 0xbd, 0x1c, 0xc2, 0x56, 0x7c, 0xa0, 0x33, 0xac, 0x40, 0xf7, 0x4a, 0xb6, + 0x95, 0x5f, 0x68, 0x3b, 0x12, 0xe4, 0xe8, 0x25, 0x4e, 0x4e, 0xa7, 0x60, 0xd3, + 0x8b, 0x3f, 0x46, 0x79, 0x1c, 0x5c, 0x4c, 0xb1, 0x2b, 0xc7, 0xcc, 0xb0, 0xed, + 0x18, 0x65, 0xf2, 0x5d, 0x60, 0x1c, 0x30, 0x3f, 0x81, 0xfb, 0x1f, 0xa1, 0xdb, + 0x48, 0x53, 0x3d, 0x3d, 0x6b, 0x28, 0x8e, 0x4d, 0x9a, 0x4d, 0xff, 0x8e, 0xc2, + 0x1c, 0x96, 0xf5, 0x78, 0x39, 0x97, 0x10, 0xc8, 0x25, 0xfe, 0x7e, 0x32, 0xf9, + 0x3a, 0x8c, 0x07, 0x43, 0xf9, 0xeb, 0xd5, 0x4c, 0xc1, 0x51, 0xc7, 0x61, 0x03, + 0x37, 0xae, 0xbf, 0x7e, 0x9b, 0x91, 0x57, 0x20, 0xa5, 0x43, 0x51, 0xd4, 0x9a, + 0xb8, 0xc2, 0x2f, 0xa3, 0x49, 0x98, 0xdc, 0xf5, 0x83, 0xd4, 0x38, 0x73, 0x61, + 0xef, 0x3f, 0xf8, 0x6f, 0x50, 0xec, 0x53, 0xf4, 0x92, 0x49, 0xe4, 0xad, 0x34, + 0x96, 0x03, 0x06, 0x6f, 0xc9, 0xc6, 0x61, 0xd6, 0x9f, 0x91, 0x1d, 0xfa, 0x72, + 0x41, 0xc8, 0xd5, 0x79, 0x2d, 0x43, 0xc4, 0x57, 0xd5, 0xde, 0x96, 0x52, 0x3a, + 0x53, 0xd6, 0x67, 0xec, 0x5c, 0x4e, 0xf9, 0xd5, 0x02, 0xa1, 0x6f, 0x15, 0x22, + 0x47, 0x58, 0x96, 0xd7, 0x9b, 0xc5, 0x78, 0x33, 0xe9, 0x77, 0x17, 0x1c, 0x32, + 0x4d, 0xce, 0x2a, 0x1e, 0xa1, 0xe4, 0x30, 0x4f, 0x49, 0xe4, 0x3a, 0xe0, 0x65, + 0xe3, 0xfb, 0x19, 0x6f, 0x76, 0xd9, 0xb8, 0x79, 0xc7, 0x20, 0x08, 0x62, 0xea, + 0xd1, 0x8d, 0xea, 0x5f, 0xb6, 0xa1, 0x7a, 0xce, 0xa3, 0x33, 0x86, 0xeb, 0x4c, + 0xa1, 0xb5, 0x14, 0x86, 0xa9, 0x14, 0x8f, 0xbd, 0xf9, 0xa9, 0x53, 0x32, 0xaa, + 0x60, 0x5c, 0x5d, 0x54, 0x83, 0xce, 0x4b, 0xa8, 0xec, 0xe0, 0x1a, 0x8f, 0xf2, + 0xb7, 0xef, 0x82, 0xd0, 0x5c, 0x0b, 0x6e, 0x86, 0x1b, 0x91, 0x5f, 0x13, 0xca, + 0x0e, 0xb3, 0xea, 0x13, 0xd5, 0x07, 0x08, 0x07, 0xa2, 0xcb, 0x66, 0x80, 0xa2, + 0x49, 0xea, 0x9c, 0x72, 0x24, 0x39, 0x2c, 0xbc, 0x8a, 0xb8, 0x25, 0x01, 0xb2, + 0x6f, 0x11, 0x2a, 0xc7, 0x89, 0xa1, 0x2a, 0x31, 0xad, 0x13, 0x14, 0xe2, 0xed, + 0xe0, 0x8f, 0xad, 0x31, 0x43, 0xaf, 0x30, 0xc2, 0x7f, 0x40, 0x3b, 0xc8, 0x66, + 0xc7, 0x55, 0x17, 0x78, 0x52, 0xaf, 0xd0, 0xab, 0xb9, 0x0a, 0xde, 0x1d, 0x68, + 0x27, 0x26, 0xf4, 0x20, 0x08, 0xb4, 0x6a, 0xd7, 0xf8, 0xab, 0xdb, 0x18, 0x11, + 0x7f, 0x72, 0x64, 0x13, 0x90, 0xf0, 0x86, 0xb6, 0xe1, 0x49, 0x8b, 0xe6, 0x95, + 0x48, 0x52, 0x7e, 0x6a, 0xda, 0x2b, 0x38, 0xb9, 0xfe, 0x12, 0x1e, 0xf6, 0x70, + 0xaf, 0x74, 0x37, 0xd3, 0x25, 0x36, 0xd5, 0xcf, 0x5c, 0x4a, 0xb1, 0x9d, 0xd9, + 0x97, 0x71, 0x58, 0x2d, 0x03, 0x81, 0x04, 0xb7, 0xe0, 0x39, 0xa3, 0x76, 0xf7, + 0xac, 0xbb, 0xea, 0xdb, 0x34, 0xf9, 0x45, 0xbe, 0xb9, 0xd7, 0xca, 0x0e, 0x4e, + 0x3d, 0x5c, 0x5e, 0x4e, 0xb1, 0xd8, 0x52, 0x6e, 0xbd, 0x13, 0xda, 0xcb, 0x1b, + 0xa3, 0x57, 0x35, 0xc6, 0xd0, 0x4a, 0x45, 0x55, 0xac, 0xf4, 0xbf, 0x11, 0x76, + 0x26, 0x50, 0x0d, 0x77, 0xb3, 0x81, 0x89, 0xdd, 0x48, 0x88, 0x04, 0x12, 0x25, + 0xac, 0xbe, 0x38, 0x74, 0xa4, 0xc0, 0xf6, 0x07, 0xfe, 0x67, 0x45, 0xf9, 0x35, + 0x5b, 0x3f, 0xa1, 0x88, 0xf1, 0xd6, 0x5c, 0x09, 0xf3, 0x89, 0xaf, 0x1b, 0x9d, + 0x62, 0x32, 0xaa, 0x79, 0x44, 0x79, 0x19, 0xc5, 0x50, 0xf6, 0xf3, 0x1f, 0xec, + 0x35, 0x48, 0x1c, 0xb9, 0x22, 0xde, 0x2d, 0xb5, 0xb4, 0xda, 0x2f, 0x81, 0x94, + 0x86, 0x17, 0x02, 0x8e, 0x32, 0x17, 0x06, 0xa3, 0xa7, 0x78, 0xc1, 0x93, 0x8c, + 0x44, 0x3b, 0xb0, 0x0e, 0x5b, 0x0f, 0xf0, 0x6a, 0xd8, 0xab, 0x9b, 0x1a, 0xb0, + 0xc1, 0x14, 0x77, 0x67, 0x3f, 0x85, 0xdf, 0x95, 0x61, 0xdb, 0xea, 0x45, 0xd5, + 0xf9, 0x78, 0x1e, 0xbe, 0x31, 0x7a, 0x07, 0x10, 0xae, 0x54, 0x61, 0xe3, 0x4f, + 0xe6, 0xf1, 0xb1, 0xaa, 0x9b, 0x4e, 0x67, 0xb1, 0x49, 0x10, 0x98, 0x48, 0x02, + 0xc2, 0xa7, 0xe3, 0x81, 0x93, 0xbc, 0x7b, 0xdc, 0x8b, 0xa3, 0xe4, 0xe3, 0xd1, + 0xd9, 0x33, 0xbf, 0xb5, 0x80, 0xf5, 0xb3, 0xe8, 0x7a, 0x2a, 0x06, 0x51, 0x70, + 0x51, 0x41, 0x0f, 0xe1, 0xb4, 0xff, 0x1e, 0xa0, 0xad, 0xe8, 0x24, 0xf3, 0x38, + 0x51, 0x54, 0x56, 0xa5, 0x7c, 0x7a, 0x91, 0x6a, 0x74, 0x38, 0x8e, 0xe8, 0xf1, + 0x28, 0x1f, 0x9a, 0xde, 0x0a, 0xe2, 0xa2, 0x61, 0x3a, 0x06, 0x12, 0xc4, 0x69, + 0xdf, 0x79, 0x2b, 0x8d, 0xf4, 0xca, 0xe4, 0xfc, 0x25, 0xc1, 0xca, 0xdb, 0xa9, + 0x5a, 0x80, 0x7c, 0xe6, 0x1e, 0x5a, 0x53, 0x03, 0xfa, 0xaf, 0x9e, 0x14, 0x65, + 0x39, 0x96, 0xb5, 0xa8, 0xad, 0xc3, 0x4f, 0xd4, 0x75, 0xef, 0x14, 0x99, 0x09, + 0x4b, 0xab, 0xaf, 0x1f, 0x3f, 0x07, 0xda, 0x9a, 0x39, 0x0b, 0x1d, 0x9f, 0xc9, + 0xa0, 0x83, 0x27, 0x98, 0x7a, 0xdf, 0xe9, 0x56, 0x48, 0x63, 0xfb, 0xdf, 0xa8, + 0xf6, 0xb4, 0x6a, 0x88, 0x41, 0x58, 0x30, 0x99, 0xaf, 0xb7, 0x87, 0x01, 0x18, + 0xfa, 0xce, 0x76, 0x34, 0x7e, 0x40, 0xb6, 0xfd, 0x8c, 0xd1, 0x55, 0x82, 0xae, + 0x8e, 0x23, 0xbe, 0x9a, 0x02, 0x19, 0xbc, 0x3e, 0x4e, 0x45, 0x46, 0xa3, 0x0d, + 0x3b, 0xbb, 0xbd, 0x16, 0x86, 0x08, 0x68, 0x76, 0xbe, 0x0e, 0x4c, 0x85, 0x9b, + 0xe7, 0x1f, 0xb5, 0x8f, 0x4f, 0xab, 0x3d, 0x28, 0xc0, 0xb4, 0xf7, 0xe7, 0x5a, + 0xd1, 0xed, 0xb7, 0xf8, 0x89, 0x46, 0xfb, 0x40, 0xcf, 0xa5, 0x78, 0x6a, 0x0f, + 0xcb, 0xa1, 0x30, 0x3c, 0x83, 0x47, 0xec, 0xee, 0x93, 0xd4, 0x6d, 0x14, 0x0b, + 0xb5, 0xf6, 0x95, 0x31, 0xd6, 0x66, 0x54, 0x8b, 0x10, 0x9c, 0xe7, 0x64, 0xbe, + 0xad, 0x7c, 0x87, 0xbd, 0x4c, 0x87, 0x64, 0x94, 0xde, 0x82, 0xdb, 0x6e, 0x50, + 0x73, 0xa6, 0xc9, 0x4f, 0x7c, 0x09, 0x9a, 0x40, 0xd7, 0xa3, 0x1c, 0x4a, 0x04, + 0xb6, 0x9c, 0x9f, 0xcc, 0xf3, 0xc7, 0xdd, 0x56, 0xf5, 0x54, 0x47, 0x76, 0xc5, + 0x3b, 0x4d, 0xf7, 0x95, 0x39, 0x81, 0xd5, 0x5a, 0x96, 0xa6, 0xdc, 0xff, 0x99, + 0x04, 0xa9, 0x08, 0x42, 0xe5, 0xba, 0xfe, 0xc8, 0x84, 0x0c, 0x2d, + ], + script_code: Script(vec![0x53, 0x63, 0x63, 0x51, 0xac, 0x00, 0x51]), + transparent_input: None, + hash_type: 1, + amount: 1345602751504862, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x15, 0x53, 0xd4, 0xf1, 0x07, 0x45, 0x10, 0x71, 0x81, 0x99, 0x00, 0x5f, 0xef, + 0xaa, 0xa8, 0x3e, 0x29, 0xd1, 0x63, 0xee, 0xbd, 0xf3, 0xc0, 0x33, 0x82, 0x79, + 0x08, 0xac, 0xb4, 0x6f, 0xa2, 0x4b, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x01, 0x13, 0xf9, 0x12, + 0xa5, 0xdc, 0x0e, 0x00, 0x00, 0x09, 0x53, 0x53, 0xac, 0x63, 0x6a, 0x53, 0x63, + 0xac, 0x6a, 0x2f, 0x2c, 0x3b, 0x86, 0x0e, 0x40, 0xe3, 0x1c, 0x61, 0x8c, 0xa1, + 0x7d, 0xf7, 0x15, 0x04, 0x00, 0x01, 0x21, 0xbf, 0x07, 0x11, 0x5b, 0x3a, 0x39, + 0xbb, 0x87, 0xf7, 0x23, 0x91, 0x52, 0x4b, 0x82, 0x0e, 0xf3, 0x5c, 0xfc, 0x09, + 0x58, 0xd4, 0x19, 0x2f, 0x49, 0x59, 0xef, 0xe4, 0xb9, 0xa7, 0xb5, 0x29, 0x98, + 0x8a, 0x3f, 0x7d, 0x27, 0x37, 0x91, 0x49, 0x0a, 0x6b, 0x48, 0x49, 0x5a, 0x80, + 0x06, 0x45, 0x5e, 0x86, 0x57, 0x71, 0xbe, 0x92, 0x06, 0xd5, 0x4b, 0x43, 0x02, + 0x4a, 0xf5, 0xe6, 0xc9, 0x5b, 0x33, 0xf6, 0xda, 0xd1, 0x66, 0x6a, 0x05, 0xf9, + 0x1a, 0xd7, 0x75, 0x79, 0x65, 0xc2, 0x99, 0x36, 0xe7, 0xfa, 0x48, 0xd7, 0x7e, + 0x89, 0xee, 0x09, 0x62, 0xf5, 0x8c, 0x05, 0x1d, 0x11, 0xd0, 0x55, 0xfc, 0xe2, + 0x04, 0xa5, 0x62, 0xde, 0x68, 0x08, 0x8a, 0x1b, 0x26, 0x48, 0xb8, 0x17, 0x4c, + 0xbc, 0xfc, 0x8b, 0x5b, 0x5c, 0xd0, 0x77, 0x11, 0x5a, 0xfd, 0xe1, 0x84, 0x05, + 0x05, 0x4e, 0x5d, 0xa9, 0xa0, 0x43, 0x10, 0x34, 0x2c, 0x5d, 0x3b, 0x52, 0x6e, + 0x0b, 0x02, 0xc5, 0xca, 0x17, 0x22, 0xba, 0xde, 0xee, 0x23, 0xd1, 0x45, 0xe8, + 0xeb, 0x22, 0x13, 0xfc, 0x4a, 0xf1, 0xe4, 0x50, 0xe4, 0xd5, 0x21, 0x7c, 0x66, + 0x17, 0x00, 0x8c, 0x78, 0xf4, 0xfb, 0x11, 0x12, 0xf4, 0x02, 0x8a, 0x70, 0x4f, + 0xc5, 0xa9, 0x38, 0x2c, 0x6b, 0x03, 0xe7, 0xd8, 0x08, 0x5e, 0x90, 0x6c, 0xf8, + 0x4c, 0xa2, 0xc1, 0x20, 0x7c, 0x87, 0xa2, 0xbc, 0xe2, 0x08, 0x0a, 0x98, 0x91, + 0x66, 0x8d, 0x69, 0xb0, 0x44, 0xbe, 0xce, 0xd6, 0xcd, 0xa3, 0x2c, 0x22, 0x9c, + 0x91, 0x17, 0x91, 0x7a, 0xa0, 0x7d, 0xdf, 0xfc, 0xd3, 0x77, 0x39, 0x5c, 0xba, + 0x61, 0x6d, 0x63, 0xc0, 0xb6, 0x9c, 0x01, 0xfc, 0xc4, 0x53, 0x91, 0xfd, 0x5b, + 0x87, 0x63, 0xfb, 0x96, 0xd7, 0xca, 0x33, 0x3a, 0x12, 0xde, 0x3c, 0xef, 0xa9, + 0x1c, 0x6c, 0x98, 0xf9, 0x47, 0x3b, 0x8e, 0x10, 0x4a, 0x71, 0x29, 0x3e, 0x46, + 0x37, 0x47, 0x05, 0xba, 0xf6, 0x5f, 0xa4, 0x13, 0x84, 0xba, 0x5c, 0x8e, 0x0c, + 0x88, 0xa3, 0xeb, 0x07, 0xe0, 0xbe, 0x34, 0xda, 0xdd, 0xfa, 0xbb, 0x7b, 0x65, + 0x54, 0x3b, 0x5f, 0x39, 0xcb, 0x20, 0x23, 0xd4, 0x67, 0x89, 0xeb, 0x7d, 0x98, + 0x9a, 0xf7, 0x79, 0xe5, 0xb8, 0xd2, 0x83, 0x85, 0xa8, 0x5b, 0x0d, 0xa2, 0xab, + 0xe0, 0x7f, 0x0c, 0x2b, 0xb4, 0x25, 0x5f, 0xce, 0xa0, 0x31, 0x88, 0x52, 0x7a, + 0x30, 0x7d, 0x40, 0x91, 0x59, 0xe9, 0x01, 0x66, 0xfa, 0xc6, 0xa0, 0x70, 0xba, + 0x05, 0xb3, 0xe4, 0xdb, 0xfd, 0x3a, 0x2b, 0xfc, 0xc9, 0xee, 0x6e, 0xd0, 0x16, + 0xc0, 0xf6, 0x65, 0xbe, 0x81, 0x33, 0xb7, 0xdc, 0x1d, 0x86, 0x04, 0x4d, 0xb0, + 0xf9, 0xdb, 0x40, 0xfb, 0x0e, 0x9f, 0x8b, 0xc2, 0xe4, 0xdb, 0x53, 0x82, 0xa8, + 0x04, 0x53, 0xfd, 0xd8, 0x8f, 0x52, 0xb0, 0x59, 0xc7, 0x96, 0x49, 0x43, 0xc1, + 0xc0, 0xdf, 0x3b, 0x6b, 0x64, 0x10, 0xf9, 0x5d, 0xef, 0x5c, 0x04, 0x78, 0xf6, + 0x85, 0xc6, 0xb3, 0x3a, 0xa0, 0xc8, 0x8e, 0x68, 0x82, 0x42, 0x8e, 0x8c, 0xba, + 0x43, 0x64, 0xea, 0x93, 0xaf, 0x37, 0x8c, 0x50, 0x64, 0x82, 0x27, 0x36, 0x58, + 0xa2, 0xc0, 0x95, 0xdd, 0x07, 0x6c, 0x39, 0x73, 0x19, 0x1d, 0x46, 0xa1, 0x03, + 0x9e, 0x5d, 0x6b, 0x7d, 0x9c, 0x08, 0x6b, 0x25, 0x85, 0x90, 0xd3, 0x2d, 0x76, + 0xe3, 0xd4, 0x20, 0x38, 0x43, 0x57, 0x0e, 0xfe, 0xed, 0x97, 0x1c, 0x14, 0x12, + 0x2f, 0x5b, 0x21, 0x5b, 0x0f, 0x38, 0xbb, 0xc7, 0x16, 0xb6, 0xa5, 0x94, 0x4e, + 0xe7, 0x27, 0x96, 0x56, 0x90, 0xe2, 0x09, 0xb4, 0x9e, 0xb9, 0x62, 0xc0, 0x39, + 0x97, 0x5f, 0x93, 0x9e, 0xd5, 0xc6, 0xe4, 0xc4, 0x00, 0xd8, 0x87, 0x75, 0x94, + 0x33, 0xd3, 0xad, 0x71, 0x6d, 0xa0, 0xcb, 0x44, 0x61, 0x13, 0xc7, 0x72, 0x7a, + 0x64, 0xb5, 0x8c, 0x3f, 0x8a, 0x0f, 0x81, 0x18, 0x9f, 0x98, 0x00, 0x52, 0x33, + 0xa8, 0x13, 0x66, 0xae, 0xe7, 0x3c, 0xec, 0x85, 0x22, 0x8e, 0xbc, 0xfd, 0x5e, + 0xe3, 0xc3, 0xfb, 0x44, 0xdb, 0x76, 0xba, 0x24, 0x3f, 0x28, 0x42, 0xb7, 0xb5, + 0xfc, 0x74, 0x6a, 0xe5, 0x1b, 0x0b, 0xc4, 0xbd, 0x4f, 0xc9, 0xfd, 0x83, 0x35, + 0x65, 0xea, 0x85, 0x2b, 0x92, 0xb2, 0x24, 0xf6, 0x99, 0x03, 0x18, 0xad, 0x8c, + 0x7d, 0x94, 0x37, 0xe2, 0x0e, 0x2a, 0x1f, 0x20, 0xe8, 0x18, 0xf9, 0x05, 0x7c, + 0x5a, 0xba, 0xaa, 0x2e, 0x5c, 0x15, 0xb9, 0x49, 0x45, 0xcd, 0x42, 0x4c, 0x28, + 0xa5, 0xfa, 0x38, 0x5d, 0xad, 0xfe, 0x49, 0x07, 0xb2, 0x74, 0xd8, 0x42, 0x70, + 0x7d, 0xb3, 0x69, 0x7a, 0x5a, 0xe6, 0xc8, 0xf5, 0x42, 0xe5, 0xec, 0xc0, 0x7f, + 0xe4, 0x73, 0x50, 0xd1, 0x01, 0x46, 0x70, 0x21, 0x2e, 0xfe, 0x81, 0xfb, 0x7c, + 0x73, 0xe8, 0x45, 0x0d, 0xf8, 0x14, 0xef, 0x62, 0x32, 0xf7, 0x49, 0x0f, 0x63, + 0xcc, 0xf0, 0x74, 0x80, 0xf8, 0x84, 0xa6, 0x6e, 0xaf, 0xfc, 0x28, 0xfe, 0xa4, + 0x48, 0xd7, 0xb4, 0x01, 0xcd, 0xae, 0x10, 0xe7, 0xc0, 0xc7, 0xf9, 0xa7, 0xb1, + 0x53, 0x31, 0x96, 0x9f, 0xc8, 0xcb, 0x36, 0x39, 0x67, 0x73, 0xde, 0x19, 0x19, + 0x31, 0xc7, 0x50, 0xf6, 0xce, 0x5c, 0xaa, 0xf2, 0x97, 0x68, 0xeb, 0xb2, 0x7d, + 0xac, 0xc7, 0x38, 0x05, 0x6a, 0x81, 0x25, 0xb4, 0x77, 0x2b, 0xf8, 0x7a, 0xe1, + 0x0a, 0x8a, 0x30, 0x9b, 0x9b, 0xd6, 0x55, 0x04, 0x3c, 0xfc, 0x31, 0x59, 0x49, + 0x43, 0x68, 0xc5, 0xab, 0x8c, 0xad, 0xb7, 0xf6, 0x71, 0xe9, 0x62, 0x6b, 0xd2, + 0x63, 0xe3, 0x11, 0x81, 0xa6, 0x04, 0xb5, 0x06, 0xa0, 0x3b, 0x43, 0x9a, 0x7f, + 0xfe, 0x43, 0x55, 0x89, 0x24, 0x77, 0xe2, 0xbd, 0xf3, 0x38, 0xc6, 0x2c, 0x39, + 0x22, 0xf7, 0xd3, 0xc9, 0xa5, 0x6c, 0x71, 0x03, 0xd9, 0x11, 0x94, 0x8a, 0x84, + 0xb5, 0xae, 0x2d, 0xbb, 0x16, 0xa3, 0x76, 0x1a, 0xdd, 0x05, 0x3a, 0x0f, 0x96, + 0x7e, 0x6b, 0x5b, 0xc9, 0x42, 0x11, 0xb6, 0x54, 0x71, 0x53, 0x26, 0x7c, 0x6e, + 0xe1, 0xca, 0xd0, 0xd9, 0x74, 0xa7, 0x10, 0x88, 0x58, 0x37, 0x35, 0xe4, 0xf6, + 0x3d, 0x33, 0x15, 0x6d, 0xad, 0xd5, 0x4c, 0x2f, 0xaf, 0x89, 0x11, 0x4a, 0x12, + 0x7b, 0x97, 0xb9, 0x4c, 0xc2, 0xa2, 0x2e, 0xf3, 0x03, 0xf4, 0x59, 0xd0, 0x4f, + 0xc0, 0xb5, 0x3a, 0xce, 0x59, 0x18, 0xd4, 0x7f, 0xf3, 0x3a, 0x55, 0x8b, 0xd7, + 0x1a, 0x75, 0xf3, 0x55, 0xfb, 0xd0, 0x6b, 0xbc, 0xcf, 0x4e, 0x02, 0xc3, 0xc0, + 0xa4, 0xb6, 0x3d, 0x0c, 0xc9, 0x49, 0x80, 0x1d, 0x63, 0xa6, 0x4c, 0xb2, 0xd3, + 0x23, 0x73, 0xb2, 0xc7, 0xb2, 0x74, 0xab, 0x2d, 0xb4, 0x68, 0x21, 0x42, 0xc8, + 0xb2, 0x1d, 0x84, 0xc4, 0x81, 0xf5, 0xef, 0x21, 0xe4, 0xb5, 0xe3, 0x60, 0x34, + 0x51, 0xbf, 0x94, 0x77, 0x4d, 0x0e, 0xf4, 0x7f, 0x63, 0xfa, 0x6a, 0xbb, 0x78, + 0xd2, 0x1c, 0x19, 0x3c, 0xbe, 0x65, 0xb6, 0x95, 0xfe, 0x67, 0x42, 0x3c, 0x1e, + 0x2d, 0x31, 0x2e, 0x27, 0x76, 0xfa, 0x24, 0xec, 0xe8, 0x46, 0x83, 0xe7, 0x48, + 0x76, 0xc5, 0x5e, 0xa0, 0x36, 0x9e, 0x4e, 0xa0, 0xe8, 0x64, 0x94, 0xe0, 0x0d, + 0xde, 0x23, 0x6a, 0x16, 0x89, 0x73, 0x1f, 0x0a, 0x5d, 0x82, 0x03, 0xaf, 0xde, + 0x5c, 0x42, 0x36, 0x40, 0xb8, 0x1e, 0x4f, 0x63, 0x1c, 0x98, 0x1c, 0x11, 0xa2, + 0xe1, 0xd1, 0x84, 0xc6, 0x7c, 0x52, 0x8d, 0xf9, 0x2d, 0x53, 0xae, 0xc4, 0x4a, + 0x40, 0xa4, 0xea, 0x2a, 0x13, 0x1b, 0x47, 0x33, 0xcf, 0xe4, 0x5c, 0x6b, 0x00, + 0x12, 0xc3, 0xe9, 0xe2, 0x09, 0x75, 0xba, 0xae, 0xcb, 0x02, 0x32, 0xdf, 0x88, + 0x0b, 0xd7, 0xd1, 0xde, 0x13, 0xe1, 0x34, 0x94, 0x62, 0xec, 0x8d, 0x5d, 0xf3, + 0xe7, 0x80, 0xff, 0xa7, 0x2e, 0xba, 0x8a, 0x8d, 0xf7, 0xfc, 0xf3, 0x98, 0xec, + 0x23, 0x05, 0x13, 0xca, 0x9d, 0x61, 0x23, 0xf8, 0xb9, 0xd8, 0x17, 0x85, 0x60, + 0xda, 0xf9, 0x75, 0x11, 0x19, 0x55, 0xa2, 0xbc, 0xa3, 0x42, 0x3e, 0xee, 0xfc, + 0x52, 0x7b, 0xe3, 0xa8, 0x54, 0x3e, 0xb9, 0x0a, 0x5e, 0xc0, 0x2f, 0x35, 0xa7, + 0xc6, 0x4b, 0x7d, 0xd5, 0x9a, 0x72, 0xda, 0x00, 0x74, 0x63, 0x4e, 0x01, 0xd2, + 0xab, 0xf3, 0x63, 0x7a, 0xdd, 0x77, 0xc7, 0x35, 0x0f, 0x12, 0xb0, 0x11, 0xb2, + 0x94, 0x16, 0x8e, 0xc7, 0x55, 0x76, 0xe4, 0x7d, 0x16, 0x9e, 0x39, 0x38, 0xbf, + 0x6a, 0xe2, 0xaa, 0x8f, 0xf7, 0xcf, 0xba, 0x7c, 0xac, 0xb1, 0xf9, 0x2b, 0x6e, + 0x4c, 0x24, 0x97, 0xbf, 0xfa, 0x9f, 0x17, 0xca, 0xd2, 0x42, 0xfa, 0x9c, 0x31, + 0x79, 0xc1, 0xa3, 0xaa, 0x81, 0xf7, 0x36, 0x16, 0x49, 0x57, 0x2c, 0x71, 0x5c, + 0x25, 0xa1, 0xf6, 0xcd, 0x5a, 0xce, 0x82, 0xc0, 0x0a, 0xb2, 0x34, 0x2b, 0x9c, + 0x3c, 0xb4, 0xff, 0xfd, 0xda, 0x16, 0x0c, 0xa5, 0xab, 0x9e, 0x9b, 0xaf, 0x21, + 0x39, 0xef, 0x9a, 0xfb, 0xe1, 0xb1, 0xf3, 0x09, 0x46, 0x2a, 0xfc, 0xe4, 0x62, + 0xa7, 0x9b, 0xb9, 0x69, 0x8e, 0x22, 0xc9, 0x57, 0xc5, 0x90, 0xa7, 0x53, 0xa7, + 0x6b, 0x87, 0xe0, 0x09, 0x12, 0x1e, 0x06, 0xf6, 0xa1, 0xbf, 0x62, 0xa0, 0x8b, + 0xf4, 0x35, 0xd9, 0x2e, 0x2f, 0xff, 0xe8, 0x6e, 0x2a, 0x9c, 0xbb, 0xa9, 0x13, + 0x3a, 0x68, 0xe4, 0xae, 0xbf, 0x33, 0xc3, 0x84, 0x36, 0xf2, 0x54, 0x5f, 0xc2, + 0xd5, 0x28, 0x32, 0xd1, 0x65, 0xaf, 0x41, 0x5b, 0x24, 0x4a, 0xdc, 0x5f, 0x57, + 0x37, 0x7d, 0xee, 0xdf, 0x46, 0x0a, 0xa3, 0xbe, 0xb4, 0x34, 0x19, 0xc6, 0xb0, + 0x82, 0xe8, 0x35, 0xce, 0x84, 0xca, 0x13, 0xb6, 0x90, 0x8a, 0x88, 0x13, 0xc0, + 0x21, 0xde, 0x9f, 0xa9, 0xa4, 0x4e, 0x4c, 0x18, 0xdc, 0xb3, 0xd2, 0x1f, 0xaa, + 0x8b, 0x86, 0xc8, 0x70, 0x28, 0xd0, 0xb5, 0x53, 0x21, 0x07, 0xf9, 0xf6, 0xfd, + 0x49, 0x00, 0x22, 0x7d, 0x0d, 0x8f, 0xf2, 0xbf, 0x9d, 0x28, 0xcb, 0xcc, 0x99, + 0x6c, 0x47, 0x3c, 0xe6, 0x16, 0x41, 0xf4, 0x88, 0x4e, 0x6e, 0xd3, 0xfd, 0x5e, + 0x4b, 0x7c, 0xb8, 0x35, 0xb8, 0x33, 0x08, 0x96, 0x4e, 0x3c, 0x46, 0x87, 0x3f, + 0xd6, 0x13, 0x31, 0x7b, 0x91, 0xd2, 0x92, 0x36, 0xea, 0x90, 0xe3, 0x65, 0x32, + 0xec, 0x5d, 0x6b, 0x42, 0x32, 0xe8, 0xbc, 0xcc, 0x36, 0x75, 0xb9, 0x8b, 0x35, + 0xf8, 0xde, 0x4d, 0x08, 0x88, 0x84, 0x14, 0x6f, 0x3d, 0xb8, 0x97, 0x0b, 0x38, + 0xd1, 0xe5, 0x01, 0xae, 0xe9, 0xc1, 0xf3, 0xfd, 0x78, 0x25, 0x49, 0xd3, 0xf3, + 0x24, 0x57, 0x59, 0x60, 0x6d, 0x9f, 0x92, 0xd5, 0x54, 0x8a, 0xcf, 0xea, 0xdb, + 0xaf, 0x9c, 0xaa, 0x6b, 0x93, 0xdc, 0x08, 0x82, 0x8d, 0x74, 0xf6, 0xd5, 0xfd, + 0xd8, 0x33, 0x31, 0xf0, 0x96, 0x91, 0x45, 0x95, 0x52, 0x97, 0xe6, 0x9f, 0x00, + 0xfd, 0x29, 0x87, 0xf2, 0xda, 0x2b, 0x94, 0xb9, 0x95, 0xfe, 0xcb, 0xe6, 0x22, + 0xa7, 0x35, 0xef, 0x7f, 0x12, 0x07, 0xf6, 0x71, 0x62, 0x94, 0x89, 0x20, 0x2b, + 0xea, 0x0b, 0x47, 0x5e, 0x51, 0x68, 0x1a, 0xa1, 0x67, 0x78, 0xb3, 0x9b, 0xd9, + 0x23, 0xc9, 0x8d, 0xc6, 0xff, 0x83, 0x73, 0xc7, 0x9b, 0xb1, 0x70, 0x30, 0x41, + 0x7b, 0xc2, 0x00, 0xc8, 0xf0, 0xb8, 0x55, 0xac, 0xfe, 0xc1, 0x79, 0xf7, 0x67, + 0x4c, 0xec, 0x27, 0x21, 0xa1, 0x0f, 0xca, 0x69, 0x3d, 0x83, 0xcf, 0xe5, 0xb8, + 0xcd, 0xcc, 0x18, 0xf8, 0x1a, 0xd6, 0x17, 0xfa, 0x26, 0xf0, 0xdf, 0xb8, 0x36, + 0x55, 0xb8, 0xa2, 0x9a, 0x7f, 0x83, 0x42, 0x32, 0x42, 0x5e, 0x8c, 0x47, 0x45, + 0x88, 0xf1, 0x8d, 0xd3, 0x26, 0xaa, 0x39, 0x6c, 0x3e, 0x47, 0x75, 0xe0, 0x02, + 0x05, 0xfc, 0x9e, 0x45, 0xf7, 0xb7, 0xd2, 0xe6, 0xd5, 0x5d, 0xcb, 0x90, 0xe2, + 0x3f, 0xf6, 0xb5, 0x08, 0x45, 0x9a, 0xa6, 0x99, 0xbf, 0xcb, 0xd5, 0x6f, 0x10, + 0x99, 0x77, 0x64, 0xd0, 0x87, 0x40, 0x89, 0x86, 0xe7, 0x3d, 0x6e, 0x28, 0x4f, + 0xea, 0x9a, 0x23, 0xc3, 0x93, 0x11, 0x78, 0x2f, 0x86, 0xca, 0xbf, 0xf9, 0x45, + 0x5e, 0x4c, 0xf6, 0x99, 0xe5, 0xf5, 0xd4, 0xbc, 0x0b, 0x39, 0x05, 0xa4, 0xe3, + 0xbd, 0x01, 0xc5, 0x4d, 0xf8, 0x64, 0x34, 0x43, 0xbe, 0x0f, 0x88, 0x90, 0x32, + 0xea, 0x32, 0x5b, 0xf0, 0x71, 0x07, 0xfd, 0x41, 0xd6, 0x73, 0xee, 0xba, 0xe6, + 0xfa, 0x63, 0x7b, 0x70, 0xcc, 0x0e, 0xd3, 0xf0, 0x09, 0x58, 0xdf, 0xb8, 0xdc, + 0xf0, 0x0e, 0x85, 0xa1, 0xd0, 0xa6, 0xa8, 0x90, 0x81, 0x40, 0xc2, 0xf4, 0x34, + 0xc2, 0xe2, 0x60, 0xef, 0xb0, 0xbc, 0xa2, 0x00, 0x35, 0x04, 0xc9, 0x99, 0x93, + 0xa9, 0xe1, 0xc0, 0xff, 0x9c, 0xef, 0xe6, 0xa6, 0x65, 0xd7, 0x91, 0x42, 0x86, + 0x90, 0xe4, 0x7e, 0xf8, 0xc1, 0x31, 0xa8, 0xe9, 0xbf, 0xb4, 0xc3, 0x08, 0x02, + 0x35, 0x03, 0x2d, 0x73, 0x1b, 0x0d, 0x38, 0x41, 0x22, 0x5f, 0x1c, 0x11, 0xe2, + 0xc2, 0x8e, 0xe8, 0x4d, 0x35, 0xf9, 0x22, 0x61, 0x00, 0x56, 0x59, 0x72, 0xeb, + 0x26, 0x9d, 0x27, 0x8e, 0xf6, 0x49, 0x79, 0xbf, 0x65, 0x15, 0xed, 0x4a, 0x68, + 0x40, 0xb0, 0x88, 0x3a, 0x9e, 0x6e, 0xf6, 0x4a, 0x0e, 0xfc, 0xae, 0x1c, 0xf2, + 0x1d, 0xfe, 0x74, 0x85, 0x4e, 0x84, 0xc2, 0x74, 0x9f, 0xac, 0x03, 0x82, 0x52, + 0x75, 0xc9, 0xb6, 0x30, 0x21, 0x84, 0xc7, 0x2d, 0xf4, 0xc4, 0xbb, 0x28, 0x62, + 0xe4, 0xe8, 0xa7, 0xd9, 0xa4, 0xa2, 0x82, 0x86, 0x6f, 0x9a, 0x7b, 0x2c, 0xfc, + 0x9a, 0x56, 0x31, 0x3d, 0xa0, 0xc4, 0x7a, 0x34, 0xb7, 0xb9, 0xcd, 0xa3, 0xac, + 0xe8, 0x18, 0x5f, 0x07, 0xdf, 0x36, 0xe4, 0x48, 0xa7, 0x6a, 0xa4, 0x77, 0xf2, + 0x24, 0xd8, 0x7a, 0x07, 0x4f, 0x43, 0xaf, 0x5d, 0x5f, 0x79, 0xb3, 0xab, 0x11, + 0x28, 0xf0, 0x81, 0x91, 0x44, 0x7f, 0xa6, 0x46, 0xbf, 0xdd, 0xe5, 0xb5, 0x1e, + 0x23, 0x3c, 0xa6, 0x15, 0x5d, 0x10, 0x15, 0x85, 0xbc, 0x2c, 0x40, 0x15, 0x8a, + 0xc2, 0x10, 0x6e, 0x66, 0xa2, 0x6e, 0x46, 0x42, 0x33, 0x70, 0x63, 0x68, 0x76, + 0xb4, 0x34, 0xa7, 0x4f, 0x8c, 0xe8, 0x06, 0x00, 0x50, 0xb0, 0x82, 0xa7, 0x9b, + 0x61, 0xbb, 0x5d, 0x34, 0x4e, 0xb5, 0xa1, 0x15, 0x83, 0x26, 0xce, 0xd9, 0xa9, + 0xd9, 0xf5, 0x4f, 0xb2, 0xfe, 0x8f, 0x9f, 0x05, 0xcd, 0x11, 0x1e, 0xe4, 0x6c, + 0x47, 0x10, 0xf6, 0xf6, 0x3a, 0x62, 0x69, 0x45, 0x57, 0xef, 0x1b, 0x12, 0xc8, + 0x80, 0x06, 0xb6, 0x78, 0x72, 0x50, 0x5f, 0x4e, 0x88, 0x3b, 0x58, 0x59, 0x07, + 0x92, 0x9a, 0x2f, 0x3f, 0xdb, 0x0d, 0x8f, 0x79, 0x14, 0xc4, 0x2d, 0xde, 0x2d, + 0x20, 0x00, 0xf5, 0xae, 0x02, 0xd4, 0x18, 0x21, 0xc8, 0xe1, 0xee, 0x01, 0x38, + 0xeb, 0xcb, 0x72, 0x8d, 0x7c, 0x6c, 0x3c, 0x80, 0x02, 0x7e, 0x43, 0x75, 0x94, + 0xc6, 0x70, 0xfd, 0x6f, 0x39, 0x08, 0x22, 0x2e, 0xe7, 0xa1, 0xb9, 0x17, 0xf8, + 0x27, 0x1a, 0xbe, 0x66, 0x0e, 0x39, 0xe0, 0x51, 0xaa, 0xa6, 0xfc, 0xa1, 0x86, + 0x22, 0x76, 0xe2, 0xba, 0xa0, 0xfe, 0x0b, 0x16, 0x2a, 0xeb, 0xcf, 0xe3, 0xd9, + 0x34, 0x9c, 0x8d, 0x15, 0x4b, 0xb7, 0xee, 0x28, 0x21, 0x2c, 0x1b, 0xaa, 0x70, + 0x5d, 0x82, 0x07, 0x0d, 0x70, 0x32, 0xf2, 0x69, 0x5d, 0x17, 0x96, 0x80, 0x9f, + 0xab, 0x41, 0x24, 0x69, 0x26, 0xaf, 0x99, 0x2b, 0x6e, 0xee, 0x95, 0xa9, 0xa0, + 0x6b, 0xc4, 0x56, 0x2c, 0x5f, 0x2f, 0x1b, 0x19, 0x54, 0x95, 0x00, 0x37, 0x2e, + 0x7a, 0xd5, 0x79, 0xa6, 0xd6, 0xd7, 0x8b, 0x33, 0x15, 0x31, 0x30, 0xfb, 0x44, + 0x8f, 0xb7, 0x9e, 0x8a, 0x66, 0x9d, 0xb8, 0xa0, 0xf3, 0x5c, 0xdf, 0x9a, 0xe5, + 0xd3, 0x2d, 0x73, 0x2f, 0xc7, 0x94, 0x18, 0xe2, 0x3b, 0x45, 0x1d, 0xdc, 0x95, + 0xa2, 0x2a, 0xba, 0xbb, 0x05, 0x6e, 0xc6, 0xb5, 0xe8, 0xba, 0x4f, 0x52, 0x4d, + 0xfa, 0xfe, 0x87, 0x52, 0x62, 0xdd, 0x7b, 0xe4, 0x1c, 0xbb, 0xc6, 0x24, 0x20, + 0xd4, 0xad, 0x6d, 0xf5, 0xc9, 0xb7, 0x13, 0x60, 0x4f, 0x65, 0x60, 0x88, 0xa4, + 0x48, 0x5e, 0x93, 0xbe, 0x19, 0x07, 0xd2, 0x7a, 0xc6, 0xec, 0x3c, 0x57, 0x25, + 0x9b, 0xd6, 0x98, 0x1d, 0x42, 0xc1, 0xb7, 0x8a, 0x29, 0xad, 0x96, 0x85, 0xe6, + 0x3c, 0x49, 0x4d, 0x41, 0x29, 0x62, 0x3e, 0xa1, 0xa7, 0xff, 0xec, 0x85, 0xfa, + 0x29, 0x41, 0x10, 0x73, 0xed, 0xb2, 0x97, 0x8e, 0xf4, 0xe4, 0x69, 0xdd, 0xd5, + 0xcd, 0xa9, 0x86, 0x18, 0x99, 0x95, 0xf8, 0x8d, 0x6a, 0xb3, 0x66, 0xdb, 0x01, + 0x90, 0x01, 0xf5, 0xb2, 0x52, 0x88, 0xcf, 0x86, 0x0f, 0xd9, 0x98, 0xee, 0x57, + 0x3c, 0x8c, 0xc4, 0x8a, 0xa9, 0xef, 0xcf, 0x9b, 0x61, 0x7e, 0x04, 0x3c, 0x99, + 0x00, 0x8e, 0x35, 0x00, 0x96, 0xfd, 0xa4, 0xeb, 0x24, 0xc2, 0x0f, 0x46, 0x90, + 0xf1, 0xe2, 0xc5, 0xef, 0x86, 0x6c, 0x0e, 0xe5, 0xdd, 0xa1, 0x19, 0xee, 0xea, + 0xf1, 0x19, 0xdb, 0xdc, 0xae, 0x8d, 0xc7, 0x6c, 0x84, 0x6c, 0xc2, 0x27, 0x27, + 0x2b, 0xfc, 0x54, 0x17, 0xdc, 0x4c, 0xf4, 0xc0, 0x87, 0xba, 0x34, 0xec, 0xf3, + 0xa5, 0x5b, 0x00, 0x1f, 0xf3, 0x09, 0xa8, 0x1c, 0x05, 0x2d, 0x69, 0x26, 0xa9, + 0xdd, 0xf0, 0xf7, 0x8c, 0x5f, 0xc0, 0x64, 0xc6, 0xa6, 0x40, 0x16, 0x21, 0xb3, + 0x8a, 0xa5, 0x49, 0x44, 0x19, 0x81, 0x99, 0x21, 0x0d, 0x2b, 0x42, 0xe6, 0x1d, + 0xde, 0x1d, 0x08, 0xaf, 0x55, 0x07, 0x3b, 0xbf, 0x06, 0x15, 0xf6, 0x7b, 0x11, + 0x00, 0xcc, 0x2e, 0xa3, 0xba, 0x3d, 0x6c, 0x1a, 0x1a, 0x90, 0x87, 0xb1, 0x19, + 0xba, 0xee, 0xbf, 0xa6, 0x2b, 0xc9, 0xf0, 0xec, 0x47, 0x9d, 0x99, 0xc1, 0xa3, + 0xb1, 0x58, 0xb5, 0x14, 0xd1, 0x62, 0x9d, 0xb3, 0x99, 0x3f, 0x11, 0x67, 0x2a, + 0x26, 0x70, 0x8e, 0x5a, 0xd8, 0x16, 0xb5, 0x47, 0xab, 0x7e, 0x82, 0x7d, 0x07, + 0x1b, 0xa7, 0x84, 0x2b, 0x3e, 0x90, 0x30, 0x53, 0x83, 0x89, 0x6e, 0xc4, 0x90, + 0x5f, 0x70, 0xc7, 0x8b, 0x69, 0x4e, 0x6a, 0x5a, 0x3e, 0x43, 0x12, 0xcd, 0x82, + 0x08, 0x13, 0x2b, 0x84, 0x0f, 0x05, 0xc7, 0x14, 0x52, 0x3c, 0xa8, 0x19, 0x72, + 0x0a, 0xe2, 0x27, 0xfd, 0x1a, 0xcb, 0xa7, 0x14, 0xfa, 0x4f, 0xc4, 0x5f, 0xc5, + 0x39, 0x88, 0x57, 0xb4, 0x0d, 0xc1, 0x48, 0x79, 0x85, 0x6f, 0x35, 0x4b, 0xa4, + 0xd2, 0x58, 0x1d, 0x0c, 0xda, 0x54, 0xb6, 0x38, 0xba, 0x9d, 0x76, 0xf9, 0xb5, + 0x2d, 0x17, 0xc8, 0xf8, 0x8e, 0xe6, 0x3f, 0x58, 0x45, 0xb5, 0xdc, 0xef, 0xa4, + 0xc3, 0x47, 0x9b, 0xce, 0x9a, 0xca, 0xd1, 0x8b, 0x4a, 0xea, 0xe0, 0x3c, 0x0e, + 0xae, 0x22, 0x5d, 0x42, 0x84, 0x8b, 0xde, 0xaa, 0x53, 0x6d, 0x7d, 0x8d, 0xd3, + 0xbc, 0x97, 0x9f, 0x06, 0x58, 0x66, 0x73, 0xbc, 0x6f, 0xf1, 0xc5, 0xd3, 0xb3, + 0x20, 0xf3, 0x49, 0xa5, 0xb3, 0xa8, 0xb3, 0x55, 0x59, 0x22, 0x96, 0xaa, 0xf6, + 0x1c, 0x5b, 0x72, 0x52, 0xf7, 0x3e, 0xc0, 0xa9, 0x46, 0x6a, 0x1b, 0x85, 0x76, + 0x4f, 0xb0, 0x83, 0x1b, 0x4a, 0x1a, 0x36, 0x89, 0x0e, 0x22, 0x4c, 0x01, 0xac, + 0xfc, 0xe4, 0x8e, 0xe3, 0xed, 0x93, 0x87, 0x73, 0x98, 0xe0, 0x72, 0x6d, 0x02, + 0x93, 0x6d, 0x0d, 0x03, 0x2e, 0x18, 0xe3, 0x28, 0x8b, 0x26, 0x70, 0xe1, 0x36, + 0x2c, 0x32, 0xd6, 0xe4, 0x73, 0x3b, 0x9d, 0xd2, 0xd5, 0xf2, 0x6e, 0x1f, 0xe3, + 0x06, 0xf7, 0x3c, 0x00, 0x7f, 0xdd, 0xca, 0xe9, 0xd9, 0xc0, 0xaa, 0xf1, 0x87, + 0xd7, 0x42, 0x8b, 0x1e, 0x9d, 0x47, 0x9c, 0x18, 0x23, 0x7b, 0x98, 0x28, 0xbc, + 0xa8, 0xb9, 0x8c, 0x9d, 0x9b, 0xec, 0x7d, 0x82, 0x70, 0xb5, 0xd8, 0xee, 0xc3, + 0xcc, 0x4f, 0x43, 0xfa, 0x01, 0x88, 0x52, 0x1b, 0xc6, 0x1b, 0x21, 0xdd, 0x04, + 0xe3, 0x7a, 0x83, 0xec, 0xe6, 0x8c, 0xa7, 0xa2, 0xfa, 0x6c, 0x8f, 0x9e, 0x34, + 0xa6, 0x29, 0x03, 0x35, 0xaa, 0x1f, 0xbd, 0x83, 0xd5, 0x4a, 0xaf, 0x44, 0x1e, + 0x31, 0x9e, 0xa4, 0x7a, 0x86, 0x2a, 0xd0, 0x29, 0x3c, 0xed, 0xf5, 0xdd, 0x9e, + 0xda, 0xde, 0xee, 0x33, 0xcb, 0x52, 0x2c, 0xd0, 0x11, 0x8b, 0xbd, 0x81, 0x1a, + 0xce, 0x9a, 0x23, 0xbd, 0xa3, 0x9a, 0xba, 0x72, 0xf1, 0x56, 0x6f, 0xc1, 0x68, + 0x84, 0x97, 0xd2, 0xa7, 0x92, 0x8c, 0x36, 0x70, 0x15, 0x25, 0x67, 0x8b, 0xc9, + 0x72, 0x14, 0xb3, 0x1b, 0x37, 0xba, 0xb4, 0x6b, 0x88, 0xf2, 0x7f, 0x04, 0x48, + 0xde, 0xcb, 0x31, 0x62, 0x2d, 0x0f, 0x0f, 0x87, 0xa8, 0x55, 0xba, 0x54, 0x00, + 0x03, 0x32, 0x03, 0x1f, 0x73, 0xab, 0xff, 0xd4, 0x65, 0x91, 0xda, 0x0b, 0x88, + 0x72, 0x35, 0x04, 0xed, 0xb2, 0x33, 0x72, 0x30, 0xda, 0xd2, 0xac, 0xc0, 0xd8, + 0xbb, 0x68, 0xbc, 0x83, 0x7a, 0x2f, 0xf9, 0x30, 0xbf, 0xf0, 0x6f, 0xde, 0x74, + 0xeb, 0x90, 0xaa, 0xe4, 0xf6, 0x0d, 0xbb, 0x6e, 0xb8, 0x27, 0xea, 0x99, 0x88, + 0x4a, 0xcd, 0x62, 0x85, 0xa9, 0x88, 0x92, 0x80, 0x2c, 0xf5, 0x9d, 0x5d, 0x60, + 0xd0, 0x16, 0x63, 0x38, 0x7b, 0x3e, 0xd2, 0x72, 0x3b, 0xd6, 0x48, 0x9e, 0x9c, + 0x2c, 0x10, 0x6d, 0x4a, 0xa2, 0xde, 0x23, 0xce, 0xd1, 0x6c, 0x72, 0x04, 0x29, + 0xc7, 0x75, 0x3a, 0x77, 0x38, 0xec, 0x7d, 0x9d, 0xb8, 0x62, 0x42, 0x29, 0xed, + 0xd2, 0x17, 0xb8, 0x0d, 0x74, 0x87, 0x5a, 0x14, 0xca, 0xe4, 0x86, 0x3f, 0x13, + 0x9e, 0x9c, 0x0b, 0x13, 0x1b, 0x2a, 0x4c, 0x28, 0x07, 0x1a, 0x38, 0xec, 0x61, + 0xf6, 0x68, 0x01, 0xaa, 0x59, 0x56, 0xfc, 0xb2, 0xa4, 0x6b, 0x95, 0x87, 0x66, + 0x5b, 0x75, 0x71, 0xaa, 0x03, 0x48, 0x1f, 0xd8, 0xd9, 0xd5, 0x69, 0x8f, 0x83, + 0x6f, 0xc8, 0x63, 0x5e, 0x69, 0xe3, 0xbd, 0xe4, 0x2f, 0x4a, 0xc0, 0x71, 0x32, + 0x8b, 0x54, 0x09, 0xf6, 0xe4, 0x2d, 0x79, 0x0a, 0xed, 0xd7, 0x3b, 0xc1, 0xa2, + 0x35, 0x47, 0x23, 0xb3, 0xb8, 0x19, 0xd0, 0x63, 0x7a, 0x6f, 0xa4, 0x66, 0x39, + 0x46, 0xa3, 0x0a, 0xc5, 0xaf, 0xdd, 0x30, 0xce, 0x83, 0x0f, 0x67, 0x91, 0xb4, + 0x57, 0x52, 0x70, 0xa1, 0x72, 0x0f, 0x91, 0x86, 0x6e, 0x2b, 0x86, 0xf4, 0x78, + 0x88, 0x94, 0xc8, 0xda, 0x62, 0xd8, 0xb9, 0x1f, 0xaf, 0x52, 0x0e, 0x3b, 0xed, + 0xbc, 0x12, 0x06, 0xa5, 0xa5, 0xe6, 0xef, 0xd3, 0xdf, 0xde, 0x08, 0x43, 0xc3, + 0xb0, 0x67, 0x57, 0x64, 0x3f, 0xc0, 0x06, 0x00, 0x88, 0x38, 0xca, 0x47, 0x30, + 0x87, 0xf8, 0x97, 0x79, 0x18, 0xcc, 0x1b, 0x81, 0xc9, 0xe6, 0x8e, 0x3b, 0x88, + 0x8f, 0xe6, 0xf7, 0xc6, 0x30, 0xf1, 0xbc, 0x7a, 0xe1, 0x88, 0xf5, 0x12, 0x84, + 0x20, 0x41, 0xca, 0xda, 0x1e, 0x05, 0xf8, 0x66, 0xd2, 0x56, 0x2d, 0xbe, 0x09, + 0xc4, 0xb4, 0x30, 0x68, 0xf7, 0x54, 0xda, 0xd3, 0x4d, 0xf0, 0xfc, 0xfc, 0x18, + 0x1f, 0x31, 0x80, 0x1a, 0x79, 0x92, 0xd2, 0xf1, 0x6b, 0xe0, 0x21, 0x1b, 0x4a, + 0x22, 0xf6, 0x2a, 0xab, 0x64, 0x70, 0x1b, 0xf4, 0xa4, 0xe6, 0xd6, 0x66, 0xfc, + 0x30, 0x4a, 0x5c, 0x79, 0xc6, 0x09, 0xac, 0xc4, 0x3b, 0x00, 0xb4, 0x86, 0x48, + 0x93, 0xd3, 0x7d, 0x50, 0x07, 0xf0, 0xc3, 0x29, 0xa4, 0x75, 0x50, 0x52, 0x57, + 0x75, 0x70, 0xdd, 0x38, 0xfa, 0xc0, 0x43, 0xcd, 0x91, 0xc1, 0x2e, 0xe3, 0x4e, + 0x9c, 0xfa, 0xe3, 0x92, 0xa7, 0x8b, 0xda, 0xbd, 0x4e, 0xe3, 0x1d, 0xc0, 0xde, + 0xb0, 0x2f, 0xe7, 0xb1, 0xd8, 0xb0, 0x17, 0x8a, 0xc9, 0x51, 0x31, 0x05, 0xfc, + 0xc7, 0xe3, 0x0b, 0xa8, 0xe0, 0x16, 0xaa, 0x36, 0xa6, 0xb5, 0xdf, 0x5e, 0x5a, + 0x19, 0x09, 0xf6, 0x3a, 0xba, 0x09, 0x5d, 0x98, 0x77, 0xa8, 0xf2, 0x6e, 0x40, + 0x3d, 0xc2, 0x54, 0x76, 0xad, 0xd8, 0x3d, 0xb1, 0xca, 0x15, 0x8f, 0x0b, 0x42, + 0x2b, 0x5f, 0xf4, 0xdb, 0x69, 0xb1, 0x24, 0x4b, 0xc0, 0x90, 0x2e, 0xd0, 0x30, + 0x3f, 0xec, 0x73, 0xa5, 0xbf, 0xfe, 0xc7, 0x3a, 0xc3, 0x4c, 0x1a, 0x73, 0x16, + 0x0f, 0x2c, 0xea, 0x1e, 0x05, 0x10, 0xf8, 0x4d, 0x2f, 0xe2, 0xf7, 0x3b, 0x6e, + 0x92, 0x19, 0x07, 0xa1, 0xb7, 0xb3, 0x75, 0x12, 0x13, 0x24, 0x30, 0x11, 0x76, + 0xb0, 0x9b, 0xc0, 0x41, 0xe4, 0x68, 0x42, 0x3e, 0x93, 0xd5, 0xdc, 0xa3, 0x3e, + 0x67, 0x1a, 0x78, 0x6d, 0x23, 0x1f, 0x16, 0x43, 0xea, 0x66, 0x43, 0x8b, 0xa7, + 0x85, 0xb8, 0x1e, 0x6c, 0x2b, 0xc7, 0x3f, 0xf0, 0x0d, 0x89, 0x3b, 0xc1, 0x28, + 0x5e, 0xfc, 0xa8, 0x25, 0x99, 0xd1, 0x81, 0xf1, 0x23, 0x51, 0xf9, 0x39, 0xa9, + 0x4e, 0xa8, 0xb9, 0x75, 0xc0, 0x65, 0xa9, 0x1f, 0xf2, 0x57, 0xca, 0xc7, 0xa9, + 0x23, 0x85, 0xfc, 0x8f, 0xa9, 0x21, 0xb1, 0x06, 0xba, 0x86, 0x60, 0xc6, 0x0a, + 0xc8, 0xba, 0x5e, 0xce, 0x45, 0x60, 0x6f, 0x04, 0xf3, 0x6a, 0x3a, 0x90, 0xbb, + 0x38, 0x38, 0xc4, 0x2a, 0xbf, 0x62, 0xdd, 0x2d, 0x84, 0xba, 0xbe, 0xf3, 0xe1, + 0x88, 0xe9, 0x17, 0x1a, 0xff, 0x9b, 0xc1, 0x16, 0x66, 0x90, 0x09, 0xd8, 0x87, + 0x13, 0x0a, 0xc9, 0xf7, 0x39, 0x6a, 0x62, 0x7a, 0x84, 0x74, 0xc1, 0x81, 0x1b, + 0x69, 0x6f, 0x99, 0x55, 0x2b, 0x14, 0xc4, 0x84, 0xdf, 0xe4, 0x2c, 0x24, 0xd5, + 0x7c, 0x3a, 0x9c, 0x3f, 0xea, 0x13, 0x76, 0xcd, 0xcb, 0x63, 0x42, 0x1c, 0x31, + 0x4a, 0x62, 0x2a, 0x9a, 0xef, 0x0b, 0xc0, 0x57, 0xcb, 0x11, 0xbc, 0x5e, 0x30, + 0x66, 0xe3, 0x3a, 0x3b, 0x9b, 0x31, 0xdf, 0x25, 0x75, 0xcd, 0x51, 0x85, 0xa4, + 0xf3, 0xfc, 0x4e, 0x4c, 0x3d, 0x40, 0x2e, 0xd4, 0x20, 0x46, 0xf8, 0x1f, 0x97, + 0x48, 0x16, 0xd2, 0x79, 0xb1, 0x51, 0x3a, 0xb8, 0x1d, 0x3f, 0x0a, 0x3c, 0x7f, + 0x7f, 0xcf, 0x2f, 0xbb, 0x4e, 0x26, 0x32, 0x19, 0x93, 0xa5, 0x13, 0xad, 0x3d, + 0x7f, 0x4a, 0xfe, 0x6c, 0x1b, 0xbd, 0xc6, 0x57, 0x58, 0x50, 0x80, 0xbb, 0x5a, + 0x0f, 0x25, 0x97, 0x3d, 0x63, 0xeb, 0x20, 0xad, 0xa0, 0x16, 0x6b, 0xbd, 0x8a, + 0x39, 0xff, 0x93, 0x24, 0x6f, 0x27, 0x89, 0x73, 0x2a, 0xd0, 0x55, 0x87, 0xf8, + 0xdb, 0x7b, 0xc8, 0x7c, 0x24, 0x2c, 0xfd, 0x36, 0xce, 0x68, 0x5a, 0x4b, 0x65, + 0x69, 0x86, 0xc3, 0x9f, 0xd7, 0xfc, 0xb2, 0x3c, 0x91, 0x91, 0x3e, 0x46, 0x11, + 0x19, 0x1e, 0xdc, 0xc8, 0x8b, 0x78, 0xf1, 0x45, 0xea, 0x29, 0xd2, 0x71, 0xb9, + 0x40, 0xc6, 0x99, 0x41, 0xe4, 0xc3, 0xfd, 0x2d, 0x71, 0xf3, 0xb1, 0x90, 0x69, + 0x0e, 0xe1, 0x6f, 0x5d, 0x14, 0xac, 0x22, 0x24, 0xe6, 0xfc, 0x89, 0x59, 0x76, + 0x54, 0x52, 0x7d, 0xab, 0xe7, 0x2e, 0x75, 0xd2, 0xd2, 0xa1, 0x3a, 0x9f, 0xba, + 0xa6, 0x37, 0x8e, 0x8a, 0x26, 0x43, 0x21, 0x08, 0x7a, 0x19, 0x00, 0xef, 0xe3, + 0xca, 0xd1, 0x4a, 0x57, 0x96, 0x86, 0xaa, 0x36, 0x36, 0xbd, 0x37, 0x5b, 0xd3, + 0x13, 0x6b, 0xee, 0x0b, 0xda, 0xab, 0xcf, 0xac, 0x88, 0x1b, 0xc7, 0x01, 0x81, + 0x27, 0x21, 0xe6, 0xfb, 0x75, 0xaa, 0x07, 0x2d, 0x2d, 0x18, 0x7e, 0x62, 0x25, + 0x8d, 0x65, 0xa1, 0x92, 0x15, 0x7c, 0xdf, 0x2e, 0xc3, 0x21, 0x40, 0x7f, 0x68, + 0x2f, 0x5e, 0xec, 0x6a, 0x32, 0x97, 0xab, 0x20, 0xb7, 0x06, 0x1c, 0x62, 0x24, + 0x57, 0x16, 0xa4, 0x4f, 0x71, 0xfb, 0xfc, 0x34, 0xc7, 0x9b, 0x44, 0xe0, 0x9e, + 0x42, 0x12, 0xac, 0x26, 0x53, 0xf6, 0xc4, 0x03, 0x64, 0x3e, 0x1c, 0x5b, 0x9a, + 0xd1, 0x34, 0xd8, 0x9c, 0x68, 0x0b, 0x70, 0x72, 0x83, 0xaf, 0x54, 0x32, 0x6f, + 0xc4, 0xf8, 0x4d, 0x6a, 0x58, 0x29, 0xa0, 0xad, 0x48, 0x30, 0x80, 0x6c, 0x05, + 0x75, 0x84, 0x92, 0xcd, 0x6a, 0xc4, 0x6b, 0xa0, 0x1a, 0x2b, 0x37, 0x22, 0xb5, + 0xe4, 0xcd, 0xaf, 0xbb, 0x3f, 0x36, 0x78, 0x5f, 0x42, 0x4a, 0xf0, 0x44, 0xda, + 0xc5, 0xdb, 0x5f, 0x7d, 0xf8, 0x39, 0xeb, 0x63, 0xc0, 0xc1, 0x7d, 0x8b, 0x0c, + 0x79, 0xdb, 0x86, 0x30, 0x94, 0x20, 0x15, 0xbe, 0x13, 0xf7, 0x9a, 0xf6, 0xf4, + 0x3e, 0x5a, 0xb0, 0x77, 0x81, 0x14, 0x79, 0x8f, 0x44, 0x22, 0x58, 0xee, 0xdc, + 0x43, 0x6f, 0xcc, 0x38, 0x6b, 0x36, 0xb5, 0x7e, 0x19, 0x17, 0xd7, 0x20, 0x17, + 0x73, 0x66, 0xf4, 0x24, 0xb0, 0xa5, 0x4b, 0x0b, 0x60, 0xf4, 0xfb, 0x13, 0x58, + 0xc2, 0x0a, 0xa4, 0x1d, 0xc5, 0x02, 0xe1, 0xdd, 0x8a, 0x16, 0x33, 0xf3, 0xd8, + 0xe3, 0x27, 0x6b, 0x59, 0xe7, 0xd2, 0xc4, 0xe6, 0x24, 0xa6, 0xf5, 0x36, 0x95, + 0xbc, 0xaf, 0x24, 0x7e, 0x36, 0x48, 0x3f, 0x13, 0xb2, 0x04, 0x42, 0x22, 0x37, + 0xfc, 0x6a, 0xb3, 0xeb, 0xa0, 0x2f, 0xc4, 0x14, 0x2b, 0x42, 0x97, 0xeb, 0xb5, + 0x68, 0x3d, 0xb8, 0xd2, 0x43, 0x19, 0x70, 0x6a, 0xd2, 0x6a, 0xaf, 0xd8, 0x1c, + 0x53, 0xb7, 0x40, 0xf3, 0x45, 0x43, 0xa6, 0xb3, 0xe9, 0xf5, 0xbb, 0x7d, 0x5c, + 0x49, 0xe8, 0xc3, 0x7f, 0x61, 0x49, 0x21, 0x25, 0x4f, 0x32, 0x12, 0x39, 0x4c, + 0x79, 0x7d, 0x1c, 0xee, 0x78, 0x99, 0xb7, 0xb4, 0xb6, 0x5b, 0x59, 0xb7, 0x34, + 0x2f, 0x92, 0x53, 0x1c, 0x1d, 0x59, 0xe1, 0x79, 0x70, 0xb7, 0x31, 0x74, 0x14, + 0x43, 0x8c, 0xd8, 0x0b, 0xd0, 0xf9, 0xa6, 0x7c, 0x9b, 0x9e, 0x55, 0x2f, 0x01, + 0x3c, 0x11, 0x5a, 0x95, 0x4f, 0x35, 0xe0, 0x61, 0x6c, 0x68, 0xd4, 0x31, 0x63, + 0xd3, 0x34, 0xda, 0xc3, 0x82, 0x70, 0x33, 0xe5, 0xad, 0x84, 0x88, 0xbf, 0xd9, + 0xc4, 0xbb, 0xbe, 0x8f, 0x59, 0x35, 0xc6, 0xc5, 0xea, 0x04, 0xc3, 0xad, 0x49, + 0xc7, 0x47, 0xa9, 0xe7, 0x23, 0x1b, 0xcd, 0x7d, 0x16, 0x21, 0x5e, 0x6e, 0x80, + 0x73, 0x7d, 0x6b, 0x54, 0xfe, 0xc8, 0xb8, 0x84, 0x02, 0xf0, 0x47, 0x52, 0x45, + 0xe1, 0x74, 0xa7, 0x45, 0xb8, 0x31, 0xf8, 0xfe, 0x03, 0xa7, 0x6f, 0xb9, 0xce, + 0xca, 0x4d, 0x22, 0xb7, 0x83, 0xc3, 0x28, 0xc6, 0x91, 0x5c, 0x43, 0x40, 0x50, + 0x64, 0xae, 0x56, 0xbc, 0x89, 0xe6, 0x4d, 0x15, 0x78, 0xe4, 0xd3, 0xa3, 0x4b, + 0xb9, 0x55, 0x91, 0xea, 0xf1, 0xd3, 0xda, 0x02, 0xa4, 0x54, 0x9f, 0xa8, 0x0d, + 0xb0, 0xff, 0x7c, 0xb0, 0x39, 0x93, 0xb6, 0x8a, 0xe1, 0x5a, 0x30, 0xe8, 0x79, + 0x49, 0xaa, 0x08, 0x0e, 0x94, 0xab, 0xde, 0x68, 0x89, 0x8c, 0x33, 0x92, 0xa2, + 0x17, 0xd6, 0x49, 0x61, 0x6b, 0xbe, 0x73, 0x9b, 0x13, 0xd1, 0x4d, 0xf0, 0x3f, + 0xf2, 0x76, 0x71, 0x48, 0x9b, 0xe0, 0xb4, 0xbe, 0xba, 0xaf, 0xa7, 0xd1, 0xe6, + 0x39, 0xd5, 0xb3, 0xe9, 0x94, 0xff, 0xb6, 0xb7, 0xa2, 0x09, 0xf6, 0xad, 0xfe, + 0x8d, 0x1e, 0x5c, 0xcf, 0x01, 0x0c, 0x19, 0x16, 0x8a, 0xeb, 0x00, 0xaa, 0x9d, + 0x68, 0x7e, 0x24, 0xad, 0xc0, 0xb1, 0x13, 0x5c, 0x70, 0xc9, 0x70, 0xe0, 0x90, + 0x3a, 0xf6, 0xe1, 0x70, 0x81, 0xd5, 0x81, 0x8e, 0x88, 0xb1, 0x4e, 0x4f, 0x60, + 0x1b, 0x8c, 0x06, 0x3e, 0x3f, 0x43, 0x87, 0xff, 0xa2, 0x32, 0x2a, 0x51, 0x81, + 0x90, 0x9f, 0x09, 0x80, 0xd6, 0x89, 0xde, 0x7f, 0x8e, 0x6a, 0x5c, 0x62, 0xa7, + 0x77, 0xd1, 0x75, 0x00, 0x2a, 0x13, 0x7d, 0xe8, 0x5b, 0x88, + ], + script_code: Script(vec![]), + transparent_input: None, + hash_type: 1, + amount: 1039204199089370, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x6c, 0x4e, 0x32, 0x44, 0xc2, 0xd2, 0xbf, 0xb8, 0xd6, 0xf6, 0x69, 0x97, 0x77, + 0xa1, 0x1a, 0x64, 0xad, 0xfe, 0xe4, 0x9b, 0x2f, 0xc7, 0x81, 0xe6, 0x95, 0x15, + 0x34, 0xf9, 0x73, 0x44, 0x0d, 0xdb, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xdc, 0xf7, 0x58, 0x76, + 0xdc, 0xa6, 0x09, 0xf9, 0xd2, 0x84, 0x71, 0xf9, 0x97, 0xfa, 0x11, 0xf9, 0x9d, + 0x42, 0x3f, 0x9c, 0xf1, 0x73, 0x4b, 0xe8, 0xa5, 0xff, 0x99, 0x7d, 0x45, 0x1e, + 0xb3, 0xcf, 0x4b, 0x3d, 0xfd, 0xd9, 0x06, 0xac, 0xac, 0x63, 0x52, 0x63, 0x6a, + 0xdc, 0x17, 0xa8, 0x36, 0xb1, 0x2b, 0x43, 0xbe, 0xfc, 0x0b, 0xe0, 0xa1, 0xbd, + 0x36, 0x97, 0x72, 0x33, 0x80, 0x78, 0xb4, 0xff, 0x7d, 0x8e, 0x2d, 0x97, 0x9a, + 0x34, 0x41, 0xe1, 0xc8, 0xf5, 0xaf, 0xe4, 0x7b, 0x1e, 0x7d, 0xa5, 0x6c, 0xf0, + 0x06, 0x02, 0x00, 0x53, 0x11, 0x0c, 0x05, 0xcf, 0x00, 0xfd, 0xa3, 0xe6, 0xcc, + 0xe3, 0x60, 0x69, 0x04, 0x1f, 0xaf, 0xfd, 0x2f, 0x77, 0xff, 0x06, 0x00, 0x02, + 0xef, 0x12, 0xc3, 0x67, 0xf2, 0x1d, 0xea, 0x65, 0xc6, 0xea, 0xaf, 0xb8, 0xaf, + 0x58, 0x42, 0x8f, 0x6c, 0x54, 0x8e, 0x50, 0x17, 0x0f, 0x9e, 0x6f, 0xcd, 0xdf, + 0xe7, 0x51, 0xe0, 0xb6, 0x80, 0x12, 0xcb, 0x59, 0xdd, 0x46, 0x27, 0xef, 0xc3, + 0xea, 0x75, 0xdc, 0xd1, 0x5c, 0x8e, 0x0c, 0x3b, 0x8d, 0x8d, 0x7d, 0x6b, 0x23, + 0x31, 0xc8, 0xe4, 0x80, 0x16, 0x6b, 0x5a, 0xa7, 0x48, 0x5c, 0x9f, 0x0f, 0x83, + 0xe1, 0x9b, 0xc3, 0x0e, 0x64, 0x03, 0x82, 0x8c, 0xdb, 0x65, 0x2a, 0x55, 0x6b, + 0x12, 0x04, 0x09, 0x31, 0x40, 0x2a, 0xa6, 0xac, 0x34, 0xfc, 0x19, 0xfd, 0xc0, + 0x6e, 0x2e, 0x77, 0x87, 0xf5, 0x58, 0xd1, 0x42, 0xd9, 0x06, 0xea, 0xdb, 0x75, + 0x90, 0xc9, 0x41, 0x36, 0xda, 0x6a, 0x06, 0x35, 0x14, 0xd6, 0xa2, 0x5f, 0x7b, + 0x37, 0xd7, 0x66, 0x4f, 0x9b, 0x97, 0x09, 0x43, 0x3e, 0x6e, 0x70, 0x21, 0x18, + 0xa4, 0xab, 0x9e, 0x7a, 0x7a, 0x3e, 0x62, 0x59, 0x12, 0x99, 0x37, 0xd2, 0x9d, + 0x0d, 0xb2, 0x60, 0x70, 0x52, 0x3e, 0x8b, 0x06, 0x43, 0x13, 0x0a, 0xbe, 0xfe, + 0x94, 0x3b, 0x40, 0x12, 0x98, 0xae, 0x01, 0xa3, 0xab, 0x00, 0xab, 0xbc, 0x60, + 0xd7, 0xdb, 0x93, 0x3c, 0x7f, 0x07, 0xa8, 0xbf, 0x0f, 0x7c, 0xe1, 0x66, 0x0b, + 0xcc, 0xb4, 0x5e, 0x04, 0x2b, 0x45, 0x1b, 0x93, 0x50, 0x02, 0xce, 0xce, 0x27, + 0xf3, 0x6a, 0xba, 0x56, 0x47, 0xac, 0x28, 0xd8, 0x18, 0x6c, 0xdd, 0x1f, 0xb9, + 0x5d, 0xc1, 0x35, 0xd4, 0x89, 0x92, 0xf6, 0x8d, 0xa1, 0x2a, 0xd6, 0x1a, 0xc7, + 0x56, 0x68, 0x0d, 0xd7, 0xf8, 0xd0, 0x77, 0x4a, 0xbd, 0x6c, 0xfd, 0xa2, 0xf0, + 0x32, 0xaf, 0x3b, 0xe1, 0x39, 0xa6, 0x33, 0xd6, 0x73, 0x3c, 0x75, 0xd1, 0xab, + 0xa8, 0x90, 0x18, 0xc8, 0x57, 0x2b, 0x99, 0xcd, 0x30, 0xc5, 0x37, 0x06, 0x79, + 0x41, 0xdf, 0x1c, 0x4b, 0xc1, 0xfd, 0x57, 0x0f, 0x7b, 0x4d, 0xdc, 0x97, 0x51, + 0x86, 0x23, 0xe3, 0xae, 0x4a, 0x87, 0xbd, 0xb9, 0x66, 0xc9, 0x4d, 0x86, 0x1e, + 0x80, 0xde, 0x88, 0xc2, 0x92, 0xae, 0xe9, 0x38, 0x71, 0x94, 0xe2, 0x56, 0xc6, + 0x70, 0x07, 0x52, 0x30, 0x1c, 0x73, 0xfc, 0x95, 0x65, 0xa4, 0x04, 0x80, 0xd8, + 0x12, 0x6e, 0x9d, 0x08, 0x58, 0x79, 0xe2, 0x4b, 0x16, 0xe9, 0xc4, 0x85, 0xd8, + 0xf0, 0xd6, 0x18, 0xca, 0x0d, 0xd1, 0x21, 0xb5, 0x1a, 0x7c, 0xab, 0x23, 0x0c, + 0x5b, 0x45, 0x67, 0x2b, 0xdb, 0x8e, 0xa3, 0xa0, 0x40, 0xf7, 0xaa, 0xa0, 0x98, + 0xba, 0x26, 0x02, 0x5d, 0x2e, 0xab, 0x79, 0x48, 0x69, 0x3d, 0xd5, 0xf6, 0xd3, + 0x09, 0x65, 0x01, 0xe9, 0xe0, 0x71, 0x25, 0xd7, 0xeb, 0x29, 0x3b, 0x3a, 0xba, + 0xd5, 0x7f, 0xd5, 0xf0, 0x11, 0x64, 0x70, 0x02, 0xd6, 0x26, 0xae, 0x88, 0xdc, + 0x61, 0xe6, 0x47, 0xff, 0x46, 0x8d, 0xfa, 0x7a, 0x03, 0x07, 0x72, 0x78, 0x79, + 0x32, 0x75, 0xf1, 0x95, 0xa9, 0x75, 0x30, 0x28, 0x91, 0x78, 0x51, 0x61, 0x80, + 0xc5, 0xff, 0x99, 0x93, 0x53, 0x6b, 0xda, 0x15, 0x04, 0xba, 0x8b, 0xb4, 0x89, + 0x19, 0x88, 0xc1, 0x33, 0x4f, 0x31, 0xfb, 0x27, 0x6a, 0x03, 0x8a, 0xa8, 0xe9, + 0x67, 0xcb, 0x62, 0xa4, 0x92, 0x1b, 0xeb, 0x22, 0xb2, 0x08, 0xb0, 0x64, 0x58, + 0x18, 0x47, 0xb2, 0xf6, 0x4c, 0xa6, 0x48, 0x37, 0x00, 0x72, 0x16, 0xde, 0x6e, + 0xca, 0xff, 0xeb, 0x4b, 0x69, 0xe6, 0x33, 0x47, 0xf8, 0x4a, 0xbc, 0xad, 0x8f, + 0x2e, 0x75, 0x7d, 0x58, 0x61, 0xce, 0x77, 0xee, 0x46, 0x51, 0x3d, 0xa7, 0x41, + 0x68, 0x37, 0xdc, 0xb2, 0x3d, 0x33, 0xea, 0x72, 0xaf, 0x23, 0xd0, 0xad, 0x8c, + 0x93, 0x07, 0xd0, 0xb5, 0x85, 0x8d, 0xa9, 0x5b, 0x77, 0xff, 0xf9, 0x02, 0x7b, + 0x88, 0x59, 0xe1, 0x1d, 0xcb, 0xd5, 0x98, 0x35, 0x0e, 0xee, 0x50, 0x93, 0x94, + 0x81, 0x70, 0x8e, 0xa7, 0x08, 0xeb, 0x9f, 0x66, 0x43, 0x88, 0xb9, 0xc6, 0x4d, + 0x6a, 0xf0, 0xf9, 0x66, 0x90, 0x34, 0x24, 0x00, 0x34, 0x8e, 0x92, 0x9e, 0x07, + 0x46, 0x02, 0x53, 0xf3, 0x83, 0x90, 0xf8, 0x7b, 0xd6, 0xc0, 0x53, 0x08, 0xc3, + 0xbd, 0xe2, 0x52, 0x28, 0xe0, 0xfa, 0x08, 0x80, 0xb0, 0x8e, 0xf3, 0x4a, 0x5a, + 0x9c, 0xc0, 0xea, 0x0a, 0x67, 0xca, 0x65, 0xb6, 0xff, 0xd0, 0x05, 0x57, 0x29, + 0x09, 0xf1, 0xc4, 0x2d, 0xd7, 0x45, 0xee, 0xee, 0x9d, 0xd6, 0xb4, 0x43, 0x9c, + 0x9f, 0x3f, 0x98, 0xa1, 0x18, 0xfe, 0x16, 0x69, 0x8e, 0x9c, 0xef, 0xf5, 0x58, + 0xf1, 0x60, 0x66, 0x97, 0x5f, 0xe3, 0x95, 0x83, 0xe9, 0xb5, 0x85, 0x3b, 0x13, + 0x11, 0x39, 0x15, 0x80, 0x01, 0x9f, 0xe5, 0x5d, 0x59, 0xd1, 0xc8, 0x28, 0xd3, + 0xfe, 0xb6, 0xa3, 0xb9, 0xce, 0x92, 0xd0, 0x89, 0xae, 0x4b, 0x40, 0x8e, 0x23, + 0xd6, 0xa4, 0x37, 0xd4, 0x98, 0x9b, 0x51, 0x9b, 0x7a, 0x9e, 0xb0, 0x8a, 0xe6, + 0xd4, 0x48, 0xa7, 0xa1, 0x6e, 0x8a, 0xed, 0x26, 0xa2, 0xec, 0xd0, 0xca, 0xd8, + 0x08, 0x44, 0xfd, 0x06, 0x50, 0xd8, 0xc4, 0xe4, 0xd2, 0xaf, 0x90, 0x65, 0x67, + 0x48, 0xd8, 0x09, 0x9a, 0x0c, 0x75, 0x6f, 0xc1, 0x6c, 0xca, 0x06, 0xa3, 0x34, + 0x43, 0x07, 0x02, 0xae, 0x19, 0x61, 0x66, 0x5b, 0x48, 0x45, 0xac, 0xd1, 0xa8, + 0xe3, 0x41, 0x01, 0xe6, 0x8b, 0xb6, 0x44, 0xac, 0x03, 0x4d, 0xc6, 0x3e, 0x6e, + 0x34, 0x4c, 0x3d, 0x63, 0x76, 0x2a, 0x7a, 0x5b, 0xf5, 0x9f, 0x13, 0x09, 0x54, + 0x10, 0x98, 0x1d, 0x6b, 0x6b, 0x16, 0xbc, 0xd4, 0xc9, 0xfa, 0x68, 0xaf, 0x6e, + 0x53, 0x01, 0xef, 0x19, 0xbf, 0x3a, 0x43, 0x2e, 0x40, 0x6f, 0x85, 0x67, 0xeb, + 0xd9, 0x77, 0x2e, 0x92, 0xb5, 0xca, 0x5a, 0x59, 0x96, 0x71, 0xcb, 0xfd, 0x7d, + 0xdf, 0xa3, 0x63, 0xa5, 0x36, 0xb7, 0xac, 0x45, 0xf5, 0x7c, 0xc3, 0x7d, 0x09, + 0x89, 0x6f, 0xa9, 0x06, 0x97, 0x2e, 0x55, 0x71, 0x80, 0xa4, 0xab, 0x5a, 0xd0, + 0x9d, 0x88, 0x46, 0xdd, 0x6d, 0xa7, 0x48, 0x76, 0x54, 0x36, 0xe0, 0x16, 0x02, + 0x40, 0xbd, 0x5c, 0x92, 0x16, 0x66, 0xa1, 0xee, 0xaa, 0xce, 0x04, 0xa7, 0x1b, + 0x50, 0x3a, 0x1c, 0xad, 0xf8, 0x0b, 0x39, 0x24, 0x26, 0x6c, 0x59, 0x50, 0x4f, + 0x8f, 0x21, 0x5f, 0x61, 0x8b, 0x05, 0xd5, 0x45, 0x43, 0xb6, 0xe2, 0x6d, 0x82, + 0x59, 0x6f, 0xc5, 0x3b, 0x52, 0x31, 0x2c, 0x77, 0x6d, 0x12, 0xeb, 0x2b, 0x65, + 0x9b, 0x4f, 0xb0, 0x98, 0xdf, 0x87, 0xd6, 0x83, 0xcf, 0x9e, 0x54, 0x12, 0xee, + 0x56, 0xc3, 0xfe, 0x98, 0x41, 0xd7, 0x3f, 0xd0, 0x70, 0xdf, 0xa5, 0x1f, 0x5b, + 0xaf, 0xed, 0xf2, 0x06, 0xf1, 0x3c, 0x52, 0x4e, 0x5c, 0x50, 0xca, 0xc9, 0x90, + 0x6e, 0xfa, 0x39, 0x32, 0x90, 0x04, 0x2e, 0x3b, 0xc5, 0x9f, 0x96, 0x0b, 0x7d, + 0x24, 0x0a, 0xe4, 0x43, 0xfc, 0x49, 0x26, 0x9c, 0xe0, 0x00, 0x61, 0xe6, 0x5c, + 0x6d, 0x74, 0x81, 0x2a, 0x30, 0xdd, 0x5f, 0x5f, 0xe7, 0x4e, 0xff, 0x61, 0xe0, + 0xcb, 0xab, 0x3c, 0xec, 0x75, 0xd0, 0xae, 0xf9, 0x50, 0x83, 0x18, 0x94, 0x52, + 0xdd, 0x3d, 0x9e, 0xdf, 0x44, 0x87, 0xbc, 0x73, 0x4c, 0x8b, 0x24, 0xf2, 0x12, + 0x96, 0xe4, 0xe9, 0xef, 0x11, 0x7d, 0x7f, 0xb9, 0x77, 0xe3, 0xb0, 0xe6, 0x40, + 0x6e, 0x63, 0x08, 0x59, 0x06, 0x33, 0x1a, 0x93, 0x03, 0x3d, 0x1c, 0xb8, 0x36, + 0x0f, 0xe6, 0xfe, 0xa6, 0x1a, 0x68, 0x26, 0xdf, 0x36, 0x25, 0x57, 0x89, 0xf9, + 0x2e, 0x40, 0xba, 0xfc, 0xb2, 0xeb, 0xcb, 0x9e, 0x55, 0x6f, 0x6c, 0x0c, 0xca, + 0xdc, 0x6a, 0xf0, 0x8e, 0x31, 0xec, 0x4a, 0xd5, 0x28, 0x80, 0x34, 0xe1, 0x6d, + 0x15, 0x5c, 0xfd, 0xca, 0xda, 0x7b, 0xab, 0x59, 0x9c, 0x2f, 0xa4, 0xad, 0x2e, + 0x62, 0x93, 0xf9, 0xfe, 0x09, 0x71, 0x69, 0x14, 0x82, 0x76, 0xb6, 0xa9, 0xea, + 0xa7, 0x2f, 0x14, 0x8b, 0x0c, 0x95, 0x65, 0xc3, 0xc2, 0xdd, 0x63, 0x12, 0x5e, + 0x0f, 0xa5, 0x30, 0x86, 0x1a, 0x71, 0x0d, 0xf8, 0xe4, 0x81, 0xf2, 0x71, 0x29, + 0x20, 0xf8, 0x78, 0x7e, 0x0a, 0xed, 0xfe, 0x61, 0x8a, 0xff, 0x50, 0xa3, 0xb5, + 0x62, 0x13, 0x88, 0x4d, 0x62, 0x62, 0xc1, 0x1d, 0xeb, 0xf2, 0xba, 0x7e, 0x8a, + 0xd6, 0x69, 0x2c, 0xb1, 0x70, 0x78, 0x33, 0x14, 0x18, 0xda, 0x4b, 0xe0, 0x64, + 0xff, 0x52, 0x70, 0x07, 0x39, 0x34, 0xab, 0xcd, 0x2a, 0xb0, 0x46, 0x9e, 0xca, + 0xf7, 0x27, 0x5b, 0x4b, 0xd7, 0x2b, 0xc6, 0xed, 0x34, 0x47, 0x8e, 0xa4, 0x08, + 0x9b, 0x73, 0x6a, 0x16, 0xdd, 0x90, 0x6d, 0x49, 0xf2, 0x5c, 0x33, 0x82, 0x7c, + 0x57, 0x1c, 0xe0, 0xb5, 0xd7, 0x21, 0x77, 0xaa, 0x35, 0x08, 0x80, 0x4b, 0xc0, + 0xf8, 0xfa, 0xa9, 0x47, 0x12, 0x22, 0x31, 0x40, 0x2d, 0x2f, 0x5c, 0xc9, 0xa0, + 0xeb, 0x0e, 0x09, 0xd4, 0x27, 0xb4, 0x27, 0x28, 0x8d, 0x93, 0x7d, 0x9d, 0x72, + 0xb7, 0x74, 0x56, 0xf8, 0x86, 0x59, 0x4c, 0xd8, 0xc6, 0xa4, 0x62, 0xf7, 0x7f, + 0xd8, 0x30, 0x76, 0x46, 0x9c, 0xc0, 0xec, 0xba, 0x3c, 0xc4, 0x0c, 0xad, 0x69, + 0xe5, 0xb5, 0x41, 0x12, 0xea, 0xb3, 0x33, 0x96, 0xae, 0xcf, 0xbc, 0x21, 0x1f, + 0x1f, 0x79, 0xcf, 0x33, 0x10, 0x8e, 0x93, 0xd9, 0x53, 0x78, 0xba, 0xe6, 0x95, + 0x82, 0x74, 0xb3, 0x10, 0x88, 0xfb, 0xd8, 0xb3, 0xa3, 0xa0, 0xd1, 0x54, 0xa7, + 0x89, 0x73, 0x5b, 0x03, 0x49, 0xc4, 0xd5, 0x1c, 0x88, 0x9d, 0x08, 0x95, 0x2d, + 0xdd, 0x54, 0x88, 0xbe, 0x95, 0x56, 0x05, 0x94, 0xe6, 0x73, 0xfa, 0x05, 0x1b, + 0xf9, 0xb6, 0x14, 0xa1, 0x5e, 0x10, 0x0b, 0x60, 0xa0, 0xfe, 0x9a, 0x7e, 0x12, + 0xa9, 0xb2, 0x56, 0xdf, 0x58, 0x9b, 0x3e, 0x48, 0xe5, 0xb8, 0x0f, 0xb8, 0xcf, + 0xf0, 0x3e, 0x86, 0xf6, 0x0c, 0xc0, 0x70, 0xfb, 0x23, 0xc9, 0x7d, 0x4c, 0x14, + 0xfa, 0x3a, 0x73, 0x46, 0xff, 0x55, 0x6b, 0xc6, 0x85, 0x5a, 0x5f, 0x83, 0xe3, + 0xdc, 0xd9, 0xf6, 0xea, 0xb3, 0xda, 0xbc, 0xd4, 0x77, 0x50, 0xe3, 0x4e, 0x7c, + 0x09, 0x38, 0xf6, 0x4d, 0x45, 0x1e, 0x39, 0x50, 0x9e, 0x90, 0x27, 0x47, 0xa7, + 0x07, 0x55, 0x12, 0x20, 0x95, 0x08, 0x2a, 0xb7, 0x98, 0x59, 0x19, 0x07, 0x31, + 0x41, 0xb6, 0xd3, 0x70, 0x20, 0x91, 0xab, 0x71, 0x72, 0x80, 0xbd, 0xc5, 0x5e, + 0x79, 0x9c, 0x01, 0xad, 0x86, 0x41, 0x90, 0x4e, 0x3b, 0x1d, 0xd2, 0x9e, 0x1a, + 0x96, 0x4c, 0x73, 0x7d, 0x3c, 0x15, 0x5a, 0xfb, 0x30, 0x7b, 0x74, 0x8e, 0x41, + 0x12, 0xb4, 0x8b, 0x77, 0xd5, 0xed, 0x57, 0x00, 0xe6, 0x00, 0x2b, 0x18, 0xb0, + 0xfe, 0xd2, 0xcf, 0xfd, 0xf6, 0x1f, 0xd9, 0x93, 0x4b, 0x60, 0x73, 0x2f, 0x4d, + 0x37, 0x81, 0x0a, 0x91, 0xac, 0xef, 0x1e, 0x03, 0x8b, 0x81, 0xd7, 0x36, 0xd9, + 0x8e, 0xad, 0xa9, 0xcd, 0x7e, 0x0c, 0x2b, 0xe2, 0x7a, 0xb8, 0x50, 0x32, 0x06, + 0x60, 0x91, 0x22, 0x4e, 0xdf, 0x87, 0x2f, 0x79, 0x63, 0x7d, 0xda, 0x39, 0x16, + 0x79, 0x6a, 0x5c, 0x62, 0xf5, 0x7f, 0x1d, 0xe3, 0x76, 0x78, 0xb6, 0xde, 0xa0, + 0x08, 0x69, 0x93, 0x36, 0x74, 0xf8, 0x8e, 0x41, 0xa9, 0x18, 0x08, 0x07, 0x3b, + 0x0f, 0x43, 0x6e, 0xbe, 0x25, 0xa5, 0xf4, 0x4a, 0x60, 0x10, 0x33, 0xe2, 0x18, + 0x4b, 0x88, 0xdb, 0x79, 0xe9, 0x68, 0xca, 0x6d, 0x89, 0xb7, 0x49, 0x01, 0xbe, + 0x6c, 0x6d, 0xb3, 0x63, 0x65, 0x80, 0x18, 0x2e, 0x65, 0x8d, 0xfc, 0x68, 0x67, + 0x67, 0xd6, 0xd8, 0x19, 0xfa, 0x92, 0x3e, 0x0c, 0xdf, 0x3e, 0xa3, 0x65, 0x76, + 0xf8, 0x52, 0xbc, 0xd4, 0xe1, 0x96, 0xa7, 0x1a, 0x13, 0x29, 0xf6, 0xc3, 0xff, + 0x8e, 0x42, 0xe3, 0x09, 0x5a, 0xbd, 0x8e, 0xc1, 0x97, 0x99, 0x07, 0x13, 0xee, + 0x89, 0x39, 0x4c, 0x57, 0x19, 0xb2, 0x76, 0xde, 0x8f, 0x81, 0x8a, 0x34, 0xa7, + 0xbe, 0xc1, 0xf2, 0x68, 0x68, 0x2e, 0x91, 0x42, 0xc7, 0xd3, 0x87, 0x89, 0xf6, + 0x76, 0xcc, 0x12, 0xb7, 0x1a, 0xb6, 0x66, 0x35, 0xc5, 0x02, 0xe6, 0x9d, 0x05, + 0xb9, 0xc7, 0xef, 0x01, 0x52, 0x97, 0x75, 0xc6, 0x23, 0xa4, 0x8e, 0x4c, 0xc5, + 0xc4, 0x15, 0xc9, 0xfd, 0x56, 0x53, 0x65, 0xa4, 0x16, 0x37, 0x68, 0x78, 0x51, + 0x53, 0x88, 0x7f, 0xb5, 0xf9, 0x63, 0xe7, 0xac, 0xc1, 0x62, 0xf2, 0x80, 0x5f, + 0x45, 0xf4, 0x44, 0x87, 0xf8, 0x5e, 0x19, 0x9c, 0x1d, 0xf4, 0xa0, 0xfc, 0xa4, + 0xd4, 0x4b, 0xaa, 0x62, 0xda, 0x7a, 0xf5, 0xed, 0x69, 0x68, 0x41, 0x12, 0xd3, + 0x5f, 0x00, 0x73, 0x73, 0x2f, 0x5a, 0x1a, 0xc3, 0xe4, 0xf0, 0x21, 0xba, 0x5c, + 0x2c, 0x32, 0xf0, 0x6e, 0x6b, 0x90, 0xfa, 0xe2, 0xd2, 0x54, 0xcf, 0x09, 0xe7, + 0x69, 0x0c, 0xf4, 0xe3, 0xaa, 0x70, 0x30, 0x98, 0x74, 0x48, 0xe1, 0x47, 0xf9, + 0x43, 0xba, 0xb5, 0xca, 0xb5, 0x58, 0x02, 0x9a, 0x36, 0x02, 0x4d, 0x2e, 0x79, + 0x0f, 0xc6, 0xfd, 0x66, 0x7f, 0x17, 0x6e, 0x0a, 0xa9, 0x9d, 0xd1, 0xd7, 0x2b, + 0x57, + ], + script_code: Script(vec![0x6a, 0x51, 0x65, 0xac]), + transparent_input: None, + hash_type: 1, + amount: 691732482992802, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x5d, 0x40, 0x5a, 0x1c, 0x4d, 0xed, 0x19, 0x87, 0x98, 0x8a, 0x10, 0x03, 0x64, + 0xa3, 0xcd, 0x6f, 0xe0, 0xba, 0x22, 0x20, 0xa6, 0xab, 0xce, 0x08, 0xc5, 0x17, + 0x13, 0x59, 0x55, 0x30, 0x65, 0xe9, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x01, 0xa4, 0x96, 0x69, 0x60, + 0x21, 0x82, 0x08, 0x46, 0x69, 0x61, 0x12, 0x94, 0x90, 0xa7, 0xd8, 0xb6, 0x5c, + 0x14, 0x70, 0xba, 0xd8, 0xdb, 0x08, 0x28, 0xef, 0x06, 0xc1, 0xcb, 0x55, 0x70, + 0x0e, 0x85, 0xe2, 0x4f, 0xde, 0xa9, 0x08, 0x52, 0x00, 0x65, 0x63, 0x52, 0x51, + 0xac, 0x51, 0x87, 0x1f, 0x88, 0xfb, 0x02, 0x57, 0x2c, 0x4f, 0x50, 0xa0, 0xf8, + 0x01, 0x00, 0x06, 0x63, 0x00, 0x63, 0x63, 0x51, 0xac, 0xcb, 0x37, 0x9c, 0x68, + 0xc8, 0x7d, 0x04, 0x00, 0x03, 0x00, 0x6a, 0x63, 0x53, 0x3c, 0x92, 0xcf, 0x4c, + 0x1c, 0xac, 0x18, 0x99, 0x41, 0x99, 0xa8, 0xec, 0x8e, 0x01, 0x00, 0x04, 0x1b, + 0x31, 0xeb, 0xfb, 0xf8, 0x18, 0xa3, 0x99, 0x2b, 0xf3, 0x68, 0xc2, 0x4e, 0x9a, + 0xcc, 0x83, 0x14, 0x2b, 0x24, 0x0f, 0xec, 0x55, 0x4c, 0xed, 0xa1, 0xd3, 0xfc, + 0x04, 0x32, 0xc5, 0x72, 0x51, 0x34, 0x19, 0xaf, 0x1d, 0xe6, 0x56, 0xfd, 0xd0, + 0x39, 0x07, 0x22, 0xa7, 0xf4, 0x6a, 0x1f, 0xc0, 0x56, 0x3f, 0x0a, 0xda, 0xb8, + 0xbc, 0xbb, 0xb0, 0xd1, 0xb2, 0x29, 0xf5, 0xa5, 0xb9, 0x23, 0x03, 0x77, 0x5a, + 0x90, 0x4d, 0xec, 0x82, 0x7f, 0xd8, 0x7a, 0x18, 0x86, 0x0d, 0x6e, 0x8a, 0x4a, + 0x52, 0xb5, 0xcf, 0x44, 0xbe, 0x28, 0xa6, 0x2d, 0x41, 0x59, 0x02, 0x09, 0x3a, + 0x0c, 0x36, 0x5d, 0x29, 0x24, 0x12, 0x01, 0xb8, 0x26, 0x1a, 0x49, 0xd4, 0x91, + 0xaf, 0x04, 0x9b, 0x39, 0xe2, 0x6d, 0x13, 0x57, 0xc3, 0x06, 0x92, 0x64, 0x16, + 0x77, 0x6d, 0x7d, 0x13, 0xf8, 0x40, 0xbd, 0x82, 0xac, 0xa0, 0x1c, 0x83, 0x1c, + 0x98, 0x3f, 0x19, 0x85, 0xee, 0x0a, 0xda, 0xe8, 0xdb, 0x84, 0x47, 0xc0, 0xe5, + 0x1c, 0x09, 0xdf, 0xe3, 0xde, 0xe3, 0x88, 0x0a, 0x97, 0x13, 0xce, 0xb7, 0x45, + 0xab, 0xfd, 0xd9, 0xf1, 0xc7, 0xea, 0xd7, 0x63, 0x08, 0xcd, 0xee, 0xa2, 0x1c, + 0x8b, 0x09, 0x57, 0x02, 0x7c, 0x5d, 0x00, 0xe5, 0x0a, 0x43, 0x88, 0xc7, 0xaf, + 0x2b, 0xd6, 0x43, 0xcb, 0x5e, 0xae, 0x49, 0x27, 0x4d, 0x12, 0x30, 0xa4, 0xcd, + 0x49, 0x23, 0x7a, 0xe3, 0x7b, 0x38, 0x10, 0xc2, 0xc3, 0x95, 0x8a, 0x7d, 0xee, + 0x02, 0x34, 0x30, 0x1b, 0x89, 0xa2, 0xdf, 0x2a, 0x78, 0xef, 0x0b, 0xfb, 0x4b, + 0xf6, 0xb3, 0x87, 0xdf, 0x2c, 0x6c, 0x86, 0xe6, 0x1c, 0xd1, 0x0c, 0xa1, 0x1f, + 0x81, 0x13, 0x01, 0x26, 0x07, 0xf1, 0x5b, 0x28, 0x56, 0x24, 0x0f, 0xdc, 0x52, + 0x06, 0x5a, 0x10, 0x28, 0xc8, 0xa2, 0xdd, 0xfd, 0xd1, 0x5c, 0xf5, 0x26, 0x5f, + 0x87, 0x38, 0x8a, 0xb9, 0xbf, 0x21, 0xc9, 0xa7, 0x8c, 0x59, 0x03, 0x8a, 0x98, + 0xab, 0x64, 0xfd, 0x67, 0x10, 0x77, 0xd4, 0x72, 0xc2, 0x09, 0xdd, 0x72, 0x9b, + 0xd7, 0xf8, 0x48, 0x09, 0x45, 0xfb, 0xa7, 0x52, 0x09, 0x8a, 0x94, 0xcc, 0xb2, + 0x4c, 0xf3, 0xbc, 0x09, 0x2d, 0x42, 0x36, 0x46, 0x11, 0xa2, 0x93, 0xaf, 0xf3, + 0xc5, 0x79, 0x37, 0x2c, 0x12, 0xe1, 0x50, 0x90, 0xaa, 0x27, 0x23, 0x20, 0x57, + 0xf2, 0xed, 0xde, 0x4e, 0x1d, 0xb2, 0x92, 0xf7, 0xb1, 0x86, 0x47, 0x22, 0x67, + 0x35, 0x17, 0x6d, 0x90, 0xf1, 0x26, 0x5b, 0x37, 0x98, 0xcc, 0xab, 0xac, 0x0b, + 0x8d, 0x79, 0xb1, 0x77, 0x20, 0xb2, 0xba, 0x71, 0xd7, 0x85, 0x0c, 0xc2, 0xa0, + 0x87, 0x2b, 0xf0, 0xf4, 0xb8, 0x14, 0x36, 0x78, 0x59, 0xf8, 0x99, 0x48, 0xf0, + 0xa1, 0xa3, 0x83, 0x60, 0x4b, 0x9e, 0x1a, 0xa4, 0xc7, 0xea, 0x28, 0x92, 0x05, + 0x6f, 0x81, 0x28, 0x5b, 0xc2, 0x6f, 0x30, 0x08, 0x5d, 0xd0, 0xef, 0x3b, 0x14, + 0xd1, 0x7d, 0xda, 0x57, 0x30, 0x6a, 0xe4, 0xf6, 0x6c, 0x45, 0x9a, 0xee, 0x8a, + 0x4e, 0xd9, 0x02, 0xc6, 0x6e, 0x49, 0x18, 0xfa, 0xee, 0x8d, 0xc0, 0x06, 0x72, + 0x46, 0x96, 0x0d, 0xb1, 0xf8, 0xcd, 0x07, 0xbf, 0x90, 0xd7, 0x53, 0x7c, 0xc2, + 0x7b, 0xbb, 0x8c, 0x9d, 0x5b, 0x29, 0x62, 0xc4, 0x7e, 0xd1, 0x82, 0xa2, 0xfc, + 0xe0, 0x5f, 0x8e, 0x03, 0xc4, 0xe2, 0x5e, 0x49, 0x6d, 0xd5, 0x7d, 0x6a, 0xb3, + 0x45, 0x8f, 0xac, 0xbd, 0x91, 0xea, 0x22, 0x72, 0xff, 0xda, 0x47, 0xbf, 0xd0, + 0x17, 0x39, 0x20, 0xd7, 0x17, 0x51, 0x30, 0xf0, 0xe4, 0xd0, 0x93, 0x74, 0x41, + 0xbc, 0xe9, 0x8c, 0xfa, 0x5b, 0x33, 0x3b, 0x66, 0x19, 0x0f, 0x2b, 0x44, 0x71, + 0x38, 0xe8, 0xc2, 0x6d, 0x84, 0x12, 0xca, 0xc8, 0x20, 0x86, 0xd6, 0x1b, 0x5d, + 0x2c, 0x8c, 0xf0, 0xbb, 0xeb, 0xac, 0x5b, 0x89, 0xbf, 0xe8, 0x2b, 0x58, 0x91, + 0x76, 0x64, 0xba, 0xb9, 0x1c, 0xe2, 0xec, 0xe2, 0x90, 0xb2, 0x7b, 0x60, 0x52, + 0xd4, 0xbf, 0x99, 0x1a, 0x33, 0xf4, 0x58, 0x1a, 0x63, 0x36, 0x25, 0x78, 0x79, + 0x58, 0x89, 0x7f, 0xca, 0x4b, 0x98, 0xb7, 0xe7, 0x27, 0x7c, 0x5e, 0x6a, 0x1d, + 0x88, 0x59, 0x48, 0xc9, 0xd4, 0x84, 0xdd, 0x0c, 0xef, 0xef, 0x85, 0x4e, 0x81, + 0x76, 0xc3, 0x97, 0xdc, 0xfa, 0x77, 0x2e, 0x71, 0x14, 0x72, 0xe7, 0x90, 0xba, + 0x8d, 0x39, 0x35, 0xd5, 0x7c, 0xa3, 0x13, 0x49, 0x37, 0x9e, 0x62, 0x83, 0xa6, + 0xaa, 0x8f, 0xc9, 0x91, 0xef, 0xc7, 0xd3, 0xb7, 0xef, 0x66, 0xb9, 0x2f, 0xe0, + 0x9d, 0x35, 0x16, 0x27, 0x0a, 0xe1, 0x9a, 0x99, 0x92, 0x16, 0xee, 0xae, 0x16, + 0x21, 0x44, 0xac, 0xea, 0x56, 0x0d, 0x17, 0x72, 0x05, 0xf2, 0x6c, 0x97, 0x03, + 0xb5, 0x4e, 0x80, 0xaf, 0x1a, 0x87, 0x94, 0xd6, 0xd3, 0xf1, 0xc5, 0xee, 0xad, + 0x22, 0x0b, 0x11, 0x9f, 0x06, 0xb2, 0x00, 0x98, 0x6c, 0x91, 0x21, 0x32, 0xcb, + 0x08, 0xa9, 0x8e, 0x0f, 0xee, 0x35, 0xe7, 0xf7, 0x7f, 0xc8, 0x52, 0x1d, 0x38, + 0x77, 0x3e, 0x61, 0x4e, 0xee, 0xb8, 0xa3, 0xea, 0xd8, 0x6a, 0x02, 0x48, 0x32, + 0xe6, 0x4a, 0x4c, 0x75, 0x72, 0x0c, 0xdc, 0xdd, 0xf9, 0xd0, 0x77, 0x09, 0xa1, + 0x68, 0xd0, 0x10, 0x12, 0xc2, 0xe4, 0xf3, 0x34, 0x30, 0xf2, 0x99, 0x70, 0xc6, + 0x0b, 0xe8, 0xc5, 0xe2, 0xc8, 0xcc, 0x8a, 0x86, 0xed, 0xcd, 0x51, 0x2d, 0xa7, + 0x0d, 0xd7, 0xbb, 0x40, 0xe2, 0x7b, 0x32, 0xdf, 0x3d, 0x77, 0x6a, 0x4a, 0x7b, + 0x00, 0xe3, 0xbd, 0x8f, 0x69, 0x7f, 0x1f, 0x4e, 0x5c, 0x9f, 0xbe, 0xbe, 0xb4, + 0xe6, 0xfa, 0xd9, 0x1e, 0x09, 0x3d, 0xd5, 0xba, 0xc9, 0x92, 0xac, 0xbc, 0xb8, + 0x38, 0x3f, 0x9a, 0x8d, 0x8c, 0x04, 0xea, 0x6e, 0x2e, 0x0d, 0x03, 0xa2, 0xdf, + 0x83, 0xd4, 0xf4, 0x94, 0x59, 0x5b, 0x2c, 0xa1, 0x0b, 0x70, 0x79, 0x25, 0x9c, + 0x50, 0x7d, 0xf1, 0xec, 0xe4, 0x4d, 0xea, 0x4e, 0x9a, 0x4a, 0xe4, 0x0e, 0xc8, + 0x33, 0x1e, 0xeb, 0x03, 0x94, 0x73, 0xbd, 0x39, 0xc0, 0x9d, 0x01, 0x4b, 0x0d, + 0x7b, 0xb9, 0x01, 0x61, 0x66, 0x55, 0x4f, 0xf3, 0x8a, 0x1d, 0x77, 0xf2, 0xfd, + 0xa4, 0xe7, 0xeb, 0xa7, 0xa7, 0x8a, 0xb3, 0x1f, 0x38, 0x29, 0x42, 0x52, 0xa2, + 0xb1, 0x0f, 0xd2, 0x86, 0x5b, 0x57, 0x05, 0x05, 0x5d, 0xfe, 0x9b, 0x3e, 0x9e, + 0x8f, 0x7a, 0xd5, 0xf4, 0x00, 0x7d, 0xbe, 0x42, 0x2b, 0x3a, 0xa0, 0xbe, 0xb9, + 0xd1, 0xc8, 0x9d, 0x37, 0x46, 0x08, 0x54, 0xff, 0x6e, 0x5f, 0x03, 0xe5, 0xff, + 0x3d, 0x4f, 0x18, 0x48, 0xf4, 0xcc, 0x64, 0x21, 0x8a, 0x01, 0xf2, 0x47, 0x2b, + 0xb0, 0x55, 0x80, 0x2f, 0x97, 0xf3, 0x20, 0x41, 0xa7, 0x92, 0x79, 0x0b, 0x7c, + 0x22, 0x6b, 0x04, 0xa6, 0xea, 0xe8, 0x5f, 0x1b, 0x71, 0xca, 0x19, 0xa1, 0x71, + 0x89, 0x02, 0xb4, 0xc3, 0xa3, 0xb5, 0x06, 0xd8, 0xc1, 0xb7, 0xae, 0x72, 0x8c, + 0x9b, 0x6c, 0xc3, 0x17, 0xe5, 0xe0, 0xde, 0xe5, 0x33, 0xe2, 0xe9, 0x99, 0x73, + 0xd8, 0x83, 0xa4, 0x0c, 0x6e, 0x68, 0xf2, 0x31, 0xd2, 0xcb, 0x01, 0x2f, 0x60, + 0xc1, 0x43, 0xcc, 0xab, 0xdd, 0x40, 0x45, 0x59, 0x0d, 0x9e, 0x43, 0xfb, 0xa3, + 0x6f, 0xe4, 0xcf, 0xd9, 0x7b, 0x4b, 0xdd, 0x0c, 0x4d, 0x2c, 0x93, 0xc5, 0x72, + 0x8b, 0x12, 0x87, 0xfd, 0x25, 0x41, 0x72, 0x2c, 0x69, 0x9b, 0xc1, 0xa0, 0x05, + 0x83, 0xdb, 0xc9, 0x48, 0xd5, 0x32, 0x4a, 0xc5, 0xbd, 0x7a, 0x68, 0x09, 0x64, + 0x67, 0x3e, 0xdf, 0x2c, 0x6d, 0xeb, 0xb1, 0xc8, 0xe1, 0xd0, 0x24, 0x16, 0xe6, + 0xbd, 0xb2, 0xa7, 0x68, 0x1b, 0xf4, 0x29, 0x92, 0x25, 0xc2, 0x1b, 0x5d, 0xb6, + 0xa8, 0x45, 0xad, 0x10, 0x4d, 0x34, 0x29, 0xcd, 0xc5, 0x9e, 0x3b, 0xca, 0xcf, + 0x6d, 0xbc, 0x88, 0xaf, 0x0f, 0x67, 0xdc, 0xbd, 0xf3, 0xa0, 0x72, 0x3e, 0x4d, + 0x4b, 0xce, 0x32, 0x85, 0x1b, 0xb5, 0x19, 0x7a, 0x8f, 0x43, 0x30, 0xb2, 0x72, + 0x27, 0xf0, 0xb7, 0x71, 0xd0, 0xaf, 0x17, 0x5e, 0x9c, 0x3f, 0x6e, 0x1f, 0x68, + 0x46, 0x2e, 0xe7, 0xfe, 0x17, 0x97, 0xd9, 0x28, 0x40, 0x6f, 0x92, 0x38, 0xa3, + 0xf3, 0xfd, 0x83, 0x6a, 0x27, 0x56, 0xdd, 0x0a, 0x11, 0xe1, 0xab, 0x94, 0x9d, + 0x5e, 0x30, 0x89, 0x4f, 0x56, 0x29, 0x95, 0x25, 0xe6, 0x5d, 0x95, 0x0f, 0x2e, + 0xb5, 0x0b, 0x3a, 0x8e, 0xa7, 0xac, 0xad, 0x82, 0xde, 0x26, 0x2f, 0xa3, 0x44, + 0x80, 0xa2, 0x9c, 0x26, 0x19, 0xba, 0x45, 0x90, 0x3d, 0xf9, 0xa7, 0xf9, 0x86, + 0x2d, 0xc0, 0x49, 0xce, 0xf3, 0x97, 0xf7, 0x73, 0xbe, 0xed, 0xd3, 0x22, 0x6a, + 0x8c, 0xab, 0x1c, 0x86, 0x4d, 0x00, 0xb8, 0xfd, 0x37, 0xea, 0xf1, 0xd5, 0x93, + 0x5a, 0x5b, 0xbb, 0x6a, 0xd9, 0xf2, 0x7a, 0x1d, 0x8b, 0xaf, 0xc0, 0xac, 0x5f, + 0x58, 0x02, 0x36, 0x93, 0x82, 0x2a, 0x1d, 0xd4, 0xa7, 0xca, 0x1c, 0x49, 0xec, + 0x81, 0x4e, 0x8f, 0xe6, 0xe0, 0xe0, 0xde, 0x54, 0x6a, 0x4f, 0xbe, 0x7d, 0x25, + 0x67, 0x0b, 0x2f, 0xc6, 0x8a, 0x8f, 0xb2, 0xc4, 0xa6, 0x3d, 0xef, 0xec, 0x79, + 0xc9, 0x0c, 0x63, 0xff, 0x96, 0xe5, 0x40, 0xb7, 0x61, 0x5d, 0x43, 0xa6, 0x26, + 0x1d, 0x57, 0x73, 0x03, 0x06, 0xb6, 0x63, 0x2c, 0x8e, 0xe6, 0x1b, 0xaa, 0x4a, + 0xb4, 0xd3, 0x08, 0x4d, 0x65, 0x9c, 0xab, 0xcf, 0xc4, 0x06, 0x4c, 0x09, 0xd2, + 0x42, 0x69, 0xb3, 0x03, 0x17, 0x10, 0xb6, 0x7d, 0x3b, 0x0b, 0x73, 0x6f, 0xac, + 0xbc, 0x18, 0x1e, 0xb1, 0xdc, 0x8c, 0x49, 0x3f, 0x10, 0xdb, 0xe6, 0xfe, 0x45, + 0xfd, 0xd4, 0xab, 0x60, 0x22, 0xfa, 0xbd, 0xd3, 0x4c, 0x09, 0xf7, 0x51, 0x04, + 0xc3, 0x85, 0xc9, 0x26, 0x83, 0x41, 0xc1, 0x6e, 0xbe, 0x80, 0xf8, 0xc8, 0x0e, + 0x8e, 0x06, 0x23, 0x06, 0x03, 0x99, 0x5a, 0xde, 0x55, 0x61, 0xfe, 0xd4, 0x5c, + 0xf8, 0xd1, 0x14, 0xd4, 0xcf, 0x02, 0x42, 0x0c, 0x4b, 0x96, 0x2d, 0xc2, 0x02, + 0xf8, 0xa5, 0x07, 0xf3, 0xd8, 0xe8, 0xa3, 0x44, 0xfb, 0xa1, 0x0a, 0x32, 0x7f, + 0xf2, 0x22, 0x54, 0xf6, 0xc3, 0xac, 0x8f, 0x3c, 0xf9, 0x70, 0x0b, 0x1f, 0xd2, + 0xec, 0xbe, 0x9f, 0x4e, 0x91, 0xe4, 0x3a, 0x65, 0x4f, 0xff, 0x02, 0x7c, 0xd9, + 0x17, 0x4b, 0x63, 0x8e, 0x6e, 0xfe, 0xc4, 0xab, 0xfb, 0xa1, 0x87, 0xf8, 0xf3, + 0xdb, 0xa0, 0x45, 0x9d, 0xa6, 0xc3, 0xf8, 0x00, 0xcb, 0x6b, 0x61, 0x33, 0xa8, + 0xb4, 0xac, 0x1e, 0xf6, 0x58, 0xd1, 0x11, 0xc0, 0x3f, 0x07, 0x22, 0x08, 0xdc, + 0xc2, 0x07, 0xa2, 0x22, 0x3a, 0x70, 0x22, 0x92, 0x43, 0x2e, 0x83, 0x06, 0xfc, + 0x03, 0x04, 0x63, 0xe7, 0x54, 0xff, 0x0f, 0x15, 0x3d, 0x97, 0xbc, 0x9c, 0xe9, + 0x6d, 0xff, 0x4b, 0xed, 0x2f, 0x1e, 0xa5, 0xb8, 0xea, 0x87, 0x6d, 0x2e, 0xe4, + 0xe4, 0xf6, 0xe4, 0x9a, 0x4a, 0x85, 0xa9, 0xcf, 0x4a, 0x33, 0xdc, 0xd9, 0x36, + 0x60, 0xa4, 0x25, 0x43, 0xe5, 0x34, 0x22, 0x39, 0x0d, 0x66, 0x5b, 0xdd, 0x30, + 0x24, 0x78, 0xb3, 0x3c, 0x8d, 0x57, 0x47, 0x92, 0x41, 0x4c, 0x5f, 0xe5, 0xb7, + 0x4f, 0xe1, 0xd1, 0x69, 0x52, 0x5c, 0x99, 0x30, 0x1a, 0x3a, 0x68, 0xa0, 0xc8, + 0x5f, 0x02, 0x0f, 0xd5, 0x8f, 0x6d, 0x9f, 0x3a, 0xcb, 0x13, 0x9c, 0x96, 0x65, + 0x38, 0x56, 0xa3, 0x2e, 0x21, 0x02, 0x7a, 0xa2, 0xba, 0x18, 0x60, 0x10, 0xd5, + 0x3c, 0xdd, 0x4c, 0x41, 0x50, 0xcb, 0x2b, 0xb2, 0x42, 0x44, 0x65, 0x42, 0xb0, + 0x17, 0x84, 0x40, 0x1f, 0xa2, 0xcb, 0xf1, 0x22, 0xc9, 0xf1, 0x1d, 0x8c, 0x81, + 0x36, 0x98, 0x7b, 0x67, 0x86, 0x29, 0x93, 0x84, 0x58, 0x5f, 0x9c, 0xa2, 0x93, + 0x53, 0x7b, 0x4b, 0xe5, 0x72, 0x6f, 0x94, 0xd4, 0x77, 0x60, 0x5a, 0x8a, 0x6c, + 0x53, 0x06, 0x02, 0xbb, 0x46, 0xc4, 0xde, 0x20, 0x7f, 0xc5, 0x9e, 0x91, 0xe4, + 0xa9, 0x0a, 0x91, 0x11, 0x77, 0x74, 0x69, 0xf1, 0xe2, 0x87, 0x82, 0x76, 0x7d, + 0x9d, 0xe5, 0x7d, 0xea, 0xde, 0xad, 0xcb, 0x4a, 0xf5, 0x19, 0x3e, 0x09, 0xc9, + 0xbb, 0x74, 0x73, 0x77, 0x3a, 0x8c, 0xa5, 0x6d, 0x76, 0x51, 0x1d, 0x65, 0x99, + 0x20, 0xdb, 0x99, 0x64, 0xd3, 0x2b, 0xad, 0xb6, 0x1f, 0x4c, 0xf6, 0xb0, 0x22, + 0xd7, 0xc1, 0x53, 0x93, 0x18, 0x49, 0x64, 0x3e, 0x8b, 0x99, 0xea, 0xe0, 0x28, + 0x4f, 0x8b, 0x01, 0x15, 0xb4, 0x23, 0x7a, 0x7c, 0x5d, 0x81, 0x97, 0x0f, 0xe8, + 0x7c, 0x6f, 0x84, 0xb6, 0x68, 0x6c, 0x46, 0x25, 0xdb, 0xdd, 0x9d, 0x79, 0xd2, + 0xc5, 0x55, 0xdd, 0x4f, 0xce, 0xed, 0x2c, 0x5e, 0x5e, 0x89, 0x6f, 0x63, 0x1a, + 0xe4, 0x59, 0x7e, 0x9c, 0xc0, 0xbe, 0xe7, 0xb3, 0x02, 0x5f, 0x95, 0x56, 0x10, + 0x6a, 0x84, 0x3a, 0x18, 0x22, 0x7f, 0x5a, 0xb9, 0x61, 0x7d, 0x7b, 0xcb, 0x1a, + 0xf5, 0x28, 0xfa, 0xa7, 0xa0, 0x52, 0xea, 0x4f, 0x52, 0xca, 0x59, 0x45, 0x57, + 0xfd, 0xad, 0x33, 0x05, 0x2b, 0xc8, 0x2b, 0x39, 0xc6, 0xa6, 0x09, 0xa0, 0x70, + 0x75, 0x3d, 0x78, 0x8b, 0x2c, 0x4a, 0x2c, 0xae, 0xbb, 0xe7, 0x9f, 0xf0, 0x12, + 0x07, 0x1c, 0x07, 0x08, 0x10, 0x94, 0xad, 0x60, 0x59, 0xc2, 0x8f, 0x48, 0xe5, + 0x56, 0xc4, 0xe8, 0xd8, 0xc5, 0x37, 0x8b, 0xc2, 0x93, 0x07, 0x6b, 0xb4, 0x97, + 0x07, 0x5f, 0x9c, 0xa0, 0xba, 0x13, 0x11, 0x55, 0x0f, 0xa2, 0x17, 0x3d, 0x0e, + 0xb1, 0xf0, 0xbd, 0xdd, 0xf3, 0xb3, 0xd5, 0xc2, 0x43, 0xff, 0xea, 0xbe, 0xe8, + 0x23, 0xcd, 0x63, 0xb4, 0x39, 0x39, 0xce, 0x95, 0x46, 0xed, 0x4c, 0x41, 0xe6, + 0x0c, 0xcc, 0x7e, 0x1c, 0x54, 0x3c, 0xb3, 0xe2, 0xd3, 0x50, 0xe2, 0xe2, 0xe9, + 0x74, 0x21, 0x5c, 0xf7, 0xaa, 0x96, 0x9b, 0x66, 0x81, 0x14, 0xac, 0xdb, 0x29, + 0xf4, 0xcd, 0xcf, 0xdc, 0xec, 0x2a, 0x8c, 0xe4, 0xf5, 0x95, 0xf4, 0xff, 0x5f, + 0x70, 0x7e, 0x7f, 0xa4, 0xde, 0xe8, 0xbf, 0x8f, 0x39, 0x52, 0xae, 0x32, 0xe7, + 0x7f, 0x34, 0xf8, 0xb3, 0xab, 0xaa, 0xe9, 0x69, 0x28, 0xba, 0x4a, 0x6c, 0x0f, + 0xbf, 0x5b, 0x29, 0x19, 0x2d, 0xae, 0x80, 0x0d, 0xfa, 0x79, 0x57, 0x0c, 0xaf, + 0x0b, 0xb8, 0x33, 0xbd, 0x37, 0xa3, 0xd4, 0xbe, 0xaf, 0x09, 0x1f, 0x6b, 0x3e, + 0x55, 0xaa, 0xe5, 0x25, 0xf4, 0x13, 0xac, 0x80, 0x4c, 0x34, 0x7d, 0x54, 0x1d, + 0x2c, 0x09, 0xec, 0x6e, 0x54, 0x03, 0x5d, 0xf1, 0xd8, 0x30, 0x28, 0x4d, 0x9b, + 0x46, 0xff, 0xd2, 0xb2, 0xeb, 0x04, 0x0b, 0x61, 0x77, 0xd0, 0xa0, 0x9c, 0x16, + 0x60, 0x34, 0xa9, 0x57, 0xb1, 0x8f, 0xf6, 0x2e, 0x43, 0x4a, 0x3e, 0xc7, 0x32, + 0x62, 0xe4, 0xb2, 0x3f, 0xec, 0x9d, 0x29, 0x0a, 0x81, 0xc5, 0xb1, 0xf7, 0x3c, + 0xb4, 0xcd, 0x1c, 0x47, 0x2b, 0x86, 0xe5, 0x34, 0xab, 0x9e, 0x65, 0x53, 0x29, + 0x5d, 0xb0, 0xcf, 0x34, 0xe1, 0x39, 0x2a, 0xad, 0x5a, 0xbc, 0xf3, 0x98, 0x64, + 0x16, 0xa7, 0x0a, 0x9d, 0xbe, 0x59, 0xbb, 0x95, 0x8e, 0xbc, 0x71, 0x1c, 0x3a, + 0xe0, 0x8c, 0xaf, 0x52, 0xec, 0xa9, 0xcb, 0x54, 0xc4, 0x58, 0xbe, 0x7f, 0x5e, + 0x62, 0x14, 0xec, 0xa0, 0xf0, 0xa3, 0x81, 0x52, 0x62, 0x20, 0x01, 0x32, 0xe6, + 0x14, 0x54, 0x37, 0xec, 0xd2, 0x1f, 0xc8, 0x03, 0x6c, 0xb0, 0x0a, 0x49, 0x13, + 0x84, 0xc3, 0x41, 0xd8, 0x72, 0xdc, 0xda, 0x31, 0xb1, 0x42, 0x96, 0x73, 0xd9, + 0xc4, 0xf5, 0x7b, 0x81, 0xa0, 0x23, 0x6d, 0xa5, 0xec, 0x55, 0x02, 0xee, 0x29, + 0x63, 0x15, 0x0a, 0x00, 0x26, 0xbd, 0x63, 0xef, 0x67, 0x9e, 0x8c, 0x25, 0xb8, + 0xec, 0xee, 0x06, 0x56, 0x4a, 0xf3, 0xb0, 0x2d, 0xea, 0xb1, 0x06, 0x97, 0xa2, + 0x4d, 0xe6, 0x7d, 0x4f, 0x65, 0x04, 0xae, 0x27, 0x37, 0xb8, 0xe1, 0x73, 0x25, + 0xc2, 0xff, 0x15, 0x0c, 0x62, 0xe3, 0x79, 0x83, 0x44, 0xa1, 0xad, 0x3c, 0xbb, + 0x75, 0xb7, 0xf2, 0xa1, 0x57, 0x38, 0xf6, 0x01, 0xcf, 0x00, 0xf7, 0xe8, 0xbc, + 0x08, 0xb6, 0x89, 0x56, 0x7e, 0x4c, 0x7c, 0x01, 0x05, 0x8b, 0xee, 0xc2, 0x90, + 0x3c, 0x5c, 0xa6, 0xb4, 0xc4, 0xa5, 0x71, 0xf4, 0x60, 0xd6, 0x05, 0x87, 0x36, + 0x29, 0x96, 0xc6, 0xe1, 0x25, 0x54, 0xe8, 0xe3, 0x4e, 0x68, 0x3a, 0x27, 0xf8, + 0xa5, 0xff, 0x97, 0x1d, 0x5a, 0x0d, 0xc2, 0xf3, 0xef, 0xd3, 0x88, 0x99, 0x87, + 0xc1, 0xcc, 0x39, 0xce, 0x5d, 0x4b, 0x6b, 0x54, 0x4c, 0xe0, 0x4c, 0x71, 0xee, + 0x4b, 0xfa, 0xe5, 0x04, 0x0d, 0x61, 0xf0, 0x57, 0xe4, 0xf7, 0x70, 0x17, 0x28, + 0xf1, 0x20, 0x04, 0xa7, 0xf7, 0xed, 0xeb, 0x3a, 0xb2, 0x26, 0x09, 0xed, 0x33, + 0xb0, 0xab, 0x5d, 0x69, 0xb1, 0x2d, 0x45, 0x76, 0x57, 0x77, 0x14, 0xdf, 0xc6, + 0xdd, 0xa7, 0x1f, 0xf6, 0x01, 0x7b, 0x55, 0xb3, 0x35, 0x4d, 0x11, 0xe9, 0x21, + 0x67, 0x92, 0xe5, 0x60, 0x9f, 0xc0, 0x67, 0x88, 0xec, 0x66, 0x8e, 0xef, 0x64, + 0x5e, 0x63, 0xb3, 0x7e, 0x2d, 0x0c, 0xd2, 0x63, 0x04, 0x08, 0x00, 0xbc, 0x8a, + 0xa2, 0x80, 0x15, 0x6a, 0x79, 0x4f, 0x62, 0xa5, 0xf6, 0x93, 0xeb, 0xd9, 0x07, + 0x4b, 0x5d, 0x35, 0x4a, 0x71, 0xc8, 0xe3, 0x36, 0xde, 0x04, 0x08, 0xac, 0x70, + 0x80, 0xa2, 0xae, 0xee, 0x36, 0x6c, 0x58, 0x14, 0x6f, 0x32, 0xe3, 0x49, 0xa9, + 0xbc, 0x65, 0x7e, 0xc9, 0xe5, 0x7a, 0x89, 0xa0, 0x4c, 0xce, 0xee, 0x21, 0xbd, + 0xf3, 0x79, 0x3e, 0x49, 0xa5, 0xcf, 0x71, 0x3a, 0x42, 0xd0, 0x29, 0xdd, 0xdb, + 0x3d, 0xb4, 0x95, 0x09, 0x2c, 0x37, 0xce, 0x81, 0x4b, 0xe7, 0x3e, 0xf4, 0xec, + 0x8d, 0x70, 0xe8, 0x69, 0xbd, 0x2b, 0x78, 0x8f, 0x15, 0x00, 0xfe, 0x5e, 0xe5, + 0x6c, 0x0c, 0xe7, 0x04, 0xeb, 0xa2, 0xc1, 0xa3, 0xa3, 0x29, 0x0d, 0xe6, 0xec, + 0x68, 0xcc, 0xb5, 0xef, 0x7c, 0xd0, 0x21, 0x2a, 0x3f, 0x09, 0x96, 0x92, 0xcf, + 0x00, 0x04, 0x8d, 0xe5, 0x01, 0x26, 0x19, 0xe7, 0x41, 0x69, 0x2b, 0xfc, 0x74, + 0x05, 0xba, 0x3e, 0x87, 0x5e, 0x98, 0xb7, 0xca, 0x31, 0xe9, 0x65, 0xa1, 0x6f, + 0xdd, 0xb5, 0xb0, 0xb7, 0x72, 0xa3, 0xf5, 0xd0, 0x50, 0xd8, 0xad, 0x7f, 0x60, + 0x7f, 0x55, 0xc0, 0xdc, 0x52, 0xb4, 0x8f, 0xb0, 0x2a, 0x8b, 0x1d, 0xef, 0xc6, + 0xc3, 0x10, 0xb2, 0x47, 0x55, 0x59, 0xb4, 0x7e, 0x84, 0x4e, 0xd3, 0x77, 0x60, + 0xd7, 0xd1, 0x6f, 0x27, 0xcb, 0x48, 0xbf, 0x36, 0x16, 0xc4, 0x6f, 0xb0, 0xcf, + 0x3c, 0x8c, 0x28, 0xb9, 0x39, 0x27, 0x80, 0x0a, 0x29, 0x16, 0xa4, 0x07, 0xa6, + 0x0d, 0x68, 0x99, 0x7b, 0x10, 0x50, 0x51, 0x32, 0xad, 0x33, 0xf9, 0xce, 0x26, + 0xb4, 0xac, 0xba, 0x27, 0xa2, 0xa0, 0xc2, 0x18, 0xdb, 0x15, 0xa5, 0xd7, 0xaa, + 0xed, 0x4f, 0x6a, 0x72, 0x00, 0x36, 0x72, 0xca, 0x70, 0x49, 0x8b, 0x05, 0x49, + 0x4a, 0x93, 0x34, 0x1f, 0xcf, 0x96, 0xc0, 0x99, 0x4e, 0x42, 0x7b, 0xeb, 0xd3, + 0x56, 0xe4, 0x17, 0x6d, 0xec, 0x83, 0xe6, 0xfe, 0x80, 0x02, 0x9c, 0xfc, 0x47, + 0x8b, 0x88, 0xb6, 0xfd, 0x38, 0xc0, 0x39, 0xe0, 0x8b, 0x6f, 0xd9, 0x5d, 0xab, + 0xcf, 0xb2, 0x5f, 0x23, 0x8b, 0x26, 0x62, 0x06, 0xb0, 0xa2, 0xf9, 0xa2, 0xee, + 0xa1, 0xc0, 0x83, 0xfa, 0xc8, 0x08, 0xaa, 0xfa, 0x03, 0x65, 0x66, 0xcc, 0xd2, + 0x02, 0xbc, 0xfa, 0x41, 0x4e, 0x71, 0xc8, 0xb4, 0x89, 0x33, 0xc8, 0xed, 0x45, + 0x28, 0x7e, 0x1b, 0x43, 0x9b, 0x61, 0x06, 0xa5, 0x50, 0x94, 0x73, 0xf5, 0x7b, + 0x87, 0x88, 0xaf, 0x52, 0x7c, 0xf9, 0xa7, 0xab, 0xa5, 0x93, 0xdc, 0x9f, 0x5e, + 0x5a, 0xca, 0x1a, 0x64, 0x8e, 0xe4, 0x88, 0xf3, 0x6d, 0xeb, 0x4a, 0x3f, 0xdb, + 0x0f, 0xf6, 0xf5, 0xa3, 0x04, 0x4a, 0x63, 0xe1, 0x7f, 0x70, 0xa4, 0x30, 0x38, + 0x24, 0x60, 0x3a, 0xb5, 0x0e, 0x9b, 0xf7, 0x5b, 0xae, 0xb5, 0x7b, 0xfd, 0xc8, + 0x9b, 0xfd, 0xbc, 0x27, 0x27, 0x9d, 0x10, 0x73, 0xbf, 0x7f, 0x95, 0x05, 0xfb, + 0x31, 0x68, 0xd2, 0x06, 0xe2, 0xbf, 0x41, 0x02, 0xbf, 0x15, 0x9c, 0xff, 0x61, + 0xe6, 0xd6, 0x6c, 0x80, 0x37, 0x50, 0xda, 0x25, 0x4c, 0xd6, 0xb8, 0x1a, 0xed, + 0x42, 0x09, 0x97, 0x94, 0xb8, 0x4e, 0xce, 0x90, 0x42, 0x18, 0xe6, 0xf6, 0x6e, + 0xc6, 0x34, 0xe9, 0x2e, 0xef, 0xf4, 0x5f, 0x52, 0xe0, 0x4b, 0x4b, 0x79, 0x5a, + 0x15, 0x25, 0xaa, 0xf9, 0xc5, 0x1d, 0x62, 0x60, 0xfb, 0xd6, 0x4e, 0x8d, 0x8a, + 0xc2, 0x66, 0xdc, 0x6e, 0x7d, 0xf6, 0x15, 0x3a, 0xd9, 0x73, 0x55, 0x83, 0x79, + 0x28, 0x40, 0x4c, 0xd5, 0x81, 0xbc, 0x9c, 0xf9, 0xdc, 0xd6, 0x67, 0x47, 0xdc, + 0x97, 0x0a, 0x9f, 0x00, 0xde, 0xb4, 0x4b, 0xd6, 0x34, 0xab, 0x04, 0x2e, 0x01, + 0x04, 0xc1, 0xce, 0x74, 0x7f, 0x53, 0x75, 0x1b, 0xc3, 0x3e, 0x38, 0x4c, 0x6b, + 0x55, 0x76, 0x39, 0x9e, 0x16, 0xf8, 0xf0, 0xcb, 0x08, 0xde, 0x35, 0x08, 0x37, + 0x33, 0x95, 0x45, 0x87, 0xc1, 0xc2, 0x4d, 0xf2, 0xae, 0x66, 0x30, 0xff, 0xfe, + 0x99, 0x62, 0x15, 0xef, 0xe4, 0xd2, 0x62, 0x6d, 0xeb, 0x20, 0x56, 0x6a, 0x8f, + 0x5e, 0xad, 0x2f, 0x04, 0xdb, 0x5d, 0x08, 0x77, 0x9c, 0x9c, 0x65, 0x9e, 0xa3, + 0x43, 0xcd, 0x78, 0x46, 0x34, 0xc9, 0x9d, 0x8c, 0x8b, 0xad, 0xa9, 0x3b, 0xe8, + 0xe6, 0xda, 0x84, 0x15, 0x94, 0xba, 0xcf, 0x7c, 0xb3, 0xe6, 0x92, 0xc7, 0x4b, + 0x5f, 0xfe, 0x95, 0x78, 0x73, 0x11, 0x3a, 0x1a, 0xb0, 0x64, 0x02, 0x6f, 0x6d, + 0xee, 0x8b, 0x48, 0xa3, 0x84, 0xa1, 0x33, 0x83, 0x18, 0x36, 0x07, 0x86, 0x50, + 0x27, 0x84, 0xd1, 0x7d, 0x40, 0x0c, 0xe3, 0xd7, 0x21, 0x78, 0x7e, 0xdc, 0x4c, + 0x6b, 0x39, 0x35, 0x66, 0x25, 0x10, 0x77, 0x10, 0x00, 0x68, 0x0d, 0x78, 0xbb, + 0x49, 0xc5, 0x66, 0xef, 0x27, 0xdf, 0x61, 0xc9, 0xfe, 0xb9, 0x2c, 0x08, 0x97, + 0x59, 0x44, 0x87, 0x27, 0xa9, 0x34, 0xe3, 0x57, 0x95, 0x3d, 0xe1, 0xe9, 0xe9, + 0x0f, 0xd8, 0xdf, 0xfe, 0x40, 0xb8, 0x73, 0xbc, 0xd5, 0xb9, 0x82, 0x08, 0xdf, + 0x4b, 0x2c, 0xa2, 0x89, 0x7a, 0xf9, 0x0d, 0x8c, 0x8a, 0x23, 0x62, 0x30, 0x02, + 0xa9, 0xd8, 0xbc, 0x02, 0xe8, 0x06, 0x25, 0x4f, 0x41, 0x0e, 0x3b, 0x02, 0x40, + 0x9c, 0xbe, 0xbf, 0xce, 0x8a, 0xcf, 0x65, 0xcf, 0x39, 0x42, 0x6b, 0x64, 0xa6, + 0xba, 0x93, 0x74, 0xa1, 0x3d, 0x72, 0x59, 0x62, 0x3f, 0x65, 0xe9, 0x3e, 0x10, + 0xbf, 0x1f, 0x16, 0xba, 0x7a, 0xe0, 0x7d, 0xa9, 0x20, 0x58, 0x1c, 0x70, 0x40, + 0x9e, 0xdc, 0x7b, 0x9e, 0x21, 0x4e, 0x95, 0x91, 0x92, 0x82, 0x4c, 0x1d, 0xa6, + 0x5d, 0x33, 0x7b, 0x73, 0x75, 0xf5, 0x03, 0x2f, 0xea, 0xd3, 0xb4, 0xf3, 0x28, + 0x48, 0x11, 0x95, 0x0c, 0x7a, 0x90, 0xae, 0xc9, 0x75, 0xd4, 0xe3, 0x62, 0x9f, + 0x52, 0xd1, 0x9a, 0x16, 0x4e, 0x51, 0x16, 0xef, 0x3a, 0xd0, 0x22, 0x44, 0x2d, + 0x1e, 0xec, 0x76, 0xb8, 0x88, 0x73, 0x8b, 0x53, 0xe5, 0x05, 0x58, 0xa7, 0x0f, + 0x20, 0xc8, 0xac, 0xb5, 0x8d, 0xee, 0x63, 0x27, 0x15, 0xe4, 0x78, 0xe2, 0xbc, + 0x21, 0xbc, 0xfb, 0xe3, 0x15, 0x59, 0x96, 0xca, 0xe7, 0xbd, 0x97, 0xf0, 0x2b, + 0x51, 0x6d, 0x32, 0x00, 0xfb, 0x3c, 0x17, 0x39, 0x7c, 0xc1, 0x2b, 0xb7, 0xa1, + 0x9f, 0xd4, 0x36, 0xe6, 0x7a, 0xbc, 0xe6, 0x6d, 0x30, 0xfe, 0xc0, 0x47, 0xfb, + 0x27, 0x70, 0x82, 0x0e, 0x47, 0x6f, 0x3e, 0x32, 0xbc, 0x48, 0x3b, 0xf5, 0x31, + 0x64, 0xae, 0x49, 0x70, 0xf1, 0x1b, 0x9c, 0xae, 0xe4, 0xed, 0x6c, 0xb8, 0xd2, + 0xd7, 0x0f, 0x69, 0x13, 0xd8, 0xe0, 0x2a, 0xf8, 0xfb, 0xb1, 0xe4, 0x09, 0xb4, + 0xef, 0x08, 0x04, 0x48, 0xe5, 0x3b, 0xe6, 0xe5, 0xe6, 0x05, 0x75, 0xdf, 0xde, + 0x94, 0x28, 0xb0, 0x06, 0x96, 0x61, 0x1a, 0x2f, 0x72, 0x33, 0x2a, 0xe2, 0x90, + 0x23, 0xdd, 0x88, 0xae, 0x77, 0xf1, 0x5b, 0x8a, 0xe2, 0xc2, 0x4b, 0x86, 0xcf, + 0x3d, 0x57, 0x43, 0x9c, 0xaf, 0x17, 0xf2, 0x8e, 0xda, 0x94, 0x93, 0x2e, 0xef, + 0x28, 0x53, 0x4e, 0x16, 0x49, 0xce, 0xf8, 0x85, 0x40, 0xfc, 0xb1, 0xa6, 0x3e, + 0x11, 0x5c, 0x58, 0x22, 0xaf, 0xa4, 0x40, 0xc8, 0xd7, 0x9d, 0x66, 0xf9, 0xbb, + 0x1f, 0x48, 0xe1, 0x14, 0x0b, 0x06, 0xec, 0x87, 0x18, 0x3c, 0xbc, 0x6e, 0x95, + 0xf6, 0xcd, 0x5f, 0x7e, 0xbc, 0xad, 0xb8, 0x97, 0xc7, 0x7b, 0x4a, 0xfb, 0x36, + 0x7b, 0x95, 0x2d, 0xbb, 0x71, 0x7f, 0x75, 0x18, 0x90, 0xc8, 0xac, 0x30, 0x36, + 0xda, 0xcd, 0xbd, 0x78, 0x4a, 0x0d, 0x83, 0xab, 0xb8, 0x44, 0x6b, 0x3f, 0x93, + 0x96, 0x33, 0x5f, 0xbf, 0x0b, 0x44, 0xed, 0xc9, 0x9e, 0x1c, 0x67, 0xc5, 0xc3, + 0x81, 0x6a, 0xce, 0x76, 0x29, 0xe6, 0xe7, 0xb0, 0x28, 0xd6, 0xc8, 0x62, 0x74, + 0x9e, 0x86, 0xeb, 0xc5, 0x11, 0x7e, 0x21, 0xf4, 0x23, 0xe1, 0x8d, 0x09, 0x76, + 0xa1, 0xf5, 0x1d, 0x45, 0x47, 0x6d, 0xa5, 0x60, 0xff, 0x23, 0x15, 0x42, 0xbb, + 0x21, 0xc3, 0xde, 0xd2, 0xf2, 0x3b, 0x2a, 0x50, 0xe0, 0xb8, 0x22, 0x56, 0x90, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x1d, 0x11, 0x65, 0xd7, 0x60, 0x70, 0x2e, 0xf1, + 0x03, 0xd2, 0x23, 0x67, 0x26, 0x90, 0x23, 0x59, 0xbe, 0x8d, 0x79, 0x73, 0x52, + 0xf9, 0x6d, 0x22, 0x46, 0xa2, 0xee, 0x0a, 0xf8, 0x0a, 0x2a, 0x2d, 0x89, 0xa5, + 0x85, 0x30, 0xd6, 0xe3, 0x6b, 0xd3, 0x3a, 0x00, 0xc1, 0xb8, 0x93, 0xd6, 0xff, + 0x8f, 0x90, 0x01, 0x44, 0x15, 0x1b, 0xee, 0x34, 0xc7, 0x94, 0x4b, 0x99, 0xed, + 0x6e, 0x79, 0x45, 0xe7, 0xf0, 0xde, 0x87, 0x26, 0x3d, 0x0b, 0xba, 0x6e, 0x55, + 0xac, 0x96, 0xa9, 0x6d, 0x49, 0x95, 0x12, 0x9b, 0xcf, 0xa9, 0xd9, 0xda, 0x6d, + 0xe6, 0xdd, 0x48, 0x26, 0x39, 0x15, 0x3a, 0x81, 0x69, 0xa4, 0xab, 0x46, 0x4e, + 0x39, 0x0b, 0x7f, 0x0a, 0x96, 0xd1, 0x4a, 0x73, 0xf7, 0x69, 0x7f, 0x7e, 0xce, + 0x3c, 0xd7, 0x81, 0xd3, 0x5d, 0xd2, 0x2a, 0xdd, 0xdd, 0x2f, 0x5d, 0x34, 0x52, + 0x04, 0xe4, 0xbb, 0x55, 0x7e, 0x88, 0x45, 0x3f, 0x18, 0x8c, 0xac, 0xbe, 0x92, + 0x29, 0x87, 0xbb, 0xe3, 0xb3, 0xd9, 0x76, 0x82, 0x61, 0x35, 0xc1, 0x03, 0xb6, + 0xca, 0x18, 0x2b, 0x63, 0xe9, 0xe6, 0x7f, 0x83, 0xdc, 0x9f, 0x48, 0x93, 0x33, + 0xd5, 0x2a, 0x7f, 0xd7, 0x68, 0x8a, 0x58, 0xd6, 0x62, 0x0b, 0x67, 0xe9, 0xc7, + 0xb0, 0x91, 0x6f, 0xef, 0x90, 0xf1, 0x5d, 0x8e, 0x4e, 0xb8, 0x0c, 0xf5, 0x99, + 0x68, 0x2f, 0x95, 0x4f, 0xf4, 0xe0, 0xb3, 0x71, 0x83, 0x13, 0x0c, 0xa2, 0xee, + 0xd0, 0x91, 0x3f, 0x46, 0xa4, 0xdb, 0x99, 0x2a, 0x1c, 0x3b, 0xf3, 0x19, 0xdc, + 0x86, 0x75, 0x94, 0x01, 0x01, 0x53, 0x7c, 0xff, 0xc4, 0xa8, 0x2d, 0x59, 0x9b, + 0xbe, 0xa0, 0xd4, 0x7e, 0x7a, 0xbf, 0xa9, 0x92, 0xb4, 0x99, 0x8c, 0xb2, 0x50, + 0x09, 0x55, 0xe6, 0x1c, 0x0d, 0x46, 0xb3, 0x21, 0x17, 0xfb, 0xb9, 0x7f, 0x7a, + 0x76, 0x32, 0xd8, 0x72, 0x4b, 0x5d, 0xff, 0x67, 0xf7, 0x5e, 0x2d, 0x31, 0x74, + 0x06, 0xa0, 0xce, 0xc2, 0x89, 0xed, 0x08, 0x3b, 0x7c, 0x58, 0x19, 0x81, 0x8c, + 0x50, 0x47, 0x93, 0xde, 0x53, 0xb6, 0xbf, 0xdb, 0x51, 0x0e, 0x7c, 0xa7, 0x29, + 0xba, 0x74, 0x3d, 0x10, 0xb3, 0xe9, 0x95, 0x7e, 0xfa, 0x84, 0x20, 0x13, 0x39, + 0x47, 0x7c, 0xf3, 0x5f, 0xbb, 0x6a, 0x27, 0x9b, 0xad, 0x9e, 0x8f, 0x42, 0xb9, + 0xb3, 0xfd, 0x6f, 0x3b, 0xc7, 0x70, 0x67, 0x1d, 0x9c, 0x19, 0x12, 0x2f, 0xa3, + 0x25, 0x6d, 0x09, 0x07, 0x36, 0xb6, 0xd6, 0x4e, 0xb9, 0xcc, 0x03, 0x20, 0xf1, + 0xea, 0xaa, 0x27, 0x1b, 0xa2, 0x86, 0x1e, 0xc4, 0xb3, 0xf3, 0xf6, 0xc8, 0x40, + 0xb6, 0x19, 0xff, 0x38, 0x8d, 0x81, 0xfc, 0x40, 0x44, 0xa0, 0xd5, 0x31, 0xa4, + 0xbb, 0x44, 0xc9, 0x3d, 0x09, 0x9d, 0xb0, 0x8a, 0x9b, 0xc3, 0x46, 0xa0, 0xb6, + 0x2f, 0x16, 0x8f, 0xfb, 0xdb, 0x73, 0x93, 0x66, 0xbb, 0x53, 0x5d, 0xde, 0x66, + 0xc2, 0xc1, 0x28, 0x7b, 0x3b, 0x27, 0x85, 0xae, 0xd6, 0x4c, 0xc4, 0x0c, 0xbc, + 0x7d, 0x33, 0xcb, 0xa4, 0xa9, 0xf3, 0xfc, 0xf5, 0xf8, 0x31, 0x36, 0xa4, 0x39, + 0x2d, 0x21, 0xa7, 0xf9, 0xeb, 0x1c, 0xe4, 0xb6, 0xe1, 0x7e, 0x6f, 0x4a, 0x85, + 0xa5, 0x79, 0x66, 0x9e, 0xfd, 0x0f, 0xb0, 0x98, 0x78, 0xe0, 0x88, 0xe3, 0x22, + 0xe9, 0x06, 0xe8, 0x0d, 0x27, 0xf8, 0xd0, 0xca, 0x7e, 0x79, 0x15, 0xab, 0x40, + 0x96, 0x59, 0xa6, 0xd8, 0x0f, 0xde, 0xd1, 0x0a, 0xff, 0x9f, 0xb7, 0x73, 0x74, + 0x9d, 0x79, 0x28, 0x57, 0xf6, 0x8c, 0x7e, 0x8c, 0xf5, 0x18, 0x26, 0x0a, 0x61, + 0x08, 0x6d, 0xe3, 0x2f, 0xff, 0x82, 0x39, 0xf4, 0x53, 0x61, 0x7a, 0x19, 0xf6, + 0xfe, 0xc2, 0x20, 0x67, 0x60, 0x65, 0xeb, 0xe2, 0x75, 0x7e, 0xfc, 0xac, 0xcb, + 0x77, 0xfc, 0x61, 0xe5, 0x9b, 0x97, 0x63, 0x7e, 0x92, 0x0d, 0xee, 0x5e, 0x7e, + 0x7a, 0x12, 0xe9, 0xd6, 0xd2, 0x28, 0xb2, 0x6b, 0x2f, 0xa8, 0x36, 0xf4, 0x72, + 0x83, 0x69, 0xad, 0xcd, 0xfc, 0xd0, 0x04, 0xdc, 0xf1, 0x9e, 0x27, 0xc0, 0xc0, + 0x84, 0x44, 0xd2, 0x9a, 0x12, 0x2b, 0x23, 0x09, 0xf7, 0x16, 0x3c, 0x99, 0x0e, + 0xb9, 0x26, 0x1f, 0xd4, 0x15, 0xc0, 0x45, 0x4a, 0x56, 0xaa, 0x3e, 0xaf, 0x9c, + 0x1f, 0x9b, 0xff, 0xf6, 0x04, 0x77, 0x6a, 0x4d, 0x25, 0xe7, 0xd3, 0xcd, 0xc5, + 0xc5, 0xf1, 0x9c, 0xd2, 0xa8, 0x79, 0x4a, 0x4f, 0x57, 0x16, 0x7f, 0xbc, 0x7e, + 0xaa, 0x06, 0x16, 0x4d, 0x51, 0xc4, 0x53, 0x06, 0x14, 0xbc, 0xf5, 0x20, 0xb2, + 0x63, 0x82, 0x0a, 0xa1, 0x7b, 0x20, 0xb4, 0x8c, 0xbf, 0x59, 0xd8, 0xe3, 0x09, + 0x32, 0x2e, 0xbe, 0x56, 0x6f, 0xbe, 0x46, 0xe0, 0xaa, 0x29, 0x76, 0x6a, 0xdf, + 0xdf, 0x01, 0x7a, 0x71, 0x05, 0x10, 0x3c, 0x7f, 0xca, 0xb7, 0xb0, 0x76, 0x48, + 0xc7, 0xc1, 0x16, 0x04, 0x84, 0xf7, 0x7a, 0x6c, 0x70, 0xa5, 0x38, 0x1b, 0x82, + 0x56, 0x40, 0xa1, 0xbe, 0x48, 0xe4, 0x15, 0xa1, 0xe6, 0xa2, 0x7d, 0x78, 0x02, + 0x2a, 0x8a, 0x2f, 0xf0, 0x70, 0xab, 0xf1, 0x23, 0x94, 0xe3, 0xae, 0x5a, 0x8c, + 0x23, 0xe3, 0x73, 0x3e, 0xa4, 0x7a, 0x44, 0xcb, 0x2c, 0x96, 0x8b, 0xca, 0x24, + 0x98, 0x37, 0xde, 0x1d, 0x39, 0xa5, 0xa1, 0xdc, 0xae, 0x71, 0x0c, 0xe0, 0x43, + 0x01, 0x69, 0xbd, 0x6e, 0x9f, 0x64, 0xab, 0xf1, 0xe6, 0x4e, 0xc4, 0x9e, 0xd0, + 0x80, 0x4e, 0xb6, 0x47, 0x74, 0x3a, 0xce, 0xa9, 0x29, 0xed, 0x0f, 0x7c, 0x90, + 0x15, 0xb0, 0xe8, 0x1e, 0x21, 0x29, 0xdb, 0x05, 0x0d, 0x5e, 0x78, 0xe6, 0x82, + 0xc8, 0x19, 0x93, 0xea, 0x87, 0x53, 0xc9, 0x91, 0xb0, 0x2e, 0x61, 0x81, 0x0e, + 0x74, 0x61, 0xed, 0x87, 0xb3, 0x80, 0xdb, 0x96, 0xab, 0xe3, 0xbe, 0xad, 0x0f, + 0x4b, 0x22, 0x12, 0xdb, 0x65, 0x8c, 0x11, 0xb8, 0x3f, 0x53, 0x11, 0x47, 0x85, + 0x27, 0x65, 0x98, 0xb0, 0x19, 0x7a, 0x7f, 0x1c, 0x25, 0x62, 0x7d, 0x79, 0x62, + 0x4d, 0xac, 0xee, 0x97, 0x7d, 0x9f, 0x4e, 0x1a, 0x35, 0xed, 0x2e, 0xaa, 0xd3, + 0xcb, 0x68, 0x25, 0x0a, 0xa9, 0xb3, 0xab, 0x1a, 0x83, 0x45, 0x72, 0x8e, 0x7d, + 0x1a, 0x78, 0xbe, 0x1f, 0xe4, 0x62, 0xce, 0x8e, 0xad, 0x52, 0x8f, 0x7c, 0x05, + 0x0f, 0x1f, 0x6e, 0x02, 0x2b, 0xa8, 0xb0, 0xce, 0xdf, 0x6e, 0x29, 0x7a, 0xb5, + 0x64, 0xca, 0x1a, 0x1f, 0xaa, 0xf4, 0xcf, 0xf1, 0xe4, 0x20, 0x32, 0xfb, 0xbb, + 0x38, 0x9d, 0x3f, 0x66, 0xd5, 0x75, 0x55, 0xef, 0x3f, 0x3e, 0x9e, 0x49, 0xc2, + 0xac, 0x4e, 0x85, 0xbb, 0x75, 0x1d, 0x62, 0x66, 0xc9, 0x03, 0x5b, 0x77, 0x9d, + 0x76, 0x9d, 0x49, 0x5c, 0x91, 0x8a, 0x05, 0x5e, 0x77, 0x67, 0xfb, 0xb4, 0xbb, + 0xac, 0x3f, 0x96, 0x3d, 0xe9, 0x97, 0x46, 0xec, 0x4d, 0xfb, 0x64, 0x2d, 0x9c, + 0x2b, 0x86, 0x38, 0xe1, 0x6c, 0x16, 0xe7, 0x27, 0x70, 0x79, 0x3b, 0x7e, 0xa1, + 0xd0, 0x70, 0xc4, 0xe1, 0x1c, 0xbc, 0x20, 0xd8, 0xff, 0x3b, 0xea, 0xd1, 0x0d, + 0xb9, 0xc9, 0x4a, 0xe0, 0x48, 0x27, 0x21, 0xe1, 0xf2, 0x2c, 0xef, 0xe0, 0xdf, + 0x7c, 0x57, 0x7a, 0xa3, 0x8e, 0xc0, 0xe6, 0xc7, 0x8c, 0x9b, 0xa1, 0x64, 0xe9, + 0xdd, 0x00, 0x55, 0xdd, 0xe8, 0x3e, 0x8a, 0xd2, 0x40, 0xe6, 0xdf, 0xdb, 0xfb, + 0xe1, 0x76, 0xe4, 0x55, 0x1f, 0xdd, 0xe9, 0x2d, 0xb1, 0x67, 0x27, 0x42, 0x04, + 0x41, 0x70, 0x06, 0x58, 0xb5, 0x0e, 0xbb, 0x5a, 0x16, 0x13, 0x26, 0x7e, 0xac, + 0x51, 0xc8, 0x0b, 0x19, 0xec, 0xb7, 0x86, 0xab, 0x3b, 0xb9, 0x37, 0xf0, 0xd9, + 0x8e, 0x08, 0xb9, 0xc9, 0xcd, 0x4d, 0xf1, 0x53, 0x4e, 0xfe, 0xe3, 0x8a, 0x8f, + 0x87, 0x8c, 0x9f, 0x3b, 0xdc, 0x7e, 0xfb, 0x2d, 0x53, 0xff, 0x84, 0xfb, 0x83, + 0xea, 0xe7, 0xc9, 0x9e, 0xff, 0xa6, 0x3c, 0x96, 0x49, 0xa1, 0xf1, 0x70, 0xd2, + 0x9a, 0xf0, 0x3a, 0x3b, 0x45, 0x58, 0x9f, 0xae, 0x81, 0xeb, 0x0b, 0x5d, 0x8e, + 0x0d, 0x38, 0x02, 0x1d, 0x3b, 0x5f, 0x07, 0xe8, 0x8c, 0x99, 0x04, 0x37, 0x6d, + 0x27, 0xf1, 0x3e, 0x44, 0x41, 0xd5, 0x38, 0x74, 0x42, 0xc5, 0xea, 0x0a, 0xf5, + 0xa2, 0x0a, 0x38, 0x32, 0xbc, 0x3b, 0x9c, 0x59, 0xb8, 0x4b, 0xca, 0x39, 0xb5, + 0x2c, 0xd6, 0xb1, 0xfa, 0x29, 0x32, 0xba, 0x9d, 0x66, 0xc4, 0x12, 0xf5, 0xcd, + 0x39, 0x35, 0x1e, 0x13, 0x33, 0xef, 0x85, 0xd0, 0xee, 0xe5, 0x45, 0xa7, 0xe4, + 0x06, 0xf6, 0xeb, 0x3b, 0xf8, 0x93, 0xf3, 0xed, 0xac, 0x94, 0x64, 0x33, 0x92, + 0xa2, 0x8b, 0x0e, 0x49, 0x0c, 0x51, 0xe4, 0xb7, 0x16, 0x3c, 0x1c, 0xf7, 0x57, + 0xd2, 0x24, 0x18, 0xdd, 0x63, 0x38, 0x1b, 0xa2, 0xf2, 0x98, 0x28, 0x83, 0x6f, + 0xe9, 0x78, 0xda, 0xb5, 0x20, 0x1b, 0x2d, 0xb0, 0x8c, 0x3b, 0x38, 0x9b, 0xa4, + 0xb6, 0xac, 0xf7, 0x78, 0xc2, 0xbf, 0x91, 0x02, 0xbe, 0x0c, 0x3e, 0x12, 0xd7, + 0x7a, 0xea, 0x6d, 0xf7, 0x53, 0x8e, 0x8c, 0xf3, 0x62, 0xba, 0xaa, 0xad, 0x1d, + 0xc5, 0x60, 0x42, 0xc6, 0xf2, 0x4c, 0xaf, 0x46, 0xbe, 0xd6, 0x6a, 0xbf, 0x4c, + 0x40, 0x2a, 0x74, 0x92, 0x4e, 0xcf, 0xd0, 0xa0, 0x8d, 0xed, 0xee, 0xa0, 0xef, + 0xce, 0xcd, 0x35, 0x2c, 0x27, 0x5f, 0x13, 0xed, 0x20, 0x76, 0x03, 0x82, 0x2b, + 0x1e, 0xf9, 0x97, 0xb7, 0xed, 0x42, 0xf4, 0xa5, 0x76, 0xb9, 0xe4, 0xc0, 0x07, + 0x38, 0x56, 0x3f, 0x82, 0xa7, 0x62, 0x85, 0x46, 0x7d, 0xa2, 0x95, 0xc2, 0x3b, + 0xa1, 0xc5, 0x87, 0xeb, 0xef, 0xaf, 0x13, 0xcd, 0x4d, 0x50, 0xf2, 0x3c, 0xa5, + 0x74, 0x3c, 0x22, 0x5c, 0x38, 0x6d, 0x46, 0xd4, 0xac, 0x70, 0x83, 0x79, 0xef, + 0x99, 0x96, 0x74, 0x4b, 0x39, 0x12, 0x04, 0x4b, 0x35, 0x5f, 0x92, 0x7a, 0x67, + 0xaf, 0x1e, 0xf2, 0x6a, 0x71, 0x7f, 0xb5, 0xa8, 0x46, 0xac, 0x9d, 0xa1, 0x5e, + 0xa3, 0xf1, 0x8f, 0x8c, 0x36, 0x18, 0x3f, 0x87, 0x9b, 0xb9, 0xa3, 0xb2, 0x98, + 0xff, 0xf9, 0xa4, 0x89, 0x64, 0x6e, 0x77, 0x8e, 0x6d, 0x67, 0x01, 0xf9, 0xad, + 0xac, 0x7a, 0xe8, 0x82, 0x09, 0xa8, 0x43, 0xba, 0x8a, 0x55, 0xd1, 0x19, 0x2b, + 0xbe, 0xef, 0x31, 0xd0, 0x71, 0x45, 0x37, 0xf7, 0xa0, 0x35, 0xb0, 0x79, 0xc6, + 0xad, 0xd4, 0xab, 0x50, 0x61, 0x2d, 0x35, 0x89, 0x7a, 0x93, 0x3d, 0x49, 0xe8, + 0xef, 0x08, 0x6c, 0xdf, 0x96, 0xc8, 0x0d, 0x28, 0x56, 0xcc, 0xc7, 0xe4, 0x5f, + 0xc4, 0xef, 0xd4, 0xbf, 0x1b, 0x98, 0xab, 0x28, 0x89, 0x1b, 0x4a, 0xea, 0x7e, + 0xf8, 0x4c, 0xf7, 0x36, 0x93, 0x5c, 0x46, 0x6b, 0x24, 0x97, 0x4d, 0xf8, 0xf5, + 0x35, 0x5b, 0x8b, 0xa3, 0x20, 0xac, 0x5f, 0xbc, 0x47, 0x5a, 0xa2, 0xcf, 0x5a, + 0xd3, 0x77, 0x80, 0xbd, 0x9f, 0x9d, 0x46, 0x42, 0xcf, 0x6c, 0x2d, 0xc6, 0xb8, + 0x2f, 0x91, 0x7d, 0x09, 0xc4, 0xf7, 0x28, 0x88, 0xf9, 0x15, 0x53, 0x44, 0x7f, + 0xc5, 0x70, 0x26, 0x6d, 0xaa, 0xfd, 0x4b, 0x96, 0xcf, 0xe2, 0xa0, 0xb0, 0x67, + 0x92, 0x46, 0x9a, 0x72, 0x7d, 0xbe, 0xd0, 0x55, 0x91, 0xea, 0x60, 0x57, 0x32, + 0x20, 0x5e, 0x26, 0x05, 0x97, 0x8a, 0x3a, 0x90, 0x2c, 0x3c, 0xd6, 0x5f, 0x94, + 0x83, 0x00, 0xf7, 0x37, 0x51, 0x88, 0x15, 0xf4, 0x63, 0xd3, 0xc6, 0x1a, 0x18, + 0x9b, 0xc3, 0xbc, 0x84, 0xb0, 0x22, 0xf6, 0x3d, 0x65, 0x4f, 0x52, 0x0e, 0x3a, + 0x7a, 0xd8, 0x8e, 0x5d, 0x8d, 0xa1, 0x50, 0x14, 0xbe, 0x4b, 0xb9, 0x67, 0x99, + 0x27, 0xdc, 0x7e, 0x0f, 0xba, 0xf0, 0x58, 0xd9, 0x3f, 0x37, 0xc7, 0x2b, 0x28, + 0x6b, 0x02, 0xb7, 0x5f, 0x3c, 0xdb, 0xfb, 0x85, 0x0e, 0xed, 0x90, 0xcb, 0x23, + 0x39, 0x24, 0x32, 0xeb, 0xc3, 0x6b, 0xd2, 0x47, 0x54, 0x46, 0x9c, 0x03, 0x73, + 0x1a, 0x7e, 0xbb, 0xed, 0x28, 0x57, 0x78, 0x49, 0x81, 0xa0, 0x71, 0x67, 0x05, + 0xd9, 0xcb, 0x47, 0xd9, 0x87, 0xf8, 0x3d, 0x34, 0x21, 0xb1, 0x07, 0xd1, 0x55, + 0xdb, 0xb6, 0x61, 0xed, 0x08, 0xf2, 0xfc, 0x2e, 0x6b, 0x4a, 0x5b, 0x09, 0x77, + 0x64, 0x51, 0xd8, 0x73, 0xb2, 0xfc, 0x63, 0x68, 0x1c, 0xe3, 0x08, 0xc8, 0x08, + 0xf5, 0x38, 0x8c, 0xb1, 0xaa, 0x55, 0x89, 0xa1, 0x87, 0x73, 0xdb, 0x39, 0x07, + 0xa0, 0x6b, 0xef, 0x62, 0xd1, 0x29, 0x60, 0xaa, 0xe7, 0x2a, 0x2b, 0x89, 0x7e, + 0x26, 0xb5, 0x75, 0xfd, 0x04, 0x8a, 0x57, 0x22, 0x2c, 0x7c, 0x68, 0x0d, 0x54, + 0xdc, 0x73, 0x28, 0xd0, 0xf0, 0xf2, 0xd7, 0x0b, 0x43, 0x10, 0x8c, 0xb2, 0x0c, + 0x5c, 0x31, 0x16, 0x46, 0x31, 0xb0, 0xe5, 0xb3, 0xbd, 0x31, 0xb7, 0xdf, 0x8f, + 0x4c, 0x1f, 0xe1, 0x43, 0x4f, 0xa7, 0x47, 0x56, 0x70, 0x6f, 0x83, 0x10, 0x60, + 0xa5, 0xb7, 0x03, 0xdf, 0x9c, 0xd4, 0x2e, 0x24, 0x96, 0x0e, 0x50, 0x8a, 0x04, + 0x36, 0x11, 0x8d, 0x4a, 0x92, 0x07, 0xb6, 0xd8, 0x50, 0x59, 0x6d, 0xde, 0xbe, + 0x30, 0xf9, 0x28, 0xee, 0xea, 0xe7, 0x35, 0x98, 0xfb, 0x3d, 0x86, 0x9d, 0x2d, + 0x18, 0x15, 0xa9, 0xe1, 0x4d, 0x12, 0x79, 0xf7, 0xb4, 0xb6, 0x3f, 0x4b, 0xca, + 0x0f, 0x56, 0x68, 0x9b, 0xf8, 0x73, 0x3b, 0x03, 0x06, 0x49, 0x64, 0xa4, 0xb0, + 0x20, 0xb0, 0x60, 0xdc, 0xf4, 0x54, 0x71, 0xfa, 0x1d, 0x41, 0xe5, 0xee, 0x03, + 0xf9, 0xbd, 0x90, 0x65, 0x2b, 0x53, 0x72, 0x30, 0x3a, 0x3a, 0xb9, 0xbb, 0x2e, + 0xe3, 0x79, 0xb9, 0xaf, 0xcd, 0x1f, 0x6a, 0x3c, 0xb9, 0x00, 0x0b, 0xb1, 0x4e, + ], + script_code: Script(vec![0x53, 0x63, 0x63, 0xac, 0x63, 0x52]), + transparent_input: None, + hash_type: 1, + amount: 1152393991505765, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x58, 0x11, 0x0e, 0x23, 0x19, 0xad, 0x85, 0x50, 0x4a, 0x69, 0x8f, 0x73, 0xe7, + 0xac, 0x31, 0xa7, 0x23, 0xa0, 0x29, 0xec, 0x07, 0xb7, 0x72, 0xfb, 0xb3, 0x2f, + 0xba, 0x17, 0xff, 0xe2, 0xcc, 0x8d, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xb5, 0xcb, 0x96, 0x49, + 0x97, 0x9e, 0x3c, 0xcf, 0x75, 0xa8, 0xda, 0xd0, 0x54, 0x60, 0x26, 0x1f, 0xcd, + 0xcb, 0x00, 0x7a, 0xeb, 0xc1, 0x5e, 0x11, 0x67, 0x5c, 0x2d, 0xb4, 0xa6, 0xcb, + 0x79, 0x38, 0xe1, 0xfe, 0xb5, 0xcd, 0x04, 0x6a, 0x65, 0x00, 0x63, 0x44, 0x1e, + 0x16, 0xc7, 0x07, 0xf0, 0x97, 0x14, 0x47, 0x4c, 0x96, 0x16, 0x0a, 0xa6, 0x8e, + 0xaa, 0x12, 0x31, 0x79, 0x06, 0x9c, 0xd2, 0x20, 0x44, 0x06, 0x26, 0xcd, 0xfe, + 0xed, 0x65, 0xf9, 0xfa, 0xbd, 0xaa, 0x6d, 0xb1, 0x76, 0x0d, 0xa5, 0xd8, 0x06, + 0x63, 0x00, 0x53, 0x6a, 0x65, 0x52, 0xfd, 0xd0, 0xd2, 0xa9, 0x01, 0xfd, 0xc8, + 0x17, 0x1c, 0x9b, 0x0e, 0x06, 0x00, 0x01, 0x65, 0x26, 0x27, 0xba, 0x0e, 0x87, + 0xb5, 0xcd, 0x0f, 0xc8, 0x7b, 0xa3, 0xa2, 0x3c, 0x78, 0x02, 0x00, 0x00, 0x02, + 0x59, 0xb1, 0xb2, 0x59, 0xc5, 0xa2, 0xd8, 0xb7, 0xa6, 0x03, 0x9b, 0x0e, 0x12, + 0xac, 0xd8, 0x89, 0xb5, 0x1b, 0x47, 0x2d, 0xd5, 0x33, 0xa4, 0x61, 0xfb, 0x0c, + 0x3f, 0x96, 0xa9, 0xc0, 0x0a, 0x0b, 0x38, 0x39, 0xfa, 0x89, 0x77, 0x6f, 0xf0, + 0x98, 0xae, 0xef, 0xc7, 0x40, 0x34, 0xff, 0x8c, 0x1f, 0x0d, 0xae, 0x63, 0x68, + 0x32, 0x4c, 0xe5, 0xda, 0x68, 0xd7, 0x71, 0x35, 0x08, 0xae, 0x6d, 0x01, 0x1a, + 0xd0, 0x5f, 0xea, 0xf2, 0x03, 0x56, 0x5c, 0x71, 0xa0, 0x48, 0x66, 0x21, 0xbd, + 0xc4, 0x3c, 0x2a, 0x8e, 0xbb, 0x82, 0x61, 0xd8, 0x47, 0x42, 0x4a, 0x4c, 0xfd, + 0x0d, 0xad, 0xcf, 0x95, 0x9d, 0xb4, 0x37, 0x2b, 0x58, 0xa0, 0xde, 0x19, 0x78, + 0x9c, 0x91, 0xfc, 0x99, 0x31, 0xec, 0xbc, 0xac, 0x64, 0x19, 0xca, 0x0e, 0x5d, + 0x97, 0xa3, 0xb4, 0x1c, 0x76, 0xc8, 0xa1, 0x96, 0xc7, 0xa3, 0xad, 0xf5, 0x5b, + 0xdb, 0xe6, 0x0e, 0x85, 0x59, 0x26, 0x4b, 0x6d, 0x8e, 0xf7, 0x5d, 0x26, 0xdc, + 0x72, 0x0f, 0xe5, 0xec, 0x1f, 0x59, 0x66, 0x2d, 0x95, 0xd0, 0x8e, 0x78, 0x9e, + 0x3a, 0xd1, 0x82, 0x9e, 0x40, 0x11, 0x9a, 0xa7, 0x89, 0x7d, 0x89, 0x40, 0x4d, + 0xc4, 0x96, 0x60, 0x46, 0x68, 0xf5, 0x59, 0xca, 0x67, 0x43, 0x7d, 0x2b, 0xfb, + 0xb7, 0xf5, 0x1f, 0x36, 0xe0, 0xa5, 0xb7, 0x22, 0x8f, 0x05, 0xb6, 0xec, 0x57, + 0x89, 0xc1, 0x3f, 0xc2, 0x71, 0x95, 0x56, 0x15, 0x52, 0x63, 0x96, 0x6e, 0x81, + 0xf5, 0x21, 0x51, 0xe2, 0xf6, 0xe3, 0x68, 0x69, 0xd8, 0xa3, 0xc4, 0xc4, 0x96, + 0xa5, 0x13, 0x63, 0x2c, 0xaa, 0x8a, 0xbe, 0x1f, 0x27, 0x35, 0xeb, 0x60, 0xfc, + 0x12, 0x85, 0x82, 0x8e, 0xad, 0xdc, 0x54, 0x41, 0xa4, 0x02, 0xa3, 0xbf, 0x5b, + 0xcd, 0x22, 0x7c, 0xd8, 0x04, 0xe3, 0xc8, 0xca, 0x21, 0x24, 0x3c, 0xdf, 0xcd, + 0x53, 0xd8, 0x66, 0x05, 0xf3, 0xf8, 0xaf, 0x1a, 0x9c, 0xc5, 0x69, 0x33, 0x15, + 0x53, 0x28, 0x28, 0x01, 0x43, 0xfa, 0xdb, 0x3a, 0x1f, 0xc3, 0x3d, 0x76, 0x9f, + 0x07, 0xff, 0xc0, 0x1e, 0x35, 0x79, 0xe1, 0x18, 0x1f, 0x19, 0x15, 0xdb, 0x89, + 0xd8, 0x2e, 0x50, 0xbd, 0x74, 0x24, 0x08, 0x7c, 0x79, 0x7d, 0x9b, 0x7b, 0x3b, + 0x7d, 0x2a, 0x53, 0xb8, 0xff, 0xf9, 0xf2, 0xd9, 0x28, 0xab, 0x99, 0x6d, 0xce, + 0x5e, 0xd2, 0x71, 0x58, 0x98, 0xe4, 0x85, 0x8e, 0xec, 0x60, 0x78, 0xa9, 0x48, + 0x8d, 0x2d, 0xa6, 0xd1, 0x73, 0x05, 0xd0, 0xa3, 0x47, 0x18, 0x62, 0xa2, 0x22, + 0x38, 0xb9, 0xbe, 0xc2, 0x3e, 0xf2, 0xe2, 0x04, 0x1d, 0x50, 0x08, 0x73, 0x3e, + 0x9e, 0xa5, 0x66, 0x2c, 0x9f, 0xea, 0x0e, 0x4a, 0xfd, 0xf3, 0x27, 0x0c, 0x11, + 0x32, 0x3b, 0xa4, 0x8b, 0x35, 0x50, 0x85, 0x74, 0x40, 0x97, 0xf3, 0xf6, 0xc5, + 0x2e, 0xe4, 0x04, 0x31, 0x73, 0x9c, 0x5c, 0xa8, 0xdb, 0x2b, 0xda, 0x13, 0xda, + 0x9b, 0x33, 0x0b, 0x62, 0x00, 0x0b, 0x79, 0xfd, 0x35, 0x44, 0xb1, 0x31, 0x83, + 0x15, 0x9d, 0x17, 0x4f, 0xfe, 0xd2, 0x54, 0x85, 0x40, 0xa5, 0x2e, 0xe4, 0xb6, + 0x2d, 0x35, 0xaa, 0x5a, 0x58, 0x63, 0xf2, 0xba, 0xa4, 0x47, 0x5f, 0x3e, 0xb6, + 0xc7, 0x35, 0x9d, 0xc8, 0x39, 0xdb, 0xc8, 0x68, 0x90, 0xd1, 0x99, 0xd8, 0xea, + 0x6c, 0x9d, 0x97, 0xf1, 0x9e, 0x79, 0x2c, 0x7b, 0xcb, 0x66, 0x25, 0xff, 0x32, + 0xb7, 0x31, 0x57, 0x5f, 0x62, 0xd9, 0x44, 0xc8, 0x06, 0xb3, 0xf9, 0x3c, 0x04, + 0xb7, 0x3a, 0x98, 0xb2, 0x73, 0x43, 0xeb, 0x25, 0xa0, 0x6c, 0x87, 0x53, 0x60, + 0xde, 0x1a, 0x14, 0x38, 0x84, 0x0a, 0xd0, 0x66, 0x1d, 0xeb, 0xdc, 0x9b, 0x82, + 0x8a, 0xd0, 0xcb, 0xc0, 0x01, 0x1b, 0x32, 0x35, 0xb2, 0xc7, 0x53, 0x77, 0x78, + 0xf4, 0x58, 0x82, 0x1b, 0x83, 0xaa, 0x4c, 0xb3, 0xe5, 0x4e, 0xd0, 0x61, 0x3e, + 0x32, 0xe6, 0x3e, 0xf9, 0x85, 0xf9, 0x35, 0xbd, 0x7f, 0xf8, 0xc7, 0x70, 0x5c, + 0x89, 0xc0, 0xbb, 0xcc, 0xda, 0x9e, 0x66, 0x5e, 0x3b, 0x06, 0xba, 0x87, 0x9f, + 0xdd, 0xf3, 0x5e, 0x0b, 0x2f, 0x60, 0xc2, 0xa7, 0x0c, 0xb8, 0xeb, 0x9d, 0xe2, + 0xf5, 0xd7, 0x38, 0xc0, 0x5e, 0x34, 0xe5, 0x0f, 0x1f, 0x26, 0x19, 0x25, 0x8b, + 0x89, 0xe5, 0x73, 0xda, 0x55, 0x75, 0x46, 0x3d, 0x2e, 0x3b, 0xce, 0x39, 0xf7, + 0x0e, 0xb4, 0x55, 0x26, 0xcd, 0x99, 0xfa, 0xd9, 0x0f, 0x97, 0x92, 0xd0, 0xcd, + 0x59, 0x3b, 0xa8, 0x6a, 0xa1, 0xae, 0xa5, 0x03, 0xdd, 0xca, 0x5e, 0x3e, 0x57, + 0x37, 0xe6, 0xfc, 0x7b, 0xab, 0x27, 0x85, 0x12, 0x69, 0x20, 0xc4, 0x47, 0xd5, + 0xe5, 0x6a, 0x75, 0xdb, 0xe8, 0x9d, 0x68, 0x8b, 0xc0, 0xda, 0xa7, 0x9a, 0xa6, + 0x2d, 0xe9, 0xea, 0x29, 0x55, 0xf7, 0x1e, 0x1a, 0x61, 0x68, 0x2a, 0x61, 0x78, + 0xf8, 0x0b, 0xca, 0xda, 0x3b, 0x97, 0xae, 0xec, 0x77, 0xd9, 0xc8, 0x56, 0x3b, + 0x06, 0x9e, 0xa0, 0x13, 0x2f, 0x72, 0x3f, 0xbe, 0x75, 0x60, 0x2d, 0xd6, 0x29, + 0xac, 0x48, 0x09, 0x93, 0xd3, 0x71, 0x4f, 0xf0, 0x2c, 0x97, 0x0e, 0xbd, 0x83, + 0xe6, 0xd6, 0xcb, 0xbe, 0x39, 0x08, 0x6b, 0x03, 0x54, 0x20, 0xe0, 0xc2, 0x75, + 0x62, 0x86, 0x58, 0xa3, 0xba, 0x92, 0x30, 0x5c, 0xc0, 0x76, 0x98, 0xf1, 0x2e, + 0xe1, 0xe4, 0x17, 0x13, 0x70, 0xac, 0x39, 0xdf, 0x0e, 0x46, 0x6d, 0xc8, 0xec, + 0xc3, 0x9d, 0xa5, 0xee, 0x47, 0xb6, 0x82, 0x9d, 0xbb, 0xa9, 0x97, 0x0f, 0x03, + 0x58, 0xed, 0x68, 0x26, 0x49, 0x60, 0x5c, 0x7b, 0xfe, 0xe6, 0x93, 0x1a, 0x29, + 0x5b, 0x14, 0xa3, 0x40, 0x76, 0x00, 0x07, 0x4e, 0xdc, 0x79, 0xfa, 0x61, 0xe6, + 0x80, 0x6f, 0x11, 0x08, 0xd3, 0x34, 0xb4, 0xa5, 0x90, 0xf7, 0xa0, 0x26, 0xb0, + 0xeb, 0x02, 0x80, 0x4d, 0x39, 0x17, 0x46, 0x6e, 0x99, 0x91, 0x20, 0x64, 0x1c, + 0xe0, 0x7e, 0xbc, 0xdc, 0x99, 0x42, 0x60, 0x82, 0xe0, 0x77, 0x1f, 0x15, 0x9c, + 0x82, 0x6a, 0x9b, 0xe6, 0xce, 0xd7, 0x2d, 0x0e, 0x9c, 0xfa, 0x5b, 0x4b, 0x8a, + 0x86, 0x40, 0xca, 0x34, 0x88, 0xa1, 0xeb, 0x2b, 0x6e, 0x37, 0x4e, 0x8c, 0x2e, + 0x00, 0x3c, 0xdf, 0xa2, 0x32, 0x10, 0x37, 0x48, 0xb5, 0xc9, 0xdc, 0x11, 0xbb, + 0x30, 0xf6, 0x46, 0xb9, 0x73, 0xd7, 0x83, 0xf5, 0x99, 0x14, 0x17, 0x4e, 0x48, + 0xbd, 0x6a, 0x84, 0xfa, 0xd8, 0x9d, 0xbc, 0xa5, 0xc7, 0x6d, 0x0a, 0xb4, 0x14, + 0x5a, 0xbd, 0x08, 0xe4, 0xd0, 0xf2, 0xc7, 0x60, 0x25, 0xfc, 0x85, 0xfc, 0x11, + 0x6c, 0xca, 0x8d, 0x30, 0x2c, 0x8a, 0x3b, 0xeb, 0x26, 0x60, 0x3a, 0x1a, 0xf1, + 0xb5, 0x93, 0x91, 0xea, 0xf4, 0x71, 0x75, 0x9a, 0xdf, 0x19, 0x4c, 0x40, 0xc2, + 0x09, 0x29, 0x8c, 0xc0, 0x51, 0xfc, 0x79, 0x03, 0xfe, 0x40, 0x90, 0x2c, 0x35, + 0x6f, 0x28, 0x27, 0x9f, 0x27, 0x94, 0xbb, 0xb9, 0xe0, 0x0b, 0x1e, 0x22, 0x1b, + 0x0a, 0x26, 0x41, 0x06, 0xea, 0x50, 0x4f, 0xb8, 0x90, 0x6a, 0x20, 0x84, 0x5a, + 0x05, 0x9a, 0x60, 0x3b, 0x4f, 0x00, 0xe7, 0x83, 0x6d, 0x40, 0x67, 0xa6, 0x04, + 0x19, 0x5f, 0x24, 0x6a, 0x0f, 0x3b, 0x31, 0x82, 0x3f, 0xdf, 0x69, 0x57, 0x8c, + 0x47, 0xdb, 0x5b, 0x3d, 0xda, 0x86, 0xaa, 0xb1, 0xec, 0x9f, 0x58, 0xd9, 0x62, + 0x26, 0xc6, 0xb9, 0x1d, 0xc0, 0xf0, 0x3f, 0xe8, 0xd7, 0xdf, 0x23, 0xcf, 0x53, + 0xca, 0x8e, 0xa2, 0xa9, 0x09, 0x4f, 0xc0, 0x28, 0x65, 0x26, 0x7c, 0x88, 0xfa, + 0x8c, 0x01, 0x0e, 0xb5, 0x66, 0x13, 0x06, 0x6e, 0x50, 0xf1, 0x55, 0x4a, 0xa4, + 0x10, 0x8e, 0x25, 0xa9, 0xe9, 0x67, 0xd3, 0x4a, 0x9c, 0xf1, 0x02, 0x8c, 0x17, + 0x05, 0xfa, 0x37, 0x67, 0xf4, 0x6d, 0x4b, 0xab, 0x70, 0x28, 0xb0, 0x9b, 0x20, + 0x38, 0xfc, 0x1b, 0x72, 0x7f, 0x61, 0x9e, 0x61, 0xc4, 0xfc, 0x16, 0xbf, 0xfe, + 0x65, 0x7e, 0x99, 0x12, 0x6a, 0xc5, 0x18, 0x4f, 0xc8, 0x7f, 0x5e, 0x53, 0x01, + 0x88, 0x64, 0x23, 0xb3, 0x56, 0x87, 0x59, 0x09, 0xec, 0x92, 0xb3, 0x2d, 0x33, + 0x08, 0x42, 0x53, 0xa1, 0xb9, 0x7c, 0x5d, 0x2e, 0xd6, 0x6c, 0x7e, 0x22, 0xd1, + 0x85, 0x58, 0xfe, 0x82, 0xb5, 0xec, 0x88, 0xc6, 0x07, 0x05, 0x82, 0xfa, 0xcf, + 0x75, 0x6d, 0x70, 0x32, 0x38, 0xd9, 0xaf, 0x94, 0x19, 0x96, 0x6b, 0xe4, 0x62, + 0xdf, 0xbd, 0x31, 0x5c, 0x5b, 0xfa, 0xf0, 0x44, 0xaa, 0x69, 0x5a, 0x05, 0xe6, + 0x9d, 0x3d, 0x41, 0xe7, 0x73, 0x78, 0x75, 0x1d, 0x4e, 0x02, 0xc2, 0x66, 0xdf, + 0xb5, 0xcb, 0x6a, 0x7c, 0x40, 0x08, 0xf9, 0x44, 0x88, 0x83, 0x11, 0xe6, 0xde, + 0x37, 0xdc, 0x7b, 0xdf, 0x65, 0xd7, 0x0c, 0xab, 0x3e, 0x07, 0x8a, 0xb4, 0x4e, + 0x23, 0x2b, 0x41, 0x1c, 0xaf, 0xb2, 0x88, 0x4e, 0x26, 0x45, 0x95, 0xbe, 0xed, + 0xf9, 0xd4, 0x9a, 0x79, 0x36, 0xbb, 0x28, 0x7f, 0xe2, 0x8e, 0x1c, 0x29, 0x63, + 0x5e, 0xae, 0xca, 0x74, 0x7d, 0x06, 0x87, 0xcf, 0x46, 0x59, 0x02, 0xd2, 0x5f, + 0x5e, 0x51, 0x58, 0x48, 0x1d, 0xaa, 0xcd, 0xd3, 0x00, 0xb4, 0x77, 0x40, 0xbc, + 0x0c, 0x62, 0x77, 0xb4, 0x47, 0xcc, 0x26, 0x64, 0x04, 0x42, 0x43, 0xdd, 0x48, + 0x11, 0x40, 0x4e, 0xcb, 0xd7, 0xc7, 0xa6, 0x3c, 0x9f, 0xb7, 0xd9, 0x37, 0xbc, + 0xd8, 0x12, 0xc2, 0x34, 0x59, 0x23, 0xb5, 0x90, 0x26, 0x83, 0xbd, 0x2e, 0xd5, + 0x4c, 0x01, 0xae, 0x04, 0x19, 0xa7, 0xf5, 0x4e, 0x8a, 0x3a, 0x59, 0xc6, 0xa6, + 0xda, 0xcf, 0x89, 0xc7, 0x37, 0x0e, 0x79, 0xb5, 0x60, 0x13, 0x6a, 0x2b, 0x00, + 0xdd, 0xb6, 0x07, 0x4d, 0x74, 0xff, 0xc5, 0xc5, 0xdf, 0xd0, 0x6b, 0x6c, 0x51, + 0x9a, 0xbe, 0xc3, 0x59, 0x6a, 0x47, 0x61, 0x13, 0xbe, 0x41, 0x38, 0xee, 0xad, + 0x5f, 0xfd, 0xe8, 0x6b, 0x1e, 0x32, 0x40, 0x1f, 0xa3, 0x84, 0x62, 0x32, 0xd0, + 0xb3, 0xc9, 0xbd, 0x56, 0x88, 0xb6, 0x4a, 0x33, 0x09, 0x38, 0x16, 0x2a, 0x8b, + 0x89, 0x29, 0xd7, 0x0c, 0x1b, 0x67, 0x53, 0x62, 0xf4, 0xc2, 0xa9, 0xbb, 0x6b, + 0x7f, 0x91, 0xeb, 0xd4, 0x7d, 0x26, 0x3c, 0xf0, 0xa4, 0x05, 0xa2, 0x8b, 0xa7, + 0x41, 0x56, 0x44, 0xf9, 0x3b, 0x6c, 0xdf, 0xa3, 0xec, 0xeb, 0xb7, 0xb8, 0xd4, + 0xee, 0x8b, 0x94, 0xb2, 0x7b, 0x61, 0xe4, 0x03, 0x5e, 0xd6, 0xa4, 0x77, 0x46, + 0x7f, 0x4a, 0x32, 0x0b, 0x8a, 0x4e, 0xba, 0x0a, 0xb5, 0x6c, 0x26, 0x3e, 0x4b, + 0xfb, 0xe2, 0x6a, 0x41, 0x8e, 0xd1, 0xcd, 0xe6, 0x18, 0x4b, 0x89, 0x50, 0xfe, + 0x7a, 0xac, 0x7f, 0x20, 0xa4, 0x7b, 0xa1, 0xbf, 0xf9, 0x80, 0x4f, 0x53, 0xf6, + 0x93, 0x23, 0xdb, 0x84, 0x75, 0x20, 0xa6, 0x58, 0x47, 0xb3, 0x03, 0x4c, 0x4e, + 0x08, 0x1b, 0xb4, 0xb8, 0x69, 0x26, 0x3b, 0x5f, 0x9b, 0x3a, 0x7a, 0x83, 0x3b, + 0x6e, 0x4c, 0xa7, 0x90, 0xcc, 0xf9, 0xfd, 0xae, 0x80, 0x79, 0xe5, 0x56, 0x09, + 0x27, 0x2c, 0x63, 0xb5, 0x49, 0xb0, 0xc8, 0x5f, 0x11, 0x0c, 0xc9, 0xc9, 0x58, + 0x68, 0x01, 0x14, 0xb3, 0x11, 0x74, 0x80, 0xaf, 0x57, 0xcb, 0x15, 0x9e, 0xdf, + 0xbe, 0x5c, 0xb9, 0xc6, 0x2b, 0xce, 0x2c, 0xf2, 0xab, 0x29, 0xb6, 0x67, 0x11, + 0xac, 0x7a, 0xa5, 0x3a, 0x74, 0x9f, 0xfa, 0x83, 0x90, 0x7e, 0xcb, 0x69, 0x12, + 0xaa, 0x56, 0x96, 0x38, 0xde, 0xa1, 0x9e, 0x54, 0x41, 0x61, 0x1e, 0xfc, 0xa3, + 0x20, 0x99, 0x65, 0x3e, 0x8a, 0x5c, 0xa1, 0xfb, 0xbd, 0xba, 0xb1, 0xd6, 0x44, + 0x71, 0xec, 0x32, 0x0e, 0xc3, 0x8e, 0xa4, 0x88, 0x40, 0x0c, 0x9b, 0x1f, 0x4e, + 0x8c, 0xb5, 0x48, 0x0c, 0x0e, 0x92, 0x42, 0xb0, 0x86, 0xa8, 0x0e, 0xee, 0xd4, + 0x90, 0xae, 0x32, 0x00, 0x0c, 0x80, 0x09, 0xec, 0xb7, 0x1f, 0xfa, 0x39, 0xf4, + 0xf3, 0xb5, 0x74, 0x9c, 0xfd, 0x1b, 0xef, 0xe0, 0xd9, 0x66, 0x7a, 0xb3, 0x02, + 0x20, 0xc2, 0xdc, 0x04, 0x39, 0x36, 0x98, 0xb2, 0xcf, 0xa2, 0x04, 0x92, 0xf2, + 0x50, 0xce, 0x14, 0x32, 0x35, 0x81, 0x58, 0x70, 0x3d, 0xf7, 0xb1, 0x39, 0xd7, + 0x45, 0xce, 0x1f, 0xc3, 0x40, 0x78, 0x77, 0x01, 0xfb, 0x51, 0xdd, 0x5e, 0x48, + 0xb8, 0x95, 0x09, 0x41, 0x7d, 0x88, 0x89, 0x00, 0x80, 0x63, 0xf9, 0xba, 0x01, + 0x5a, 0x07, 0xd8, 0xd3, 0x9b, 0xbd, 0x00, 0x76, 0x2f, 0x59, 0x5a, 0xfa, 0xd8, + 0xd8, 0x59, 0xea, 0xab, 0xf0, 0xd8, 0x2d, 0x46, 0x33, 0xcf, 0x82, 0x98, 0xb0, + 0x9b, 0xea, 0x3f, 0x22, 0x28, 0x55, 0xa9, 0x2a, 0x08, 0x43, 0xf5, 0x2f, 0xa5, + 0x8d, 0xb3, 0xa1, 0x75, 0xc3, 0x0d, 0x2a, 0xbe, 0x64, 0x82, 0x64, 0x90, 0xcb, + 0xe6, 0xca, 0x14, 0x88, 0xfe, 0x3a, 0x01, 0x5a, 0x94, 0x6d, 0xc9, 0xc4, 0x5a, + 0xc3, 0x09, 0x25, 0x72, 0x7a, 0x13, 0xe0, 0x89, 0x78, 0xf7, 0x24, 0x03, 0x47, + 0x20, 0x8a, 0x4d, 0x25, 0x38, 0xc2, 0xd5, 0x61, 0x24, 0x37, 0x8c, 0x22, 0xc0, + 0x4e, 0x23, 0xdc, 0x28, 0xb1, 0x50, 0x19, 0xbe, 0x77, 0x6d, 0x70, 0xbf, 0xc1, + 0xd2, 0x64, 0x5b, 0x5e, 0x80, 0xd1, 0xfd, 0x84, 0x19, 0xdf, 0x72, 0x90, 0x43, + 0x80, 0xe2, 0xe1, 0xfc, 0x4d, 0xd1, 0xdf, 0x1b, 0xa3, 0xdf, 0xe4, 0x80, 0xcc, + 0x84, 0x6d, 0x51, 0x51, 0x4a, 0x06, 0x5e, 0xd7, 0x62, 0x78, 0x7a, 0xfd, 0x6e, + 0xb9, 0x0b, 0xdf, 0x8f, 0xbb, 0xad, 0x5e, 0xb3, 0xd2, 0x3f, 0xdc, 0x8c, 0x54, + 0xcc, 0xa1, 0x0f, 0xa1, 0xfe, 0x54, 0x64, 0x82, 0xf5, 0xe1, 0x42, 0x4b, 0xfd, + 0xa8, 0x7a, 0xa7, 0xfb, 0x78, 0x6e, 0x26, 0x0f, 0x26, 0x14, 0xbe, 0x08, 0x11, + 0xee, 0x16, 0xb8, 0xd2, 0x9d, 0xf9, 0xa0, 0xf3, 0x30, 0xe9, 0x70, 0x9f, 0x63, + 0xc9, 0x50, 0xfb, 0xd9, 0x03, 0xff, 0x7d, 0x5b, 0x0c, 0xa2, 0x9f, 0xd6, 0x3b, + 0x0f, 0x97, 0x51, 0x77, 0x69, 0x02, 0x5c, 0xc3, 0x6a, 0x52, 0xe0, 0x00, 0x15, + 0x93, 0x4a, 0x3c, 0xa2, 0x58, 0xb8, 0xba, 0xb9, 0x00, 0x16, 0xa4, 0x01, 0xd5, + 0xd8, 0xd7, 0xc3, 0xb9, 0x44, 0x92, 0x5b, 0x35, 0xa9, 0x34, 0x9a, 0x1a, 0xc7, + 0xd9, 0x85, 0x21, 0x61, 0x0c, 0x2f, 0xad, 0x8b, 0x5c, 0x8b, 0x31, 0x9c, 0xd6, + 0xe0, 0x5f, 0x9b, 0xbe, 0xd3, 0x53, 0xf1, 0xd0, 0xc8, 0x65, 0xa9, 0x4a, 0xa4, + 0x56, 0xdc, 0xd1, 0x8a, 0x39, 0xe2, 0xf5, 0x85, 0xd9, 0xbe, 0xa8, + ], + script_code: Script(vec![0x63, 0x00, 0x6a, 0x53, 0x63, 0x6a, 0xac, 0x00]), + transparent_input: None, + hash_type: 1, + amount: 1788797765223798, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0xcb, 0xfa, 0x22, 0x69, 0x9b, 0x04, 0xbe, 0xb7, 0x67, 0x07, 0xb5, 0x1d, 0x62, + 0x5e, 0x94, 0xd2, 0x6c, 0x0d, 0xf8, 0xad, 0xa7, 0xcf, 0x68, 0xfc, 0xde, 0xd9, + 0x60, 0x65, 0x4b, 0x20, 0xf3, 0x60, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x00, 0x02, 0x12, 0x9a, 0x03, + 0xd5, 0x7d, 0x32, 0x07, 0x00, 0x07, 0x52, 0x51, 0xac, 0x51, 0x65, 0xac, 0x00, + 0x91, 0xdb, 0xbd, 0x6f, 0xf3, 0x8f, 0x01, 0x00, 0x05, 0x53, 0x6a, 0x63, 0x63, + 0x53, 0x6f, 0x34, 0xc4, 0x99, 0x32, 0x68, 0xc7, 0x09, 0xef, 0xf1, 0x20, 0x1b, + 0x50, 0x92, 0x05, 0x00, 0x02, 0x3b, 0x5c, 0x8b, 0x5b, 0x80, 0xe7, 0x7b, 0x87, + 0xf1, 0xeb, 0x73, 0xaf, 0x77, 0x60, 0xed, 0xae, 0x0e, 0x19, 0x3e, 0x38, 0x96, + 0xb1, 0x5c, 0x55, 0x8f, 0x00, 0x4e, 0x7c, 0x7d, 0x93, 0x24, 0xd3, 0x85, 0xb4, + 0x50, 0xcd, 0x4b, 0x98, 0x2a, 0xba, 0x8d, 0x2e, 0x91, 0xf4, 0x1f, 0x22, 0xee, + 0xe7, 0xf3, 0x6d, 0x79, 0xcc, 0xa9, 0xc0, 0xe0, 0x1b, 0x26, 0xc4, 0x65, 0x11, + 0x18, 0xea, 0x77, 0x15, 0x14, 0xc7, 0x7e, 0xd6, 0x0c, 0xd5, 0x24, 0x51, 0x94, + 0x2d, 0xc8, 0x5b, 0x3f, 0xba, 0x44, 0x8b, 0x2d, 0x63, 0x10, 0xf2, 0x77, 0x79, + 0x42, 0x83, 0x2e, 0x21, 0xcf, 0x3d, 0x44, 0x87, 0x4f, 0x8d, 0xca, 0x98, 0x2b, + 0x68, 0x7c, 0x9e, 0xd7, 0xe0, 0xb2, 0x32, 0x77, 0x07, 0x3c, 0x19, 0x30, 0xa4, + 0x73, 0xd1, 0x66, 0x8e, 0xf2, 0xe9, 0xae, 0x96, 0x63, 0xcf, 0xf0, 0x58, 0x16, + 0x62, 0x6c, 0xd3, 0xc5, 0xbf, 0x77, 0x16, 0x53, 0xd7, 0x78, 0x51, 0x81, 0x35, + 0x5c, 0x05, 0xae, 0xd2, 0x4a, 0x99, 0xc4, 0xb6, 0x74, 0xd2, 0x4a, 0x0f, 0x08, + 0xf4, 0xb0, 0xcf, 0xbe, 0x90, 0xf2, 0xfd, 0xba, 0xb4, 0x24, 0x82, 0xe9, 0x8f, + 0x13, 0xff, 0xfc, 0xd1, 0xad, 0x33, 0xf4, 0xf4, 0xc0, 0x4d, 0xeb, 0xc8, 0x9f, + 0x40, 0xb5, 0xdb, 0xf6, 0x45, 0x46, 0xc5, 0x20, 0xdc, 0xa5, 0xd0, 0xec, 0xf3, + 0xf6, 0x5d, 0x3a, 0x77, 0xd0, 0x12, 0x9f, 0x60, 0x03, 0x71, 0x10, 0x8a, 0xac, + 0x30, 0xa9, 0xec, 0xa8, 0xbe, 0xe5, 0x52, 0x4f, 0xab, 0x67, 0x1f, 0xc0, 0x86, + 0x58, 0x76, 0x2c, 0x87, 0x38, 0xab, 0xc9, 0xfa, 0x76, 0x93, 0xe3, 0x9d, 0x39, + 0xd7, 0x03, 0xd5, 0xcd, 0x94, 0x2b, 0x5a, 0x55, 0xfe, 0xda, 0xfe, 0xcc, 0xae, + 0xf7, 0x02, 0x17, 0x69, 0xe9, 0x2c, 0xc9, 0xd3, 0xac, 0x7b, 0x4c, 0x23, 0xb3, + 0x3f, 0xc2, 0x23, 0x21, 0x85, 0x4b, 0xa3, 0x3f, 0x49, 0xee, 0xba, 0xdd, 0xca, + 0x29, 0xb3, 0x56, 0x40, 0xe4, 0xf0, 0xc2, 0xfd, 0x8c, 0x12, 0xb9, 0x84, 0x52, + 0x97, 0x60, 0xe0, 0x65, 0xfe, 0xcb, 0xa1, 0x21, 0x86, 0xd2, 0x0a, 0xee, 0xc3, + 0xda, 0x58, 0xfc, 0x35, 0x9b, 0xa8, 0x25, 0xe5, 0xb8, 0xe2, 0xe1, 0x8f, 0x12, + 0xcf, 0x29, 0x49, 0xc3, 0x12, 0xf6, 0x3c, 0x4d, 0xd7, 0xa7, 0x9b, 0x0e, 0x66, + 0xb9, 0xc8, 0xb6, 0x6f, 0xe8, 0x9a, 0xd7, 0xed, 0xc6, 0x2a, 0xc4, 0xd2, 0x07, + 0xe2, 0x77, 0xb9, 0x33, 0xb0, 0xc2, 0x06, 0xdd, 0x7c, 0x22, 0xd2, 0xdb, 0x26, + 0x33, 0xfc, 0x01, 0xa8, 0x3c, 0x24, 0xfc, 0xad, 0x40, 0x9c, 0xee, 0xd5, 0x36, + 0xa6, 0xd3, 0xe8, 0xe0, 0x8d, 0x42, 0xb5, 0x13, 0x48, 0x97, 0xb4, 0x36, 0xbf, + 0xf3, 0xa1, 0xbc, 0xef, 0xc5, 0x3a, 0xec, 0x30, 0xed, 0x89, 0x11, 0x0f, 0x89, + 0x60, 0x88, 0x8a, 0x1c, 0xf2, 0x41, 0x5c, 0xc6, 0x93, 0xa8, 0x52, 0x97, 0xd6, + 0xb7, 0x89, 0x14, 0x1e, 0x04, 0x1a, 0x3c, 0x14, 0xa5, 0xf9, 0xc6, 0x46, 0x33, + 0xbe, 0x06, 0x56, 0x45, 0xe9, 0xca, 0x36, 0x37, 0xf3, 0x73, 0x83, 0x04, 0xec, + 0x3b, 0x16, 0x51, 0x31, 0x46, 0x83, 0xa0, 0x27, 0x5e, 0x73, 0x36, 0x79, 0x70, + 0x01, 0x06, 0x78, 0x23, 0x17, 0x79, 0x3e, 0x86, 0x6c, 0xed, 0x59, 0x89, 0x21, + 0x3f, 0x3b, 0xac, 0xfc, 0xfd, 0x20, 0x02, 0xea, 0x86, 0x6f, 0x3f, 0x17, 0x07, + 0x35, 0x12, 0x64, 0xb6, 0x67, 0x88, 0xf4, 0xeb, 0x7f, 0x68, 0xc5, 0xa5, 0x36, + 0xfa, 0x9c, 0x13, 0x0d, 0x20, 0x26, 0xea, 0x80, 0x97, 0x94, 0xd3, 0xb7, 0x4d, + 0x78, 0x01, 0x7e, 0xe0, 0xfb, 0xca, 0x83, 0xcc, 0x7e, 0x5c, 0xbd, 0x52, 0x7a, + 0xcd, 0xe7, 0x46, 0x53, 0x73, 0x51, 0x2c, 0x07, 0x64, 0x6a, 0x62, 0xc6, 0x0f, + 0x5c, 0x16, 0xc2, 0xef, 0x9f, 0x41, 0x8d, 0x8c, 0x7d, 0x18, 0x8f, 0x7b, 0x13, + 0xdd, 0x45, 0x38, 0xa5, 0x5d, 0x18, 0x6a, 0xd6, 0x36, 0x2a, 0x58, 0x9a, 0x9f, + 0x52, 0xb2, 0x5e, 0x61, 0x6f, 0xb2, 0xa3, 0x57, 0xac, 0xca, 0xde, 0x63, 0x57, + 0xfa, 0x5a, 0x42, 0xa7, 0x98, 0xe4, 0x17, 0x13, 0x11, 0xad, 0xe9, 0xcc, 0xfd, + 0x15, 0xf2, 0x7c, 0x8c, 0x19, 0x72, 0x17, 0x9d, 0x26, 0x1f, 0xb9, 0xb0, 0x9b, + 0xc7, 0xa0, 0x36, 0xc1, 0x05, 0x55, 0x9b, 0x04, 0x38, 0x9d, 0xfd, 0x8a, 0x7b, + 0xe2, 0xa3, 0xae, 0x2b, 0xba, 0x2a, 0xfb, 0xd1, 0xe9, 0xbf, 0x90, 0x05, 0xc8, + 0xb3, 0x66, 0x35, 0x4f, 0x90, 0x9b, 0xe7, 0x1e, 0x52, 0xc0, 0x90, 0x80, 0xfb, + 0xa7, 0x45, 0x23, 0x77, 0xe8, 0xf1, 0x2c, 0x18, 0x4f, 0xe7, 0xed, 0x46, 0x5b, + 0x32, 0xc9, 0xf9, 0xb2, 0x81, 0x9e, 0xa1, 0xd1, 0x19, 0xfc, 0x26, 0x7c, 0x8a, + 0x75, 0x33, 0x81, 0xeb, 0x51, 0xac, 0xf8, 0x54, 0xc1, 0x9e, 0x8d, 0x58, 0xff, + 0x42, 0x74, 0xeb, 0xa8, 0xc6, 0x3f, 0x0f, 0xa1, 0x70, 0xa6, 0x3c, 0xbf, 0xce, + 0x2c, 0xf8, 0x7b, 0xdc, 0xdf, 0x32, 0xb7, 0xe1, 0x98, 0x04, 0x54, 0x1c, 0x2c, + 0x58, 0x97, 0x24, 0xef, 0xc6, 0x9b, 0xc4, 0x65, 0xd0, 0x90, 0x8e, 0x09, 0xb8, + 0x4d, 0x1f, 0x50, 0x41, 0x2b, 0xb0, 0x7f, 0x47, 0xfb, 0x9f, 0x0d, 0x47, 0x29, + 0x28, 0x16, 0x14, 0xca, 0xca, 0xb6, 0x14, 0xef, 0x65, 0xce, 0xba, 0x13, 0x96, + 0xb5, 0x24, 0x9d, 0x2c, 0x61, 0x70, 0x4f, 0xb6, 0xf3, 0x48, 0x44, 0x71, 0x83, + 0xf9, 0x88, 0x2a, 0x98, 0xae, 0x9c, 0x71, 0xa7, 0x66, 0x33, 0xe0, 0x5b, 0x33, + 0x3a, 0x1b, 0xce, 0xee, 0xc9, 0xbd, 0x44, 0xb8, 0x87, 0x6f, 0xab, 0x6c, 0xd7, + 0x2a, 0x5e, 0x33, 0x5c, 0x97, 0x7a, 0x04, 0x55, 0xc5, 0x36, 0x5f, 0xe8, 0x7f, + 0x17, 0xa0, 0x5c, 0x0f, 0x8c, 0x23, 0x3b, 0x97, 0x29, 0xc1, 0x09, 0x3b, 0xae, + 0xb8, 0x57, 0x5c, 0xe5, 0xfd, 0xfb, 0xfd, 0x6e, 0x6a, 0x8e, 0xbf, 0x76, 0x46, + 0x47, 0x81, 0xf9, 0xb2, 0x10, 0xed, 0xb3, 0x9d, 0x20, 0x6a, 0x68, 0x5d, 0x0d, + 0xc7, 0xec, 0x06, 0x8e, 0x3c, 0x6b, 0x13, 0xd2, 0xf2, 0xaa, 0x74, 0x11, 0x92, + 0xbc, 0x86, 0x25, 0xab, 0xd3, 0x0d, 0xb6, 0xe6, 0x64, 0xfb, 0x33, 0x30, 0xf9, + 0x5c, 0xb3, 0x1b, 0xa1, 0x29, 0x0b, 0xd7, 0xf8, 0x30, 0x31, 0xc7, 0x89, 0xc2, + 0x4f, 0xd5, 0x73, 0x93, 0x46, 0x90, 0xa7, 0x3b, 0x54, 0xa9, 0x05, 0xdf, 0x8e, + 0x1d, 0x59, 0x32, 0x2f, 0x26, 0x2b, 0xbf, 0xbe, 0x95, 0xcc, 0x5b, 0x9b, 0x1e, + 0x20, 0x31, 0x0b, 0x76, 0x35, 0x0b, 0x4d, 0x60, 0x4c, 0xd1, 0xa4, 0x58, 0x66, + 0x1d, 0xc4, 0x74, 0xfe, 0x4c, 0x58, 0x79, 0x04, 0xc0, 0x53, 0x47, 0x5e, 0x17, + 0x61, 0xb8, 0x0a, 0x60, 0xcc, 0x48, 0xed, 0xd9, 0x54, 0x34, 0xdf, 0x02, 0x3b, + 0x94, 0xa5, 0x8a, 0x99, 0xd6, 0x25, 0x66, 0xe0, 0x0f, 0x67, 0x77, 0x90, 0xdc, + 0xa0, 0x76, 0xa4, 0xf1, 0x67, 0x47, 0x0c, 0x43, 0xa8, 0x1e, 0x6c, 0x32, 0xf0, + 0xd0, 0x0d, 0x23, 0x65, 0x6b, 0xa7, 0x48, 0x28, 0xb8, 0xe4, 0xd4, 0x75, 0x38, + 0xe5, 0x0c, 0x0e, 0xce, 0xe2, 0xcd, 0xfe, 0x0d, 0x59, 0x43, 0xe2, 0x3e, 0x3f, + 0x17, 0x33, 0x82, 0x9d, 0x3e, 0x1b, 0x80, 0x53, 0x93, 0x30, 0xe0, 0x6c, 0x6a, + 0xe3, 0xd0, 0xec, 0xe7, 0x38, 0xc0, 0xdd, 0x74, 0x2a, 0xa5, 0x86, 0x0f, 0x43, + 0xb5, 0x30, 0xf0, 0x3d, 0xc5, 0x5d, 0xeb, 0xf7, 0x20, 0x12, 0x3f, 0x8f, 0xba, + 0xf2, 0xe5, 0x68, 0x59, 0xa5, 0x34, 0x3d, 0x46, 0x12, 0xee, 0x21, 0x46, 0x4d, + 0xb2, 0x50, 0x1d, 0x4f, 0x35, 0x31, 0x47, 0xf3, 0xe1, 0xa5, 0xab, 0xb8, 0x93, + 0x85, 0x08, 0x16, 0xc8, 0x0a, 0xf2, 0x9d, 0x88, 0x92, 0x48, 0xc9, 0x2a, 0x72, + 0x9a, 0x0e, 0x2b, 0xe2, 0xb6, 0x6c, 0xc1, 0x3a, 0xc5, 0xd9, 0x96, 0xb2, 0x50, + 0x14, 0x66, 0x6d, 0xdc, 0x63, 0x8a, 0x1f, 0xd2, 0xa0, 0xaf, 0xee, 0x93, 0xd9, + 0x8e, 0x31, 0xdc, 0x1e, 0xa8, 0x58, 0xd7, 0x2b, 0x84, 0xbb, 0xd3, 0x2f, 0xc0, + 0xc6, 0x16, 0xe7, 0xd4, 0xab, 0xda, 0xf3, 0xc1, 0x8f, 0xf9, 0x60, 0x13, 0x24, + 0x5d, 0x83, 0xb3, 0xbd, 0xf9, 0x21, 0xf4, 0x03, 0xf1, 0xae, 0xcf, 0xdd, 0xd8, + 0x85, 0xfd, 0xcf, 0xc7, 0x33, 0x87, 0x0f, 0x76, 0x0c, 0xb8, 0x7e, 0xd4, 0xfc, + 0xd9, 0xcc, 0xa9, 0x33, 0x2e, 0x8e, 0x1c, 0x85, 0x62, 0x3b, 0x20, 0x66, 0x09, + 0xf8, 0x87, 0xeb, 0xdb, 0xcf, 0x9d, 0xa1, 0x0f, 0x38, 0x14, 0x19, 0x7a, 0x9f, + 0x82, 0x07, 0x05, 0xea, 0xa1, 0x28, 0x3a, 0xc7, 0x93, 0x16, 0x83, 0x08, 0x3f, + 0x22, 0xfc, 0x4d, 0xc7, 0xff, 0x68, 0x1a, 0xb8, 0x46, 0x18, 0x6f, 0x22, 0xd5, + 0x73, 0x08, 0x43, 0xde, 0x71, 0x00, 0xf0, 0x31, 0x17, 0xa3, 0xbb, 0xa0, 0x64, + 0xca, 0x3c, 0xea, 0x93, 0xf3, 0xab, 0xd3, 0x0b, 0xe6, 0xdb, 0x09, 0x35, 0x52, + 0x9d, 0xed, 0x0b, 0x50, 0xec, 0xef, 0x9f, 0x59, 0x6d, 0xb0, 0x1a, 0x87, 0xa8, + 0xda, 0xdb, 0x82, 0x7a, 0x1b, 0xe8, 0xb5, 0x79, 0x9b, 0x33, 0xc9, 0x9a, 0x82, + 0x2b, 0x73, 0xf7, 0xe6, 0x62, 0xed, 0x6f, 0x86, 0x03, 0x45, 0xa2, 0x62, 0x83, + 0xc1, 0xb4, 0x08, 0x0e, 0xcd, 0xf5, 0x79, 0xd7, 0x0e, 0x7b, 0x0c, 0x0a, 0xb7, + 0x1e, 0x11, 0x6e, 0xe2, 0xd9, 0xda, 0x27, 0x46, 0x1e, 0x28, 0x12, 0x2a, 0x09, + 0xca, 0x04, 0xde, 0x38, 0x76, 0x50, 0x2f, 0xd2, 0x4d, 0xff, 0x92, 0x09, 0x55, + 0x2f, 0x91, 0x13, 0x87, 0x70, 0x78, 0xa0, 0x94, 0xe0, 0xe5, 0xf8, 0xce, 0xbb, + 0x41, 0x54, 0xe0, 0x3a, 0x6b, 0x56, 0xf6, 0x04, 0xdf, 0x98, 0x4b, 0xd2, 0x9e, + 0xfd, 0x4f, 0x88, 0xc3, 0xf6, 0x29, 0xea, 0x2b, 0xba, 0x91, 0x27, 0xea, 0x5a, + 0x6c, 0xc5, 0xa3, 0x9d, 0x74, 0x1e, 0xdd, 0x71, 0x1a, 0x24, 0x44, 0x7f, 0xe0, + 0x6c, 0xf8, 0x45, 0x5a, 0x44, 0x06, 0x5e, 0x24, 0x52, 0x76, 0x3b, 0x0d, 0x93, + 0xf8, 0x6a, 0x31, 0x47, 0xbd, 0x08, 0x75, 0x7a, 0x4f, 0x7a, 0xa7, 0x79, 0x3c, + 0x97, 0x82, 0x1c, 0x2b, 0x57, 0x22, 0xc9, 0xdb, 0xad, 0x20, 0xf6, 0xa1, 0xe7, + 0xad, 0xf6, 0x8b, 0xf2, 0x22, 0x7b, 0xe5, 0x12, 0x04, 0xe9, 0xde, 0xca, 0x8d, + 0x9e, 0xb6, 0x26, 0x6f, 0x65, 0x9b, 0x33, 0x55, 0xc8, 0x97, 0x7e, 0xae, 0x7e, + 0x9e, 0xd5, 0x39, 0xd1, 0x79, 0x39, 0xf0, 0xc6, 0x16, 0x6b, 0x01, 0x13, 0x2d, + 0xb0, 0x01, 0x66, 0x25, 0x0e, 0xa9, 0x64, 0xe3, 0x9d, 0x9d, 0x55, 0xab, 0x43, + 0x9a, 0x29, 0xbb, 0x0b, 0xcf, 0xd3, 0xa9, 0x99, 0xb3, 0x1f, 0xe7, 0xa9, 0x51, + 0x00, 0x2e, 0xe5, 0xdc, 0x01, 0x27, 0x03, 0x24, 0xb1, 0x10, 0x10, 0x37, 0x89, + 0x29, 0x42, 0x90, 0x7c, 0x6e, 0x19, 0x50, 0x9a, 0x6c, 0x5f, 0x66, 0x59, 0xba, + 0xf7, 0xf4, 0x36, 0x3c, 0x49, 0x15, 0xe6, 0x1b, 0xda, 0x34, 0x06, 0x9b, 0xd9, + 0x86, 0xb6, 0x37, 0x7f, 0xf6, 0x04, 0xed, 0xe5, 0xa7, 0x42, 0x5d, 0xb2, 0x88, + 0x86, 0xb1, 0xa2, 0x61, 0x36, 0x6d, 0xa8, 0xa1, 0x39, 0x86, 0x65, 0xbe, 0xed, + 0x3b, 0xe9, 0xbc, 0x2e, 0x05, 0x5e, 0x71, 0x1b, 0x7d, 0x36, 0xdd, 0xbd, 0xd3, + 0x65, 0xcc, 0xdc, 0xd7, 0xfc, 0xba, 0xfe, 0x71, 0x29, 0x66, 0x95, 0x08, 0xda, + 0xc0, 0xad, 0x2d, 0x55, 0xee, 0x7f, 0xc6, 0x0b, 0xce, 0x22, 0x88, 0x50, 0xba, + 0x7b, 0x94, 0x3a, 0x8d, 0x50, 0xff, 0xcb, 0x2a, 0x67, 0x06, 0x51, 0xd3, 0x15, + 0xd8, 0x71, 0x9c, 0x7b, 0x57, 0xf6, 0x37, 0xa3, 0x7e, 0xdd, 0x32, 0x6a, 0xbc, + 0x76, 0xf0, 0xa7, 0x69, 0x0c, 0x23, 0x68, 0x80, 0x16, 0x01, 0x07, 0xc2, 0xb4, + 0xc8, 0x5e, 0xcf, 0x2a, 0xd9, 0xf5, 0xdd, 0x26, 0x45, 0x62, 0x6e, 0x40, 0x90, + 0xf1, 0x00, 0x47, 0xcc, 0x13, 0x15, 0x40, 0xca, 0x58, 0x03, 0x04, 0x5a, 0x6a, + 0xee, 0x91, 0xea, 0x0b, 0x3f, 0x9b, 0x77, 0xc4, 0x43, 0x40, 0x69, 0xc5, 0x32, + 0x0c, 0xf5, 0xb7, 0x01, 0x82, 0xd9, 0xfb, 0xbf, 0x30, 0x98, 0x30, 0x60, 0x11, + 0x75, 0x9d, 0x0d, 0x64, 0xa8, 0x84, 0x14, 0x1e, 0xa0, 0x21, 0xcd, 0xd9, 0x5e, + 0xfa, 0x32, 0x63, 0xa5, 0x05, 0xb8, 0x52, 0x29, 0xd1, 0x54, 0xec, 0xaa, 0x23, + 0x5e, 0x8f, 0xa1, 0x07, 0x95, 0xc9, 0xda, 0x27, 0x41, 0xcd, 0x98, 0x71, 0x90, + 0x16, 0xa9, 0x01, 0x17, 0xa7, 0x6f, 0x84, 0xf0, 0x0b, 0x5c, 0x3d, 0x4b, 0xce, + 0xd7, 0x9a, 0x73, 0xbf, 0xb3, 0xa1, 0xc7, 0x8a, 0xd1, 0xad, 0xea, 0x50, 0x78, + 0xf2, 0xf1, 0xb0, 0x0f, 0x81, 0x5b, 0xc7, 0xa3, 0x0e, 0xf8, 0x58, 0x40, 0x07, + 0x77, 0x32, 0xdc, 0xb1, 0xa6, 0x1e, 0xd4, 0xbc, 0xbd, 0x66, 0x35, 0x28, 0x50, + 0x29, 0x77, 0x94, 0xad, 0x67, 0xd2, 0x93, 0xdc, 0xe9, 0x10, 0x61, 0x13, 0x0c, + 0xa4, 0x8b, 0xab, 0xca, 0xaa, 0xd6, 0x0b, 0x1f, 0x7c, 0xed, 0x07, 0xac, 0x3f, + 0xf3, 0x32, 0xd5, 0xc8, 0xd3, 0x2b, 0xa2, 0xf1, 0xe7, 0x8a, 0x23, 0xb0, 0x66, + 0x29, 0xb8, 0x89, 0x06, 0x6f, 0x07, 0x9b, 0xcd, 0xa2, 0x9f, 0xb5, 0xc8, 0x3b, + 0xbb, 0xd3, 0x7e, 0xc6, 0x17, 0x3e, 0x8a, 0x74, 0x81, 0x22, 0x12, 0x86, 0x6b, + 0xcb, 0x58, 0x80, 0x5e, 0x9e, 0x70, 0x17, 0x96, 0xa7, 0x23, 0xd5, 0x15, 0xe6, + 0x15, 0x33, 0x20, 0x0b, 0xe0, 0x6b, 0x01, 0x5f, 0xa0, 0x22, 0x35, 0xc8, 0xcb, + 0xc9, 0xf3, 0x61, 0x7e, 0xe8, 0x19, 0x5f, 0xe1, 0xbc, 0xf5, 0xbb, 0x1b, 0x63, + 0x4c, 0xd4, 0x3f, 0x62, 0xea, 0x93, 0xa4, 0x6d, 0x88, 0xf2, 0xfc, 0xbc, 0x3e, + 0x28, 0x40, 0x84, 0xe7, 0x04, 0xfb, 0x1d, 0x7d, 0x0d, 0x9a, 0xcb, 0x91, 0x96, + 0x1e, 0x2e, 0xeb, 0xe2, 0xdc, 0x9e, 0xbe, 0x36, 0x5b, 0x25, 0xb5, 0x66, 0x75, + 0x97, 0x3d, 0x0c, 0x38, 0xf4, 0x76, 0x30, 0x57, 0x47, 0x23, 0xcd, 0x3e, 0xc6, + 0x6c, 0x8f, 0x3b, 0x12, 0x82, 0x21, 0xa7, 0x90, 0xd9, 0x2c, 0x89, 0x5b, 0x94, + 0x27, 0x0f, 0xe9, 0x40, 0x51, 0xa1, 0x70, 0xe9, 0x5b, 0x8b, 0xe7, 0x16, 0x34, + 0x86, 0xec, 0x8c, 0x0b, 0xee, 0xbe, 0xf6, 0x5e, 0x16, 0x26, 0xb0, 0x46, 0xd7, + 0xe7, 0xf8, 0x26, 0x37, 0x2b, 0x6a, 0xa1, 0x0b, 0xae, 0xfb, 0x84, 0x8f, 0xa1, + 0xdf, 0x6b, 0xb1, 0xdc, 0x43, 0x95, 0x40, 0xf6, 0x3c, 0x9c, 0x7a, 0x9d, 0x5f, + 0x88, 0x13, 0x40, 0x29, 0x62, 0x65, 0x1e, 0xe9, 0x84, 0x39, 0x02, 0xb6, 0xc3, + 0x98, 0x2d, 0xce, 0x50, 0xa6, 0x17, 0x8a, 0x55, 0xa1, 0xad, 0xc0, 0x1c, 0xe7, + 0xdc, 0x6c, 0x83, 0x38, 0xe1, 0xa9, 0xce, 0xef, 0xc1, 0x78, 0xdc, 0x43, 0x14, + 0xf6, 0x74, 0x9a, 0x81, 0xa7, 0x31, 0xee, 0x3c, 0x7f, 0xc0, 0xc3, 0x5d, 0x1c, + 0xe3, 0x63, 0xce, 0xf1, 0x13, 0x28, 0xf3, 0x87, 0xc4, 0x01, 0xfe, 0xf2, 0x7a, + 0x67, 0xa6, 0x29, 0x2f, 0x6f, 0x72, 0xb0, 0xa1, 0xd6, 0xc3, 0x89, 0x16, 0x2d, + 0x16, 0x2e, 0xf0, 0x50, 0xae, 0x5f, 0x3d, 0xdb, 0xb5, 0x5c, 0xaa, 0xbc, 0xa9, + 0xa1, 0xbe, 0x89, 0xb4, 0x63, 0x49, 0x4d, 0x74, 0x39, 0xfb, 0x56, 0x47, 0xa9, + 0x18, 0x12, 0x8b, 0x96, 0x25, 0xd3, 0x3e, 0xac, 0xa6, 0x19, 0xd5, 0x2f, 0x03, + 0x5f, 0xe6, 0x08, 0x9c, 0xe8, 0xd8, 0xb9, 0x0f, 0xe3, 0x67, 0x0d, 0x8c, 0x5a, + 0x2e, 0x3e, 0x05, 0x49, 0x69, 0xa3, 0xd9, 0x7e, 0x61, 0xb5, 0xe6, 0x30, 0x67, + 0x4f, 0xc7, 0x08, 0x57, 0xf1, 0xbb, 0xf1, 0x0f, 0xdc, 0x40, 0x49, 0xef, 0xf5, + 0x60, 0xeb, 0xa5, 0xf2, 0x2a, 0xcc, 0x8d, 0x77, 0xdb, 0xee, 0x0b, 0x20, 0x55, + 0x7f, 0xa4, 0xd0, 0x33, 0x31, 0x72, 0xcb, 0xb5, 0xcb, 0xcc, 0x2b, 0x13, 0x5f, + 0x2c, 0xcd, 0xe0, 0x14, 0xe6, 0x3e, 0xbe, 0x4e, 0xdf, 0x92, 0x5e, 0x61, 0xba, + 0x2a, 0x32, 0x0c, 0xd3, 0x99, 0x91, 0x5a, 0xdd, 0xfc, 0xeb, 0x1a, 0xd0, 0x69, + 0xa9, 0xfd, 0x5b, 0x62, 0x10, 0xa4, 0xb6, 0xe5, 0x04, 0x52, 0xb1, 0xf9, 0x06, + 0xdd, 0x16, 0xf0, 0x16, 0x68, 0xf0, 0xaf, 0x56, 0x6a, 0x28, 0x7c, 0xce, 0xfc, + 0xd8, 0x94, 0x73, 0x41, 0x85, 0x9a, 0xe7, 0xdc, 0x3a, 0x06, 0xf6, 0xbf, 0x15, + 0x74, 0xfe, 0xb9, 0x31, 0xf9, 0x27, 0xe2, 0xd5, 0x05, 0xf6, 0x08, 0x59, 0x9e, + 0x23, 0xb0, 0x5a, 0xf7, 0xc3, 0x23, 0x69, 0x83, 0x97, 0xa8, 0x01, 0xdc, 0x7f, + 0x78, 0x82, 0x5c, 0xc7, 0xeb, 0x9f, 0xcc, 0xe6, 0xc6, 0xc4, 0xf8, 0xf6, 0x88, + 0x39, 0xd3, 0x0a, 0xc5, 0x67, 0x14, 0x8e, 0x70, 0x84, 0xdb, 0x2b, 0x37, 0x58, + 0x30, 0xa0, 0x7b, 0x30, 0x5f, 0xed, 0xd6, 0x07, 0xa3, 0x47, 0xfa, 0x65, 0xde, + 0xf0, 0x1d, 0x4e, 0x1f, 0xd6, 0xc1, 0x6b, 0x4b, 0x47, 0xf5, 0xb0, 0x1b, 0x43, + 0x65, 0xb7, 0x72, 0x26, 0xe6, 0x0f, 0xdd, 0x40, 0xf2, 0x2a, 0x39, 0x5a, 0xa2, + 0x35, 0xf0, 0xdf, 0xda, 0x8f, 0xb4, 0xd3, 0xde, 0x65, 0xb0, 0xcf, 0x4f, 0x4c, + 0x22, 0x0b, 0x3b, 0x4a, 0x9e, 0x32, 0xbc, 0x0d, 0xb6, 0x4f, 0x16, 0x2c, 0x07, + 0xdf, 0x42, 0xa1, 0x01, 0x99, 0x03, 0xa6, 0x7c, 0xda, 0x69, 0x3d, 0xde, 0xb5, + 0xca, 0x39, 0xa0, 0xfe, 0x50, 0x08, 0x50, 0xec, 0x7c, 0x06, 0xbe, 0xe7, 0x18, + 0x66, 0xb3, 0x55, 0xcc, 0xbc, 0x07, 0x8c, 0xd4, 0xdc, 0x03, 0x6f, 0xda, 0xa8, + 0x1c, 0xb2, 0xde, 0x99, 0xcc, 0x88, 0xf6, 0x0a, 0x49, 0x46, 0x42, 0x87, 0xf5, + 0x9f, 0xc7, 0x14, 0x8b, 0x1a, 0xfb, 0x4a, 0x2f, 0x9b, 0xb8, 0x97, 0x14, 0xe1, + 0xeb, 0x8c, 0x03, 0x61, 0xe5, 0x99, 0x2a, 0x5b, 0x79, 0xcd, 0xbb, 0x91, 0xd9, + 0xbf, 0x29, 0xeb, 0x59, 0x8c, 0xbb, 0x4b, 0xda, 0x92, 0x3d, 0x26, 0x7f, 0xea, + 0xcb, 0x91, 0xce, 0x72, 0xd6, 0x1a, 0xb1, 0xea, 0x00, 0xf5, 0x6a, 0xa6, 0x76, + 0x6e, 0xab, 0xc4, 0x7d, 0xca, 0xa6, 0x9a, 0x02, 0x4b, 0xbf, 0xf2, 0xf2, 0x96, + 0x91, 0x7f, 0x17, 0xa3, 0xf8, 0xc9, 0x3e, 0x1b, 0xf2, 0x9c, 0x3c, 0xfc, 0x99, + 0x1a, 0x2b, 0xe8, 0xcf, 0xa7, 0x0e, 0x5d, 0xe3, 0xf2, 0xdd, 0x52, 0xa7, 0x55, + 0x01, 0x38, 0x68, 0x7a, 0xec, 0x28, 0x92, 0x6f, 0xa1, 0x68, 0xb1, 0x81, 0xdb, + 0x72, 0x82, 0xbd, 0x60, 0xda, 0xd3, 0x31, 0x0d, 0xfe, 0x54, 0x2c, 0xeb, 0xe6, + 0x94, 0x74, 0x00, 0x25, 0xc7, 0xec, 0x2a, 0x20, 0x43, 0xfe, 0xbb, 0x77, 0x9f, + 0x7f, 0x37, 0x89, 0xa5, 0xe2, 0x42, 0xdb, 0x48, 0x03, 0xee, 0x36, 0x72, 0x52, + 0xc4, 0x63, 0xc9, 0xa8, 0x8b, 0x41, 0x7b, 0x70, 0x86, 0x6d, 0x9a, 0xfb, 0x7a, + 0x08, 0x27, 0x68, 0x01, 0xf9, 0x22, 0x7c, 0x63, 0x81, 0xf1, 0x5c, 0xc0, 0x94, + 0xac, 0x7b, 0xd1, 0x54, 0xa4, 0xce, 0xf9, 0x0b, 0x48, 0x47, 0xdc, 0x16, 0x8a, + 0x01, 0xf1, 0xe3, 0x1e, 0xec, 0x74, 0xa7, 0xef, 0xce, 0xba, 0x11, 0xf5, 0x07, + 0x69, 0xf5, 0xd8, 0xf5, 0x4d, 0x36, 0x20, 0xc2, 0x3e, 0xc8, 0x99, 0x3f, 0x7a, + 0xef, 0x27, 0xc1, 0xd3, 0x51, 0x96, 0xb1, 0x02, 0xb3, 0xcf, 0x3f, 0xed, 0x8b, + 0xf8, 0x5d, 0x8a, 0x45, 0xf6, 0x96, 0x83, 0xec, 0xdd, 0x1a, 0x23, 0x44, 0xef, + 0xb8, 0x48, 0x07, 0xd9, 0x0f, 0x18, 0x35, 0xb4, 0xf2, 0xf2, 0x4d, 0x8f, 0xf8, + 0x12, 0x30, 0x47, 0xeb, 0x9f, 0x7d, 0x30, 0x62, 0x3e, 0x14, 0x29, 0x0d, 0x56, + 0x17, 0x96, 0x3b, 0x42, 0x21, 0x40, 0x4a, 0xe7, 0x61, 0xc8, 0x6b, 0xec, 0x7a, + 0x07, 0xbf, 0x81, 0xa0, 0xb9, 0xa7, 0xf7, 0xd0, 0x87, 0xac, 0x26, 0xce, 0x3d, + 0xfa, 0x9c, 0x93, 0xfe, 0xea, 0xeb, 0xd1, 0x0d, 0xc1, 0x88, 0xc6, 0x27, 0xd4, + 0xb9, 0x1d, 0x2a, 0x79, 0x01, 0xee, 0x5a, 0x1b, 0x38, 0x4d, 0xa3, 0x6e, 0x78, + 0x95, 0xd5, 0xb7, 0x78, 0x21, 0xfe, 0x6b, 0xca, 0xa3, 0xaf, 0xe8, 0xf2, 0x3a, + 0x96, 0x8f, 0xc9, 0xab, 0xa3, 0x7b, 0x1a, 0x4e, 0x25, 0xf5, 0xdb, 0xa1, 0xd1, + 0x42, 0xff, 0x11, 0xff, 0xd7, 0xa1, 0xba, 0x41, 0xef, 0x82, 0xc6, 0x2a, 0x95, + 0x94, 0x66, 0xe7, 0x11, 0x2a, 0xf7, 0x79, 0x6d, 0x47, 0x67, 0x12, 0x69, 0xad, + 0xd3, 0xee, 0x2b, 0x17, 0x21, 0x3e, 0xc3, 0xbd, 0x51, 0x30, 0x24, 0x4c, 0xb9, + 0x07, 0x0e, 0xa8, 0xcf, 0xa4, 0x6d, 0x44, 0xf2, 0xfc, 0xd9, 0x64, 0x06, 0x37, + 0xf7, 0xde, 0xfd, 0x50, 0xb6, 0xdc, 0x93, 0x60, 0x19, 0x45, 0xb9, 0x31, 0xe8, + 0xbb, 0x72, 0x67, 0x1f, 0xe4, 0xb4, 0xb5, 0x88, 0xc9, 0x0a, 0xd5, 0xc0, 0x0b, + 0x55, 0xdc, 0x8c, 0x8a, 0xf9, 0xb0, 0xf6, 0xa3, 0xca, 0x1e, 0x07, 0xef, 0xf1, + 0x58, 0x11, 0x39, 0x1c, 0x53, 0xf7, 0xe4, 0x3b, 0x1b, 0x81, 0x16, 0xda, 0xdc, + 0x01, 0x6d, 0x19, 0x26, 0xc8, 0x48, 0x0d, 0x4e, 0xe3, 0x4e, 0x76, 0x19, 0x1b, + 0x79, 0xbe, 0xd0, 0xce, 0x95, 0x97, 0x3a, 0x4c, 0x7c, 0xf2, 0xf0, 0x57, 0xc7, + 0x14, 0x7e, 0xdb, 0x01, 0x3d, 0x20, 0x5d, 0x81, 0xe2, 0x36, 0x08, 0x88, 0xa2, + 0xab, 0xdd, 0xcc, 0xf0, 0xf6, 0xf3, 0xd8, 0xf8, 0xba, 0x11, 0x1d, 0x64, 0x2c, + 0x52, 0xd0, 0x4e, 0xbd, 0x3c, 0xe1, 0x7c, 0x60, 0xd9, 0x22, 0x57, 0xea, 0x58, + 0x69, 0x09, 0x45, 0x01, 0xbb, 0x67, 0x12, 0x68, 0xb2, 0x24, 0x47, 0x7a, 0x8e, + 0x01, 0x41, 0xd6, 0xff, 0x37, 0xe2, 0x4f, 0xf1, 0xc7, 0x65, 0xe8, 0x4d, 0x26, + 0x4d, 0xb8, 0x8f, 0x00, 0x92, 0x8e, 0x64, 0xc4, 0x12, 0xbd, 0x59, 0x15, 0x1a, + 0x65, 0x71, 0xc6, 0x67, 0x09, 0x16, 0xb0, 0x70, 0x6b, 0x04, 0x4f, 0xc5, 0xc2, + 0xbd, 0x93, 0xad, 0xe3, 0x96, 0x79, 0x57, 0xcd, 0xb9, 0x41, 0x27, 0x4c, 0xc6, + 0xbd, 0xb4, 0xe0, 0x36, 0xb7, 0x67, 0xb9, 0x50, 0xc0, 0x9e, 0x46, 0x26, 0xa1, + 0xd0, 0x05, 0xbc, 0xf4, 0x83, 0x6e, 0xf6, 0xa1, 0xde, 0x48, 0x09, 0x5d, 0xcb, + 0x46, 0x12, 0x78, 0xb1, 0x6c, 0x45, 0x68, 0x90, 0xb2, 0x3d, 0x40, 0xbd, 0x36, + 0x04, 0x10, 0xf0, 0x01, 0x0a, 0x55, 0xf5, 0x05, 0xfe, 0x5e, 0x2d, 0xb2, 0x01, + 0xc7, 0x52, 0xe9, 0xb5, 0xb1, 0x5b, 0xf8, 0xaa, 0x9e, 0x82, 0xd6, 0x49, 0xab, + 0x11, 0x73, 0xba, 0x2a, 0x51, 0x32, 0xe0, 0xcc, 0x50, 0x51, 0xcc, 0xf7, 0x4c, + 0x7a, 0x6a, 0x37, 0x07, 0xab, 0x59, 0x83, 0xf7, 0xcc, 0x27, 0x5c, 0x99, 0x1a, + 0xbe, 0x4d, 0x7c, 0xee, 0x5f, 0x28, 0x9e, 0xfe, 0x72, 0x7e, 0xb3, 0xda, 0x86, + 0xfa, 0x21, 0xa2, 0x8d, 0x6b, 0x8a, 0x2a, 0xff, 0xd4, 0x2d, 0xb9, 0x8b, 0xb2, + 0xa4, 0x6c, 0xd8, 0xa3, 0x29, 0x31, 0x2f, 0xa9, 0x45, 0x39, 0xd9, 0xcb, 0x35, + 0xdc, 0xb6, 0x04, 0x67, 0x8b, 0x63, 0x90, 0x64, 0xd9, 0x20, 0x05, 0xdf, 0x2d, + 0x10, 0x68, 0x1c, 0x64, 0xb9, 0xed, 0x8c, 0xe4, 0x7d, 0x7e, 0xba, 0x0f, 0x2b, + 0x50, 0x2b, 0x20, 0x6a, 0xd4, 0xb2, 0xe9, 0x2b, 0xbe, 0x45, 0x86, 0xf6, 0xd7, + 0x50, 0x9e, 0x57, 0xa6, 0x37, 0x7f, 0xea, 0xbe, 0x38, 0xb3, 0xcc, 0x6c, 0x95, + 0x5d, 0x5e, 0x7b, 0xdf, 0x7e, 0xb1, 0x32, 0xd8, 0x6b, 0xc0, 0x7a, 0x30, 0x98, + 0xb4, 0x13, 0xe4, 0x40, 0x5d, 0xaa, 0xa2, 0x55, 0x29, 0x1d, 0x55, 0x2b, 0x2c, + 0x80, 0x07, 0xbe, 0xd4, 0x1e, 0x22, 0xf1, 0xcf, 0x79, 0x11, 0x82, 0x12, 0x00, + 0x55, 0x5e, 0x9c, 0x4f, 0xfb, 0x09, 0xef, 0xc1, 0x22, 0x38, 0x11, 0x75, 0x03, + 0x1c, 0x38, 0x28, 0x0b, 0x53, 0x26, 0xeb, 0xbe, 0xaf, 0x33, 0x4f, 0xdc, 0xf0, + 0xdc, 0x44, 0x4e, 0x62, 0x9f, 0x93, 0x95, 0x51, 0x54, 0x0b, 0xcb, 0xbb, 0xb1, + 0xab, 0x9c, 0x23, 0x1a, 0x86, 0x6b, 0x32, 0x9e, 0x85, 0x24, 0xab, 0x25, 0xf9, + 0x3e, 0x5e, 0x33, 0x4a, 0x05, 0x27, 0x2a, 0x3f, 0x82, 0x6f, 0x9d, 0x05, 0xa4, + 0x50, 0x58, 0xdf, 0xcd, 0xf6, 0x88, 0x43, 0xa8, 0xb9, 0x36, 0xa0, 0xcf, 0x5e, + 0x6a, 0xa8, 0xae, 0x1b, 0x80, 0xf6, 0x01, 0x61, 0xbf, 0x41, 0x4f, 0x28, 0x02, + 0x11, 0x11, 0x09, 0x21, 0xa9, 0xc8, 0x5f, 0x51, 0x04, 0xa0, 0x16, 0x8e, 0x8e, + 0x72, 0xde, 0x4f, 0x8a, 0xa0, 0x41, 0x32, 0xeb, 0x25, 0x88, 0x76, 0xf1, 0x9d, + 0x7b, 0xe5, 0xf2, 0xdd, 0x2b, 0x0b, 0x30, 0x4b, 0x92, 0x3b, 0x29, 0x52, 0xd9, + 0x1f, 0xde, 0xe7, 0xe5, 0x52, 0x05, 0xdb, 0xb1, 0x94, 0xeb, 0xba, 0x32, 0x2f, + 0xdc, 0x67, 0xb2, 0x52, 0x2c, 0x92, 0x61, 0x21, 0xc7, 0xfa, 0x1a, 0xf1, 0x7e, + 0xd0, 0x6c, 0x47, 0x27, 0x8f, 0x96, 0x08, 0x92, 0x96, 0x08, 0x7a, 0x70, 0x4b, + 0x7d, 0x0f, 0x84, 0x7d, 0x51, 0xd6, 0xcc, 0x68, 0xac, 0xc5, 0x22, 0x07, 0x74, + 0x73, 0x41, 0xf6, 0xb9, 0x8c, 0xb1, 0xcd, 0x4f, 0xaf, 0xcd, 0x2b, 0xb0, 0xd0, + 0x5b, 0xc7, 0x9b, 0xb8, 0x0d, 0x7c, 0x4b, 0x8a, 0x1a, 0x11, 0xbc, 0x0a, 0x3b, + 0xde, 0xca, 0x45, 0x41, 0x86, 0x9b, 0x4d, 0xc9, 0xd6, 0xb4, 0x8c, 0xd7, 0x86, + 0x9b, 0xf7, 0x63, 0xb9, 0xdc, 0x42, 0x45, 0x27, 0x3c, 0x70, 0x4b, 0x0d, 0x8d, + 0xec, 0x4b, 0x85, 0xd1, 0x6d, 0xd4, 0x38, 0xce, 0xd6, 0x22, 0x0f, 0xa6, 0x69, + 0x26, 0x66, 0x3f, 0xcc, 0x22, 0x8f, 0xc6, 0xc4, 0xd2, 0x7e, 0x17, 0xe3, 0x27, + 0x83, 0x4b, 0x67, 0x57, 0x91, 0x4d, 0x1b, 0xcb, 0xf3, 0x4b, 0x65, 0xd8, 0x58, + 0xab, 0x8b, 0x5c, 0x12, 0x0c, 0xb0, 0x85, 0x05, 0x22, 0xf5, 0x42, 0x89, 0x3f, + 0xdd, 0xb1, 0x79, 0xe8, 0x7f, 0x83, 0x2d, 0xaa, 0xa1, 0x52, 0xc8, 0x31, 0xf1, + 0x35, 0x64, 0x00, 0x9c, 0x41, 0x81, 0x23, 0x53, 0x3d, 0xe2, 0xc6, 0x79, 0x49, + 0xe3, 0xaf, 0x2d, 0xcb, 0x60, 0xd6, 0xbd, 0xbd, 0xda, 0xda, 0x63, 0xa3, 0x0b, + 0x4b, 0x54, 0xcd, 0x1c, 0xe5, 0xa5, 0xa0, 0x0f, 0x8e, 0x85, 0x57, 0xeb, 0xa9, + 0x23, 0x4e, 0x81, 0x17, 0x8d, 0x0f, 0xca, 0xb5, 0x61, 0x0f, 0xba, 0x96, 0x69, + 0xcf, 0xeb, 0x1b, 0xd0, 0x8c, 0xd9, 0x65, 0x33, 0x49, 0x8b, 0x27, 0x2c, 0x57, + 0x79, 0xa9, 0xf9, 0x39, 0x69, 0x1d, 0xe1, 0xad, 0x88, 0x1c, 0x80, 0x87, 0x8d, + 0x6c, 0x29, 0x42, 0x15, 0x23, 0x0b, 0xbb, 0x61, 0x90, 0x69, 0xb4, 0xdc, 0x17, + 0xb3, 0xe5, 0x9d, 0xbd, 0x24, 0x2c, 0xd8, 0x8e, 0xcc, 0x3b, 0xe3, 0xa2, 0x69, + 0x6b, 0xf7, 0xf2, 0xd9, 0xe5, 0xb8, 0xc1, 0x52, 0xcc, 0x0d, 0x99, 0xa0, 0xa5, + 0xe9, 0xa3, 0x8b, 0x1b, 0x8e, 0xb1, 0xa0, 0x13, 0xeb, 0x76, 0x51, 0x33, 0x37, + 0xa7, 0xb0, 0xda, 0xdb, 0x4e, 0x81, 0x7b, 0x6f, 0x49, 0x78, 0x02, 0xbd, 0x47, + 0xe9, 0x3a, 0x82, 0x0c, 0x4f, 0xad, 0x6c, 0x65, 0x09, 0x74, 0x42, 0xb9, 0xca, + 0xc1, 0x61, 0xb6, 0x4d, 0x0f, 0xcb, 0xfb, 0xf5, 0x4f, 0xc3, 0x04, 0xc9, 0xb7, + 0x0c, 0x74, 0xfb, 0xb0, 0xd7, 0x05, 0xc7, 0x4d, 0x56, 0xac, 0xb2, 0xfe, 0x6c, + 0x91, 0x74, 0xcc, 0x00, 0xea, 0xbe, 0xa0, 0xe5, 0x97, 0x0d, 0xff, 0xe3, 0x2c, + 0xb6, 0x12, 0x92, 0x05, 0x3d, 0xb8, 0x6d, 0x36, 0x6b, 0x7e, 0x6b, 0x30, 0x13, + 0xd1, 0x4b, 0x20, 0x5f, 0xb4, 0x5d, 0x06, 0x7e, 0x37, 0x50, 0x2e, 0x37, 0x9c, + 0x4a, 0xa1, 0x38, 0xbe, 0xc2, 0xc6, 0xbd, 0x33, 0x1f, 0x58, 0xe9, 0xaa, 0x10, + 0x09, 0xb0, 0x66, 0xdc, 0xe9, 0x9a, 0xcc, 0x1d, 0xc5, 0xa6, 0x3a, 0x8f, 0x75, + 0xd1, 0x98, 0x22, 0x7c, 0x2f, 0xbd, 0x20, 0xd5, 0x34, 0xf1, 0x20, 0x30, 0xc4, + 0x00, 0x99, 0xd8, 0x77, 0xca, 0xbe, 0x81, 0xb0, 0x87, 0x50, 0xe3, 0xfb, 0xfe, + 0x63, 0x12, 0xf6, 0x38, 0x0b, 0x98, 0xfb, 0x85, 0x0a, 0x2a, 0x14, 0x2b, 0x91, + 0x4a, 0xdc, 0x71, 0x54, 0x47, 0xc5, 0x79, 0x1a, 0x1b, 0x67, 0xae, 0x65, 0x6c, + 0xad, 0xdd, 0x21, 0xe1, 0xb4, 0x6d, 0xc9, 0xa7, 0x64, 0x12, 0x7b, 0xc0, 0xa3, + 0x01, 0xb4, 0x80, 0x04, 0xa9, 0xc5, 0x27, 0x6b, 0xcf, 0x08, 0xe7, 0xfe, 0x4a, + 0xe5, 0x2d, 0x76, 0xe4, 0x31, 0x48, 0x8a, 0x5b, 0x9d, 0x43, 0x1f, 0xa1, 0x36, + 0x34, 0x6e, 0x5a, 0x53, 0xab, 0x3f, 0x68, 0x12, 0xf2, 0xd9, 0x70, 0xf7, 0xb3, + 0x98, 0x98, 0xcf, 0x8b, 0x62, 0xf2, 0xdb, 0xf6, 0x1e, 0x99, 0xa2, 0x91, 0x5d, + 0xfb, 0x75, 0xae, 0x22, 0xb7, 0x9f, 0x84, 0xcf, 0x25, 0x97, 0xeb, 0x34, 0xec, + 0x3d, 0x29, 0x2e, 0x6b, 0x5d, 0x84, 0xeb, 0xac, 0x4d, 0x92, 0xde, 0x52, 0xe1, + 0xf8, 0xbf, 0x6b, 0xfd, 0xba, 0xda, 0x63, 0x44, 0x09, 0xf2, 0x0e, 0xf2, 0xcc, + 0x6e, 0x3c, 0x39, 0x0e, 0x43, 0x5f, 0x47, 0xe3, 0x47, 0x23, 0x8d, 0xb4, 0x86, + 0x90, 0x84, 0x04, 0x73, 0xb0, 0xa0, 0x83, 0x1a, 0x5a, 0x8a, 0x58, 0xc4, 0xdc, + 0xfc, 0x4e, 0xab, 0x7b, 0x41, 0x8c, 0xba, 0x2a, 0x41, 0x4f, 0x95, 0x57, 0x71, + 0x90, 0xff, 0x88, 0xd7, 0x27, 0xf7, 0x3e, 0x2f, 0xff, 0x97, 0xaa, 0xbd, 0x11, + 0x14, 0xb7, 0x64, 0xe3, 0xed, 0xbc, 0x18, 0x3e, 0x60, 0x3a, 0xcf, 0xb7, 0xc0, + 0x9b, 0xf1, 0x32, 0xbb, 0x01, 0xef, 0xc7, 0x17, 0x8d, 0x4f, 0x9a, 0x2d, 0xba, + 0xf4, 0x92, 0x4f, 0xd8, 0x0f, 0xbe, 0x0e, 0x60, 0x4f, 0x60, 0x39, 0x08, 0x32, + 0xeb, 0x98, 0x04, 0x79, 0xe0, 0x4e, 0x9c, 0x9a, 0x2b, 0xb2, 0xfb, 0x36, 0x84, + 0xd8, 0xf8, 0x06, 0x48, 0xd5, 0x80, 0x78, 0x38, 0x54, 0x58, 0x4f, 0x62, 0xbe, + 0x0c, 0xc9, 0x21, 0x88, 0x32, 0x38, 0x56, 0x10, 0xd9, 0x62, 0x36, 0x5f, 0x50, + 0x71, 0xfa, 0x3d, 0x36, 0x8f, 0xfb, 0x67, 0x1b, 0xa2, 0xc2, 0xf9, 0xa0, 0xfc, + 0x68, 0xd8, 0x07, 0x22, 0x19, 0xa7, 0x7b, 0xef, 0x2d, 0x6b, 0x4a, 0x19, 0xf1, + 0x6d, 0xd5, 0x30, 0x74, 0x22, 0x47, 0x46, 0xbb, 0xa5, 0xf1, 0x72, 0x82, 0x20, + 0xb1, 0x96, 0xe4, 0x0f, 0x93, 0x7c, 0x47, 0x05, 0x42, 0x9d, 0x04, 0xaa, 0x3c, + 0x50, 0x5c, 0x95, 0x60, 0x3e, 0x05, 0xff, 0x55, 0x2e, 0xc1, 0x86, 0x42, 0xd5, + 0x67, 0x05, 0x02, 0x67, 0xb9, 0xf9, 0x92, 0x9c, 0x2e, 0x13, 0x80, 0x14, 0xb5, + 0xef, 0x1b, 0xa7, 0x1d, 0x9a, 0x71, 0x86, 0xe3, 0xd1, 0x3c, 0x8a, 0x8e, 0x40, + 0x8c, 0x2a, 0x9d, 0x12, 0x01, 0xa7, 0xfe, 0xbb, 0x83, 0x34, 0x51, 0x2b, 0x44, + 0xb8, 0x2b, 0xb2, 0x01, 0x78, 0x9f, 0x63, 0x58, 0x04, 0x89, 0x6e, 0x3e, 0xb2, + 0x1b, 0x5b, 0xd8, 0xc4, 0x21, 0xf0, 0xb4, 0xcf, 0xba, 0x04, 0xde, 0x92, 0x52, + 0x8f, 0x04, 0xfb, 0x4b, 0x52, 0x6b, 0x73, 0x7e, 0xe3, 0x2d, 0xa8, 0x63, 0xf5, + 0x98, 0x45, 0x61, 0x31, 0x98, 0x3a, 0x01, 0x35, 0x8f, 0xb0, 0x7d, 0xe6, 0x75, + 0x21, 0x11, 0x58, 0x5a, 0x86, 0x25, 0x6c, 0xe0, 0x34, 0xc0, 0xd8, 0x57, 0x5a, + 0x42, 0x76, 0x13, 0x61, 0xb1, 0x18, 0x77, 0x05, 0x0b, 0xc6, 0xaf, 0xc3, 0x16, + 0x15, 0x64, 0xe9, 0x6f, 0xd8, 0xcf, 0x04, 0x8f, 0xeb, 0xeb, 0x2a, 0x92, 0x20, + 0x07, 0x1c, 0xff, 0x18, 0x2d, 0x6c, 0xa0, 0x37, 0xce, 0x2c, 0x2d, 0xed, 0x91, + 0x6b, 0xd7, 0xb8, 0x4d, 0xe2, 0x8a, 0xc0, 0x17, 0x1d, 0x97, 0xfc, 0x24, 0x95, + 0x6c, 0x26, 0x66, 0x69, 0xc1, 0x03, 0x6b, 0x2b, 0x1a, 0x23, 0xda, 0xbc, 0xf3, + 0x4e, 0x38, 0xf3, 0x51, 0x45, 0x12, 0xae, 0x8a, 0x47, 0xb3, 0x53, 0xb4, 0x16, + 0x69, 0x96, 0x75, 0xe4, 0xd3, 0x1a, 0x2f, 0xe0, 0x34, 0x08, 0xe4, 0x24, 0xa7, + 0x82, 0x9a, 0x06, 0xad, 0xe6, 0x36, 0x53, 0x61, 0xd8, 0xa9, 0x61, 0x25, 0x7c, + 0xbe, 0x25, 0xb0, 0xcd, 0xe3, 0x3e, 0x96, 0x48, 0x77, 0xdf, 0x5e, 0x57, 0xc5, + 0x3d, 0xb2, 0x83, 0x51, 0x77, 0x34, 0x3e, 0x2d, 0x87, 0x6d, 0x51, 0x4c, 0x62, + 0xfb, 0xb3, 0xb4, 0xa7, 0x08, 0xce, 0x62, 0x62, 0x05, 0xcc, 0xf9, 0x2f, 0x24, + 0x0d, 0x60, 0x2c, 0xdb, 0x5d, 0x68, 0x41, 0xfd, 0x29, 0xda, 0x63, 0x08, 0xb6, + 0xca, 0x40, 0x97, 0xd8, 0x52, 0x54, 0x10, 0x46, 0x54, 0x52, 0x23, 0x9b, 0x04, + 0x51, 0xa8, 0xdb, 0xed, 0xac, 0x1e, 0x41, 0xed, 0xdd, 0x0f, 0x6b, 0xe0, 0xe3, + 0xd8, 0x89, 0x69, 0x07, 0x03, 0xa3, 0x14, 0x57, 0x07, 0xe0, 0xb3, 0xf5, 0xdb, + 0x91, 0xb8, 0x19, 0x37, 0x56, 0xe0, 0xe3, 0x47, 0xb6, 0x64, 0xa1, 0xcc, 0xcb, + 0xd7, 0x86, 0x9a, 0x40, 0x22, 0xea, 0xdf, 0x3f, 0x87, 0x3c, 0x10, 0xec, 0xab, + 0x9a, 0x93, 0xf2, 0xca, 0xdc, 0xa7, 0xa3, 0x33, 0xb8, 0x1b, 0xb6, 0x10, 0x4e, + 0x82, 0xea, 0x14, 0xfe, 0x74, 0x1e, 0xb0, 0x62, 0x08, 0x0d, 0xc8, 0x5a, 0xcb, + 0xc8, 0xcc, 0x3a, 0x9b, 0xc8, 0x0c, 0x03, 0xd9, 0x1f, 0xfb, 0x3c, 0x25, 0xf9, + 0xe4, 0x2b, 0xc2, 0x5c, 0xf7, 0x7d, 0x73, 0x90, 0xc3, 0xab, 0xaf, 0x26, 0x10, + 0xf4, 0xec, 0xdb, 0x01, 0x9b, 0x15, 0x8d, 0xa2, 0x15, 0x5b, 0xef, 0xec, 0xb9, + 0xc2, 0x29, 0x6d, 0x03, 0xf8, 0x23, 0xea, 0xac, 0x0c, 0x74, 0x0d, 0x2a, 0x44, + 0x89, 0xb8, 0x28, 0x4c, 0x7e, 0x7b, 0x3a, 0x72, 0x9a, 0xfb, 0x69, 0xbd, 0x5b, + 0xfa, 0x5f, 0x62, 0xf9, 0xb5, 0x27, 0x37, 0x97, 0xdd, 0x24, 0xa0, 0x18, 0x30, + 0x7f, 0xc6, 0x20, 0xe6, 0x42, 0xaa, 0x27, 0xe7, 0x50, 0x6e, 0x17, 0xb1, 0x98, + 0xdc, 0xa4, 0x79, 0x0e, 0x8d, 0xe1, 0xbf, 0xb6, 0x71, 0xd8, 0xdc, 0x75, 0x13, + 0x91, 0x0e, 0x95, 0x43, 0x10, 0x72, 0x1b, 0x4f, 0xb5, 0x37, 0x33, 0xc9, 0x18, + 0xf0, 0xd1, 0x89, 0x85, 0x18, 0x89, 0x62, 0x73, 0x22, 0xd5, 0x20, 0xca, 0xcc, + 0x9d, 0xd7, 0x03, 0x6b, 0xb4, 0x39, 0xa1, 0x69, 0xef, 0x2c, 0xdd, 0x6c, 0xdb, + 0xae, 0xa5, 0xa9, 0x1b, 0xc2, 0x4a, 0xb3, 0xfc, 0xa1, 0x57, 0x4c, 0x12, 0xc9, + 0x31, 0xe7, 0xaa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0xc6, 0x49, 0x66, 0xc0, 0x6b, + 0x62, 0x2d, 0x23, 0xc8, 0x8d, 0xb2, 0xfd, 0x4b, 0x8f, 0xa5, 0x0b, 0xe3, 0x61, + 0x94, 0x3b, 0x79, 0x6d, 0x14, 0x85, 0x5f, 0x20, 0x71, 0xd3, 0x20, 0xd4, 0x3d, + 0x6c, 0x49, 0x4c, 0x9e, 0xda, 0x35, 0xcf, 0x9b, 0xf3, 0x7d, 0xc5, 0x4b, 0x40, + 0x2e, 0xb2, 0x87, 0x64, 0xa0, 0xb9, 0x17, 0x6c, 0xf9, 0x49, 0xb2, 0xa7, 0x78, + 0x64, 0x19, 0x83, 0x89, 0x2f, 0xfb, 0x5c, 0x7b, 0xfa, 0x68, 0xe6, 0x36, 0xde, + 0xfe, 0xfc, 0xb2, 0xfa, 0x07, 0x94, 0x45, 0xec, 0xd3, 0xad, 0xdf, 0x0c, 0x22, + 0xb2, 0x61, 0x72, 0x49, 0x92, 0xe2, 0xf0, 0xd2, 0x7c, 0xff, 0x23, 0xa6, 0x46, + 0x15, 0x30, 0xdc, 0x05, 0xf4, 0x9e, 0x97, 0x2d, 0xa3, 0x71, 0x6f, 0x41, 0x91, + 0xbf, 0xf4, 0xed, 0x29, 0x02, 0x67, 0x46, 0xf0, 0x9e, 0xfa, 0x9d, 0xfc, 0xbc, + 0xde, 0xc5, 0xa6, 0x95, 0xb1, 0xf7, 0x31, 0x36, 0x14, 0x64, 0xec, 0x42, 0xe3, + 0xb5, 0x26, 0x7e, 0xb6, 0x5f, 0x55, 0x6b, 0x26, 0x7a, 0xf3, 0x59, 0x71, 0xb4, + 0x14, 0x9b, 0xb3, 0xe5, 0xaa, 0x03, 0xa4, 0x95, 0xfb, 0xeb, 0x90, 0x15, 0xac, + 0x3f, 0xf1, 0x3a, 0x5c, 0x1c, 0x2a, 0x5f, 0x81, 0x96, 0x47, 0x3d, 0x5b, 0xfe, + 0x70, 0x48, 0xdf, 0x27, 0x7f, 0x0b, 0x5c, 0xf4, 0xe6, 0xc7, 0x1c, 0xa9, 0x36, + 0x6e, 0xca, 0x3b, 0x9c, 0xf1, 0xe6, 0x06, 0x9d, 0x53, 0x9e, 0x5c, 0xe4, 0x3f, + 0xd9, 0xaa, 0x25, 0xc2, 0x11, 0xd3, 0x79, 0x92, 0xc3, 0x40, 0xad, 0xea, 0x8b, + 0x24, 0x9f, 0x28, 0xab, 0x23, 0x49, 0x39, 0x17, 0xc4, 0x9d, 0xeb, 0x28, 0x3b, + 0x4c, 0x8a, 0x64, 0x90, 0x41, 0x88, 0x7e, 0x66, 0x83, 0x8d, 0x1c, 0x42, 0x9d, + 0xec, 0xdb, 0x31, 0x59, 0xcb, 0x30, 0xaf, 0xe4, 0xfb, 0x31, 0x68, 0xcc, 0xec, + 0x44, 0x98, 0x2e, 0x05, 0xf8, 0x71, 0x13, 0x2e, 0xfa, 0x63, 0xd6, 0x5a, 0x24, + 0x93, 0xcd, 0xf2, 0x39, 0xe8, 0xb2, 0xc8, 0x09, 0x05, 0xe8, 0x04, 0xa8, 0x4d, + 0xd7, 0x6a, 0xfe, 0xaa, 0x68, 0x94, 0x79, 0x1d, 0x49, 0xb1, 0xe4, 0x00, 0xb3, + 0xfc, 0xaa, 0x82, 0x73, 0x99, 0x60, 0xad, 0xda, 0x36, 0x45, 0xbb, 0x85, 0x75, + 0x6c, 0x63, 0x00, 0x5c, 0x01, 0x6f, 0x65, 0x8b, 0xa6, 0xab, 0x52, 0x57, 0xc4, + 0x86, 0xaf, 0x13, 0xed, 0xc9, 0xb4, 0x6b, 0xf6, 0x29, 0x34, 0xaa, 0x71, 0x4f, + 0x00, 0x36, 0x05, 0x96, 0x5a, 0xc5, 0x4d, 0x82, 0x50, 0xa5, 0x53, 0x52, 0x00, + 0xd1, 0x20, 0x2a, 0xcc, 0xca, 0xaa, 0x9e, 0x42, 0xea, 0x98, 0x2a, 0x21, 0x61, + 0x8e, 0xdb, 0xb1, 0x34, 0xc3, 0x3b, 0xc8, 0x4e, 0x35, 0xfc, 0x76, 0x56, 0x05, + 0x86, 0xa3, 0xc3, 0x43, 0x8e, 0x8f, 0x2b, 0x0c, 0xe7, 0x0d, 0x86, 0x31, 0x71, + 0xdf, 0x23, 0x8e, 0x12, 0x60, 0xd5, 0x9f, 0x82, 0x40, 0x37, 0xa7, 0x71, 0x7b, + 0x2e, 0x21, 0xa9, 0x6e, 0x4d, 0x79, 0x9b, 0x8e, 0xc4, 0xc9, 0x8b, 0x8d, 0x16, + 0x83, 0x6c, 0x18, 0x22, 0xb2, 0x45, 0x62, 0x66, 0x46, 0x59, 0x86, 0x85, 0x0d, + 0x23, 0x31, 0xc7, 0x29, 0x34, 0xbd, 0xb6, 0x71, 0x54, 0xab, 0xa0, 0xad, 0x49, + 0xbe, 0x0e, 0x52, 0xd8, 0xb0, 0x78, 0x41, 0x11, 0x7c, 0x0e, 0xb7, 0x6a, 0x39, + 0x54, 0x96, 0x39, 0xf7, 0xad, 0xe7, 0x6a, 0x90, 0x71, 0x0e, 0x79, 0x83, 0x97, + 0x8e, 0x9b, 0x23, 0x34, 0x9b, 0xee, 0x22, 0xcd, 0x0c, 0x71, 0xa1, 0xf0, 0x72, + 0x70, 0xe2, 0xce, 0x8b, 0x36, 0x05, 0x1b, 0x00, 0x55, 0xba, 0x97, 0x05, 0xab, + 0x22, 0x2e, 0x8e, 0x85, 0x8d, 0xc4, 0x5b, 0x66, 0xc1, 0xef, 0x3f, 0xe2, 0x66, + 0x55, 0x03, 0xe7, 0x8b, 0x30, 0x29, 0xef, 0xfb, 0xd5, 0xbb, 0x13, 0x9e, 0x85, + 0x2c, 0x3b, 0xf9, 0x07, 0x13, 0x2e, 0x54, 0xc3, 0xed, 0xad, 0x03, 0xf7, 0xe8, + 0x68, 0xf5, 0x23, 0x15, 0x5f, 0x9f, 0x6b, 0xce, 0xf4, 0x50, 0xbc, 0x9b, 0x56, + 0x31, 0x0c, 0xda, 0x17, 0x3e, 0x50, 0xe9, 0x5a, 0x6e, 0xe5, 0xf0, 0x68, 0xb2, + 0x5e, 0x32, 0x9c, 0x35, 0x48, 0xfc, 0x24, 0x99, 0x37, 0x3c, 0xde, 0x29, 0x36, + 0x0f, 0xbb, 0xfa, 0x5b, 0x64, 0xb5, 0x74, 0x4a, 0xb0, 0x3a, 0x4b, 0xd5, 0xd9, + 0x48, 0xc1, 0xbe, 0xf8, 0xcf, 0x4e, 0x6b, 0xd9, 0x4c, 0x32, 0x80, 0x9b, 0x18, + 0xf1, 0x18, 0x9c, 0x32, 0xbb, 0x8f, 0xae, 0x27, 0x53, 0xe4, 0x85, 0x1c, 0x31, + 0x96, 0xf5, 0xbb, 0x1d, 0xa0, 0x78, 0x51, 0xb5, 0xd3, 0x1f, 0x20, 0xa0, 0xfd, + 0x3a, 0x7a, 0x4b, 0x45, 0x01, 0xf3, 0x18, 0x5d, 0x26, 0x7b, 0x1c, 0x8b, 0xb3, + 0x59, 0x5d, 0x85, 0xc5, 0x3c, 0xae, 0x18, 0x9e, 0xc9, 0xdb, 0x6f, 0x14, 0x53, + 0xb3, 0xc6, 0xad, 0x4f, 0x3b, 0x93, 0xdd, 0x10, 0x6a, 0x3a, 0x39, 0x0d, 0xb2, + 0x7a, 0x1a, 0x75, 0x0e, 0x7e, 0xd0, 0x89, 0x7e, 0xbb, 0x61, 0x98, 0x48, 0x4d, + 0xcc, 0xdf, 0xa7, 0xa7, 0xe1, 0xd8, 0xeb, 0x2f, 0x23, 0x66, 0x8d, 0x54, 0xe9, + 0x8f, 0x9e, 0xd3, 0xae, 0x90, 0xfe, 0x0c, 0x27, 0x5f, 0x17, 0x7e, 0xcf, 0x70, + 0x1f, 0xd3, 0x0b, 0x92, 0xf6, 0x1b, 0x3c, 0x12, 0x53, 0xcc, 0x31, 0x78, 0x95, + 0xfe, 0x5e, 0x39, 0xc4, 0xea, 0x03, 0x24, 0x8e, 0x83, 0x20, 0x2e, 0xa5, 0x89, + 0xa0, 0xe8, 0xfc, 0xaf, 0xc4, 0x34, 0x07, 0xb5, 0x71, 0x9c, 0x08, 0x6a, 0xc2, + 0xf5, 0x8c, 0x1c, 0x4e, 0x05, 0x63, 0x69, 0x56, 0xb6, 0x30, 0x4e, 0x31, 0x7f, + 0x4f, 0x65, 0xb4, 0xe2, 0xb9, 0x9f, 0x25, 0xe8, 0xd7, 0xbb, 0x53, 0x28, 0xea, + 0x1f, 0x31, 0x13, 0x25, 0x6a, 0x45, 0x08, 0x01, 0x6a, 0x3e, 0x9d, 0x01, 0x2e, + 0xf8, 0x19, 0xfa, 0x36, 0xa5, 0xdb, 0xce, 0x7e, 0x3a, 0xff, 0x47, 0x42, 0xc0, + 0xcd, 0x3d, 0x5d, 0x9e, 0xb8, 0x40, 0x44, 0xa0, 0x03, 0x23, 0x39, 0x40, 0x69, + 0x9b, 0xc2, 0x79, 0x45, 0xb9, 0xac, 0x93, 0x82, 0x23, 0xc1, 0x17, 0x3f, 0x34, + 0xd1, 0x7e, 0x7e, 0x2e, 0x7b, 0xbc, 0xad, 0x2d, 0x91, 0x9d, 0x1a, 0xf5, 0x54, + 0x94, 0x0b, 0x68, 0xd7, 0x43, 0x3a, 0x6d, 0x67, 0xe8, 0x5c, 0xd3, 0x35, 0x66, + 0xb0, 0x60, 0xe4, 0x48, 0xb4, 0xa2, 0xa0, 0x52, 0xa8, 0xb7, 0x9e, 0x27, 0x57, + 0x8d, 0xce, 0x6e, 0x09, 0x88, 0x6e, 0xf0, 0x92, 0xef, 0x09, 0x67, 0x97, 0x47, + 0x8b, 0xb5, 0x4b, 0x9a, 0xbb, 0xa5, 0xae, 0x26, 0x79, 0x9b, 0x07, 0xcd, 0xc8, + 0x8c, 0x80, 0x2e, 0x6a, 0xf5, 0xcb, 0xfd, 0x41, 0x24, 0x29, 0x57, 0x00, 0xac, + 0x12, 0xd9, 0x10, 0xa0, 0x2a, 0x74, 0xc8, 0xab, 0xd2, 0x4d, 0x39, 0x88, 0x72, + 0xdd, 0x9d, 0x3a, 0xb3, 0xc5, 0x4c, 0x63, 0xa0, 0x9e, 0x51, 0xbb, 0x51, 0x62, + 0x54, 0x01, 0x03, 0xab, 0x0c, 0xae, 0xfc, 0x6e, 0x5b, 0x88, 0x05, 0x21, 0xf4, + 0x9c, 0x55, 0x93, 0xa7, 0xec, 0xe1, 0xef, 0xdc, 0x00, 0xad, 0x96, 0xc3, 0x82, + 0xfe, 0xcf, 0x0f, 0x9c, 0x1c, 0x8e, 0xcd, 0xcb, 0xc2, 0x2e, 0x89, 0x07, 0xce, + 0x99, 0xdf, 0x99, 0x4a, 0x33, 0x0a, 0x90, 0x44, 0x6d, 0xae, 0xec, 0xab, 0x71, + 0xf0, 0x02, 0x35, 0xdd, 0x70, 0x23, 0x3c, 0x43, 0x17, 0xd6, 0x4e, 0xf6, 0xba, + 0x3f, 0x65, 0x76, 0x42, 0xba, 0xad, 0x97, 0x35, 0xe5, 0x48, 0x68, 0xc1, 0x97, + 0x54, 0x56, 0x89, 0xa0, 0x57, 0x0b, 0xd4, 0x58, 0x4a, 0xad, 0xe4, 0x1a, 0x59, + 0x08, 0xb8, 0xaa, 0x33, 0x54, 0x95, 0x72, 0xc7, 0x20, 0x9f, 0x63, 0xad, 0x0b, + 0x80, 0x4c, 0x76, 0x02, 0xf4, 0x8d, 0xed, 0x66, 0x8c, 0x31, 0xa0, 0x7d, 0x76, + 0x02, 0xd6, 0xf8, 0x24, 0x29, 0xc3, 0xd2, 0xde, 0xe9, 0x2f, 0x38, 0xdb, 0x5b, + 0x92, 0x03, 0xac, 0x84, 0xd0, 0xfe, 0x14, 0xba, 0x6a, 0xc1, 0x9a, 0xaf, 0x94, + 0x00, 0xf2, 0xe3, 0x58, 0x3f, 0xb1, 0x68, 0xd3, 0x03, 0xca, 0x7a, 0x88, 0x71, + 0xdd, 0xd9, 0xa2, 0x95, 0x04, 0x1b, 0x30, 0xb8, 0x1e, 0xea, 0x1e, 0x7d, 0x82, + 0x24, 0x34, 0x4b, 0xd2, 0x68, 0xa9, 0x4a, 0x11, 0x1e, 0xa7, 0xc9, 0xb0, 0x6e, + 0xc5, 0x69, 0x12, 0x45, 0x2e, 0xeb, 0x01, 0xcf, 0x88, 0x87, 0xa3, 0xe2, 0x6e, + 0x14, 0x40, 0x6f, 0xfe, 0xec, 0x4b, 0xfd, 0x7a, 0x9f, 0xd8, 0x77, 0xce, 0x52, + 0x03, 0xfe, 0x6b, 0x05, 0x8d, 0x23, 0x1e, 0xc7, 0x1a, 0xf9, 0xca, 0x18, 0xed, + 0x5c, 0x73, 0x55, 0x06, 0xd7, 0xba, 0x28, 0xee, 0x68, 0xee, 0x66, 0x58, 0x7c, + 0x99, 0x8c, 0x8f, 0xec, 0xa7, 0xae, 0x06, 0x8c, 0x8e, 0xd0, 0x79, 0xe5, 0xa9, + 0xa4, 0x36, 0x72, 0x8c, 0xce, 0xe1, 0x0c, 0x8f, 0x12, 0x6f, 0x7b, 0x2f, 0xa0, + 0xd0, 0xff, 0x91, 0xcc, 0x41, 0xee, 0x28, 0xa1, 0x96, 0x23, 0x03, 0x37, 0xc6, + 0x1f, 0x42, 0xe9, 0x52, 0x2b, 0xf6, 0xde, 0x64, 0xfc, 0x5a, 0x57, 0xe3, 0x74, + 0x77, 0x06, 0x07, 0x63, 0x0b, 0xc1, 0x96, 0xed, 0x05, 0x2d, 0xff, 0x00, 0x83, + 0x61, 0xfc, 0x59, 0xfd, 0x9c, 0x48, 0xd2, 0x62, 0xb9, 0x3a, 0xee, 0x45, 0x65, + 0x2c, 0x78, 0x78, 0x05, 0xdf, 0xac, 0xe8, 0x3d, 0x04, 0xe5, 0x24, 0x40, 0x3a, + 0x25, 0xa1, 0x66, 0xa1, 0xf4, 0x8e, 0xcc, 0x8f, 0xff, 0x84, 0x4f, 0x09, 0xde, + 0x67, 0x48, 0x04, 0x52, 0xa6, 0x78, 0x9d, 0x48, 0xb7, 0xbd, 0xbd, 0x81, 0x1f, + 0x0e, 0xda, 0xda, 0xa8, 0xee, 0x8e, 0xb9, 0x16, 0x17, 0x99, 0x2e, 0xad, 0x6f, + 0x8a, 0x8b, 0x9e, 0xf4, 0xc5, 0xad, 0xb6, 0xf2, 0x52, 0x48, 0xb2, 0x13, 0xf3, + 0xd6, 0x93, 0xf6, 0x3c, 0x0d, 0x5d, 0x15, 0xab, 0x54, 0x32, 0x88, 0x07, 0x14, + 0x27, 0x35, 0x79, 0x37, 0x3c, 0x49, 0xcb, 0xf1, 0x47, 0xf9, 0x4a, 0x84, 0xad, + 0xe6, 0x48, 0x49, 0xeb, 0x5a, 0x94, 0x04, 0x40, 0x13, 0x38, 0x96, 0xa2, 0x45, + 0x55, 0xe4, 0x01, 0x55, 0x99, 0xc0, 0x46, 0xdf, 0xa6, 0xf1, 0x4a, 0x28, 0x70, + 0x53, 0x3a, 0xe4, 0x7d, 0x33, 0xff, 0x81, 0x6b, 0x8e, 0x46, 0x63, 0xf0, 0x70, + 0xc8, 0x0d, 0x8d, 0xb0, 0x1b, 0x43, 0xc6, 0x0f, 0x5f, 0xc0, 0x2c, 0x85, 0xac, + 0xf5, 0xe1, 0x06, 0xd3, 0xba, 0x71, 0xea, 0x69, 0x3b, 0xa4, 0x65, 0xdd, 0x61, + 0xff, 0x1d, 0x80, 0xfe, 0xee, 0xa1, 0xb6, 0xd5, 0xa1, 0x63, 0xd0, 0xc9, 0x62, + 0x43, 0x16, 0x36, 0xe1, 0xed, 0x62, 0x19, 0x66, 0xfe, 0x28, 0x5b, 0xc9, 0x70, + 0xa2, 0x66, 0xbb, 0x40, 0x8d, 0x4d, 0x48, 0xd5, 0x5e, 0xf7, 0x17, 0x04, 0xf5, + 0xb7, 0x98, 0x62, 0xbd, 0x80, 0x6a, 0x6a, 0x33, 0xe1, 0x13, 0xb1, 0x88, 0x32, + 0xb3, 0xd5, 0x9e, 0x3a, 0x69, 0x84, 0xe1, 0x4f, 0xd5, 0x2a, 0xc9, 0xd2, 0xbe, + 0x3a, 0xea, 0xaa, 0xbf, 0x38, 0x29, 0xcb, 0xf4, 0xdf, 0xca, 0x68, 0x03, 0xaf, + 0xcd, 0x1f, 0xc4, 0xcd, 0x02, 0x44, 0xd7, 0xb6, 0x3b, 0x4c, 0x9d, 0x4a, 0xa1, + 0xa2, 0x27, 0xad, 0xda, 0x80, 0x6a, 0x46, 0x24, 0xa0, 0x79, 0x65, 0xb9, 0xfd, + 0xa1, 0x73, 0xa2, 0xd9, 0x9a, 0x62, 0x4f, 0x4a, 0x78, 0xe9, 0xc7, 0x17, 0x63, + 0x01, 0x2b, 0x77, 0xaf, 0x32, 0x6c, 0x75, 0x22, 0x6b, 0x7d, 0xe8, 0x29, 0x74, + 0x4b, 0x6d, 0x39, 0x72, 0xe4, 0x7f, 0x6a, 0x14, 0x5b, 0x81, 0x34, 0x0d, 0x27, + 0x16, 0x20, 0x1e, 0x07, 0x1e, 0x47, 0x1a, 0x85, 0x5e, 0x9c, 0xc3, 0x6d, 0x39, + 0x49, 0x97, 0x15, 0x74, 0xbf, 0x3a, 0x06, 0x0f, 0xc0, 0xd8, 0x82, 0xd0, 0xa9, + 0x86, 0x5c, 0x24, 0xe0, 0x94, 0x03, 0x17, 0x30, 0xcb, 0xe1, 0x88, 0xe6, 0xfd, + 0xaf, 0xcb, 0xba, 0xf7, 0x51, 0xbe, 0x87, 0xaf, 0x96, 0x5c, 0xd9, 0x8d, 0x99, + 0x31, 0x04, 0xca, 0x6e, 0xdd, 0x29, 0x28, 0x0c, 0xda, 0x86, 0x55, 0x67, 0xbd, + 0xd4, 0xb4, 0xba, 0x47, 0x37, 0xe6, 0x1c, 0x3f, 0x0a, 0xd8, 0x75, 0xa8, 0xde, + 0xe6, 0xe6, 0xcd, 0xff, 0x26, 0x81, 0x88, 0x08, 0xff, 0x9b, 0x2d, 0x55, 0x87, + 0x95, 0xd6, 0x5d, 0x2a, 0x95, 0xb4, 0x56, 0x56, 0x19, 0xf7, 0xb2, 0x41, 0x62, + 0xcc, 0x47, 0x59, 0x9a, 0x33, 0x13, 0x06, 0xe3, 0x65, 0x2f, 0xfb, 0xc3, 0xb3, + 0xfd, 0x06, 0xc1, 0x46, 0x0c, 0x80, 0x6f, 0x4e, 0x61, 0xbe, 0xc2, 0xa2, 0xa7, + 0xb6, 0xc7, 0x96, 0xf6, 0x5d, 0xcf, 0x36, 0xa4, 0xaf, 0xc6, 0xd8, 0x10, 0x09, + 0x35, 0x21, 0x0a, 0x86, 0x38, 0x9f, 0x24, 0x9e, 0x2f, 0x82, 0x32, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7b, 0x8b, 0x33, 0x6b, 0x5f, 0x55, 0x40, 0x0b, 0x06, 0x79, 0xba, + 0x0c, 0x1e, 0xf0, 0x20, 0xc9, 0x26, 0x85, 0xa4, 0x24, 0x91, 0x79, 0x95, 0xea, + 0x63, 0xad, 0x1d, 0x5e, 0x28, 0xdd, 0x63, 0x99, 0x83, 0x82, 0xc7, 0xb3, 0x9d, + 0x26, 0xdb, 0x80, 0xb4, 0x3e, 0x32, 0x4f, 0xaf, 0x5d, 0x63, 0x60, 0x4a, 0x84, + 0xf2, 0x13, 0x5c, 0xbf, 0xf5, 0x98, 0xeb, 0x50, 0xe1, 0xd3, 0xa4, 0xb9, 0x9c, + 0xd6, 0x6c, 0x7a, 0xfd, 0xe6, 0x7f, 0xac, 0x47, 0xf0, 0x35, 0x8e, 0xc7, 0x83, + 0xbe, 0x35, 0x95, 0x47, 0x96, 0xe5, 0x97, 0x3a, 0xcf, 0xf6, 0x31, 0x98, 0xa3, + 0x55, 0x94, 0x18, 0x7e, 0xf8, 0x17, 0x00, 0x0b, 0x08, 0x88, 0x1e, 0x70, 0xe0, + 0xb2, 0xcd, 0xe2, 0x31, 0x51, 0x79, 0xc0, 0x84, 0x15, 0x51, 0xe8, 0xbd, 0x92, + 0x8e, 0xb6, 0x24, 0x87, 0x6e, 0x86, 0xb0, 0xb3, 0x3a, 0xab, 0x0c, 0xde, 0x87, + 0xeb, 0x8f, 0xd4, 0x78, 0x8d, 0xe9, 0xfb, 0x37, 0xea, 0xb3, 0xb4, 0x7f, 0xd5, + 0xdf, 0xe9, 0xb3, 0x7b, 0xcb, 0xb6, 0xe1, 0xf2, 0x25, 0xfd, 0x29, 0xab, 0x07, + 0xfc, 0x9f, 0xf5, 0xa0, 0x8f, 0x48, 0x66, 0x9e, 0x1c, 0x99, 0x68, 0xf5, 0x21, + 0x7a, 0xd3, 0x45, 0x2d, 0xad, 0x04, 0x78, 0x39, 0x07, 0x44, 0xe9, 0xd1, 0x67, + 0x85, 0xcd, 0x54, 0xa5, 0x03, 0x98, 0xb0, 0x14, 0xa0, 0x7b, 0x78, 0x45, 0x99, + 0x7a, 0x5b, 0x11, 0x6b, 0xb2, 0xc2, 0xf4, 0xc4, 0xe5, 0x64, 0x6e, 0x63, 0x08, + 0x2c, 0x5e, 0x3f, 0xee, 0x50, 0x92, 0xff, 0x2f, 0xa8, 0x9a, 0xe3, 0x2a, 0xd6, + 0x99, 0x07, 0x50, 0x4d, 0x68, 0x85, 0xb5, 0xbd, 0x72, 0xc8, 0x23, 0xd4, 0xc7, + 0x0d, 0x5e, 0xd4, 0x5c, 0xb0, 0x0c, 0x3e, 0x04, 0x05, 0x89, 0x2c, 0x88, 0x83, + 0x74, 0x53, 0xfe, 0xf2, 0xef, 0xb7, 0x51, 0x37, 0xf3, 0xc2, 0xab, 0xbc, 0x35, + 0x47, 0xdf, 0x86, 0xee, 0x01, 0x36, 0xb6, 0xe8, 0x5f, 0x33, 0xc5, 0x25, 0x58, + 0x3f, 0xfe, 0x27, 0xe6, 0xff, 0x48, 0xa8, 0x0d, 0x12, 0x4e, 0xf8, 0x01, 0xd3, + 0x24, 0x75, 0x4e, 0x16, 0x1d, 0x8b, 0xd6, 0x77, 0x44, 0xdf, 0x8a, 0xc5, 0x84, + 0x9b, 0x65, 0x5a, 0xcf, 0x9f, 0xa7, 0xb2, 0xea, 0x84, 0x62, 0x1d, 0x8e, 0x4d, + 0xd8, 0x57, 0x6d, 0xa7, 0x5e, 0xd1, 0xb4, 0x8a, 0xcb, 0x91, 0x08, 0x03, 0x27, + 0x3e, 0x48, 0x37, 0x73, 0xa9, 0x9d, 0x58, 0xcb, 0x70, 0x40, 0x8f, 0x3f, 0x23, + 0xa3, 0xea, 0x71, 0xd6, 0x73, 0x23, 0xb8, 0xf9, 0xfd, 0x51, 0x93, 0xb8, 0xdb, + 0x90, 0x6a, 0x18, 0x86, 0xe4, 0x26, 0xd0, 0xd3, 0x21, 0x6e, 0x7f, 0x0f, 0x42, + 0xa9, 0xaa, 0xe0, 0x0f, 0xc3, 0x79, 0x12, 0x20, 0xdb, 0xb1, 0x03, 0x15, 0x19, + 0xbc, 0x1e, 0xcc, 0xf8, 0x29, 0x8a, 0x22, 0xab, 0x20, 0x92, 0x71, 0x65, 0xaa, + 0x95, 0xd5, 0x46, 0x88, 0x83, 0x48, 0x17, 0x58, 0x3c, 0x64, 0x90, 0x28, 0x77, + 0x34, 0xea, 0x30, 0x0c, 0x38, 0x94, 0xf9, 0x9b, 0xaa, 0x29, 0xee, 0x97, 0x50, + 0x9d, 0x1c, 0x10, 0x71, 0xf2, 0x17, 0x42, 0xba, 0x67, 0x13, 0xed, 0xa0, 0x20, + 0x38, 0x1e, 0x60, 0x98, 0xb0, 0x5a, 0xde, 0x28, 0x09, 0x63, 0xb3, 0x98, 0xc0, + 0x3b, 0xf4, 0xc4, 0xe1, 0xf1, 0x9a, 0xd1, 0xad, 0xf1, 0xf0, 0xd6, 0x1f, 0xac, + 0xbf, 0x99, 0x66, 0xbd, 0xb0, 0x1f, 0xd1, 0x84, 0xb2, 0x00, 0xf8, 0x66, 0xc5, + 0xd1, 0x2e, 0x3d, 0xc5, 0x7e, 0xcf, 0x4f, 0xcd, 0x60, 0xc4, 0xa7, 0x56, 0x19, + 0x1d, 0xcf, 0x50, 0xbb, 0x0f, 0x97, 0x6f, 0x00, 0xe4, 0x36, 0x36, 0xa6, 0x83, + 0x08, 0x69, 0x2f, 0x40, 0x24, 0x4c, 0x39, 0x15, 0x34, 0x4b, 0x6f, 0x1f, 0x5e, + 0xe7, 0x0e, 0x51, 0xe1, 0x2b, 0x28, 0x53, 0x85, 0x53, 0x40, 0x3b, 0xe1, 0x49, + 0x8e, 0x00, 0x75, 0xdb, 0xda, 0x3e, 0x66, 0x6d, 0x9e, 0xbd, 0x18, 0xa1, 0x27, + 0x21, 0xc9, 0x73, 0x49, 0xac, 0x10, 0xe8, 0xfa, 0x2d, 0x6a, 0x59, 0xb2, 0x23, + 0x56, 0xa7, 0x71, 0x96, 0x18, 0xaa, 0xb5, 0xc7, 0x57, 0xf8, 0x82, 0x1e, 0xfc, + 0x3e, 0x07, 0x1b, 0x75, 0xf2, 0x15, 0xb2, 0x00, 0xb7, 0xd2, 0x99, 0x98, 0xed, + 0x7a, 0xe0, 0x05, 0x7f, 0xb2, 0x32, 0x9c, 0xa9, 0x13, 0x6d, 0xd2, 0xbc, 0x51, + 0xa6, 0x59, 0x01, 0x71, 0xdf, 0xca, 0x3b, 0xcb, 0x93, 0x6b, 0x11, 0xc6, 0x3c, + 0x03, 0xbb, 0x7f, 0xce, 0x30, 0xa0, 0x5f, 0x9b, 0x6f, 0x8f, 0xf3, 0x54, 0x06, + 0x04, 0x50, 0xa3, 0x45, 0x2d, 0xa1, 0x86, 0xe9, 0x3d, 0x6c, 0x32, 0xda, 0x62, + 0x72, 0xb8, 0x9b, 0xc4, 0xd6, 0xd5, 0xe8, 0x47, 0x8f, 0x29, 0x91, 0x01, 0x98, + 0x97, 0x11, 0xa9, 0xd2, 0x20, 0x97, 0xcd, 0xb7, 0x0c, 0x15, 0x0e, 0xd2, 0x6d, + 0xf4, 0x7b, 0x0c, 0xdd, 0xee, 0x52, 0x1b, 0x4f, 0x1e, 0x98, 0x96, 0xa1, 0xb6, + 0x97, 0x86, 0x53, 0xa4, 0xe3, 0x8b, 0x0d, 0x28, 0x52, 0x6e, 0x1e, 0x3a, 0x87, + 0x43, 0x5a, 0xc4, 0xfd, 0x30, 0x97, 0xaf, 0xe3, 0x21, 0xe7, 0x2d, 0x40, 0xc4, + 0x70, 0xf3, 0xb5, 0x3f, 0x5c, 0x35, 0x8d, 0x2e, 0x53, 0x69, 0x7c, 0xaf, 0x66, + 0x9d, 0xea, 0xa1, 0x1d, 0xe7, 0x7c, 0x98, 0x4a, 0x73, 0x0e, 0x5b, 0xf7, 0xb3, + 0x8e, 0xf6, 0x58, 0x9a, 0x5a, 0xa7, 0x55, 0x81, 0xbf, 0xd3, 0xc0, 0x07, 0x8a, + 0x63, 0xa3, 0x92, 0x96, 0x0e, 0xc3, 0xf2, 0xa0, 0x5c, 0x08, 0x1a, 0x48, 0x4e, + 0xb4, 0xf4, 0x25, 0xb7, 0x08, 0x36, 0x0f, 0x82, 0x85, 0x3c, 0xfd, 0x50, 0xa0, + 0x27, 0xfa, 0x92, 0x51, 0x76, 0x86, 0x96, 0xf3, 0x73, 0x5c, 0xd9, 0xed, 0xf7, + 0x9e, 0xcd, 0x4b, 0xe0, 0x8c, 0x57, 0x85, 0xc8, 0xae, 0xe7, 0x9a, 0x13, 0x23, + 0x87, 0x09, 0x94, 0x2f, 0x2c, 0xfd, 0x0f, 0x80, 0x7d, 0xaa, 0xb5, 0x0c, 0xc6, + 0x13, 0x1b, 0xab, 0x91, 0x25, 0x67, 0x36, 0x27, 0xf5, 0xe9, 0xa3, 0xd5, 0x3d, + 0x99, 0xfa, 0x02, 0x5c, 0x39, 0xfa, 0xb0, 0x9e, 0x2a, 0x21, 0x34, 0x6d, 0xc7, + 0xf8, 0x60, 0xa6, 0x2d, 0xd2, 0x10, 0x8e, 0x04, 0x41, 0x17, 0x8e, 0xf9, 0x76, + 0x21, 0xae, 0xfc, 0xe8, 0x97, 0x28, 0x10, 0xa4, 0xc7, 0xfc, 0x1b, 0x3c, 0x7e, + 0xaa, 0x83, 0xd4, 0xa6, 0x2b, 0xd7, 0x10, 0x98, 0x96, 0x11, 0xdd, 0x7e, 0x2f, + 0x4b, 0xdf, 0x15, 0xd8, 0x31, 0x00, 0x60, 0x11, 0xb4, 0x4e, 0xd9, 0x59, 0xdc, + 0x61, 0xd8, 0xde, 0x52, 0x74, 0x5e, 0x30, 0x67, 0x9c, 0xef, 0x04, 0x01, 0x3a, + 0xc6, 0x15, 0x4e, 0xf0, 0x64, 0x69, 0x82, 0x38, 0x74, 0x25, 0x21, 0x62, 0x26, + 0x3f, 0x3a, 0x4b, 0xa5, 0x65, 0x7b, 0x8d, 0x0e, 0xcf, 0x03, 0x86, 0x44, 0x1f, + 0x87, 0x30, 0xd0, 0xf1, 0x4e, 0x86, 0x8a, 0x32, 0x46, 0x37, 0xb0, 0xd3, 0x4a, + 0x9d, 0x1d, 0xd6, 0xc3, 0x9f, 0x28, 0xfd, 0x9a, 0xf3, 0x50, 0xdc, 0x23, 0x93, + 0x79, 0x29, 0xe3, 0x79, 0x70, 0xf8, 0x87, 0x37, 0x01, 0xd3, 0xfa, 0x47, 0x10, + 0x10, 0xa7, 0x21, 0x40, 0x68, 0xad, 0x1b, 0x89, 0x02, 0x52, 0x26, 0x1d, 0xd9, + 0x0d, 0x89, 0xc5, 0xa6, 0xf2, 0x90, 0x4b, 0xc6, 0x16, 0xb0, 0x27, 0xd7, 0xbe, + 0xc8, 0x79, 0xb7, 0xa1, 0x78, 0x25, 0x4f, 0xdc, 0xaa, 0x99, 0x1b, 0x42, 0x2b, + 0x7a, 0x96, 0x93, 0xe7, 0x64, 0xa1, 0x27, 0xb1, 0x72, 0xa0, 0xdc, 0xca, 0xc4, + 0x4f, 0x15, 0x27, 0x08, 0x6c, 0x48, 0x89, 0x85, 0xf9, 0x23, 0x5e, 0x28, 0x82, + 0xb4, 0x78, 0x16, 0x44, 0xeb, 0xa9, 0xed, 0x09, 0x61, 0xca, 0x7a, 0x68, 0x45, + 0xb5, 0x73, 0x65, 0xd8, 0x75, 0x4b, 0xdc, 0x79, 0x1f, 0x81, 0xc8, 0x09, 0xd0, + 0x12, 0xbd, 0x32, 0x9b, 0x6a, 0x44, 0xbd, 0x3d, 0xfa, 0x34, 0x73, 0x5c, 0xe4, + 0xc7, 0x38, 0xed, 0xef, 0xa4, 0x2d, 0x3c, 0x74, 0x09, 0x2b, 0x5c, 0xba, 0x9c, + 0x35, 0x81, 0x57, 0xd2, 0xab, 0x8a, 0x68, 0x83, 0x04, 0x0f, 0x40, 0xce, 0xc7, + 0x98, 0xa6, 0x9d, 0x7e, 0x0e, 0xa3, 0xb4, 0x76, 0xd9, 0x93, 0xd6, 0x96, 0xdb, + 0x0a, 0xdd, 0xd5, 0x43, 0x3f, 0x9e, 0x7a, 0x0f, 0xfb, 0xe0, 0x24, 0x26, 0x1e, + 0x79, 0x8d, 0xad, 0x05, 0x8e, 0xc8, 0xde, 0x26, 0x7c, 0x94, 0x78, 0xc8, 0x01, + 0xff, 0x37, 0x1e, 0x41, 0xc0, 0xbc, 0x0c, 0xf4, 0x6a, 0x4a, 0x84, 0xd0, 0xac, + 0xa4, 0x73, 0xe8, 0x80, 0xde, 0x96, 0x29, 0x69, 0xe9, 0xde, 0x23, 0x99, 0xa2, + 0x99, 0x56, 0x80, 0xdd, 0x76, 0x8f, 0xd7, 0x6b, 0xc6, 0x89, 0x6f, 0xe0, 0x2a, + 0xa4, 0x82, 0xf7, 0x6c, 0x72, 0x52, 0xe6, 0x65, 0x04, 0xe8, 0x80, 0xd2, 0x76, + 0xbf, 0x7d, 0x55, 0x7b, 0x39, 0x6a, 0xde, 0x3b, 0xb4, 0x7a, 0x6b, 0x0e, 0x0d, + 0xcf, 0x06, 0x3b, 0x1a, 0xd8, 0x56, 0x69, 0x4f, 0x8e, 0xef, 0x54, 0xca, 0x7d, + 0xf4, 0x2b, 0x41, 0xf9, 0xc6, 0x15, 0x3e, 0xa7, 0x47, 0x1c, 0xd5, 0x4f, 0x90, + 0x54, 0x7c, 0xc4, 0xd4, 0xef, 0x5f, 0xb1, 0xbf, 0xe5, 0x82, 0x88, 0x22, 0x59, + 0xc7, 0x77, 0xef, 0xc4, 0xeb, 0x8f, 0x5d, 0x75, 0x53, 0x1c, 0x1b, 0x80, 0x1b, + 0x72, 0x12, 0xc6, 0xf1, 0x45, 0x09, 0x78, 0x40, 0x20, 0xcb, 0xc3, 0xb0, 0x0e, + 0xb5, 0x31, 0xc5, 0x62, 0x44, 0x36, 0x89, 0x28, 0xa8, 0x51, 0xae, 0x53, 0x7c, + 0x74, 0x80, 0xee, 0x6e, 0x45, 0x1b, 0x29, 0x74, 0x32, 0xee, 0x17, 0x58, 0x22, + 0x99, 0x50, 0xcf, 0x78, 0x08, 0x49, 0x32, 0x6c, 0x3f, 0x28, 0xdd, 0x53, 0xd6, + 0x81, 0x19, 0xd2, 0x96, 0x95, 0x50, 0x12, 0xa2, 0x6f, 0x83, 0x3c, 0xdd, 0x29, + 0xc6, 0xf4, 0xc7, 0x16, 0xf1, 0xd3, 0x37, 0xd3, 0xf4, 0xd2, 0x1c, 0x7a, 0x63, + 0xf8, 0x54, 0xc9, 0xf4, 0xc1, 0xc4, 0xcc, 0xf1, 0x81, 0xad, 0x43, 0x16, 0xca, + 0xb1, 0x36, 0x46, 0x7c, 0x01, 0xd9, 0x6d, 0x36, 0xe2, 0x98, 0x1c, 0x86, 0xc4, + 0x76, 0x56, 0x7d, 0x83, 0x77, 0x6b, 0x73, 0x37, 0x35, 0xd5, 0x65, 0x8a, 0x48, + 0xf9, 0x89, 0x7c, 0xf1, 0xe5, 0x05, 0x2b, 0x37, 0xec, 0x1c, 0x88, 0x91, 0x47, + 0x36, 0xd9, 0xf9, 0x7c, 0x54, 0x99, 0xd7, 0x3d, 0x92, 0x3b, 0x45, 0x00, 0x69, + 0x4f, 0xfa, 0x57, 0x35, 0xc9, 0x3c, 0xdb, 0x87, 0xb3, 0x5d, 0x82, 0x95, 0x49, + 0xb1, 0xc6, 0x38, 0x3e, 0x95, 0xfd, 0x19, 0x02, 0xad, 0x29, 0x80, 0xf2, 0xa3, + 0xa2, 0x48, 0x3a, 0xce, 0x74, 0xb7, 0x64, 0x3d, 0x8e, 0xae, 0x8d, 0x07, 0x9a, + 0xa0, 0x06, 0x75, 0x41, 0x00, 0x6b, 0x94, 0xa6, 0xf9, 0x13, 0xdc, 0xff, 0x13, + 0xd6, 0x7c, 0xd9, 0xa8, 0xcf, 0xdf, 0x30, 0xb0, 0xc3, 0xd1, 0x5a, 0xaa, 0x47, + 0x0b, 0x3f, 0x89, 0x56, 0x10, 0x51, 0x42, 0xfa, 0x26, 0x11, 0xfe, 0xda, 0xa4, + 0x3f, 0xac, 0xbb, 0x3f, 0x05, 0x96, 0xf6, 0x78, 0x87, 0xcd, 0xee, 0x91, 0x42, + 0xc5, 0x09, 0x0a, 0x84, 0xe6, 0x25, 0x29, 0x31, 0xff, 0xcf, 0x61, 0xa5, 0x0a, + 0x4b, 0x92, 0x85, 0x30, 0x60, 0xe8, 0xb8, 0x7e, 0x10, 0xce, 0xa8, 0xce, 0x00, + 0xe4, 0x66, 0x5e, 0x5f, 0x93, 0x1f, 0x0e, 0x08, 0xdc, 0x52, 0x47, 0xbe, 0x1a, + 0xed, 0xc7, 0x9e, 0xbb, 0x7c, 0x20, 0x16, 0x2f, 0xca, 0x7b, 0xf9, 0x0e, 0x58, + 0x83, 0x02, 0x5f, 0xc9, 0x24, 0x36, 0x8d, 0x42, 0x45, 0x0b, 0x4f, 0xb7, 0xa7, + 0xe1, 0x91, 0x0e, 0xdd, 0x8d, 0x29, 0x5f, 0x03, 0xd4, 0xde, 0x03, 0xde, 0x60, + 0x51, 0xd1, 0xfc, 0xf2, 0x87, 0xf5, 0x4f, 0x38, 0x24, 0x41, 0xdd, 0xe0, 0x0c, + 0xb6, 0x83, 0xa4, 0x04, 0x8c, 0xe5, 0x4d, 0x42, 0x20, 0x90, 0x57, 0x24, 0xb3, + 0x09, 0xc7, 0x99, 0x92, 0x4b, 0x85, 0x4a, 0xfa, 0x37, 0x7b, 0x80, 0x1a, 0x03, + 0x52, 0xfc, 0x44, 0x50, 0xb3, 0x35, 0x27, 0x7a, 0xda, 0xd7, 0x61, 0xe4, 0x8a, + 0x1d, 0x1d, 0xd3, 0x78, 0x93, 0x6a, 0x49, 0x1e, 0x28, 0x6c, 0xaf, 0xc7, 0x00, + 0xb4, 0x8e, 0xdf, 0x15, 0xf1, 0xc2, 0xd6, 0xed, 0xf1, 0xa2, 0x4e, 0x0e, 0x51, + 0xb3, 0x98, 0x55, 0x64, 0xeb, 0xa9, 0x69, 0xcd, 0x6e, 0xe6, 0x59, 0xba, 0xae, + 0xf7, 0x46, 0xe1, 0x3a, 0xba, 0x64, 0xaf, 0xad, 0x58, 0xaf, 0x52, 0xf4, 0x28, + 0x17, 0x36, 0x45, 0x75, 0x7a, 0x40, 0x7e, 0x1f, 0xdf, 0xd9, 0x89, 0x38, 0x0c, + 0x02, 0xbc, 0xc3, 0xc3, 0x7f, 0x48, 0x90, 0xc0, 0x8e, 0xb9, 0x31, 0x62, 0xcf, + 0x78, 0xbc, 0x3c, 0x74, 0x53, 0xf3, 0xf9, 0x92, 0xa7, 0x94, 0x53, 0x4c, 0x07, + 0xe3, 0x96, 0x8d, 0x82, 0x70, 0xaa, 0x19, 0x1f, 0x67, 0x80, 0x0a, 0x0b, 0xb3, + 0xe7, 0xbf, 0xa5, 0x4b, 0x0f, 0x6f, 0xa5, 0x3e, 0xe8, 0xfb, 0x13, 0x69, 0x82, + 0xce, 0x71, 0xf4, 0x08, 0x64, 0xb5, 0x4d, 0x00, 0x45, 0x1a, 0xf3, 0xf5, 0x32, + 0x74, 0x22, 0x42, 0x16, 0x06, 0xea, 0x10, 0xc0, 0xd6, 0x12, 0x7c, 0x02, 0xf9, + 0x1a, 0xd3, 0xae, 0xb9, 0xff, 0xd6, 0x11, 0x12, 0x25, 0x14, 0x14, 0x48, 0xbe, + 0x82, 0x40, 0xc4, 0x29, 0x73, 0xac, 0x52, 0xd7, 0x1b, 0x01, 0x2f, 0xe8, 0xef, + 0x41, 0xf0, 0x0e, 0xc1, 0x96, 0xc7, 0x57, 0x89, 0x9e, 0xf8, 0xc0, 0x0e, 0xf8, + 0xdf, 0x44, 0x5c, 0x56, 0x54, 0x69, 0xd8, 0x4b, 0xd0, 0x2c, 0x7f, 0xc4, 0x1b, + 0xfc, 0xdf, 0x98, 0x95, 0x1f, 0x50, 0xe8, 0x3f, 0x19, 0xa0, 0x00, 0xa9, 0xe4, + 0x53, 0xf6, 0x21, 0x67, 0xe7, 0x35, 0x0f, 0x92, 0x36, 0x08, 0x31, 0xbd, 0x7c, + 0x52, 0x22, 0xb6, 0x70, 0x61, 0x6e, 0x4b, 0x6c, 0xa8, 0xa2, 0x35, 0x50, 0xca, + 0xd8, 0xac, 0x0d, 0xdb, 0x76, 0x45, 0xe2, 0xb9, 0x71, 0x3b, 0xe7, + ], + script_code: Script(vec![0x6a, 0x00, 0x00, 0x65, 0x53, 0xac, 0x63, 0x53, 0x63]), + transparent_input: None, + hash_type: 1, + amount: 1871432121379810, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x36, 0x77, 0xa9, 0x48, 0x4f, 0x04, 0x04, 0xfb, 0x50, 0x64, 0x58, 0x56, 0xf4, + 0xd4, 0xa7, 0x0b, 0x2e, 0x2b, 0x1c, 0x2d, 0x86, 0x2f, 0x1d, 0x4e, 0xf6, 0x8d, + 0x52, 0x09, 0x60, 0xa1, 0x2a, 0x2b, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0xa0, 0x1e, 0x64, 0x49, + 0xae, 0x43, 0x5c, 0x24, 0xbe, 0x7b, 0x9d, 0x28, 0x8a, 0xd7, 0x57, 0x12, 0xc9, + 0x2a, 0xa5, 0x06, 0x18, 0xdf, 0xba, 0x18, 0xe8, 0x4e, 0x88, 0xd4, 0x60, 0x68, + 0xdf, 0x0b, 0x42, 0xaf, 0x89, 0x29, 0x07, 0x00, 0x6a, 0x52, 0xac, 0x65, 0x51, + 0x63, 0x6e, 0x99, 0x51, 0xd0, 0x09, 0xa9, 0x39, 0xf7, 0x59, 0xa8, 0xa2, 0xc0, + 0x49, 0xde, 0xf0, 0x97, 0x7f, 0x61, 0xea, 0x11, 0x23, 0x14, 0x06, 0xcd, 0x10, + 0x95, 0x6d, 0x16, 0x55, 0x78, 0xbb, 0x29, 0xe4, 0x76, 0x96, 0x76, 0x9a, 0x58, + 0x0e, 0x07, 0x01, 0x00, 0x15, 0xaf, 0x3b, 0x50, 0x00, 0x13, 0x58, 0xd0, 0x37, + 0xe5, 0x70, 0xfe, 0x0b, 0x50, 0x0e, 0xe2, 0x99, 0x8c, 0xdf, 0x06, 0x00, 0x03, + 0x7e, 0x28, 0x30, 0x34, 0x34, 0x96, 0x2f, 0x03, 0x92, 0x48, 0x3d, 0xec, 0xad, + 0x2f, 0x9f, 0x4e, 0xbc, 0x99, 0x05, 0x4b, 0xbc, 0xf1, 0x55, 0xff, 0xae, 0x67, + 0x76, 0x34, 0xc3, 0xfb, 0x98, 0x0d, 0xc5, 0xe8, 0xec, 0x67, 0xa4, 0x65, 0x7e, + 0x80, 0xa2, 0x9a, 0x79, 0x6f, 0x39, 0x62, 0xae, 0x0c, 0xb9, 0xc7, 0x86, 0x82, + 0xb3, 0xf4, 0xf9, 0x2e, 0x5a, 0x1e, 0xd1, 0xda, 0x2b, 0xbf, 0xc1, 0x71, 0x07, + 0x7e, 0xef, 0x83, 0x65, 0xbb, 0x38, 0xce, 0x94, 0xca, 0xb0, 0x28, 0x33, 0xce, + 0x47, 0xd4, 0xa0, 0x98, 0x65, 0x72, 0x94, 0xec, 0x10, 0xb2, 0x99, 0x74, 0x22, + 0x22, 0xd0, 0xbf, 0x74, 0x3f, 0x40, 0xc8, 0xea, 0x97, 0x14, 0x32, 0x5c, 0x8a, + 0x37, 0x05, 0x08, 0x24, 0xfa, 0x75, 0x62, 0xd2, 0xc9, 0x25, 0x2c, 0x34, 0xa9, + 0x84, 0x50, 0x27, 0xd6, 0x63, 0x90, 0xe9, 0x56, 0xb2, 0x5e, 0x16, 0x6c, 0x44, + 0x95, 0xd3, 0xde, 0xd3, 0xf7, 0xac, 0xcf, 0x74, 0x76, 0x38, 0x99, 0x47, 0x35, + 0x11, 0x34, 0x12, 0x98, 0xfe, 0xb1, 0x89, 0xb7, 0xed, 0x34, 0xe5, 0x67, 0xd7, + 0x2f, 0x1d, 0xf4, 0xbf, 0x69, 0x7f, 0x71, 0x46, 0x49, 0x3f, 0xa5, 0xc2, 0x36, + 0x91, 0x22, 0x7b, 0x90, 0xb2, 0x51, 0x22, 0xc5, 0x40, 0xdf, 0x0a, 0x6f, 0x2e, + 0xc0, 0x6f, 0x9d, 0x89, 0xa3, 0xf7, 0x71, 0xe9, 0xb8, 0xed, 0x74, 0x79, 0x40, + 0x85, 0x51, 0x06, 0xd5, 0xea, 0x71, 0xba, 0x89, 0xe8, 0xf2, 0x0c, 0xde, 0xa6, + 0x9a, 0x77, 0x8a, 0x59, 0xe4, 0xdf, 0x79, 0x28, 0xc0, 0x35, 0x56, 0x23, 0x31, + 0xc8, 0xe1, 0x62, 0xb8, 0xfd, 0x5e, 0xbb, 0xd5, 0xe2, 0xb3, 0x7b, 0xea, 0x7a, + 0xf0, 0x69, 0x07, 0x10, 0x40, 0xc3, 0x7c, 0x1a, 0x1c, 0x37, 0xf0, 0x76, 0x0f, + 0xed, 0x7d, 0xb7, 0xfa, 0x70, 0xa9, 0x48, 0x94, 0x03, 0x00, 0x45, 0x76, 0xa2, + 0xcc, 0xe9, 0x0a, 0x39, 0x4b, 0x5e, 0xc5, 0x8b, 0x2e, 0x5d, 0x0e, 0x1a, 0xf8, + 0xb0, 0x29, 0x6d, 0x0b, 0xf0, 0x2c, 0x55, 0x97, 0xa4, 0x33, 0x54, 0x14, 0x43, + 0x35, 0xe0, 0x6a, 0x80, 0x1c, 0x6e, 0x7c, 0x73, 0x29, 0x7d, 0xfe, 0x0b, 0x32, + 0xfc, 0xb8, 0x75, 0x33, 0x81, 0x71, 0xdd, 0x1e, 0xeb, 0xeb, 0x12, 0x3f, 0xea, + 0xfa, 0x32, 0xa5, 0xd8, 0xc7, 0xce, 0x58, 0x39, 0x0e, 0xa2, 0xdf, 0x26, 0xc6, + 0x88, 0x88, 0xda, 0xf3, 0x81, 0x6b, 0x7d, 0x02, 0x97, 0xa1, 0x7b, 0x5f, 0x5d, + 0x20, 0x8d, 0xe9, 0x22, 0xe7, 0x73, 0x97, 0x2b, 0x95, 0xe6, 0x96, 0x5e, 0x58, + 0xfb, 0xf6, 0x4f, 0xae, 0x06, 0xf0, 0xc3, 0x89, 0x6e, 0x0b, 0x57, 0x89, 0x0d, + 0xd7, 0xf3, 0xc6, 0x4c, 0x3d, 0x5c, 0xeb, 0xb6, 0xa7, 0x44, 0xc5, 0x93, 0x38, + 0x61, 0x22, 0x71, 0x82, 0x08, 0x04, 0x95, 0xce, 0x9a, 0xc2, 0xe1, 0x73, 0x09, + 0x9c, 0xdc, 0x35, 0x8d, 0xa8, 0x7d, 0xd7, 0x4a, 0x77, 0x34, 0xff, 0xff, 0xc4, + 0x5f, 0xb6, 0xad, 0x1f, 0x38, 0x9c, 0x6a, 0x4d, 0x49, 0x86, 0x62, 0x64, 0x60, + 0x56, 0x08, 0x4d, 0x09, 0xb7, 0x84, 0x88, 0xa3, 0xba, 0x1d, 0x8a, 0x3d, 0x6b, + 0x48, 0x9a, 0xfd, 0xf2, 0x32, 0xd6, 0xd0, 0x70, 0xa1, 0xb5, 0x06, 0x0c, 0xaa, + 0x44, 0x3d, 0x0c, 0x7e, 0xe5, 0x19, 0x04, 0x54, 0x7f, 0xaf, 0x53, 0x95, 0xcb, + 0xd0, 0xba, 0x99, 0x48, 0x0a, 0xd0, 0x4a, 0xe0, 0xe1, 0x91, 0x5b, 0xd7, 0x7f, + 0xa2, 0x6d, 0x04, 0x17, 0x5b, 0x00, 0xfd, 0xc8, 0x1e, 0xf6, 0xf3, 0x79, 0x23, + 0x72, 0x49, 0x27, 0xf0, 0x82, 0x66, 0xb6, 0x86, 0x40, 0x93, 0x13, 0xdc, 0x13, + 0xbc, 0x39, 0x9d, 0x19, 0x77, 0xb8, 0xf6, 0x58, 0x8c, 0x0e, 0x08, 0x72, 0x10, + 0xf0, 0x51, 0xcf, 0x6e, 0x36, 0xe1, 0x4e, 0x32, 0xaa, 0x23, 0xba, 0x6a, 0xe4, + 0x33, 0x1f, 0x22, 0x39, 0xe7, 0x05, 0xf6, 0x79, 0x54, 0x2f, 0xbd, 0x4e, 0xd2, + 0xbf, 0x31, 0x91, 0x24, 0x36, 0x81, 0xf8, 0x27, 0x89, 0x6b, 0x1b, 0xb1, 0xc4, + 0xb7, 0x8b, 0x34, 0xc4, 0x87, 0xa4, 0xed, 0xfa, 0x97, 0xd3, 0x6d, 0x62, 0xee, + 0x32, 0x49, 0xef, 0xe0, 0x94, 0xc3, 0x87, 0x8a, 0xde, 0xdf, 0x9f, 0x2b, 0x17, + 0xd5, 0x11, 0x99, 0x80, 0x4f, 0x42, 0x9c, 0xd7, 0x04, 0xa7, 0xc8, 0x6c, 0x85, + 0x0c, 0xe1, 0x5d, 0x3c, 0x5f, 0x01, 0xd1, 0xad, 0x17, 0xeb, 0xb6, 0xc2, 0x88, + 0x3f, 0x28, 0xe8, 0x15, 0xbc, 0x45, 0x2a, 0x56, 0x07, 0x98, 0x05, 0xa5, 0xdd, + 0x69, 0x00, 0xe5, 0x5f, 0x47, 0x7e, 0xca, 0xc2, 0x14, 0x3f, 0x02, 0xee, 0x98, + 0xc8, 0xd9, 0xb1, 0xb7, 0x03, 0x93, 0xa1, 0x70, 0xba, 0x25, 0x48, 0x06, 0xb4, + 0x08, 0x5b, 0x8d, 0xf9, 0xca, 0x04, 0x07, 0x18, 0x42, 0xa3, 0xaf, 0x93, 0x33, + 0x16, 0x83, 0x0d, 0x53, 0xa7, 0xcb, 0x88, 0xd2, 0xa9, 0x82, 0x3b, 0xcd, 0xfb, + 0xec, 0x8f, 0x18, 0xc8, 0x6a, 0xc3, 0xdf, 0x89, 0x42, 0x38, 0x00, 0x1b, 0xa8, + 0xfa, 0x31, 0x3f, 0x80, 0xcf, 0xe7, 0x5f, 0x7c, 0xb5, 0xd9, 0x73, 0xcc, 0x77, + 0xf3, 0x21, 0xf1, 0x95, 0x2f, 0x30, 0x50, 0x18, 0xc0, 0xbf, 0x23, 0x8b, 0x80, + 0xe3, 0x21, 0x19, 0x90, 0x60, 0x66, 0xf6, 0x4e, 0x64, 0x5e, 0x2b, 0xca, 0xd7, + 0xe4, 0xcd, 0xbe, 0xf0, 0x07, 0xf7, 0xe9, 0xad, 0x8a, 0x31, 0x83, 0x8b, 0x9e, + 0xae, 0xc3, 0x85, 0xe3, 0xf2, 0x5e, 0x16, 0x04, 0xa6, 0xd4, 0x64, 0x99, 0x87, + 0x5a, 0xc1, 0x4a, 0x6c, 0xb3, 0x55, 0xa3, 0xd4, 0x32, 0x91, 0x45, 0x80, 0x3c, + 0x6b, 0xfb, 0x82, 0xe2, 0x9a, 0xb0, 0x29, 0x9f, 0x91, 0x7a, 0x74, 0x02, 0x81, + 0x57, 0x71, 0x7c, 0x08, 0x48, 0x68, 0x63, 0x94, 0x5c, 0x5a, 0x02, 0x36, 0x58, + 0xee, 0xe4, 0xa8, 0xb2, 0x89, 0x56, 0x4c, 0x22, 0xa9, 0x67, 0x1c, 0x56, 0x91, + 0x33, 0x5e, 0xb1, 0x25, 0x89, 0x88, 0x51, 0x67, 0x8f, 0x54, 0x93, 0x45, 0x10, + 0xbf, 0x30, 0x91, 0xc6, 0x02, 0xe1, 0x2a, 0x32, 0x03, 0xa2, 0xf3, 0x2f, 0x34, + 0x7d, 0x4b, 0xdc, 0x9d, 0x44, 0x92, 0x4d, 0xc8, 0x67, 0x5c, 0x9f, 0x24, 0x06, + 0x4d, 0x35, 0xb0, 0x09, 0xb6, 0xdd, 0xbd, 0xb2, 0x37, 0x20, 0x75, 0x33, 0xd5, + 0xbb, 0xad, 0x3b, 0xa1, 0xa3, 0xd6, 0xb0, 0x89, 0x32, 0x9b, 0xe1, 0x47, 0x23, + 0x4e, 0x75, 0x1a, 0x49, 0x27, 0x9d, 0x74, 0xdb, 0x88, 0xdb, 0x5c, 0xa1, 0x02, + 0xd5, 0xe0, 0xe1, 0xaa, 0xc7, 0xcc, 0xf9, 0x66, 0xb0, 0xa8, 0x13, 0x67, 0x09, + 0x5d, 0xa2, 0x1d, 0xc4, 0xb7, 0x36, 0x55, 0x95, 0x30, 0x80, 0xe3, 0x54, 0xbd, + 0x22, 0x09, 0xf2, 0x66, 0x82, 0x10, 0xe9, 0x47, 0x41, 0x27, 0x31, 0x1d, 0x93, + 0x45, 0xce, 0x1e, 0xbd, 0x3a, 0xe5, 0x24, 0x24, 0x5b, 0xbb, 0x44, 0x7a, 0x44, + 0x50, 0x80, 0xb5, 0xfa, 0x23, 0xcd, 0xfe, 0x98, 0xb3, 0xf6, 0xf6, 0x3c, 0x44, + 0xeb, 0xe7, 0x22, 0xb9, 0x7a, 0x79, 0x10, 0xdf, 0x7e, 0xa6, 0x22, 0x5e, 0xd9, + 0xdc, 0xb4, 0x49, 0x84, 0x93, 0xe8, 0xef, 0x55, 0x31, 0xf9, 0xf9, 0x77, 0x31, + 0x84, 0xd7, 0xb4, 0xf5, 0x36, 0x77, 0xb1, 0xd0, 0x44, 0xf6, 0xf1, 0x44, 0x07, + 0xde, 0x5d, 0x67, 0xe0, 0x77, 0xd2, 0x0f, 0x2e, 0x9d, 0x7f, 0xd7, 0x15, 0xbf, + 0x9b, 0x19, 0x9b, 0x93, 0xb9, 0x84, 0x02, 0x46, 0xef, 0x9c, 0x07, 0x35, 0xe4, + 0x88, 0xff, 0x7c, 0x80, 0xb9, 0x41, 0x78, 0xac, 0xa3, 0x1b, 0x13, 0xc3, 0x7c, + 0x9a, 0xeb, 0x7f, 0x62, 0xe2, 0xd8, 0x58, 0x97, 0xea, 0x2e, 0x2a, 0x23, 0x28, + 0xee, 0x03, 0xc9, 0x7f, 0x2f, 0x3f, 0x4d, 0x20, 0xa8, 0xe7, 0x30, 0x24, 0xc5, + 0x50, 0x8e, 0xee, 0xbd, 0x3a, 0x12, 0x67, 0x31, 0xcd, 0xbf, 0x21, 0xfd, 0xad, + 0xb1, 0x4b, 0x4e, 0x59, 0x1c, 0xba, 0xb1, 0x44, 0xbe, 0xc3, 0x5a, 0x72, 0xac, + 0xbf, 0x94, 0x84, 0xf4, 0x7a, 0x10, 0xb9, 0x1e, 0xfc, 0x04, 0x27, 0xfe, 0xcf, + 0x3f, 0xfc, 0xf1, 0x69, 0xd7, 0x00, 0x59, 0xb4, 0x02, 0x79, 0xff, 0xa0, 0x2c, + 0x51, 0x06, 0x74, 0x27, 0xa0, 0xda, 0xea, 0xd6, 0xf9, 0x4b, 0xaf, 0xe4, 0xc1, + 0x23, 0x3a, 0x22, 0x25, 0xeb, 0x56, 0x00, 0x3f, 0xc3, 0x85, 0x42, 0x0d, 0x5a, + 0x9f, 0xf3, 0xd5, 0x91, 0x55, 0x23, 0xa0, 0x8c, 0x87, 0xeb, 0x2e, 0xa6, 0x69, + 0x17, 0x23, 0x3a, 0x73, 0x25, 0xfe, 0x79, 0x3f, 0x41, 0x07, 0x6d, 0x64, 0x25, + 0x5a, 0xbd, 0x15, 0x21, 0x47, 0x66, 0x60, 0xe9, 0x04, 0x91, 0x60, 0x2c, 0x69, + 0xa4, 0xab, 0xb1, 0x38, 0x84, 0x43, 0x10, 0x72, 0xef, 0x96, 0xa0, 0x95, 0xbe, + 0x41, 0x1f, 0xfc, 0xff, 0xb7, 0x86, 0x3f, 0xef, 0x7d, 0xab, 0x4d, 0x4a, 0x72, + 0xa2, 0xd0, 0xbb, 0xd3, 0x6f, 0x9f, 0xdf, 0x0b, 0x35, 0x38, 0xb3, 0x9c, 0xae, + 0x5f, 0xf6, 0x0e, 0x5a, 0xc6, 0xb6, 0x09, 0x70, 0x72, 0x43, 0x14, 0x6e, 0xb5, + 0x36, 0x0a, 0xe7, 0xf9, 0x3f, 0x79, 0x9b, 0x6c, 0x27, 0xe6, 0x5a, 0x0a, 0x06, + 0x39, 0x87, 0x38, 0x66, 0x0f, 0xda, 0xd2, 0xcf, 0xb3, 0x1a, 0xa5, 0x40, 0xd5, + 0xe8, 0x90, 0x06, 0x78, 0xb9, 0xda, 0xb5, 0x24, 0x79, 0xbd, 0x0c, 0xd6, 0xf1, + 0xa5, 0x98, 0x67, 0x3e, 0xed, 0x9c, 0x76, 0xe3, 0x38, 0x10, 0x49, 0x47, 0x18, + 0xd0, 0x5d, 0xdf, 0xdc, 0x00, 0x7a, 0x54, 0xbc, 0xd1, 0xcc, 0x4c, 0x97, 0x40, + 0xf7, 0xe5, 0x3a, 0x31, 0x68, 0x1d, 0x2b, 0x2c, 0x6e, 0xde, 0x79, 0x28, 0x11, + 0x49, 0xea, 0xc3, 0x0f, 0x6e, 0xe5, 0x83, 0x60, 0x5a, 0xc2, 0xff, 0xae, 0xc1, + 0x55, 0x00, 0x35, 0xdc, 0x5a, 0xbb, 0x35, 0x89, 0x44, 0x68, 0xf1, 0x2d, 0x5d, + 0x08, 0xd7, 0x34, 0x36, 0xa8, 0x59, 0xe5, 0x50, 0x7f, 0xdd, 0x1a, 0x46, 0x38, + 0xfb, 0xe6, 0x81, 0xb0, 0xa0, 0xef, 0xfb, 0xbb, 0xf7, 0x4c, 0x99, 0x39, 0x9d, + 0xca, 0x69, 0x02, 0xa0, 0x74, 0xc8, 0x33, 0x35, 0x60, 0x7a, 0x0c, 0x0d, 0xb0, + 0x1c, 0xa3, 0xca, 0x2f, 0xa8, 0x18, 0x57, 0x24, 0x02, 0xe2, 0xfa, 0xef, 0xb3, + 0x07, 0xbe, 0x22, 0xc7, 0xd5, 0x61, 0x1f, 0xf6, 0xfb, 0x5a, 0x31, 0xb4, 0x62, + 0x16, 0x59, 0xd8, 0x4d, 0x8a, 0x7a, 0x1a, 0xdc, 0xa2, 0xfc, 0x4e, 0xb8, 0xb8, + 0x97, 0x04, 0x43, 0x93, 0x27, 0x64, 0x46, 0x31, 0xa7, 0xbb, 0xc1, 0xa8, 0x41, + 0xf3, 0x65, 0x83, 0x0d, 0x27, 0xc8, 0xaa, 0x4d, 0x75, 0xc8, 0x07, 0x87, 0xbd, + 0x10, 0xb7, 0x14, 0xcb, 0x97, 0x9c, 0x1b, 0x0f, 0x3f, 0x0b, 0x41, 0xee, 0x94, + 0x22, 0x94, 0x24, 0x8c, 0x48, 0x5c, 0xf9, 0x9c, 0x6b, 0xc4, 0x63, 0x20, 0x7a, + 0xf3, 0x83, 0x61, 0x97, 0x83, 0x57, 0x41, 0x41, 0x5d, 0xe6, 0x1f, 0xf2, 0x9f, + 0xad, 0x30, 0x01, 0x82, 0x71, 0x4c, 0x20, 0xca, 0x34, 0x04, 0x7b, 0xcc, 0xb7, + 0x05, 0x81, 0x0f, 0xfa, 0xe5, 0x3a, 0x34, 0x16, 0xa5, 0x3f, 0x28, 0xaf, 0xc0, + 0x08, 0xe8, 0xbf, 0xf9, 0x49, 0xe4, 0x3a, 0x54, 0x10, 0xe6, 0xad, 0xb6, 0x65, + 0xf9, 0x9f, 0xa4, 0xca, 0xfa, 0xc2, 0xe0, 0xf2, 0xc0, 0xf1, 0x34, 0xbd, 0xba, + 0x83, 0x81, 0xc2, 0xbb, 0xac, 0x43, 0x33, 0x2a, 0xcd, 0xcb, 0x10, 0x08, 0x2e, + 0xf3, 0x43, 0xa3, 0x5a, 0xc6, 0x4f, 0x4b, 0xa1, 0x6e, 0x49, 0x57, 0xc7, 0x1e, + 0x9a, 0x2b, 0xd9, 0xa5, 0xcd, 0x6a, 0x92, 0x25, 0x8a, 0x9e, 0x58, 0x8e, 0x02, + 0x1a, 0x06, 0x65, 0x09, 0x04, 0x67, 0x0d, 0xa2, 0xc0, 0xe5, 0x2c, 0x52, 0x4f, + 0x6e, 0x5c, 0xe3, 0xee, 0x27, 0x5a, 0x0a, 0x63, 0x10, 0x3b, 0x5f, 0x92, 0x64, + 0x16, 0xc0, 0xbd, 0x5d, 0xa1, 0xae, 0x65, 0x69, 0xd3, 0xa4, 0xee, 0x4d, 0xbc, + 0x5e, 0xc0, 0x8b, 0x29, 0x72, 0x02, 0xc9, 0xd7, 0x13, 0xab, 0xc3, 0x47, 0x4d, + 0xe4, 0x94, 0x0f, 0x59, 0xb1, 0xf3, 0xfe, 0x0e, 0x92, 0x76, 0xa1, 0x76, 0x3b, + 0x2d, 0xea, 0x39, 0x40, 0xb0, 0xc1, 0xf7, 0xab, 0x5d, 0xa3, 0xf4, 0x55, 0x62, + 0x3e, 0x04, 0x96, 0x82, 0xd0, 0x92, 0x18, 0x9c, 0xb7, 0x9e, 0xcf, 0xd4, 0x3c, + 0x3b, 0xf1, 0x0e, 0x7f, 0x2c, 0x8d, 0x4d, 0xe3, 0xa7, 0x36, 0xf8, 0x69, 0xf0, + 0x87, 0x03, 0xc4, 0xe5, 0x9f, 0x57, 0x4f, 0x77, 0xaa, 0x86, 0x1c, 0xbf, 0xdd, + 0xd0, 0x7f, 0x77, 0xdc, 0x24, 0xa9, 0x74, 0x10, 0xaf, 0xc7, 0xcf, 0xbe, 0x3c, + 0xe1, 0xff, 0xd2, 0x24, 0x53, 0x5c, 0xf3, 0x05, 0xce, 0xcc, 0x78, 0x56, 0xa4, + 0xd4, 0x8a, 0x6d, 0xec, 0x17, 0xa2, 0x4b, 0x6d, 0x27, 0xfe, 0x26, 0x64, 0xbc, + 0x2b, 0x2b, 0x71, 0x1d, 0x67, 0x13, 0x90, 0x6c, 0xed, 0x8a, 0x80, 0x66, 0x62, + 0x18, 0x40, 0xd9, 0x0c, 0x23, 0xae, 0x33, 0x77, 0x30, 0x67, 0x9d, 0x2c, 0xde, + 0x32, 0x69, 0xab, 0x1f, 0x42, 0xac, 0x03, 0xff, 0xdb, 0xa0, 0x32, 0xd3, 0x2c, + 0xa8, 0x79, 0x63, 0x82, 0x56, 0x56, 0x5d, 0xe1, 0xd2, 0xde, 0x39, 0xf5, 0x6f, + 0x94, 0x57, 0x95, 0xd6, 0xe9, 0x58, 0xe6, 0x93, 0xdc, 0x8c, 0xbf, 0x6d, 0x04, + 0x30, 0x00, 0xcc, 0x7a, 0x40, 0x15, 0xf0, 0x2d, 0x0f, 0xe3, 0x97, 0xec, 0x57, + 0xf8, 0xfe, 0x29, 0x2e, 0x85, 0x14, 0x24, 0xe8, 0x40, 0x6d, 0x38, 0xdd, 0xb8, + 0xd1, 0xde, 0x9d, 0xef, 0x67, 0x2e, 0x92, 0x7d, 0x3d, 0xc1, 0xf4, 0x11, 0xdc, + 0x78, 0xad, 0xa7, 0x61, 0x00, 0x91, 0xbf, 0xe2, 0x63, 0xcd, 0x79, 0x96, 0xd1, + 0x80, 0x5e, 0xe4, 0x91, 0xe9, 0x95, 0x91, 0xd6, 0xef, 0xdb, 0x2e, 0x3c, 0x79, + 0x71, 0x57, 0x41, 0xd0, 0xd4, 0x72, 0xac, 0x11, 0xdb, 0x78, 0x64, 0x4f, 0x3d, + 0x23, 0xe5, 0x8f, 0x0b, 0x01, 0xa8, 0x61, 0xe0, 0x85, 0x65, 0x53, 0x52, 0x07, + 0xcd, 0x5e, 0x71, 0x0f, 0xc3, 0x3e, 0xb2, 0xf8, 0x92, 0x8b, 0xc7, 0xd4, 0x01, + 0x7e, 0x4e, 0x56, 0xc0, 0xc2, 0xeb, 0x95, 0x85, 0xd6, 0x99, 0x74, 0x5e, 0x3b, + 0xb9, 0x61, 0x8b, 0x2c, 0x1b, 0x90, 0xf2, 0x35, 0x1b, 0xaf, 0x27, 0x6a, 0x70, + 0x17, 0xb0, 0xfc, 0xfa, 0xcb, 0x52, 0xea, 0x27, 0x31, 0x95, 0xa8, 0xde, 0xe1, + 0x67, 0x79, 0x13, 0xc7, 0x86, 0xcc, 0x3a, 0xcb, 0x06, 0xa9, 0xec, 0x7a, 0x37, + 0xb0, 0x58, 0x98, 0x0c, 0xeb, 0x3c, 0x82, 0xaa, 0xb0, 0x3e, 0xaf, 0xc1, 0xbb, + 0x88, 0xcf, 0x7a, 0xb7, 0x98, 0xf1, 0x65, 0x1d, 0x67, 0xbf, 0x22, 0x30, 0xd5, + 0x34, 0xec, 0x55, 0x23, 0x1d, 0x21, 0x31, 0x7b, 0x1c, 0xb3, 0x0b, 0x3c, 0x38, + 0xff, 0x8d, 0x21, 0x1b, 0x76, 0x36, 0x70, 0x2a, 0x25, 0xca, 0x7c, 0xa1, 0xbf, + 0xf1, 0xf2, 0xc1, 0x58, 0xc6, 0xef, 0x22, 0x13, 0xff, 0xab, 0xb9, 0xc0, 0x9f, + 0x5c, 0x47, 0xe7, 0x3b, 0xbe, 0xbb, 0xd3, 0x7f, 0x3d, 0x3e, 0xbc, 0x24, 0xa6, + 0x65, 0xb2, 0x9f, 0x10, 0xde, 0x8b, 0x9c, 0xf1, 0x94, 0x2d, 0x90, 0xb4, 0xc3, + 0x1d, 0x89, 0xa9, 0x88, 0x3b, 0xf5, 0xa0, 0x27, 0xe9, 0x20, 0xd1, 0xb8, 0x51, + 0x19, 0xf2, 0xf2, 0xf9, 0x5f, 0xd5, 0x5e, 0xda, 0x85, 0x75, 0xa4, 0xdb, 0x62, + 0x69, 0x05, 0x68, 0x1c, 0x29, 0xe8, 0xd8, 0xe7, 0x41, 0xd4, 0x20, 0xa8, 0x34, + 0x42, 0xa9, 0xd3, 0x8a, 0xf4, 0x19, 0x9e, 0xf9, 0x5c, 0xb3, 0x0b, 0xc4, 0x4e, + 0x93, 0xfe, 0x4d, 0x0e, 0xb7, 0x42, 0x22, 0xfc, 0x10, 0xac, 0x8d, 0x40, 0x0e, + 0x10, 0xed, 0x4e, 0x56, 0xfa, 0x39, 0xda, 0x01, 0x2a, 0xc1, 0x8d, 0xee, 0x4d, + 0x99, 0x42, 0x5c, 0x8f, 0x71, 0x4c, 0x51, 0xac, 0x1b, 0xa5, 0x6e, 0x0e, 0x81, + 0x47, 0x4b, 0xad, 0x3e, 0x74, 0x18, 0xed, 0x4c, 0x82, 0xb4, 0xd7, 0x75, 0x12, + 0x0b, 0x19, 0x3e, 0xdc, 0x66, 0x76, 0x30, 0x32, 0x66, 0xe3, 0x1e, 0xcf, 0x55, + 0x1e, 0xb9, 0x13, 0xa6, 0x41, 0x15, 0xbc, 0xcb, 0xbb, 0x2e, 0xcc, 0x89, 0x81, + 0x55, 0x21, 0xe5, 0x6e, 0x07, 0xc8, 0x8b, 0xbb, 0x4a, 0x55, 0xe9, 0x94, 0x5d, + 0x03, 0xdb, 0x2d, 0xa0, 0xfc, 0xae, 0x3c, 0x08, 0xf1, 0xd7, 0x7c, 0x57, 0x26, + 0x1e, 0x98, 0x23, 0x66, 0x03, 0xa8, 0xc5, 0x2c, 0x6c, 0x27, 0x98, 0xb5, 0x45, + 0x61, 0xaf, 0xfe, 0x07, 0x61, 0xe6, 0xab, 0x24, 0x72, 0x07, 0xad, 0xfc, 0x3c, + 0x43, 0x22, 0xbe, 0x0f, 0xb2, 0x49, 0xbf, 0xd3, 0xc5, 0xe7, 0xfb, 0x38, 0x37, + 0xe9, 0xff, 0x21, 0x35, 0x07, 0x3a, 0xe1, 0x36, 0x0d, 0xcf, 0xaf, 0x5f, 0xb6, + 0x78, 0x56, 0x8f, 0xd8, 0x4d, 0x99, 0xa5, 0x1f, 0x32, 0xeb, 0x94, 0xcc, 0xf5, + 0xf2, 0x39, 0x02, 0x5b, 0x2b, 0x97, 0xbe, 0xf6, 0x25, 0xdb, 0xb6, 0x7f, 0x20, + 0xc3, 0xe0, 0xd9, 0x51, 0x73, 0x12, 0x9c, 0x06, 0x37, 0x50, 0x39, 0x52, 0x13, + 0x41, 0x49, 0x24, 0xe0, 0xa3, 0xfd, 0xd3, 0x66, 0xff, 0xd4, 0x69, 0xc9, 0xeb, + 0xea, 0x79, 0xfb, 0x76, 0xaf, 0x10, 0xea, 0x45, 0xb5, 0x66, 0xf1, 0xfc, 0x92, + 0xaf, 0x48, 0xce, 0xe2, 0x11, 0xf8, 0xe1, 0xb0, 0x58, 0xfb, 0x72, 0x1a, 0x8b, + 0x22, 0xce, 0x43, 0x0c, 0x54, 0x94, 0x0e, 0x24, 0xb3, 0x30, 0x8e, 0x57, 0x0a, + 0xb8, 0x57, 0x25, 0x0d, 0x10, 0xcd, 0xec, 0xe1, 0x05, 0x07, 0x1b, 0xc8, 0x66, + 0xea, 0x4d, 0x6d, 0x5c, 0x69, 0xf9, 0x59, 0x28, 0xf3, 0x9f, 0x7f, 0x1f, 0xcd, + 0xf1, 0x5a, 0xcd, 0xbb, 0xec, 0x67, 0xd8, 0x48, 0xf7, 0xc1, 0xb2, 0xef, 0x57, + 0x7f, 0x48, 0xa7, 0x0b, 0x4b, 0xf3, 0xd8, 0xa7, 0x88, 0x14, 0x31, 0x6b, 0x3d, + 0x7f, 0xa3, 0xe3, 0xc9, 0x8c, 0xdf, 0xa1, 0x78, 0xb9, 0x89, 0xbc, 0x78, 0xde, + 0x8d, 0x24, 0xc1, 0xbb, 0xc0, 0x9d, 0x20, 0x7e, 0x11, 0x18, 0x1e, 0x59, 0x1a, + 0x60, 0x9a, 0xbf, 0xf9, 0xa2, 0x00, 0xd3, 0x4e, 0x1a, 0xc6, 0x3a, 0x38, 0xf0, + 0x40, 0x05, 0x3a, 0x32, 0x01, 0x68, 0xb8, 0x23, 0xac, 0x76, 0x6e, 0x02, 0x6c, + 0xbe, 0x1a, 0xbf, 0x27, 0x55, 0xbe, 0x0c, 0x73, 0xc8, 0xfd, 0x98, 0x62, 0x55, + 0x56, 0x40, 0x6c, 0x14, 0x99, 0x3f, 0x6a, 0x28, 0xae, 0x4b, 0xb3, 0xa4, 0x73, + 0xa1, 0x8d, 0xd3, 0x74, 0x3d, 0x88, 0x7e, 0xac, 0x54, 0x8e, 0xb7, 0xca, 0x4d, + 0x46, 0x15, 0x7c, 0x62, 0xb7, 0x29, 0xf3, 0x66, 0xa9, 0x56, 0x02, 0x28, 0x7c, + 0x8c, 0x56, 0x33, 0x5b, 0x78, 0xbc, 0x68, 0x9f, 0xc5, 0x38, 0x9c, 0x39, 0x79, + 0xb8, 0xe7, 0x5d, 0xaf, 0x31, 0xbd, 0x60, 0xa9, 0xcc, 0x2a, 0x92, 0x0d, 0xbc, + 0xc6, 0x71, 0xdd, 0xe2, 0x7e, 0xb4, 0x60, 0x0f, 0x12, 0xdc, 0x2a, 0xb3, 0x94, + 0x4a, 0xa1, 0x9c, 0x71, 0xa9, 0x87, 0xd8, 0x71, 0x3d, 0x99, 0xa4, 0xba, 0x9b, + 0x9a, 0x19, 0xa9, 0x21, 0x60, 0x6c, 0x56, 0x20, 0xc1, 0x67, 0xd4, 0xc7, 0xf4, + 0xa2, 0x8a, 0x46, 0x4a, 0x9d, 0x16, 0xc4, 0xb0, 0xd7, 0x4e, 0x0e, 0x75, 0xdf, + 0x6d, 0xba, 0x0e, 0x1d, 0xfe, 0x60, 0x1c, 0x04, 0xc8, 0xeb, 0x37, 0x01, 0x0e, + 0x13, 0x92, 0x1d, 0x5b, 0x6c, 0x93, 0xb9, 0xf0, 0xc3, 0xdd, 0xd3, 0x2f, 0x7b, + 0xec, 0xb2, 0xd7, 0x7d, 0x79, 0xa1, 0x61, 0x8a, 0x79, 0xf7, 0x3c, 0x45, 0x9b, + 0x0d, 0xf5, 0x29, 0x7f, 0x8e, 0xab, 0xd6, 0xed, 0x06, 0xfd, 0x23, 0x40, 0xe8, + 0x60, 0x0a, 0x95, 0xd7, 0x2c, 0xef, 0xd1, 0x2e, 0x62, 0x2c, 0x57, 0xb4, 0x57, + 0xa4, 0xe8, 0x39, 0x75, 0x93, 0x74, 0x6a, 0x6b, 0xcf, 0x04, 0xc4, 0x9c, 0x6d, + 0xd4, 0xa3, 0x36, 0x68, 0xda, 0x53, 0x8d, 0x90, 0x93, 0xa4, 0x50, 0xa4, 0xd8, + 0x24, 0x51, 0xb6, 0x12, 0xff, 0x54, 0x70, 0x73, 0x8e, 0x62, 0xbf, 0xdf, 0xc7, + 0x9b, 0x3e, 0x31, 0xbb, 0x47, 0xfc, 0xa1, 0xe9, 0x87, 0x22, 0xa5, 0x98, 0x3a, + 0xff, 0xe5, 0xf6, 0x32, 0x84, 0x0b, 0x92, 0x3a, 0xb5, 0x6b, 0x1d, 0xa1, 0x53, + 0xd3, 0x5d, 0x82, 0x23, 0x24, 0xe7, 0xd5, 0x6d, 0x61, 0x3c, 0x73, 0xeb, 0xc6, + 0x34, 0x1e, 0xa0, 0x3b, 0xee, 0x3a, 0xb9, 0x73, 0xe8, 0x4d, 0x8f, 0xfc, 0x4a, + 0x7c, 0x58, 0x13, 0x83, 0xe2, 0x14, 0x2d, 0x29, 0x2a, 0x58, 0x0b, 0x6d, 0x30, + 0x83, 0x43, 0xdc, 0xf1, 0xef, 0x49, 0x29, 0xa9, 0xe3, 0xe6, 0x15, 0x32, 0xfc, + 0xff, 0xb7, 0x4d, 0x30, 0x19, 0xf4, 0xe2, 0xd6, 0xd3, 0x11, 0x78, 0x57, 0x5a, + 0xca, 0x94, 0x12, 0x99, 0x22, 0x50, 0x44, 0xe1, 0xd3, 0x7b, 0xab, 0x9f, 0x10, + 0xe2, 0x9f, 0xd9, 0x6f, 0x9c, 0xf6, 0x84, 0xaf, 0x98, 0xed, 0x64, 0x8b, 0x83, + 0xd6, 0x1e, 0x52, 0x5b, 0xe3, 0x2c, 0xdb, 0x45, 0x3d, 0x2d, 0x38, 0x93, 0x5f, + 0xee, 0xb3, 0x22, 0xce, 0xb9, 0xd2, 0xa2, 0xe9, 0x5e, 0xb7, 0xfc, 0x61, 0x2d, + 0x89, 0xf4, 0xcf, 0xe8, 0x93, 0x22, 0x8e, 0x88, 0x28, 0xb1, 0x89, 0x00, 0x90, + 0x45, 0x62, 0x90, 0x75, 0xc0, 0xc2, 0x03, 0x9d, 0x5a, 0x73, 0x32, 0xfd, 0xbc, + 0xd7, 0xc7, 0xb0, 0x91, 0x01, 0x5c, 0x45, 0x69, 0xa3, 0x00, 0x53, 0x23, 0x56, + 0xbb, 0xad, 0x08, 0xff, 0xa3, 0xbb, 0x16, 0x7a, 0x3e, 0xbe, 0xb4, 0x62, 0x66, + 0xb7, 0x06, 0x06, 0x49, 0x4a, 0xda, 0xe9, 0x14, 0x9e, 0x1a, 0x64, 0xc0, 0xa0, + 0xaa, 0x5d, 0xaa, 0x53, 0x62, 0xd3, 0xc7, 0xa8, 0x96, 0xfd, 0x52, 0x78, 0x08, + 0xd0, 0xa3, 0xc1, 0xcf, 0x70, 0x61, 0xba, 0x67, 0x89, 0x39, 0x80, 0x78, 0x85, + 0x0b, 0xe4, 0xb9, 0x94, 0x0e, 0x01, 0xae, 0xbb, 0x93, 0x6d, 0xd8, 0x1a, 0x31, + 0x82, 0x04, 0x28, 0x1d, 0x43, 0x97, 0x6f, 0x4e, 0x0f, 0xa2, 0x07, 0xe4, 0xbe, + 0x1f, 0xb8, 0x2c, 0x91, 0xbb, 0x26, 0x42, 0xf7, 0x36, 0x85, 0x6d, 0xcd, 0x5a, + 0xeb, 0x75, 0xc5, 0x0a, 0xf2, 0x00, 0xe1, 0x4b, 0xe5, 0xb7, 0x8c, 0xe6, 0x9a, + 0x88, 0x51, 0x54, 0xef, 0xe3, 0x0e, 0xdd, 0x09, 0xae, 0x8c, 0x5e, 0xb5, 0x3f, + 0x4b, 0x8b, 0x7c, 0x75, 0x35, 0x37, 0x3c, 0x0f, 0xe6, 0xcf, 0xe4, 0x48, 0xa9, + 0xb9, 0xf4, 0xd9, 0xe3, 0x10, 0x93, 0x03, 0xd6, 0xce, 0xe9, 0x10, 0x6a, 0xa2, + 0x2b, 0xd5, 0x9a, 0xe0, 0xe0, 0x27, 0xd3, 0x25, 0x6a, 0x75, 0xb9, 0xc5, 0xd6, + 0x07, 0x09, 0x09, 0x97, 0x53, 0xce, 0x57, 0x2c, 0x9e, 0x29, 0xdc, 0x92, 0x56, + 0x2d, 0x1c, 0x3f, 0x4a, 0x0b, 0x4d, 0x36, 0xa6, 0xfe, 0xc2, 0x1b, 0xa4, 0x94, + 0x17, 0x3e, 0x44, 0xd7, 0x9b, 0xc2, 0x34, 0x18, 0x95, 0xbd, 0x0c, 0x70, 0x96, + 0xf0, 0x97, 0x4f, 0x12, 0x67, 0xfe, 0xf6, 0x72, 0x1d, 0x58, 0xb8, 0xc4, 0xe3, + 0x34, 0xf1, 0x4d, 0x86, 0xc0, 0xee, 0x3b, 0x1a, 0xb5, 0x88, 0x0c, 0xa4, 0x29, + 0x8d, 0x7f, 0x84, 0x76, 0x3b, 0xdc, 0x71, 0x09, 0xbc, 0x82, 0x0f, 0x45, 0xc5, + 0x04, 0x53, 0xe3, 0x3d, 0x96, 0x8e, 0xf9, 0xd8, 0x6c, 0xd6, 0xeb, 0xe7, 0x15, + 0xe8, 0x9d, 0x5d, 0xe3, 0x24, 0x09, 0x10, 0xc5, 0x9c, 0x36, 0xec, 0x8f, 0xe9, + 0x9b, 0x32, 0x49, 0x16, 0x30, 0xab, 0x35, 0xb1, 0x24, 0x53, 0x1d, 0x9c, 0x29, + 0xe0, 0x46, 0xc4, 0x78, 0xe6, 0x2a, 0xd1, 0x8b, 0x25, 0x39, 0xa5, 0x09, 0x6e, + 0xe2, 0x9a, 0x4d, 0x4b, 0x4b, 0x53, 0xa1, 0xcf, 0xfa, 0x93, 0x23, 0xbc, 0x73, + 0x21, 0x81, 0x7d, 0x96, 0xfd, 0x02, 0x05, 0xea, 0x9c, 0xbc, 0x4e, 0x15, 0x88, + 0xb7, 0x61, 0x3d, 0x4c, 0x39, 0x3c, 0xac, 0x21, 0x05, 0xb2, 0x8f, 0xd0, 0x46, + 0x7a, 0x0b, 0xf0, 0x23, 0xf0, 0x0d, 0x1a, 0x17, 0xf6, 0x53, 0xcd, 0xb6, 0xb5, + 0xa8, 0x3e, 0x4c, 0xf1, 0x5c, 0x34, 0x7b, 0x34, 0xb9, 0x7f, 0xbf, 0xe6, 0xea, + 0xee, 0x13, 0xbb, 0x90, 0x15, 0x3a, 0xfd, 0xc9, 0x11, 0x26, 0x37, 0xfa, 0xd1, + 0xcf, 0xe1, 0x7e, 0xdd, 0xcb, 0x0c, 0x81, 0x9e, 0x60, 0xd3, 0x50, 0x39, 0x34, + 0x9b, 0x69, 0xf7, 0xca, 0x9b, 0xa6, 0x4d, 0xf9, 0xf5, 0xe4, 0x71, 0x11, 0x5c, + 0xd6, 0x79, 0x26, 0xbd, 0xf1, 0x6e, 0x30, 0x12, 0x39, 0x8d, 0xae, 0x59, 0x5b, + 0xfd, 0x25, 0xf3, 0xae, 0xe5, 0x8a, 0xcf, 0xfe, 0x2f, 0x3e, 0xd7, 0x48, 0xfd, + 0xf9, 0x3a, 0x6e, 0xd2, 0x1e, 0x87, 0x2d, 0x94, 0x97, 0xa9, 0xf3, 0xb7, 0xb1, + 0x6b, 0x7e, 0xa9, 0xea, 0x19, 0xf2, 0x47, 0x9e, 0x4f, 0x8b, 0x6d, 0x42, 0x3f, + 0xa1, 0x5f, 0xbc, 0xdf, 0xa3, 0xc9, 0x9b, 0x9a, 0x39, 0x70, 0xee, 0x74, 0xa8, + 0xd8, 0x5e, 0xc2, 0x15, 0x96, 0x52, 0xda, 0xa7, 0x67, 0x03, 0x12, 0x63, 0xbb, + 0x4b, 0x49, 0x28, 0x5d, 0x70, 0x5e, 0x24, 0xe8, 0x19, 0x26, 0x86, 0xeb, 0xc8, + 0xff, 0x85, 0x98, 0xd2, 0x4b, 0x51, 0x23, 0x2a, 0x99, 0x38, 0x56, 0x5d, 0x0f, + 0x68, 0xbe, 0x7f, 0x3a, 0x53, 0x36, 0x4a, 0xcc, 0x69, 0x21, 0xa3, 0x5b, 0xc5, + 0x99, 0x10, 0xbb, 0x71, 0xfb, 0x58, 0xb8, 0x67, 0x37, 0x3c, 0xe9, 0x5f, 0x19, + 0x84, 0x09, 0xaa, 0xef, 0x97, 0xf4, 0x01, 0xe4, 0x33, 0x00, 0x4b, 0x99, 0x19, + 0x04, 0x9f, 0x93, 0x7f, 0xd7, 0x76, 0xc4, 0xb6, 0x31, 0xa5, 0x91, 0x2a, 0x08, + 0xd4, 0x9f, 0xdf, 0x65, 0x28, 0xf8, 0x1a, 0x6f, 0x32, 0x00, 0x09, 0x37, 0x67, + 0xbb, 0x77, 0x89, 0xd9, 0x5a, 0x75, 0x03, 0x0a, 0xc1, 0xd2, 0x4c, 0x2c, 0x75, + 0xbd, 0x60, 0x38, 0x25, 0x52, 0x86, 0x3f, 0x09, 0x8d, 0x36, 0xbd, 0x48, 0x33, + 0x28, 0x3d, 0x3a, 0x2d, 0x21, 0x5d, 0x10, 0xc7, 0xff, 0xe9, 0xc8, 0x40, 0x37, + 0x23, 0x14, 0x45, 0x58, 0x33, 0x29, 0x26, 0x16, 0x74, 0x19, 0x3b, 0xdd, 0x1c, + 0x64, 0x81, 0xbe, 0xf9, 0xf2, 0x26, 0xe1, 0xe6, 0x0b, 0xb1, 0xc7, 0x76, 0xa4, + 0xbe, 0x7d, 0xc6, 0x9b, 0x44, 0x30, 0xa7, 0x5a, 0x0c, 0xbd, 0x55, 0x86, 0x7a, + 0x6f, 0x46, 0xff, 0x93, 0x03, 0xf9, 0xa2, 0x9b, 0x6f, 0x3f, 0x7c, 0x7a, 0x9c, + 0x9f, 0xbc, 0xf7, 0x47, 0xb2, 0x3f, 0x86, 0x45, 0xf4, 0xda, 0x3d, 0x9f, 0x72, + 0xd0, 0xd8, 0x76, 0xa7, 0x5e, 0x54, 0x8a, 0x49, 0xdb, 0x37, 0x5b, 0x40, 0xeb, + 0xe1, 0xbb, 0xe0, 0x81, 0x7a, 0x99, 0x49, 0xde, 0xc1, 0x15, 0x7d, 0x62, 0xa7, + 0x1d, 0xbf, 0xbd, 0x9b, 0xb1, 0xd6, 0x55, 0x17, 0x53, 0xdf, 0xf5, 0xbb, 0x7f, + 0xc9, 0x36, 0x48, 0xd4, 0xeb, 0x6c, 0xad, 0x41, 0x67, 0x33, 0xad, 0xfd, 0xcc, + 0x87, 0x08, 0xdd, 0xe8, 0xbe, 0x87, 0x34, 0xd0, 0x5d, 0xec, 0x9e, 0x45, 0xdf, + 0x3f, 0xa4, 0x5a, 0xda, 0xc4, 0x1a, 0x6d, 0x23, 0xa2, 0x24, 0xa0, 0x4f, 0xdc, + 0x0d, 0x96, 0x73, 0x87, 0x98, 0x0f, 0x95, 0xe6, 0x27, 0xe6, 0xb3, 0xdc, 0xe1, + 0x9c, 0xaf, 0x01, 0x09, 0x84, 0x8c, 0xa9, 0xda, 0xea, 0x2e, 0x24, 0x6e, 0x62, + 0xc2, 0x85, 0x07, 0xd2, 0x56, 0xeb, 0xab, 0xe1, 0x18, 0xf1, 0xf6, 0xef, 0x97, + 0x6e, 0x4a, 0x31, 0xa0, 0xe4, 0x14, 0x3c, 0x43, 0x60, 0xd8, 0xb1, 0x79, 0xb3, + 0x0e, 0x4b, 0xfa, 0x7e, 0x16, 0x1b, 0x1e, 0x6c, 0x70, 0x7d, 0x8e, 0xae, 0x76, + 0x28, 0x71, 0x59, 0x21, 0x94, 0x1e, 0x78, 0x54, 0xe1, 0x0d, 0x11, 0x99, 0x12, + 0x58, 0xc4, 0x3f, 0xe6, 0xc4, 0x45, 0x29, 0xf6, 0x61, 0x4b, 0x58, 0x41, 0x61, + 0x5d, 0x3e, 0x4e, 0x77, 0xfb, 0x09, 0xa6, 0xf0, 0x20, 0xe0, 0xb8, 0x32, 0x28, + 0xac, 0x17, 0x55, 0xad, 0x47, 0x71, 0x16, 0xde, 0xca, 0xac, 0x51, 0x7b, 0xfb, + 0xcf, 0x67, 0x37, 0xf5, 0xbb, 0x99, 0xe0, 0x07, 0xeb, 0x64, 0x00, 0x76, 0x6b, + 0x6c, 0xfd, 0xd7, 0x37, 0xe2, 0x08, 0x57, 0xdf, 0x3c, 0x85, 0xca, 0x16, 0xab, + 0x21, 0x17, 0x7b, 0x53, 0x1e, 0x55, 0x32, 0xc4, 0x45, 0xde, 0xd0, 0x0c, 0x1e, + 0x96, 0x63, 0x5e, 0x9f, 0x50, 0x0b, 0xa8, 0x76, 0x44, 0xb8, 0xc1, 0xd5, 0x33, + 0x25, 0x37, 0xab, 0xf2, 0x9f, 0xcc, 0xab, 0x8a, 0xe3, 0xe3, 0x88, 0x27, 0x18, + 0x82, 0x6b, 0xdb, 0x8d, 0xbd, 0xb8, 0x51, 0xa4, 0x77, 0x05, 0xeb, 0x0d, 0xec, + 0x2d, 0x5e, 0xe9, 0x39, 0xdc, 0x79, 0x87, 0x25, 0x6f, 0xee, 0xe6, 0x7f, 0x09, + 0x90, 0x28, 0xf1, 0x45, 0xe2, 0x0b, 0xf4, 0x88, 0x94, 0x98, 0x24, 0x30, 0x14, + 0x35, 0x13, 0x73, 0xfd, 0xf6, 0x33, 0x01, 0x8d, 0x21, 0x7c, 0x58, 0x8c, 0x52, + 0x98, 0x6f, 0xc5, 0x24, 0xe7, 0x97, 0x97, 0xab, 0x65, 0x58, 0x43, 0xc2, 0x61, + 0xae, 0x7f, 0xc9, 0xcc, 0x3f, 0x47, 0x05, 0x46, 0x00, 0xe4, 0xcd, 0x38, 0x5c, + 0x46, 0x7a, 0x78, 0x8a, 0x9f, 0xff, 0xc3, 0x7e, 0x9d, 0xdb, 0xb5, 0xd3, 0xe8, + 0xa4, 0xbd, 0x0c, 0x4e, 0x8f, 0x56, 0xe5, 0x69, 0x5a, 0xfa, 0x90, 0xfe, 0x50, + 0xce, 0x0a, 0x30, 0x04, 0xfe, 0xd7, 0x12, 0xb4, 0xde, 0x15, 0xad, 0x5f, 0x01, + 0x71, 0xad, 0x51, 0xed, 0xfa, 0x54, 0xdb, 0xd4, 0x8b, 0x1f, 0xcc, 0x5e, 0xf6, + 0xac, 0x73, 0xcf, 0x0a, 0x28, 0xe9, 0xd9, 0x3e, 0x0c, 0xaf, 0xad, 0x88, 0x16, + 0x76, 0x1b, 0x3b, 0xe6, 0x38, 0x39, 0x8c, 0x00, 0x14, 0x33, 0x38, 0xea, 0x27, + 0xa9, 0xff, 0xf2, 0x2e, 0xc4, 0x73, 0x16, 0x36, 0x96, 0x12, 0x25, 0xca, 0x49, + 0xe0, 0x13, 0xa6, 0xdc, 0x80, 0x2b, 0xc7, 0xfb, 0x77, 0xca, 0xd1, 0x0a, 0xca, + 0xfe, 0xfc, 0xe5, 0xfa, 0x9a, 0x37, 0x35, 0x63, 0xb3, 0x91, 0x7a, 0x3a, 0x37, + 0x39, 0xcc, 0x97, 0x80, 0xea, 0x81, 0x50, 0x73, 0xde, 0x8e, 0xb4, 0x2e, 0x3f, + 0x66, 0x93, 0xe8, 0x52, 0xbe, 0xfd, 0xde, 0xdd, 0x61, 0x91, 0x29, 0xd0, 0xaa, + 0x13, 0xc4, 0xbd, 0x83, 0x86, 0x22, 0xb5, 0xe3, 0x28, 0x56, 0x35, 0x8e, 0x6d, + 0x82, 0x78, 0x78, 0x95, 0x7e, 0x5d, 0xc8, 0x2c, 0xd4, 0x37, 0x0b, 0x66, 0x10, + 0x84, 0x9e, 0x95, 0x6d, 0x0a, 0x7c, 0xdf, 0xf5, 0x61, 0x8f, 0x5c, 0x2c, 0xea, + 0x61, 0x23, 0x0b, 0x47, 0x00, 0x1c, 0x30, 0xe5, 0xa8, 0xf9, 0x37, 0xca, 0x7f, + 0x9f, 0x9e, 0x66, 0x0f, 0xfa, 0xa7, 0x71, 0x80, 0xcb, 0xa2, 0x6f, 0x90, 0xda, + 0x00, 0x7c, 0xda, 0x40, 0x57, 0xa6, 0xce, 0xa2, 0xe2, 0x6b, 0xfd, 0xe5, 0x0c, + 0x7f, 0x90, 0x79, 0x88, 0x00, 0x53, 0xd0, 0x5d, 0xaa, 0xaa, 0xb3, 0xd7, 0xe4, + 0xdc, 0x9d, 0x81, 0xd0, 0x99, 0x0d, 0x2b, 0xc3, 0x69, 0xa6, 0x6b, 0x55, 0xac, + 0x8b, 0x63, 0x97, 0xbd, 0x47, 0xdb, 0x42, 0x89, 0xc5, 0x45, 0x22, 0x85, 0x55, + 0x1a, 0xaa, 0x7f, 0xa6, 0x7b, 0x01, 0x36, 0xcd, 0x11, 0x9f, 0x87, 0xd8, 0x21, + 0x9e, 0x00, 0x02, 0x97, 0xf0, 0x2c, 0x0c, 0xe6, 0xe3, 0x7b, 0x62, 0x0f, 0x5e, + 0x47, 0xfc, 0xa0, 0x3a, 0xcd, 0xd6, 0x54, 0x4a, 0x47, 0xf4, 0xde, 0xef, 0x19, + 0x4f, 0x95, 0x9a, 0xdc, 0x36, 0x8b, 0x3b, 0x5d, 0x27, 0xd3, 0x83, 0xfe, 0x2f, + 0x2b, 0x52, 0x5d, 0xae, 0x04, 0x50, 0x55, 0x06, 0x35, 0xaa, 0x21, 0x58, 0x18, + 0xf7, 0xf5, 0x03, 0x78, 0x90, 0xf0, 0x53, 0x23, 0x3f, 0x9a, 0xa5, 0x0a, 0xe2, + 0x9c, 0x05, 0x56, 0xc3, 0x6d, 0x67, 0xb2, 0x64, 0x7e, 0x54, 0xeb, 0xe7, 0x58, + 0x8e, 0x1f, 0x02, 0xb3, 0xc7, 0x17, 0xdf, 0x02, 0x98, 0x43, 0x0e, 0xc9, 0xd2, + 0xbb, 0x11, 0x4b, 0x35, 0x42, 0xb7, 0x5d, 0x01, 0x0d, 0x93, 0x4e, 0x58, 0x96, + 0xe1, 0xd2, 0xd1, 0x0a, 0x09, 0x20, 0x11, 0x9d, 0xf7, 0x29, 0x2c, 0x8c, 0x28, + 0x47, 0x65, 0x0f, 0xbf, 0x42, 0x80, 0x57, 0x12, 0x8a, 0x02, 0x04, 0x0e, 0xb3, + 0xe3, 0x2d, 0xb5, 0x0c, 0xa7, 0xd8, 0xda, 0x7f, 0xf4, 0xc4, 0xa7, 0xa0, 0xe9, + 0xcf, 0x4b, 0x65, 0x2b, 0x65, 0x3d, 0x42, 0x8f, 0x83, 0xf4, 0x85, 0x33, 0x57, + 0x84, 0x1b, 0x28, 0x13, 0x80, 0x55, 0xb9, 0x13, 0x81, 0x17, 0x79, 0x0a, 0x91, + 0xe2, 0x8f, 0xaa, 0x41, 0x2f, 0xd7, 0xd0, 0x73, 0x32, 0x56, 0x73, 0x44, 0x85, + 0xd1, 0xd6, 0xd1, 0xa9, 0x8c, 0xc2, 0xd7, 0xc8, 0x2b, 0x37, 0x9e, 0x60, 0x72, + 0x5d, 0x31, 0x8c, 0x14, 0x77, 0xce, 0x49, 0x6c, 0x95, 0x86, 0x31, 0x08, 0xa1, + 0xc7, 0xe4, 0xf0, 0x20, 0x0b, 0x7a, 0x3c, 0x08, 0x8d, 0xe7, 0x7e, 0xb4, 0xbc, + 0x95, 0xa1, 0xc6, 0xc8, 0x39, 0xd7, 0x5f, 0xab, 0x59, 0x40, 0xd3, 0x07, 0x94, + 0x24, 0xd5, 0x23, 0xd6, 0xd9, 0xa4, 0x6b, 0xe5, 0x4e, 0x18, 0xf5, 0x29, 0xdc, + 0x9e, 0x56, 0x77, 0x6c, 0x5e, 0xc4, 0x51, 0xce, 0x28, 0x07, 0x9d, 0x37, 0x82, + 0x6a, 0xec, 0x40, 0x97, 0xca, 0x7a, 0xee, 0xc8, 0x08, 0x3f, 0xf5, 0xc4, 0x29, + 0x56, 0x9f, 0x91, 0x53, 0xf6, 0x96, 0xbe, 0x62, 0xbd, 0x38, 0xa3, 0xe7, 0x27, + 0xa6, 0x8a, 0xcc, 0xdf, 0xab, 0x02, 0x9b, 0x0b, 0x21, 0xe6, 0xd0, 0xcd, 0x46, + 0x0a, 0x57, 0xd5, 0xf9, 0x03, 0xda, 0x18, 0xa6, 0x07, 0x86, 0xb3, 0x91, 0xdd, + 0x1f, 0x5b, 0xe9, 0x49, 0x82, 0x7e, 0x0c, 0xe7, 0xdf, 0xd1, 0xe0, 0x84, 0x27, + 0xf0, 0xd3, 0xc2, 0x86, 0x53, 0x78, 0xc7, 0x3d, 0x46, 0xa7, 0x3c, 0x55, 0x4a, + 0x12, 0x99, 0x86, 0x02, 0x2a, 0x4f, 0x38, 0x36, 0x0c, 0x39, 0xeb, 0x9c, 0xdd, + 0x05, 0x0f, 0x56, 0xec, 0x05, 0x95, 0x68, 0x65, 0x3c, 0x78, 0x29, 0xe0, 0xa4, + 0x4f, 0x2c, 0x70, 0x10, 0xad, 0xb6, 0x73, 0xe8, 0xde, 0x77, 0x04, 0xe5, 0x4c, + 0x03, 0xa7, 0x7a, 0xb7, 0x8e, 0x85, 0xb6, 0x3f, 0x2b, 0x91, 0x18, 0x5c, 0xa5, + 0xda, 0x67, 0xd3, 0x28, 0x30, 0x65, 0x8b, 0x54, 0xbb, 0x33, 0x58, 0x75, 0x13, + 0xc4, 0x2e, 0x03, 0xb5, 0x2c, 0xeb, 0x9a, 0x19, 0x57, 0xa9, 0xe9, 0x05, 0x84, + 0x72, 0x37, 0xce, 0x44, 0x56, 0xe5, 0x33, 0x50, 0x68, 0x26, 0x49, 0x0e, 0xc5, + 0x55, 0x2b, 0x39, 0x12, 0xdb, 0x1c, 0x88, 0x0e, 0xd4, 0x71, 0xb1, 0x09, 0x29, + 0x98, 0xdc, 0xc1, 0x6f, 0xa9, 0x8d, 0x5a, 0xe9, 0xe7, 0x6f, 0xd2, 0x9d, 0x17, + 0x9f, 0xd7, 0x36, 0x59, 0x78, 0xc0, 0x80, 0x44, 0x51, 0x18, 0x80, 0x1a, 0xc1, + 0x0d, 0xc0, 0xf5, 0x78, 0x8f, 0x47, 0x86, 0x69, 0x34, 0xb9, 0x8a, 0xad, 0xb9, + 0xc6, 0x8d, 0xd8, 0x84, 0x83, 0xc1, 0x5d, 0x47, 0xaf, 0x8f, 0xf4, 0x2e, 0x6b, + 0xfb, 0xb8, 0xe0, 0xe5, 0x3a, 0x04, 0x7e, 0x58, 0xe5, 0xba, 0x90, 0xd1, 0xdb, + 0x1e, 0xa1, 0x26, 0x01, 0x7c, 0x65, 0x6d, 0x01, 0x1c, 0x68, 0x7b, 0xb0, 0x4f, + 0x47, 0xa5, 0x60, 0xef, 0x7c, 0xed, 0x23, 0x1b, 0x24, 0x38, 0x7f, 0xf4, 0x01, + 0x90, 0x43, 0xcf, 0xfd, 0x67, 0xfb, 0x9d, 0x89, 0x20, 0x06, 0xc3, 0x91, 0x7f, + 0xd7, 0xa9, 0x6f, 0xe0, 0x3d, 0x7b, 0xea, 0xa2, 0x17, 0x12, 0x8d, 0x71, 0xf0, + 0xa2, 0x8a, 0x83, 0x78, 0x7a, 0x86, 0xcf, 0xc9, 0x33, 0x69, 0xd0, 0xdd, 0x54, + 0x65, 0x32, 0x7f, 0xc4, 0x29, 0x4d, 0xae, 0x81, 0xc4, 0x35, 0x1c, 0x42, 0xa6, + 0xf0, 0xa8, 0x0e, 0xef, 0xa6, 0x1d, 0xb6, 0xa4, 0x0b, 0xb6, 0x81, 0xf5, 0x58, + 0xf8, 0x1b, 0x10, 0x1e, 0xb6, 0x57, 0xf6, 0x57, 0x27, 0xd6, 0x17, 0x69, 0x1b, + 0x8b, 0xee, 0x3a, 0xa7, 0xe5, 0x75, 0xb4, 0x11, 0xa0, 0x12, 0x8a, 0x3f, 0x24, + 0x75, 0x3e, 0x52, 0xee, 0x34, 0x90, 0x04, 0xcf, 0x6d, 0x25, 0xfa, 0xd6, 0xc4, + 0x68, 0x1b, 0x02, 0xa2, 0xe1, 0x96, 0x14, 0xe8, 0x0c, 0x95, 0x83, 0x81, 0x36, + 0x2a, 0x91, 0xd3, 0xcd, 0x3b, 0x4e, 0x76, 0x58, 0x32, 0x94, 0x31, 0x0c, 0x82, + 0x41, 0x11, 0x29, 0xac, 0x97, 0xf2, 0xad, 0x5a, 0x5b, 0x9f, 0xa8, 0x64, 0xa9, + 0xc5, 0xd0, 0x2d, 0x8c, 0x92, 0xd6, 0x42, 0x44, 0xfa, 0x6c, 0x40, 0x9c, 0x21, + 0x69, 0x48, 0x62, 0xc4, 0x42, 0x7d, 0xc5, 0x1a, 0xec, 0x57, 0x7f, 0x6e, 0xa3, + 0x38, 0x05, 0x03, 0x13, 0x99, 0x91, 0xe6, 0xe8, 0x89, 0x09, 0x87, 0x64, 0x9f, + 0xa7, 0xc4, 0x3a, 0xc8, 0x03, 0xf6, 0x89, 0xb6, 0x9d, 0x70, 0xab, 0xd7, 0xef, + 0xa7, 0x1c, 0xf9, 0xa0, 0xf2, 0xa4, 0x1d, 0xf9, 0x41, 0x89, 0x76, 0xa4, 0xff, + 0xa4, 0x4f, 0x43, 0x75, 0x92, 0xf1, 0x9c, 0x09, 0xcb, 0x49, 0x31, 0xb3, 0xd3, + 0xcd, 0x01, 0x59, 0x31, 0xcf, 0xfa, 0xe1, 0x71, 0xe0, 0x8a, 0xc5, 0x92, 0x88, + 0x61, 0xfc, 0xc3, 0x2e, 0x08, 0x81, 0x15, 0x59, 0x76, 0x49, 0x66, 0xbe, 0xbc, + 0x14, 0x14, 0x36, 0xb9, 0x17, 0xc5, 0x27, 0x1b, 0x2c, 0x68, 0x0c, 0xdc, 0x50, + 0x2c, 0xba, 0xd5, 0x27, 0xac, 0x08, 0x7b, 0x34, 0x65, 0x6f, 0x75, 0x5d, 0xfb, + 0xf0, 0xae, 0x5a, 0xed, 0xc8, 0x09, 0x85, 0xf6, 0x3d, 0x0c, 0xa4, 0x4a, 0x76, + 0x2f, 0x9b, 0x31, 0x1f, 0x15, 0x6d, 0xe6, 0x27, 0x74, 0x19, 0x19, 0x99, 0x8e, + 0x67, 0x44, 0x66, 0xc7, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x04, 0xc4, 0x9e, + 0xb1, 0x87, 0xfb, 0xf7, 0x5e, 0x5f, 0x7c, 0xee, 0x26, 0x1e, 0x30, 0x75, 0xc2, + 0xb2, 0xc2, 0x81, 0x2f, 0xe8, 0x32, 0x32, 0xc4, 0x1a, 0x5f, 0x10, 0xf4, 0x0b, + 0x91, 0x1e, 0xbc, 0xeb, 0xb7, 0x8c, 0x91, 0xc2, 0x0b, 0x82, 0xc0, 0x05, 0x0f, + 0xe2, 0xee, 0x10, 0x4b, 0x39, 0x20, 0xed, 0x0a, 0x05, 0xd1, 0x7b, 0x06, 0x0d, + 0x99, 0xd5, 0x87, 0x01, 0x98, 0xe6, 0x3c, 0xcf, 0x51, 0xb1, 0x5d, 0xf8, 0x0e, + 0x87, 0xac, 0xbd, 0x30, 0x12, 0x6c, 0xda, 0x2a, 0xff, 0xb8, 0xf1, 0xce, 0xcb, + 0x1b, 0xaa, 0x6a, 0x91, 0x9e, 0x0a, 0x97, 0x87, 0x91, 0x39, 0x69, 0x04, 0x44, + 0x9a, 0xde, 0x4b, 0x0b, 0x02, 0x92, 0x0f, 0xb8, 0xc0, 0xbf, 0x7f, 0xc0, 0x82, + 0xeb, 0x74, 0x98, 0x73, 0xc1, 0x0d, 0x17, 0xdb, 0xd9, 0x1f, 0xfe, 0xa9, 0x36, + 0x10, 0xee, 0xea, 0x62, 0x57, 0x90, 0xad, 0xa2, 0x8e, 0x3a, 0x2c, 0xf2, 0x2c, + 0x0d, 0x4e, 0xa2, 0xb9, 0x26, 0x41, 0xf2, 0x16, 0xd3, 0x92, 0x2c, 0x1f, 0xc3, + 0x2d, 0xbc, 0x1e, 0x0e, 0x99, 0x00, 0x38, 0x6c, 0xf8, 0x98, 0xcb, 0x8e, 0xd5, + 0x6c, 0x06, 0x4e, 0x5b, 0x12, 0xb0, 0x26, 0xbf, 0x03, 0x5d, 0xfb, 0xc4, 0xeb, + 0x92, 0xce, 0x33, 0xf8, 0x2b, 0xbe, 0x48, 0xca, 0x94, 0x5f, 0x12, 0x44, 0x83, + 0x10, 0xd7, 0xb9, 0xdb, 0x85, 0xf1, 0xb0, 0x46, 0xdc, 0x9c, 0x56, 0x51, 0x2f, + 0x61, 0xe0, 0xa3, 0x96, 0x6f, 0xa4, 0xab, 0x71, 0xd1, 0x5f, 0x4e, 0x23, 0xe4, + 0xe3, 0x1c, 0xb9, 0x62, 0x10, 0x60, 0x14, 0xc4, 0xc2, 0x9e, 0xc3, 0xb9, 0x10, + 0xe0, 0x72, 0x2d, 0xac, 0x38, 0xaa, 0x4d, 0xc8, 0x1e, 0x17, 0x6d, 0x72, 0xfe, + 0xaf, 0x2f, 0x93, 0xf9, 0xec, 0xd5, 0x04, 0xcb, 0xaf, 0x95, 0x59, 0x83, 0x30, + 0x09, 0xd9, 0x2c, 0x9d, 0x2f, 0x81, 0x68, 0x7b, 0xf5, 0x89, 0xa4, 0x93, 0x66, + 0xcd, 0x0a, 0xba, 0xe7, 0xa1, 0x74, 0xa4, 0x8f, 0xf7, 0x6c, 0xd7, 0x2f, 0x02, + 0xb1, 0x8a, 0xf8, 0x18, 0x75, 0x26, 0xd4, 0x70, 0x94, 0x9c, 0xb8, 0xd9, 0x3e, + 0xfe, 0x6c, 0x5b, 0xc7, 0x91, 0xca, 0x93, 0xb1, 0x10, 0xc1, 0x82, 0x5b, 0x6a, + 0xfb, 0x04, 0x5d, 0x9d, 0x8c, 0xa3, 0x51, 0xf7, 0xad, 0xa3, 0x28, 0xfd, 0xd5, + 0x2a, 0xec, 0x29, 0x77, 0xd2, 0x94, 0x0e, 0x2c, 0xdc, 0xb2, 0x66, 0x4d, 0x78, + 0xb7, 0x6a, 0xc0, 0xe0, 0x6d, 0x78, 0x8e, 0x57, 0xf8, 0x24, 0x4f, 0x44, 0x2c, + 0x88, 0x6a, 0x8f, 0x31, 0x13, 0x7c, 0xd7, 0xf1, 0x9e, 0x82, 0x21, 0xa3, 0x85, + 0xcb, 0xfb, 0x3f, 0x7f, 0x2a, 0x1e, 0x79, 0x50, 0x4b, 0xcf, 0x1a, 0xe0, 0x83, + 0xb1, 0x29, 0x02, 0xa5, 0x01, 0x2c, 0xd5, 0xea, 0x2f, 0xc8, 0x56, 0x43, 0xdd, + 0xec, 0xee, 0xf4, 0xab, 0x95, 0x93, 0x43, 0x21, 0x9b, 0x0c, 0x63, 0xdd, 0x0a, + 0x8b, 0x0e, 0x23, 0x3e, 0xfc, 0x68, 0xfc, 0x63, 0x30, 0x73, 0xe6, 0x6c, 0x59, + 0x97, 0x5f, 0x23, 0x52, 0x4b, 0x6a, 0xa1, 0xab, 0x9a, 0xe7, 0xb1, 0x33, 0xd5, + 0xf3, 0x0c, 0xf9, 0xe1, 0xd0, 0xf9, 0xba, 0xd7, 0x1f, 0x67, 0x3f, 0x5b, 0x75, + 0x4c, 0xf4, 0x00, 0x99, 0x77, 0x57, 0xa6, 0x45, 0x8a, 0xd3, 0xb9, 0xdc, 0x8e, + 0xc0, 0xc6, 0x9c, 0x66, 0x09, 0x66, 0x3b, 0x42, 0xbb, 0xb0, 0xca, 0x1a, 0x55, + 0x73, 0x37, 0x42, 0x81, 0x1f, 0x0d, 0x71, 0x30, 0xe0, 0x13, 0xfe, 0x2f, 0x88, + 0x05, 0x8e, 0xe8, 0x9b, 0x90, 0xa7, 0x5c, 0xd0, 0x69, 0xda, 0xf1, 0x00, 0x37, + 0x25, 0x4d, 0x10, 0x16, 0xd3, 0xac, 0xf7, 0xe6, 0x2f, 0x18, 0x3b, 0x2c, 0x55, + 0x1a, 0x59, 0x90, 0xe4, 0xed, 0x73, 0xdc, 0xd8, 0x94, 0xf7, 0x85, 0x70, 0xfd, + 0x19, 0x56, 0xcb, 0x22, 0x7c, 0x65, 0x00, 0x01, 0xf2, 0x7f, 0x94, 0x23, 0xf4, + 0xed, 0x12, 0x56, 0x0b, 0x2e, 0x1c, 0x8d, 0xbc, 0xb4, 0xc3, 0x02, 0x15, 0xb2, + 0x16, 0x7f, 0x02, 0xef, 0xeb, 0x70, 0x7a, 0xf1, 0xb5, 0xc7, 0x84, 0xb7, 0xf5, + 0x8b, 0x2e, 0x51, 0x73, 0x03, 0xf3, 0xaf, 0x71, 0xb1, 0xee, 0x39, 0xa9, 0xae, + 0x06, 0xb9, 0x77, 0x28, 0xe6, 0x4f, 0x67, 0x6d, 0xed, 0x50, 0xa3, 0xf5, 0x1b, + 0xc9, 0xe0, 0x17, 0x07, 0xbf, 0x57, 0x95, 0x6f, 0x01, 0xb7, 0xda, 0x7c, 0x23, + 0xe6, 0x93, 0x52, 0x06, 0x57, 0x28, 0x6f, 0xe7, 0x3e, 0xee, 0x9e, 0xb1, 0xd5, + 0x83, 0x75, 0x22, 0x03, 0xf3, 0xd9, 0x2b, 0xd4, 0x04, 0x7b, 0x83, 0xfd, 0x38, + 0xf5, 0x66, 0xdd, 0x25, 0xb9, 0x6d, 0x11, 0xb7, 0x22, 0x2b, 0x67, 0x82, 0xda, + 0xde, 0xf5, 0xee, 0x78, 0x82, 0x14, 0x7c, 0xbb, 0x4f, 0xcf, 0xe7, 0x0d, 0x2c, + 0xa7, 0xf3, 0x9a, 0x29, 0x7b, 0x21, 0xd5, 0x6d, 0x66, 0x10, 0xe9, 0xda, 0x9d, + 0x8e, 0xef, 0xdc, 0x69, 0x9e, 0x4a, 0x30, 0x06, 0x8a, 0x14, 0x57, 0xcf, 0x5e, + 0xaf, 0x69, 0x87, 0x78, 0x21, 0xd3, 0x9e, 0xa0, 0x85, 0x94, 0xc2, 0xfb, 0x9e, + 0xb9, 0xd8, 0x04, 0x64, 0x50, 0xe4, 0x13, 0x03, 0xf1, 0x95, 0xbd, 0xc9, 0x05, + 0xe4, 0xf2, 0x58, 0x3c, 0x6a, 0xe3, 0x86, 0x1b, 0x87, 0x19, 0xbb, 0xce, 0xd1, + 0xce, 0x58, 0xc4, 0x68, 0x81, 0x6d, 0x45, 0x15, 0xe6, 0x09, 0x7b, 0x3e, 0x2e, + 0x81, 0x82, 0x21, 0x0f, 0x6c, 0x1b, 0xb3, 0xaa, 0xa6, 0x2a, 0xe0, 0xf6, 0x9f, + 0x79, 0xfc, 0xc5, 0x47, 0xba, 0xab, 0x31, 0x1d, 0x99, 0x7c, 0x84, 0x95, 0xd6, + 0xab, 0xe3, 0xa5, 0x1f, 0x56, 0x53, 0xf3, 0x1c, 0x5a, 0x2e, 0xea, 0x8d, 0x31, + 0x90, 0x97, 0xf3, 0x04, 0x5e, 0x6c, 0x3c, 0x3d, 0x8c, 0x87, 0xc9, 0xbd, 0x55, + 0xb4, 0x19, 0x2e, 0xbf, 0x00, 0xff, 0x8f, 0xc7, 0xf4, 0x1e, 0x18, 0x93, 0x0a, + 0x99, 0x72, 0xa3, 0x4d, 0x9e, 0x6a, 0xa9, 0xd9, 0x1d, 0x2e, 0x28, 0x17, 0xeb, + 0x6d, 0xe9, 0xba, 0x38, 0x9e, 0x69, 0xaa, 0x51, 0x2f, 0x3f, 0xb4, 0xdf, 0xf8, + 0xca, 0x1c, 0xe7, 0xc9, 0xca, 0x39, 0x6e, 0x8a, 0x9d, 0x99, 0xd4, 0x96, 0x51, + 0xb0, 0x58, 0x2f, 0xc5, 0x86, 0xce, 0x92, 0x7e, 0xa2, 0x64, 0x5b, 0xda, 0xa3, + 0x79, 0x28, 0x6f, 0x95, 0xd3, 0x9b, 0x95, 0x81, 0xde, 0xb2, 0xc5, 0x37, 0x75, + 0xae, 0xef, 0x20, 0xe7, 0xbd, 0xbc, 0x3b, 0x19, 0xd8, 0x9b, 0xac, 0xee, 0xa1, + 0x3b, 0x74, 0xe6, 0xc7, 0xf5, 0x20, 0x89, 0x39, 0x7d, 0x11, 0x6e, 0xbf, 0xac, + 0x6a, 0x30, 0xed, 0x27, 0xd6, 0x27, 0x81, 0xa0, 0x3b, 0x66, 0xb0, 0x52, 0xf7, + 0x51, 0xfb, 0x36, 0x88, 0x2b, 0x9a, 0x14, 0x34, 0x23, 0xad, 0x02, 0xf3, 0x36, + 0x0a, 0xfa, 0x54, 0xc4, 0xcf, 0x23, 0x53, 0x0c, 0x68, 0xd6, 0x0e, 0x99, 0x56, + 0x1c, 0xce, 0x0d, 0x6a, 0x9c, 0x32, 0xef, 0xc7, 0x1f, 0xef, 0xaf, 0x23, 0x57, + 0x86, 0x3f, 0xa0, 0xb9, 0xf7, 0xbe, 0x76, 0xc2, 0xd1, 0xd3, 0x88, 0x49, 0xa0, + 0x0a, 0xb0, 0x41, 0xf1, 0x82, 0xad, 0x63, 0x35, 0xe9, 0x55, 0xcc, 0x65, 0xcd, + 0xfd, 0x3b, 0x69, 0x1a, 0x3d, 0x96, 0xc4, 0xbd, 0x56, 0xf5, 0x25, 0xce, 0xdb, + 0x7f, 0xdc, 0xb7, 0x33, 0xe7, 0x67, 0x06, 0x2f, 0xd8, 0xa4, 0xef, 0x1a, 0x4b, + 0x71, 0x5e, 0x5e, 0xdf, 0x76, 0x26, 0x14, 0x4e, 0x28, 0x5f, 0x2b, 0x3c, 0x4e, + 0x2c, 0xb4, 0x1b, 0x7d, 0xb9, 0x66, 0x35, 0x82, 0xad, 0x65, 0xa5, 0x41, 0x6e, + 0x57, 0xf7, 0x48, 0x5f, 0x39, 0xc0, 0x5e, 0x8e, 0x7a, 0xf9, 0x6b, 0x36, 0x78, + 0xc8, 0x0a, 0x8d, 0x4b, 0xa2, 0xf9, 0x5d, 0x5f, 0xeb, 0x0c, 0xcb, 0x0f, 0x71, + 0x7b, 0x9d, 0xb7, 0x24, 0xab, 0xf4, 0xcc, 0xd4, 0x10, 0x49, 0x00, 0x18, 0x6f, + 0x4a, 0x93, 0x0d, 0x4b, 0x2a, 0xcb, 0x9f, 0x9a, 0x16, 0xaf, 0x89, 0x77, 0x27, + 0x7d, 0x6f, 0x0b, 0xc9, 0x0a, 0xb8, 0x59, 0xc3, 0x33, 0x3b, 0x3d, 0xe8, 0x6f, + 0x41, 0xfa, 0x85, 0xd5, 0x70, 0xf1, 0x6c, 0x74, 0x82, 0x0a, 0x70, 0x41, 0xfe, + 0xa1, 0x5e, 0xe9, 0x50, 0xc3, 0x30, 0xac, 0xa3, 0xf1, 0xe5, 0x1c, 0x69, 0x44, + 0x74, 0x72, 0xf2, 0x6a, 0x3d, 0x67, 0x41, 0xbc, 0x67, 0xe9, 0x2e, 0x00, 0xa0, + 0x83, 0xb6, 0x95, 0x33, 0x03, 0xb3, 0x73, 0x1c, 0xf2, 0x84, 0x8d, 0x81, 0x7c, + 0xeb, 0x77, 0xf1, 0xcc, 0xa7, 0x1e, 0xc9, 0x13, 0x91, 0x20, 0x2b, 0x73, 0x4d, + 0x54, 0x8f, 0xa3, 0x14, 0x2c, 0x37, 0xe6, 0xfc, 0xac, 0x51, 0x92, 0xfc, 0xa2, + 0x8d, 0x63, 0x98, 0x1f, 0x67, 0xdd, 0xdc, 0x28, 0xb3, 0x1f, 0xd0, 0xb9, 0x3a, + 0x7f, 0x21, 0x88, 0xc1, 0xec, 0xa2, 0xc1, 0xef, 0xa4, 0x61, 0xd2, 0xdd, 0x73, + 0x38, 0xdf, 0x07, 0x05, 0xae, 0x70, 0x10, 0x62, 0xfb, 0xcd, 0x8d, 0x50, 0x29, + 0x98, 0x85, 0xd8, 0xe3, 0xd4, 0xfb, 0xd6, 0xa4, 0xf2, 0x15, 0x5d, 0xc8, 0xd8, + 0xfd, 0x0b, 0x05, 0x8f, 0x3c, 0x77, 0x50, 0x83, 0xf5, 0x96, 0x12, 0xac, 0x66, + 0x02, 0xd9, 0xad, 0xfa, 0x49, 0xe2, 0x60, 0x2a, 0x12, 0xf2, 0x90, 0x0d, 0x22, + 0xb9, 0x9c, 0x0b, 0x8a, 0x32, 0x68, 0xa0, 0x19, 0xc0, 0xdd, 0xf3, 0x14, 0x3e, + 0x8a, 0xf4, 0x13, 0x07, 0xd9, 0x26, 0x74, 0x02, 0x13, 0x08, 0x59, 0xee, 0x92, + 0x43, 0x4d, 0x23, 0x79, 0xe9, 0x4b, 0xcb, 0xbe, 0x56, 0x1d, 0xe0, 0x42, 0x92, + 0xb5, 0x32, 0xab, 0xc3, 0x5d, 0xde, 0x53, 0xd2, 0xad, 0x86, 0x7f, 0x7a, 0xd9, + 0x42, 0x00, 0xe4, 0x8e, 0x50, 0x3e, 0x7d, 0x41, 0x6b, 0xcf, 0x98, 0x29, 0x9f, + 0x82, 0xfc, 0xba, 0xe2, 0xdc, 0x42, 0xae, 0xc1, 0x8a, 0x29, 0x3b, 0x63, 0x79, + 0x5b, 0x68, 0x63, 0xf3, 0x22, 0x49, 0xcd, 0x20, 0x5e, 0x54, 0xd7, 0xcb, 0x7c, + 0x82, 0x3b, 0x00, 0x74, 0x77, 0x35, 0x96, 0xc1, 0xc5, 0x33, 0x92, 0x1d, 0x3b, + 0xae, 0x11, 0xfe, 0x1c, 0x6b, 0xfb, 0x77, 0x74, 0xe1, 0x49, 0x88, 0x64, 0xf3, + 0xb6, 0x26, 0xd4, 0xcb, 0x14, 0x47, 0x95, 0xd8, 0xf3, 0x59, 0xf5, 0xc5, 0x5d, + 0xa3, 0xd7, 0x11, 0x70, 0x4e, 0x74, 0x29, 0x58, 0x95, 0x5e, 0xaf, 0xa4, 0xb7, + 0xd0, 0x31, 0xb2, 0xd6, 0xda, 0x0c, 0x52, 0x9d, 0x41, 0xf3, 0x16, 0x93, 0xe4, + 0xe5, 0x10, 0xb6, 0xb1, 0xe4, 0xab, 0xb6, 0x01, 0x5f, 0x0d, 0x6d, 0x12, 0x61, + 0x5e, 0xc1, 0xea, 0xf2, 0x75, 0xd4, 0x62, 0x96, 0x2f, 0x17, 0x68, 0x4a, 0x7a, + 0x25, 0x30, 0x1a, 0x99, 0x55, 0x5d, 0xef, 0x47, 0x15, 0xff, 0x62, 0xce, 0x3c, + 0xa6, 0x2f, 0x82, 0xe1, 0xf0, 0xec, 0x3b, 0x76, 0xd9, 0xea, 0x82, 0x5a, 0xbc, + 0x46, 0xfa, 0x2c, 0xf2, 0xb7, 0xa9, 0x64, 0x3e, 0xf2, 0x11, 0x0d, 0x16, 0xef, + 0x7a, 0x37, 0x0a, 0x5a, 0x99, 0xc0, 0xf7, 0x3d, 0xd2, 0x07, 0xb7, 0xba, 0xc5, + 0x2f, 0x36, 0x7d, 0xc4, 0xba, 0x9f, 0x52, 0x1c, 0x2d, 0x48, 0x77, 0xba, 0x68, + 0x98, 0xb8, 0xc9, 0x0c, 0x6d, 0xa7, 0x33, 0x64, 0x5c, 0xfb, 0x78, 0xc6, 0xf4, + 0x09, 0x92, 0xc7, 0x20, 0x96, 0x8f, 0xe4, 0x3c, 0x32, 0xbb, 0x59, 0x39, 0x9b, + 0xa8, 0x82, 0x36, 0x06, 0x4a, 0xa0, 0xa6, 0x8f, 0x1a, 0x5a, 0xfa, 0xae, 0xd0, + 0xf5, 0x39, 0xc2, 0x4e, 0xf9, 0xe6, 0x9d, 0x37, 0xdd, 0xba, 0x2d, 0x15, 0x86, + 0xc0, 0x3b, 0x52, 0x45, 0x48, 0xd8, 0x20, 0x7d, 0xa9, 0x58, 0x92, 0xc0, 0x0a, + 0xcf, 0xee, 0x51, 0xb2, 0x42, 0x4c, 0x2d, 0x1a, 0x4d, 0x7d, 0x4d, 0xd9, 0x8a, + 0x1c, 0x6f, 0x2a, 0x5f, 0x6b, 0x39, 0x20, 0x64, 0x30, 0xf1, 0x84, 0x37, 0x3a, + 0x96, 0xc6, 0xaa, 0x58, 0xcc, 0xe2, 0xe1, 0xc5, 0x04, 0xd4, 0x0e, 0xe9, 0xef, + 0xda, 0x58, 0x8d, 0x43, 0xef, 0xb1, 0xda, 0x53, 0xc7, 0x3d, 0x53, 0x0f, 0xa7, + 0x6b, 0x11, 0x2f, 0x33, 0xb4, 0xaf, 0xa9, 0x41, 0xcd, 0x1e, 0x20, 0x5a, 0xcd, + 0x72, 0xca, 0x86, 0x84, 0xad, 0xe8, 0x33, 0x3d, 0x46, 0x32, 0xab, 0x94, 0x3d, + 0x69, 0x0f, 0x13, 0x2b, 0xc9, 0x9f, 0x5c, 0x5a, 0x1d, 0x3e, 0xa4, 0xe0, 0xca, + 0x8f, 0x43, 0x23, 0x8c, 0xd9, 0xeb, 0x09, 0xc8, 0xbf, 0x11, 0xe9, 0x18, 0xa9, + 0xc7, 0xf8, 0x83, 0xbe, 0x94, 0x89, 0x06, 0x56, 0x33, 0x66, 0x67, 0x95, 0x4a, + 0x51, 0xa8, 0xae, 0xcd, 0xc4, 0xcb, 0xd3, 0x9a, 0xca, 0xc7, 0x52, 0x05, 0x6e, + 0x71, 0xcc, 0x96, 0x91, 0x55, 0xdd, 0x65, 0x6d, 0x79, 0x59, 0x00, 0x8c, 0x0e, + 0xcf, 0x61, 0x83, 0x2a, 0x5c, 0x44, 0xe2, 0xe0, 0xde, 0x68, 0xf5, 0x04, 0xc1, + 0x77, 0xdf, 0x68, 0x8b, 0xee, 0x55, 0x8c, 0x6f, 0x4e, 0x5e, 0xa5, 0xf9, 0xad, + 0x78, 0x26, 0x73, 0x40, 0xe2, 0xc8, 0x35, 0xb9, 0x74, 0xdd, 0x12, 0xcd, 0xb9, + 0x05, 0x08, 0x87, 0x60, 0x34, 0xdd, 0xde, 0x0d, 0x97, 0xea, 0xfa, 0xf9, 0x70, + 0x18, 0x34, 0x90, 0xcd, 0x22, 0xea, 0x57, 0xb9, 0x8a, 0xbd, 0x1a, 0x7f, 0x79, + 0xe2, 0xcf, 0x23, 0xcf, 0x8d, 0x1b, 0x0e, 0x9b, 0x7c, 0x93, 0x8a, 0xcc, 0x6b, + 0x14, 0x4b, 0x54, 0x13, 0xd3, 0x2f, 0x50, 0xcd, 0x09, 0x61, 0x8f, 0xa9, 0x74, + 0x10, 0x3a, 0x72, 0x8e, 0x2b, 0x71, 0x76, 0x63, 0xd4, 0xbd, 0x9b, 0x07, 0x20, + 0xb7, 0x75, 0xf5, 0xee, 0x25, 0xa6, 0xd7, 0x4d, 0x12, 0x8c, 0x49, 0xb4, 0x0a, + 0x19, 0x74, 0x1c, 0x37, 0xdd, 0x34, 0x61, 0x6d, 0xb3, 0x1e, 0xac, 0x0b, 0xe7, + 0xf5, 0x3f, 0xfa, 0x61, 0x9f, 0x45, 0x18, 0x1f, 0x5a, 0x4d, 0xbe, 0x5b, 0x1b, + 0x48, 0x09, 0x8e, 0xba, 0x2c, 0x2e, 0xc2, 0x0a, 0x0a, 0xc0, 0x44, 0x3b, 0xa8, + 0xe9, 0x48, 0x7b, 0xcf, 0x7d, + ], + script_code: Script(vec![0xac, 0x53, 0x63, 0x52, 0x6a, 0x51, 0xac]), + transparent_input: None, + hash_type: 1, + amount: 1501997449504444, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0xa1, 0xcf, 0x50, 0xcf, 0xfe, 0x59, 0xbe, 0x5f, 0x31, 0x5f, 0xfe, 0x51, 0x6e, + 0x28, 0x9e, 0xe8, 0x02, 0x5e, 0x59, 0x38, 0xf1, 0xe8, 0xe1, 0x88, 0x53, 0x7f, + 0xf1, 0xa8, 0x93, 0xac, 0x71, 0x14, + ], + }, + Test0243Vector { + tx: vec![ + 0x04, 0x00, 0x00, 0x80, 0x85, 0x20, 0x2f, 0x89, 0x02, 0x88, 0x1d, 0xdf, 0x4f, + 0x95, 0x78, 0x97, 0x34, 0xfc, 0xc1, 0x65, 0xee, 0x1e, 0x04, 0x40, 0x85, 0xb6, + 0xe7, 0xa1, 0x77, 0x50, 0x8c, 0x29, 0xda, 0x0c, 0xe7, 0x7d, 0xed, 0x75, 0x08, + 0x98, 0xde, 0x89, 0xd2, 0x60, 0xd3, 0x02, 0x63, 0x52, 0x44, 0xcc, 0x75, 0xe1, + 0x98, 0x34, 0x52, 0x5f, 0xba, 0x56, 0x90, 0x0d, 0xe9, 0x93, 0x85, 0x44, 0x2e, + 0xb9, 0xec, 0x9a, 0x5f, 0x18, 0x2b, 0x87, 0x5d, 0x70, 0xb5, 0xb1, 0x53, 0x79, + 0x0a, 0x1e, 0xe7, 0x9c, 0x0e, 0x86, 0x78, 0x37, 0x95, 0xfa, 0x06, 0x6a, 0x00, + 0x63, 0x00, 0x00, 0x63, 0xfc, 0x92, 0x29, 0x92, 0x00, 0x83, 0x64, 0xff, 0xfc, + 0x7c, 0x00, 0xc0, 0x0e, 0x0f, 0x99, 0xde, 0x47, 0x42, 0x89, 0x06, 0x00, 0x01, + 0x39, 0x21, 0x97, 0xd6, 0x23, 0xf7, 0xeb, 0xda, 0x07, 0xcd, 0x00, 0x58, 0xd9, + 0xa1, 0xd1, 0x72, 0x04, 0x3c, 0x2f, 0xc9, 0x4f, 0x14, 0x19, 0x3e, 0x27, 0x0e, + 0xef, 0xe8, 0x3c, 0x3f, 0x01, 0xb2, 0x65, 0x05, 0x4c, 0x3f, 0x6a, 0x60, 0xe2, + 0xb7, 0x6e, 0x17, 0x56, 0x08, 0x8b, 0x87, 0xda, 0x83, 0x9f, 0x77, 0x2c, 0xbd, + 0x0f, 0x27, 0x5c, 0x92, 0x28, 0x38, 0x5a, 0x04, 0xbb, 0x50, 0xec, 0x3c, 0xfa, + 0x9e, 0xe2, 0xe1, 0x5b, 0x15, 0x3d, 0x4c, 0x85, 0xfe, 0x50, 0xb6, 0x00, 0x62, + 0x58, 0xe9, 0xe8, 0xc2, 0x52, 0x99, 0xc0, 0x9d, 0xf8, 0xb4, 0x55, 0x46, 0x6b, + 0xa2, 0x5f, 0x7e, 0x4c, 0x8f, 0xe7, 0xe2, 0x50, 0xed, 0xba, 0x60, 0x69, 0x5d, + 0xa4, 0x7f, 0xaa, 0xfd, 0xd6, 0x26, 0xba, 0x7e, 0x9d, 0x48, 0x96, 0xe4, 0xb8, + 0xa8, 0xa1, 0xa1, 0xdc, 0x21, 0x5b, 0x0a, 0x25, 0xee, 0xb0, 0x4e, 0xd1, 0xbe, + 0xfb, 0x5b, 0x31, 0x38, 0xc6, 0x9f, 0xe5, 0x28, 0xe7, 0x29, 0x11, 0x23, 0xfc, + 0xdf, 0x8a, 0x36, 0x6c, 0x25, 0x7d, 0x32, 0x95, 0x38, 0x25, 0x0a, 0x0c, 0xb7, + 0xf5, 0x4e, 0x1c, 0x01, 0x6c, 0xe1, 0xc6, 0x23, 0xb2, 0xe2, 0x76, 0xa5, 0x2c, + 0x6e, 0x41, 0x24, 0x1b, 0x2a, 0xc5, 0x09, 0x37, 0x3c, 0x18, 0x81, 0x40, 0xe8, + 0x36, 0x5c, 0x94, 0xf5, 0x8c, 0x63, 0xf2, 0x7f, 0xf8, 0xe6, 0xe8, 0x69, 0xa9, + 0x85, 0xaf, 0xb6, 0x1e, 0x97, 0xd8, 0xce, 0xec, 0x2a, 0x78, 0x24, 0xa5, 0xc1, + 0x07, 0xb0, 0xba, 0xa4, 0xd6, 0xe7, 0x9a, 0x6c, 0x71, 0x87, 0x2a, 0x7b, 0x3b, + 0x17, 0xef, 0x91, 0x8a, 0xe4, 0xe2, 0x5f, 0x98, 0xa7, 0x2d, 0xb5, 0x3b, 0xa7, + 0xf2, 0x6e, 0x40, 0x8b, 0xd4, 0xd1, 0xf9, 0xe3, 0x47, 0x4d, 0xdc, 0xa5, 0x83, + 0x3f, 0xf5, 0xff, 0x8d, 0x11, 0xb1, 0xbf, 0x1e, 0x2b, 0xb4, 0xd1, 0x96, 0x8a, + 0x82, 0x38, 0x88, 0xbd, 0x91, 0xa2, 0x1a, 0x76, 0x79, 0x6b, 0xca, 0x44, 0x53, + 0xe2, 0x89, 0x2d, 0x1b, 0x6e, 0x13, 0x63, 0xed, 0x10, 0x7a, 0x9e, 0x7e, 0xd9, + 0x3f, 0xb1, 0xda, 0x99, 0x4a, 0x9d, 0x4e, 0x7e, 0xc9, 0x2e, 0x29, 0xa6, 0x87, + 0xf2, 0x18, 0xd2, 0x8a, 0x76, 0x46, 0x06, 0x9b, 0xca, 0xcb, 0x4d, 0xa7, 0xba, + 0xdf, 0x4e, 0xb1, 0x33, 0x1a, 0xab, 0x21, 0x2b, 0x92, 0xc6, 0xea, 0x64, 0x76, + 0xa0, 0xa0, 0x9d, 0x6b, 0xd2, 0xe0, 0xf7, 0x6f, 0xa8, 0x73, 0x79, 0xab, 0xfd, + 0x17, 0x58, 0x2f, 0x3e, 0xb2, 0x3b, 0x86, 0xc9, 0x66, 0x9f, 0x86, 0x73, 0x70, + 0x48, 0xd7, 0x71, 0x84, 0x9b, 0x8f, 0x70, 0xbd, 0x87, 0x99, 0x01, 0x3b, 0xe0, + 0xbf, 0xbd, 0x7b, 0x57, 0xbe, 0xa1, 0xa4, 0x9a, 0x4a, 0x39, 0x14, 0x79, 0x12, + 0xd7, 0xba, 0xf6, 0x80, 0x04, 0xd4, 0x15, 0x02, 0x6b, 0xbc, 0x6f, 0x69, 0x32, + 0x5f, 0x4f, 0xf7, 0x87, 0x28, 0x77, 0x5a, 0x67, 0xaa, 0xdd, 0x72, 0x2c, 0x73, + 0x31, 0x1d, 0xba, 0x5c, 0x2c, 0xf1, 0x4c, 0xcb, 0xd5, 0x7e, 0xab, 0xed, 0x71, + 0x92, 0x0f, 0xf9, 0x62, 0x32, 0x89, 0xbb, 0x76, 0x05, 0x1c, 0x73, 0xa2, 0x06, + 0xa3, 0xc2, 0xb4, 0x0c, 0xac, 0x01, 0xd5, 0xf1, 0x1f, 0xa6, 0x4c, 0x1b, 0x7d, + 0xed, 0x70, 0xea, 0x17, 0x42, 0x9c, 0x66, 0x21, 0xca, 0x9b, 0x92, 0x3c, 0x48, + 0x11, 0x85, 0x0c, 0x3d, 0xf4, 0x01, 0x3d, 0x17, 0xbd, 0xc5, 0x10, 0x1c, 0x8d, + 0x80, 0xb3, 0xa0, 0x4a, 0x4c, 0xc2, 0x3d, 0x13, 0xfe, 0x31, 0x84, 0xe8, 0xb1, + 0xad, 0xe6, 0x35, 0x17, 0x59, 0x3f, 0x7b, 0xe6, 0x69, 0x48, 0xc0, 0x85, 0x7a, + 0xec, 0xe0, 0x1b, 0xc2, 0x72, 0x29, 0x5e, 0x60, 0xb1, 0x80, 0x69, 0x46, 0xc9, + 0x3b, 0xc8, 0xc7, 0xd2, 0xa2, 0xed, 0xc3, 0x7f, 0xa3, 0x7c, 0x47, 0x7a, 0x69, + 0xa9, 0x0b, 0x59, 0xb4, 0xc6, 0x91, 0x2e, 0x91, 0x3a, 0x57, 0xef, 0xa9, 0xd5, + 0x4c, 0x7e, 0x80, 0xd5, 0xac, 0x8a, 0x42, 0x94, 0xd0, 0xfd, 0x31, 0xa4, 0x02, + 0xe4, 0xb4, 0x7e, 0xc7, 0xbf, 0x03, 0x31, 0xb2, 0xc9, 0xa4, 0x8f, 0x44, 0x57, + 0x3f, 0xc7, 0xe7, 0xf1, 0x02, 0xed, 0x48, 0xc9, 0x75, 0x08, 0xcb, 0xe4, 0x30, + 0x65, 0xa9, 0xe9, 0x9f, 0xb4, 0xce, 0x13, 0x62, 0xbb, 0x8a, 0x76, 0xb1, 0x41, + 0x9d, 0x95, 0x03, 0x0e, 0x9c, 0x24, 0xee, 0xba, 0x9f, 0xf8, 0xcf, 0xda, 0x95, + 0x7b, 0x17, 0x09, 0x8c, 0xdf, 0x8c, 0x9a, 0x91, 0x9e, 0x47, 0xa1, 0x3a, 0x5b, + 0x33, 0x46, 0xe3, 0x7e, 0x82, 0x7c, 0xc8, 0x3b, 0x3c, 0x9a, 0xab, 0xf2, 0xd0, + 0xba, 0x17, 0xff, 0x3d, 0x9e, 0x0d, 0x22, 0x3c, 0x41, 0xc8, 0x8e, 0xc2, 0x39, + 0x1c, 0x76, 0x62, 0x2d, 0x7b, 0xd6, 0x21, 0x17, 0x33, 0x1e, 0x21, 0xff, 0xec, + 0x32, 0x72, 0xc1, 0xe1, 0x42, 0x39, 0x82, 0xc6, 0xb6, 0x3a, 0xec, 0x8d, 0xbf, + 0x5c, 0xa2, 0xdd, 0x15, 0x81, 0x0f, 0x53, 0x42, 0xaf, 0x49, 0xfa, 0xd2, 0x79, + 0xb7, 0xca, 0x23, 0xde, 0xd3, 0x08, 0x24, 0x79, 0x96, 0x30, 0xde, 0xdc, 0x6d, + 0xb7, 0x24, 0xbc, 0xe1, 0x11, 0x36, 0x21, 0xc4, 0xa6, 0x47, 0x9d, 0xd5, 0x55, + 0xf4, 0x85, 0x21, 0x7c, 0xb5, 0x67, 0x13, 0x9e, 0xea, 0xdd, 0x7e, 0xe8, 0xdc, + 0x5b, 0x26, 0x62, 0xf1, 0x06, 0x6a, 0x7c, 0x60, 0xde, 0xe0, 0x09, 0x3c, 0x92, + 0x46, 0xde, 0x7a, 0x05, 0xe8, 0xb0, 0xf6, 0xbe, 0xf0, 0x03, 0x3d, 0xde, 0x2e, + 0x87, 0xcb, 0xa6, 0x8d, 0x23, 0x6e, 0xf6, 0x6a, 0x23, 0xd5, 0x5e, 0x7b, 0xd2, + 0x8d, 0x02, 0x59, 0x9c, 0xca, 0x0d, 0xf7, 0xa9, 0x00, 0x63, 0x7b, 0xb3, 0x46, + 0x4d, 0x62, 0x2b, 0x7c, 0x9c, 0x9c, 0x8c, 0x91, 0x46, 0x89, 0x74, 0x88, 0x01, + 0x64, 0xde, 0xf7, 0x99, 0x90, 0x8a, 0x11, 0xa5, 0x91, 0xab, 0xb3, 0xc8, 0xd8, + 0xbd, 0x9c, 0x12, 0xb1, 0xf6, 0xf3, 0xcd, 0xc9, 0xed, 0x8e, 0x16, 0xe5, 0x7d, + 0x23, 0x34, 0xb2, 0x17, 0x79, 0x7d, 0xf1, 0x90, 0x52, 0xfe, 0xeb, 0xed, 0x6c, + 0xdb, 0x99, 0xac, 0x44, 0xea, 0x13, 0xaf, 0xea, 0xc4, 0x37, 0x7d, 0x0f, 0xa3, + 0x7e, 0xf5, 0x16, 0xdd, 0xac, 0xea, 0xb0, 0xd9, 0x39, 0x5b, 0xd4, 0x40, 0x46, + 0x0e, 0x28, 0xb5, 0xf5, 0x7a, 0x6e, 0xfd, 0x37, 0xd2, 0x68, 0xa8, 0x64, 0xcb, + 0x5c, 0xa3, 0x4b, 0xe2, 0x87, 0xe1, 0x04, 0x8e, 0xfc, 0x1e, 0x40, 0xcd, 0xf4, + 0xfc, 0xfc, 0x02, 0x4c, 0xf1, 0x82, 0x03, 0x8b, 0x9d, 0x80, 0xed, 0x1c, 0x07, + 0x63, 0x62, 0x00, 0xc8, 0x19, 0xa7, 0xe7, 0xc2, 0x40, 0xc3, 0xc4, 0xf7, 0xa9, + 0x17, 0x32, 0xe3, 0xff, 0x13, 0xe2, 0xa5, 0x6a, 0x64, 0x66, 0x66, 0x10, 0xca, + 0xd9, 0x84, 0x1c, 0x1a, 0x93, 0x4f, 0xe9, 0x33, 0xb0, 0xf1, 0x9f, 0xb7, 0x1d, + 0x06, 0x1c, 0x58, 0xf2, 0x1a, 0x49, 0x81, 0xce, 0x3e, 0x68, 0xc5, 0x02, 0x39, + 0x03, 0x60, 0x8d, 0xe5, 0x83, 0x02, 0xc6, 0xc8, 0xde, 0xf4, 0xe5, 0x61, 0x9e, + 0xc0, 0xd9, 0x1c, 0xf9, 0x35, 0x44, 0x75, 0x97, 0x2b, 0xfe, 0x0d, 0x75, 0x75, + 0x60, 0x2a, 0xaf, 0x0e, 0x9e, 0x88, 0x5c, 0x6b, 0xaf, 0x9d, 0x56, 0x7b, 0x1f, + 0xcb, 0x63, 0x19, 0x0c, 0xb7, 0x92, 0xf1, 0xd8, 0x71, 0x61, 0x1a, 0xdb, 0x4f, + 0x3d, 0x1e, 0xd3, 0x28, 0x02, 0x69, 0x18, 0xe2, 0x8d, 0x2f, 0xd4, 0x5a, 0xb9, + 0xd3, 0x70, 0xe7, 0x29, 0x2e, 0xd7, 0x54, 0xce, 0x29, 0xfb, 0x78, 0x7f, 0xd5, + 0xd0, 0x9e, 0x6d, 0x47, 0xcb, 0xc8, 0x00, 0x21, 0xab, 0xf7, 0xd2, 0xef, 0xeb, + 0xdb, 0xe0, 0xad, 0xd8, 0x70, 0x16, 0x8f, 0x51, 0xdc, 0xc4, 0x09, 0x57, 0xa4, + 0xa3, 0xc8, 0xe1, 0x92, 0x60, 0x13, 0x83, 0xb7, 0x68, 0x41, 0x36, 0xdc, 0xa2, + 0x82, 0x62, 0x3f, 0x31, 0xba, 0x7a, 0xe5, 0x36, 0x6b, 0x45, 0x3c, 0x6a, 0x26, + 0xf6, 0x8a, 0x14, 0xdb, 0x65, 0x59, 0xbc, 0xb1, 0x02, 0x37, 0x37, 0x9a, 0x27, + 0xa9, 0x50, 0x2f, 0xf9, 0xd6, 0x4a, 0x33, 0x83, 0x20, 0x75, 0x15, 0x30, 0xf1, + 0xf8, 0x92, 0xa6, 0xd4, 0x6f, 0x50, 0x31, 0x1b, 0x5e, 0x18, 0xf0, 0x33, 0x6f, + 0xc4, 0x77, 0x21, 0x56, 0x66, 0xe1, 0x88, 0x93, 0x3c, 0x69, 0x39, 0x98, 0x9f, + 0x6e, 0x6a, 0x3a, 0xdb, 0xa2, 0x29, 0x96, 0xaa, 0xe6, 0xa0, 0xfe, 0x1b, 0xdd, + 0xcb, 0xe1, 0x49, 0x6d, 0x96, 0x8d, 0xe0, 0x93, 0xdf, 0x44, 0xa3, 0x30, 0x0f, + 0x75, 0x15, 0xa1, 0x2c, 0x9d, 0x82, 0x22, 0x6d, 0x6b, 0x4d, 0x62, 0xc4, 0x6a, + 0x21, 0x3d, 0x5f, 0x01, 0x07, 0x10, 0x6f, 0xd2, 0xa2, 0x2d, 0x3b, 0x59, 0x86, + 0x13, 0xdb, 0x49, 0x1f, 0x70, 0xcc, 0xb1, 0xf0, 0x3b, 0x86, 0x59, 0x66, 0x9e, + 0xd7, 0x44, 0x34, 0xe4, 0x3b, 0x77, 0x1f, 0x22, 0x78, 0x07, 0x10, 0xfb, 0xd8, + 0xf2, 0xf2, 0x0e, 0x98, 0x97, 0xdf, 0x5c, 0xc2, 0x35, 0x48, 0x77, 0x9c, 0x6c, + 0x08, 0x30, 0x83, 0x9d, 0x23, 0x1c, 0x3f, 0xf9, 0xac, 0x54, 0x40, 0x7d, 0xfd, + 0xfc, 0xc5, 0x90, 0x14, 0xbf, 0x67, 0xd9, 0x68, 0x57, 0x06, 0xa5, 0x62, 0x2e, + 0x38, 0xf7, 0xa9, 0x33, 0xc3, 0x4a, 0xfb, 0xb6, 0xaa, 0x8c, 0xdf, 0xd9, 0x3b, + 0xd2, 0xec, 0x91, 0xad, 0x37, 0x90, 0x4c, 0xe1, 0x3b, 0x8a, 0xb8, 0xef, 0x77, + 0x23, 0x66, 0xfa, 0xd3, 0xc3, 0xeb, 0xee, 0x8f, 0x26, 0x11, 0xee, 0x7b, 0x6c, + 0x2a, 0xf7, 0xe6, 0x53, 0xef, 0xbe, 0xc4, 0xdc, 0x4c, 0xbf, 0x13, 0xac, 0xf3, + 0x7e, 0x39, 0x9e, 0x2b, 0x0b, 0x05, 0xb6, 0x1c, 0xb7, 0xe1, 0x7b, 0x15, 0x62, + 0x7b, 0x62, 0x96, 0x2e, 0x21, 0x00, 0xb1, 0x95, 0xfe, 0xfe, 0x94, 0xbc, 0x48, + 0x4e, 0x88, 0x13, 0x97, 0x00, 0x73, 0x7d, 0xe1, 0xa5, 0xec, 0x7d, 0x9c, 0xc8, + 0x5d, 0x53, 0x3b, 0x61, 0xec, 0xad, 0x86, 0x53, 0xce, 0xdb, 0xb7, 0x71, 0xf6, + 0x75, 0xaf, 0x61, 0xe4, 0xc6, 0xf7, 0xef, 0xaa, 0xcc, 0x9f, 0x7e, 0x42, 0x4c, + 0x16, 0x71, 0x5b, 0x0a, 0x98, 0xc4, 0x46, 0x05, 0x9a, 0x27, 0x1a, 0x27, 0xbd, + 0x56, 0x9d, 0x1b, 0x5d, 0xbf, 0xae, 0x8f, 0x53, 0x89, 0x85, 0x24, 0xca, 0xe8, + 0x70, 0x59, 0xff, 0x34, 0xfb, 0x2a, 0x53, 0x32, 0x26, 0xbd, 0x29, 0xa0, 0xba, + 0x6f, 0x8d, 0x08, 0x36, 0xfd, 0x0a, 0x4c, 0x0d, 0x60, 0x9a, 0x72, 0xe1, 0x05, + 0x39, 0xa4, 0x4f, 0x8c, 0x39, 0xf6, 0x27, 0x9b, 0xe3, 0x96, 0xe4, 0x1c, 0xa9, + 0xf2, 0x9a, 0x28, 0xce, 0x9f, 0xa0, 0xdd, 0x51, 0xa3, 0x02, 0xe7, 0x70, 0xe1, + 0xe3, 0xdb, 0x70, 0x6a, 0x34, 0xcb, 0x90, 0x4e, 0xf0, 0x8d, 0x9c, 0x82, 0xc5, + 0x5b, 0xc7, 0x28, 0xc9, 0x55, 0xb1, 0x20, 0xbb, 0x2e, 0xc3, 0x73, 0xfc, 0xff, + 0xff, 0x3c, 0x46, 0xd6, 0x03, 0xab, 0x38, 0x78, 0x96, 0xd4, 0x9c, 0xd2, 0x1b, + 0x2f, 0x77, 0xec, 0xfb, 0xbb, 0x02, 0xa5, 0xe1, 0x53, 0xb1, 0x71, 0xaf, 0xed, + 0x98, 0x6c, 0x15, 0xda, 0x6f, 0x2d, 0x4c, 0xf7, 0x45, 0xd1, 0x99, 0x5f, 0x51, + 0x36, 0xe1, 0xb3, 0xe6, 0x8a, 0x67, 0xa8, 0x99, 0x6f, 0xe7, 0x65, 0x61, 0x6d, + 0x8a, 0xa1, 0x1b, 0xcd, 0x9f, 0x8b, 0x59, 0x1d, 0xb8, 0x7e, 0xfc, 0xda, 0xaf, + 0xfd, 0x41, 0x00, 0x3e, 0xc7, 0x29, 0x36, 0x05, 0x42, 0x62, 0x08, 0x54, 0xfb, + 0x04, 0xb8, 0x0c, 0xb8, 0x61, 0xa6, 0x36, 0xa4, 0x71, 0x7d, 0x66, 0x68, 0x94, + 0xc3, 0x2f, 0x1f, 0x2b, 0xf2, 0x24, 0x7c, 0xc4, 0x15, 0xde, 0x1d, 0x0c, 0x4e, + 0x71, 0x2b, 0x95, 0x88, 0x42, 0xd6, 0xa4, 0xb2, 0x76, 0xde, 0xa5, 0xdb, 0x88, + 0x42, 0x3f, 0x2b, 0x4c, 0x66, 0x4b, 0x1d, 0x2b, 0x18, 0x77, 0xba, 0xf3, 0x37, + 0x47, 0x34, 0x36, 0x14, 0xe5, 0xeb, 0xe9, 0xb7, 0xe1, 0x2e, 0xd0, 0x15, 0x3f, + 0x9c, 0xa7, 0x45, 0x8e, 0x4d, 0xa4, 0x97, 0x63, 0x9d, 0xff, 0x13, 0x52, 0xff, + 0x0e, 0xfa, 0xe0, 0x1d, 0x14, 0x03, 0x21, 0xc2, 0x8d, 0xd0, 0xb6, 0x7b, 0x06, + 0x98, 0x90, 0xf6, 0x13, 0x0f, 0x82, 0x46, 0xab, 0x85, 0x44, 0x71, 0x75, 0x32, + 0xd3, 0xa5, 0xf6, 0x36, 0x39, 0xa9, 0x9d, 0x7f, 0x8e, 0x98, 0x31, 0xc6, 0x48, + 0x51, 0xb7, 0xef, 0x68, 0x93, 0xb3, 0xc9, 0x74, 0x0f, 0x98, 0x44, 0xd1, 0x8a, + 0x61, 0x3b, 0x5f, 0x9a, 0x6a, 0xb4, 0xbd, 0x6e, 0x6a, 0x93, 0xe8, 0xe4, 0xbe, + 0xa5, 0x57, 0x5d, 0x2c, 0xb4, 0x33, 0x0c, 0x0a, 0xf8, 0x55, 0x83, 0x19, 0xa9, + 0x09, 0xa5, 0x98, 0x8a, 0x99, 0x2e, 0x40, 0x63, 0x43, 0xdd, 0x1c, 0x74, 0x2d, + 0x64, 0xcd, 0x4a, 0x17, 0xa2, 0xf3, 0x79, 0x5e, 0x8d, 0xb4, 0xd3, 0x0c, 0xcd, + 0xf4, 0x41, 0x56, 0x55, 0xed, 0xa7, 0xb4, 0x37, 0xe3, 0x39, 0x73, 0x23, 0x89, + 0x6b, 0x11, 0xb1, 0xbe, 0xd7, 0x2d, 0x63, 0xe3, 0x10, 0xaa, 0x49, 0x67, 0x1d, + 0x85, 0x53, 0x4f, 0x6d, 0xbc, 0x18, 0x1f, 0xeb, 0xb5, 0xbd, 0xc0, 0x8a, 0xc0, + 0xd1, 0x23, 0x82, 0x9d, 0x10, 0x8c, 0xd2, 0x69, 0xf3, 0xb0, 0xa3, 0x96, 0xf4, + 0x24, 0x1e, 0x7d, 0xda, 0x72, 0xf5, 0x48, 0x62, 0xbe, 0xde, 0xf0, 0x1c, 0x12, + 0xe3, 0xc6, 0xcf, 0xdf, 0x75, 0xf6, 0x76, 0xc2, 0xdd, 0xef, 0x91, 0xaf, 0x7f, + 0x8a, 0x8a, 0x76, 0x9c, 0x25, 0xe1, 0x77, 0xcd, 0x43, 0x0b, 0xed, 0xe7, 0x4b, + 0x57, 0x69, 0x05, 0x19, 0xa9, 0x8d, 0xb1, 0xfb, 0x5c, 0x36, 0x12, 0x80, 0xf7, + 0x54, 0x0a, 0xc8, 0x27, 0xa9, 0x1b, 0x2d, 0x08, 0x75, 0x2d, 0xec, 0xfb, 0x71, + 0x56, 0xfc, 0xdb, 0x61, 0x75, 0x78, 0xb0, 0x53, 0xee, 0xe4, 0x1f, 0x66, 0xa6, + 0x0e, 0x04, 0x5c, 0x3a, 0x56, 0x9f, 0x3f, 0x7e, 0xdb, 0x76, 0x31, 0x68, 0x2f, + 0xde, 0x9e, 0xf9, 0x1e, 0xa8, 0x81, 0x1f, 0xc2, 0xc7, 0x8f, 0x64, 0x6a, 0xf6, + 0xb4, 0x71, 0x0e, 0xdb, 0xb8, 0xbf, 0x23, 0x28, 0xbd, 0x32, 0x73, 0xa2, 0xcb, + 0x72, 0xff, 0xcc, 0xa7, 0xc2, 0x17, 0xb8, 0x27, 0x19, 0x2d, 0xd2, 0xea, 0x92, + 0x9e, 0x97, 0x6d, 0x13, 0x1c, 0x9d, 0x20, 0x2e, 0xc5, 0x06, 0xa3, 0x5d, 0x93, + 0xab, 0x21, 0x6f, 0x64, 0xbd, 0x73, 0xfe, 0x5d, 0x8a, 0xba, 0xe4, 0x57, 0x1f, + 0x85, 0xbe, 0xb8, 0x4a, 0x7f, 0x93, 0xa3, 0xde, 0x37, 0xa4, 0x51, 0xf3, 0x08, + 0xf7, 0xde, 0x6c, 0xcd, 0x1a, 0x6e, 0xef, 0xef, 0x24, 0x69, 0x9f, 0x21, 0x58, + 0xd1, 0x26, 0x1f, 0xe2, 0x51, 0x82, 0xb5, 0x02, 0xda, 0x3e, 0x74, 0x61, 0x1a, + 0x61, 0x16, 0xfc, 0x30, 0x64, 0xfa, 0x72, 0x3c, 0x5a, 0x81, 0xad, 0xc0, 0xa3, + 0x2f, 0x1e, 0xd6, 0x29, 0x91, 0x57, 0xd1, 0xc1, 0x1c, 0x0a, 0xd9, 0x90, 0x41, + 0x89, 0x46, 0x96, 0x30, 0x1d, 0x5b, 0x3f, 0x1b, 0xf4, 0x32, 0x05, 0xd7, 0xdc, + 0xcf, 0xa6, 0x8b, 0xbb, 0x4a, 0x1f, 0x5e, 0x24, 0x2b, 0x3e, 0x69, 0x0b, 0xfc, + 0x97, 0xb9, 0x43, 0x66, 0xa3, 0x43, 0xf5, 0xdd, 0x16, 0xdf, 0x67, 0xb2, 0xed, + 0x2b, 0xe2, 0x1c, 0x74, 0x71, 0x18, 0x87, 0x2b, 0x46, 0x2e, 0xe2, 0x0c, 0x77, + 0x8c, 0xed, 0x85, 0x6f, 0xa9, 0x80, 0x40, 0x3f, 0xb2, 0x4b, 0x78, 0x61, 0x37, + 0xd0, 0xef, 0x02, 0x78, 0x53, 0x9b, 0x00, 0xce, 0x6e, 0x23, 0xc0, 0x7e, 0xf2, + 0xa0, 0x7c, 0xb2, 0x4c, 0x51, 0xc5, 0xb4, 0x85, 0xe4, 0x54, 0xed, 0xf6, 0x61, + 0xdb, 0x4b, 0x93, 0x1a, 0xb8, 0xcb, 0x49, 0x4e, 0xb3, 0x94, 0xfd, 0x13, 0xc1, + 0xb3, 0x20, 0x85, 0xf2, 0x7b, 0x20, 0x4a, 0x4b, 0x87, 0xee, 0x6c, 0x80, 0x63, + 0x45, 0xd7, 0x58, 0x4c, 0xb1, 0x61, 0x00, 0x6a, 0xd9, 0x84, 0x8a, 0x24, 0xa2, + 0x2a, 0x57, 0x71, 0xe3, 0xa2, 0xab, 0x65, 0x46, 0x3f, 0x55, 0x3d, 0x52, 0xcd, + 0x53, 0x5e, 0xf1, 0x0b, 0xdd, 0x40, 0xd8, 0x87, 0x73, 0x72, 0xa5, 0x32, 0xe3, + 0x73, 0x1b, 0x0e, 0xe9, 0x0c, 0x04, 0xe8, 0xe4, 0x37, 0x47, 0xcc, 0x3e, 0xb9, + 0x6b, 0xb8, 0x79, 0xbd, 0x94, 0xd7, 0x01, 0x2a, 0xf4, 0x6a, 0x93, 0xba, 0x17, + 0x70, 0x37, 0xf0, 0x62, 0x74, 0x4d, 0x3f, 0xdf, 0xcc, 0xd3, 0x6a, 0xab, 0xe0, + 0xf8, 0xcc, 0xca, 0x19, 0xdc, 0xf7, 0x84, 0x1b, 0x1e, 0xe2, 0xf4, 0xfe, 0xb1, + 0x80, 0x0e, 0x75, 0x44, 0x1c, 0x51, 0xe9, 0x5c, 0xce, 0x94, 0xce, 0xee, 0xcd, + 0x85, 0x87, 0xfb, 0xf5, 0x74, 0x30, 0x8d, 0xd7, 0x63, 0x63, 0x1b, 0x73, 0x35, + 0x78, 0x30, 0x91, 0xf4, 0xc8, 0xb3, 0xc8, 0xfb, 0x3c, 0xd9, 0x39, 0x70, 0xce, + 0xf0, 0xed, 0xa4, 0xca, 0x08, 0x44, 0x75, 0x68, 0x23, 0x9c, 0x02, 0xfe, 0x8f, + 0x67, 0x5e, 0x15, 0xc4, 0x9b, 0x51, 0x21, 0xb1, 0x00, 0xcc, 0x19, 0xfc, 0xc2, + 0xb2, 0x91, 0x3d, 0xf7, 0x4f, 0x75, 0x8f, 0x70, 0xbd, 0x6e, 0xeb, 0x73, 0x39, + 0x51, 0x6e, 0x5f, 0x1e, 0xff, 0x97, 0x00, 0xf8, 0xee, 0x13, 0x0e, 0x5c, 0x84, + 0xce, 0xd7, 0xb1, 0xce, 0xd6, 0x6b, 0xe9, 0xa0, 0x55, 0x96, 0xbe, 0x8e, 0x55, + 0xf6, 0xd9, 0xfd, 0xf7, 0xcf, 0x0f, 0xa6, 0x22, 0x90, 0xec, 0x67, 0x0b, 0x6b, + 0xdd, 0x67, 0x38, 0xbb, 0x5c, 0xfb, 0x34, 0x1e, 0xf5, 0xff, 0xb4, 0x2b, 0xc2, + 0xab, 0xc5, 0x08, 0xff, 0x23, 0x12, 0x48, 0xf2, 0xc2, 0xdc, 0x15, 0x77, 0x0d, + 0x33, 0x72, 0x2b, 0x9c, 0x9d, 0xae, + ], + script_code: Script(vec![0xac, 0x65]), + transparent_input: Some(0), + hash_type: 3, + amount: 391892287957268, + consensus_branch_id: consensus::BranchId::Sapling, + sighash: [ + 0x6a, 0x3b, 0x2b, 0xcc, 0x15, 0x57, 0x89, 0xa2, 0x74, 0x39, 0xaa, 0x27, 0x5c, + 0xa9, 0x9e, 0xc6, 0x48, 0xdd, 0xd5, 0x88, 0xe8, 0x2e, 0xfa, 0xe4, 0xac, 0x46, + 0xba, 0x3f, 0xd0, 0xe3, 0xbb, 0xa0, + ], + }, + ] + } +} diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 7412406..1271a2d 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -1,9 +1,13 @@ +//! Implementation of [ZIP 32] for hierarchical deterministic key management. +//! +//! [ZIP 32]: https://zips.z.cash/zip-0032 + use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; -use ff::Field; use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::bls12_381::Bls12; +use std::ops::AddAssign; use crate::{ jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform}, @@ -16,8 +20,8 @@ use crate::{ JUBJUB, }; -pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling"; -pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP"; +pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling"; +pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP"; // Common helper functions @@ -83,9 +87,9 @@ impl ChildIndex { } fn to_index(&self) -> u32 { - match self { - &ChildIndex::Hardened(i) => i + (1 << 31), - &ChildIndex::NonHardened(i) => i, + match *self { + ChildIndex::Hardened(i) => i + (1 << 31), + ChildIndex::NonHardened(i) => i, } } } @@ -198,7 +202,7 @@ impl std::cmp::PartialEq for ExtendedSpendingKey { } impl std::fmt::Debug for ExtendedSpendingKey { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})", @@ -221,7 +225,7 @@ impl std::cmp::PartialEq for ExtendedFullViewingKey { } impl std::fmt::Debug for ExtendedFullViewingKey { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})", @@ -434,7 +438,7 @@ impl ExtendedFullViewingKey { Ok(ret) => ret, Err(()) => return Err(()), }; - match self.fvk.vk.into_payment_address(d_j, &JUBJUB) { + match self.fvk.vk.to_payment_address(d_j, &JUBJUB) { Some(addr) => Ok((j, addr)), None => Err(()), } @@ -449,7 +453,7 @@ impl ExtendedFullViewingKey { mod tests { use super::*; - use ff::{PrimeField, PrimeFieldRepr}; + use ff::PrimeField; #[test] fn derive_nonhardened_child() { @@ -548,7 +552,7 @@ mod tests { let (j_m, addr_m) = xsk_m.default_address().unwrap(); assert_eq!(j_m.0, [0; 11]); assert_eq!( - addr_m.diversifier.0, + addr_m.diversifier().0, // Computed using this Rust implementation [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19] ); @@ -1010,11 +1014,8 @@ mod tests { let xsk = &xsks[j]; let tv = &test_vectors[j]; - let mut buf = [0; 32]; - xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.ask.unwrap()); - xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.nsk.unwrap()); + assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); @@ -1039,13 +1040,7 @@ mod tests { assert_eq!(xfvk.dk.0, tv.dk); assert_eq!(xfvk.chain_code.0, tv.c); - xfvk.fvk - .vk - .ivk() - .into_repr() - .write_le(&mut buf[..]) - .unwrap(); - assert_eq!(buf, tv.ivk); + assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk); let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index b7fa659..3d2a8d4 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -1,24 +1,33 @@ [package] name = "zcash_proofs" -version = "0.0.0" +description = "Zcash zk-SNARK circuits and proving APIs" +version = "0.2.0" authors = [ "Jack Grigg ", ] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" [dependencies] -bellman = { path = "../bellman" } +bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" byteorder = "1" directories = { version = "1", optional = true } -ff = { path = "../ff" } -pairing = { path = "../pairing" } -rand_os = "0.2" -zcash_primitives = { path = "../zcash_primitives" } +ff = { version = "0.6", path = "../ff" } +pairing = { version = "0.16", path = "../pairing" } +rand_core = "0.5.1" +zcash_primitives = { version = "0.2", path = "../zcash_primitives" } [dev-dependencies] -rand_core = "0.5" rand_xorshift = "0.2" [features] -default = ["local-prover"] +default = ["local-prover", "multicore"] local-prover = ["directories"] +multicore = ["bellman/multicore"] + +[badges] +maintenance = { status = "actively-developed" } diff --git a/zcash_proofs/README.md b/zcash_proofs/README.md index 92d9c77..f365c5a 100644 --- a/zcash_proofs/README.md +++ b/zcash_proofs/README.md @@ -7,7 +7,8 @@ and verifying proofs. Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/zcash_proofs/examples/bench.rs b/zcash_proofs/examples/bench.rs index a5c8a13..2f48786 100644 --- a/zcash_proofs/examples/bench.rs +++ b/zcash_proofs/examples/bench.rs @@ -1,11 +1,3 @@ -extern crate bellman; -extern crate ff; -extern crate pairing; -extern crate rand_core; -extern crate rand_xorshift; -extern crate zcash_primitives; -extern crate zcash_proofs; - use bellman::groth16::*; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; @@ -58,7 +50,7 @@ fn main() { nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.into_viewing_key(jubjub_params); + let viewing_key = proof_generation_key.to_viewing_key(jubjub_params); let payment_address; @@ -69,7 +61,7 @@ fn main() { Diversifier(d) }; - if let Some(p) = viewing_key.into_payment_address(diversifier, jubjub_params) { + if let Some(p) = viewing_key.to_payment_address(diversifier, jubjub_params) { payment_address = p; break; } diff --git a/zcash_proofs/src/circuit.rs b/zcash_proofs/src/circuit.rs index ac7e74c..6d26fa4 100644 --- a/zcash_proofs/src/circuit.rs +++ b/zcash_proofs/src/circuit.rs @@ -1,3 +1,5 @@ +//! Implementations of the Zcash circuits and Zcash-specific gadgets. + pub mod ecc; pub mod pedersen_hash; diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 4c07829..d287e1b 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -1,5 +1,8 @@ +//! Gadgets implementing Jubjub elliptic curve operations. + use ff::Field; use pairing::Engine; +use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use bellman::{ConstraintSystem, SynthesisError}; @@ -41,16 +44,16 @@ where { let chunk_a = chunk .get(0) - .map(|e| e.clone()) - .unwrap_or(Boolean::constant(false)); + .cloned() + .unwrap_or_else(|| Boolean::constant(false)); let chunk_b = chunk .get(1) - .map(|e| e.clone()) - .unwrap_or(Boolean::constant(false)); + .cloned() + .unwrap_or_else(|| Boolean::constant(false)); let chunk_c = chunk .get(2) - .map(|e| e.clone()) - .unwrap_or(Boolean::constant(false)); + .cloned() + .unwrap_or_else(|| Boolean::constant(false)); let (x, y) = lookup3_xy( cs.namespace(|| format!("window table lookup {}", i)), @@ -58,7 +61,7 @@ where window, )?; - let p = EdwardsPoint { x: x, y: y }; + let p = EdwardsPoint { x, y }; if result.is_none() { result = Some(p); @@ -121,9 +124,9 @@ impl EdwardsPoint { { let mut tmp = vec![]; - let x = self.x.into_bits_le_strict(cs.namespace(|| "unpack x"))?; + let x = self.x.to_bits_le_strict(cs.namespace(|| "unpack x"))?; - let y = self.y.into_bits_le_strict(cs.namespace(|| "unpack y"))?; + let y = self.y.to_bits_le_strict(cs.namespace(|| "unpack y"))?; tmp.extend(y); tmp.push(x[0].clone()); @@ -141,7 +144,7 @@ impl EdwardsPoint { where CS: ConstraintSystem, { - let p = p.map(|p| p.into_xy()); + let p = p.map(|p| p.to_xy()); // Allocate x let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(p.get()?.0))?; @@ -320,8 +323,7 @@ impl EdwardsPoint { // Compute C = d*A*A let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { - let mut t0 = *a.get_value().get()?; - t0.square(); + let mut t0 = a.get_value().get()?.square(); t0.mul_assign(params.edwards_d()); Ok(t0) @@ -337,18 +339,16 @@ impl EdwardsPoint { // Compute x3 = (2.A) / (1 + C) let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { let mut t0 = *a.get_value().get()?; - t0.double(); + t0 = t0.double(); let mut t1 = E::Fr::one(); t1.add_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let res = t1.invert().map(|t1| t0 * &t1); + if bool::from(res.is_some()) { + Ok(res.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -363,20 +363,17 @@ impl EdwardsPoint { // Compute y3 = (U - 2.A) / (1 - C) let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { let mut t0 = *a.get_value().get()?; - t0.double(); - t0.negate(); + t0 = t0.double().neg(); t0.add_assign(t.get_value().get()?); let mut t1 = E::Fr::one(); t1.sub_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let res = t1.invert().map(|t1| t0 * &t1); + if bool::from(res.is_some()) { + Ok(res.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -450,13 +447,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.add_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -477,13 +472,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.sub_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -508,7 +501,7 @@ impl MontgomeryPoint { /// a point in the birationally equivalent twisted /// Edwards curve. pub fn into_edwards( - &self, + self, mut cs: CS, params: &E::Params, ) -> Result, SynthesisError> @@ -520,13 +513,11 @@ impl MontgomeryPoint { let mut t0 = *self.x.get_value().get()?; t0.mul_assign(params.scale()); - match self.y.get_value().get()?.inverse() { - Some(invy) => { - t0.mul_assign(&invy); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = self.y.get_value().get()?.invert().map(|invy| t0 * &invy); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -544,13 +535,11 @@ impl MontgomeryPoint { t0.sub_assign(&E::Fr::one()); t1.add_assign(&E::Fr::one()); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -570,7 +559,7 @@ impl MontgomeryPoint { /// on the curve. Useful for constants and /// window table lookups. pub fn interpret_unchecked(x: Num, y: Num) -> Self { - MontgomeryPoint { x: x, y: y } + MontgomeryPoint { x, y } } /// Performs an affine point addition, not defined for @@ -592,12 +581,11 @@ impl MontgomeryPoint { let mut d = *other.x.get_value().get()?; d.sub_assign(self.x.get_value().get()?); - match d.inverse() { - Some(d) => { - n.mul_assign(&d); - Ok(n) - } - None => Err(SynthesisError::DivisionByZero), + let ret = d.invert().map(|d| n * &d); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -610,8 +598,7 @@ impl MontgomeryPoint { // Compute x'' = lambda^2 - A - x - x' let xprime = AllocatedNum::alloc(cs.namespace(|| "xprime"), || { - let mut t0 = *lambda.get_value().get()?; - t0.square(); + let mut t0 = lambda.get_value().get()?.square(); t0.sub_assign(params.montgomery_a()); t0.sub_assign(self.x.get_value().get()?); t0.sub_assign(other.x.get_value().get()?); @@ -639,7 +626,7 @@ impl MontgomeryPoint { t0.sub_assign(self.x.get_value().get()?); t0.mul_assign(lambda.get_value().get()?); t0.add_assign(self.y.get_value().get()?); - t0.negate(); + t0 = t0.neg(); Ok(t0) })?; @@ -666,6 +653,7 @@ mod test { use pairing::bls12_381::{Bls12, Fr}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; + use std::ops::SubAssign; use bellman::gadgets::test::*; use zcash_primitives::jubjub::fs::Fs; @@ -688,8 +676,8 @@ mod test { let mut cs = TestConstraintSystem::::new(); let p = montgomery::Point::::rand(rng, params); - let (u, v) = edwards::Point::from_montgomery(&p, params).into_xy(); - let (x, y) = p.into_xy().unwrap(); + let (u, v) = edwards::Point::from_montgomery(&p, params).to_xy(); + let (x, y) = p.to_xy().unwrap(); let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || Ok(y)).unwrap(); @@ -728,7 +716,7 @@ mod test { let mut cs = TestConstraintSystem::::new(); let q = EdwardsPoint::witness(&mut cs, Some(p.clone()), ¶ms).unwrap(); - let p = p.into_xy(); + let p = p.to_xy(); assert!(cs.is_satisfied()); assert_eq!(q.x.get_value().unwrap(), p.0); @@ -737,7 +725,7 @@ mod test { for _ in 0..100 { let p = edwards::Point::::rand(rng, ¶ms); - let (x, y) = p.into_xy(); + let (x, y) = p.to_xy(); let mut cs = TestConstraintSystem::::new(); let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); @@ -779,9 +767,9 @@ mod test { let p = params.generator(FixedGenerators::NoteCommitmentRandomness); let s = Fs::random(rng); let q = p.mul(s, params); - let (x1, y1) = q.into_xy(); + let (x1, y1) = q.to_xy(); - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); @@ -823,8 +811,8 @@ mod test { let s = Fs::random(rng); let q = p.mul(s, params); - let (x0, y0) = p.into_xy(); - let (x1, y1) = q.into_xy(); + let (x0, y0) = p.to_xy(); + let (x1, y1) = q.to_xy(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); @@ -834,7 +822,7 @@ mod test { y: num_y0, }; - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); s_bits.truncate(Fs::NUM_BITS as usize); @@ -873,7 +861,7 @@ mod test { let p = edwards::Point::::rand(rng, params); - let (x0, y0) = p.into_xy(); + let (x0, y0) = p.to_xy(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); @@ -941,9 +929,9 @@ mod test { let p3 = p1.add(&p2, params); - let (x0, y0) = p1.into_xy(); - let (x1, y1) = p2.into_xy(); - let (x2, y2) = p3.into_xy(); + let (x0, y0) = p1.to_xy(); + let (x1, y1) = p2.to_xy(); + let (x2, y2) = p3.to_xy(); let mut cs = TestConstraintSystem::::new(); @@ -1002,8 +990,8 @@ mod test { let p1 = edwards::Point::::rand(rng, params); let p2 = p1.double(params); - let (x0, y0) = p1.into_xy(); - let (x1, y1) = p2.into_xy(); + let (x0, y0) = p1.to_xy(); + let (x1, y1) = p2.to_xy(); let mut cs = TestConstraintSystem::::new(); @@ -1037,8 +1025,9 @@ mod test { let x = Fr::random(rng); let s: bool = rng.next_u32() % 2 != 0; - if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { - break p; + let p = montgomery::Point::::get_for_x(x, s, params); + if p.is_some().into() { + break p.unwrap(); } }; @@ -1046,16 +1035,17 @@ mod test { let x = Fr::random(rng); let s: bool = rng.next_u32() % 2 != 0; - if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { - break p; + let p = montgomery::Point::::get_for_x(x, s, params); + if p.is_some().into() { + break p.unwrap(); } }; let p3 = p1.add(&p2, params); - let (x0, y0) = p1.into_xy().unwrap(); - let (x1, y1) = p2.into_xy().unwrap(); - let (x2, y2) = p3.into_xy().unwrap(); + let (x0, y0) = p1.to_xy().unwrap(); + let (x1, y1) = p2.to_xy().unwrap(); + let (x2, y2) = p3.to_xy().unwrap(); let mut cs = TestConstraintSystem::::new(); @@ -1096,4 +1086,87 @@ mod test { assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate lambda")); } } + + #[test] + fn test_assert_not_small_order() { + let params = &JubjubBls12::new(); + + let check_small_order_from_p = |p: edwards::Point, is_small_order| { + let mut cs = TestConstraintSystem::::new(); + + let p = EdwardsPoint::witness(&mut cs, Some(p), params).unwrap(); + assert!(cs.is_satisfied()); + assert!(p.assert_not_small_order(&mut cs, params).is_err() == is_small_order); + }; + + let check_small_order_from_strs = |x, y| { + //let (x,y) = (Fr::from_str("14080418777298869350588389379361252092475090129841789940098060767181937064268").unwrap(), Fr::from_str("4408371274642418797323679050836535851651768103477128764103246588657558662748").unwrap()); + let (x, y) = (Fr::from_str(x).unwrap(), Fr::from_str(y).unwrap()); + let p = edwards::Point::::get_for_y(y, false, params).unwrap(); + assert_eq!(x, p.to_xy().0); + + check_small_order_from_p(p, true); + }; + + // zero has low order + check_small_order_from_strs("0", "1"); + + // prime subgroup order + let prime_subgroup_order = Fs::from_str( + "6554484396890773809930967563523245729705921265872317281365359162392183254199", + ) + .unwrap(); + let largest_small_subgroup_order = Fs::from_str("8").unwrap(); + + let (zero_x, zero_y) = (Fr::from_str("0").unwrap(), Fr::from_str("1").unwrap()); + + // generator for jubjub + let (x, y) = ( + Fr::from_str( + "11076627216317271660298050606127911965867021807910416450833192264015104452986", + ) + .unwrap(), + Fr::from_str( + "44412834903739585386157632289020980010620626017712148233229312325549216099227", + ) + .unwrap(), + ); + let g = edwards::Point::::get_for_y(y, false, params).unwrap(); + assert_eq!(x, g.to_xy().0); + check_small_order_from_p(g.clone(), false); + + // generator for the prime subgroup + let g_prime = g.mul(largest_small_subgroup_order, params); + check_small_order_from_p(g_prime.clone(), false); + let mut prime_subgroup_order_minus_1 = prime_subgroup_order.clone(); + prime_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + + let should_not_be_zero = g_prime.mul(prime_subgroup_order_minus_1, params); + assert_ne!(zero_x, should_not_be_zero.to_xy().0); + assert_ne!(zero_y, should_not_be_zero.to_xy().1); + let should_be_zero = should_not_be_zero.add(&g_prime, params); + assert_eq!(zero_x, should_be_zero.to_xy().0); + assert_eq!(zero_y, should_be_zero.to_xy().1); + + // generator for the small order subgroup + let g_small = g.mul(prime_subgroup_order_minus_1, params); + let g_small = g_small.add(&g, params); + check_small_order_from_p(g_small.clone(), true); + + // g_small does have order 8 + let mut largest_small_subgroup_order_minus_1 = largest_small_subgroup_order.clone(); + largest_small_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + + let should_not_be_zero = g_small.mul(largest_small_subgroup_order_minus_1, params); + assert_ne!(zero_x, should_not_be_zero.to_xy().0); + assert_ne!(zero_y, should_not_be_zero.to_xy().1); + + let should_be_zero = should_not_be_zero.add(&g_small, params); + assert_eq!(zero_x, should_be_zero.to_xy().0); + assert_eq!(zero_y, should_be_zero.to_xy().1); + + // take all the points from the script + // assert should be different than multiplying by cofactor, which is the solution + // is user input verified? https://github.com/zcash/librustzcash/blob/f5d2afb4eabac29b1b1cc860d66e45a5b48b4f88/src/rustzcash.rs#L299 + } } diff --git a/zcash_proofs/src/circuit/pedersen_hash.rs b/zcash_proofs/src/circuit/pedersen_hash.rs index 9558acc..18c2aae 100644 --- a/zcash_proofs/src/circuit/pedersen_hash.rs +++ b/zcash_proofs/src/circuit/pedersen_hash.rs @@ -1,3 +1,5 @@ +//! Gadget for Zcash's Pedersen hash. + use super::ecc::{EdwardsPoint, MontgomeryPoint}; use bellman::gadgets::boolean::Boolean; use bellman::gadgets::lookup::*; @@ -9,7 +11,7 @@ fn get_constant_bools(person: &Personalization) -> Vec { person .get_bits() .into_iter() - .map(|e| Boolean::constant(e)) + .map(Boolean::constant) .collect() } @@ -65,7 +67,7 @@ where segment_windows = &segment_windows[1..]; - if segment_windows.len() == 0 { + if segment_windows.is_empty() { break; } @@ -114,6 +116,31 @@ mod test { use rand_xorshift::XorShiftRng; use zcash_primitives::pedersen_hash; + /// Predict the number of constraints of a Pedersen hash + fn ph_num_constraints(input_bits: usize) -> usize { + // Account for the 6 personalization bits. + let personalized_bits = 6 + input_bits; + // Constant booleans in the personalization and padding don't need lookup "precomp" constraints. + let precomputed_booleans = 2 + (personalized_bits % 3 == 1) as usize; + + // Count chunks and segments with ceiling division + let chunks = (personalized_bits + 3 - 1) / 3; + let segments = (chunks + 63 - 1) / 63; + let all_but_last_segments = segments - 1; + let last_chunks = chunks - all_but_last_segments * 63; + + // Constraints per operation + let lookup_chunk = 2; + let add_chunks = 3; // Montgomery addition + let convert_segment = 2; // Conversion to Edwards + let add_segments = 6; // Edwards addition + + return (chunks) * lookup_chunk - precomputed_booleans + + segments * convert_segment + + all_but_last_segments * ((63 - 1) * add_chunks + add_segments) + + (last_chunks - 1) * add_chunks; + } + #[test] fn test_pedersen_hash_constraints() { let mut rng = XorShiftRng::from_seed([ @@ -121,32 +148,56 @@ mod test { 0xbc, 0xe5, ]); let params = &JubjubBls12::new(); - let mut cs = TestConstraintSystem::::new(); - let input: Vec = (0..(Fr::NUM_BITS * 2)) - .map(|_| rng.next_u32() % 2 != 0) - .collect(); + let leaves_len = 2 * 255; + let note_len = 64 + 256 + 256; - let input_bools: Vec = input - .iter() - .enumerate() - .map(|(i, b)| { - Boolean::from( - AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap(), - ) - }) - .collect(); + for &n_bits in [ + 0, + 3 * 63 - 6, + 3 * 63 - 6 + 1, + 3 * 63 - 6 + 2, + leaves_len, + note_len, + ] + .iter() + { + let mut cs = TestConstraintSystem::::new(); - pedersen_hash( - cs.namespace(|| "pedersen hash"), - Personalization::NoteCommitment, - &input_bools, - params, - ) - .unwrap(); + let input: Vec = (0..n_bits).map(|_| rng.next_u32() % 2 != 0).collect(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 1377); + let input_bools: Vec = input + .iter() + .enumerate() + .map(|(i, b)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)) + .unwrap(), + ) + }) + .collect(); + + pedersen_hash( + cs.namespace(|| "pedersen hash"), + Personalization::NoteCommitment, + &input_bools, + params, + ) + .unwrap(); + + assert!(cs.is_satisfied()); + + let bitness_constraints = n_bits; + let ph_constraints = ph_num_constraints(n_bits); + assert_eq!(cs.num_constraints(), bitness_constraints + ph_constraints); + // The actual usages + if n_bits == leaves_len { + assert_eq!(cs.num_constraints(), leaves_len + 867) + }; + if n_bits == note_len { + assert_eq!(cs.num_constraints(), note_len + 982) + }; + } } #[test] @@ -159,7 +210,7 @@ mod test { for length in 0..751 { for _ in 0..5 { - let mut input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); + let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); let mut cs = TestConstraintSystem::::new(); @@ -189,7 +240,7 @@ mod test { input.clone().into_iter(), params, ) - .into_xy(); + .to_xy(); assert_eq!(res.get_x().get_value().unwrap(), expected.0); assert_eq!(res.get_y().get_value().unwrap(), expected.1); @@ -200,11 +251,64 @@ mod test { input.into_iter(), params, ) - .into_xy(); + .to_xy(); assert!(res.get_x().get_value().unwrap() != unexpected.0); assert!(res.get_y().get_value().unwrap() != unexpected.1); } } } + + #[test] + fn test_pedersen_hash_external_test_vectors() { + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, + 0xbc, 0xe5, + ]); + let params = &JubjubBls12::new(); + + let expected_xs = [ + "28161926966428986673895580777285905189725480206811328272001879986576840909576", + "39669831794597628158501766225645040955899576179071014703006420393381978263045", + ]; + let expected_ys = [ + "26869991781071974894722407757894142583682396277979904369818887810555917099932", + "2112827187110048608327330788910224944044097981650120385961435904443901436107", + ]; + for length in 300..302 { + let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); + + let mut cs = TestConstraintSystem::::new(); + + let input_bools: Vec = input + .iter() + .enumerate() + .map(|(i, b)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)) + .unwrap(), + ) + }) + .collect(); + + let res = pedersen_hash( + cs.namespace(|| "pedersen hash"), + Personalization::MerkleTree(1), + &input_bools, + params, + ) + .unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!( + res.get_x().get_value().unwrap(), + Fr::from_str(expected_xs[length - 300]).unwrap() + ); + assert_eq!( + res.get_y().get_value().unwrap(), + Fr::from_str(expected_ys[length - 300]).unwrap() + ); + } + } } diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 0554ff4..fe20e4f 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -1,4 +1,6 @@ -use ff::{Field, PrimeField, PrimeFieldRepr}; +//! The Sapling circuits. + +use ff::{Field, PrimeField}; use bellman::{Circuit, ConstraintSystem, SynthesisError}; @@ -150,7 +152,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Witness nsk as bits let nsk = boolean::field_into_boolean_vec_le( cs.namespace(|| "nsk"), - self.proof_generation_key.as_ref().map(|k| k.nsk.clone()), + self.proof_generation_key.as_ref().map(|k| k.nsk), )?; // NB: We don't ensure that the bit representation of nsk @@ -243,7 +245,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut coeff = E::Fr::one(); for bit in &value_bits { value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff); - coeff.double(); + coeff = coeff.double(); } // Place the value in the note @@ -336,8 +338,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // they will be unable to find an authentication path in the // tree with high probability. let mut preimage = vec![]; - preimage.extend(xl.into_bits_le(cs.namespace(|| "xl into bits"))?); - preimage.extend(xr.into_bits_le(cs.namespace(|| "xr into bits"))?); + preimage.extend(xl.to_bits_le(cs.namespace(|| "xl into bits"))?); + preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?); // Compute the new subtree value cur = pedersen_hash::pedersen_hash( @@ -464,7 +466,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // they would like. { // Just grab pk_d from the witness - let pk_d = self.payment_address.as_ref().map(|e| e.pk_d.into_xy()); + let pk_d = self.payment_address.as_ref().map(|e| e.pk_d().to_xy()); // Witness the y-coordinate, encoded as little // endian bits (to match the representation) @@ -476,7 +478,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( cs.namespace(|| "pk_d bit of x"), - pk_d.map(|e| e.0.into_repr().is_odd()), + pk_d.map(|e| e.0.is_odd()), )?); // Extend the note with pk_d representation @@ -567,7 +569,7 @@ fn test_input_circuit_with_bls12_381() { nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.into_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(params); let payment_address; @@ -578,24 +580,24 @@ fn test_input_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.into_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier, params) { payment_address = p; break; } } - let g_d = payment_address.diversifier.g_d(params).unwrap(); + let g_d = payment_address.diversifier().g_d(params).unwrap(); let commitment_randomness = fs::Fs::random(rng); let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; let ar = fs::Fs::random(rng); { - let rk = viewing_key.rk(ar, params).into_xy(); - let expected_value_cm = value_commitment.cm(params).into_xy(); + let rk = viewing_key.rk(ar, params).to_xy(); + let expected_value_cm = value_commitment.cm(params).to_xy(); let note = Note { value: value_commitment.value, g_d: g_d.clone(), - pk_d: payment_address.pk_d.clone(), + pk_d: payment_address.pk_d().clone(), r: commitment_randomness.clone(), }; @@ -613,8 +615,8 @@ fn test_input_circuit_with_bls12_381() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); lhs.reverse(); rhs.reverse(); @@ -626,7 +628,191 @@ fn test_input_circuit_with_bls12_381() { .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), params, ) - .into_xy() + .to_xy() + .0; + + if b { + position |= 1 << i; + } + } + + let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = multipack::bytes_to_bits_le(&expected_nf); + let expected_nf = multipack::compute_multipacking::(&expected_nf); + assert_eq!(expected_nf.len(), 2); + + let mut cs = TestConstraintSystem::::new(); + + let instance = Spend { + params, + value_commitment: Some(value_commitment.clone()), + proof_generation_key: Some(proof_generation_key.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(commitment_randomness), + ar: Some(ar), + auth_path: auth_path.clone(), + anchor: Some(cur), + }; + + instance.synthesize(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 98777); + assert_eq!( + cs.hash(), + "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" + ); + + assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + + assert_eq!(cs.num_inputs(), 8); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!( + cs.get_input(3, "value commitment/commitment point/x/input variable"), + expected_value_cm.0 + ); + assert_eq!( + cs.get_input(4, "value commitment/commitment point/y/input variable"), + expected_value_cm.1 + ); + assert_eq!(cs.get_input(5, "anchor/input variable"), cur); + assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); + assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); + } + } +} + +#[test] +fn test_input_circuit_with_bls12_381_external_test_vectors() { + use bellman::gadgets::test::*; + use ff::{BitIterator, Field}; + use pairing::bls12_381::*; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; + use zcash_primitives::{ + jubjub::{edwards, fs, JubjubBls12}, + pedersen_hash, + primitives::{Diversifier, Note, ProofGenerationKey}, + }; + + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + let tree_depth = 32; + + let expected_cm_xs = vec![ + "43821661663052659750276289184181083197337192946256245809816728673021647664276", + "7220807656052227578299730541645543434083158611414003423211850718229633594616", + "13239753550660714843257636471668037031928211668773449453628093339627668081697", + "10900524635678389360790699587556574797582192824300145558807405770494079767974", + "1411013767457690636461779630023011774660680126764323588543800715293173598850", + "32334206652383066267661379202183359608706535021387905923603014648832344657662", + "20206750741605167608500278423400565295188703622528437817438897624149653579380", + "46716485782200334735478719487356079850582051575003452698983255860512578229998", + "31221372899739042781372142393132358519434268512685538373976981051223051220367", + "18269767207277008186871145355531741929166733260352590789136389380124992250945", + ]; + + let expected_cm_ys = vec![ + "27630722367128086497290371604583225252915685718989450292520883698391703910", + "23310648738313092772044712773481584369462075017189681529702825235349449805260", + "25709635353183537915646348052945798827495141780341329896098121888376871589480", + "10516315852014492141081718791576479298042117442649432716255936672048164184691", + "23970713991179488695004801139667700217127937225554773561645815034212389459772", + "3256052161046564597126736968199320852691566092694819239485673781545479548450", + "18887250722195819674378865377623103071236046274361890247643850134985809137409", + "36501156873031641173054592888886902104303750771545647842488588827138867116570", + "21927526310070011864833939629345235038589128172309792087590183778192091594775", + "32959334601512756708397683646222389414681003290313255304927423560477040775488", + ]; + + for i in 0..10 { + let value_commitment = ValueCommitment { + value: i, + randomness: fs::Fs::from_str(&(1000 * (i + 1)).to_string()).unwrap(), + }; + + let nsk = fs::Fs::random(rng); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + + let proof_generation_key = ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone(), + }; + + let viewing_key = proof_generation_key.to_viewing_key(params); + + let payment_address; + + loop { + let diversifier = { + let mut d = [0; 11]; + rng.fill_bytes(&mut d); + Diversifier(d) + }; + + if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + payment_address = p; + break; + } + } + + let g_d = payment_address.diversifier().g_d(params).unwrap(); + let commitment_randomness = fs::Fs::random(rng); + let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; + let ar = fs::Fs::random(rng); + + { + let rk = viewing_key.rk(ar, params).to_xy(); + let expected_value_cm = value_commitment.cm(params).to_xy(); + assert_eq!( + expected_value_cm.0, + Fr::from_str(&expected_cm_xs[i as usize]).unwrap() + ); + assert_eq!( + expected_value_cm.1, + Fr::from_str(&expected_cm_ys[i as usize]).unwrap() + ); + let note = Note { + value: value_commitment.value, + g_d: g_d.clone(), + pk_d: payment_address.pk_d().clone(), + r: commitment_randomness.clone(), + }; + + let mut position = 0u64; + let cm: Fr = note.cm(params); + let mut cur = cm.clone(); + + for (i, val) in auth_path.clone().into_iter().enumerate() { + let (uncle, b) = val.unwrap(); + + let mut lhs = cur; + let mut rhs = uncle; + + if b { + ::std::mem::swap(&mut lhs, &mut rhs); + } + + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); + + lhs.reverse(); + rhs.reverse(); + + cur = pedersen_hash::pedersen_hash::( + pedersen_hash::Personalization::MerkleTree(i), + lhs.into_iter() + .take(Fr::NUM_BITS as usize) + .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), + params, + ) + .to_xy() .0; if b { @@ -714,7 +900,7 @@ fn test_output_circuit_with_bls12_381() { nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.into_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(params); let payment_address; @@ -725,7 +911,7 @@ fn test_output_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.into_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier, params) { payment_address = p; break; } @@ -738,7 +924,7 @@ fn test_output_circuit_with_bls12_381() { let mut cs = TestConstraintSystem::::new(); let instance = Output { - params: params, + params, value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), @@ -759,13 +945,13 @@ fn test_output_circuit_with_bls12_381() { .expect("should be valid") .cm(params); - let expected_value_cm = value_commitment.cm(params).into_xy(); + let expected_value_cm = value_commitment.cm(params).to_xy(); let expected_epk = payment_address .g_d(params) .expect("should be valid") .mul(esk, params); - let expected_epk_xy = expected_epk.into_xy(); + let expected_epk_xy = expected_epk.to_xy(); assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); diff --git a/zcash_proofs/src/circuit/sprout/input.rs b/zcash_proofs/src/circuit/sprout/input.rs index ad6091f..a2726d4 100644 --- a/zcash_proofs/src/circuit/sprout/input.rs +++ b/zcash_proofs/src/circuit/sprout/input.rs @@ -54,7 +54,7 @@ impl InputNote { // Witness into the merkle tree let mut cur = cm.clone(); - for (i, layer) in auth_path.into_iter().enumerate() { + for (i, layer) in auth_path.iter().enumerate() { let cs = &mut cs.namespace(|| format!("layer {}", i)); let cur_is_right = AllocatedBit::alloc( @@ -112,7 +112,7 @@ impl InputNote { ); } - Ok(InputNote { mac: mac, nf: nf }) + Ok(InputNote { mac, nf }) } } diff --git a/zcash_proofs/src/circuit/sprout/mod.rs b/zcash_proofs/src/circuit/sprout/mod.rs index 358e1bb..6afe677 100644 --- a/zcash_proofs/src/circuit/sprout/mod.rs +++ b/zcash_proofs/src/circuit/sprout/mod.rs @@ -1,3 +1,15 @@ +//! The "hybrid Sprout" circuit. +//! +//! "Hybrid Sprout" refers to the implementation of the [Sprout statement] in +//! `bellman` for [`groth16`], instead of the [original implementation][oldimpl] +//! using [`libsnark`] for [BCTV14]. +//! +//! [Sprout statement]: https://zips.z.cash/protocol/protocol.pdf#joinsplitstatement +//! [`groth16`]: bellman::groth16 +//! [oldimpl]: https://github.com/zcash/zcash/tree/v2.0.7/src/zcash/circuit +//! [`libsnark`]: https://github.com/scipr-lab/libsnark +//! [BCTV14]: https://eprint.iacr.org/2013/879 + use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::multipack::pack_into_inputs; use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError}; @@ -234,10 +246,7 @@ impl NoteValue { )?); } - Ok(NoteValue { - value: value, - bits: bits, - }) + Ok(NoteValue { value, bits }) } /// Encodes the bits of the value into little-endian @@ -247,7 +256,7 @@ impl NoteValue { .chunks(8) .flat_map(|v| v.iter().rev()) .cloned() - .map(|e| Boolean::from(e)) + .map(Boolean::from) .collect() } @@ -259,7 +268,7 @@ impl NoteValue { let mut coeff = E::Fr::one(); for b in &self.bits { tmp = tmp + (coeff, b.get_variable()); - coeff.double(); + coeff = coeff.double(); } tmp @@ -326,6 +335,7 @@ where } #[test] +#[ignore] fn test_sprout_constraints() { use bellman::gadgets::test::*; use pairing::bls12_381::Bls12; @@ -379,11 +389,11 @@ fn test_sprout_constraints() { let a_sk = Some(SpendingKey(get_u256(&mut test_vector))); inputs.push(JSInput { - value: value, - a_sk: a_sk, - rho: rho, - r: r, - auth_path: auth_path, + value, + a_sk, + rho, + r, + auth_path, }); } @@ -395,11 +405,7 @@ fn test_sprout_constraints() { get_u256(&mut test_vector); let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); - outputs.push(JSOutput { - value: value, - a_pk: a_pk, - r: r, - }); + outputs.push(JSOutput { value, a_pk, r }); } let vpub_old = Some(test_vector.read_u64::().unwrap()); @@ -415,13 +421,13 @@ fn test_sprout_constraints() { let mac2 = get_u256(&mut test_vector); let js = JoinSplit { - vpub_old: vpub_old, - vpub_new: vpub_new, - h_sig: h_sig, - phi: phi, - inputs: inputs, - outputs: outputs, - rt: rt, + vpub_old, + vpub_new, + h_sig, + phi, + inputs, + outputs, + rt, }; js.synthesize(&mut cs).unwrap(); diff --git a/zcash_proofs/src/circuit/sprout/output.rs b/zcash_proofs/src/circuit/sprout/output.rs index a9a1e48..73a9851 100644 --- a/zcash_proofs/src/circuit/sprout/output.rs +++ b/zcash_proofs/src/circuit/sprout/output.rs @@ -11,7 +11,7 @@ pub struct OutputNote { } impl OutputNote { - pub fn compute<'a, E, CS>( + pub fn compute( mut cs: CS, a_pk: Option, value: &NoteValue, @@ -41,6 +41,6 @@ impl OutputNote { &r, )?; - Ok(OutputNote { cm: cm }) + Ok(OutputNote { cm }) } } diff --git a/zcash_proofs/src/hashreader.rs b/zcash_proofs/src/hashreader.rs index dbe686f..f8487b8 100644 --- a/zcash_proofs/src/hashreader.rs +++ b/zcash_proofs/src/hashreader.rs @@ -11,7 +11,7 @@ impl HashReader { /// Construct a new `HashReader` given an existing `reader` by value. pub fn new(reader: R) -> Self { HashReader { - reader: reader, + reader, hasher: State::new(), } } diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 3851481..7faca6c 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -1,19 +1,10 @@ -extern crate bellman; -extern crate blake2b_simd; -extern crate byteorder; -extern crate ff; -extern crate pairing; -extern crate rand_os; -extern crate zcash_primitives; +//! *Zcash circuits and proofs.* +//! +//! `zcash_proofs` contains the zk-SNARK circuits used by Zcash, and the APIs for creating +//! and verifying proofs. -#[cfg(feature = "local-prover")] -extern crate directories; - -#[cfg(test)] -extern crate rand_core; - -#[cfg(test)] -extern crate rand_xorshift; +// Catch documentation errors caused by code changes. +#![deny(intra_doc_link_resolution_failure)] use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey}; use pairing::bls12_381::Bls12; @@ -24,17 +15,15 @@ use std::path::Path; pub mod circuit; mod hashreader; pub mod sapling; +pub mod sprout; #[cfg(feature = "local-prover")] pub mod prover; pub fn load_parameters( spend_path: &Path, - spend_hash: &str, output_path: &Path, - output_hash: &str, sprout_path: Option<&Path>, - sprout_hash: Option<&str>, ) -> ( Parameters, PreparedVerifyingKey, @@ -42,6 +31,11 @@ pub fn load_parameters( PreparedVerifyingKey, Option>, ) { + // Sapling circuit hashes + const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; + const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; + const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; + // Load from each of the paths let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file"); let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file"); @@ -82,15 +76,18 @@ pub fn load_parameters( .expect("couldn't finish reading Sprout groth16 parameter file"); } - if spend_fs.into_hash() != spend_hash { + if spend_fs.into_hash() != SAPLING_SPEND_HASH { panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if output_fs.into_hash() != output_hash { + if output_fs.into_hash() != SAPLING_OUTPUT_HASH { panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if sprout_fs.map(|fs| fs.into_hash()) != sprout_hash.map(|h| h.to_owned()) { + if sprout_fs + .map(|fs| fs.into_hash() != SPROUT_HASH) + .unwrap_or(false) + { panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index 6dd5767..3dd665a 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -9,7 +9,7 @@ use zcash_primitives::{ primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, }; use zcash_primitives::{ - merkle_tree::CommitmentTreeWitness, + merkle_tree::MerklePath, prover::TxProver, redjubjub::{PublicKey, Signature}, sapling::Node, @@ -19,9 +19,6 @@ use zcash_primitives::{ use crate::{load_parameters, sapling::SaplingProvingContext}; -const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; -const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; - /// An implementation of [`TxProver`] using Sapling Spend and Output parameters from /// locally-accessible paths. pub struct LocalTxProver { @@ -50,14 +47,8 @@ impl LocalTxProver { /// This function will panic if the paths do not point to valid parameter files with /// the expected hashes. pub fn new(spend_path: &Path, output_path: &Path) -> Self { - let (spend_params, spend_vk, output_params, _, _) = load_parameters( - spend_path, - SAPLING_SPEND_HASH, - output_path, - SAPLING_OUTPUT_HASH, - None, - None, - ); + let (spend_params, spend_vk, output_params, _, _) = + load_parameters(spend_path, output_path, None); LocalTxProver { spend_params, spend_vk, @@ -127,7 +118,7 @@ impl TxProver for LocalTxProver { ar: Fs, value: u64, anchor: Fr, - witness: CommitmentTreeWitness, + merkle_path: MerklePath, ) -> Result< ( [u8; GROTH_PROOF_SIZE], @@ -143,7 +134,7 @@ impl TxProver for LocalTxProver { ar, value, anchor, - witness, + merkle_path, &self.spend_params, &self.spend_vk, &JUBJUB, diff --git a/zcash_proofs/src/sapling/mod.rs b/zcash_proofs/src/sapling/mod.rs index 9aa62f1..cd37578 100644 --- a/zcash_proofs/src/sapling/mod.rs +++ b/zcash_proofs/src/sapling/mod.rs @@ -1,6 +1,8 @@ +//! Helpers for creating Sapling proofs. + use pairing::bls12_381::Bls12; use zcash_primitives::jubjub::{ - edwards, fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown, + edwards, fs::Fs, FixedGenerators, JubjubBls12, JubjubParams, Unknown, }; use zcash_primitives::transaction::components::Amount; @@ -28,7 +30,7 @@ fn compute_value_balance( // Compute it in the exponent let mut value_balance = params .generator(FixedGenerators::ValueCommitmentValue) - .mul(FsRepr::from(abs), params); + .mul(Fs::from(abs), params); // Negate if necessary if is_negative { diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 283e76b..f7b0373 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -4,13 +4,14 @@ use bellman::{ }; use ff::Field; use pairing::bls12_381::{Bls12, Fr}; -use rand_os::OsRng; +use rand_core::OsRng; +use std::ops::{AddAssign, Neg}; use zcash_primitives::{ jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown}, primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment}, }; use zcash_primitives::{ - merkle_tree::CommitmentTreeWitness, + merkle_tree::MerklePath, redjubjub::{PrivateKey, PublicKey, Signature}, sapling::Node, transaction::components::Amount, @@ -22,7 +23,8 @@ use crate::circuit::sapling::{Output, Spend}; /// A context object for creating the Sapling components of a Zcash transaction. pub struct SaplingProvingContext { bsk: Fs, - bvk: edwards::Point, + // (sum of the Spend value commitments) - (sum of the Output value commitments) + cv_sum: edwards::Point, } impl SaplingProvingContext { @@ -30,7 +32,7 @@ impl SaplingProvingContext { pub fn new() -> Self { SaplingProvingContext { bsk: Fs::zero(), - bvk: edwards::Point::zero(), + cv_sum: edwards::Point::zero(), } } @@ -45,7 +47,7 @@ impl SaplingProvingContext { ar: Fs, value: u64, anchor: Fr, - witness: CommitmentTreeWitness, + merkle_path: MerklePath, proving_key: &Parameters, verifying_key: &PreparedVerifyingKey, params: &JubjubBls12, @@ -65,7 +67,7 @@ impl SaplingProvingContext { // Accumulate the value commitment randomness in the context { - let mut tmp = rcv.clone(); + let mut tmp = rcv; tmp.add_assign(&self.bsk); // Update the context @@ -74,18 +76,17 @@ impl SaplingProvingContext { // Construct the value commitment let value_commitment = ValueCommitment:: { - value: value, + value, randomness: rcv, }; // Construct the viewing key - let viewing_key = proof_generation_key.into_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(params); // Construct the payment address with the viewing key / diversifier - let payment_address = match viewing_key.into_payment_address(diversifier, params) { - Some(p) => p, - None => return Err(()), - }; + let payment_address = viewing_key + .to_payment_address(diversifier, params) + .ok_or(())?; // This is the result of the re-randomization, we compute it for the caller let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( @@ -96,15 +97,15 @@ impl SaplingProvingContext { // Let's compute the nullifier while we have the position let note = Note { - value: value, + value, g_d: diversifier .g_d::(params) .expect("was a valid diversifier before"), - pk_d: payment_address.pk_d.clone(), + pk_d: payment_address.pk_d().clone(), r: rcm, }; - let nullifier = note.nf(&viewing_key, witness.position, params); + let nullifier = note.nf(&viewing_key, merkle_path.position, params); // We now have the full witness for our circuit let instance = Spend { @@ -114,10 +115,10 @@ impl SaplingProvingContext { payment_address: Some(payment_address), commitment_randomness: Some(rcm), ar: Some(ar), - auth_path: witness + auth_path: merkle_path .auth_path .iter() - .map(|n| n.map(|(node, b)| (node.into(), b))) + .map(|(node, b)| Some(((*node).into(), *b))) .collect(), anchor: Some(anchor), }; @@ -130,12 +131,12 @@ impl SaplingProvingContext { // Construct public input for circuit let mut public_input = [Fr::zero(); 7]; { - let (x, y) = rk.0.into_xy(); + let (x, y) = rk.0.to_xy(); public_input[0] = x; public_input[1] = y; } { - let (x, y) = value_commitment.cm(params).into_xy(); + let (x, y) = value_commitment.cm(params).to_xy(); public_input[2] = x; public_input[3] = y; } @@ -169,10 +170,10 @@ impl SaplingProvingContext { // Accumulate the value commitment in the context { let mut tmp = value_commitment.clone(); - tmp = tmp.add(&self.bvk, params); + tmp = tmp.add(&self.cv_sum, params); // Update the context - self.bvk = tmp; + self.cv_sum = tmp; } Ok((proof, value_commitment, rk)) @@ -200,8 +201,7 @@ impl SaplingProvingContext { // Accumulate the value commitment randomness in the context { - let mut tmp = rcv.clone(); - tmp.negate(); // Outputs subtract from the total. + let mut tmp = rcv.neg(); // Outputs subtract from the total. tmp.add_assign(&self.bsk); // Update the context @@ -210,7 +210,7 @@ impl SaplingProvingContext { // Construct the value commitment for the proof instance let value_commitment = ValueCommitment:: { - value: value, + value, randomness: rcv, }; @@ -220,7 +220,7 @@ impl SaplingProvingContext { value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(rcm), - esk: Some(esk.clone()), + esk: Some(esk), }; // Create proof @@ -234,10 +234,10 @@ impl SaplingProvingContext { { let mut tmp = value_commitment.clone(); tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.bvk, params); + tmp = tmp.add(&self.cv_sum, params); // Update the context - self.bvk = tmp; + self.cv_sum = tmp; } (proof, value_commitment) @@ -261,18 +261,15 @@ impl SaplingProvingContext { let bvk = PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, params); // In order to check internal consistency, let's use the accumulated value - // commitments (as the verifier would) and apply valuebalance to compare + // commitments (as the verifier would) and apply value_balance to compare // against our derived bvk. { // Compute value balance - let mut value_balance = match compute_value_balance(value_balance, params) { - Some(a) => a, - None => return Err(()), - }; + let mut value_balance = compute_value_balance(value_balance, params).ok_or(())?; - // Subtract value_balance from current bvk to get final bvk + // Subtract value_balance from cv_sum to get final bvk value_balance = value_balance.negate(); - let mut tmp = self.bvk.clone(); + let mut tmp = self.cv_sum.clone(); tmp = tmp.add(&value_balance, params); // The result should be the same, unless the provided valueBalance is wrong. diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index 0801023..b886912 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -18,14 +18,15 @@ fn is_small_order(p: &edwards::Point, params: &JubjubBls12) /// A context object for verifying the Sapling components of a Zcash transaction. pub struct SaplingVerificationContext { - bvk: edwards::Point, + // (sum of the Spend value commitments) - (sum of the Output value commitments) + cv_sum: edwards::Point, } impl SaplingVerificationContext { /// Construct a new context to be used with a single transaction. pub fn new() -> Self { SaplingVerificationContext { - bvk: edwards::Point::zero(), + cv_sum: edwards::Point::zero(), } } @@ -54,10 +55,10 @@ impl SaplingVerificationContext { // Accumulate the value commitment in the context { let mut tmp = cv.clone(); - tmp = tmp.add(&self.bvk, params); + tmp = tmp.add(&self.cv_sum, params); // Update the context - self.bvk = tmp; + self.cv_sum = tmp; } // Grab the nullifier as a sequence of bytes @@ -82,12 +83,12 @@ impl SaplingVerificationContext { // Construct public input for circuit let mut public_input = [Fr::zero(); 7]; { - let (x, y) = rk.0.into_xy(); + let (x, y) = rk.0.to_xy(); public_input[0] = x; public_input[1] = y; } { - let (x, y) = cv.into_xy(); + let (x, y) = cv.to_xy(); public_input[2] = x; public_input[3] = y; } @@ -137,21 +138,21 @@ impl SaplingVerificationContext { { let mut tmp = cv.clone(); tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.bvk, params); + tmp = tmp.add(&self.cv_sum, params); // Update the context - self.bvk = tmp; + self.cv_sum = tmp; } // Construct public input for circuit let mut public_input = [Fr::zero(); 5]; { - let (x, y) = cv.into_xy(); + let (x, y) = cv.to_xy(); public_input[0] = x; public_input[1] = y; } { - let (x, y) = epk.into_xy(); + let (x, y) = epk.to_xy(); public_input[2] = x; public_input[3] = y; } @@ -177,8 +178,8 @@ impl SaplingVerificationContext { binding_sig: Signature, params: &JubjubBls12, ) -> bool { - // Obtain current bvk from the context - let mut bvk = PublicKey(self.bvk.clone()); + // Obtain current cv_sum from the context + let mut bvk = PublicKey(self.cv_sum.clone()); // Compute value balance let mut value_balance = match compute_value_balance(value_balance, params) { @@ -186,7 +187,7 @@ impl SaplingVerificationContext { None => return false, }; - // Subtract value_balance from current bvk to get final bvk + // Subtract value_balance from current cv_sum to get final bvk value_balance = value_balance.negate(); bvk.0 = bvk.0.add(&value_balance, params); diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs new file mode 100644 index 0000000..30ec821 --- /dev/null +++ b/zcash_proofs/src/sprout.rs @@ -0,0 +1,179 @@ +//! APIs for creating and verifying Sprout proofs. + +use bellman::{ + gadgets::multipack, + groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof}, +}; +use pairing::bls12_381::Bls12; +use rand_core::OsRng; + +use crate::circuit::sprout::*; + +const GROTH_PROOF_SIZE: usize = 48 // π_A + + 96 // π_B + + 48; // π_C +pub const WITNESS_PATH_SIZE: usize = 1 + 33 * TREE_DEPTH + 8; + +/// Sprout JoinSplit proof generation. +pub fn create_proof( + phi: [u8; 32], + rt: [u8; 32], + h_sig: [u8; 32], + + // First input + in_sk1: [u8; 32], + in_value1: u64, + in_rho1: [u8; 32], + in_r1: [u8; 32], + in_auth1: &[u8; WITNESS_PATH_SIZE], + + // Second input + in_sk2: [u8; 32], + in_value2: u64, + in_rho2: [u8; 32], + in_r2: [u8; 32], + in_auth2: &[u8; WITNESS_PATH_SIZE], + + // First output + out_pk1: [u8; 32], + out_value1: u64, + out_r1: [u8; 32], + + // Second output + out_pk2: [u8; 32], + out_value2: u64, + out_r2: [u8; 32], + + // Public value + vpub_old: u64, + vpub_new: u64, + + proving_key: &Parameters, +) -> Proof { + let mut inputs = Vec::with_capacity(2); + { + let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| { + let value = Some(value); + let rho = Some(UniqueRandomness(rho)); + let r = Some(CommitmentRandomness(r)); + let a_sk = Some(SpendingKey(sk)); + + // skip the first byte + assert_eq!(auth[0], TREE_DEPTH as u8); + auth = &auth[1..]; + + let mut auth_path = [None; TREE_DEPTH]; + for i in (0..TREE_DEPTH).rev() { + // skip length of inner vector + assert_eq!(auth[0], 32); + auth = &auth[1..]; + + let mut sibling = [0u8; 32]; + sibling.copy_from_slice(&auth[0..32]); + auth = &auth[32..]; + + auth_path[i] = Some((sibling, false)); + } + + let mut position = { + let mut bytes = [0; 8]; + bytes.copy_from_slice(&auth[0..8]); + u64::from_le_bytes(bytes) + }; + + for entry in auth_path.iter_mut() { + if let Some(p) = entry { + p.1 = (position & 1) == 1; + } + + position >>= 1; + } + + inputs.push(JSInput { + value, + a_sk, + rho, + r, + auth_path, + }); + }; + + handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]); + handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]); + } + + let mut outputs = Vec::with_capacity(2); + { + let mut handle_output = |a_pk, value, r| { + outputs.push(JSOutput { + value: Some(value), + a_pk: Some(PayingKey(a_pk)), + r: Some(CommitmentRandomness(r)), + }); + }; + + handle_output(out_pk1, out_value1, out_r1); + handle_output(out_pk2, out_value2, out_r2); + } + + let js = JoinSplit { + vpub_old: Some(vpub_old), + vpub_new: Some(vpub_new), + h_sig: Some(h_sig), + phi: Some(phi), + inputs, + outputs, + rt: Some(rt), + }; + + // Initialize secure RNG + let mut rng = OsRng; + + create_random_proof(js, proving_key, &mut rng).expect("proving should not fail") +} + +/// Sprout JoinSplit proof verification. +pub fn verify_proof( + proof: &[u8; GROTH_PROOF_SIZE], + rt: &[u8; 32], + h_sig: &[u8; 32], + mac1: &[u8; 32], + mac2: &[u8; 32], + nf1: &[u8; 32], + nf2: &[u8; 32], + cm1: &[u8; 32], + cm2: &[u8; 32], + vpub_old: u64, + vpub_new: u64, + verifying_key: &PreparedVerifyingKey, +) -> bool { + // Prepare the public input for the verifier + let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); + public_input.extend(rt); + public_input.extend(h_sig); + public_input.extend(nf1); + public_input.extend(mac1); + public_input.extend(nf2); + public_input.extend(mac2); + public_input.extend(cm1); + public_input.extend(cm2); + public_input.extend(&vpub_old.to_le_bytes()); + public_input.extend(&vpub_new.to_le_bytes()); + + let public_input = multipack::bytes_to_bits(&public_input); + let public_input = multipack::compute_multipacking::(&public_input); + + let proof = match Proof::read(&proof[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // Verify the proof + match groth16::verify_proof(verifying_key, &proof, &public_input[..]) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } +}